Running CAT.NET as a Custom MSBuild Task

Syed Aslam Basha here. I am a tester on the Information Security Tools team and responsible for testing CAT.NET. You can run CAT.NET as;

  • A Visual studio add-in
  • From Command prompt
  • As an FXCop rule
  • Lastly, integrated into VSTF Team build as an MSBuild custom task

Here am going to describe using CAT.NET as an MSBUILD custom task.

First you will need to create an MSBUILD project as shown below;

CATNET.csproj

 <Project xmlns="https://schemas.microsoft.com/developer/msbuild/2003">
 <PropertyGroup>
  <CATNETPath>C:\Program Files\Microsoft\CAT.NET\CATNetCmd.exe</CATNETPath>
 </PropertyGroup>
<PropertyGroup>
  <ReportLocation>D:\CATNET\report.xml</ReportLocation>
</PropertyGroup>
 <ItemGroup>
  <ScanFiles Include="D:\CATNET\WebSite2_deploy.dll" />
 </ItemGroup>
 <UsingTask AssemblyFile="D:\CATNET\CATNETCustomTask.dll" TaskName="CATNETScan"/>
 <Target Name="Default">
    <Message Text=" "/>
    <Message Text=" "/>
    <Message Text="Testing CAT.NET Scan"/>
    <Message Text="------------------"/>
    <Message Text=" "/>
<CATNETScan ScannedFiles="@(ScanFiles)" CATNETPath="$(CATNETPath)" ReportLocation="$(ReportLocation)"/>
  </Target>
</Project>

Where CATNETPath is CATNETCmd.exe path, ReportLocation is path to report along with the filename, scanfiles is the file/s you want to scan using CAT.NET, UsingTaskAssemblyFile is the assembly(DLL) from the below code.

Create a class library solution “CATNETCustomTask.sln”, add a class and copy paste the below code and build it to generate “CATNETCustomTask.dll”.

(Add references, Microsoft.Build.Framework, Microsoft.Build.Tasks and Microsoft.Build.Utilities.v.3.5)

 using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Build.Tasks;
using Microsoft.Build.Utilities;
using Microsoft.Build.Framework;
using System.IO;
using System.Reflection;
using System.Diagnostics;
using System.Xml;

namespace CustomTask
{
    public class CATNETScan : ToolTask
    {

        private string _catnetPath;

        // The [Required] attribute indicates a required property.
        // If a project file invokes this task without passing a value
        // to this property, the build will fail immediately.
        [Required]
        public string CATNETPath
        {
            get { return _catnetPath; }
            set { _catnetPath = value; }
        }

        private string _scannedFiles;
        [Required]
        public string ScannedFiles
        {
            get { return _scannedFiles; }
            set { _scannedFiles = value; }
        }

        private string _reportLocation;
        [Required]
        public string ReportLocation
        {
            get { return _reportLocation; }
            set { _reportLocation = value; }
        }


        public override bool Execute()
        {
            try
            {
                int numIssues = 0;
                bool successStatus = base.Execute();
                if (!successStatus)
                {
                    return successStatus;
                }
                using (FileStream xmlFileStream = new FileStream(this.ReportLocation, FileMode.Open))
                {
                    XmlTextReader catnetReport = new XmlTextReader(xmlFileStream);
                    while (catnetReport.Read())
                    {
                        if (catnetReport.LocalName == "Issue" && catnetReport.NodeType == XmlNodeType.Element)
                        {
                            successStatus = false;
                            numIssues++;
                        }
                    }
                    if (!successStatus)
                    {
                        Log.LogError("Found {0} CATNET warnings, see {1} for more details", numIssues, xmlFileStream.Name);
                    }
                    return successStatus;
                }
            }
            catch (Exception e)
            {
                Log.LogErrorFromException(e);
                return false;
            }
        }

        protected override string GenerateFullPathToTool()
        {
            return CATNETPath;
        }

        protected override string GenerateCommandLineCommands()
        {
            CommandLineBuilder builder = new CommandLineBuilder();

            if (ScannedFiles != null)
            {
                builder.AppendSwitch("/file:" + ScannedFiles);
            }
            else
            {
                Log.LogWarning("CATNET Task requires TargetAssembly");
            }

            if (ReportLocation != null)
            {
                builder.AppendSwitch("/report:" + ReportLocation);
            }
            return builder.ToString();
        }

        protected override string ToolName
        {
            get { return "CATNETcmd.exe"; }
        }
    }
}

At command prompt run msbuild as

D:\CATNET>msbuild catnet.csproj /filelogger

This generates report.xml, msbuild.log and report.html file

CATNET folder has CATNETCustomTask.dll, WebSite2_deploy.dll and catnet.csproj

Report.html shows the list of security flaws identified by the CAT.NET.

You can add more property groups such as “Rule”,”config” etc.

 <PropertyGroup>
   <ConfigPath>C:\Program Files\Microsoft\CAT.NET\Config\config.xml</ConfigPath>
</PropertyGroup>
<PropertyGroup>
   <RulePath>C:\Program Files\Microsoft\CAT.NET\Rules\</RulePath>
</PropertyGroup>

The CATNETScan will change;

 <CATNETScan ScannedFiles="@(ScanFiles)" CATNETPath="$(CATNETPath)" RulePath="$(RulePath)" ConfigPath="$(ConfigPath)" ReportLocation="$(ReportLocation)"/>

The code will also change accordingly;

 private string _configPath;
        [Required]
        public string ConfigPath
        {
            get { return _configPath; }
            set { _configPath = value; }
        }

        private string _rulePath;
        [Required]
        public string RulePath
        {
            get { return _rulePath; }
            set { _rulePath = value; }
        }

       protected override string GenerateCommandLineCommands()
        {
            CommandLineBuilder builder = new CommandLineBuilder();

            if (ScannedFiles != null)
            {
                builder.AppendSwitch("/file:" + ScannedFiles);
            }
            else
            {
                Log.LogWarning("CATNET Task requires TargetAssembly");
            }
            
            if (ConfigPath!= null)
            {
                builder.AppendSwitch("/config:" + ConfigPath);
            }

            if (RulePath != null)
            {
                builder.AppendSwitch("/rules:" + RulePath); 
            }

            if (ReportLocation != null)
            {
                builder.AppendSwitch("/report:" + ReportLocation);
            }
            return builder.ToString();
        }

That’s it!