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