FxCop Custom Rule

Create Custom FxCop Rules

FxCop is a free, open source code analysis tool from Microsoft. FxCop, stands for"Framework Police" and is a rules-based engine thatKent Boogaart: Code Analysis without Visual Studio 2008 checks managed code assemblies. .NET assemblies contain metadata entries that describe the assembly and all of its contained types. FxCop reads the assembly metadata and checks it for compliance against a set of rules that describe recommended coding standards and practices.

Once FxCop has analyzed the assemblies, it reports any rule violations and produces well-formatted XML test reports. Areas covered by FxCop are localization, performance, and security. FxCop checks .Net code against rules stored in an embedded xml file.

FxCop performs tests on the assemblies by parsing the MSIL (Microsoft Intermediate Language) they contain, inspecting, and reporting on code that fails to meet the rules. Since FxCop performs the tests on managed assemblies and not the source code, it's .NET-language neutral. Thus, you can run it against any assembly created using any language that targets the Microsoft .NET managed environment.

FxCop was developed by Microsoft and is available as a free download from their site.

FxCop has an extensive set of pre-defined rules. However, it also allows you to develop your own custom rules. To develop your own rules, you will need to delve into the MSIL. There are a number of ways to do this. One way to explore the MSIL for an assembly is to use the MSIL Disassembler (IL DASM) that comes with Visual Studio. A better way is to use the freeReflector for .NET by Lutz Roeder.

Now the bad news. There is no documentation for FxCop so developing custom rules is a matter of trial and error. Looking through the object model of FxCop is time well spent. To that end, I am sharing, on the following pages, a number of custom rules I created to check VB6 applications that were converted to VB.NET. I created these rules mostly to verify the converted applications used .NET statements and controls and did not rely on older VB6 controls or use the Microsoft.VisualBasic.Compatibility namespace.

FxCop Rule Overview

Each FxCop rule resides in its own class whose name must match the name of the rule.Also, each rule must have its own node in the embedded XML file. The XML file is what supplies the rule's message, description, resolution, and other attributes.

Most FxCop rules start out by calling either the Check or VisitCall method. Then, once a rule violation has been determined, return Problem or ProblemCollection after making a call to theGetResolution method to retrieve the rule information such as, message, resolution, certainty level, etc. from the XML file.

Note:from envykok

VisitCall method is  old data flow analysis engine that has been removed from the next version of FxCop/Visual Studio.

Hopefully, the following samples and some Googling, will help get you started.

Custom FxCop Rule - Do not call .NET Garbage Collector

Here is the CSharp source code for a custom FxCop rule to enforce not calling the .NET Garbage Collector directly from code. This rule, named DoNotCallGarbageCollectorDirectlyinherits from the BaseMigrationControlFlowRule class and calls the VisitCall method.

If you create a small VB.NET sample program to call the .NET Framework's Garbage Collector, build the code, and examine the MSIL, you will see calls to:

[mscorlib]System.GC::GetTotalMemory
[mscorlib]System.GC::CollectionCount
[mscorlib]System.GC::SuppressFinalize

So, we can create an FxCop rule that looks for calls to System.GC which is the class. We do not need to look for the particular methods.

Sample VB.NET code to call the garbage collector:

          Dim totMem As Long = GC.GetTotalMemory(True)
          Dim colCount As Integer = GC.CollectionCount(0)
          GC.SuppressFinalize(Me)

C# code for custom FxCop rule:

using System;
using Microsoft.Cci;
using Microsoft.Fugue;
using Microsoft.FxCop.Sdk;
using Microsoft.FxCop.Sdk.Introspection;

public class DoNotCallGarbageCollectorDirectly : BaseMigrationControlFlowRule
{
    public DoNotCallGarbageCollectorDirectly() : base("DoNotCallGarbageCollectorDirectly")
    {
    }

    public override void VisitCall(Variable destination, 
               Variable receiver, Method callee, ExpressionList arguments, 
               bool isVirtualCall, IProgramContext programContext, 
               IExecutionState stateBeforeInstruction, 
               IExecutionState stateAfterInstruction)
    {
        if (callee.DeclaringType.FullName.Trim().ToUpper().Equals("SYSTEM.GC"))
        {
            base.Problems.Add(new Problem(GetResolution(), programContext));
        }
        base.VisitCall(destination, receiver, callee, arguments, 
                isVirtualCall, programContext, stateBeforeInstruction, 
                stateAfterInstruction);
   }
}

Rule definition in the XML rules file:

<Rule TypeName="DoNotCallGarbageCollectorDirectly" 
          Category="VBMigration" CheckId="AA1001">
          
    <Name>
        Do not call Garbage Collector directly
    </Name>
    <Description>
        Calls to the Garbage Collector directly are not advised.
    </Description>
    <Url>
        http://www.thescarms.com/
    </Url>    
    <Resolution>
        Do not call Garbage Collector directly. To ensure objects are 
        disposed of properly, use the Using KeyWord.
    </Resolution>
    <MessageLevel Certainty="99">
        Warning
    </MessageLevel>
    <FixCategories>
        NonBreaking
    </FixCategories>
    <Owner />
<Rule>

The XML rules file is mostly self explanatory. You have the rule name which must match the class name, category the rule falls in so FxCop can group the results of its analysis, a certainty level, which in this case is set high since we can positively identify GC calls, and a recommended resolution. The CheckId is a unique id for the rule. Each rule must have a different CheckId.

Note: from envykok

since VisitCall is removed, here is alternative way:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.FxCop.Sdk;

namespace MyCustomRules
{
    internal sealed class DoNotCallGarbageCollectorDirectly : BaseFxCopRule
    {
        public DoNotCallGarbageCollectorDirectly()
            : base("DoNotCallGarbageCollectorDirectly")
        {
        }
        public override ProblemCollection Check(Member member)
        {
            Method method = member as Method;
            if (method == null)
                return null;

            var callees = new List<Method>(Callees.CalleesFor((Method)member));
            foreach (var callee in callees)
            {
                if (callee.DeclaringType.FullName.Trim().ToUpper().Equals("SYSTEM.GC"))
                {
                    Problems.Add(new Problem(GetResolution()));
                }
            }

            return base.Problems;
        }
        
    }
}


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.FxCop.Sdk;

namespace MyCustomRules
{
    public sealed class Callees
    {
        public static IList<Method> CalleesFor(Method caller)
        {
            if (caller == null)
                throw new ArgumentNullException("caller");
            CalleeVisitor cv = new CalleeVisitor();
            cv.VisitStatements(caller.Body.Statements);
            return cv.Callees;
        }
        private class CalleeVisitor : BinaryReadOnlyVisitor
        {
            private List<Method> m_Callees = new List<Method>();
            private Dictionary<int, int> m_CalleeIds = new Dictionary<int, int>();
            public IList<Method> Callees
            {
                get
                {
                    return m_Callees;
                }
            }
            public override void VisitMethodCall(MethodCall call)
            {
                base.VisitMethodCall(call);
                MemberBinding mb = call.Callee as MemberBinding;
                if (mb == null)
                    return;
                Method methd = mb.BoundMember as Method;
                if (methd == null)
                    return;
                int key = methd.UniqueKey;
                if (!m_CalleeIds.ContainsKey(key))
                {
                    m_Callees.Add(methd);
                    m_CalleeIds.Add(key, key);
                }
            }
        }
        // Prevent instantiation of this class; all members are static.
        private Callees()
        {
        }
    }
}

Custom FxCop Rule - Do not use GOTO Statements

This custom FxCop rule enforces the idea of not using VB Goto statements in your code - never a good programming practice.

To determine if we have GOTOs in the MSIL (intermediate language), we need to look at theOpCodes. This can be done with any dissasembler. The opcode for a GOTO is an unconditional branch. Looking at the IL you will see br.s instructions followed by an address. Many source code statements can yield unconditional branch IL instructions, but this doesn't mean the statement is a GOTO. We need to see if we have any unconditional branches to locations where those locations do nothing, e.g. contain the opcode of NOP.

C# code for the custom FxCop rule:

using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
using Microsoft.Cci;
using Microsoft.FxCop.Sdk;
using Microsoft.FxCop.Sdk.Introspection;

public class DoNotUseGotoStatements : BaseMigrationIntrospectionRule
{
    public DoNotUseGotoStatements() : base("DoNotUseGotoStatements")
    {
    }

    public override ProblemCollection Check(Member member)
    {
        // Method is a subtype of Member.
        Method method = member as Method;

        if (method == null)
            return base.Check(member);

        //
        // Number of instructions in the method Method's name.
        //
        InstructionList ops = method.Instructions;
        string methodName = method.Name.Name.ToUpper();

        //
        // If the method doesn't have any opcodes, no analysis is needed.
        //
        if (ops.Length.Equals(0))
            return base.Check(member);

        //
        // Dont bother with default constructors.
        // Default constructor does nothing except call the constructor 
        // in its base class. It is usually inserted into an assembly by 
        // the compiler.
        //
        if (methodName == ".CTOR" && IsDefaultCtor(method))
              return base.Check(member);
        int numberGotos = 0;

        //
        // Count the number of branches that branch to locations 
        // containing the NOP opcode.
        //
        if (isGotoUsed(method, out numberGotos))
        {
            base.Problems.Add(new Problem(GetResolution(
                method.FullName, numberGotos.ToString()), method));
        }
        return base.Problems;
    }
        
    //
    // See if the method is a default constructor - constructor that does nothing 
    // except call the constructor in its base class, usually inserted into your 
    // assemblies by the compiler.
    //
    private bool IsDefaultCtor(Method method)
    {
        InstructionList ops = method.Instructions;

        if (ops.Length.Equals(4))
        {
            //
            // Filter out default ctor generated by the compiler.
            //
            LocalList localList = ops[0].Value as LocalList;

            if (localList != null && localList.Length != 0) return false;
            if (ops[1].OpCode != OpCode.Ldarg_0) return false;
            if (ops[2].OpCode != OpCode.Call) return false;

            InstanceInitializer init = ops[2].Value as InstanceInitializer;

            if (init == null) return false;
            if (ops[3].OpCode != OpCode.Ret) return false;

            return true;
        }
        return false;
    }

    //
    // Checks if the method contains Goto statements.
    //
    private bool isGotoUsed(Method method, out int numberGotos)
    {
        InstructionList ops = method.Instructions;
        ArrayList targetAddresses = new ArrayList();
        ArrayList nopAddresses = new ArrayList();
        numberGotos = 0;

        //
        // Save a list of all unconditional Break opcodes and 
        // their target offsets.
        //
        for (int i = 0; i < ops.Length; i++)
        {
            switch (ops[i].OpCode)
            {
                case OpCode.Br_S:
                    targetAddresses.Add(ops[i].Value.ToString());
                    break;
                case OpCode.Nop:
                    nopAddresses.Add(ops[i].Offset.ToString());
                    break;
                default:
                    break;
            }
        }
        
        //
        // If any Break has a target that is a NOP, it's probably a Goto statement.
        //
        if (targetAddresses.Count > 0 && nopAddresses.Count > 0)
        {
            for (int i = 0; i < targetAddresses.Count; i++)
            {
                if (nopAddresses.Contains(targetAddresses[i]))
                {
                    numberGotos += 1;
                }
            }
        }
        return (numberGotos > 0);
    }
}
 

Rule definition in the XML rules file:

<Rule TypeName="DoNotUseGotoStatements" 
        Category="VBMigration" CheckId="AA1002">
    <Name>
        Do not use 'Goto' statements
    </Name>
    <Description>
        'Goto' statements should be avoided completely in .NET
    </Description>
    <Url>
        http://www.thescarms.com/
    </Url>
    <Resolution>
        Do not use 'Goto' statements in .NET. Break out functionality into 
        separate functions. {1} Goto(s) used in '{0}'
    </Resolution>
    <MessageLevel Certainty="85">
        Warning</MessageLevel>
    <FixCategories>
        NonBreaking
    </FixCategories>
    <Owner />
<Rule>

Custom FxCop Rule - Do not use VB6 Conversion Functions

This custom FxCop rule looks to see that you have not used the older VB6 conversion functions like CStrCDateCInt etc. Instead, you should be using Convert.ToString(),Convert.ToDateTime()Convert.ToInt32(), ...

If you create a VB.NET sample program with the above conversion calls, when you build the code and examine the MSIL, you will see calls to:

      [Microsoft.VisualBasic]Microsoft.VisualBasic.CompilerServices.Conversions

This rule looks for calls to the CompilerServices.Conversions namespace.

C# code for custom FxCop rule:

using System;
using Microsoft.Cci;
using Microsoft.Fugue;
using Microsoft.FxCop.Sdk;
using Microsoft.FxCop.Sdk.Introspection;

public class DoNotUseVB6Conversions : BaseMigrationControlFlowRule
{
    public DoNotUseVB6Conversions() : base("DoNotUseVB6Conversions")
    {
    }

    // Check for 'CompilerServices.Conversions' in the generated Intermediate Language.
    public override void VisitCall(Variable destination, Variable receiver, 
              Method callee, ExpressionList arguments, bool isVirtualCall, 
              IProgramContext programContext, IExecutionState stateBeforeInstruction, 
              IExecutionState stateAfterInstruction)
    {
        if (callee.DeclaringType.FullName.Trim().ToUpper() ==
             "MICROSOFT.VISUALBASIC.COMPILERSERVICES.CONVERSIONS")
        {
            base.Problems.Add(new Problem(GetResolution(), programContext));
            //
            // Can output IL statement w/ this parameter. 
            // Must add {0} placeholder to Rules.xml <Resolution> value.
            //
            //base.Problems.Add(new Problem(GetResolution(calleeName), programContext));
        }
        base.VisitCall(destination, receiver, callee, arguments, 
            isVirtualCall, programContext, stateBeforeInstruction, 
            stateAfterInstruction);
    }
}

Rule definition in the XML rules file:

<Rule TypeName="DoNotUseVB6Conversions" 
          Category="VBMigration" CheckId="AA1001">
    <Name>
        Do not use the VB6 conversion functions
    </Name>
    <Description>
         Do not use the VB6 data conversion functions  
         such as CStr, CInt, CDate, etc.
    </Description>
    <Url>
        http://www.thescarms.com/</Url>
    <Resolution>
        Do not use the VB6 conversion functions. Use the 'Convert.To...' 
        methods from the 'System.Convert' namespace instead.
    </Resolution>
    <MessageLevel Certainty="99">
        Warning</MessageLevel>
    <FixCategories>
        NonBreaking
    </FixCategories>
    <Owner />
<Rule>

Custom FxCop Rule - Do Use the VB Compatibility Namespace

The Microsoft VisualBasic Compatability namespace is used to invoke older VB6 functionality in VB.NET code. To use this namespace, you need to set a reference to it. So to test for this we can look at the assemblies referenced by our assembly.

C# code for custom FxCop rule:

using System;
using Microsoft.Cci;
using Microsoft.FxCop.Sdk;
using Microsoft.FxCop.Sdk.Introspection;

public class DoNotUseVBCompatibilityNamespace : BaseMigrationIntrospectionRule
{
    public DoNotUseVBCompatibilityNamespace()
        : base("DoNotUseVBCompatibilityNamespace")
    {
    }

    public override ProblemCollection Check(Module module)
    {
        if (module == null)
            return base.Check(module);

        AssemblyReferenceList refList = module.AssemblyReferences;
        foreach (AssemblyReference reference in refList)
        {
            if (reference.Name.ToUpper().Contains("MICROSOFT.VISUALBASIC.COMPATIBILITY"))
                base.Problems.Add(new Problem(GetResolution(module.Name), module));
        }

        return base.Problems;
    }
}

Rule definition in the XML rules file:

<Rule TypeName="DoNotUseVBCompatibilityNamespace" 
          Category="VBMigration" CheckId="AA1001">
    <Name>
        Do not use 'Microsoft.VisualBasic.Compatibility' namespace
    </Name>
    <Description>
        Avoid dependency on the Microsoft.VisualBasic.Compatibility namespace
    </Description>
    <Url>
        http://www.thescarms.com/
    </Url>
    <Resolution>
        '{0}' has imported the namespace of 'Microsoft.VisualBasic.Compatibility' or 
        'Microsoft.VisualBasic.Compatibility.Data'. Replace the code that references this 
        namespace with the .NET equivalent
    </Resolution>
    <MessageLevel Certainty="99">
        Warning
    </MessageLevel>
    <FixCategories>
        NonBreaking
    </FixCategories>
    <Owner />
<Rule>

Custom FxCop Rule - Do not use MSFlexGrid Interop

All of the older VB6 and even VB5 ActiveX controls can be used in VB.NET. To use these older COM controls, .NET uses the interoperability layer and creates an interop dll. If you add an older control, such as the MS FlexGrid in this example, to a VB.NET application and compile it, you will see an Interop.MSFlexGrid.dll in the bin folder. To spot a reference to an older component in FxCop, look at the list of referenced assemblies.

C# code for custom FxCop rule:

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Cci;
using Microsoft.FxCop.Sdk;
using Microsoft.FxCop.Sdk.Introspection;

public class DoNotUseMSFlexGridInterop : BaseMigrationIntrospectionRule
{
    public DoNotUseMSFlexGridInterop()
        : base("DoNotUseMSFlexGridInterop")
    {
    }
    public override ProblemCollection Check(Module module)
    {
        if (module == null)
            return base.Check(module);

        AssemblyReferenceList asmRefList = module.AssemblyReferences;
        foreach (AssemblyReference asmReference in asmRefList)
        {
            if (asmReference.Name.ToUpper().Contains("MSFLEXGRIDLIB"))
            {
                base.Problems.Add(new Problem(GetResolution(module.Name), module));
            }
        }

        return base.Problems;
    }
}

Rule definition in the XML rules file:

<Rule TypeName="DoNotUseMSFlexGridInterop" 
          Category="VBMigration" CheckId="AA1001">
    <Name>
        Do not use 'Interop.MSFlexGridLib' namespace
    </Name>
    <Description>
        Avoid dependency on the COM Interop for the older MSFlexGrid
    </Description>
    <Url>
        http://www.thescarms.com/
    </Url>
    <Resolution>
        '{0}' has imported the namespace 'Interop.MSFlexGridLib'. 
         Replace the Microsoft FlexGrid 6.0 with equivalent .NET control.
    </Resolution>
    <MessageLevel Certainty="99">
        Warning
    </MessageLevel>
    <FixCategories>
        NonBreaking
    </FixCategories>
    <Owner />
<Rule>

Custom FxCop Rule - Remove Empty Methods

This FxCop rule checks for empty methods and recommends removing them or commenting them out. An empty method can be a Sub, Function or Property in Visual Basic or any type of method in C#, with either no code or where all its code is commented out.

To find such methods, we need to examine the MSIL and look for a specific sequence of Opcode instructions.

C# code for custom FxCop rule:

//
// Description: 
//  Enforces the rule that empty void methods (Subs in VB), non-void
//  methods (Functions in VB), and Set properties should be commented 
//  out or removed.
//
//  Example: The following VB / C# methods will be caught:
//
//    <Public | Private...> Sub mySub(<optional parameter list>)
//      ' No code statements or all statements commented out.
//    End Sub
//
//    <public | private...> void mySub(<optional parameters list>) 
//    {
//      // No code statements or all statements commented out.
//    }
//
//    <Public | Private...> Function myFunc(<optional parameter list>) as <type>
//      ' No code statements, no Return, or all statements commented out.
//    End Sub
//
//    <public | private...> <type> myFunc(<optional parameters list>) 
//    {
//      // No code statements, no return, or all statements commented out.
//    }
//
//    NOTE: if a return statement is present, the method is valid even if
//          the return statement is the only valid line of code.    
//
//
//   <Public | Private...> <WriteOnly> Property myProp(ByVal someParm As <type>) As <type>
//      Set(ByVal value As <type>)
//        ' No code statements or all statements commented out.
//      End Set
//   End Property
//
//   <Public | Private...> Property myProp(ByVal someParm As <type>) As <type>
//        Get
//            Return <type>
//        End Get
//        ' Not needed
//        Set(ByVal value As <type>)
//        ' No code statements or all statements commented out.
//        End Set
//   End Property
//
//   <public | private...> <type> myProp 
//   {
//      get { return <type>; }
//      set { "No code statements or all statements commented out" }
//   }
//
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Cci;
using Microsoft.FxCop.Sdk;
using Microsoft.FxCop.Sdk.Introspection;

public class RemoveEmptyMethods : BaseMigrationIntrospectionRule
{
    public RemoveEmptyMethods() : base("RemoveEmptyMethods")
    {
    }

    public override ProblemCollection Check(Member member)
    {
       // Method is a subtype of Member.
       Method method = member as Method;

       if (method == null)
           return base.Check(member);

       //
       // Number of instructions in the method. Method's name.
       //
       InstructionList ops = method.Instructions;
       string methodName = method.Name.Name.ToUpper();

       //
       // If the method doesn't have any opcodes, no analysis is needed.
       //
       if (ops.Length.Equals(0)) 
           return base.Check(member); 

       //
       // Dont bother with default constructors. 
       // Default constructor does nothing except  call the constructor
       // in  its base class. It is usually inserted into an assembly by
       // the compiler.
       //
       if (methodName == ".CTOR" && IsDefaultCtor(method))
          return base.Check(member);
        
       //if (isEmptyFunctionOrGetProperty(method)) 
       //   return base.Check(member); 
       
       //
       // Check for empty methods (Subs), functions, properties. 
       //
       if (isEmptyVoidMethodOrSetProperty(method) || isEmptyNonVoidMethod(method) ) 
       { 
          // For testing purposes, uncomment to print out opcodes.
          //string opcodes == ""; 
          //for (int i = 0;  i <  ops.Length; i++)
          //{
          //    opcodes += ops[i].OpCode.ToString();
          //}
          //base.Problems.Add(new Problem(GetResolution(
          //      method.Name.FullName, 
          //      method.FullName + " ops:" + ops.Length.ToString() + " opcodes: " + opcodes), 
          //      method));
          base.Problems.Add(new Problem(GetResolution(method.FullName), method));
       }
       return base.Problems;
    }
    
    //
    // See if the method is a default constructor - constructor that does nothing 
    // except call the constructor in its base class, usually inserted into your 
    // assemblies by the compiler.
    //
    private bool IsDefaultCtor(Method method)
    {
        InstructionList ops = method.Instructions;

        if (ops.Length.Equals(4))
        {
            //
            // Filter out default ctor generated by the compiler.
            //
            LocalList localList = ops[0].Value as LocalList;

            if (localList != null && localList.Length != 0) return false;
            if (ops[1].OpCode != OpCode.Ldarg_0) return false;
            if (ops[2].OpCode != OpCode.Call) return false;

            InstanceInitializer init = ops[2].Value as InstanceInitializer;

            if (init == null) return false;
            if (ops[3].OpCode != OpCode.Ret) return false;

            return true;
        }
        return false;
    }


    //
    // Checks if the method is an method or 'set' property. 
    // See examples in header comment.
    //
    private bool isEmptyVoidMethodOrSetProperty(Method method)
    {
        InstructionList ops = method.Instructions;

        if (!ops.Length.Equals(4))
            return false;

        if ((ops[0].OpCode == OpCode._Locals) &&
                (ops[1].OpCode == OpCode.Nop) &&
                (ops[2].OpCode == OpCode.Nop) &&
                (ops[3].OpCode == OpCode.Ret))
        {
            return true;
        }
        return false;
    }

    //
    // Checks if the method is an empty function. See examples
    // in header comment.
    //
    private bool isEmptyNonVoidMethod(Method method)
    {
        InstructionList ops = method.Instructions;

        if (!ops.Length.Equals(4))
            return false;

        if ((ops[0].OpCode == OpCode._Locals) &&
            (ops[1].OpCode == OpCode.Nop) &&
            (ops[2].OpCode == OpCode.Ldloc_0) &&
            (ops[3].OpCode == OpCode.Ret))
        {
            return true;
        }
        return false;
    }


    //
    // Checks if the method is an empty function. See examples
    // in header comment.
    //
    private bool isEmptyFunctionOrGetProperty(Method method)
    {
        InstructionList ops = method.Instructions;

        if (!ops.Length.Equals(7))
            return false;

        //
        // Don't care about ops[2].OpCode which will be some 
        // type of load instruction, e.g. OpCode.ldxxx.
        //
        // Opcode 5 returns a value so there is no way of knowing
        // if this is a valid function or not.
        //
        if ((ops[0].OpCode == OpCode._Locals) &&
            (ops[1].OpCode == OpCode.Nop) &&
            (ops[3].OpCode == OpCode.Stloc_0) &&
            (ops[4].OpCode == OpCode.Br_S) &&
            (ops[5].OpCode == OpCode.Ldloc_0) &&
            (ops[6].OpCode == OpCode.Ret))
        {
            return true;
        }
        return false;
    }
}

Rule definition in the XML rules file:

<Rule TypeName="RemoveEmptyMethods" 
          Category="VBMigration" CheckId="AA1001">
    <Name>
        Remove all empty methods and properties
    </Name>
    <Description>
        Empty methods and properties should be commented or removed.
    </Description>
    <Url>
        http://www.thescarms.com/
    </Url>
    <Resolution>
        All empty methods, functions, and 'set' properties should be commented 
        out or removed. Empty method: '{0}'.
    </Resolution>
    <MessageLevel Certainty="99">
        Warning
    </MessageLevel>
    <FixCategories>
        NonBreaking
    </FixCategories>
    <Owner />
<Rule>

Custom FxCop Rule - Verify use of VB's IsNothing or IsDBNull

Often times programmers confuse Visual Basic's IsDBNull and IsNothing statements which are not the same. IsDBNull indicates a variable evaluates to the System.DBNull type representing missing or nonexistent data. IsNothing indicates a variable has not yet been initialized. So, this FxCop rule checks for the usage of these statements and issues a warning to verify their usage.

Create a small VB.NET sample which includes these two statements then build the application. Look at the .exe with IL DASM to view its intermediate language and you will see the following calls. Knowing this, it is an easy matter to create a custom FxCop rule to determine if these statements are used.

     Microsoft.VisualBasic.Information::IsNothing
     Microsoft.VisualBasic.Information::IsDBNull

C# code for custom FxCop rule:

using System;
using Microsoft.Cci;
using Microsoft.Fugue;
using Microsoft.FxCop.Sdk;
using Microsoft.FxCop.Sdk.Introspection;

public class VerifyUseOfIsNothingIsDBNull : BaseMigrationControlFlowRule
{
    public VerifyUseOfIsNothingIsDBNull() : base("VerifyUseOfIsNothingIsDBNull")
    {
    }

    //
    // Check for 'Information::IsNothing' or 'Information::IsDBNull'
    // in the generated Intermediate Language.
    //
    public override void VisitCall(Variable destination, Variable receiver, 
           Method callee, ExpressionList arguments, bool isVirtualCall, 
           IProgramContext programContext, IExecutionState stateBeforeInstruction, 
           IExecutionState stateAfterInstruction)
    {
        string calleeName = callee.Name.Name.Trim().ToUpper();

        if (callee.DeclaringType.FullName.Trim().ToUpper() == 
            "MICROSOFT.VISUALBASIC.INFORMATION"
            && (calleeName.Equals("ISNOTHING") || calleeName.Equals("ISDBNULL")))
        {
            base.Problems.Add(new Problem(GetResolution(callee.Name.Name), programContext));
        }
        base.VisitCall(destination, receiver, callee, arguments, isVirtualCall, 
             programContext, stateBeforeInstruction, stateAfterInstruction);
    }
}

Rule definition in the XML rules file:

<Rule TypeName="VerifyUseOfIsNothingIsDBNull" 
          Category="VBMigration" CheckId="AA1001">
    <Name>
        Verify usage of IsDBNull and IsNothing methods
    </Name>
    <Description>
        IsDBNull and IsNothing methods are not equivalent
    </Description>
    <Url>
        http://www.thescarms.com/
    </Url>
    <Resolution>
        Verify usage of '{0}'. IsDBNull and IsNothing are not equivalent. 
        IsDBNull indicates a variable evaluates to the System.DBNull type 
        representing missing or nonexistent data. IsNothing indicates a 
        variable has not yet been initialized
    </Resolution>
    <MessageLevel Certainty="99">
        Warning
    </MessageLevel>
    <FixCategories>
        NonBreaking
    </FixCategories>
    <Owner />
<Rule>

Custom FxCop Rule - Do not use Wildcards in Assembly Version Number

Here is an FxCop rule which prevents you from using wildcards in an assembly's version number. A better programming practice it to explicitly set the version number in theassemblyinfo file.

While there is no certain way to know that a wildcard was used, we can infer it by looking at the length of the individual components of the version number. If any of them are five characters longer, it is a safe bet that they were set by .NET since most programmers would typically use a shorter number.

C# code for custom FxCop rule:

using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Cci;
using Microsoft.FxCop.Sdk;
using Microsoft.FxCop.Sdk.Introspection;

public class DoNotUseWildcardsInVersion : BaseMigrationIntrospectionRule
{
    public DoNotUseWildcardsInVersion() : base("DoNotUseWildcardsInVersion")
    {
    }
    public override ProblemCollection Check(Module module)
    {
        AssemblyNode a = module.ContainingAssembly;

        if ((a == null) || (a.Version == null))
            return base.Check(module);

        string major = a.Version.Major.ToString();
        string minor = a.Version.Minor.ToString();
        string build = a.Version.Build.ToString();
        string revision = a.Version.Revision.ToString();

        if (major.Trim().Length.Equals(5))
        {
            base.Problems.Add(new Problem(GetResolution(module.Name), module));
        }

        if (minor.Trim().Length.Equals(5))
        {
            base.Problems.Add(new Problem(GetResolution(module.Name), module));
        }
        if (build.Trim().Length.Equals(5))
        {
            base.Problems.Add(new Problem(GetResolution(module.Name), module));
        }

        if (revision.Trim().Length.Equals(5))
        {
            base.Problems.Add(new Problem(GetResolution(module.Name), module));
        }

        return base.Problems;
    }
}

Rule definition in the XML rules file:

<Rule TypeName="DoNotUseWildcardsInVersion" 
          Category="VBMigration" CheckId="AA1001">
    <Name>
        Do not use wildcards in an assembly's version number
    </Name>
    <Description>
        Wildcards have been used in an assembly's version number..
    </Description>
    <Url>
        http://www.thescarms.com/
    </Url>
    <Resolution>
        Do not use wildcards in an assembly's version number. You should
        explicitly set all components of an assembly's version number.
    </Resolution>
    <MessageLevel Certainty="80">
        Warning
    </MessageLevel>
    <FixCategories>
        NonBreaking
    </FixCategories>
    <Owner />
<Rule>

Custom FxCop Rule - Do not store passwords in variables in code

It is not secure to store security credentials such as user Ids or passwords in code, especially in constants or literals where their values can be obtained by looking at an assembly's intermediate language. There is no definitive way to know if security credentials are stored in source code but we can infer this by looking at the names of the variables and constants used.

If you create a small VB or C# program with a few local or module level variables or literals, build the code, then look at the IL, you can plainly see the names of the variables. The following FxCop rule does just this by calling the Check method for each method and looking at the method's LocalList of variables. If any of them contain the word 'user' or 'password' in their name, we can infer they hold sensitive data. However, this is no guarantee, that's way this rule's certainty level is set lower.

C# code for custom FxCop rule:

//
// Description: 
//  Enforces the rule of not storing database credentials in module
//  level or local fields. This rule looks for variables and constants  
//  that contain the word "user" or "password".
//
//  Because the rule doesn't discern how these fields are used, the 
//  rule's certainty is set to a lower value.
//
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Cci;
using Microsoft.FxCop.Sdk;
using Microsoft.FxCop.Sdk.Introspection;

public class DoNotKeepDBCredentialsInVariables : BaseMigrationIntrospectionRule
{
    public DoNotKeepDBCredentialsInVariables() : base("DoNotKeepDBCredentialsInVariables")
    {
    }

    public override ProblemCollection Check(Member member)
    {
        // Check Member Variables.
        if (ContainsUserOrPasswordInName(member.Name.Name))
        {
            // Found an offending member variable.
            base.Problems.Add(new Problem(base.GetResolution(member.Name.Name), 
                 member.Name.Name));
        }

        // Look at local variables.
        Method method = member as Method;
        if (method == null)
            return null;

        LocalList list = null;
        if (method.Instructions.Length > 0)
        {
            list = method.Instructions[0].Value as LocalList;
        }

        if (list != null)
        {
            for (int i = 0; i < list.Length; i++)
            {
                Local local = list[i];
                if (ContainsUserOrPasswordInName(local.Name.Name))
                {
                    // Found an offending local variable.
                    base.Problems.Add(new Problem(base.GetResolution(local.Name.Name), 
                       local.Name.Name));
                }
            }
        }
        return base.Problems;
    }

    private bool ContainsUserOrPasswordInName(string variableName)
    {
        variableName = variableName.ToUpper();

        if (variableName.Contains("USER") || variableName.Contains("PASSWORD"))
        {
            return true;
        }
        else
        {
            return false;
        }
    }
}

Rule definition in the XML rules file:

<Rule TypeName="DoNotKeepDBCredentialsInVariables" 
          Category="VBMigration" CheckId="AA1001">
    <Name>
        Do not store security credentials in variables
    </Name>
    <Description>
        Do not store security credentials in module level or local variables
    </Description>
    <Url>
        http://www.thescarms.com/
    </Url>
    <Resolution>
        Do not store security credentials in module level or local variables. Variables with the 
        word 'user' or 'password' in their name have been found. It is not secure to store
        credential information in variables.
    </Resolution>
    <MessageLevel Certainty="75"> 
        Warning
    </MessageLevel>
    <FixCategories>
        NonBreaking
    </FixCategories>
    <Owner />
<Rule>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值