Exemple #1
0
        /// <summary>
        /// Constructs a sample SARIF log and writes it to the specified location.
        /// </summary>
        /// <param name="options">Create verb options.</param>
        /// <returns>Exit code</returns>
        static int CreateSarifLogFile(CreateOptions options)
        {
            // We'll use this source file for several defect results -- the
            // SampleSourceFiles folder should be at the same level as the project folder
            // Because this file can actually be accessed by this app, its
            // content will be embedded in the log file
            var fileLocation = new FileLocation {
                Uri = new Uri($"file://{AppDomain.CurrentDomain.BaseDirectory}/../../../../SampleSourceFiles/AnalysisSample.cs")
            };

            // Create a list of rules that will be enforced during your analysis
            #region Rules list
            var rules = new List <Rule>()
            {
                new Rule
                {
                    Id   = "CA1819",
                    Name = new Message {
                        Text = "Properties should not return arrays"
                    },
                    FullDescription = new Message {
                        Text = "Arrays returned by properties are not write-protected, even if the property is read-only. To keep the array tamper-proof, the property must return a copy of the array. Typically, users will not understand the adverse performance implications of calling such a property."
                    }
                },
                new Rule
                {
                    Id   = "CA1820",
                    Name = new Message {
                        Text = "Test for empty strings using string length"
                    },
                    FullDescription = new Message {
                        Text = "Comparing strings by using the String.Length property or the String.IsNullOrEmpty method is significantly faster than using Equals."
                    }
                },
                new Rule
                {
                    Id   = "CA2105",
                    Name = new Message {
                        Text = "Array fields should not be read only"
                    },
                    FullDescription = new Message {
                        Text = "When you apply the read-only (ReadOnly in Visual Basic) modifier to a field that contains an array, the field cannot be changed to reference a different array. However, the elements of the array stored in a read-only field can be changed."
                    }
                },
                new Rule
                {
                    Id   = "CA2215",
                    Name = new Message {
                        Text = "Dispose methods should call base class dispose"
                    },
                    FullDescription = new Message {
                        Text = "If a type inherits from a disposable type, it must call the Dispose method of the base type from its own Dispose method."
                    }
                },
                //new Rule
                //{
                //    Id ="CA1816",
                //    Name = new Message { Text = "Call GC.SuppressFinalize correctly" },
                //    FullDescription = new Message { Text = "A method that is an implementation of Dispose does not call GC.SuppressFinalize, or a method that is not an implementation of Dispose calls GC.SuppressFinalize, or a method calls GC.SuppressFinalize and passes something other than this (Me in Visual Basic)." }
                //},
                //new Rule
                //{
                //    Id ="CA2006",
                //    Name = new Message { Text = "Use SafeHandle to encapsulate native resources" },
                //    FullDescription = new Message { Text = "Use of IntPtr in managed code might indicate a potential security and reliability problem. All uses of IntPtr must be reviewed to determine whether use of a SafeHandle, or similar technology, is required in its place." }
                //}
            };
            #endregion

            // Regions will be calculated by your analysis process
            #region Regions
            var regions = new List <Region>()
            {
                new Region // CA1819
                {
                    StartLine   = 16,
                    StartColumn = 19,
                    EndLine     = 16,
                    EndColumn   = 38,
                    Offset      = 331, // Offset should account for the BOM, if present in source file
                    Length      = 19
                },
                new Region // CA1820
                {
                    StartLine   = 23,
                    StartColumn = 21,
                    EndLine     = 23,
                    EndColumn   = 44,
                    Offset      = 507, // Offset should account for the BOM, if present in source file
                    Length      = 23
                },
                new Region // CA2105
                {
                    StartLine   = 11,
                    StartColumn = 9,
                    EndLine     = 11,
                    EndColumn   = 50,
                    Offset      = 198, // Offset should account for the BOM, if present in source file
                    Length      = 41
                },
                new Region // CA2215
                {
                    StartLine   = 32,
                    StartColumn = 9,
                    EndLine     = 32,
                    EndColumn   = 30,
                    Offset      = 646, // Offset should account for the BOM, if present in source file
                    Length      = 21
                }
            };
            #endregion

            // Sets of fixes corresponding to each rule
            // Multiple fixes can be provided for the user to choose from
            #region Fixes
            IList <Fix[]> fixes = new List <Fix[]>
            {
                null, // no suggested fixes for CA1819

                new[]
                {
                    new Fix // CA1820
                    {
                        Description = new Message {
                            Text = "Replace empty string test with test for zero length."
                        },
                        FileChanges = new[]
                        {
                            new FileChange
                            {
                                FileLocation = fileLocation,
                                Replacements = new[]
                                {
                                    new Replacement
                                    {
                                        DeletedLength = 6,
                                        InsertedBytes = ".Length == 0",
                                        Offset        = 524
                                    }
                                }
                            }
                        },
                    }
                },

                null, // no suggested fix for CA2105

                new[]
                {
                    new Fix // CA2215
                    {
                        Description = new Message {
                            Text = "Call base.Dispose in the derived's class's Dispose method."
                        },
                        FileChanges = new[]
                        {
                            new FileChange
                            {
                                FileLocation = fileLocation,
                                Replacements = new[]
                                {
                                    new Replacement
                                    {
                                        DeletedLength = 0,
                                        InsertedBytes = @"\nbase.Dispose();",
                                        Offset        = 656
                                    }
                                }
                            }
                        }
                    }
                }
            };
            #endregion

            // The SarifLogger will write the JSON-formatted log to this StringBuilder
            var sb = new StringBuilder();

            using (var textWriter = new StringWriter(sb))
            {
                using (var sarifLogger = new SarifLogger(
                           textWriter,
                           analysisTargets: null,
                           loggingOptions: LoggingOptions.PersistTextFileContents | // <-- embed source file content directly in the log file -- great for portability of the log!
                           LoggingOptions.ComputeFileHashes |
                           LoggingOptions.PrettyPrint,                              // <-- use PrettyPrint to generate readable (multi-line, indented) JSON
                           prereleaseInfo: null,
                           invocationTokensToRedact: null,
                           invocationPropertiesToLog: null))
                {
                    // Create one result for each rule
                    for (int i = 0; i < rules.Count; i++)
                    {
                        Rule   rule   = rules[i];
                        Region region = regions[i];

                        var result = new Result()
                        {
                            RuleId         = rule.Id,
                            AnalysisTarget = new FileLocation {
                                Uri = new Uri(@"file://d:/src/module/foo.dll")
                            },                                                                                    // This is the file that was analyzed
                            Locations = new[]
                            {
                                new Location
                                {
                                    PhysicalLocation = new PhysicalLocation
                                    {
                                        FileLocation = fileLocation,
                                        Region       = region
                                    }
                                },
                            },
                            Fixes            = fixes[i],
                            RelatedLocations = new[]
                            {
                                new Location
                                {
                                    PhysicalLocation = new PhysicalLocation
                                    {
                                        FileLocation = new FileLocation
                                        {
                                            // Because this file doesn't exist, it will be included in the files list but will only have a path and MIME type
                                            // This is the behavior you'll see any time a file can't be located/accessed
                                            Uri = new Uri($"file://{AppDomain.CurrentDomain.BaseDirectory}/../../../SampleSourceFiles/SomeOtherSourceFile.cs"),
                                        },
                                        Region = new Region
                                        {
                                            StartLine   = 147,
                                            StartColumn = 19,
                                            EndLine     = 147,
                                            EndColumn   = 40,
                                            Offset      = 1245,
                                            Length      = 21
                                        }
                                    }
                                }
                            },
                            Stacks = new[]
                            {
                                new Stack
                                {
                                    Frames = new[]
                                    {
                                        new StackFrame
                                        {
                                            // The method that contains the defect
                                            Location = new Location
                                            {
                                                PhysicalLocation = new PhysicalLocation
                                                {
                                                    FileLocation = fileLocation,
                                                    Region       = new Region
                                                    {
                                                        StartLine = 212
                                                    }
                                                }
                                            }
                                        },
                                        new StackFrame
                                        {
                                            // The method that calls the one above, e.g. ComputeSomeValue()
                                            Location = new Location
                                            {
                                                PhysicalLocation = new PhysicalLocation
                                                {
                                                    FileLocation = fileLocation,
                                                    Region       = new Region
                                                    {
                                                        StartLine = 452 // Fake example
                                                    }
                                                }
                                            }
                                        },
                                        new StackFrame
                                        {
                                            // The method that calls the one above, e.g. Main()
                                            Location = new Location
                                            {
                                                PhysicalLocation = new PhysicalLocation
                                                {
                                                    FileLocation = fileLocation,
                                                    Region       = new Region
                                                    {
                                                        StartLine = 145
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        };

                        // Let's add a CodeFlow for the first defect (properties shouldn't return arrays)
                        // This flow shows where the array is declared, and where it is returned by a property
                        if (i == 0)
                        {
                            var codeFlow = new CodeFlow
                            {
                                // This is what a single-threaded result looks like
                                // TIP: use SarifUtilities.CreateSingleThreadedCodeFlow to reduce repetition
                                // Multi-threaded example coming soon!
                                ThreadFlows = new[]
                                {
                                    new ThreadFlow
                                    {
                                        Locations = new[]
                                        {
                                            new CodeFlowLocation
                                            {
                                                // This is the defect statement's location
                                                Location = new Location
                                                {
                                                    PhysicalLocation = new PhysicalLocation
                                                    {
                                                        FileLocation = fileLocation,
                                                        Region       = region
                                                    }
                                                },
                                                Step       = 1,
                                                Importance = CodeFlowLocationImportance.Essential
                                            },
                                            new CodeFlowLocation
                                            {
                                                // This is the declaration of the array
                                                Location = new Location
                                                {
                                                    PhysicalLocation = new PhysicalLocation
                                                    {
                                                        FileLocation = fileLocation,
                                                        Region       = new Region
                                                        {
                                                            StartLine = 12
                                                        }
                                                    }
                                                },
                                                NestingLevel = 1,
                                                Step         = 2,
                                                Importance   = CodeFlowLocationImportance.Important
                                            }
                                        }
                                    }
                                }
                            };
                            result.CodeFlows = new[] { codeFlow };
                        }

                        sarifLogger.Log(rule, result);
                    }
                }
            }

            File.WriteAllText(options.OutputFilePath, sb.ToString());
            return(0);
        }
Exemple #2
0
        /// <summary>
        /// Constructs a sample SARIF log and writes it to the specified location.
        /// </summary>
        /// <param name="options">Create verb options.</param>
        /// <returns>Exit code</returns>
        internal static int CreateSarifLogFile(CreateOptions options)
        {
            // We'll use this source file for several defect results -- the
            // SampleSourceFiles folder should be a child of the project folder,
            // two levels up from the folder that contains the EXE (e.g., bin\Debug).
            string scanRootDirectory = Path.GetFullPath(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"..\..\SampleSourceFiles\"));
            var    scanRootUri       = new Uri(scanRootDirectory, UriKind.Absolute);
            var    artifactLocation  = new ArtifactLocation
            {
                Uri       = new Uri("AnalysisSample.cs", UriKind.Relative),
                UriBaseId = RepoRootBaseId
            };

            // Create a list of rules that will be enforced during your analysis
            #region Rules list
            var rules = new List <ReportingDescriptor>()
            {
                new ReportingDescriptor
                {
                    Id              = "CA1819",
                    Name            = "Properties should not return arrays",
                    FullDescription = new MultiformatMessageString {
                        Text = "Arrays returned by properties are not write-protected, even if the property is read-only. To keep the array tamper-proof, the property must return a copy of the array. Typically, users will not understand the adverse performance implications of calling such a property."
                    },
                    MessageStrings = new Dictionary <string, MultiformatMessageString>
                    {
                        {
                            "Default",
                            new MultiformatMessageString
                            {
                                Text = "The property {0} returns an array."
                            }
                        }
                    },
                    HelpUri = new Uri("https://www.example.com/rules/CA1819")
                },
                new ReportingDescriptor
                {
                    Id              = "CA1820",
                    Name            = "Test for empty strings using string length",
                    FullDescription = new MultiformatMessageString {
                        Text = "Comparing strings by using the String.Length property or the String.IsNullOrEmpty method is significantly faster than using Equals."
                    },
                    MessageStrings = new Dictionary <string, MultiformatMessageString>
                    {
                        {
                            "Default",
                            new MultiformatMessageString
                            {
                                Text = "The test for an empty string is performed by a string comparison rather than by testing String.Length."
                            }
                        }
                    },
                    HelpUri = new Uri("https://www.example.com/rules/CA1820")
                },
                new ReportingDescriptor
                {
                    Id              = "CA2105",
                    Name            = "Array fields should not be read only",
                    FullDescription = new MultiformatMessageString {
                        Text = "When you apply the read-only (ReadOnly in Visual Basic) modifier to a field that contains an array, the field cannot be changed to reference a different array. However, the elements of the array stored in a read-only field can be changed."
                    },
                    MessageStrings = new Dictionary <string, MultiformatMessageString>
                    {
                        {
                            "Default",
                            new MultiformatMessageString
                            {
                                Text = "The array-valued field {0} is marked readonly."
                            }
                        }
                    },
                    HelpUri = new Uri("https://www.example.com/rules/CA2105")
                },
                new ReportingDescriptor
                {
                    Id              = "CA2215",
                    Name            = "Dispose methods should call base class dispose",
                    FullDescription = new MultiformatMessageString {
                        Text = "If a type inherits from a disposable type, it must call the Dispose method of the base type from its own Dispose method."
                    },
                    MessageStrings = new Dictionary <string, MultiformatMessageString>
                    {
                        {
                            "Default",
                            new MultiformatMessageString
                            {
                                Text = "The Dispose method does not call the base class Dispose method."
                            }
                        }
                    },
                    HelpUri = new Uri("https://www.example.com/rules/CA2215")
                }
            };
            #endregion

            // Regions will be calculated by your analysis process
            #region Regions
            var regions = new List <Region>()
            {
                new Region // CA1819
                {
                    StartLine   = 17,
                    StartColumn = 16,
                    EndColumn   = 32
                },
                new Region // CA1820
                {
                    StartLine   = 26,
                    StartColumn = 21,
                    EndColumn   = 44
                },
                new Region // CA2105
                {
                    StartLine   = 14,
                    StartColumn = 17,
                    EndColumn   = 25
                },
                new Region // CA2215
                {
                    StartLine   = 37,
                    StartColumn = 9,
                    EndColumn   = 9
                }
            };
            #endregion

            #region Message arguments
            string[][] messageArguments = new string[][]
            {
                new string[]
                {
                    "MyIntArray"
                },

                null,

                new string[]
                {
                    "_myStringArray"
                },

                null
            };
            #endregion

            // Sets of fixes corresponding to each rule
            // Multiple fixes can be provided for the user to choose from
            #region Fixes
            IList <Fix[]> fixes = new List <Fix[]>
            {
                null, // no suggested fixes for CA1819

                new[]
                {
                    new Fix // CA1820
                    {
                        Description = new Message {
                            Text = "Replace empty string test with test for zero length."
                        },
                        ArtifactChanges = new[]
                        {
                            new ArtifactChange
                            {
                                ArtifactLocation = artifactLocation,
                                Replacements     = new[]
                                {
                                    new Replacement
                                    {
                                        DeletedRegion = new Region
                                        {
                                            StartLine   = 26,
                                            StartColumn = 38,
                                            EndColumn   = 44
                                        },
                                        InsertedContent = new ArtifactContent
                                        {
                                            Text = ".Length == 0"
                                        }
                                    }
                                }
                            }
                        },
                    }
                },

                null, // no suggested fix for CA2105

                new[]
                {
                    new Fix // CA2215
                    {
                        Description = new Message {
                            Text = "Call base.Dispose in the derived's class's Dispose method."
                        },
                        ArtifactChanges = new[]
                        {
                            new ArtifactChange
                            {
                                ArtifactLocation = artifactLocation,
                                Replacements     = new[]
                                {
                                    new Replacement
                                    {
                                        DeletedRegion = new Region
                                        {
                                            StartLine   = 37,
                                            StartColumn = 1,
                                            EndColumn   = 1
                                        },
                                        InsertedContent = new ArtifactContent
                                        {
                                            Text = @"            base.Dispose();\n"
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            };
            #endregion

            string binRootDirectory = @"d:\src\module\";
            var    binRootUri       = new Uri(binRootDirectory, UriKind.Absolute);

            var run = new Run
            {
                OriginalUriBaseIds = new Dictionary <string, ArtifactLocation>
                {
                    [RepoRootBaseId] = new ArtifactLocation
                    {
                        Uri = scanRootUri
                    },
                    [BinRootBaseId] = new ArtifactLocation
                    {
                        Uri = binRootUri
                    }
                },
                VersionControlProvenance = new VersionControlDetails[]
                {
                    new VersionControlDetails
                    {
                        RepositoryUri = new Uri("https://github.com/microsoft/sarif-sdk"),
                        RevisionId    = "ee5a1ca8",
                        Branch        = "master",
                        MappedTo      = new ArtifactLocation
                        {
                            Uri       = new Uri(".", UriKind.Relative),
                            UriBaseId = RepoRootBaseId
                        }
                    }
                }
            };

            // The SarifLogger will write the JSON-formatted log to this StringBuilder
            var sb = new StringBuilder();

            using (var textWriter = new StringWriter(sb))
            {
                using (var sarifLogger = new SarifLogger(
                           textWriter,
                           logFilePersistenceOptions: LogFilePersistenceOptions.PrettyPrint, // Use PrettyPrint to generate readable (multi-line, indented) JSON
                           dataToInsert:
                           OptionallyEmittedData.TextFiles |                                 // Embed source file content directly in the log file -- great for portability of the log!
                           OptionallyEmittedData.Hashes |
                           OptionallyEmittedData.RegionSnippets,
                           tool: null,
                           run: run,
                           analysisTargets: null,
                           invocationTokensToRedact: null,
                           invocationPropertiesToLog: null,
                           defaultFileEncoding: null))
                {
                    // Create one result for each rule
                    for (int i = 0; i < rules.Count; i++)
                    {
                        ReportingDescriptor rule = rules[i];
                        Region region            = regions[i];

                        var result = new Result()
                        {
                            RuleId         = rule.Id,
                            AnalysisTarget = new ArtifactLocation
                            {
                                Uri       = new Uri("example.dll", UriKind.Relative), // This is the file that was analyzed
                                UriBaseId = BinRootBaseId
                            },
                            Message = new Message
                            {
                                Id        = "Default",
                                Arguments = messageArguments[i]
                            },
                            Locations = new[]
                            {
                                new Location
                                {
                                    PhysicalLocation = new PhysicalLocation
                                    {
                                        ArtifactLocation = artifactLocation,
                                        Region           = region
                                    }
                                },
                            },
                            Fixes            = fixes[i],
                            RelatedLocations = new[]
                            {
                                new Location
                                {
                                    PhysicalLocation = new PhysicalLocation
                                    {
                                        ArtifactLocation = new ArtifactLocation
                                        {
                                            // Because this file doesn't exist, it will be included in the files list but will only have a path and MIME type
                                            // This is the behavior you'll see any time a file can't be located/accessed
                                            Uri       = new Uri("SomeOtherSourceFile.cs", UriKind.Relative),
                                            UriBaseId = RepoRootBaseId
                                        },
                                        Region = new Region
                                        {
                                            StartLine   = 147,
                                            StartColumn = 19,
                                            EndLine     = 147,
                                            EndColumn   = 40
                                        }
                                    }
                                }
                            },
                            Stacks = new[]
                            {
                                new Stack
                                {
                                    Frames = new[]
                                    {
                                        new StackFrame
                                        {
                                            // The method that contains the defect
                                            Location = new Location
                                            {
                                                PhysicalLocation = new PhysicalLocation
                                                {
                                                    ArtifactLocation = artifactLocation,
                                                    Region           = new Region
                                                    {
                                                        StartLine = 17
                                                    }
                                                }
                                            }
                                        },
                                        new StackFrame
                                        {
                                            // The method that calls the one above, e.g. ComputeSomeValue()
                                            Location = new Location
                                            {
                                                PhysicalLocation = new PhysicalLocation
                                                {
                                                    ArtifactLocation = artifactLocation,
                                                    Region           = new Region
                                                    {
                                                        StartLine = 24 // Fake example
                                                    }
                                                }
                                            }
                                        },
                                        new StackFrame
                                        {
                                            // The method that calls the one above, e.g. Main()
                                            Location = new Location
                                            {
                                                PhysicalLocation = new PhysicalLocation
                                                {
                                                    ArtifactLocation = artifactLocation,
                                                    Region           = new Region
                                                    {
                                                        StartLine = 26 // Fake example
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        };

                        // Let's add a CodeFlow for the first defect (properties shouldn't return arrays)
                        // This flow shows where the array is declared, and where it is returned by a property
                        if (i == 0)
                        {
                            var codeFlow = new CodeFlow
                            {
                                // This is what a single-threaded result looks like
                                // TIP: use SarifUtilities.CreateSingleThreadedCodeFlow to reduce repetition
                                // Multi-threaded example coming soon!
                                ThreadFlows = new[]
                                {
                                    new ThreadFlow
                                    {
                                        Locations = new[]
                                        {
                                            new ThreadFlowLocation
                                            {
                                                // This is the defect statement's location
                                                Location = new Location
                                                {
                                                    PhysicalLocation = new PhysicalLocation
                                                    {
                                                        ArtifactLocation = artifactLocation,
                                                        Region           = region
                                                    }
                                                },
                                                Importance = ThreadFlowLocationImportance.Essential
                                            },
                                            new ThreadFlowLocation
                                            {
                                                // This is the declaration of the array
                                                Location = new Location
                                                {
                                                    PhysicalLocation = new PhysicalLocation
                                                    {
                                                        ArtifactLocation = artifactLocation,
                                                        Region           = new Region
                                                        {
                                                            StartLine = 12
                                                        }
                                                    }
                                                },
                                                NestingLevel = 1,
                                                Importance   = ThreadFlowLocationImportance.Important
                                            }
                                        }
                                    }
                                }
                            };
                            result.CodeFlows = new[] { codeFlow };
                        }

                        sarifLogger.Log(rule, result);
                    }
                }
            }

            File.WriteAllText(options.OutputFilePath, sb.ToString());
            return(0);
        }