Hey gendarmers,

This new rule in the Correctness category emits a defect when a regular
expression string provided to methods requiring those is an invalid pattern
(and would consequently throw an exception at run-time).

Bad example:
Regex re = new Regex ("^\\"); //Invalid end of pattern

Good example:
Regex re = new Regex (@"^\\");

Bad example 2:
Regex.IsMatch (input, "^([a-z)*"); //Unterminated [] set

Good example 2:
Regex.IsMatch (input, "^([a-z])*");


For example a defect like example 2 looks like :

3. ProvideCorrectRegexPatternRule

Problem: An invalid regular expression pattern is provided to a
method/constructor requiring a valid pattern.
* Severity: High, Confidence: High
* Target:   System.Void RegexModule::.cctor()
* Source:   /home/cedric/tmp/regex.boo(≈1)
* Details:  Pattern '[a-z' is invalid. Reason: parsing "[a-z" - Unterminated
[] set.

Solution: Fix the invalid regular expression.
More info available at:
http://www.mono-project.com/Gendarme.Rules.Correctness#ProvideCorrectRegexPatternRule


Please review,

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Gendarme" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to 
[email protected]
For more options, visit this group at 
http://groups.google.com/group/gendarme?hl=en
-~----------~----~----~----~------~----~------~--~---

From 942e816885666dfceba614037a16a20ec4787ff4 Mon Sep 17 00:00:00 2001
From: Cedric Vivier <[email protected]>
Date: Sun, 21 Dec 2008 02:41:07 +0800
Subject: [PATCH] WIP in 'ProvideCorrectRegexPatternRule' branch.

2008-12-21  Cedric Vivier  <[email protected]>

	* ProvideCorrectRegexPatternRule.cs: New. Rule that checks if
	methods/constructors requiring a regular expression string are
	provided a valid pattern.
---
 .../rules/Gendarme.Rules.Correctness/ChangeLog     |    6 +
 .../rules/Gendarme.Rules.Correctness/Makefile.am   |    2 +
 .../ProvideCorrectRegexPatternRule.cs              |  202 +++++++++++++++
 .../Test/ProvideCorrectRegexPatternTest.cs         |  273 ++++++++++++++++++++
 gendarme/rules/common.make                         |    2 +-
 5 files changed, 484 insertions(+), 1 deletions(-)
 create mode 100644 gendarme/rules/Gendarme.Rules.Correctness/ProvideCorrectRegexPatternRule.cs
 create mode 100644 gendarme/rules/Gendarme.Rules.Correctness/Test/ProvideCorrectRegexPatternTest.cs

diff --git a/gendarme/rules/Gendarme.Rules.Correctness/ChangeLog b/gendarme/rules/Gendarme.Rules.Correctness/ChangeLog
index 0b6597a..c79266b 100644
--- a/gendarme/rules/Gendarme.Rules.Correctness/ChangeLog
+++ b/gendarme/rules/Gendarme.Rules.Correctness/ChangeLog
@@ -1,3 +1,9 @@
+2008-12-21  Cedric Vivier  <[email protected]>
+
+	* ProvideCorrectRegexPatternRule.cs: New. Rule that checks if
+	methods/constructors requiring a regular expression string are
+	provided a valid pattern.
+
 2008-12-19  Sebastien Pouliot  <[email protected]>
 
 	* CheckParametersNullityInVisibleMethodsRule.cs: Make sure (null ==
diff --git a/gendarme/rules/Gendarme.Rules.Correctness/Makefile.am b/gendarme/rules/Gendarme.Rules.Correctness/Makefile.am
index 6acfde5..2b1354e 100644
--- a/gendarme/rules/Gendarme.Rules.Correctness/Makefile.am
+++ b/gendarme/rules/Gendarme.Rules.Correctness/Makefile.am
@@ -16,6 +16,7 @@ rules_sources =  \
 	ReviewInconsistentIdentityRule.cs \
 	MethodCanBeMadeStaticRule.cs \
 	ProvideCorrectArgumentsToFormattingMethodsRule.cs \
+	ProvideCorrectRegexPatternRule.cs \
 	ReviewCastOnIntegerDivisionRule.cs \
 	ReviewCastOnIntegerMultiplicationRule.cs \
 	ReviewDoubleAssignmentRule.cs \
@@ -38,6 +39,7 @@ tests_sources = \
 	FinalizersShouldCallBaseClassFinalizerTest.cs \
 	ReviewInconsistentIdentityTest.cs \
 	MethodCanBeMadeStaticTest.cs \
+	ProvideCorrectRegexPatternTest.cs \
 	ReviewCastOnIntegerDivisionTest.cs \
 	ReviewCastOnIntegerMultiplicationTest.cs \
 	ReviewDoubleAssignmentTest.cs \
diff --git a/gendarme/rules/Gendarme.Rules.Correctness/ProvideCorrectRegexPatternRule.cs b/gendarme/rules/Gendarme.Rules.Correctness/ProvideCorrectRegexPatternRule.cs
new file mode 100644
index 0000000..c158023
--- /dev/null
+++ b/gendarme/rules/Gendarme.Rules.Correctness/ProvideCorrectRegexPatternRule.cs
@@ -0,0 +1,202 @@
+//
+// Gendarme.Rules.Correctness.ProvideCorrectRegexPatternRule class
+//
+// Authors:
+//	Cedric Vivier <[email protected]>
+//
+// Copyright (C) 2008 Cedric Vivier
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using System.Text;
+
+using Mono.Cecil;
+using Mono.Cecil.Cil;
+
+using Gendarme.Framework;
+using Gendarme.Framework.Rocks;
+using Gendarme.Framework.Engines;
+using Gendarme.Framework.Helpers;
+
+using System.Collections.Generic;
+using System.Text.RegularExpressions;
+
+namespace Gendarme.Rules.Correctness {
+
+	/// <summary>
+	/// This rule checks if methods/constructors requiring a regular expression
+	/// string are provided a valid pattern.
+	/// </summary>
+	/// <example>
+	/// Bad example:
+	/// <code>
+	/// Regex re = new Regex ("^\\"); //Invalid end of pattern
+	/// </code>
+	/// </example>
+	/// <example>
+	/// Good example:
+	/// <code>
+	/// Regex re = new Regex (@"^\\");
+	/// </code>
+	/// </example>
+	/// <example>
+	/// Bad example:
+	/// <code>
+	/// Regex re = new Regex ("([a-z)*"); //Unterminated [] set
+	/// </code>
+	/// </example>
+	/// <example>
+	/// Good example:
+	/// <code>
+	/// Regex re = new Regex ("([a-z])*");
+	/// </code>
+	/// </example>
+	/// <example>
+	/// Bad example:
+	/// <code>
+	/// Regex.IsMatch (code, @"(\w)-\2"); //Reference to undefined group number 2
+	/// </code>
+	/// </example>
+	/// <example>
+	/// Good example:
+	/// <code>
+	/// Regex.IsMatch (code, @"(\w)-\1");
+	/// </code>
+	/// </example>
+
+	[Problem ("An invalid regular expression pattern is provided to a method/constructor requiring a valid pattern.")]
+	[Solution ("Fix the invalid regular expression.")]
+	[EngineDependency (typeof (OpCodeEngine))]
+	public class ProvideCorrectRegexPatternRule : Rule, IMethodRule {
+
+		static OpCodeBitmask callsAndNewobjBitmask = BuildCallsAndNewobjOpCodeBitmask ();
+
+		MethodDefinition method;
+
+		const string RegexClass = "System.Text.RegularExpressions.Regex";
+		const string ValidatorClass = "System.Configuration.RegexStringValidator";
+
+		public override void Initialize (IRunner runner)
+		{
+			base.Initialize (runner);
+
+			Runner.AnalyzeModule += delegate (object o, RunnerEventArgs e) {
+				bool usingRegexClass = e.CurrentAssembly.Name.Name == "System"
+				                       || e.CurrentModule.TypeReferences.ContainsType (RegexClass);
+				bool usingValidatorClass = e.CurrentAssembly.Runtime >= TargetRuntime.NET_2_0
+				                           && (e.CurrentAssembly.Name.Name == "System.Configuration"
+				                              || e.CurrentModule.TypeReferences.ContainsType (ValidatorClass));
+				Active = usingRegexClass | usingValidatorClass;
+			};
+		}
+
+		void CheckPattern (Instruction ins, int argumentIndex)
+		{
+			Instruction ld = ins;
+			for (int i = 0; i < argumentIndex; ++i)
+				ld = ld.Previous;
+
+			switch (ld.OpCode.Code) {
+			case Code.Ldstr:
+				CheckPattern (ins, (string) ld.Operand);
+				break;
+			case Code.Ldsfld:
+				{
+					FieldReference f = (FieldReference) ld.Operand;
+					if (f.Name != "Empty" || f.DeclaringType.FullName != "System.String")
+						return;
+				}
+				CheckPattern (ins, null);
+				break;
+			case Code.Ldnull:
+				CheckPattern (ins, null);
+				break;
+			}
+		}
+
+		void CheckPattern (Instruction ins, string pattern)
+		{
+			if (string.IsNullOrEmpty (pattern)) {
+				Runner.Report (method, ins, Severity.High, Confidence.High, "Pattern is null or empty.");
+				return;
+			}
+
+			try {
+				new Regex (pattern);
+			} catch (Exception e) {
+				string msg = string.Format ("Pattern '{0}' is invalid. Reason: {1}", pattern, e.Message);
+				Runner.Report (method, ins, Severity.High, Confidence.High, msg);
+			}
+		}
+
+		void CheckCall (Instruction ins, MethodDefinition call)
+		{
+			//check only constructors and static non-property methods
+			if (call.Name != MethodDefinition.Ctor && (call.HasThis || call.IsProperty ()))
+				return;
+			if (!call.HasParameters)
+				return;
+			if (call.DeclaringType.FullName != RegexClass && call.DeclaringType.FullName != ValidatorClass)
+				return;
+
+			for (int i = 0; i < call.Parameters.Count; ++i) {
+				if ((call.Parameters [i].Name == "pattern" || call.Parameters [i].Name == "regex")
+				    && call.Parameters [i].ParameterType.FullName == "System.String") {
+					CheckPattern (ins, call.Parameters.Count - i);
+					return;
+				}
+			}
+		}
+
+		public RuleResult CheckMethod (MethodDefinition method)
+		{
+			if (!method.HasBody)
+				return RuleResult.DoesNotApply;
+
+			this.method = method;
+
+			//is there any interesting opcode in the method?
+			if (!callsAndNewobjBitmask.Intersect (OpCodeEngine.GetBitmask (method)))
+				return RuleResult.DoesNotApply;
+
+			foreach (Instruction ins in method.Body.Instructions) {
+				if (!callsAndNewobjBitmask.Get (ins.OpCode.Code))
+					continue;
+
+				MethodReference call = (MethodReference) ins.Operand;
+				CheckCall (ins, call.Resolve ());
+			}
+
+			return Runner.CurrentRuleResult;
+		}
+
+		static OpCodeBitmask BuildCallsAndNewobjOpCodeBitmask ()
+		{
+			#if true
+				return new OpCodeBitmask (0x8000000000, 0x4400000000000, 0x0, 0x0);
+			#else
+				OpCodeBitmask mask = new OpCodeBitmask ();
+				mask.UnionWith (OpCodeBitmask.Calls);
+				mask.Set (Code.Newobj);
+				return mask;
+			#endif
+		}
+	}
+}
diff --git a/gendarme/rules/Gendarme.Rules.Correctness/Test/ProvideCorrectRegexPatternTest.cs b/gendarme/rules/Gendarme.Rules.Correctness/Test/ProvideCorrectRegexPatternTest.cs
new file mode 100644
index 0000000..4942925
--- /dev/null
+++ b/gendarme/rules/Gendarme.Rules.Correctness/Test/ProvideCorrectRegexPatternTest.cs
@@ -0,0 +1,273 @@
+//
+// Unit tests for ProvideCorrectRegexPatternRule
+//
+// Authors:
+//	Cedric Vivier <[email protected]>
+//
+// Copyright (C) 2008 Cedric Vivier
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to deal
+// in the Software without restriction, including without limitation the rights
+// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+// copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+// THE SOFTWARE.
+
+using System;
+using NUnit.Framework;
+
+using Gendarme.Rules.Correctness;
+
+using Test.Rules.Fixtures;
+using Test.Rules.Helpers;
+using Test.Rules.Definitions;
+
+using System.Text.RegularExpressions;
+using System.Configuration;
+
+
+namespace Test.Rules.Correctness {
+
+	class RegexCases {
+		string foo = "foo";
+		const string good1 = "good1";
+		const string bad1 = "bad1\\";
+		Regex re;
+
+		string DoesNotApply1 () {
+			return bad1;
+		}
+
+		void Success0 () {
+			re = new Regex (@"^\\");
+		}
+
+		void Failure0 () {
+			re = new Regex ("^\\");
+		}
+
+		void Success1 () {
+			re = new Regex (good1);
+		}
+
+		void Failure1 () {
+			re = new Regex (bad1);
+		}
+
+		void Success2 (string pattern) {
+			var re = new Regex (pattern);
+		}
+
+		void Failure2 () {
+			var re = new Regex (@"^([a-z)*");
+		}
+
+		bool Success2b (Regex re) {
+			return re.IsMatch ("^([a-z])*");
+		}
+
+		bool Success3 (string pattern) {
+			return Regex.IsMatch (foo, pattern);
+		}
+
+		bool Failure3 (string text) {
+			return Regex.IsMatch (text, @"\9");
+		}
+
+		bool Success4 (string pattern) {
+			if (null != Regex.Match (good1, "good"))
+				return true;
+			return Regex.IsMatch (foo, pattern);
+		}
+
+		bool Failure4 (string text) {
+			if (null != Regex.Match (good1, "good"))
+				return true;
+			return Regex.IsMatch (text, @"\9");
+		}
+
+		string Success5 (string source) {
+			return Regex.Replace (source, "foo", "niaa");
+		}
+
+		string Failure5 (string source) {
+			return Regex.Replace (source, "foo\\", "niaa");
+		}
+
+		string Success6 (string source) {
+			var re = new Regex ("good");
+			return re.Replace ("bad\\", "bad\\");
+		}
+
+		string Failure6 (string source) {
+			return Regex.Replace (source, null, "niaa");
+		}
+
+		string Failure7 (string source) {
+			return Regex.Replace (source, "", "niaa");
+		}
+
+		string Failure67 (string source) {
+			source = Regex.Replace (source, null, "niaa");
+			return Regex.Replace (source, "", "niaa");
+		}
+	}
+
+	class ValidatorCases {
+		void Success1 (string input) {
+			(new RegexStringValidator ("^[a-z]+")).Validate (input);
+		}
+
+		void Failure1 (string input) {
+			(new RegexStringValidator ("^[a-z+")).Validate (input);
+		}
+	}
+
+	[TestFixture]
+	public class ProvideCorrectRegexPatternTest : MethodRuleTestFixture<ProvideCorrectRegexPatternRule> {
+
+		static bool raisedAnalyzeModuleEvent;
+
+		[SetUp]
+		public void RaiseAnalyzeModuleEvent ()
+		{
+			if (raisedAnalyzeModuleEvent)
+				return;
+
+			raisedAnalyzeModuleEvent = true;
+			((TestRunner) Runner).OnModule (DefinitionLoader.GetTypeDefinition<RegexCases> ().Module);
+		}
+
+		[Test]
+		public void DoesNotApply0 ()
+		{
+			AssertRuleDoesNotApply (SimpleMethods.EmptyMethod);
+		}
+
+		[Test]
+		public void DoesNotApply ()
+		{
+			AssertRuleDoesNotApply<RegexCases> ("DoesNotApply1");
+		}
+
+		[Test]
+		public void Success0 ()
+		{
+			AssertRuleSuccess<RegexCases> ("Success0");
+		}
+
+		[Test]
+		public void Success1 ()
+		{
+			AssertRuleSuccess<RegexCases> ("Success1");
+		}
+
+		[Test]
+		public void Success2 ()
+		{
+			AssertRuleSuccess<RegexCases> ("Success2");
+			AssertRuleSuccess<RegexCases> ("Success2b");
+		}
+
+		[Test]
+		public void Success3 ()
+		{
+			AssertRuleSuccess<RegexCases> ("Success3");
+		}
+
+		[Test]
+		public void Success4 ()
+		{
+			AssertRuleSuccess<RegexCases> ("Success4");
+		}
+
+		[Test]
+		public void Success5 ()
+		{
+			AssertRuleSuccess<RegexCases> ("Success5");
+		}
+
+		[Test]
+		public void Success6 ()
+		{
+			AssertRuleSuccess<RegexCases> ("Success6");
+		}
+
+		[Test]
+		public void Failure0 ()
+		{
+			AssertRuleFailure<RegexCases> ("Failure0", 1);
+		}
+
+		[Test]
+		public void Failure1 ()
+		{
+			AssertRuleFailure<RegexCases> ("Failure1", 1);
+		}
+
+		[Test]
+		public void Failure2 ()
+		{
+			AssertRuleFailure<RegexCases> ("Failure2", 1);
+		}
+
+		[Test]
+		public void Failure3 ()
+		{
+			AssertRuleFailure<RegexCases> ("Failure3", 1);
+		}
+
+		[Test]
+		public void Failure4 ()
+		{
+			AssertRuleFailure<RegexCases> ("Failure4", 1);
+		}
+
+		[Test]
+		public void Failure5 ()
+		{
+			AssertRuleFailure<RegexCases> ("Failure5", 1);
+		}
+
+		[Test]
+		public void Failure6 ()
+		{
+			AssertRuleFailure<RegexCases> ("Failure6", 1);
+		}
+
+		[Test]
+		public void Failure7 ()
+		{
+			AssertRuleFailure<RegexCases> ("Failure7", 1);
+		}
+
+		[Test]
+		public void Failure67 ()
+		{
+			AssertRuleFailure<RegexCases> ("Failure67", 2);
+		}
+
+		[Test]
+		public void ValidatorClassSuccess1 ()
+		{
+			AssertRuleSuccess<ValidatorCases> ("Success1");
+		}
+
+		[Test]
+		public void ValidatorClassFailure1 ()
+		{
+			AssertRuleFailure<ValidatorCases> ("Failure1", 1);
+		}
+	}
+}
diff --git a/gendarme/rules/common.make b/gendarme/rules/common.make
index b8ba71d..0be1059 100644
--- a/gendarme/rules/common.make
+++ b/gendarme/rules/common.make
@@ -7,7 +7,7 @@ EXTRA_TESTS_OPTIONS := $(TESTS_OPTIONS)
 
 console_runner=../../bin/gendarme.exe
 framework=../../bin/Gendarme.Framework.dll
-common_tests=../Test.Rules/Test.Rules.dll
+common_tests=../Test.Rules/Test.Rules.dll,System.Configuration
 
 prefixed_rules_category = $(shell expr "$(PWD)" : '.*\(Gendarme.Rules.*\)')
 rules_category = $(shell echo $(prefixed_rules_category) | cut -c 16-)
-- 
1.6.0.3

Reply via email to