From f65bd19c5c7b30cf5e9e4b80c800bd03b85cefac Mon Sep 17 00:00:00 2001 From: Gabe Stocco <98900+gfs@users.noreply.github.com> Date: Tue, 30 Aug 2022 15:09:42 -0700 Subject: [PATCH] Remove beta flag in version.json (#505) * Remove beta flag in version.json * Add the new ruleverifier options to the pack command. * Typos * Run formatting - no logic changes. * Update Dependencies * Refactor tests a bit * Fix template issue after reformatting. Add missing integrity flags. --- AppInspector.CLI/AppInspector.CLI.csproj | 142 +- AppInspector.CLI/CLICmdOptions.cs | 364 ++-- AppInspector.CLI/Program.cs | 756 ++++---- .../PublishProfiles/FolderProfile.pubxml | 22 +- AppInspector.CLI/ResultsWriter.cs | 164 +- AppInspector.CLI/TagInfo.cs | 244 ++- AppInspector.CLI/Writers/AnalyzeHtmlWriter.cs | 1090 +++++------ AppInspector.CLI/Writers/AnalyzeJsonWriter.cs | 79 +- .../Writers/AnalyzeSarifWriter.cs | 293 ++- AppInspector.CLI/Writers/AnalyzeTextWriter.cs | 297 ++- AppInspector.CLI/Writers/CmdResultsWriter.cs | 39 +- .../Writers/ExportTagsTextWriter.cs | 66 +- AppInspector.CLI/Writers/JsonWriter.cs | 77 +- AppInspector.CLI/Writers/TagDiffTextWriter.cs | 71 +- .../Writers/VerifyRulesTextWriter.cs | 71 +- AppInspector.CLI/Writers/WriterFactory.cs | 233 ++- AppInspector.CLI/html/index.html | 114 +- .../html/resources/css/appinspector.css | 52 +- .../html/resources/js/appinspector.js | 41 +- .../AppInspector.Common.csproj | 100 +- AppInspector.Common/MsgHelp.cs | 195 +- AppInspector.Common/OpException.cs | 34 +- .../PublishProfiles/FolderProfile.pubxml | 16 +- .../Properties/Resources.Designer.cs | 1 - AppInspector.Common/Properties/Resources.resx | 380 ++-- AppInspector.Common/Utils.cs | 155 +- .../AppInspector.Logging.csproj | 72 +- AppInspector.Logging/LogOptions.cs | 70 +- AppInspector.RulesEngine/AbstractRuleSet.cs | 444 +++-- .../AppInspector.RulesEngine.csproj | 110 +- AppInspector.RulesEngine/Boundary.cs | 25 +- AppInspector.RulesEngine/Comment.cs | 29 +- AppInspector.RulesEngine/Confidence.cs | 18 +- AppInspector.RulesEngine/LanguageInfo.cs | 40 +- AppInspector.RulesEngine/Languages.cs | 360 ++-- AppInspector.RulesEngine/Location.cs | 11 +- AppInspector.RulesEngine/MatchRecord.cs | 306 ++- .../ApplicationInspectorAnalyzer.cs | 17 +- .../OatExtensions/ConvertedOatRule.cs | 31 +- .../OatExtensions/OatRegexWithIndexClause.cs | 30 +- .../OatRegexWithIndexOperation.cs | 292 ++- .../OatExtensions/OatSubstringIndexClause.cs | 36 +- .../OatSubstringIndexOperation.cs | 226 +-- .../OatExtensions/WithinClause.cs | 35 +- .../OatExtensions/WithinOperation.cs | 335 ++-- AppInspector.RulesEngine/PatternScope.cs | 17 +- AppInspector.RulesEngine/PatternType.cs | 23 +- .../Resources/languages.json | 225 ++- AppInspector.RulesEngine/Rule.cs | 177 +- AppInspector.RulesEngine/RuleProcessor.cs | 904 +++++---- AppInspector.RulesEngine/RulesVerifier.cs | 423 ++--- .../RulesVerifierOptions.cs | 32 +- .../RulesVerifierResult.cs | 3 +- AppInspector.RulesEngine/Ruleset.cs | 26 +- AppInspector.RulesEngine/SearchCondition.cs | 20 +- AppInspector.RulesEngine/SearchPattern.cs | 95 +- AppInspector.RulesEngine/Severity.cs | 63 +- AppInspector.RulesEngine/TextContainer.cs | 597 +++--- AppInspector.RulesEngine/TypedRuleSet.cs | 91 +- AppInspector.Tests/AppInspector.Tests.csproj | 58 +- AppInspector.Tests/Commands/TestAnalyzeCmd.cs | 1555 ++++++++-------- .../Commands/TestExportTagsCmd.cs | 117 +- .../Commands/TestPackRulesCmd.cs | 61 +- AppInspector.Tests/Commands/TestTagDiffCmd.cs | 220 +-- .../Commands/TestVerifyRulesCmd.cs | 816 ++++---- .../DefaultRules/TestDefaultRules.cs | 123 +- .../Languages/LanguagesTests.cs | 144 +- .../RuleProcessor/LoadRulesTests.cs | 102 +- .../RuleProcessor/RegexWithIndexTests.cs | 375 ++-- AppInspector.Tests/RuleProcessor/RuleTests.cs | 72 +- .../RuleProcessor/SubstringWithIndexTests.cs | 398 ++-- .../RuleProcessor/WithinClauseTests.cs | 1040 +++++------ .../RuleProcessor/XmlAndJsonTests.cs | 270 +++ AppInspector.Tests/TestHelpers.cs | 209 ++- AppInspector/AppInspector.Commands.csproj | 164 +- AppInspector/Commands/AnalyzeCommand.cs | 1654 ++++++++--------- AppInspector/Commands/ExportTagsCommand.cs | 197 +- AppInspector/Commands/PackRulesCommand.cs | 187 +- AppInspector/Commands/TagDiffCommand.cs | 429 +++-- AppInspector/Commands/VerifyRulesCommand.cs | 225 ++- AppInspector/FileRecord.cs | 53 +- AppInspector/MetaData.cs | 442 ++--- AppInspector/MetaDataHelper.cs | 621 +++---- AppInspector/MetricTagCounter.cs | 36 +- .../PublishProfiles/FolderProfile.pubxml | 16 +- AppInspector/Properties/Resources.Designer.cs | 1 - AppInspector/Properties/Resources.resx | 378 ++-- AppInspector/Result.cs | 21 +- AppInspector/RuleSetUtils.cs | 52 +- AppInspector/rules/README.md | 15 +- AppInspector/rules/SECURITY.md | 40 +- AppInspector/rules/SUPPORT.md | 13 +- .../default/cloud_services/ad_networks.json | 36 +- .../rules/default/cloud_services/bigdata.json | 8 +- .../default/cloud_services/cloud_hosting.json | 308 ++- .../default/cloud_services/data_storage.json | 187 +- .../default/cloud_services/ecommerce.json | 49 +- .../default/cloud_services/socialmedia.json | 65 +- .../default/cloud_services/web_analytics.json | 85 +- .../default/components/active_content.json | 108 +- .../rules/default/components/load_dll.json | 50 +- .../algorithm_implementation.json | 32 +- .../default/cryptography/certificate.json | 127 +- .../rules/default/cryptography/ciphers.json | 121 +- .../default/cryptography/crypto_currency.json | 28 +- .../rules/default/cryptography/encoding.json | 12 +- .../rules/default/cryptography/extended.json | 140 +- .../cryptography/external_libraries.json | 88 +- .../default/cryptography/hash_algorithm.json | 75 +- .../default/cryptography/key_derivation.json | 40 +- .../rules/default/cryptography/protocol.json | 93 +- .../rules/default/cryptography/random.json | 65 +- .../rules/default/cryptography/weakssl.json | 132 +- .../data_handling/compressed_files.json | 12 +- .../rules/default/data_handling/database.json | 311 +++- .../data_handling/deserialization.json | 212 ++- .../default/data_handling/json_parsing.json | 33 +- .../default/data_handling/media_parsing.json | 32 +- .../rules/default/data_handling/pastebin.json | 13 +- .../default/data_handling/xml_parsing.json | 118 +- .../rules/default/data_types/financial.json | 81 +- .../rules/default/data_types/media.json | 44 +- .../rules/default/data_types/secrets.json | 83 +- .../rules/default/data_types/sensitive.json | 145 +- .../device_permissions/IOSPermissions.json | 224 ++- .../rules/default/device_permissions/UWP.json | 208 ++- .../device_permissions/android_intents.json | 276 ++- .../rules/default/frameworks/PHP.json | 180 +- .../rules/default/frameworks/build.json | 184 +- AppInspector/rules/default/frameworks/c.json | 40 +- .../default/frameworks/csharp-nonMS.json | 16 +- .../rules/default/frameworks/java.json | 272 ++- .../rules/default/frameworks/javascript.json | 287 ++- .../rules/default/frameworks/logging.json | 92 +- .../rules/default/frameworks/microsoft.json | 166 +- .../rules/default/frameworks/python.json | 256 ++- .../rules/default/frameworks/ruby.json | 48 +- .../rules/default/frameworks/rust.json | 60 +- .../rules/default/general/OSS_license.json | 117 +- .../rules/default/general/code_metrics.json | 99 +- .../rules/default/general/dependencies.json | 150 +- .../rules/default/general/hygiene.json | 36 +- .../rules/default/general/platforms.json | 236 ++- .../rules/default/general/solutioninfo.json | 292 ++- .../hashicorp_packers_tmpl.json | 12 +- .../hashicorp_terraform_tmpl.json | 16 +- .../infrastructure/microsoft_arm_tmpl.json | 136 +- .../networkcomms/outbound_network.json | 287 ++- AppInspector/rules/default/os/acl.json | 152 +- .../rules/default/os/dynamic_execution.json | 273 ++- AppInspector/rules/default/os/file_io.json | 327 +++- AppInspector/rules/default/os/process.json | 120 +- AppInspector/rules/default/os/setenv.json | 176 +- .../rules/default/os/system_registry.json | 144 +- AppInspector/rules/default/os/user_accts.json | 24 +- .../security_feature/authentication.json | 311 +++- .../security_feature/authorization.json | 96 +- .../default/test_frameworks/cpp_testing.json | 122 +- .../default/test_frameworks/go_testing.json | 16 +- .../default/test_frameworks/java_testing.json | 716 +++++-- .../test_frameworks/javascript_testing.json | 759 ++++++-- .../test_frameworks/objectiveC_testing.json | 146 +- .../test_frameworks/powershell_testing.json | 17 +- .../test_frameworks/python_testing.json | 88 +- .../default/test_frameworks/ruby_testing.json | 118 +- .../rules/default/tools/pipeline.json | 144 +- AppInspector/rules/default/webapp/comms.json | 36 +- .../rules/default/webapp/headers.json | 132 +- AppInspector/rules/default/webapp/media.json | 16 +- .../rules/default/webapp/sessions.json | 156 +- .../rules/default/webapp/storage.json | 72 +- Benchmarks/AnalyzeBenchmark.cs | 102 +- Benchmarks/Benchmarks.csproj | 40 +- Benchmarks/DistinctBenchmarks.cs | 62 +- Benchmarks/Program.cs | 23 +- Directory.Build.props | 12 +- README.md | 100 +- version.json | 2 +- 178 files changed, 18443 insertions(+), 13065 deletions(-) create mode 100644 AppInspector.Tests/RuleProcessor/XmlAndJsonTests.cs diff --git a/AppInspector.CLI/AppInspector.CLI.csproj b/AppInspector.CLI/AppInspector.CLI.csproj index 9c30654..5b03395 100644 --- a/AppInspector.CLI/AppInspector.CLI.csproj +++ b/AppInspector.CLI/AppInspector.CLI.csproj @@ -1,78 +1,78 @@ - - Exe - net6.0 - Microsoft.ApplicationInspector.CLI - ApplicationInspector.CLI - © Microsoft Corporation. All rights reserved. - Application Inspector - Microsoft - Microsoft - 0.0.0-placeholder - Microsoft Application Inspector is a software source code analysis tool that helps identify and surface well-known features and other interesting characteristics of source code to aid in determining what the software is or what it does. This is a dotnet tool package. For the library, see Microsoft.CST.ApplicationInspector. - 0.0.0.0 - 0.0.0.0 - false - true - Microsoft.CST.ApplicationInspector.CLI - 0.0.0 - https://github.com/microsoft/ApplicationInspector - Security Static Analyzer - appinspector - LICENSE.txt - icon-128.png - true - snupkg - enable - 10.0 - + + Exe + net6.0 + Microsoft.ApplicationInspector.CLI + ApplicationInspector.CLI + © Microsoft Corporation. All rights reserved. + Application Inspector + Microsoft + Microsoft + 0.0.0-placeholder + Microsoft Application Inspector is a software source code analysis tool that helps identify and surface well-known features and other interesting characteristics of source code to aid in determining what the software is or what it does. This is a dotnet tool package. For the library, see Microsoft.CST.ApplicationInspector. + 0.0.0.0 + 0.0.0.0 + false + true + Microsoft.CST.ApplicationInspector.CLI + 0.0.0 + https://github.com/microsoft/ApplicationInspector + Security Static Analyzer + appinspector + LICENSE.txt + icon-128.png + true + snupkg + enable + 10.0 + - - - + + + - - - - - - + + + + + + - - - - - + + + + + - - - Always - - - Always - - - Always - - - Always - - - - - - - - - - - - - - - - - - + + + Always + + + Always + + + Always + + + Always + + + + + + + + + + + + + + + + + + diff --git a/AppInspector.CLI/CLICmdOptions.cs b/AppInspector.CLI/CLICmdOptions.cs index 1c815ee..84a427e 100644 --- a/AppInspector.CLI/CLICmdOptions.cs +++ b/AppInspector.CLI/CLICmdOptions.cs @@ -1,170 +1,210 @@ // Copyright (C) Microsoft. All rights reserved. // Licensed under the MIT License. See LICENSE.txt in the project root for license information. +using System; +using System.Collections.Generic; +using CommandLine; +using Microsoft.ApplicationInspector.Commands; using Microsoft.ApplicationInspector.Logging; +using Microsoft.ApplicationInspector.RulesEngine; -namespace Microsoft.ApplicationInspector.CLI +namespace Microsoft.ApplicationInspector.CLI; + +/// +/// CLI command option classes add output arguments to common properties for each command verb +/// +public record CLICommandOptions : LogOptions { - using CommandLine; - using Microsoft.ApplicationInspector.Commands; - using Microsoft.ApplicationInspector.RulesEngine; - using System.Collections.Generic; + [Option('o', "output-file-path", Required = false, HelpText = "Output file path")] + public string? OutputFilePath { get; set; } - /// - /// CLI command option classes add output arguments to common properties for each command verb - /// - /// - public record CLICommandOptions : LogOptions - { - [Option('o', "output-file-path", Required = false, HelpText = "Output file path")] - public string? OutputFilePath { get; set; } - - [Option('f', "output-file-format", Required = false, HelpText = "Output format [json|text]", Default = "text")] - public string OutputFileFormat { get; set; } = "text"; - } - - public record CLICustomRulesCommandOptions: CLICommandOptions - { - [Option('r', "custom-rules-path", Required = false, HelpText = "Custom rules file or directory path")] - public string? CustomRulesPath { get; set; } - - [Option("custom-languages-path", Required = false, HelpText = "Replace the default languages set with a custom languages.json.")] - public string? CustomLanguagesPath { get; set; } - - [Option("custom-comments-path", Required = false, HelpText = "Replace the default comment specification set with a custom comments.json.")] - public string? CustomCommentsPath { get; set; } - - [Option("disable-require-unique-ids", Required = false, HelpText = "Allow rules with duplicate IDs.")] - public bool DisableRequireUniqueIds { get; set; } - /// - /// Return a success error code when no matches were found but operation was apparently successful. Useful for CI scenarios - /// - [Option("success-error-code-with-no-matches", Required = false, HelpText = "When processing is apparently successful but there are no matches return a success error code - useful for CI.")] - public bool SuccessErrorCodeOnNoMatches { get; set; } - - [Option("require-must-match", Required = false, HelpText = "When validating, require rules to have MustMatch self-tests.")] - public bool RequireMustMatch { get; set; } - - [Option("require-must-not-match", Required = false, HelpText = "When validating, require rules to have MustNotMatch self-tests.")] - public bool RequireMustNotMatch { get; set; } - - } - - public record CLIAnalysisSharedCommandOptions : CLICustomRulesCommandOptions - { - [Option("disable-custom-rule-validation", Required = false, HelpText = "By default when providing custom rules they are validated. When set, validation will be skipped.")] - public bool DisableCustomRuleValidation { get; set; } = false; - - [Option('i', "ignore-default-rules", Required = false, HelpText = "Exclude default rules bundled with application", Default = false)] - public bool IgnoreDefaultRules { get; set; } - - [Option('F', "file-timeout", Required = false, HelpText = "Maximum amount of time in milliseconds to allow for processing each file. 0 is infinity. Default: 60000.", Default = 60000)] - public int FileTimeOut { get; set; } = 60000; - - [Option('p', "processing-timeout", Required = false, HelpText = "Maximum amount of time in milliseconds to allow for processing. When NoShowProgress is set this includes enumeration time. 0 is infinity. Default: 0.", Default = 0)] - public int ProcessingTimeOut { get; set; } - - [Option("enumeration-timeout", Required = false, HelpText = "Maximum amount of time in milliseconds to allow for enumerating. 0 is infinity. Default: 0.", Default = 0)] - public int EnumeratingTimeout { get; set; } - - [Option("disable-archive-crawling", Required = false, HelpText = "Disable Archive Enumeration.")] - public bool DisableArchiveCrawling { get; set; } - - [Option('S', "single-threaded", Required = false, HelpText = "Disables parallel processing. May be helpful for debugging with higher verbosity.")] - public bool SingleThread { get; set; } - - [Option('g', "exclusion-globs", Required = false, HelpText = "Exclude source files that match glob patterns. Example: \"**/.git/**,*Tests*\". Use \"none\" to disable.", Default = new string[] { "**/bin/**", "**/obj/**", "**/.vs/**", "**/.git/**" }, Separator = ',')] - public IEnumerable FilePathExclusions { get; set; } = System.Array.Empty(); - - [Option('u', "scan-unknown-filetypes", Required = false, HelpText = "Scan files of unknown types.")] - public bool ScanUnknownTypes { get; set; } - - [Option('c', "confidence-filters", Required = false, Separator = ',', HelpText = "Output only matches with specified confidence ,. Default: Medium,High. [High|Medium|Low]", Default = new Confidence[]{ Confidence.High, Confidence.Medium })] - public IEnumerable ConfidenceFilters { get; set; } = new Confidence[] { Confidence.High, Confidence.Medium }; - - [Option("severity-filters", Required = false, Separator = ',', - HelpText = - "Output only matches with specified severity ,. Default: All are enabled. [Critical|Important|Moderate|BestPractice|ManualReview]", Default = new Severity[] { Severity.Critical, Severity.Important, Severity.Moderate, Severity.BestPractice, Severity.ManualReview })] - public IEnumerable SeverityFilters { get; set; } = new Severity[] { Severity.Critical, Severity.Important, Severity.Moderate, Severity.BestPractice, Severity.ManualReview }; - } - - /// - /// CLI command distinct arguments - /// - [Verb("analyze", HelpText = "Inspect source directory/file/compressed file (.tgz|zip) against defined characteristics")] - public record CLIAnalyzeCmdOptions : CLIAnalysisSharedCommandOptions - { - [Option('s', "source-path", Required = true, HelpText = "Source file or directory to inspect, comma separated", Separator = ',')] - public IEnumerable SourcePath { get; set; } = System.Array.Empty(); - - [Option('f', "output-file-format", Required = false, HelpText = "Output format [html|json|text]", Default = "html")] - public new string OutputFileFormat { get; set; } = "html"; - - [Option('e', "text-format", Required = false, HelpText = "Match text format specifiers", Default = "Tag:%T,Rule:%N,Ruleid:%R,Confidence:%X,File:%F,Sourcetype:%t,Line:%L,Sample:%m")] - public string TextOutputFormat { get; set; } = "Tag:%T,Rule:%N,Ruleid:%R,Confidence:%X,File:%F,Sourcetype:%t,Line:%L,Sample:%m"; - - [Option('N',"no-show-progress", Required = false, HelpText = "Disable progress information.")] - public bool NoShowProgressBar { get; set; } - - [Option('C',"context-lines", Required = false, HelpText = "Number of lines of context on each side to include in excerpt (up to a maximum of 100 * NumLines characters on each side). 0 to skip exerpt. -1 to not extract samples or excerpts (implied by -t). When outputting sarif use -1 for no snippets, all other values ignored.")] - public int ContextLines { get; set; } = 3; - - [Option('t',"tags-only", Required = false, HelpText = "Only get tags (no detailed match data). Ignored if output format is sarif.")] - public bool TagsOnly { get; set; } - - [Option('n', "no-file-metadata", Required = false, HelpText = "Don't collect metadata about each individual file.")] - public bool NoFileMetadata { get; set; } - - [Option('A', "allow-all-tags-in-build-files", Required = false, HelpText = "Allow all tags (not just Metadata tags) in files of type Build.")] - public bool AllowAllTagsInBuildFiles { get; set; } - - [Option('M', "max-num-matches-per-tag", Required = false, HelpText = "If non-zero, and TagsOnly is not set, will ignore rules based on if all of their tags have been found the set value number of times.")] - public int MaxNumMatchesPerTag { get; set; } = 0; - - [Option("base-path", Required = false, HelpText = "If set, when outputting sarif, will have paths made relative to the provided path.")] - public string? BasePath { get; set; } = null; - - [Option("repository-uri", Required = false, HelpText = "If set, when outputting sarif, include this information.")] - public string? RepositoryUri { get; set; } = null; - - [Option("commit-hash", Required = false, HelpText = "If set, when outputting sarif, include this information.")] - public string? CommitHash { get; set; } = null; - } - - [Verb("tagdiff", HelpText = "Compares unique tag values between two source paths")] - public record CLITagDiffCmdOptions : CLIAnalysisSharedCommandOptions - { - [Option("src1", Required = true, HelpText = "Source 1 to compare (commaa separated)")] - public IEnumerable SourcePath1 { get; set; } = System.Array.Empty(); - - [Option("src2", Required = true, HelpText = "Source 2 to compare (commaa separated)")] - public IEnumerable SourcePath2 { get; set; } = System.Array.Empty(); - - [Option('t', "test-type", Required = false, HelpText = "Type of test to run [Equality|Inequality]", Default = TagTestType.Equality)] - public TagTestType TestType { get; set; } = TagTestType.Equality; - } - - [Verb("exporttags", HelpText = "Export the list of tags associated with the specified rules. Does not scan source code.")] - public record CLIExportTagsCmdOptions : CLICommandOptions - { - [Option('r', "custom-rules-path", Required = false, HelpText = "Custom rules file or directory path")] - public string? CustomRulesPath { get; set; } - - [Option('i', "ignore-default-rules", Required = false, HelpText = "Exclude default rules bundled with application", Default = false)] - public bool IgnoreDefaultRules { get; set; } - } - - [Verb("verifyrules", HelpText = "Verify custom rules syntax is valid")] - public record CLIVerifyRulesCmdOptions : CLICustomRulesCommandOptions - { - [Option('d', "verify-default-rules", Required = false, Default = false, HelpText = "Verify the rules embedded in the binary.")] - public bool VerifyDefaultRules { get; set; } - } - - [Verb("packrules", HelpText = "Combine multiple rule files into one file for ease in distribution")] - public record CLIPackRulesCmdOptions : CLICustomRulesCommandOptions - { - [Option('e', "pack-embedded-rules", Required = false, HelpText = "Pack the rules that are embedded in the application inspector binary.")] - public bool PackEmbeddedRules { get; set; } - } + [Option('f', "output-file-format", Required = false, HelpText = "Output format [json|text]", Default = "text")] + public string OutputFileFormat { get; set; } = "text"; } + +public record CLICustomRulesCommandOptions : CLICommandOptions +{ + [Option('r', "custom-rules-path", Required = false, HelpText = "Custom rules file or directory path")] + public string? CustomRulesPath { get; set; } + + [Option("custom-languages-path", Required = false, + HelpText = "Replace the default languages set with a custom languages.json.")] + public string? CustomLanguagesPath { get; set; } + + [Option("custom-comments-path", Required = false, + HelpText = "Replace the default comment specification set with a custom comments.json.")] + public string? CustomCommentsPath { get; set; } + + [Option("disable-require-unique-ids", Required = false, HelpText = "Allow rules with duplicate IDs.")] + public bool DisableRequireUniqueIds { get; set; } + + /// + /// Return a success error code when no matches were found but operation was apparently successful. Useful for CI + /// scenarios + /// + [Option("success-error-code-with-no-matches", Required = false, + HelpText = + "When processing is apparently successful but there are no matches return a success error code - useful for CI.")] + public bool SuccessErrorCodeOnNoMatches { get; set; } + + [Option("require-must-match", Required = false, + HelpText = "When validating, require rules to have MustMatch self-tests.")] + public bool RequireMustMatch { get; set; } + + [Option("require-must-not-match", Required = false, + HelpText = "When validating, require rules to have MustNotMatch self-tests.")] + public bool RequireMustNotMatch { get; set; } +} + +public record CLIAnalysisSharedCommandOptions : CLICustomRulesCommandOptions +{ + [Option("disable-custom-rule-validation", Required = false, + HelpText = "By default when providing custom rules they are validated. When set, validation will be skipped.")] + public bool DisableCustomRuleValidation { get; set; } = false; + + [Option('i', "ignore-default-rules", Required = false, HelpText = "Exclude default rules bundled with application", + Default = false)] + public bool IgnoreDefaultRules { get; set; } + + [Option('F', "file-timeout", Required = false, + HelpText = + "Maximum amount of time in milliseconds to allow for processing each file. 0 is infinity. Default: 60000.", + Default = 60000)] + public int FileTimeOut { get; set; } = 60000; + + [Option('p', "processing-timeout", Required = false, + HelpText = + "Maximum amount of time in milliseconds to allow for processing. When NoShowProgress is set this includes enumeration time. 0 is infinity. Default: 0.", + Default = 0)] + public int ProcessingTimeOut { get; set; } + + [Option("enumeration-timeout", Required = false, + HelpText = "Maximum amount of time in milliseconds to allow for enumerating. 0 is infinity. Default: 0.", + Default = 0)] + public int EnumeratingTimeout { get; set; } + + [Option("disable-archive-crawling", Required = false, HelpText = "Disable Archive Enumeration.")] + public bool DisableArchiveCrawling { get; set; } + + [Option('S', "single-threaded", Required = false, + HelpText = "Disables parallel processing. May be helpful for debugging with higher verbosity.")] + public bool SingleThread { get; set; } + + [Option('g', "exclusion-globs", Required = false, + HelpText = + "Exclude source files that match glob patterns. Example: \"**/.git/**,*Tests*\". Use \"none\" to disable.", + Default = new[] { "**/bin/**", "**/obj/**", "**/.vs/**", "**/.git/**" }, Separator = ',')] + public IEnumerable FilePathExclusions { get; set; } = Array.Empty(); + + [Option('u', "scan-unknown-filetypes", Required = false, HelpText = "Scan files of unknown types.")] + public bool ScanUnknownTypes { get; set; } + + [Option('c', "confidence-filters", Required = false, Separator = ',', + HelpText = + "Output only matches with specified confidence ,. Default: Medium,High. [High|Medium|Low]", + Default = new[] { Confidence.High, Confidence.Medium })] + public IEnumerable ConfidenceFilters { get; set; } = new[] { Confidence.High, Confidence.Medium }; + + [Option("severity-filters", Required = false, Separator = ',', + HelpText = + "Output only matches with specified severity ,. Default: All are enabled. [Critical|Important|Moderate|BestPractice|ManualReview]", + Default = new[] + { Severity.Critical, Severity.Important, Severity.Moderate, Severity.BestPractice, Severity.ManualReview })] + public IEnumerable SeverityFilters { get; set; } = new[] + { Severity.Critical, Severity.Important, Severity.Moderate, Severity.BestPractice, Severity.ManualReview }; +} + +/// +/// CLI command distinct arguments +/// +[Verb("analyze", HelpText = "Inspect source directory/file/compressed file (.tgz|zip) against defined characteristics")] +public record CLIAnalyzeCmdOptions : CLIAnalysisSharedCommandOptions +{ + [Option('s', "source-path", Required = true, HelpText = "Source file or directory to inspect, comma separated", + Separator = ',')] + public IEnumerable SourcePath { get; set; } = Array.Empty(); + + [Option('f', "output-file-format", Required = false, HelpText = "Output format [html|json|text]", Default = "html")] + public new string OutputFileFormat { get; set; } = "html"; + + [Option('e', "text-format", Required = false, HelpText = "Match text format specifiers", + Default = "Tag:%T,Rule:%N,Ruleid:%R,Confidence:%X,File:%F,Sourcetype:%t,Line:%L,Sample:%m")] + public string TextOutputFormat { get; set; } = + "Tag:%T,Rule:%N,Ruleid:%R,Confidence:%X,File:%F,Sourcetype:%t,Line:%L,Sample:%m"; + + [Option('N', "no-show-progress", Required = false, HelpText = "Disable progress information.")] + public bool NoShowProgressBar { get; set; } + + [Option('C', "context-lines", Required = false, + HelpText = + "Number of lines of context on each side to include in excerpt (up to a maximum of 100 * NumLines characters on each side). 0 to skip exerpt. -1 to not extract samples or excerpts (implied by -t). When outputting sarif use -1 for no snippets, all other values ignored.")] + public int ContextLines { get; set; } = 3; + + [Option('t', "tags-only", Required = false, + HelpText = "Only get tags (no detailed match data). Ignored if output format is sarif.")] + public bool TagsOnly { get; set; } + + [Option('n', "no-file-metadata", Required = false, HelpText = "Don't collect metadata about each individual file.")] + public bool NoFileMetadata { get; set; } + + [Option('A', "allow-all-tags-in-build-files", Required = false, + HelpText = "Allow all tags (not just Metadata tags) in files of type Build.")] + public bool AllowAllTagsInBuildFiles { get; set; } + + [Option('M', "max-num-matches-per-tag", Required = false, + HelpText = + "If non-zero, and TagsOnly is not set, will ignore rules based on if all of their tags have been found the set value number of times.")] + public int MaxNumMatchesPerTag { get; set; } = 0; + + [Option("base-path", Required = false, + HelpText = "If set, when outputting sarif, will have paths made relative to the provided path.")] + public string? BasePath { get; set; } = null; + + [Option("repository-uri", Required = false, HelpText = "If set, when outputting sarif, include this information.")] + public string? RepositoryUri { get; set; } = null; + + [Option("commit-hash", Required = false, HelpText = "If set, when outputting sarif, include this information.")] + public string? CommitHash { get; set; } = null; +} + +[Verb("tagdiff", HelpText = "Compares unique tag values between two source paths")] +public record CLITagDiffCmdOptions : CLIAnalysisSharedCommandOptions +{ + [Option("src1", Required = true, HelpText = "Source 1 to compare (commaa separated)")] + public IEnumerable SourcePath1 { get; set; } = Array.Empty(); + + [Option("src2", Required = true, HelpText = "Source 2 to compare (commaa separated)")] + public IEnumerable SourcePath2 { get; set; } = Array.Empty(); + + [Option('t', "test-type", Required = false, HelpText = "Type of test to run [Equality|Inequality]", + Default = TagTestType.Equality)] + public TagTestType TestType { get; set; } = TagTestType.Equality; +} + +[Verb("exporttags", + HelpText = "Export the list of tags associated with the specified rules. Does not scan source code.")] +public record CLIExportTagsCmdOptions : CLICommandOptions +{ + [Option('r', "custom-rules-path", Required = false, HelpText = "Custom rules file or directory path")] + public string? CustomRulesPath { get; set; } + + [Option('i', "ignore-default-rules", Required = false, HelpText = "Exclude default rules bundled with application", + Default = false)] + public bool IgnoreDefaultRules { get; set; } +} + +[Verb("verifyrules", HelpText = "Verify custom rules syntax is valid")] +public record CLIVerifyRulesCmdOptions : CLICustomRulesCommandOptions +{ + [Option('d', "verify-default-rules", Required = false, Default = false, + HelpText = "Verify the rules embedded in the binary.")] + public bool VerifyDefaultRules { get; set; } +} + +[Verb("packrules", HelpText = "Combine multiple rule files into one file for ease in distribution")] +public record CLIPackRulesCmdOptions : CLICustomRulesCommandOptions +{ + [Option('e', "pack-embedded-rules", Required = false, + HelpText = "Pack the rules that are embedded in the application inspector binary.")] + public bool PackEmbeddedRules { get; set; } +} \ No newline at end of file diff --git a/AppInspector.CLI/Program.cs b/AppInspector.CLI/Program.cs index 706af38..fb66876 100644 --- a/AppInspector.CLI/Program.cs +++ b/AppInspector.CLI/Program.cs @@ -1,434 +1,436 @@ //Copyright(c) Microsoft Corporation.All rights reserved. // Licensed under the MIT License. +using System; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using CommandLine; using CommandLine.Text; +using Microsoft.ApplicationInspector.Commands; +using Microsoft.ApplicationInspector.Common; using Microsoft.ApplicationInspector.Logging; +using Microsoft.Extensions.Logging; +using ShellProgressBar; -namespace Microsoft.ApplicationInspector.CLI +namespace Microsoft.ApplicationInspector.CLI; + +public static class Program { - using CommandLine; - using Commands; - using Microsoft.ApplicationInspector.Common; - using Microsoft.Extensions.Logging; - using ShellProgressBar; - using System; - using System.IO; - using System.Linq; - using System.Threading; - using System.Threading.Tasks; - using ILogger = Extensions.Logging.ILogger; + private static ILoggerFactory loggerFactory = new LoggerFactory(); - public static class Program + /// + /// CLI program entry point which defines command verbs and options to running + /// + /// + public static int Main(string[] args) { - private static ILoggerFactory loggerFactory = new LoggerFactory(); + var finalResult = (int)Utils.ExitCode.CriticalError; - /// - /// CLI program entry point which defines command verbs and options to running - /// - /// - public static int Main(string[] args) + Utils.CLIExecutionContext = true; //set manually at start from CLI + try { - int finalResult = (int)Common.Utils.ExitCode.CriticalError; - - Common.Utils.CLIExecutionContext = true;//set manually at start from CLI - try - { - var argsResult = new Parser(settings => - { - settings.CaseInsensitiveEnumValues = true; - }).ParseArguments { settings.CaseInsensitiveEnumValues = true; }) + .ParseArguments(args); - return argsResult.MapResult( - (CLIAnalyzeCmdOptions cliAnalyzeCmdOptions) => VerifyOutputArgsAndRunAnalyze(cliAnalyzeCmdOptions), - (CLITagDiffCmdOptions cliTagDiffCmdOptions) => VerifyOutputArgsAndRunTagDiff(cliTagDiffCmdOptions), - (CLIExportTagsCmdOptions cliExportTagsCmdOptions) => VerifyOutputArgsAndRunExportTags(cliExportTagsCmdOptions), - (CLIVerifyRulesCmdOptions cliVerifyRulesCmdOptions) => VerifyOutputArgsAndRunVerifyRules(cliVerifyRulesCmdOptions), - (CLIPackRulesCmdOptions cliPackRulesCmdOptions) => VerifyOutputArgsAndRunPackRules(cliPackRulesCmdOptions), - errs => - { - Console.WriteLine(HelpText.AutoBuild(argsResult)); - return (int)Common.Utils.ExitCode.CriticalError; - }); - } - catch (Exception e) - { - var logger = loggerFactory.CreateLogger("Program"); - logger.LogError("Uncaught exception: {type}:{message}. {stackTrace}", e.GetType().Name, e.Message, e.StackTrace); - loggerFactory.Dispose(); - } - - return finalResult; - } - - private static int VerifyOutputArgsAndRunTagDiff(CLITagDiffCmdOptions options) - { - loggerFactory = options.GetLoggerFactory(); - - CommonOutputChecks(options); - return RunTagDiffCommand(options); - } - - private static int VerifyOutputArgsAndRunExportTags(CLIExportTagsCmdOptions options) - { - loggerFactory = options.GetLoggerFactory(); - - CommonOutputChecks(options); - return RunExportTagsCommand(options); - } - - private static int VerifyOutputArgsAndRunVerifyRules(CLIVerifyRulesCmdOptions options) - { - loggerFactory = options.GetLoggerFactory(); - - CommonOutputChecks(options); - return RunVerifyRulesCommand(options); - } - - private static int VerifyOutputArgsAndRunPackRules(CLIPackRulesCmdOptions options) - { - loggerFactory = options.GetLoggerFactory(); - ILogger logger = loggerFactory.CreateLogger("Program"); - - if (string.IsNullOrEmpty(options.OutputFilePath)) - { - logger.LogError(MsgHelp.GetString(MsgHelp.ID.PACK_MISSING_OUTPUT_ARG)); - throw new OpException(MsgHelp.GetString(MsgHelp.ID.PACK_MISSING_OUTPUT_ARG)); - } - else - { - CommonOutputChecks(options); - } - - return RunPackRulesCommand(options); - } - - private static int VerifyOutputArgsAndRunAnalyze(CLIAnalyzeCmdOptions options) - { - loggerFactory = options.GetLoggerFactory(); - - //analyze with html format limit checks - if (options.OutputFileFormat == "html") - { - options.OutputFilePath ??= "output.html"; - string extensionCheck = Path.GetExtension(options.OutputFilePath); - if (extensionCheck is not ".html" and not ".htm") + return argsResult.MapResult( + (CLIAnalyzeCmdOptions cliAnalyzeCmdOptions) => VerifyOutputArgsAndRunAnalyze(cliAnalyzeCmdOptions), + (CLITagDiffCmdOptions cliTagDiffCmdOptions) => VerifyOutputArgsAndRunTagDiff(cliTagDiffCmdOptions), + (CLIExportTagsCmdOptions cliExportTagsCmdOptions) => + VerifyOutputArgsAndRunExportTags(cliExportTagsCmdOptions), + (CLIVerifyRulesCmdOptions cliVerifyRulesCmdOptions) => + VerifyOutputArgsAndRunVerifyRules(cliVerifyRulesCmdOptions), + (CLIPackRulesCmdOptions cliPackRulesCmdOptions) => + VerifyOutputArgsAndRunPackRules(cliPackRulesCmdOptions), + errs => { - loggerFactory.CreateLogger("Program").LogInformation(MsgHelp.GetString(MsgHelp.ID.ANALYZE_HTML_EXTENSION)); - } - } - if (CommonOutputChecks(options)) - { - return RunAnalyzeCommand(options); - } - else - { - return (int)Utils.ExitCode.CriticalError; - } + Console.WriteLine(HelpText.AutoBuild(argsResult)); + return (int)Utils.ExitCode.CriticalError; + }); } - - /// - /// Checks that either output filepath is valid or console verbosity is not visible to ensure - /// some output can be achieved...other command specific inputs that are relevant to both CLI - /// and NuGet callers are checked by the commands themselves - /// - /// - private static bool CommonOutputChecks(CLICommandOptions options) + catch (Exception e) { - //validate requested format - string fileFormatArg = options.OutputFileFormat; - string[] validFormats = - { - "html", - "text", - "json", - "sarif" - }; var logger = loggerFactory.CreateLogger("Program"); - string[] checkFormats; - if (options is CLIAnalyzeCmdOptions cliAnalyzeOptions) + logger.LogError("Uncaught exception: {type}:{message}. {stackTrace}", e.GetType().Name, e.Message, + e.StackTrace); + loggerFactory.Dispose(); + } + + return finalResult; + } + + private static int VerifyOutputArgsAndRunTagDiff(CLITagDiffCmdOptions options) + { + loggerFactory = options.GetLoggerFactory(); + + CommonOutputChecks(options); + return RunTagDiffCommand(options); + } + + private static int VerifyOutputArgsAndRunExportTags(CLIExportTagsCmdOptions options) + { + loggerFactory = options.GetLoggerFactory(); + + CommonOutputChecks(options); + return RunExportTagsCommand(options); + } + + private static int VerifyOutputArgsAndRunVerifyRules(CLIVerifyRulesCmdOptions options) + { + loggerFactory = options.GetLoggerFactory(); + + CommonOutputChecks(options); + return RunVerifyRulesCommand(options); + } + + private static int VerifyOutputArgsAndRunPackRules(CLIPackRulesCmdOptions options) + { + loggerFactory = options.GetLoggerFactory(); + var logger = loggerFactory.CreateLogger("Program"); + + if (string.IsNullOrEmpty(options.OutputFilePath)) + { + logger.LogError(MsgHelp.GetString(MsgHelp.ID.PACK_MISSING_OUTPUT_ARG)); + throw new OpException(MsgHelp.GetString(MsgHelp.ID.PACK_MISSING_OUTPUT_ARG)); + } + + CommonOutputChecks(options); + + return RunPackRulesCommand(options); + } + + private static int VerifyOutputArgsAndRunAnalyze(CLIAnalyzeCmdOptions options) + { + loggerFactory = options.GetLoggerFactory(); + + //analyze with html format limit checks + if (options.OutputFileFormat == "html") + { + options.OutputFilePath ??= "output.html"; + var extensionCheck = Path.GetExtension(options.OutputFilePath); + if (extensionCheck is not ".html" and not ".htm") + loggerFactory.CreateLogger("Program") + .LogInformation(MsgHelp.GetString(MsgHelp.ID.ANALYZE_HTML_EXTENSION)); + } + + if (CommonOutputChecks(options)) + return RunAnalyzeCommand(options); + return (int)Utils.ExitCode.CriticalError; + } + + /// + /// Checks that either output filepath is valid or console verbosity is not visible to ensure + /// some output can be achieved...other command specific inputs that are relevant to both CLI + /// and NuGet callers are checked by the commands themselves + /// + /// + private static bool CommonOutputChecks(CLICommandOptions options) + { + //validate requested format + var fileFormatArg = options.OutputFileFormat; + string[] validFormats = + { + "html", + "text", + "json", + "sarif" + }; + var logger = loggerFactory.CreateLogger("Program"); + string[] checkFormats; + if (options is CLIAnalyzeCmdOptions cliAnalyzeOptions) + { + checkFormats = validFormats; + fileFormatArg = cliAnalyzeOptions.OutputFileFormat; + } + else if (options is CLIPackRulesCmdOptions cliPackRulesOptions) + { + checkFormats = validFormats.Skip(2).Take(1).ToArray(); + fileFormatArg = cliPackRulesOptions.OutputFileFormat; + } + else + { + checkFormats = validFormats.Skip(1).Take(2).ToArray(); + } + + var isValidFormat = checkFormats.Any(v => v.Equals(fileFormatArg.ToLower())); + if (!isValidFormat) + { + logger.LogError(MsgHelp.GetString(MsgHelp.ID.CMD_INVALID_ARG_VALUE), "-f"); + return false; + } + + if (!string.IsNullOrEmpty(options.OutputFilePath) && !CanWritePath(options.OutputFilePath)) + { + logger.LogError(MsgHelp.GetString(MsgHelp.ID.CMD_INVALID_LOG_PATH), options.OutputFilePath); + return false; + } + + return true; + } + + /// + /// Ensure output file path can be written to + /// + /// + private static bool CanWritePath(string filePath) + { + try + { + File.WriteAllText(filePath, ""); //verify ability to write to location + } + catch (Exception) + { + return false; + } + + return true; + } + + + /// + /// This method gets a logging factory with Console disabled when the progress bar is enabled. Does not change the + /// original options. + /// + /// The original options. + /// A logging factory with adjusted settings. + private static ILoggerFactory GetAdjustedFactory(CLIAnalyzeCmdOptions cliOptions) + { + return new LogOptions + { + ConsoleVerbosityLevel = cliOptions.ConsoleVerbosityLevel, + LogFileLevel = cliOptions.LogFileLevel, + LogFilePath = cliOptions.LogFilePath, + DisableConsoleOutput = !cliOptions.NoShowProgressBar || cliOptions.DisableConsoleOutput + }.GetLoggerFactory(); + } + + private static (bool initialSuccess, bool backupSuccess, string? backupLocation) TryWriteResults( + ResultsWriter writer, AnalyzeResult analyzeResult, CLIAnalyzeCmdOptions cliOptions, ILogger logger) + { + try + { + writer.Write(analyzeResult, cliOptions); + return (true, false, null); + } + catch (Exception e) + { + logger.LogError("Exception hit while writing. {Type} : {Message}", e.GetType().Name, + e.Message); + analyzeResult.ResultCode = AnalyzeResult.ExitCode.CriticalError; + // If HTML + if (cliOptions.OutputFileFormat.Equals("html", StringComparison.InvariantCultureIgnoreCase)) { - checkFormats = validFormats; - fileFormatArg = cliAnalyzeOptions.OutputFileFormat; - } - else if (options is CLIPackRulesCmdOptions cliPackRulesOptions) - { - checkFormats = validFormats.Skip(2).Take(1).ToArray(); - fileFormatArg = cliPackRulesOptions.OutputFileFormat; + var alternatepath = "backupOutput.json"; + var optionsCopy = cliOptions with { OutputFileFormat = "json", OutputFilePath = alternatepath }; + try + { + writer.Write(analyzeResult, optionsCopy); + logger.LogInformation("Failed to write HTML report. Exported JSON instead at {AlternatePath}", + alternatepath); + return (false, true, alternatepath); + } + catch (Exception e2) + { + logger.LogError("Exception hit while writing backup of result object. {Type} : {Message}", + e2.GetType().Name, + e2.Message); + } } + } + + return (false, false, null); + } + + private static int RunAnalyzeCommand(CLIAnalyzeCmdOptions cliOptions) + { + // If the user manually specified -1 this means they also don't even want the snippet in sarif, so we respect that option + // Otherwise if we are outputting sarif we don't use any of the context information so we set context to 0, if not sarif leave it alone. + var isSarif = cliOptions.OutputFileFormat.Equals("sarif", StringComparison.InvariantCultureIgnoreCase); + var numContextLines = cliOptions.ContextLines == -1 ? cliOptions.ContextLines : + isSarif ? 0 : cliOptions.ContextLines; + // tagsOnly isn't compatible with sarif output, we choose to prioritize the choice of sarif. + var tagsOnly = !isSarif && cliOptions.TagsOnly; + var logger = cliOptions.GetLoggerFactory().CreateLogger("Program"); + if (!cliOptions.NoShowProgressBar) + { + if (string.IsNullOrEmpty(cliOptions.LogFilePath)) + logger.LogInformation( + "Progress bar is enabled so console output will be suppressed. No LogFilePath has been configured so you will not receive log messages"); else - { - checkFormats = validFormats.Skip(1).Take(2).ToArray(); - } - - bool isValidFormat = checkFormats.Any(v => v.Equals(fileFormatArg.ToLower())); - if (!isValidFormat) - { - logger.LogError(MsgHelp.GetString(MsgHelp.ID.CMD_INVALID_ARG_VALUE), "-f"); - return false; - } - - if (!string.IsNullOrEmpty(options.OutputFilePath) && !CanWritePath(options.OutputFilePath)) - { - logger.LogError(MsgHelp.GetString(MsgHelp.ID.CMD_INVALID_LOG_PATH), options.OutputFilePath); - return false; - } - - return true; + logger.LogInformation( + "Progress bar is enabled so console output will be suppressed. For log messages check the log at {LogPath}", + cliOptions.LogFilePath); } - /// - /// Ensure output file path can be written to - /// - /// - private static bool CanWritePath(string filePath) + var adjustedFactory = GetAdjustedFactory(cliOptions); + AnalyzeCommand command = new(new AnalyzeOptions { - try - { - File.WriteAllText(filePath, "");//verify ability to write to location - } - catch (Exception) - { - return false; - } - return true; + SourcePath = cliOptions.SourcePath ?? Array.Empty(), + CustomRulesPath = cliOptions.CustomRulesPath ?? "", + CustomCommentsPath = cliOptions.CustomCommentsPath, + CustomLanguagesPath = cliOptions.CustomLanguagesPath, + IgnoreDefaultRules = cliOptions.IgnoreDefaultRules, + ConfidenceFilters = cliOptions.ConfidenceFilters, + SeverityFilters = cliOptions.SeverityFilters, + FilePathExclusions = cliOptions.FilePathExclusions, + SingleThread = cliOptions.SingleThread, + NoShowProgress = cliOptions.NoShowProgressBar, + FileTimeOut = cliOptions.FileTimeOut, + ProcessingTimeOut = cliOptions.ProcessingTimeOut, + ContextLines = numContextLines, + ScanUnknownTypes = cliOptions.ScanUnknownTypes, + TagsOnly = tagsOnly, + NoFileMetadata = cliOptions.NoFileMetadata, + AllowAllTagsInBuildFiles = cliOptions.AllowAllTagsInBuildFiles, + MaxNumMatchesPerTag = cliOptions.MaxNumMatchesPerTag, + DisableCrawlArchives = cliOptions.DisableArchiveCrawling, + EnumeratingTimeout = cliOptions.EnumeratingTimeout, + DisableCustomRuleVerification = cliOptions.DisableCustomRuleValidation, + DisableRequireUniqueIds = cliOptions.DisableRequireUniqueIds, + SuccessErrorCodeOnNoMatches = cliOptions.SuccessErrorCodeOnNoMatches, + RequireMustMatch = cliOptions.RequireMustMatch, + RequireMustNotMatch = cliOptions.RequireMustNotMatch + }, adjustedFactory); + + var analyzeResult = command.GetResult(); + + ResultsWriter writer = new(loggerFactory); + if (cliOptions.NoShowProgressBar) + { + TryWriteResults(writer, analyzeResult, cliOptions, logger); } - - - - /// - /// This method gets a logging factory with Console disabled when the progress bar is enabled. Does not change the original options. - /// - /// The original options. - /// A logging factory with adjusted settings. - private static ILoggerFactory GetAdjustedFactory(CLIAnalyzeCmdOptions cliOptions) + else { - return new LogOptions() + ProgressBarOptions outputWriterBarOptions = new() { - ConsoleVerbosityLevel = cliOptions.ConsoleVerbosityLevel, - LogFileLevel = cliOptions.LogFileLevel, - LogFilePath = cliOptions.LogFilePath, - DisableConsoleOutput = !cliOptions.NoShowProgressBar || cliOptions.DisableConsoleOutput - }.GetLoggerFactory(); - } + ForegroundColor = ConsoleColor.Yellow, + ForegroundColorDone = ConsoleColor.DarkGreen, + BackgroundColor = ConsoleColor.DarkGray, + ForegroundColorError = ConsoleColor.Red, + BackgroundCharacter = '\u2593', + DisableBottomPercentage = true + }; - private static (bool initialSuccess, bool backupSuccess, string? backupLocation) TryWriteResults(ResultsWriter writer, AnalyzeResult analyzeResult, CLIAnalyzeCmdOptions cliOptions, ILogger logger) - { - try + using (IndeterminateProgressBar pbar = new("Writing Result Files.", outputWriterBarOptions)) { - writer.Write(analyzeResult, cliOptions); - return (true, false, null); - } - catch (Exception e) - { - logger.LogError("Exception hit while writing. {Type} : {Message}", e.GetType().Name, - e.Message); - analyzeResult.ResultCode = AnalyzeResult.ExitCode.CriticalError; - // If HTML - if (cliOptions.OutputFileFormat.Equals("html",StringComparison.InvariantCultureIgnoreCase)) + var done = false; + (var initialSuccess, var backupSuccess, string? alternatePath) = (false, false, null); + _ = Task.Factory.StartNew(() => { - string alternatepath = "backupOutput.json"; - var optionsCopy = cliOptions with {OutputFileFormat = "json", OutputFilePath = alternatepath}; - try - { - writer.Write(analyzeResult, optionsCopy); - logger.LogInformation("Failed to write HTML report. Exported JSON instead at {AlternatePath}", alternatepath); - return (false, true, alternatepath); - } - catch (Exception e2) - { - logger.LogError("Exception hit while writing backup of result object. {Type} : {Message}", e2.GetType().Name, - e2.Message); - } - } - } - return (false, false, null); - } + (initialSuccess, backupSuccess, alternatePath) = TryWriteResults(writer, analyzeResult, cliOptions, + adjustedFactory.CreateLogger("RunAnalyzeCommand")); + done = true; + }); - private static int RunAnalyzeCommand(CLIAnalyzeCmdOptions cliOptions) - { - // If the user manually specified -1 this means they also don't even want the snippet in sarif, so we respect that option - // Otherwise if we are outputting sarif we don't use any of the context information so we set context to 0, if not sarif leave it alone. - bool isSarif = cliOptions.OutputFileFormat.Equals("sarif", StringComparison.InvariantCultureIgnoreCase); - int numContextLines = cliOptions.ContextLines == -1 ? cliOptions.ContextLines : isSarif ? 0 : cliOptions.ContextLines; - // tagsOnly isn't compatible with sarif output, we choose to prioritize the choice of sarif. - bool tagsOnly = !isSarif && cliOptions.TagsOnly; - var logger = cliOptions.GetLoggerFactory().CreateLogger("Program"); - if (!cliOptions.NoShowProgressBar) - { - if (string.IsNullOrEmpty(cliOptions.LogFilePath)) - { - logger.LogInformation("Progress bar is enabled so console output will be suppressed. No LogFilePath has been configured so you will not receive log messages"); - } - else - { - logger.LogInformation("Progress bar is enabled so console output will be suppressed. For log messages check the log at {LogPath}", cliOptions.LogFilePath); - } - } - ILoggerFactory adjustedFactory = GetAdjustedFactory(cliOptions); - AnalyzeCommand command = new(new AnalyzeOptions() - { - SourcePath = cliOptions.SourcePath ?? Array.Empty(), - CustomRulesPath = cliOptions.CustomRulesPath ?? "", - CustomCommentsPath = cliOptions.CustomCommentsPath, - CustomLanguagesPath = cliOptions.CustomLanguagesPath, - IgnoreDefaultRules = cliOptions.IgnoreDefaultRules, - ConfidenceFilters = cliOptions.ConfidenceFilters, - SeverityFilters = cliOptions.SeverityFilters, - FilePathExclusions = cliOptions.FilePathExclusions, - SingleThread = cliOptions.SingleThread, - NoShowProgress = cliOptions.NoShowProgressBar, - FileTimeOut = cliOptions.FileTimeOut, - ProcessingTimeOut = cliOptions.ProcessingTimeOut, - ContextLines = numContextLines, - ScanUnknownTypes = cliOptions.ScanUnknownTypes, - TagsOnly = tagsOnly, - NoFileMetadata = cliOptions.NoFileMetadata, - AllowAllTagsInBuildFiles = cliOptions.AllowAllTagsInBuildFiles, - MaxNumMatchesPerTag = cliOptions.MaxNumMatchesPerTag, - DisableCrawlArchives = cliOptions.DisableArchiveCrawling, - EnumeratingTimeout = cliOptions.EnumeratingTimeout, - DisableCustomRuleVerification = cliOptions.DisableCustomRuleValidation, - DisableRequireUniqueIds = cliOptions.DisableRequireUniqueIds, - SuccessErrorCodeOnNoMatches = cliOptions.SuccessErrorCodeOnNoMatches, - RequireMustMatch = cliOptions.RequireMustMatch, - RequireMustNotMatch = cliOptions.RequireMustNotMatch - }, adjustedFactory); + while (!done) Thread.Sleep(100); - AnalyzeResult analyzeResult = command.GetResult(); - - ResultsWriter writer = new(loggerFactory); - if (cliOptions.NoShowProgressBar) - { - TryWriteResults(writer, analyzeResult, cliOptions, logger); - } - else - { - ProgressBarOptions outputWriterBarOptions = new() - { - ForegroundColor = ConsoleColor.Yellow, - ForegroundColorDone = ConsoleColor.DarkGreen, - BackgroundColor = ConsoleColor.DarkGray, - ForegroundColorError = ConsoleColor.Red, - BackgroundCharacter = '\u2593', - DisableBottomPercentage = true - }; - - using (IndeterminateProgressBar pbar = new("Writing Result Files.", outputWriterBarOptions)) - { - var done = false; - (bool initialSuccess, bool backupSuccess, string? alternatePath) = (false, false, null); - _ = Task.Factory.StartNew(() => - { - (initialSuccess, backupSuccess, alternatePath) = TryWriteResults(writer, analyzeResult, cliOptions, adjustedFactory.CreateLogger("RunAnalyzeCommand")); - done = true; - }); - - while (!done) - { - Thread.Sleep(100); - } - - pbar.ObservedError = !initialSuccess; - pbar.Message = !initialSuccess ? - (backupSuccess ? $"Failed to write results. Wrote backup results to {alternatePath}." : "Failed to write Results and failed to write Backup Results.") : $"Results written to {cliOptions.OutputFilePath}."; - pbar.Finished(); - } - Console.Write(Environment.NewLine); + pbar.ObservedError = !initialSuccess; + pbar.Message = !initialSuccess + ? backupSuccess + ? $"Failed to write results. Wrote backup results to {alternatePath}." + : "Failed to write Results and failed to write Backup Results." + : $"Results written to {cliOptions.OutputFilePath}."; + pbar.Finished(); } - return (int)analyzeResult.ResultCode; + Console.Write(Environment.NewLine); } - private static int RunTagDiffCommand(CLITagDiffCmdOptions cliOptions) + return (int)analyzeResult.ResultCode; + } + + private static int RunTagDiffCommand(CLITagDiffCmdOptions cliOptions) + { + TagDiffCommand command = new(new TagDiffOptions { - TagDiffCommand command = new(new TagDiffOptions() - { - SourcePath1 = cliOptions.SourcePath1, - SourcePath2 = cliOptions.SourcePath2, - CustomRulesPath = cliOptions.CustomRulesPath, - CustomCommentsPath = cliOptions.CustomCommentsPath, - CustomLanguagesPath = cliOptions.CustomLanguagesPath, - IgnoreDefaultRules = cliOptions.IgnoreDefaultRules, - FilePathExclusions = cliOptions.FilePathExclusions, - TestType = cliOptions.TestType, - ConfidenceFilters = cliOptions.ConfidenceFilters, - SeverityFilters = cliOptions.SeverityFilters, - FileTimeOut = cliOptions.FileTimeOut, - ProcessingTimeOut = cliOptions.ProcessingTimeOut, - ScanUnknownTypes = cliOptions.ScanUnknownTypes, - SingleThread = cliOptions.SingleThread, - DisableCustomRuleValidation = cliOptions.DisableCustomRuleValidation, - DisableRequireUniqueIds = cliOptions.DisableRequireUniqueIds, - SuccessErrorCodeOnNoMatches = cliOptions.SuccessErrorCodeOnNoMatches, - RequireMustMatch = cliOptions.RequireMustMatch, - RequireMustNotMatch = cliOptions.RequireMustNotMatch - }, loggerFactory); + SourcePath1 = cliOptions.SourcePath1, + SourcePath2 = cliOptions.SourcePath2, + CustomRulesPath = cliOptions.CustomRulesPath, + CustomCommentsPath = cliOptions.CustomCommentsPath, + CustomLanguagesPath = cliOptions.CustomLanguagesPath, + IgnoreDefaultRules = cliOptions.IgnoreDefaultRules, + FilePathExclusions = cliOptions.FilePathExclusions, + TestType = cliOptions.TestType, + ConfidenceFilters = cliOptions.ConfidenceFilters, + SeverityFilters = cliOptions.SeverityFilters, + FileTimeOut = cliOptions.FileTimeOut, + ProcessingTimeOut = cliOptions.ProcessingTimeOut, + ScanUnknownTypes = cliOptions.ScanUnknownTypes, + SingleThread = cliOptions.SingleThread, + DisableCustomRuleValidation = cliOptions.DisableCustomRuleValidation, + DisableRequireUniqueIds = cliOptions.DisableRequireUniqueIds, + SuccessErrorCodeOnNoMatches = cliOptions.SuccessErrorCodeOnNoMatches, + RequireMustMatch = cliOptions.RequireMustMatch, + RequireMustNotMatch = cliOptions.RequireMustNotMatch + }, loggerFactory); - TagDiffResult tagDiffResult = command.GetResult(); + var tagDiffResult = command.GetResult(); - ResultsWriter writer = new(loggerFactory); - writer.Write(tagDiffResult, cliOptions); + ResultsWriter writer = new(loggerFactory); + writer.Write(tagDiffResult, cliOptions); - return (int)tagDiffResult.ResultCode; - } - - private static int RunExportTagsCommand(CLIExportTagsCmdOptions cliOptions) + return (int)tagDiffResult.ResultCode; + } + + private static int RunExportTagsCommand(CLIExportTagsCmdOptions cliOptions) + { + ExportTagsCommand command = new(new ExportTagsOptions { - ExportTagsCommand command = new(new ExportTagsOptions() - { - IgnoreDefaultRules = cliOptions.IgnoreDefaultRules, - CustomRulesPath = cliOptions.CustomRulesPath, - }, loggerFactory); + IgnoreDefaultRules = cliOptions.IgnoreDefaultRules, + CustomRulesPath = cliOptions.CustomRulesPath + }, loggerFactory); - ExportTagsResult exportTagsResult = command.GetResult(); + var exportTagsResult = command.GetResult(); - ResultsWriter writer = new(loggerFactory); - writer.Write(exportTagsResult, cliOptions); + ResultsWriter writer = new(loggerFactory); + writer.Write(exportTagsResult, cliOptions); - return (int)exportTagsResult.ResultCode; - } + return (int)exportTagsResult.ResultCode; + } - private static int RunVerifyRulesCommand(CLIVerifyRulesCmdOptions cliOptions) + private static int RunVerifyRulesCommand(CLIVerifyRulesCmdOptions cliOptions) + { + VerifyRulesCommand command = new(new VerifyRulesOptions { - VerifyRulesCommand command = new(new VerifyRulesOptions() - { - VerifyDefaultRules = cliOptions.VerifyDefaultRules, - CustomRulesPath = cliOptions.CustomRulesPath, - CustomCommentsPath = cliOptions.CustomCommentsPath, - CustomLanguagesPath = cliOptions.CustomLanguagesPath, - DisableRequireUniqueIds = cliOptions.DisableRequireUniqueIds, - RequireMustMatch = cliOptions.RequireMustMatch, - RequireMustNotMatch = cliOptions.RequireMustNotMatch - }, loggerFactory); + VerifyDefaultRules = cliOptions.VerifyDefaultRules, + CustomRulesPath = cliOptions.CustomRulesPath, + CustomCommentsPath = cliOptions.CustomCommentsPath, + CustomLanguagesPath = cliOptions.CustomLanguagesPath, + DisableRequireUniqueIds = cliOptions.DisableRequireUniqueIds, + RequireMustMatch = cliOptions.RequireMustMatch, + RequireMustNotMatch = cliOptions.RequireMustNotMatch + }, loggerFactory); - VerifyRulesResult exportTagsResult = command.GetResult(); + var verifyRulesResult = command.GetResult(); - ResultsWriter writer = new(loggerFactory); - writer.Write(exportTagsResult, cliOptions); + ResultsWriter writer = new(loggerFactory); + writer.Write(verifyRulesResult, cliOptions); - return (int)exportTagsResult.ResultCode; - } + return (int)verifyRulesResult.ResultCode; + } - private static int RunPackRulesCommand(CLIPackRulesCmdOptions cliOptions) + private static int RunPackRulesCommand(CLIPackRulesCmdOptions cliOptions) + { + PackRulesCommand command = new(new PackRulesOptions { - PackRulesCommand command = new(new PackRulesOptions() - { - CustomRulesPath = cliOptions.CustomRulesPath, - CustomCommentsPath = cliOptions.CustomCommentsPath, - CustomLanguagesPath = cliOptions.CustomLanguagesPath, - PackEmbeddedRules = cliOptions.PackEmbeddedRules - }, loggerFactory); + CustomRulesPath = cliOptions.CustomRulesPath, + CustomCommentsPath = cliOptions.CustomCommentsPath, + CustomLanguagesPath = cliOptions.CustomLanguagesPath, + PackEmbeddedRules = cliOptions.PackEmbeddedRules, + DisableRequireUniqueIds = cliOptions.DisableRequireUniqueIds, + RequireMustMatch = cliOptions.RequireMustMatch, + RequireMustNotMatch = cliOptions.RequireMustNotMatch + }, loggerFactory); - PackRulesResult exportTagsResult = command.GetResult(); + var packRulesResult = command.GetResult(); - ResultsWriter writer = new(loggerFactory); - writer.Write(exportTagsResult, cliOptions); - - return (int)exportTagsResult.ResultCode; - } + ResultsWriter writer = new(loggerFactory); + writer.Write(packRulesResult, cliOptions); + return (int)packRulesResult.ResultCode; } } \ No newline at end of file diff --git a/AppInspector.CLI/Properties/PublishProfiles/FolderProfile.pubxml b/AppInspector.CLI/Properties/PublishProfiles/FolderProfile.pubxml index 03f13ad..d728993 100644 --- a/AppInspector.CLI/Properties/PublishProfiles/FolderProfile.pubxml +++ b/AppInspector.CLI/Properties/PublishProfiles/FolderProfile.pubxml @@ -3,15 +3,15 @@ https://go.microsoft.com/fwlink/?LinkID=208121. --> - - FileSystem - Release - Any CPU - netcoreapp3.1 - bin\Release\netcoreapp3.1\publish\ - win-x86 - false - False - False - + + FileSystem + Release + Any CPU + netcoreapp3.1 + bin\Release\netcoreapp3.1\publish\ + win-x86 + false + False + False + \ No newline at end of file diff --git a/AppInspector.CLI/ResultsWriter.cs b/AppInspector.CLI/ResultsWriter.cs index 66ac315..ef78679 100644 --- a/AppInspector.CLI/ResultsWriter.cs +++ b/AppInspector.CLI/ResultsWriter.cs @@ -1,92 +1,92 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -namespace Microsoft.ApplicationInspector.CLI +using System; +using System.IO; +using Microsoft.ApplicationInspector.Commands; +using Microsoft.ApplicationInspector.Common; +using Microsoft.Extensions.Logging; + +namespace Microsoft.ApplicationInspector.CLI; + +/// +/// Wrapper for CLI only output arg validation which may be unique to a command +/// and for allocating the correct writter type and format writter object +/// +public class ResultsWriter { - using Microsoft.ApplicationInspector.Commands; - using Microsoft.ApplicationInspector.Common; - using Microsoft.Extensions.Logging; - using System; - using System.IO; + private readonly ILoggerFactory _loggerFactory; + + private readonly ILogger _logger; + + public ResultsWriter(ILoggerFactory loggerFactory) + { + _logger = loggerFactory.CreateLogger(); + _loggerFactory = loggerFactory; + } + + public void Write(Result result, CLICommandOptions options) + { + var writerFactory = new WriterFactory(_loggerFactory); + var writer = writerFactory.GetWriter(options); + string commandCompletedMsg; + + //perform type checking and assign final msg string + if (result is TagDiffResult) + { + commandCompletedMsg = "Tag Diff"; + } + else if (result is ExportTagsResult) + { + commandCompletedMsg = "Export Tags"; + } + else if (result is VerifyRulesResult) + { + commandCompletedMsg = "Verify Rules"; + } + else if (result is PackRulesResult) + { + commandCompletedMsg = "Pack Rules"; + } + else if (result is AnalyzeResult analyzeResult && + options is CLIAnalyzeCmdOptions cLIAnalyzeCmdOptions) //special handling for html format + { + commandCompletedMsg = "Analyze"; + + //additional prechecks required for analyze html format + if (writer is AnalyzeHtmlWriter) + { + var MAX_HTML_REPORT_FILE_SIZE = 1024 * 1000 * 3; //warn about potential slow rendering + + writer.WriteResults(analyzeResult, cLIAnalyzeCmdOptions); + + //post checks + if (options.OutputFilePath is not null && File.Exists(options.OutputFilePath) && + new FileInfo(options.OutputFilePath).Length > MAX_HTML_REPORT_FILE_SIZE) + _logger.LogInformation(MsgHelp.GetString(MsgHelp.ID.ANALYZE_REPORTSIZE_WARN)); + + Finalize(writer, "Analyze"); + return; + } + } + else + { + throw new Exception("Unrecognized object types for write results"); + } + + //general for all but analyze html format + writer.WriteResults(result, options); + Finalize(writer, commandCompletedMsg); + } /// - /// Wrapper for CLI only output arg validation which may be unique to a command - /// and for allocating the correct writter type and format writter object + /// Allow for final actions if even and common file path notice to console + /// Most Writer.Write operations flushandclose the stream automatically but .Flush /// - public class ResultsWriter + /// + /// + internal void Finalize(CommandResultsWriter? outputWriter, string commandName) { - public ResultsWriter(ILoggerFactory loggerFactory) - { - _logger = loggerFactory.CreateLogger(); - _loggerFactory = loggerFactory; - } - - private ILogger _logger; - private readonly ILoggerFactory _loggerFactory; - - public void Write(Result result, CLICommandOptions options) - { - WriterFactory writerFactory = new WriterFactory(_loggerFactory); - CommandResultsWriter? writer = writerFactory.GetWriter(options); - string commandCompletedMsg; - - //perform type checking and assign final msg string - if (result is TagDiffResult) - { - commandCompletedMsg = "Tag Diff"; - } - else if (result is ExportTagsResult) - { - commandCompletedMsg = "Export Tags"; - } - else if (result is VerifyRulesResult) - { - commandCompletedMsg = "Verify Rules"; - } - else if (result is PackRulesResult) - { - commandCompletedMsg = "Pack Rules"; - } - else if (result is AnalyzeResult analyzeResult && options is CLIAnalyzeCmdOptions cLIAnalyzeCmdOptions) //special handling for html format - { - commandCompletedMsg = "Analyze"; - - //additional prechecks required for analyze html format - if (writer is AnalyzeHtmlWriter) - { - int MAX_HTML_REPORT_FILE_SIZE = 1024 * 1000 * 3; //warn about potential slow rendering - - writer.WriteResults(analyzeResult, cLIAnalyzeCmdOptions); - - //post checks - if (options.OutputFilePath is not null && File.Exists(options.OutputFilePath) && new FileInfo(options.OutputFilePath).Length > MAX_HTML_REPORT_FILE_SIZE) - { - _logger.LogInformation(MsgHelp.GetString(MsgHelp.ID.ANALYZE_REPORTSIZE_WARN)); - } - - Finalize(writer, "Analyze"); - return; - } - } - else - { - throw new Exception("Unrecognized object types for write results"); - } - - //general for all but analyze html format - writer.WriteResults(result, options); - Finalize(writer, commandCompletedMsg); - } - - /// - /// Allow for final actions if even and common file path notice to console - /// Most Writer.Write operations flushandclose the stream automatically but .Flush - /// - /// - /// - internal void Finalize(CommandResultsWriter? outputWriter, string commandName) - { - _logger.LogInformation(MsgHelp.FormatString(MsgHelp.ID.CMD_COMPLETED, commandName)); - } + _logger.LogInformation(MsgHelp.FormatString(MsgHelp.ID.CMD_COMPLETED, commandName)); } } \ No newline at end of file diff --git a/AppInspector.CLI/TagInfo.cs b/AppInspector.CLI/TagInfo.cs index 91370c6..c17f627 100644 --- a/AppInspector.CLI/TagInfo.cs +++ b/AppInspector.CLI/TagInfo.cs @@ -1,160 +1,150 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -namespace Microsoft.ApplicationInspector.Commands +using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using DotLiquid; +using Microsoft.ApplicationInspector.RulesEngine; +using Newtonsoft.Json; + +namespace Microsoft.ApplicationInspector.Commands; + +/// +/// Root parent for tag group preferences file and used in Writers\AnalyzeHtmlWriter.cs +/// +public class TagCategory { - using DotLiquid; - using Newtonsoft.Json; - using System; - using System.Collections.Generic; - using System.Text.RegularExpressions; - - /// - /// Root parent for tag group preferences file and used in Writers\AnalyzeHtmlWriter.cs - /// - public class TagCategory + public enum tagInfoType { - public enum tagInfoType { uniqueTags, allTags }; + uniqueTags, + allTags + } - [JsonProperty(PropertyName = "type")] - public tagInfoType Type; + [JsonProperty(PropertyName = "type")] public tagInfoType Type; - [JsonProperty(PropertyName = "categoryName")] - public string? Name { get; set; } + public TagCategory() + { + Groups = new List(); + } - [JsonProperty(PropertyName = "groups")] - public List? Groups { get; set; } + [JsonProperty(PropertyName = "categoryName")] + public string? Name { get; set; } - public TagCategory() + [JsonProperty(PropertyName = "groups")] + public List? Groups { get; set; } +} + +/// +/// Used to read customizable preference for Profile page e.g. rules\profile\profile.json +/// +public class TagGroup : Drop +{ + public TagGroup() + { + Patterns = new List(); + } + + [JsonProperty(PropertyName = "title")] public string? Title { get; set; } + + [JsonIgnore] public string? IconURL { get; set; } + + [JsonProperty(PropertyName = "dataRef")] + public string? DataRef { get; set; } + + [JsonProperty(PropertyName = "patterns")] + public List? Patterns { get; set; } +} + +public class TagSearchPattern : Drop +{ + private Regex? _expression; + private string _searchPattern = ""; + + [JsonProperty(PropertyName = "searchPattern")] + public string SearchPattern + { + get => _searchPattern; + set { - Groups = new List(); + _searchPattern = value; + _expression = null; } } - /// - /// Used to read customizable preference for Profile page e.g. rules\profile\profile.json - /// - public class TagGroup : Drop + public Regex Expression { - [JsonProperty(PropertyName = "title")] - public string? Title { get; set; } - - [JsonIgnore] - public string? IconURL { get; set; } - - [JsonProperty(PropertyName = "dataRef")] - public string? DataRef { get; set; } - - [JsonProperty(PropertyName = "patterns")] - public List? Patterns { get; set; } - - public TagGroup() + get { - Patterns = new List(); + if (_expression == null) + _expression = new Regex(SearchPattern, RegexOptions.Compiled | RegexOptions.IgnoreCase); + return _expression; } } - public class TagSearchPattern : Drop + [JsonProperty(PropertyName = "displayName")] + public string? DisplayName { get; set; } + + [JsonProperty(PropertyName = "detectedIcon")] + public string? DetectedIcon { get; set; } = "fas fa-cat"; //default + + [JsonProperty(PropertyName = "notDetectedIcon")] + public string? NotDetectedIcon { get; set; } + + [JsonProperty(PropertyName = "detected")] + public bool Detected { get; set; } + + [JsonProperty(PropertyName = "details")] + public string Details { - private string _searchPattern = ""; - private Regex? _expression; - - [JsonProperty(PropertyName = "searchPattern")] - public string SearchPattern + get { - get - { - return _searchPattern; - } - set - { - _searchPattern = value; - _expression = null; - } + var result = Detected ? "View" : "N/A"; + return result; } - - public static bool ShouldSerializeExpression() - { - return false; - } - - public Regex Expression - { - get - { - if (_expression == null) - { - _expression = new Regex(SearchPattern, RegexOptions.Compiled | RegexOptions.IgnoreCase); - } - return _expression; - } - } - - [JsonProperty(PropertyName = "displayName")] - public string? DisplayName { get; set; } - - [JsonProperty(PropertyName = "detectedIcon")] - public string? DetectedIcon { get; set; } = "fas fa-cat";//default - - [JsonProperty(PropertyName = "notDetectedIcon")] - public string? NotDetectedIcon { get; set; } - - [JsonProperty(PropertyName = "detected")] - public bool Detected { get; set; } - - [JsonProperty(PropertyName = "details")] - public string Details - { - get - { - string result = Detected ? "View" : "N/A"; - return result; - } - } - - [JsonProperty(PropertyName = "confidence")] - public string Confidence { get; set; } = "Medium"; } - /// - /// Primary use is development of lists of tags with specific group or pattern properties in reporting - /// - public class TagInfo : Drop + [JsonProperty(PropertyName = "confidence")] + public string Confidence { get; set; } = "Medium"; + + public static bool ShouldSerializeExpression() { - [JsonProperty(PropertyName = "tag")] - public string? Tag { get; set; } + return false; + } +} - [JsonProperty(PropertyName = "displayName")] - public string? ShortTag { get; set; } +/// +/// Primary use is development of lists of tags with specific group or pattern properties in reporting +/// +public class TagInfo : Drop +{ + private string _confidence = "Medium"; - [JsonIgnore] - public string? StatusIcon { get; set; } + [JsonProperty(PropertyName = "tag")] public string? Tag { get; set; } - private string _confidence = "Medium"; + [JsonProperty(PropertyName = "displayName")] + public string? ShortTag { get; set; } - [JsonProperty(PropertyName = "confidence")] - public string Confidence + [JsonIgnore] public string? StatusIcon { get; set; } + + [JsonProperty(PropertyName = "confidence")] + public string Confidence + { + get => _confidence; + set { - get => _confidence; - set - { - if (Enum.TryParse(value, true, out RulesEngine.Confidence test)) - { - _confidence = value; - } - } + if (Enum.TryParse(value, true, out Confidence test)) _confidence = value; } - - [JsonProperty(PropertyName = "severity")] - public string Severity { get; set; } = "Moderate"; - - [JsonProperty(PropertyName = "detected")] - public bool Detected { get; set; } } - public class TagException - { - [JsonProperty(PropertyName = "tag")] - public string? Tag { get; set; } - } + [JsonProperty(PropertyName = "severity")] + public string Severity { get; set; } = "Moderate"; + + [JsonProperty(PropertyName = "detected")] + public bool Detected { get; set; } +} + +public class TagException +{ + [JsonProperty(PropertyName = "tag")] public string? Tag { get; set; } } \ No newline at end of file diff --git a/AppInspector.CLI/Writers/AnalyzeHtmlWriter.cs b/AppInspector.CLI/Writers/AnalyzeHtmlWriter.cs index 3d46efb..62d4e76 100644 --- a/AppInspector.CLI/Writers/AnalyzeHtmlWriter.cs +++ b/AppInspector.CLI/Writers/AnalyzeHtmlWriter.cs @@ -1,665 +1,601 @@ // Copyright (C) Microsoft. All rights reserved. // Licensed under the MIT License. See LICENSE.txt in the project root for license information. -namespace Microsoft.ApplicationInspector.CLI +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Reflection; +using System.Text; +using System.Text.RegularExpressions; +using DotLiquid; +using DotLiquid.FileSystems; +using Microsoft.ApplicationInspector.Commands; +using Microsoft.ApplicationInspector.Common; +using Microsoft.ApplicationInspector.RulesEngine; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Newtonsoft.Json; + +namespace Microsoft.ApplicationInspector.CLI; + +public class AnalyzeHtmlWriter : CommandResultsWriter { - using DotLiquid; - using DotLiquid.FileSystems; - using Microsoft.ApplicationInspector.Common; - using Microsoft.ApplicationInspector.Commands; - using Microsoft.ApplicationInspector.RulesEngine; - using Newtonsoft.Json; - using System; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Reflection; - using System.Text; - using System.Text.RegularExpressions; - using Microsoft.Extensions.Logging.Abstractions; - using Microsoft.Extensions.Logging; + private readonly ILogger _logger; + private AnalyzeResult? _analyzeResult; - public class AnalyzeHtmlWriter : CommandResultsWriter + private MetaData? _appMetaData; + + public AnalyzeHtmlWriter(TextWriter textWriter, ILoggerFactory? loggerFactory = null) : base(textWriter) { - private readonly ILogger _logger; + _logger = loggerFactory?.CreateLogger() ?? NullLogger.Instance; + KeyedTagInfoLists = new Dictionary>(); + KeyedSortedTagInfoLists = new Dictionary>(); + } - public Dictionary> KeyedTagInfoLists { get; } = new Dictionary>(); + public Dictionary> KeyedTagInfoLists { get; } = new(); - public Dictionary> KeyedSortedTagInfoLists { get; } = new Dictionary>(); + public Dictionary> KeyedSortedTagInfoLists { get; } = new(); - public List? TagGroupPreferences { get; set; }//read preferred list of groups and tags for profile / features page + public List? + TagGroupPreferences { get; set; } //read preferred list of groups and tags for profile / features page - private MetaData? _appMetaData; - private AnalyzeResult? _analyzeResult; + /// + /// Pre: AnalyzeCommand GetResults created and populated from RulesEngine + /// + /// + /// + /// + public override void WriteResults(Result result, CLICommandOptions cLICommandOptions, bool autoClose = true) + { + //recover metadata results from prior analyzecommand GetResults() + _analyzeResult = (AnalyzeResult)result; + _appMetaData = _analyzeResult.Metadata; - public AnalyzeHtmlWriter(TextWriter textWriter, ILoggerFactory? loggerFactory = null) : base(textWriter) + PopulateTagGroups(); + WriteHtmlResult(); + } + + private void WriteHtmlResult() + { + RenderResultsSafeforHTML(); + + //Grab any local css and js files that are needed i.e. don't have hosted URL's or are proprietary + var allCSS = ""); + + RegisterSafeType(typeof(MetaData)); + + //Prepare data for use in appinspector.js and html partials resources + var htmlTemplate = Template.Parse(htmlTemplateText); + var data = new Dictionary(); + data["MetaData"] = _appMetaData ?? new MetaData("", ""); + + var hashData = new Hash(); + string? jsonData; + try { - _logger = loggerFactory?.CreateLogger() ?? NullLogger.Instance; - KeyedTagInfoLists = new Dictionary>(); - KeyedSortedTagInfoLists = new Dictionary>(); + jsonData = JsonConvert.SerializeObject(data); + } + catch (Exception e) + { + _logger.LogError( + "Failed to write HTML report. Failed to serialize JSON representation of results in memory. {Type} : {Message}", + e.GetType().Name, e.Message); + throw; } - /// - /// Pre: AnalyzeCommand GetResults created and populated from RulesEngine - /// - /// - /// - /// - public override void WriteResults(Result result, CLICommandOptions cLICommandOptions, bool autoClose = true) - { - //recover metadata results from prior analyzecommand GetResults() - _analyzeResult = (AnalyzeResult)result; - _appMetaData = _analyzeResult.Metadata; + hashData["json"] = jsonData; //json serialization required for [js] access to objects + hashData["application_version"] = Utils.GetVersionString(); - PopulateTagGroups(); - WriteHtmlResult(); - } + //add dynamic sets of groups of taginfo read from preferences for Profile page + var tagGroupList = GetCategoryTagGroups("profile"); + hashData["groups"] = tagGroupList; - private void WriteHtmlResult() - { - RenderResultsSafeforHTML(); + //add summary values for sorted tags lists of taginfo + foreach (var outerKey in KeyedSortedTagInfoLists.Keys) + hashData.Add(outerKey, KeyedSortedTagInfoLists[outerKey]); - //Grab any local css and js files that are needed i.e. don't have hosted URL's or are proprietary - string allCSS = ""); + private void RegisterSafeType(Type type) + { + Template.RegisterSafeType(type, t => t.ToString()); + Template.RegisterSafeType(type, type.GetMembers(BindingFlags.Instance).Select(e => e.Name).ToArray()); + } - RegisterSafeType(typeof(MetaData)); + private string MergeResourceFiles(string inputPath) + { + StringBuilder stringBuilder = new(); - //Prepare data for use in appinspector.js and html partials resources - var htmlTemplate = Template.Parse(htmlTemplateText); - var data = new Dictionary(); - data["MetaData"] = _appMetaData ?? new MetaData("", ""); - - var hashData = new Hash(); - string? jsonData; + if (Directory.Exists(inputPath)) try { - jsonData = JsonConvert.SerializeObject(data); - } - catch (Exception e) - { - _logger.LogError("Failed to write HTML report. Failed to serialize JSON representation of results in memory. {Type} : {Message}", e.GetType().Name, e.Message); - throw; - } - hashData["json"] = jsonData;//json serialization required for [js] access to objects - hashData["application_version"] = Utils.GetVersionString(); - - //add dynamic sets of groups of taginfo read from preferences for Profile page - List tagGroupList = GetCategoryTagGroups("profile"); - hashData["groups"] = tagGroupList; - - //add summary values for sorted tags lists of taginfo - foreach (string outerKey in KeyedSortedTagInfoLists.Keys) - { - hashData.Add(outerKey, KeyedSortedTagInfoLists[outerKey]); - } - - hashData["cputargets"] = _appMetaData?.CPUTargets; - hashData["apptypes"] = _appMetaData?.AppTypes ?? new List(); - hashData["packagetypes"] = _appMetaData?.PackageTypes ?? new List(); - hashData["ostargets"] = _appMetaData?.OSTargets ?? new List(); - hashData["outputs"] = _appMetaData?.Outputs ?? new List(); - hashData["filetypes"] = _appMetaData?.FileExtensions ?? new List(); - hashData["tagcounters"] = ConvertTagCounters(_appMetaData?.TagCounters ?? new List()); - - //final render and close - var htmlResult = htmlTemplate.Render(hashData); - TextWriter?.Write(htmlResult); - FlushAndClose(); - } - - private void RegisterSafeType(Type type) - { - Template.RegisterSafeType(type, (t) => t.ToString()); - Template.RegisterSafeType(type, type.GetMembers(BindingFlags.Instance).Select((e) => e.Name).ToArray()); - } - - private string MergeResourceFiles(string inputPath) - { - StringBuilder stringBuilder = new(); - - if (Directory.Exists(inputPath)) - { - try - { - IEnumerable srcfileList = Directory.EnumerateFiles(inputPath, "*.*", SearchOption.AllDirectories); - if (!srcfileList.Any()) - { - throw new OpException(MsgHelp.FormatString(MsgHelp.ID.CMD_INVALID_FILE_OR_DIR, inputPath)); - } - - foreach (string fileName in srcfileList) - { - stringBuilder.Append(string.Format("\n\n/*FILE: {0}*/\n\n", fileName)); - stringBuilder.Append(File.ReadAllText(fileName)); - } - } - catch (Exception) - { + var srcfileList = Directory.EnumerateFiles(inputPath, "*.*", SearchOption.AllDirectories); + if (!srcfileList.Any()) throw new OpException(MsgHelp.FormatString(MsgHelp.ID.CMD_INVALID_FILE_OR_DIR, inputPath)); + + foreach (var fileName in srcfileList) + { + stringBuilder.Append(string.Format("\n\n/*FILE: {0}*/\n\n", fileName)); + stringBuilder.Append(File.ReadAllText(fileName)); } } + catch (Exception) + { + throw new OpException(MsgHelp.FormatString(MsgHelp.ID.CMD_INVALID_FILE_OR_DIR, inputPath)); + } - return stringBuilder.ToString(); - } + return stringBuilder.ToString(); + } - - /// - /// Renders code i.e. user input safe for html display for default report - /// Delayed html encoding allows original values to be rendered for the output form i.e. - /// json, text formats, even nuget objects retain the originals for readability and - /// for managing output transformations as desired - /// - private void RenderResultsSafeforHTML() + + /// + /// Renders code i.e. user input safe for html display for default report + /// Delayed html encoding allows original values to be rendered for the output form i.e. + /// json, text formats, even nuget objects retain the originals for readability and + /// for managing output transformations as desired + /// + private void RenderResultsSafeforHTML() + { + //safeguard simple string meta-data + if (_appMetaData?.ApplicationName != null) + _appMetaData.ApplicationName = SafeString(_appMetaData?.ApplicationName); + + if (_appMetaData?.Description != null) + _appMetaData.Description = SafeString(_appMetaData?.Description); + + if (_appMetaData?.Authors != null) + _appMetaData.Authors = SafeString(_appMetaData?.Authors); + + if (_appMetaData?.SourceVersion != null) + _appMetaData.SourceVersion = SafeString(_appMetaData?.SourceVersion); + + //safeguard lists data + SafeList(_appMetaData?.AppTypes); + SafeList(_appMetaData?.CloudTargets); + SafeList(_appMetaData?.OSTargets); + SafeList(_appMetaData?.Outputs); + SafeList(_appMetaData?.PackageTypes); + SafeList(_appMetaData?.Targets); + SafeList(_appMetaData?.CPUTargets); + + //safeguard displayable fields in match records + foreach (var matchRecord in _appMetaData?.Matches ?? new List()) { - //safeguard simple string meta-data - if (_appMetaData?.ApplicationName != null) - _appMetaData.ApplicationName = SafeString(_appMetaData?.ApplicationName); - - if (_appMetaData?.Description != null) - _appMetaData.Description = SafeString(_appMetaData?.Description); - - if (_appMetaData?.Authors != null) - _appMetaData.Authors = SafeString(_appMetaData?.Authors); - - if (_appMetaData?.SourceVersion != null) - _appMetaData.SourceVersion = SafeString(_appMetaData?.SourceVersion); - - //safeguard lists data - SafeList(_appMetaData?.AppTypes); - SafeList(_appMetaData?.CloudTargets); - SafeList(_appMetaData?.OSTargets); - SafeList(_appMetaData?.Outputs); - SafeList(_appMetaData?.PackageTypes); - SafeList(_appMetaData?.Targets); - SafeList(_appMetaData?.CPUTargets); - - //safeguard displayable fields in match records - foreach (MatchRecord matchRecord in _appMetaData?.Matches ?? new List()) - { - //safeguard sample output now that we've matched properties for blocking browser xss - matchRecord.Sample = System.Net.WebUtility.HtmlEncode(matchRecord.Sample); - matchRecord.Excerpt = System.Net.WebUtility.HtmlEncode(matchRecord.Excerpt); - } + //safeguard sample output now that we've matched properties for blocking browser xss + matchRecord.Sample = WebUtility.HtmlEncode(matchRecord.Sample); + matchRecord.Excerpt = WebUtility.HtmlEncode(matchRecord.Excerpt); } + } - private void SafeList(List? valuesList) + private void SafeList(List? valuesList) + { + for (var i = 0; i < valuesList?.Count; i++) valuesList[i] = SafeString(valuesList[i]); + } + + private string SafeString(string? value) + { + if (!string.IsNullOrEmpty(value)) return WebUtility.HtmlEncode(value); + + return ""; + } + + /// + /// Processing for organizing results into easy to use TagGroups for customizable display organization in HTML UI + /// + public void PopulateTagGroups() + { + //read default/user preferences on what tags to report presence on and groupings + if (File.Exists(Utils.GetPath(Utils.AppPath.tagGroupPref))) + TagGroupPreferences = + JsonConvert.DeserializeObject>( + File.ReadAllText(Utils.GetPath(Utils.AppPath.tagGroupPref))); + else + TagGroupPreferences = new List(); + + string[] unSupportedGroupsOrPatterns = { "metric", "dependency" }; + + //for each preferred group of tag patterns determine if at least one instance was detected + foreach (var tagCategory in TagGroupPreferences ?? new List()) + foreach (var tagGroup in tagCategory.Groups ?? new List()) { - for (int i = 0; i < valuesList?.Count; i++) + if (string.IsNullOrEmpty(tagGroup.Title)) { - valuesList[i] = SafeString(valuesList[i]); - } - } - - private string SafeString(string? value) - { - if (!string.IsNullOrEmpty(value)) - { - return System.Net.WebUtility.HtmlEncode(value); + _logger.LogWarning("Tag group with no title skipped"); + continue; } - return ""; - } + var test = tagGroup.Title.ToLower().Contains(unSupportedGroupsOrPatterns[0]); + if (unSupportedGroupsOrPatterns.Any(x => tagGroup.Title.ToLower().Contains(x))) + _logger.LogWarning( + "Unsupported tag group or pattern detected '{title}'. See online documentation at https://github.com/microsoft/ApplicationInspector/wiki/3.5-Tags", + tagGroup.Title); - /// - /// Processing for organizing results into easy to use TagGroups for customizable display organization in HTML UI - /// - public void PopulateTagGroups() - { - //read default/user preferences on what tags to report presence on and groupings - if (File.Exists(Utils.GetPath(Utils.AppPath.tagGroupPref))) + foreach (var pattern in tagGroup.Patterns ?? new List()) { - TagGroupPreferences = JsonConvert.DeserializeObject>(File.ReadAllText(Utils.GetPath(Utils.AppPath.tagGroupPref))); - } - else - { - TagGroupPreferences = new List(); - } + pattern.Detected = _appMetaData?.UniqueTags is not null && + _appMetaData.UniqueTags.Any(v => v == pattern.SearchPattern); + if (unSupportedGroupsOrPatterns.Any(x => pattern.SearchPattern.ToLower().Contains(x))) + _logger.LogWarning( + "Unsupported tag group or pattern detected '{pattern}'. See online documentation at https://github.com/microsoft/ApplicationInspector/wiki/3.5-Tags", + pattern.SearchPattern); - string[] unSupportedGroupsOrPatterns = new string[] { "metric", "dependency" }; - - //for each preferred group of tag patterns determine if at least one instance was detected - foreach (TagCategory tagCategory in TagGroupPreferences ?? new List()) - { - foreach (TagGroup tagGroup in tagCategory.Groups ?? new List()) + //create dynamic "category" groups of tags with pattern relationship established from TagReportGroups.json + //that can be used to populate reports with various attributes for each tag detected + if (pattern.Detected) { - if (string.IsNullOrEmpty(tagGroup.Title)) - { - _logger.LogWarning("Tag group with no title skipped"); - continue; - } - - bool test = tagGroup.Title.ToLower().Contains(unSupportedGroupsOrPatterns[0]); - if (unSupportedGroupsOrPatterns.Any(x => tagGroup.Title.ToLower().Contains(x))) - { - _logger.LogWarning("Unsupported tag group or pattern detected '{title}'. See online documentation at https://github.com/microsoft/ApplicationInspector/wiki/3.5-Tags", tagGroup.Title); - } - - foreach (TagSearchPattern pattern in tagGroup.Patterns ?? new List()) - { - pattern.Detected = _appMetaData?.UniqueTags is not null && _appMetaData.UniqueTags.Any(v => v == pattern.SearchPattern); - if (unSupportedGroupsOrPatterns.Any(x => pattern.SearchPattern.ToLower().Contains(x))) - { - _logger.LogWarning("Unsupported tag group or pattern detected '{pattern}'. See online documentation at https://github.com/microsoft/ApplicationInspector/wiki/3.5-Tags", pattern.SearchPattern); - } - - //create dynamic "category" groups of tags with pattern relationship established from TagReportGroups.json - //that can be used to populate reports with various attributes for each tag detected - if (pattern.Detected) - { - bool uniqueTagsOnly = tagCategory.Type == TagCategory.tagInfoType.uniqueTags; - KeyedTagInfoLists["tagGrp" + tagGroup.DataRef] = GetTagInfoListByTagGroup(tagGroup, uniqueTagsOnly); - } - } + var uniqueTagsOnly = tagCategory.Type == TagCategory.tagInfoType.uniqueTags; + KeyedTagInfoLists["tagGrp" + tagGroup.DataRef] = GetTagInfoListByTagGroup(tagGroup, uniqueTagsOnly); } } - - //create simple ranked page lists HTML use - KeyedSortedTagInfoLists["tagGrpAllTagsByConfidence"] = GetTagInfoListByConfidence(); - KeyedSortedTagInfoLists["tagGrpAllTagsBySeverity"] = GetTagInfoListBySeverity(); - KeyedSortedTagInfoLists["tagGrpAllTagsByName"] = GetTagInfoListByName(); } - /// - /// Get a list of TagGroup for a given category section name e.g. profile - /// - /// - /// - public List GetCategoryTagGroups(string category) - { - List result = new(); - //get all tag groups for specified category - foreach (TagCategory categoryTagGroup in TagGroupPreferences ?? new List()) + //create simple ranked page lists HTML use + KeyedSortedTagInfoLists["tagGrpAllTagsByConfidence"] = GetTagInfoListByConfidence(); + KeyedSortedTagInfoLists["tagGrpAllTagsBySeverity"] = GetTagInfoListBySeverity(); + KeyedSortedTagInfoLists["tagGrpAllTagsByName"] = GetTagInfoListByName(); + } + + /// + /// Get a list of TagGroup for a given category section name e.g. profile + /// + /// + /// + public List GetCategoryTagGroups(string category) + { + List result = new(); + //get all tag groups for specified category + foreach (var categoryTagGroup in TagGroupPreferences ?? new List()) + if (categoryTagGroup.Name == category) { - if (categoryTagGroup.Name == category) - { - result = categoryTagGroup.Groups ?? new List(); - break; - } + result = categoryTagGroup.Groups ?? new List(); + break; } - //now get all matches for that group i.e. Authentication - foreach (TagGroup group in result) + //now get all matches for that group i.e. Authentication + foreach (var group in result) GetTagInfoListByTagGroup(group); + + return result; + } + + /// + /// MetaData.UniqueTags should already exists and be created incrementally but here in case + /// + /// + private HashSet GetUniqueTags() + { + HashSet results = new(); + + foreach (var match in _appMetaData?.Matches ?? new List()) + foreach (var tag in match.Tags ?? Array.Empty()) + results.Add(tag); + + return results; + } + + /// + /// Builds list of matching tags by profile pattern + /// Ensures only one instance of a given tag in results unlike GetAllMatchingTags method + /// with highest confidence level for that tag pattern + /// + /// + /// + private List GetTagInfoListByTagGroup(TagGroup tagGroup, bool addNotFound = true) + { + List result = new(); + HashSet hashSet = new(); + + foreach (var pattern in tagGroup.Patterns ?? new List()) + if (pattern.Detected) //set at program.RollUp already so don't search for again { - GetTagInfoListByTagGroup(group); - } - - return result; - } - - /// - /// MetaData.UniqueTags should already exists and be created incrementally but here in case - /// - /// - private HashSet GetUniqueTags() - { - HashSet results = new(); - - foreach (MatchRecord match in _appMetaData?.Matches ?? new List()) - { - foreach (string tag in match.Tags ?? Array.Empty()) + var tagPatternRegex = pattern.Expression; + if (_appMetaData?.TotalMatchesCount > 0) { - results.Add(tag); - } - } - - return results; - } - - /// - /// Builds list of matching tags by profile pattern - /// Ensures only one instance of a given tag in results unlike GetAllMatchingTags method - /// with highest confidence level for that tag pattern - /// - /// - /// - private List GetTagInfoListByTagGroup(TagGroup tagGroup, bool addNotFound = true) - { - List result = new(); - HashSet hashSet = new(); - - foreach (TagSearchPattern pattern in tagGroup.Patterns ?? new List()) - { - if (pattern.Detected)//set at program.RollUp already so don't search for again - { - var tagPatternRegex = pattern.Expression; - if (_appMetaData?.TotalMatchesCount > 0) - { - foreach (var match in _appMetaData?.Matches ?? new List()) + foreach (var match in _appMetaData?.Matches ?? new List()) + foreach (var tagItem in match.Tags ?? Array.Empty()) + if (tagPatternRegex.IsMatch(tagItem)) { - foreach (var tagItem in match.Tags ?? Array.Empty()) - { - if (tagPatternRegex.IsMatch(tagItem)) - { - if (!hashSet.Contains(pattern.SearchPattern)) - { - result.Add(new TagInfo - { - Tag = tagItem, - Confidence = match.Confidence.ToString(), - Severity = match.Severity.ToString(), - ShortTag = pattern.DisplayName, - StatusIcon = pattern.DetectedIcon, - Detected = true - }); - - hashSet.Add(pattern.SearchPattern); - - pattern.Confidence = match.Confidence.ToString(); - } - else - { - //ensure we get highest confidence, severity as there are likely multiple matches for this tag pattern - foreach (TagInfo updateItem in result) - { - if (updateItem.Tag == tagItem) - { - Confidence oldConfidence; - Enum.TryParse(updateItem.Confidence, out oldConfidence); - - if (match.Confidence > oldConfidence) - { - updateItem.Confidence = match.Confidence.ToString(); - pattern.Confidence = match.Confidence.ToString(); - } - - Severity oldSeverity; - Enum.TryParse(updateItem.Severity, out oldSeverity); - if (match.Severity > oldSeverity) - { - updateItem.Severity = match.Severity.ToString(); - } - - break; - } - } - } - } - } - } - } - else - { - foreach (var tagItem in _appMetaData?.UniqueTags ?? new List()) - { - if (tagPatternRegex.IsMatch(tagItem) && !hashSet.Contains(pattern.SearchPattern)) + if (!hashSet.Contains(pattern.SearchPattern)) { result.Add(new TagInfo { Tag = tagItem, + Confidence = match.Confidence.ToString(), + Severity = match.Severity.ToString(), ShortTag = pattern.DisplayName, StatusIcon = pattern.DetectedIcon, Detected = true }); - hashSet.Add(tagItem); + hashSet.Add(pattern.SearchPattern); + + pattern.Confidence = match.Confidence.ToString(); } - } - } - } - else if (addNotFound) //allow to report on false presense items - { - TagInfo tagInfo = new() - { - Tag = pattern.SearchPattern, - Detected = false, - ShortTag = pattern.DisplayName, - StatusIcon = pattern.NotDetectedIcon, - Confidence = "", - Severity = "" - }; - - pattern.Confidence = ""; - result.Add(tagInfo); - hashSet.Add(tagInfo.Tag); - } - } - - return result; - } - - /// - /// Gets a set of matching tags for a set of patterns, returning for all matches - /// - /// - /// - /// - private List GetAllMatchingTagInfoList(TagGroup tagGroup, bool addNotFound = true) - { - List result = new(); - HashSet hashSet = new(); - - foreach (TagSearchPattern pattern in tagGroup.Patterns ?? new List()) - { - if (pattern.Detected) - { - var tagPatternRegex = pattern.Expression; - - foreach (var match in _appMetaData?.Matches ?? new List()) - { - foreach (var tagItem in match.Tags ?? Array.Empty()) - { - if (tagPatternRegex.IsMatch(tagItem)) + else { - if (!hashSet.Contains(tagItem)) - { - result.Add(new TagInfo + //ensure we get highest confidence, severity as there are likely multiple matches for this tag pattern + foreach (var updateItem in result) + if (updateItem.Tag == tagItem) { - Tag = tagItem, - Confidence = match.Confidence.ToString(), - Severity = match.Severity.ToString(), - ShortTag = tagItem[(tagItem.LastIndexOf('.') + 1)..], - StatusIcon = pattern.DetectedIcon, - Detected = true - }); + Confidence oldConfidence; + Enum.TryParse(updateItem.Confidence, out oldConfidence); - hashSet.Add(tagItem); - } - else - { //ensure we have highest confidence, severity as there are likly multiple matches for this tag pattern - foreach (TagInfo updateItem in result) - { - if (updateItem.Tag == tagItem) + if (match.Confidence > oldConfidence) { - Confidence oldConfidence; - Enum.TryParse(updateItem.Confidence, out oldConfidence); - - if (match.Confidence > oldConfidence) - { - updateItem.Confidence = match.Confidence.ToString(); - pattern.Confidence = match.Confidence.ToString(); - } - - Severity oldSeverity; - Enum.TryParse(updateItem.Severity, out oldSeverity); - if (match.Severity > oldSeverity) - { - updateItem.Severity = match.Severity.ToString(); - } - - break; + updateItem.Confidence = match.Confidence.ToString(); + pattern.Confidence = match.Confidence.ToString(); } + + Severity oldSeverity; + Enum.TryParse(updateItem.Severity, out oldSeverity); + if (match.Severity > oldSeverity) + updateItem.Severity = match.Severity.ToString(); + + break; } - } } } - } - } - } - - return result; - } - - /// - /// List of taginfo items ordered by name - /// - /// - private List GetTagInfoListByName() - { - HashSet dupCheck = new(); - List result = new(); - - foreach (string tag in _appMetaData?.UniqueTags ?? new List()) - { - if (_appMetaData?.TotalMatchesCount > 0) - { - foreach (var match in _appMetaData?.Matches ?? new List()) - { - foreach (string testTag in match.Tags ?? Array.Empty()) - { - if (tag == testTag) - { - if (dupCheck.Add(testTag)) - { - result.Add(new TagInfo - { - Tag = testTag, - Confidence = match.Confidence.ToString(), - Severity = match.Severity.ToString(), - ShortTag = testTag[(testTag.LastIndexOf('.') + 1)..], - }); - - break; - } - } - } - } } else { - result.Add(new TagInfo - { - Tag = tag, - ShortTag = tag[(tag.LastIndexOf('.') + 1)..], - }); - } - } - - return result; - } - - /// - /// Tags sorted by confidence - /// - /// - private List GetTagInfoListByConfidence() - { - List result = new(); - HashSet dupCheck = new(); - RulesEngine.Confidence[] confidences = { Confidence.High, Confidence.Medium, Confidence.Low }; - - foreach (string tag in _appMetaData?.UniqueTags ?? new List()) - { - var searchPattern = new Regex(tag, RegexOptions.IgnoreCase); - foreach (Confidence confidence in confidences) - { - foreach (var match in _appMetaData?.Matches ?? new List()) - { - foreach (string testTag in match.Tags ?? Array.Empty()) + foreach (var tagItem in _appMetaData?.UniqueTags ?? new List()) + if (tagPatternRegex.IsMatch(tagItem) && !hashSet.Contains(pattern.SearchPattern)) { - if (searchPattern.IsMatch(testTag)) + result.Add(new TagInfo { - if (match.Confidence == confidence && dupCheck.Add(tag)) - { - result.Add(new TagInfo - { - Tag = testTag, - Confidence = confidence.ToString(), - Severity = match.Severity.ToString(), - ShortTag = testTag[(testTag.LastIndexOf('.') + 1)..], - }); - } - } + Tag = tagItem, + ShortTag = pattern.DisplayName, + StatusIcon = pattern.DetectedIcon, + Detected = true + }); + + hashSet.Add(tagItem); } - } } } - return result; - } - - /// - /// Sorted by Severity - /// - /// - private List GetTagInfoListBySeverity() - { - List result = new(); - HashSet dupCheck = new(); - Severity[] severities = { Severity.Critical, Severity.Important, Severity.Moderate, Severity.BestPractice, Severity.ManualReview }; - - foreach (string tag in _appMetaData?.UniqueTags ?? new List()) + else if (addNotFound) //allow to report on false presense items { - var searchPattern = new Regex(tag, RegexOptions.IgnoreCase); - foreach (Severity severity in severities) + TagInfo tagInfo = new() { - foreach (var match in _appMetaData?.Matches ?? new List()) - { - foreach (string testTag in match.Tags ?? Array.Empty()) - { - if (searchPattern.IsMatch(testTag)) - { - if (match.Severity == severity && dupCheck.Add(tag)) - { - result.Add(new TagInfo - { - Tag = testTag, - Confidence = match.Confidence.ToString(), - Severity = severity.ToString(), - ShortTag = testTag[(testTag.LastIndexOf('.') + 1)..], - }); - } - } - } - } - } + Tag = pattern.SearchPattern, + Detected = false, + ShortTag = pattern.DisplayName, + StatusIcon = pattern.NotDetectedIcon, + Confidence = "", + Severity = "" + }; + + pattern.Confidence = ""; + result.Add(tagInfo); + hashSet.Add(tagInfo.Tag); } - return result; - } - - /// - /// Opportunity for any final data prep before report gen - /// - public List ConvertTagCounters(IEnumerable metricTagCounters) - { - List result = new(); - //TagCountersUI is liquid compatible while TagCounters is not to support json serialization; the split prevents exception - //not fixable via json iteration disabling - - result.AddRange(metricTagCounters.Select(counter => new TagCounterUI() - { - Tag = counter.Tag, - Count = counter.Count - })); - - return result; - } - - } + return result; + } /// - /// Compatible for liquid; used to avoid use in MetaData as it adds unwanted properties to json serialization + /// Gets a set of matching tags for a set of patterns, returning for all matches /// - public class TagCounterUI : Drop + /// + /// + /// + private List GetAllMatchingTagInfoList(TagGroup tagGroup, bool addNotFound = true) { - [JsonProperty(PropertyName = "tag")] - public string? Tag { get; set; } + List result = new(); + HashSet hashSet = new(); - [JsonProperty(PropertyName = "displayName")] - public string? ShortTag { get; set; } + foreach (var pattern in tagGroup.Patterns ?? new List()) + if (pattern.Detected) + { + var tagPatternRegex = pattern.Expression; - [JsonProperty(PropertyName = "count")] - public int Count { get; set; } + foreach (var match in _appMetaData?.Matches ?? new List()) + foreach (var tagItem in match.Tags ?? Array.Empty()) + if (tagPatternRegex.IsMatch(tagItem)) + { + if (!hashSet.Contains(tagItem)) + { + result.Add(new TagInfo + { + Tag = tagItem, + Confidence = match.Confidence.ToString(), + Severity = match.Severity.ToString(), + ShortTag = tagItem[(tagItem.LastIndexOf('.') + 1)..], + StatusIcon = pattern.DetectedIcon, + Detected = true + }); - [JsonProperty(PropertyName = "includeAsMatch")] - public bool IncludeAsMatch => false; + hashSet.Add(tagItem); + } + else + { + //ensure we have highest confidence, severity as there are likly multiple matches for this tag pattern + foreach (var updateItem in result) + if (updateItem.Tag == tagItem) + { + Confidence oldConfidence; + Enum.TryParse(updateItem.Confidence, out oldConfidence); + + if (match.Confidence > oldConfidence) + { + updateItem.Confidence = match.Confidence.ToString(); + pattern.Confidence = match.Confidence.ToString(); + } + + Severity oldSeverity; + Enum.TryParse(updateItem.Severity, out oldSeverity); + if (match.Severity > oldSeverity) updateItem.Severity = match.Severity.ToString(); + + break; + } + } + } + } + + return result; + } + + /// + /// List of taginfo items ordered by name + /// + /// + private List GetTagInfoListByName() + { + HashSet dupCheck = new(); + List result = new(); + + foreach (var tag in _appMetaData?.UniqueTags ?? new List()) + if (_appMetaData?.TotalMatchesCount > 0) + { + foreach (var match in _appMetaData?.Matches ?? new List()) + foreach (var testTag in match.Tags ?? Array.Empty()) + if (tag == testTag) + if (dupCheck.Add(testTag)) + { + result.Add(new TagInfo + { + Tag = testTag, + Confidence = match.Confidence.ToString(), + Severity = match.Severity.ToString(), + ShortTag = testTag[(testTag.LastIndexOf('.') + 1)..] + }); + + break; + } + } + else + { + result.Add(new TagInfo + { + Tag = tag, + ShortTag = tag[(tag.LastIndexOf('.') + 1)..] + }); + } + + return result; + } + + /// + /// Tags sorted by confidence + /// + /// + private List GetTagInfoListByConfidence() + { + List result = new(); + HashSet dupCheck = new(); + Confidence[] confidences = { Confidence.High, Confidence.Medium, Confidence.Low }; + + foreach (var tag in _appMetaData?.UniqueTags ?? new List()) + { + var searchPattern = new Regex(tag, RegexOptions.IgnoreCase); + foreach (var confidence in confidences) + foreach (var match in _appMetaData?.Matches ?? new List()) + foreach (var testTag in match.Tags ?? Array.Empty()) + if (searchPattern.IsMatch(testTag)) + if (match.Confidence == confidence && dupCheck.Add(tag)) + result.Add(new TagInfo + { + Tag = testTag, + Confidence = confidence.ToString(), + Severity = match.Severity.ToString(), + ShortTag = testTag[(testTag.LastIndexOf('.') + 1)..] + }); + } + + return result; + } + + /// + /// Sorted by Severity + /// + /// + private List GetTagInfoListBySeverity() + { + List result = new(); + HashSet dupCheck = new(); + Severity[] severities = + { Severity.Critical, Severity.Important, Severity.Moderate, Severity.BestPractice, Severity.ManualReview }; + + foreach (var tag in _appMetaData?.UniqueTags ?? new List()) + { + var searchPattern = new Regex(tag, RegexOptions.IgnoreCase); + foreach (var severity in severities) + foreach (var match in _appMetaData?.Matches ?? new List()) + foreach (var testTag in match.Tags ?? Array.Empty()) + if (searchPattern.IsMatch(testTag)) + if (match.Severity == severity && dupCheck.Add(tag)) + result.Add(new TagInfo + { + Tag = testTag, + Confidence = match.Confidence.ToString(), + Severity = severity.ToString(), + ShortTag = testTag[(testTag.LastIndexOf('.') + 1)..] + }); + } + + return result; + } + + /// + /// Opportunity for any final data prep before report gen + /// + public List ConvertTagCounters(IEnumerable metricTagCounters) + { + List result = new(); + //TagCountersUI is liquid compatible while TagCounters is not to support json serialization; the split prevents exception + //not fixable via json iteration disabling + + result.AddRange(metricTagCounters.Select(counter => new TagCounterUI + { + Tag = counter.Tag, + Count = counter.Count + })); + + return result; } } + +/// +/// Compatible for liquid; used to avoid use in MetaData as it adds unwanted properties to json serialization +/// +public class TagCounterUI : Drop +{ + [JsonProperty(PropertyName = "tag")] public string? Tag { get; set; } + + [JsonProperty(PropertyName = "displayName")] + public string? ShortTag { get; set; } + + [JsonProperty(PropertyName = "count")] public int Count { get; set; } + + [JsonProperty(PropertyName = "includeAsMatch")] + public bool IncludeAsMatch => false; +} \ No newline at end of file diff --git a/AppInspector.CLI/Writers/AnalyzeJsonWriter.cs b/AppInspector.CLI/Writers/AnalyzeJsonWriter.cs index 7eb8759..4b50bc2 100644 --- a/AppInspector.CLI/Writers/AnalyzeJsonWriter.cs +++ b/AppInspector.CLI/Writers/AnalyzeJsonWriter.cs @@ -1,53 +1,46 @@ // Copyright (C) Microsoft. All rights reserved. // Licensed under the MIT License. See LICENSE.txt in the project root for license information. -namespace Microsoft.ApplicationInspector.CLI +using System.IO; +using Microsoft.ApplicationInspector.Commands; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Newtonsoft.Json; + +namespace Microsoft.ApplicationInspector.CLI; + +/// +/// Writes in json format +/// Users can select arguments to filter output to 1. only simple tags 2. only matchlist without rollup metadata etc. +/// 3. everything +/// Lists of tagreportgroups are written as well as match list details so users have chose to present the same +/// UI as shown in the HTML report to the level of detail desired... +/// +public class AnalyzeJsonWriter : CommandResultsWriter { - using Microsoft.ApplicationInspector.Commands; - using Microsoft.Extensions.Logging; - using Microsoft.Extensions.Logging.Abstractions; - using Newtonsoft.Json; - using System.IO; + private readonly ILogger _logger; + + public AnalyzeJsonWriter(TextWriter textWriter, ILoggerFactory? loggerFactory = null) : base(textWriter) + { + _logger = loggerFactory?.CreateLogger() ?? NullLogger.Instance; + } + + public override void WriteResults(Result result, CLICommandOptions commandOptions, bool autoClose = true) + { + var analyzeResult = (AnalyzeResult)result; + + JsonSerializer jsonSerializer = new(); + jsonSerializer.Formatting = Formatting.Indented; + if (TextWriter != null) jsonSerializer.Serialize(TextWriter, analyzeResult); + + if (autoClose) FlushAndClose(); + } /// - /// Writes in json format - /// Users can select arguments to filter output to 1. only simple tags 2. only matchlist without rollup metadata etc. 3. everything - /// Lists of tagreportgroups are written as well as match list details so users have chose to present the same - /// UI as shown in the HTML report to the level of detail desired... + /// simple wrapper for serializing results for simple tags only during processing /// - public class AnalyzeJsonWriter : CommandResultsWriter + private class TagsFile { - private readonly ILogger _logger; - - public AnalyzeJsonWriter(TextWriter textWriter, ILoggerFactory? loggerFactory = null) : base(textWriter) - { - _logger = loggerFactory?.CreateLogger() ?? NullLogger.Instance; - } - - /// - /// simple wrapper for serializing results for simple tags only during processing - /// - private class TagsFile - { - [JsonProperty(PropertyName = "tags")] - public string[]? Tags { get; set; } - } - - public override void WriteResults(Result result, CLICommandOptions commandOptions, bool autoClose = true) - { - AnalyzeResult analyzeResult = (AnalyzeResult)result; - - JsonSerializer jsonSerializer = new(); - jsonSerializer.Formatting = Formatting.Indented; - if (TextWriter != null) - { - jsonSerializer.Serialize(TextWriter, analyzeResult); - } - - if (autoClose) - { - FlushAndClose(); - } - } + [JsonProperty(PropertyName = "tags")] public string[]? Tags { get; set; } } } \ No newline at end of file diff --git a/AppInspector.CLI/Writers/AnalyzeSarifWriter.cs b/AppInspector.CLI/Writers/AnalyzeSarifWriter.cs index c4e0f2b..a9db41f 100644 --- a/AppInspector.CLI/Writers/AnalyzeSarifWriter.cs +++ b/AppInspector.CLI/Writers/AnalyzeSarifWriter.cs @@ -1,168 +1,158 @@ // Copyright (C) Microsoft. All rights reserved. // Licensed under the MIT License. See LICENSE.txt in the project root for license information. -namespace Microsoft.ApplicationInspector.CLI +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Microsoft.ApplicationInspector.Commands; +using Microsoft.ApplicationInspector.RulesEngine; +using Microsoft.CodeAnalysis.Sarif; +using Microsoft.CST.OAT.Utils; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Newtonsoft.Json; +using Location = Microsoft.CodeAnalysis.Sarif.Location; +using Result = Microsoft.ApplicationInspector.Commands.Result; + +namespace Microsoft.ApplicationInspector.CLI; + +internal static class AnalyzeSarifWriterExtensions { - using Microsoft.ApplicationInspector.Commands; - using Microsoft.CodeAnalysis.Sarif; - using Microsoft.CST.OAT.Utils; - using Microsoft.Extensions.Logging; - using Microsoft.Extensions.Logging.Abstractions; - using Newtonsoft.Json; - using System; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using Location = Microsoft.CodeAnalysis.Sarif.Location; - using Result = Microsoft.ApplicationInspector.Commands.Result; - - internal static class AnalyzeSarifWriterExtensions + internal static void AddRange(this TagsCollection tc, IEnumerable? tagsToAdd) { - internal static void AddRange(this TagsCollection tc, IEnumerable? tagsToAdd) - { - if (tagsToAdd is null) return; - foreach (string tag in tagsToAdd) - { - tc.Add(tag); - } - } + if (tagsToAdd is null) return; + foreach (var tag in tagsToAdd) tc.Add(tag); } - /// - /// Writes in sarif format - /// - public class AnalyzeSarifWriter : CommandResultsWriter +} + +/// +/// Writes in sarif format +/// +public class AnalyzeSarifWriter : CommandResultsWriter +{ + private readonly ILogger _logger; + + public AnalyzeSarifWriter(TextWriter textWriter, ILoggerFactory? loggerFactory = null) : base(textWriter) { - private readonly ILogger _logger; + _logger = loggerFactory?.CreateLogger() ?? NullLogger.Instance; + } - public AnalyzeSarifWriter(TextWriter textWriter, ILoggerFactory? loggerFactory = null) : base(textWriter) + public override void WriteResults(Result result, CLICommandOptions commandOptions, bool autoClose = true) + { + if (TextWriter is null) throw new NullReferenceException(nameof(TextWriter)); + string? basePath = null; + if (commandOptions is CLIAnalyzeCmdOptions cLIAnalyzeCmdOptions) { - _logger = loggerFactory?.CreateLogger() ?? NullLogger.Instance; - } + basePath = cLIAnalyzeCmdOptions.BasePath; - public override void WriteResults(Result result, CLICommandOptions commandOptions, bool autoClose = true) - { - if (TextWriter is null) + if (result is AnalyzeResult analyzeResult) { - throw new NullReferenceException(nameof(TextWriter)); - } - string? basePath = null; - if (commandOptions is CLIAnalyzeCmdOptions cLIAnalyzeCmdOptions) - { - basePath = cLIAnalyzeCmdOptions.BasePath; + SarifLog log = new(); + var sarifVersion = SarifVersion.Current; + log.SchemaUri = sarifVersion.ConvertToSchemaUri(); + log.Version = sarifVersion; + log.Runs = new List(); + var run = new Run(); - if (result is AnalyzeResult analyzeResult) - { - SarifLog log = new(); - SarifVersion sarifVersion = SarifVersion.Current; - log.SchemaUri = sarifVersion.ConvertToSchemaUri(); - log.Version = sarifVersion; - log.Runs = new List(); - var run = new Run(); - - if (Uri.TryCreate(cLIAnalyzeCmdOptions.RepositoryUri, UriKind.RelativeOrAbsolute, out Uri? uri)) + if (Uri.TryCreate(cLIAnalyzeCmdOptions.RepositoryUri, UriKind.RelativeOrAbsolute, out var uri)) + run.VersionControlProvenance = new List { - run.VersionControlProvenance = new List() + new() { - new VersionControlDetails() - { - RepositoryUri = uri, - RevisionId = cLIAnalyzeCmdOptions.CommitHash - } - }; - } - - var artifacts = new List(); - run.Tool = new Tool - { - Driver = new ToolComponent - { - Name = $"Application Inspector", - InformationUri = new Uri("https://github.com/microsoft/ApplicationInspector/"), - Organization = "Microsoft", - Version = Helpers.GetVersionString(), + RepositoryUri = uri, + RevisionId = cLIAnalyzeCmdOptions.CommitHash } }; - var reportingDescriptors = new List(); - run.Results = new List(); - foreach (var match in analyzeResult.Metadata.Matches) + + var artifacts = new List(); + run.Tool = new Tool + { + Driver = new ToolComponent { - var sarifResult = new CodeAnalysis.Sarif.Result(); + Name = "Application Inspector", + InformationUri = new Uri("https://github.com/microsoft/ApplicationInspector/"), + Organization = "Microsoft", + Version = Helpers.GetVersionString() + } + }; + var reportingDescriptors = new List(); + run.Results = new List(); + foreach (var match in analyzeResult.Metadata.Matches) + { + var sarifResult = new CodeAnalysis.Sarif.Result(); - if (match.Rule is not null) + if (match.Rule is not null) + { + if (!reportingDescriptors.Any(r => r.Id == match.Rule.Id)) { - if (!reportingDescriptors.Any(r => r.Id == match.Rule.Id)) + ReportingDescriptor reportingDescriptor = new() { - ReportingDescriptor reportingDescriptor = new() + FullDescription = new MultiformatMessageString { Text = match.Rule.Description }, + Id = match.Rule.Id, + Name = match.Rule.Name, + DefaultConfiguration = new ReportingConfiguration { - FullDescription = new MultiformatMessageString() { Text = match.Rule.Description }, - Id = match.Rule.Id, - Name = match.Rule.Name, - DefaultConfiguration = new ReportingConfiguration() - { - Level = GetSarifFailureLevel(match.Rule.Severity) - } - }; - reportingDescriptor.Tags.AddRange(match.Rule.Tags); - reportingDescriptors.Add(reportingDescriptor); - } - - sarifResult.Level = GetSarifFailureLevel(match.Rule.Severity); - sarifResult.RuleId = match.Rule.Id; - sarifResult.Tags.AddRange(match.Rule.Tags); - sarifResult.Message = new Message() - { - Text = match.Rule.Description - }; - - if (match.FileName is not null) - { - string fileName = match.FileName; - if (basePath is not null) - { - fileName = Path.GetRelativePath(basePath, fileName); + Level = GetSarifFailureLevel(match.Rule.Severity) } - if (Uri.TryCreate(fileName, UriKind.RelativeOrAbsolute, out Uri? outUri)) + }; + reportingDescriptor.Tags.AddRange(match.Rule.Tags); + reportingDescriptors.Add(reportingDescriptor); + } + + sarifResult.Level = GetSarifFailureLevel(match.Rule.Severity); + sarifResult.RuleId = match.Rule.Id; + sarifResult.Tags.AddRange(match.Rule.Tags); + sarifResult.Message = new Message + { + Text = match.Rule.Description + }; + + if (match.FileName is not null) + { + var fileName = match.FileName; + if (basePath is not null) fileName = Path.GetRelativePath(basePath, fileName); + if (Uri.TryCreate(fileName, UriKind.RelativeOrAbsolute, out var outUri)) + { + var artifactIndex = artifacts.FindIndex(a => a.Location.Uri.Equals(outUri)); + if (artifactIndex == -1) { - int artifactIndex = artifacts.FindIndex(a => a.Location.Uri.Equals(outUri)); - if (artifactIndex == -1) + Artifact artifact = new() { - Artifact artifact = new() + Location = new ArtifactLocation { - Location = new ArtifactLocation() - { - Index = artifacts.Count, - Uri = outUri - }, - }; - artifactIndex = artifact.Location.Index; - artifact.Tags.AddRange(match.Rule.Tags); - if (match.LanguageInfo is { } languageInfo) - { - artifact.SourceLanguage = languageInfo.Name; + Index = artifacts.Count, + Uri = outUri } - artifacts.Add(artifact); - } - else - { - artifacts[artifactIndex].Tags.AddRange(match.Rule.Tags); - } - sarifResult.Locations = new List() + }; + artifactIndex = artifact.Location.Index; + artifact.Tags.AddRange(match.Rule.Tags); + if (match.LanguageInfo is { } languageInfo) + artifact.SourceLanguage = languageInfo.Name; + artifacts.Add(artifact); + } + else { - new Location() + artifacts[artifactIndex].Tags.AddRange(match.Rule.Tags); + } + + sarifResult.Locations = new List + { + new() { - PhysicalLocation = new PhysicalLocation() + PhysicalLocation = new PhysicalLocation { - ArtifactLocation = new ArtifactLocation() + ArtifactLocation = new ArtifactLocation { Index = artifactIndex }, - Region = new Region() + Region = new Region { StartLine = match.StartLocationLine, StartColumn = match.StartLocationColumn, EndLine = match.EndLocationLine, EndColumn = match.EndLocationColumn, - Snippet = new ArtifactContent() + Snippet = new ArtifactContent { Text = match.Sample } @@ -170,39 +160,42 @@ namespace Microsoft.ApplicationInspector.CLI } } }; - } } } - - run.Artifacts = artifacts; - run.Tool.Driver.Rules = reportingDescriptors; - run.Results.Add(sarifResult); } - log.Runs.Add(run); - JsonSerializerSettings serializerSettings = new(); - var serializer = new JsonSerializer(); - serializer.Serialize(TextWriter, log); - FlushAndClose(); - } - else - { - throw new ArgumentException("This writer can only write Analyze results.", nameof(result)); + run.Artifacts = artifacts; + run.Tool.Driver.Rules = reportingDescriptors; + run.Results.Add(sarifResult); } + + log.Runs.Add(run); + JsonSerializerSettings serializerSettings = new(); + var serializer = new JsonSerializer(); + serializer.Serialize(TextWriter, log); + FlushAndClose(); } else { - throw new ArgumentException("This writer requires a CLIAnalyzeCmdOptions options argument.", nameof(commandOptions)); + throw new ArgumentException("This writer can only write Analyze results.", nameof(result)); } } - - private static FailureLevel GetSarifFailureLevel(RulesEngine.Severity severity) => severity switch + else { - RulesEngine.Severity.BestPractice => FailureLevel.Note, - RulesEngine.Severity.Critical => FailureLevel.Error, - RulesEngine.Severity.Important => FailureLevel.Warning, - RulesEngine.Severity.ManualReview => FailureLevel.Note, - RulesEngine.Severity.Moderate => FailureLevel.Warning, + throw new ArgumentException("This writer requires a CLIAnalyzeCmdOptions options argument.", + nameof(commandOptions)); + } + } + + private static FailureLevel GetSarifFailureLevel(Severity severity) + { + return severity switch + { + Severity.BestPractice => FailureLevel.Note, + Severity.Critical => FailureLevel.Error, + Severity.Important => FailureLevel.Warning, + Severity.ManualReview => FailureLevel.Note, + Severity.Moderate => FailureLevel.Warning, _ => FailureLevel.Note }; } diff --git a/AppInspector.CLI/Writers/AnalyzeTextWriter.cs b/AppInspector.CLI/Writers/AnalyzeTextWriter.cs index c0756ce..ee6140d 100644 --- a/AppInspector.CLI/Writers/AnalyzeTextWriter.cs +++ b/AppInspector.CLI/Writers/AnalyzeTextWriter.cs @@ -1,186 +1,153 @@ // Copyright (C) Microsoft. All rights reserved. // Licensed under the MIT License. See LICENSE.txt in the project root for license information. -namespace Microsoft.ApplicationInspector.CLI +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using Microsoft.ApplicationInspector.Commands; +using Microsoft.ApplicationInspector.RulesEngine; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; + +namespace Microsoft.ApplicationInspector.CLI; + +public class AnalyzeTextWriter : CommandResultsWriter { - using System.Linq; - using Microsoft.ApplicationInspector.Commands; - using Microsoft.ApplicationInspector.RulesEngine; - using Microsoft.Extensions.Logging; - using Microsoft.Extensions.Logging.Abstractions; - using System; - using System.Collections.Generic; - using System.IO; - using System.Text; + private readonly string _formatString; + private readonly ILogger _logger; + private readonly int COLUMN_MAX = 80; - public class AnalyzeTextWriter : CommandResultsWriter + public AnalyzeTextWriter(TextWriter textWriter, string formatString, ILoggerFactory? loggerFactory = null) : + base(textWriter) { - private readonly int COLUMN_MAX = 80; - public override void WriteResults(Result result, CLICommandOptions commandOptions, bool autoClose = true) + _logger = loggerFactory?.CreateLogger() ?? NullLogger.Instance; + if (string.IsNullOrEmpty(formatString)) + _formatString = + "Tag:%T,Rule:%N,Ruleid:%R,Confidence:%X,File:%F,Language:%l,SourceType:%tLine:%L,%C,Sample:%m"; + else + _formatString = formatString; + } + + public override void WriteResults(Result result, CLICommandOptions commandOptions, bool autoClose = true) + { + var cLIAnalyzeCmdOptions = (CLIAnalyzeCmdOptions)commandOptions; + var analyzeResult = (AnalyzeResult)result; + if (TextWriter is null) throw new ArgumentNullException(nameof(TextWriter)); + TextWriter.WriteLine("Results"); + + WriteAppMeta(analyzeResult.Metadata); + WriteDependencies(analyzeResult.Metadata); + TextWriter.WriteLine(MakeHeading("Match Details")); + + foreach (var match in analyzeResult.Metadata.Matches ?? new List()) WriteMatch(match); + + if (autoClose) FlushAndClose(); + } + + + private string StringList(IEnumerable data) + { + return string.Join(' ', data); + } + + private string StringList(IDictionary data) + { + return string.Join(' ', data.Keys); + } + + private string StringList(IDictionary data) + { + StringBuilder build = new(); + + foreach (var s in data.Values) { - CLIAnalyzeCmdOptions cLIAnalyzeCmdOptions = (CLIAnalyzeCmdOptions)commandOptions; - AnalyzeResult analyzeResult = (AnalyzeResult)result; - if (TextWriter is null) - { - throw new ArgumentNullException(nameof(TextWriter)); - } - TextWriter.WriteLine("Results"); - - WriteAppMeta(analyzeResult.Metadata); - WriteDependencies(analyzeResult.Metadata); - TextWriter.WriteLine(MakeHeading("Match Details")); - - foreach (MatchRecord match in analyzeResult.Metadata.Matches ?? new List()) - { - WriteMatch(match); - } - - if (autoClose) - { - FlushAndClose(); - } + build.Append(s); + build.Append(" "); } - public AnalyzeTextWriter(TextWriter textWriter, string formatString, ILoggerFactory? loggerFactory = null) : base(textWriter) - { - _logger = loggerFactory?.CreateLogger() ?? NullLogger.Instance; - if (string.IsNullOrEmpty(formatString)) - { - _formatString = "Tag:%T,Rule:%N,Ruleid:%R,Confidence:%X,File:%F,Language:%l,SourceType:%tLine:%L,%C,Sample:%m"; - } - else - { - _formatString = formatString; - } - } + return build.ToString(); + } - - private string StringList(IEnumerable data) - { - return string.Join(' ', data); - } + /// + /// even out delineator for headings + /// + /// + /// + private string MakeHeading(string header) + { + StringBuilder build = new(); + build.Append(string.Format("[{0}]", header)); + for (var i = header.Length; i < COLUMN_MAX; i++) build.Append("-"); - private string StringList(IDictionary data) - { - return string.Join(' ', data.Keys); - } + return build.ToString(); + } - private string StringList(IDictionary data) - { - StringBuilder build = new(); - foreach (string s in data.Values) - { - build.Append(s); - build.Append(" "); - } + public void WriteAppMeta(MetaData metaData) + { + if (TextWriter is null) throw new ArgumentNullException(nameof(TextWriter)); + //write predefined characteristics + TextWriter.WriteLine(string.Format(MakeHeading("Project Info"))); + TextWriter.WriteLine($"Name: {metaData.ApplicationName + " " + metaData.SourceVersion}"); + TextWriter.WriteLine($"Description: {metaData.Description}"); + TextWriter.WriteLine($"Source path: {metaData.SourcePath}"); + TextWriter.WriteLine($"Authors: {metaData.Authors}"); + TextWriter.WriteLine($"Last Updated: {metaData.LastUpdated}"); + TextWriter.WriteLine( + $"Languages: {(metaData.Languages is not null ? StringList(metaData.Languages) : string.Empty)}"); + TextWriter.WriteLine(string.Format(MakeHeading("Scan Settings"))); + TextWriter.WriteLine($"Date scanned: {metaData.DateScanned}"); + TextWriter.WriteLine(string.Format(MakeHeading("Source Info"))); + TextWriter.WriteLine($"Application type: {StringList(metaData.AppTypes ?? new List())}"); + TextWriter.WriteLine($"Package types: {StringList(metaData.PackageTypes ?? new List())}"); + TextWriter.WriteLine($"File extensions: {StringList(metaData.FileExtensions ?? new List())}"); + TextWriter.WriteLine(string.Format(MakeHeading("Detetected Targets"))); + TextWriter.WriteLine($"Output types: {StringList(metaData.Outputs ?? new List())}"); + TextWriter.WriteLine($"OS Targets: {StringList(metaData.OSTargets ?? new List())}"); + TextWriter.WriteLine($"CPU Targets: {StringList(metaData.CPUTargets ?? new List())}"); + TextWriter.WriteLine($"Cloud targets: {StringList(metaData.CloudTargets ?? new List())}"); + TextWriter.WriteLine(string.Format(MakeHeading("Stats"))); + TextWriter.WriteLine($"Files analyzed: {metaData.FilesAnalyzed}"); + TextWriter.WriteLine($"Files skipped: {metaData.FilesSkipped}"); + TextWriter.WriteLine($"Total files: {metaData.TotalFiles}"); + TextWriter.WriteLine($"Total matches: {metaData.TotalMatchesCount} in {metaData.FilesAffected} file(s)"); + TextWriter.WriteLine($"Unique matches: {metaData.UniqueMatchesCount}"); - return build.ToString(); - } + TextWriter.WriteLine(MakeHeading("UniqueTags")); + foreach (var tag in metaData.UniqueTags) TextWriter.WriteLine(tag); - /// - /// even out delineator for headings - /// - /// - /// - private string MakeHeading(string header) - { - StringBuilder build = new(); - build.Append(string.Format("[{0}]", header)); - for (int i = header.Length; i < COLUMN_MAX; i++) - { - build.Append("-"); - } + TextWriter.WriteLine(MakeHeading("Select Counters")); + foreach (var tagCounter in metaData.TagCounters ?? new List()) + TextWriter.WriteLine($"Tagname: {tagCounter.Tag}, Count: {tagCounter.Count}"); + } - return build.ToString(); - } + public void WriteMatch(MatchRecord match) + { + if (TextWriter is null) throw new ArgumentNullException(nameof(TextWriter)); + var output = _formatString.Replace("%F", match.FileName); + output = output.Replace("%l", match.LanguageInfo.Name); + output = output.Replace("%t", match.LanguageInfo.Type.ToString()); + output = output.Replace("%L", match.StartLocationLine.ToString()); + output = output.Replace("%C", match.StartLocationColumn.ToString()); + output = output.Replace("%l", match.EndLocationLine.ToString()); + output = output.Replace("%c", match.EndLocationColumn.ToString()); + output = output.Replace("%R", match.RuleId); + output = output.Replace("%N", match.RuleName); + output = output.Replace("%S", match.Severity.ToString()); + output = output.Replace("%X", match.Confidence.ToString()); + output = output.Replace("%D", match.RuleDescription); + output = output.Replace("%m", match.Sample); + output = output.Replace("%T", string.Join(',', match.Tags ?? Array.Empty())); - - public void WriteAppMeta(MetaData metaData) - { - if (TextWriter is null) - { - throw new ArgumentNullException(nameof(TextWriter)); - } - //write predefined characteristics - TextWriter.WriteLine(string.Format(MakeHeading("Project Info"))); - TextWriter.WriteLine($"Name: {metaData.ApplicationName + " " + metaData.SourceVersion}"); - TextWriter.WriteLine($"Description: {metaData.Description}"); - TextWriter.WriteLine($"Source path: {metaData.SourcePath}"); - TextWriter.WriteLine($"Authors: {metaData.Authors}"); - TextWriter.WriteLine($"Last Updated: {metaData.LastUpdated}"); - TextWriter.WriteLine( - $"Languages: {(metaData.Languages is not null ? StringList(metaData.Languages) : string.Empty)}"); - TextWriter.WriteLine(string.Format(MakeHeading("Scan Settings"))); - TextWriter.WriteLine($"Date scanned: {metaData.DateScanned}"); - TextWriter.WriteLine(string.Format(MakeHeading("Source Info"))); - TextWriter.WriteLine($"Application type: {StringList(metaData.AppTypes ?? new List())}"); - TextWriter.WriteLine($"Package types: {StringList(metaData.PackageTypes ?? new List())}"); - TextWriter.WriteLine($"File extensions: {StringList(metaData.FileExtensions ?? new List())}"); - TextWriter.WriteLine(string.Format(MakeHeading("Detetected Targets"))); - TextWriter.WriteLine($"Output types: {StringList(metaData.Outputs ?? new List())}"); - TextWriter.WriteLine($"OS Targets: {StringList(metaData.OSTargets ?? new List())}"); - TextWriter.WriteLine($"CPU Targets: {StringList(metaData.CPUTargets ?? new List())}"); - TextWriter.WriteLine($"Cloud targets: {StringList(metaData.CloudTargets ?? new List())}"); - TextWriter.WriteLine(string.Format(MakeHeading("Stats"))); - TextWriter.WriteLine($"Files analyzed: {metaData.FilesAnalyzed}"); - TextWriter.WriteLine($"Files skipped: {metaData.FilesSkipped}"); - TextWriter.WriteLine($"Total files: {metaData.TotalFiles}"); - TextWriter.WriteLine($"Total matches: {metaData.TotalMatchesCount} in {metaData.FilesAffected} file(s)"); - TextWriter.WriteLine($"Unique matches: {metaData.UniqueMatchesCount}"); + TextWriter.WriteLine(output); + } - TextWriter.WriteLine(MakeHeading("UniqueTags")); - foreach (string tag in metaData.UniqueTags) - { - TextWriter.WriteLine(tag); - } + private void WriteDependencies(MetaData metaData) + { + if (TextWriter is null) throw new ArgumentNullException(nameof(TextWriter)); + TextWriter.WriteLine(MakeHeading("Dependencies")); - TextWriter.WriteLine(MakeHeading("Select Counters")); - foreach (MetricTagCounter tagCounter in metaData.TagCounters ?? new List()) - { - TextWriter.WriteLine($"Tagname: {tagCounter.Tag}, Count: {tagCounter.Count}"); - } - } - - public void WriteMatch(MatchRecord match) - { - if (TextWriter is null) - { - throw new ArgumentNullException(nameof(TextWriter)); - } - string output = _formatString.Replace("%F", match.FileName); - output = output.Replace("%l", match.LanguageInfo.Name); - output = output.Replace("%t", match.LanguageInfo.Type.ToString()); - output = output.Replace("%L", match.StartLocationLine.ToString()); - output = output.Replace("%C", match.StartLocationColumn.ToString()); - output = output.Replace("%l", match.EndLocationLine.ToString()); - output = output.Replace("%c", match.EndLocationColumn.ToString()); - output = output.Replace("%R", match.RuleId); - output = output.Replace("%N", match.RuleName); - output = output.Replace("%S", match.Severity.ToString()); - output = output.Replace("%X", match.Confidence.ToString()); - output = output.Replace("%D", match.RuleDescription); - output = output.Replace("%m", match.Sample); - output = output.Replace("%T", string.Join(',', match.Tags ?? System.Array.Empty())); - - TextWriter.WriteLine(output); - } - - private void WriteDependencies(MetaData metaData) - { - if (TextWriter is null) - { - throw new ArgumentNullException(nameof(TextWriter)); - } - TextWriter.WriteLine(MakeHeading("Dependencies")); - - foreach (string s in metaData.UniqueDependencies ?? new List()) - { - TextWriter.WriteLine(s); - } - } - - private readonly string _formatString; - private readonly ILogger _logger; + foreach (var s in metaData.UniqueDependencies ?? new List()) TextWriter.WriteLine(s); } } \ No newline at end of file diff --git a/AppInspector.CLI/Writers/CmdResultsWriter.cs b/AppInspector.CLI/Writers/CmdResultsWriter.cs index c18394b..9688992 100644 --- a/AppInspector.CLI/Writers/CmdResultsWriter.cs +++ b/AppInspector.CLI/Writers/CmdResultsWriter.cs @@ -1,26 +1,27 @@ // Copyright (C) Microsoft. All rights reserved. // Licensed under the MIT License. See LICENSE.txt in the project root for license information. -namespace Microsoft.ApplicationInspector.CLI -{ - using Microsoft.ApplicationInspector.Commands; - using System.IO; +using System.IO; +using Microsoft.ApplicationInspector.Commands; - /// - /// Common class for command specific writers - /// - public abstract class CommandResultsWriter +namespace Microsoft.ApplicationInspector.CLI; + +/// +/// Common class for command specific writers +/// +public abstract class CommandResultsWriter +{ + protected CommandResultsWriter(TextWriter writer) { - protected CommandResultsWriter(TextWriter writer) - { - TextWriter = writer; - } - public TextWriter TextWriter { get; } - public abstract void WriteResults(Result result, CLICommandOptions commandOptions, bool autoClose = true); - public void FlushAndClose() - { - TextWriter?.Flush(); - TextWriter?.Close(); - } + TextWriter = writer; + } + + public TextWriter TextWriter { get; } + public abstract void WriteResults(Result result, CLICommandOptions commandOptions, bool autoClose = true); + + public void FlushAndClose() + { + TextWriter?.Flush(); + TextWriter?.Close(); } } \ No newline at end of file diff --git a/AppInspector.CLI/Writers/ExportTagsTextWriter.cs b/AppInspector.CLI/Writers/ExportTagsTextWriter.cs index 80e3c9b..96602b8 100644 --- a/AppInspector.CLI/Writers/ExportTagsTextWriter.cs +++ b/AppInspector.CLI/Writers/ExportTagsTextWriter.cs @@ -1,50 +1,40 @@ // Copyright (C) Microsoft. All rights reserved. // Licensed under the MIT License. See LICENSE.txt in the project root for license information. -namespace Microsoft.ApplicationInspector.CLI +using System; +using System.IO; +using Microsoft.ApplicationInspector.Commands; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; + +namespace Microsoft.ApplicationInspector.CLI; + +internal class ExportTagsTextWriter : CommandResultsWriter { - using Microsoft.ApplicationInspector.Commands; - using Microsoft.Extensions.Logging; - using Microsoft.Extensions.Logging.Abstractions; - using System; - using System.IO; + private readonly ILogger _logger; - internal class ExportTagsTextWriter : CommandResultsWriter + internal ExportTagsTextWriter(TextWriter textWriter, ILoggerFactory? loggerFactory = null) : base(textWriter) { - private readonly ILogger _logger; + _logger = loggerFactory?.CreateLogger() ?? NullLogger.Instance; + } - internal ExportTagsTextWriter(TextWriter textWriter, ILoggerFactory? loggerFactory = null) : base(textWriter) + public override void WriteResults(Result result, CLICommandOptions commandOptions, bool autoClose = true) + { + if (TextWriter is null) throw new ArgumentNullException(nameof(TextWriter)); + + var exportTagsResult = (ExportTagsResult)result; + + if (exportTagsResult.TagsList.Count > 0) { - _logger = loggerFactory?.CreateLogger() ?? NullLogger.Instance; + TextWriter.WriteLine("Results"); + + foreach (var tag in exportTagsResult.TagsList) TextWriter.WriteLine(tag); + } + else + { + TextWriter.WriteLine("No tags found"); } - public override void WriteResults(Result result, CLICommandOptions commandOptions, bool autoClose = true) - { - if (TextWriter is null) - { - throw new ArgumentNullException(nameof(TextWriter)); - } - - ExportTagsResult exportTagsResult = (ExportTagsResult)result; - - if (exportTagsResult.TagsList.Count > 0) - { - TextWriter.WriteLine("Results"); - - foreach (string tag in exportTagsResult.TagsList) - { - TextWriter.WriteLine(tag); - } - } - else - { - TextWriter.WriteLine("No tags found"); - } - - if (autoClose) - { - FlushAndClose(); - } - } + if (autoClose) FlushAndClose(); } } \ No newline at end of file diff --git a/AppInspector.CLI/Writers/JsonWriter.cs b/AppInspector.CLI/Writers/JsonWriter.cs index 8996274..d54bb6a 100644 --- a/AppInspector.CLI/Writers/JsonWriter.cs +++ b/AppInspector.CLI/Writers/JsonWriter.cs @@ -1,51 +1,44 @@ -namespace Microsoft.ApplicationInspector.CLI.Writers +using System; +using System.IO; +using Microsoft.ApplicationInspector.Commands; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Newtonsoft.Json; + +namespace Microsoft.ApplicationInspector.CLI.Writers; + +internal class JsonWriter : CommandResultsWriter { - using Microsoft.ApplicationInspector.Commands; - using Microsoft.Extensions.Logging; - using Microsoft.Extensions.Logging.Abstractions; - using Newtonsoft.Json; - using System; - using System.IO; + private readonly ILogger _logger; - internal class JsonWriter : CommandResultsWriter + internal JsonWriter(TextWriter textWriter, ILoggerFactory? loggerFactory = null) : base(textWriter) { - private readonly ILogger _logger; + _logger = loggerFactory?.CreateLogger() ?? NullLogger.Instance; + } - internal JsonWriter(TextWriter textWriter, ILoggerFactory? loggerFactory = null) : base(textWriter) + public override void WriteResults(Result result, CLICommandOptions commandOptions, bool autoClose = true) + { + JsonSerializer jsonSerializer = new(); + jsonSerializer.Formatting = Formatting.Indented; + jsonSerializer.NullValueHandling = NullValueHandling.Ignore; + jsonSerializer.DefaultValueHandling = DefaultValueHandling.Ignore; + + if (TextWriter is null) throw new ArgumentNullException(nameof(TextWriter)); + + switch (result) { - _logger = loggerFactory?.CreateLogger() ?? NullLogger.Instance; + case TagDiffResult: + case ExportTagsResult: + case VerifyRulesResult: + jsonSerializer.Serialize(TextWriter, result); + break; + case PackRulesResult prr: + jsonSerializer.Serialize(TextWriter, prr.Rules); + break; + default: + throw new Exception("Unexpected object type for json writer"); } - public override void WriteResults(Result result, CLICommandOptions commandOptions, bool autoClose = true) - { - JsonSerializer jsonSerializer = new(); - jsonSerializer.Formatting = Formatting.Indented; - jsonSerializer.NullValueHandling = NullValueHandling.Ignore; - jsonSerializer.DefaultValueHandling = DefaultValueHandling.Ignore; - - if (TextWriter is null) - { - throw new ArgumentNullException(nameof(TextWriter)); - } - - switch (result) - { - case TagDiffResult: - case ExportTagsResult: - case VerifyRulesResult: - jsonSerializer.Serialize(TextWriter, result); - break; - case PackRulesResult prr: - jsonSerializer.Serialize(TextWriter, prr.Rules); - break; - default: - throw new System.Exception("Unexpected object type for json writer"); - } - - if (autoClose) - { - FlushAndClose(); - } - } + if (autoClose) FlushAndClose(); } } \ No newline at end of file diff --git a/AppInspector.CLI/Writers/TagDiffTextWriter.cs b/AppInspector.CLI/Writers/TagDiffTextWriter.cs index d50b26a..a0e360d 100644 --- a/AppInspector.CLI/Writers/TagDiffTextWriter.cs +++ b/AppInspector.CLI/Writers/TagDiffTextWriter.cs @@ -1,52 +1,43 @@ // Copyright (C) Microsoft. All rights reserved. // Licensed under the MIT License. See LICENSE.txt in the project root for license information. -namespace Microsoft.ApplicationInspector.CLI +using System.IO; +using Microsoft.ApplicationInspector.Commands; +using Microsoft.ApplicationInspector.Common; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; + +namespace Microsoft.ApplicationInspector.CLI; + +public class TagDiffTextWriter : CommandResultsWriter { - using Microsoft.ApplicationInspector.Commands; - using Microsoft.ApplicationInspector.Common; - using Microsoft.Extensions.Logging; - using Microsoft.Extensions.Logging.Abstractions; - using System.IO; + private readonly ILogger _logger; - public class TagDiffTextWriter : CommandResultsWriter + public TagDiffTextWriter(TextWriter textWriter, ILoggerFactory? loggerFactory = null) : base(textWriter) { - private readonly ILogger _logger; + _logger = loggerFactory?.CreateLogger() ?? NullLogger.Instance; + } - public TagDiffTextWriter(TextWriter textWriter, ILoggerFactory? loggerFactory = null) : base(textWriter) + public override void WriteResults(Result result, CLICommandOptions commandOptions, bool autoClose = true) + { + var tagDiffResult = (TagDiffResult)result; + var cLITagDiffCmdOptions = (CLITagDiffCmdOptions)commandOptions; + + TextWriter.WriteLine(MsgHelp.FormatString(MsgHelp.ID.TAGTEST_RESULTS_TEST_TYPE, cLITagDiffCmdOptions.TestType)); + + if (tagDiffResult.ResultCode == TagDiffResult.ExitCode.TestFailed) + TextWriter.WriteLine(MsgHelp.GetString(MsgHelp.ID.TAGTEST_RESULTS_FAIL)); + else + TextWriter.WriteLine(MsgHelp.GetString(MsgHelp.ID.TAGTEST_RESULTS_SUCCESS)); + + //Results list + if (tagDiffResult.TagDiffList.Count > 0) { - _logger = loggerFactory?.CreateLogger() ?? NullLogger.Instance; + TextWriter.WriteLine("Differences"); + foreach (var tagDiff in tagDiffResult.TagDiffList) + TextWriter.WriteLine("Tag: {0}, Only found in file: {1}", tagDiff.Tag, tagDiff.Source); } - public override void WriteResults(Result result, CLICommandOptions commandOptions, bool autoClose = true) - { - TagDiffResult tagDiffResult = (TagDiffResult)result; - CLITagDiffCmdOptions cLITagDiffCmdOptions = (CLITagDiffCmdOptions)commandOptions; - TextWriter.WriteLine(MsgHelp.FormatString(MsgHelp.ID.TAGTEST_RESULTS_TEST_TYPE, cLITagDiffCmdOptions.TestType)); - - if (tagDiffResult.ResultCode == TagDiffResult.ExitCode.TestFailed) - { - TextWriter.WriteLine(MsgHelp.GetString(MsgHelp.ID.TAGTEST_RESULTS_FAIL)); - } - else - { - TextWriter.WriteLine(MsgHelp.GetString(MsgHelp.ID.TAGTEST_RESULTS_SUCCESS)); - } - - //Results list - if (tagDiffResult.TagDiffList.Count > 0) - { - TextWriter.WriteLine("Differences"); - foreach (TagDiff tagDiff in tagDiffResult.TagDiffList) - { - TextWriter.WriteLine(string.Format("Tag: {0}, Only found in file: {1}", tagDiff.Tag, tagDiff.Source)); - } - } - - if (autoClose) - { - FlushAndClose(); - } - } + if (autoClose) FlushAndClose(); } } \ No newline at end of file diff --git a/AppInspector.CLI/Writers/VerifyRulesTextWriter.cs b/AppInspector.CLI/Writers/VerifyRulesTextWriter.cs index ad62236..c67403b 100644 --- a/AppInspector.CLI/Writers/VerifyRulesTextWriter.cs +++ b/AppInspector.CLI/Writers/VerifyRulesTextWriter.cs @@ -1,55 +1,42 @@ // Copyright (C) Microsoft. All rights reserved. // Licensed under the MIT License. See LICENSE.txt in the project root for license information. -using Microsoft.ApplicationInspector.RulesEngine; +using System.IO; +using Microsoft.ApplicationInspector.Commands; +using Microsoft.ApplicationInspector.Common; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; -namespace Microsoft.ApplicationInspector.CLI +namespace Microsoft.ApplicationInspector.CLI; + +internal class VerifyRulesTextWriter : CommandResultsWriter { - using Microsoft.ApplicationInspector.Commands; - using Microsoft.ApplicationInspector.Common; - using Microsoft.Extensions.Logging; - using Microsoft.Extensions.Logging.Abstractions; - using System.IO; + private readonly ILogger _logger; - internal class VerifyRulesTextWriter : CommandResultsWriter + public VerifyRulesTextWriter(TextWriter textWriter, ILoggerFactory? loggerFactory = null) : base(textWriter) { - private readonly ILogger _logger; + _logger = loggerFactory?.CreateLogger() ?? NullLogger.Instance; + } - public VerifyRulesTextWriter(TextWriter textWriter, ILoggerFactory? loggerFactory = null) : base(textWriter) + public override void WriteResults(Result result, CLICommandOptions commandOptions, bool autoClose = true) + { + var verifyRulesResult = (VerifyRulesResult)result; + + if (string.IsNullOrEmpty(commandOptions.OutputFilePath)) TextWriter.WriteLine("Results"); + + if (verifyRulesResult.ResultCode != VerifyRulesResult.ExitCode.Verified) + TextWriter.WriteLine(MsgHelp.ID.TAGTEST_RESULTS_FAIL); + else + TextWriter.WriteLine(MsgHelp.ID.TAGTEST_RESULTS_SUCCESS); + + if (verifyRulesResult.RuleStatusList.Count > 0) { - _logger = loggerFactory?.CreateLogger() ?? NullLogger.Instance; + TextWriter.WriteLine("Rule status"); + foreach (var ruleStatus in verifyRulesResult.RuleStatusList) + TextWriter.WriteLine("Ruleid: {0}, Rulename: {1}, Status: {2}", ruleStatus.RulesId, + ruleStatus.RulesName, ruleStatus.Verified); } - public override void WriteResults(Result result, CLICommandOptions commandOptions, bool autoClose = true) - { - VerifyRulesResult verifyRulesResult = (VerifyRulesResult)result; - if (string.IsNullOrEmpty(commandOptions.OutputFilePath)) - { - TextWriter.WriteLine("Results"); - } - - if (verifyRulesResult.ResultCode != VerifyRulesResult.ExitCode.Verified) - { - TextWriter.WriteLine(MsgHelp.ID.TAGTEST_RESULTS_FAIL); - } - else - { - TextWriter.WriteLine(MsgHelp.ID.TAGTEST_RESULTS_SUCCESS); - } - - if (verifyRulesResult.RuleStatusList.Count > 0) - { - TextWriter.WriteLine("Rule status"); - foreach (RuleStatus ruleStatus in verifyRulesResult.RuleStatusList) - { - TextWriter.WriteLine("Ruleid: {0}, Rulename: {1}, Status: {2}", ruleStatus.RulesId, ruleStatus.RulesName, ruleStatus.Verified); - } - } - - if (autoClose) - { - FlushAndClose(); - } - } + if (autoClose) FlushAndClose(); } } \ No newline at end of file diff --git a/AppInspector.CLI/Writers/WriterFactory.cs b/AppInspector.CLI/Writers/WriterFactory.cs index 2a9c479..42bb783 100644 --- a/AppInspector.CLI/Writers/WriterFactory.cs +++ b/AppInspector.CLI/Writers/WriterFactory.cs @@ -1,135 +1,130 @@ // Copyright (C) Microsoft. All rights reserved. // Licensed under the MIT License. See LICENSE.txt in the project root for license information. -namespace Microsoft.ApplicationInspector.CLI +using System; +using System.IO; +using Microsoft.ApplicationInspector.CLI.Writers; +using Microsoft.ApplicationInspector.Common; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; + +namespace Microsoft.ApplicationInspector.CLI; + +public class WriterFactory { - using Microsoft.ApplicationInspector.CLI.Writers; - using Microsoft.ApplicationInspector.Common; - using Microsoft.Extensions.Logging; - using Microsoft.Extensions.Logging.Abstractions; - using System; - using System.IO; + private readonly ILogger _logger; + private readonly ILoggerFactory? _loggerFactory; - public class WriterFactory + public WriterFactory(ILoggerFactory? loggerFactory = null) { - private readonly ILoggerFactory? _loggerFactory; - private readonly ILogger _logger; + _loggerFactory = loggerFactory; + _logger = _loggerFactory?.CreateLogger() ?? NullLogger.Instance; + } - public WriterFactory(ILoggerFactory? loggerFactory = null) + /// + /// Responsible for returning the correct cmd and format writer for output of cmd results. An an output + /// file will be opened as a stream if provided otherwise the console.out stream is used + /// A downcast is expected as the input param containing the common output format and filepath for simplifying + /// the allocation to a single method and serves as a type selector but is also recast for command specific + /// options in the writer as needed + /// + /// + /// + public CommandResultsWriter GetWriter(CLICommandOptions options) + { + return options switch { - _loggerFactory = loggerFactory; - _logger = _loggerFactory?.CreateLogger() ?? NullLogger.Instance; - } - /// - /// Responsible for returning the correct cmd and format writer for output of cmd results. An an output - /// file will be opened as a stream if provided otherwise the console.out stream is used - /// A downcast is expected as the input param containing the common output format and filepath for simplifying - /// the allocation to a single method and serves as a type selector but is also recast for command specific - /// options in the writer as needed - /// - /// - /// - public CommandResultsWriter GetWriter(CLICommandOptions options) + CLIAnalyzeCmdOptions cliAnalyzeCmdOptions => GetAnalyzeWriter(cliAnalyzeCmdOptions), + CLITagDiffCmdOptions cliTagDiffCmdOptions => GetTagDiffWriter(cliTagDiffCmdOptions), + CLIExportTagsCmdOptions cliExportTagsCmdOptions => GetExportTagsWriter(cliExportTagsCmdOptions), + CLIVerifyRulesCmdOptions cliVerifyRulesCmdOptions => GetVerifyRulesWriter(cliVerifyRulesCmdOptions), + CLIPackRulesCmdOptions cliPackRulesCmdOptions => GetPackRulesWriter(cliPackRulesCmdOptions), + _ => throw new OpException($"Unrecognized object type {options.GetType().Name} in writer request") + }; + } + + /// + /// Only AnalyzeResultsWriter supports an html option + /// + /// + /// + private CommandResultsWriter GetAnalyzeWriter(CLIAnalyzeCmdOptions options) + { + var textWriter = GetTextWriter(options.OutputFilePath); + return options.OutputFileFormat.ToLower() switch { - return options switch + "json" => new AnalyzeJsonWriter(textWriter, _loggerFactory), + "text" => new AnalyzeTextWriter(textWriter, options.TextOutputFormat, _loggerFactory), + "html" => new AnalyzeHtmlWriter(textWriter, _loggerFactory), + "sarif" => new AnalyzeSarifWriter(textWriter, _loggerFactory), + _ => throw new OpException(MsgHelp.FormatString(MsgHelp.ID.CMD_INVALID_ARG_VALUE, "-f")) + }; + } + + public CommandResultsWriter GetExportTagsWriter(CLIExportTagsCmdOptions options) + { + var writer = GetTextWriter(options.OutputFilePath); + return options.OutputFileFormat.ToLower() switch + { + "json" => new JsonWriter(writer, _loggerFactory), + "text" => new ExportTagsTextWriter(writer, _loggerFactory), + _ => throw new OpException(MsgHelp.FormatString(MsgHelp.ID.CMD_INVALID_ARG_VALUE, "-f")) + }; + } + + private CommandResultsWriter GetTagDiffWriter(CLITagDiffCmdOptions options) + { + var writer = GetTextWriter(options.OutputFilePath); + return options.OutputFileFormat.ToLower() switch + { + "json" => new JsonWriter(writer, _loggerFactory), + "text" => new TagDiffTextWriter(writer, _loggerFactory), + _ => throw new OpException(MsgHelp.FormatString(MsgHelp.ID.CMD_INVALID_ARG_VALUE, "-f")) + }; + } + + private CommandResultsWriter GetVerifyRulesWriter(CLIVerifyRulesCmdOptions options) + { + var writer = GetTextWriter(options.OutputFilePath); + return options.OutputFileFormat.ToLower() switch + { + "json" => new JsonWriter(writer, _loggerFactory), + "text" => new VerifyRulesTextWriter(writer, _loggerFactory), + _ => throw new OpException(MsgHelp.FormatString(MsgHelp.ID.CMD_INVALID_ARG_VALUE, "-f")) + }; + } + + private CommandResultsWriter GetPackRulesWriter(CLIPackRulesCmdOptions options) + { + var writer = GetTextWriter(options.OutputFilePath); + return options.OutputFileFormat.ToLower() switch + { + "json" => new JsonWriter(writer, _loggerFactory), + _ => throw new OpException(MsgHelp.FormatString(MsgHelp.ID.CMD_INVALID_ARG_VALUE, "-f")) + }; + } + + /// + /// Create a TextWriter for the given path or console. + /// + /// The path to create, if null or empty will use Console.Out. + /// + private TextWriter GetTextWriter(string? outputFileName) + { + TextWriter textWriter; + if (string.IsNullOrEmpty(outputFileName)) + textWriter = Console.Out; + else + try { - CLIAnalyzeCmdOptions cliAnalyzeCmdOptions => GetAnalyzeWriter(cliAnalyzeCmdOptions), - CLITagDiffCmdOptions cliTagDiffCmdOptions => GetTagDiffWriter(cliTagDiffCmdOptions), - CLIExportTagsCmdOptions cliExportTagsCmdOptions => GetExportTagsWriter(cliExportTagsCmdOptions), - CLIVerifyRulesCmdOptions cliVerifyRulesCmdOptions => GetVerifyRulesWriter(cliVerifyRulesCmdOptions), - CLIPackRulesCmdOptions cliPackRulesCmdOptions => GetPackRulesWriter(cliPackRulesCmdOptions), - _ => throw new OpException($"Unrecognized object type {options.GetType().Name} in writer request") - - }; - } - - /// - /// Only AnalyzeResultsWriter supports an html option - /// - /// - /// - private CommandResultsWriter GetAnalyzeWriter(CLIAnalyzeCmdOptions options) - { - TextWriter textWriter = GetTextWriter(options.OutputFilePath); - return options.OutputFileFormat.ToLower() switch - { - "json" => new AnalyzeJsonWriter(textWriter, _loggerFactory), - "text" => new AnalyzeTextWriter(textWriter, options.TextOutputFormat, _loggerFactory), - "html" => new AnalyzeHtmlWriter(textWriter, _loggerFactory), - "sarif" => new AnalyzeSarifWriter(textWriter, _loggerFactory), - _ => throw new OpException(MsgHelp.FormatString(MsgHelp.ID.CMD_INVALID_ARG_VALUE, "-f")) - }; - } - - public CommandResultsWriter GetExportTagsWriter(CLIExportTagsCmdOptions options) - { - TextWriter writer = GetTextWriter(options.OutputFilePath); - return options.OutputFileFormat.ToLower() switch - { - "json" => new JsonWriter(writer, _loggerFactory), - "text" => new ExportTagsTextWriter(writer, _loggerFactory), - _ => throw new OpException((MsgHelp.FormatString(MsgHelp.ID.CMD_INVALID_ARG_VALUE, "-f"))) - }; - } - - private CommandResultsWriter GetTagDiffWriter(CLITagDiffCmdOptions options) - { - TextWriter writer = GetTextWriter(options.OutputFilePath); - return options.OutputFileFormat.ToLower() switch - { - "json" => new JsonWriter(writer, _loggerFactory), - "text" => new TagDiffTextWriter(writer, _loggerFactory), - _ => throw new OpException((MsgHelp.FormatString(MsgHelp.ID.CMD_INVALID_ARG_VALUE, "-f"))) - }; - } - - private CommandResultsWriter GetVerifyRulesWriter(CLIVerifyRulesCmdOptions options) - { - TextWriter writer = GetTextWriter(options.OutputFilePath); - return options.OutputFileFormat.ToLower() switch - { - "json" => new JsonWriter(writer, _loggerFactory), - "text" => new VerifyRulesTextWriter(writer, _loggerFactory), - _ => throw new OpException((MsgHelp.FormatString(MsgHelp.ID.CMD_INVALID_ARG_VALUE, "-f"))) - }; - } - - private CommandResultsWriter GetPackRulesWriter(CLIPackRulesCmdOptions options) - { - TextWriter writer = GetTextWriter(options.OutputFilePath); - return options.OutputFileFormat.ToLower() switch - { - "json" => new JsonWriter(writer, _loggerFactory), - _ => throw new OpException((MsgHelp.FormatString(MsgHelp.ID.CMD_INVALID_ARG_VALUE, "-f"))) - }; - } - - /// - /// Create a TextWriter for the given path or console. - /// - /// The path to create, if null or empty will use Console.Out. - /// - private TextWriter GetTextWriter(string? outputFileName) - { - TextWriter textWriter; - if (string.IsNullOrEmpty(outputFileName)) - { - textWriter = Console.Out; + textWriter = File.CreateText(outputFileName); } - else + catch (Exception) { - try - { - textWriter = File.CreateText(outputFileName); - } - catch (Exception) - { - _logger.LogError(MsgHelp.GetString(MsgHelp.ID.CMD_INVALID_FILE_OR_DIR), outputFileName); - throw; - } + _logger.LogError(MsgHelp.GetString(MsgHelp.ID.CMD_INVALID_FILE_OR_DIR), outputFileName); + throw; } - return textWriter; - } + return textWriter; } } \ No newline at end of file diff --git a/AppInspector.CLI/html/index.html b/AppInspector.CLI/html/index.html index 5d890d8..ebffce2 100644 --- a/AppInspector.CLI/html/index.html +++ b/AppInspector.CLI/html/index.html @@ -1,68 +1,80 @@  - - - - - - + + + + + + {{ application_version }} - - -
-
-
-
- {% include "report_overview" %} -
-
- {% include "report_summary" %} -
-
- {% include "report_profile" %} -
-
+
+ {% include "report_summary" %}
-
+
+ {% include "report_profile" %} +
+
+ + - {% include "file_listing" %} +{% include "file_listing" %} - - - - - - - - - + + + + + + + + + - + \ No newline at end of file diff --git a/AppInspector.CLI/html/resources/css/appinspector.css b/AppInspector.CLI/html/resources/css/appinspector.css index 0d81d1b..fe0e2fa 100644 --- a/AppInspector.CLI/html/resources/css/appinspector.css +++ b/AppInspector.CLI/html/resources/css/appinspector.css @@ -1,6 +1,6 @@ body { background-color: #fcfefd !important; - font-family: 'Segoe UI',Roboto,Oxygen-Sans,Ubuntu,Cantarell,'Helvetica Neue',sans-serif; + font-family: 'Segoe UI', Roboto, Oxygen-Sans, Ubuntu, Cantarell, 'Helvetica Neue', sans-serif; } a:hover { @@ -25,22 +25,22 @@ a:hover { padding-bottom: 5px; } - .navbar-lg .navbar-brand { - margin-left: 80px; - font-size: 24px; - font-weight: bold; - color: #000; - } +.navbar-lg .navbar-brand { + margin-left: 80px; + font-size: 24px; + font-weight: bold; + color: #000; +} - .navbar-lg .navbar-nav > li { - margin-left: 15px; - margin-right: 15px; - font-size: 0.90em; - } +.navbar-lg .navbar-nav > li { + margin-left: 15px; + margin-right: 15px; + font-size: 0.90em; +} - .navbar-lg .navbar-nav > li > a { - color: #000; - } +.navbar-lg .navbar-nav > li > a { + color: #000; +} #file_listing_modal div.modal-dialog { max-width: 70%; @@ -60,19 +60,19 @@ div.section { padding: 0.10rem 0.55rem 0.10rem 0.55rem; } - #page__report_overview table td:first-child { - text-align: center; - color: #888; - } +#page__report_overview table td:first-child { + text-align: center; + color: #888; +} - #page__report_overview table td:nth-child(2) { - text-align: center; - font-weight: bold; - } +#page__report_overview table td:nth-child(2) { + text-align: center; + font-weight: bold; +} - #page__report_overview table td:nth-child(3) { - color: #888; - } +#page__report_overview table td:nth-child(3) { + color: #888; +} /* Report Summary */ .c3-chart-arcs-title { diff --git a/AppInspector.CLI/html/resources/js/appinspector.js b/AppInspector.CLI/html/resources/js/appinspector.js index 5e8aaa2..a490c99 100644 --- a/AppInspector.CLI/html/resources/js/appinspector.js +++ b/AppInspector.CLI/html/resources/js/appinspector.js @@ -46,8 +46,7 @@ // Decode the content (HTML encoded) for Ace to display // Disabled, needs better testing, since it's prone to XSS if content contains JS. // Maybe there is a better way of doing this. - if (false) - { + if (false) { const htmlEntityDecoder = (content) => { const textArea = document.createElement('textarea'); textArea.innerHTML = content; @@ -92,7 +91,9 @@ class TemplateInsertion { donut: { title: "Analyzed Files", label: { - format: function(value) { return value; } + format: function (value) { + return value; + } } } }); @@ -112,7 +113,9 @@ class TemplateInsertion { donut: { title: "Results", label: { - format: function(value) { return value; } + format: function (value) { + return value; + } } } }); @@ -129,7 +132,9 @@ class TemplateInsertion { donut: { title: "Source Types", label: { - format: function(value) { return value; } + format: function (value) { + return value; + } } } }); @@ -149,8 +154,10 @@ class TemplateInsertion { const _a = a.toLowerCase(); const _b = b.toLowerCase(); - const map = { 'low': 1, 'medium': 2, 'high': 4 }; - if (map[_a] > map[_b]) { return a; } + const map = {'low': 1, 'medium': 2, 'high': 4}; + if (map[_a] > map[_b]) { + return a; + } return b; } @@ -228,14 +235,14 @@ class TemplateInsertion { // We're goint to go through all results (this.md) and if it contains // a tag that matches what we're looking for, we'll keep that icon visible. search_loop: - for (let match of this.md) { - for (let tag of match.tags) { - if (targetRegex.exec(tag)) { - foundTag = true; // We have at least one match for this icon - break search_loop; + for (let match of this.md) { + for (let tag of match.tags) { + if (targetRegex.exec(tag)) { + foundTag = true; // We have at least one match for this icon + break search_loop; + } } } - } if (!foundTag) { $(elt).addClass('disabled'); } @@ -261,8 +268,7 @@ class TemplateInsertion { //Toggles display blocks and image; NOTE jquery selectors won't work due to spaces in some group //names even using escape() or '[id=value] methods so html DOM methods used and work - $('.toggle_image').click(function () - { + $('.toggle_image').click(function () { var btnToggleBlock = "data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAEEAAAA2CAYAAACY0PQ8AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAAHYYAAB2GAV2iE4EAAACgSURBVGhD7dmxDcIwEEBRhyWYjumYjinCQ0qProIo/zV2aX35GnvbWRd3O9ZLKwJFoAgUgSJQBIpAESgCRaAIFIEiUASKQBEoAkWgCBSBIjB+ct+er2P33/bH/dh9N7oJZwnwMTlr40ARGEWYzNmvTc7aXySNA0WgCBSBIlAEikARKAJFoAgUgSJQBIpAESgCRaAIFIEiUASKQBEowlrrDWP+FV5LruwQAAAAAElFTkSuQmCC"; var btnToggleNone = "data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAEEAAAA2CAYAAACY0PQ8AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsQAAA7EAZUrDhsAAADBSURBVGhD7dqxCYBADEDRnEs4ndM5nVPoP7hCxCbNBfG/Ru3kYxCD7UT83DKOv2YEGAFGgBFQ+nZo+zHOIs5tHWfzlT0J9wDd83omxwFGgBFgBBgBRoARYAQYAUaAEWAEGAFGgBGQXqpUfvdnZJY0qSfhKwG6zL06DjACUhEql6FZmXst2za/zWxVZMcBRoARYAQYAUaAEWAEGAFGgBFgBBgBRoARYAQYAWURnlukytWdv/rDcYARYAQYAUaAESLiAh45KVR0Pg9xAAAAAElFTkSuQmCC"; @@ -302,10 +308,9 @@ class TemplateInsertion { $tbody.empty(); // Now we iterate through all of the rules that relate to this icon - for (let [rule] of Object.entries(identifiedRules)) - { + for (let [rule] of Object.entries(identifiedRules)) { let $tr = $(''); - $tr.on('click', 'td', { 'obj': this }, this.show_file_listing); + $tr.on('click', 'td', {'obj': this}, this.show_file_listing); let $td0 = $(''); let $td0a = $(''); $td0a.attr('href', '#'); diff --git a/AppInspector.Common/AppInspector.Common.csproj b/AppInspector.Common/AppInspector.Common.csproj index 55753a0..73f29a1 100644 --- a/AppInspector.Common/AppInspector.Common.csproj +++ b/AppInspector.Common/AppInspector.Common.csproj @@ -1,57 +1,57 @@ - + Library - Microsoft.CST.ApplicationInspector.Common - 0.0.0-placeholder - https://github.com/microsoft/ApplicationInspector - https://github.com/microsoft/ApplicationInspector - Security Static Analyzer - Microsoft Application Inspector is a software source code analysis tool that helps identify and surface well-known features and other interesting characteristics of source code to aid in determining what the software is or what it does. - © Microsoft Corporation. All rights reserved. - true - Application Inspector - Microsoft - 0.0.0-placeholder - ApplicationInspector.Common - Microsoft.ApplicationInspector.Common - Microsoft - true - 0.0.0 - 0.0.0 - false - LICENSE.txt - icon-128.png - true - snupkg - enable - 10.0 - net6.0;netstandard2.1 - + Microsoft.CST.ApplicationInspector.Common + 0.0.0-placeholder + https://github.com/microsoft/ApplicationInspector + https://github.com/microsoft/ApplicationInspector + Security Static Analyzer + Microsoft Application Inspector is a software source code analysis tool that helps identify and surface well-known features and other interesting characteristics of source code to aid in determining what the software is or what it does. + © Microsoft Corporation. All rights reserved. + true + Application Inspector + Microsoft + 0.0.0-placeholder + ApplicationInspector.Common + Microsoft.ApplicationInspector.Common + Microsoft + true + 0.0.0 + 0.0.0 + false + LICENSE.txt + icon-128.png + true + snupkg + enable + 10.0 + net6.0;netstandard2.1 + - - - - + + - - - Resources.resx - True - True - - + - - - Resources.Designer.cs - ResXFileCodeGenerator - - - - - - - - + + + Resources.resx + True + True + + + + + + Resources.Designer.cs + ResXFileCodeGenerator + + + + + + + + diff --git a/AppInspector.Common/MsgHelp.cs b/AppInspector.Common/MsgHelp.cs index a14143b..3814913 100644 --- a/AppInspector.Common/MsgHelp.cs +++ b/AppInspector.Common/MsgHelp.cs @@ -1,110 +1,109 @@ // Copyright (C) Microsoft. All rights reserved. // Licensed under the MIT License. See LICENSE.txt in the project root for license information. -namespace Microsoft.ApplicationInspector.Common +using System; +using Microsoft.ApplicationInspector.Common.Properties; + +namespace Microsoft.ApplicationInspector.Common; + +public static class MsgHelp { - using Microsoft.ApplicationInspector.Common.Properties; - using System; - - public static class MsgHelp + /// + /// Maps enum values to resource strings for ensuring values exists at compile time + /// + public enum ID { - /// - /// Maps enum values to resource strings for ensuring values exists at compile time - /// - public enum ID - { - ANALYZE_COMPRESSED_FILETYPE, - ANALYZE_FILES_PROCESSED_PCNT, - ANALYZE_NOPATTERNS, - ANALYZE_PROCESSING_TIMED_OUT, - ANALYZE_NOSUPPORTED_FILETYPES, - ANALYZE_UNCOMPRESSED_FILETYPE, - ANALYZE_UNSUPPORTED_COMPR_TYPE, - ANALYZE_FILESIZE_SKIPPED, - ANALYZE_EXCLUDED_TYPE_SKIPPED, - ANALYZE_EXCLUDED_BINARY, - ANALYZE_LANGUAGE_NOTFOUND, - ANALYZE_COMPRESSED_FILESIZE_WARN, - ANALYZE_COMPRESSED_PROCESSING, - ANALYZE_COMPRESSED_ERROR, - ANALYZE_FILE_TYPE_OPEN, - ANALYZE_REPORTSIZE_WARN, - ANALYZE_NODUPLICATES_HTML_FORMAT, - ANALYZE_SIMPLETAGS_HTML_FORMAT, - ANALYZE_HTML_EXTENSION, - CMD_NO_OUTPUT, - CMD_PREPARING_REPORT, - CMD_COMPLETED, - CMD_CRITICAL_FILE_ERR, - CMD_INVALID_ARG_VALUE, - CMD_INVALID_FILE_OR_DIR, - CMD_NO_FILES_IN_SOURCE, - CMD_REPORT_DONE, - CMD_REQUIRED_ARG_MISSING, - CMD_RUNNING, - CMD_INVALID_RULE_PATH, - CMD_NORULES_SPECIFIED, - CMD_INVALID_LOG_PATH, - CMD_VIEW_OUTPUT_FILE, - CMD_REMINDER_CHECK_LOG, - TAGDIFF_NO_TAGS_FOUND, - TAGDIFF_RESULTS_DIFFER, - TAGDIFF_RESULTS_GAP, - TAGDIFF_RESULTS_TEST_TYPE, - TAGDIFF_SAME_FILE_ARG, - TAGDIFF_RESULTS_SUCCESSS, - TAGDIFF_RESULTS_FAIL, - TAGTEST_RESULTS_NONE, - TAGTEST_RESULTS_TAGS_FOUND, - TAGTEST_RESULTS_TAGS_MISSING, - TAGTEST_RESULTS_TEST_TYPE, - TAGTEST_RESULTS_SUCCESS, - TAGTEST_RESULTS_FAIL, - VERIFY_RULE_LOADFILE_FAILED, - VERIFY_RULES_RESULTS_FAIL, - VERIFY_RULES_NULLID_FAIL, - VERIFY_RULES_REGEX_FAIL, - VERIFY_RULES_LANGUAGE_FAIL, - VERIFY_RULES_CONDITION_FAIL, - VERIFY_RULES_DUPLICATEID_FAIL, - VERIFY_RULES_RESULTS_SUCCESS, - VERIFY_RULES_NO_CLI_DEFAULT, - RUNTIME_ERROR_NAMED, - RUNTIME_ERROR_UNNAMED, - RUNTIME_ERROR_PRELOG, - BROWSER_ENVIRONMENT_VAR, - BROWSER_START_FAIL, - BROWSER_START_SUCCESS, - PACK_MISSING_OUTPUT_ARG, - PACK_RULES_NO_CLI_DEFAULT, - PACK_RULES_NO_DEFAULT, - }; + ANALYZE_COMPRESSED_FILETYPE, + ANALYZE_FILES_PROCESSED_PCNT, + ANALYZE_NOPATTERNS, + ANALYZE_PROCESSING_TIMED_OUT, + ANALYZE_NOSUPPORTED_FILETYPES, + ANALYZE_UNCOMPRESSED_FILETYPE, + ANALYZE_UNSUPPORTED_COMPR_TYPE, + ANALYZE_FILESIZE_SKIPPED, + ANALYZE_EXCLUDED_TYPE_SKIPPED, + ANALYZE_EXCLUDED_BINARY, + ANALYZE_LANGUAGE_NOTFOUND, + ANALYZE_COMPRESSED_FILESIZE_WARN, + ANALYZE_COMPRESSED_PROCESSING, + ANALYZE_COMPRESSED_ERROR, + ANALYZE_FILE_TYPE_OPEN, + ANALYZE_REPORTSIZE_WARN, + ANALYZE_NODUPLICATES_HTML_FORMAT, + ANALYZE_SIMPLETAGS_HTML_FORMAT, + ANALYZE_HTML_EXTENSION, + CMD_NO_OUTPUT, + CMD_PREPARING_REPORT, + CMD_COMPLETED, + CMD_CRITICAL_FILE_ERR, + CMD_INVALID_ARG_VALUE, + CMD_INVALID_FILE_OR_DIR, + CMD_NO_FILES_IN_SOURCE, + CMD_REPORT_DONE, + CMD_REQUIRED_ARG_MISSING, + CMD_RUNNING, + CMD_INVALID_RULE_PATH, + CMD_NORULES_SPECIFIED, + CMD_INVALID_LOG_PATH, + CMD_VIEW_OUTPUT_FILE, + CMD_REMINDER_CHECK_LOG, + TAGDIFF_NO_TAGS_FOUND, + TAGDIFF_RESULTS_DIFFER, + TAGDIFF_RESULTS_GAP, + TAGDIFF_RESULTS_TEST_TYPE, + TAGDIFF_SAME_FILE_ARG, + TAGDIFF_RESULTS_SUCCESSS, + TAGDIFF_RESULTS_FAIL, + TAGTEST_RESULTS_NONE, + TAGTEST_RESULTS_TAGS_FOUND, + TAGTEST_RESULTS_TAGS_MISSING, + TAGTEST_RESULTS_TEST_TYPE, + TAGTEST_RESULTS_SUCCESS, + TAGTEST_RESULTS_FAIL, + VERIFY_RULE_LOADFILE_FAILED, + VERIFY_RULES_RESULTS_FAIL, + VERIFY_RULES_NULLID_FAIL, + VERIFY_RULES_REGEX_FAIL, + VERIFY_RULES_LANGUAGE_FAIL, + VERIFY_RULES_CONDITION_FAIL, + VERIFY_RULES_DUPLICATEID_FAIL, + VERIFY_RULES_RESULTS_SUCCESS, + VERIFY_RULES_NO_CLI_DEFAULT, + RUNTIME_ERROR_NAMED, + RUNTIME_ERROR_UNNAMED, + RUNTIME_ERROR_PRELOG, + BROWSER_ENVIRONMENT_VAR, + BROWSER_START_FAIL, + BROWSER_START_SUCCESS, + PACK_MISSING_OUTPUT_ARG, + PACK_RULES_NO_CLI_DEFAULT, + PACK_RULES_NO_DEFAULT + } - public static string GetString(MsgHelp.ID id) + public static string GetString(ID id) + { + string result; + try { - string result; - try - { - result = Resources.ResourceManager.GetString(id.ToString()) ?? ""; - } - catch (Exception e) - { - string error = string.Format("Unable to locate requested string resource {0}", id); - error += e.Message + "\n" + e.StackTrace; - throw new Exception(error); - } - - return result ?? ""; + result = Resources.ResourceManager.GetString(id.ToString()) ?? ""; + } + catch (Exception e) + { + var error = string.Format("Unable to locate requested string resource {0}", id); + error += e.Message + "\n" + e.StackTrace; + throw new Exception(error); } - public static string FormatString(MsgHelp.ID id, params object[]? parameters) - { - return string.Format(GetString(id), parameters ?? Array.Empty()); - } + return result ?? ""; + } - public static string FormatString(MsgHelp.ID id, int value) - { - return string.Format(GetString(id), value); - } + public static string FormatString(ID id, params object[]? parameters) + { + return string.Format(GetString(id), parameters ?? Array.Empty()); + } + + public static string FormatString(ID id, int value) + { + return string.Format(GetString(id), value); } } \ No newline at end of file diff --git a/AppInspector.Common/OpException.cs b/AppInspector.Common/OpException.cs index f5408e3..2fa62b2 100644 --- a/AppInspector.Common/OpException.cs +++ b/AppInspector.Common/OpException.cs @@ -1,29 +1,27 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using System; using System.Diagnostics.CodeAnalysis; -namespace Microsoft.ApplicationInspector.Common +namespace Microsoft.ApplicationInspector.Common; + +/// +/// Used to distinguish exceptions which are expected to have been safely written to log and console for CLI use +/// to avoid duplication of error messages to better support both CLI and NuGet entry / exit points +/// +[ExcludeFromCodeCoverage] +public class OpException : Exception { - using System; - - /// - /// Used to distinguish exceptions which are expected to have been safely written to log and console for CLI use - /// to avoid duplication of error messages to better support both CLI and NuGet entry / exit points - /// - [ExcludeFromCodeCoverage] - public class OpException : Exception + public OpException(string? msg) : base(msg) { - public OpException(string? msg) : base(msg) - { - } + } - public OpException() : base() - { - } + public OpException() + { + } - public OpException(string? message, Exception? innerException) : base(message, innerException) - { - } + public OpException(string? message, Exception? innerException) : base(message, innerException) + { } } \ No newline at end of file diff --git a/AppInspector.Common/Properties/PublishProfiles/FolderProfile.pubxml b/AppInspector.Common/Properties/PublishProfiles/FolderProfile.pubxml index 3978c9a..3c20dae 100644 --- a/AppInspector.Common/Properties/PublishProfiles/FolderProfile.pubxml +++ b/AppInspector.Common/Properties/PublishProfiles/FolderProfile.pubxml @@ -3,12 +3,12 @@ https://go.microsoft.com/fwlink/?LinkID=208121. --> - - FileSystem - Release - Any CPU - netcoreapp3.0 - bin\Release\netcoreapp3.0\publish\ - false - + + FileSystem + Release + Any CPU + netcoreapp3.0 + bin\Release\netcoreapp3.0\publish\ + false + \ No newline at end of file diff --git a/AppInspector.Common/Properties/Resources.Designer.cs b/AppInspector.Common/Properties/Resources.Designer.cs index 0263f27..b4e7f5a 100644 --- a/AppInspector.Common/Properties/Resources.Designer.cs +++ b/AppInspector.Common/Properties/Resources.Designer.cs @@ -1,7 +1,6 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/AppInspector.Common/Properties/Resources.resx b/AppInspector.Common/Properties/Resources.resx index c2d8b16..80ca370 100644 --- a/AppInspector.Common/Properties/Resources.resx +++ b/AppInspector.Common/Properties/Resources.resx @@ -1,316 +1,326 @@  - - - - - - - + + + + - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, + PublicKeyToken=b77a5c561934e089 + + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, + PublicKeyToken=b77a5c561934e089 + + + Problem while decompressing {0}: Unzip and try running on uncompressed files - + Decompressing may take longer for larger files - + compressed - + Decompressing files... - + File skipped: Name or path in exclude list. {0} - + File skipped: First 100 bytes appear to indicate this is a binary file. {0} - + File skipped: File size is too large. {0} - + {0}% source files processed - + Unable to determine file type. File open error for {0} - + The output file does not have an html or htm extension and may not open properly in your browser - + File skipped: Language not found for file {0} - + Allow duplicate matches argument not supported for html output format. Select a different output format (text/json) or remove the argument. - + No pattern matches were detected for files in source path - + No file types found in source path that are supported - + The output.html file size is large and may render slowly. Consider running using alternate output format options as needed. - + Simple tags only argument not supported for html output format. Select a different output format or remove the argument - + uncompressed - + unsupported - + Unable to launch output.html automatically. Set the BROWSER environment variable to your desired browser and try again or launch your browser and navigate to the file to view the report file manually. - + Unable to launch output.html in default browser. Launch your browser manually to view output.html report file - + Opening default browser to output.html report - + {0} command completed - + Critical error processing file {0} - + Invalid {0} argument value - + Invalid file or directory {0} - + Specified source location does not contain any files. {0} - + The requested log file {0} could not be read. - + Invalid rule path {0} - + No rules specified. At least one valid rules path required - + No output specified. Raise the console versosity or provide an output file argument. - + Preparing report - + Additional details may be found in log file at {0} - + {0} report completed - + Required {0} argument missing - + {0} command running - + See output file at {0} - - - ..\..\AppInspector.RulesEngine\Resources\comments.json;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - ..\..\AppInspector.RulesEngine\Resources\languages.json;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - + + + ..\..\AppInspector.RulesEngine\Resources\comments.json;System.Byte[], mscorlib, Version=4.0.0.0, + Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\..\AppInspector.RulesEngine\Resources\languages.json;System.Byte[], mscorlib, Version=4.0.0.0, + Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Output file not specified for custom rules pack command - + Pack default rules request from outside CLI call not supported. Use custom rules option. - + Unable to locate local default rules folder. This command is intended to be run as part of the source code build operation only. Use the custom rules option instead. - + Additional details may be found in log file at {0} - + No additional information or log available - + Additional details may be found in log file at {0} - + No tags found in one or both source paths - + Files contain tag differences: - + failed - + Tags in {0} not detected in {1}: - + succeeded - + Test for all [{0}] in source - + Same file passed in for both sources. Test terminated - + failed - + none - + succeeded - + Found {0} in source - + Missing {0} in source - + Tagtest for [{0}]: - + Rule {0} failed from dupicate rule id specified - + Rule {0} failed from unrecognized language {1} specified - + Verify default rules request from outside CLI call not supported. Use custom rules option. - + Rule {0} failed from missing rule id - + Rule {0} failed from invalid regex '{1}' with {2} - + Verify rules failed. See log file for details - + Verify rules succeeded - + Rule parsing failed for file {0} \ No newline at end of file diff --git a/AppInspector.Common/Utils.cs b/AppInspector.Common/Utils.cs index 8927da0..df4e573 100644 --- a/AppInspector.Common/Utils.cs +++ b/AppInspector.Common/Utils.cs @@ -1,76 +1,85 @@ -namespace Microsoft.ApplicationInspector.Common +using System; +using System.IO; +using System.Reflection; + +namespace Microsoft.ApplicationInspector.Common; + +public static class Utils { - using System.IO; - using System.Reflection; - - public static class Utils + public enum AppPath { - public enum ExitCode - { - Success = 0, - PartialFail = 1, - CriticalError = 2 - } - - private static string? _basePath; - - public enum AppPath { basePath, defaultRulesSrc, defaultRulesPackedFile, defaultLog, tagGroupPref, tagCounterPref }; - - public static string GetVersionString() - { - return string.Format("Microsoft Application Inspector {0}", GetVersion()); - } - - public static string GetVersion() - { - return (Assembly.GetExecutingAssembly().GetCustomAttributes(typeof(AssemblyInformationalVersionAttribute), false) as AssemblyInformationalVersionAttribute[])?[0].InformationalVersion ?? "Unknown"; - } - - public static bool CLIExecutionContext { get; set; } - - public static string GetPath(AppPath pathType) - { - string result = ""; - switch (pathType) - { - case AppPath.basePath: - result = GetBaseAppPath(); - break; - - case AppPath.defaultLog: - result = "appinspector.log.txt"; - break; - - case AppPath.defaultRulesSrc://Packrules source use - result = Path.GetFullPath(Path.Combine(GetBaseAppPath(), "..", "..", "..", "..", "AppInspector", "rules", "default"));//used to ref project folder - break; - - case AppPath.defaultRulesPackedFile://Packrules default output use - result = Path.Combine(GetBaseAppPath(), "..", "..", "..", "..", "AppInspector", "Resources", "defaultRulesPkd.json");//packed default file in project resources - break; - - case AppPath.tagGroupPref://CLI use only - result = Path.Combine(GetBaseAppPath(), "preferences", "tagreportgroups.json"); - break; - - case AppPath.tagCounterPref://CLI use only - result = Path.Combine(GetBaseAppPath(), "preferences", "tagcounters.json"); - break; - } - - result = Path.GetFullPath(result); - return result; - } - - private static string GetBaseAppPath() - { - if (!string.IsNullOrEmpty(_basePath)) - { - return _basePath; - } - - _basePath = Path.GetFullPath(System.AppContext.BaseDirectory); - return _basePath; - } + basePath, + defaultRulesSrc, + defaultRulesPackedFile, + defaultLog, + tagGroupPref, + tagCounterPref } -} + + public enum ExitCode + { + Success = 0, + PartialFail = 1, + CriticalError = 2 + } + + private static string? _basePath; + + public static bool CLIExecutionContext { get; set; } + + public static string GetVersionString() + { + return string.Format("Microsoft Application Inspector {0}", GetVersion()); + } + + public static string GetVersion() + { + return (Assembly.GetExecutingAssembly() + .GetCustomAttributes(typeof(AssemblyInformationalVersionAttribute), false) as + AssemblyInformationalVersionAttribute[])?[0].InformationalVersion ?? "Unknown"; + } + + public static string GetPath(AppPath pathType) + { + var result = ""; + switch (pathType) + { + case AppPath.basePath: + result = GetBaseAppPath(); + break; + + case AppPath.defaultLog: + result = "appinspector.log.txt"; + break; + + case AppPath.defaultRulesSrc: //Packrules source use + result = Path.GetFullPath(Path.Combine(GetBaseAppPath(), "..", "..", "..", "..", "AppInspector", + "rules", "default")); //used to ref project folder + break; + + case AppPath.defaultRulesPackedFile: //Packrules default output use + result = Path.Combine(GetBaseAppPath(), "..", "..", "..", "..", "AppInspector", "Resources", + "defaultRulesPkd.json"); //packed default file in project resources + break; + + case AppPath.tagGroupPref: //CLI use only + result = Path.Combine(GetBaseAppPath(), "preferences", "tagreportgroups.json"); + break; + + case AppPath.tagCounterPref: //CLI use only + result = Path.Combine(GetBaseAppPath(), "preferences", "tagcounters.json"); + break; + } + + result = Path.GetFullPath(result); + return result; + } + + private static string GetBaseAppPath() + { + if (!string.IsNullOrEmpty(_basePath)) return _basePath; + + _basePath = Path.GetFullPath(AppContext.BaseDirectory); + return _basePath; + } +} \ No newline at end of file diff --git a/AppInspector.Logging/AppInspector.Logging.csproj b/AppInspector.Logging/AppInspector.Logging.csproj index cf5cca6..afa23b2 100644 --- a/AppInspector.Logging/AppInspector.Logging.csproj +++ b/AppInspector.Logging/AppInspector.Logging.csproj @@ -1,45 +1,45 @@ - Library - Microsoft.CST.ApplicationInspector.Logging - 0.0.0-placeholder - https://github.com/microsoft/ApplicationInspector - https://github.com/microsoft/ApplicationInspector - Security Static Analyzer - Microsoft Application Inspector is a software source code analysis tool that helps identify and surface well-known features and other interesting characteristics of source code to aid in determining what the software is or what it does. - © Microsoft Corporation. All rights reserved. - true - Application Inspector - Microsoft - 0.0.0-placeholder - ApplicationInspector.Logging - Microsoft.ApplicationInspector.Logging - Microsoft - true - 0.0.0 - 0.0.0 - false - LICENSE.txt - icon-128.png - true - snupkg - enable - 10.0 - net6.0;netstandard2.1 + Library + Microsoft.CST.ApplicationInspector.Logging + 0.0.0-placeholder + https://github.com/microsoft/ApplicationInspector + https://github.com/microsoft/ApplicationInspector + Security Static Analyzer + Microsoft Application Inspector is a software source code analysis tool that helps identify and surface well-known features and other interesting characteristics of source code to aid in determining what the software is or what it does. + © Microsoft Corporation. All rights reserved. + true + Application Inspector + Microsoft + 0.0.0-placeholder + ApplicationInspector.Logging + Microsoft.ApplicationInspector.Logging + Microsoft + true + 0.0.0 + 0.0.0 + false + LICENSE.txt + icon-128.png + true + snupkg + enable + 10.0 + net6.0;netstandard2.1 - - - - + + + + + + + + + + - - - - - - diff --git a/AppInspector.Logging/LogOptions.cs b/AppInspector.Logging/LogOptions.cs index 9036655..a46428e 100644 --- a/AppInspector.Logging/LogOptions.cs +++ b/AppInspector.Logging/LogOptions.cs @@ -1,44 +1,42 @@ // Copyright (C) Microsoft. All rights reserved. // Licensed under the MIT License. See LICENSE.txt in the project root for license information. -namespace Microsoft.ApplicationInspector.Logging +using CommandLine; +using Microsoft.Extensions.Logging; +using Serilog; +using Serilog.Events; + +namespace Microsoft.ApplicationInspector.Logging; + +/// +/// base for common options for the logging in commands +/// +public record LogOptions { - using CommandLine; - using Microsoft.Extensions.Logging; - using Serilog; + [Option('x', "console-verbosity", Required = false, + HelpText = "Console verbosity [Verbose|Debug|Information|Warning|Error|Fatal]", + Default = LogEventLevel.Information)] + public LogEventLevel ConsoleVerbosityLevel { get; set; } = LogEventLevel.Information; - /// - /// base for common options for the logging in commands - /// - public record LogOptions + [Option("disable-console", Required = false, HelpText = "Disable console output of logging messages.", + Default = false)] + public bool DisableConsoleOutput { get; set; } = false; + + [Option('v', "log-file-level", Required = false, + HelpText = "Log file level [Verbose|Debug|Information|Warning|Error|Fatal]", Default = LogEventLevel.Error)] + public LogEventLevel LogFileLevel { get; set; } = LogEventLevel.Error; + + [Option('l', "log-file-path", Required = false, HelpText = "Log file path. If not set, will not log to file.")] + public string? LogFilePath { get; set; } + + public ILoggerFactory GetLoggerFactory() { - [Option('x', "console-verbosity", Required = false, HelpText = "Console verbosity [Verbose|Debug|Information|Warning|Error|Fatal]", Default = Serilog.Events.LogEventLevel.Information)] - public Serilog.Events.LogEventLevel ConsoleVerbosityLevel { get; set; } = Serilog.Events.LogEventLevel.Information; - - [Option("disable-console", Required = false, HelpText = "Disable console output of logging messages.", Default = false)] - public bool DisableConsoleOutput { get; set; } = false; - - [Option('v', "log-file-level", Required = false, HelpText = "Log file level [Verbose|Debug|Information|Warning|Error|Fatal]", Default = Serilog.Events.LogEventLevel.Error)] - public Serilog.Events.LogEventLevel LogFileLevel { get; set; } = Serilog.Events.LogEventLevel.Error; - - [Option('l', "log-file-path", Required = false, HelpText = $"Log file path. If not set, will not log to file.")] - public string? LogFilePath { get; set; } - - public ILoggerFactory GetLoggerFactory() - { - var configuration = new LoggerConfiguration() - .MinimumLevel.Is(ConsoleVerbosityLevel < LogFileLevel ? ConsoleVerbosityLevel : LogFileLevel); - if (!string.IsNullOrEmpty(LogFilePath)) - { - configuration = configuration.WriteTo.File(LogFilePath, LogFileLevel); - } - if (!DisableConsoleOutput) - { - configuration = configuration.WriteTo.Console(ConsoleVerbosityLevel); - } - var serilogLogger = configuration - .CreateLogger(); - return new LoggerFactory().AddSerilog(serilogLogger); - } + var configuration = new LoggerConfiguration() + .MinimumLevel.Is(ConsoleVerbosityLevel < LogFileLevel ? ConsoleVerbosityLevel : LogFileLevel); + if (!string.IsNullOrEmpty(LogFilePath)) configuration = configuration.WriteTo.File(LogFilePath, LogFileLevel); + if (!DisableConsoleOutput) configuration = configuration.WriteTo.Console(ConsoleVerbosityLevel); + var serilogLogger = configuration + .CreateLogger(); + return new LoggerFactory().AddSerilog(serilogLogger); } } \ No newline at end of file diff --git a/AppInspector.RulesEngine/AbstractRuleSet.cs b/AppInspector.RulesEngine/AbstractRuleSet.cs index 505f684..9ba1935 100644 --- a/AppInspector.RulesEngine/AbstractRuleSet.cs +++ b/AppInspector.RulesEngine/AbstractRuleSet.cs @@ -11,258 +11,244 @@ using Microsoft.CST.OAT; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; -namespace Microsoft.ApplicationInspector.RulesEngine +namespace Microsoft.ApplicationInspector.RulesEngine; + +/// +/// Base class for a set of objects to be operated on by the . This is +/// the abstract class used to allow for the default and for use +/// with rules that have extra properties. +/// +public abstract class AbstractRuleSet { + protected readonly List _oatRules = new(); + private readonly Regex _searchInRegex = new("\\((.*),(.*)\\)", RegexOptions.Compiled); + protected ILogger _logger = NullLogger.Instance; + protected IEnumerable _rules => _oatRules.Select(x => x.AppInspectorRule); + /// - /// Base class for a set of objects to be operated on by the . This is the abstract class used to allow for the default and for use with rules that have extra properties. + /// Filters rules within Ruleset by language /// - public abstract class AbstractRuleSet + /// + /// Filtered rules + public IEnumerable ByLanguage(string language) { - protected ILogger _logger = NullLogger.Instance; - protected readonly List _oatRules = new(); - protected IEnumerable _rules { get => _oatRules.Select(x => x.AppInspectorRule); } - private readonly Regex _searchInRegex = new("\\((.*),(.*)\\)", RegexOptions.Compiled); + if (!string.IsNullOrEmpty(language)) + return _oatRules.Where(x => + x.AppInspectorRule.AppliesTo is { } appliesList && appliesList.Contains(language)); + return Array.Empty(); + } - /// - /// Filters rules within Ruleset by language - /// - /// - /// Filtered rules - public IEnumerable ByLanguage(string language) - { - if (!string.IsNullOrEmpty(language)) + /// + /// Filters rules within Ruleset filename + /// + /// + /// Filtered rules + public IEnumerable ByFilename(string input) + { + if (!string.IsNullOrEmpty(input)) + return _oatRules.Where(x => x.AppInspectorRule.CompiledFileRegexes.Any(y => y.IsMatch(input))); + return Array.Empty(); + } + + /// + /// Get the set of rules that apply to all files + /// + /// + public IEnumerable GetUniversalRules() + { + return _oatRules.Where(x => + (x.AppInspectorRule.FileRegexes is null || x.AppInspectorRule.FileRegexes.Length == 0) && + (x.AppInspectorRule.AppliesTo is null || x.AppInspectorRule.AppliesTo.Length == 0)); + } + + /// + /// Convert an AppInspector rule into an OAT rule. + /// + /// The to convert. + /// A if the AI rule was valid otherwise null. + public ConvertedOatRule? AppInspectorRuleToOatRule(Rule rule) + { + var clauses = new List(); + var clauseNumber = 0; + var expression = new StringBuilder("("); + foreach (var pattern in rule.Patterns) + if (GenerateClause(pattern, clauseNumber) is { } clause) { - return _oatRules.Where(x => x.AppInspectorRule.AppliesTo is { } appliesList && appliesList.Contains(language)); - } - return Array.Empty(); - } - - /// - /// Filters rules within Ruleset filename - /// - /// - /// Filtered rules - public IEnumerable ByFilename(string input) - { - if (!string.IsNullOrEmpty(input)) - { - return _oatRules.Where(x => x.AppInspectorRule.CompiledFileRegexes.Any(y => y.IsMatch(input))); - } - return Array.Empty(); - } - - /// - /// Get the set of rules that apply to all files - /// - /// - public IEnumerable GetUniversalRules() - { - return _oatRules.Where(x => (x.AppInspectorRule.FileRegexes is null || x.AppInspectorRule.FileRegexes.Length == 0) && (x.AppInspectorRule.AppliesTo is null || x.AppInspectorRule.AppliesTo.Length == 0)); - } - - /// - /// Convert an AppInspector rule into an OAT rule. - /// - /// The to convert. - /// A if the AI rule was valid otherwise null. - public ConvertedOatRule? AppInspectorRuleToOatRule(Rule rule) - { - var clauses = new List(); - int clauseNumber = 0; - var expression = new StringBuilder("("); - foreach (var pattern in rule.Patterns) - { - if (GenerateClause(pattern, clauseNumber) is { } clause) - { - clauses.Add(clause); - if (clauseNumber > 0) - { - expression.Append(" OR "); - } - expression.Append(clauseNumber); - clauseNumber++; - } - else - { - _logger.LogWarning("Clause could not be generated from pattern {pattern}", pattern.Pattern); - } - } - - if (clauses.Count > 0) - { - expression.Append(')'); + clauses.Add(clause); + if (clauseNumber > 0) expression.Append(" OR "); + expression.Append(clauseNumber); + clauseNumber++; } else { - return new ConvertedOatRule(rule.Id, rule); + _logger.LogWarning("Clause could not be generated from pattern {pattern}", pattern.Pattern); } - foreach (var condition in rule.Conditions ?? Array.Empty()) + if (clauses.Count > 0) + expression.Append(')'); + else + return new ConvertedOatRule(rule.Id, rule); + + foreach (var condition in rule.Conditions ?? Array.Empty()) + { + var clause = GenerateCondition(condition, clauseNumber); + if (clause is { }) { - Clause? clause = GenerateCondition(condition, clauseNumber); - if (clause is { }) - { - clauses.Add(clause); - expression.Append(" AND "); - expression.Append(clauseNumber); - clauseNumber++; - } + clauses.Add(clause); + expression.Append(" AND "); + expression.Append(clauseNumber); + clauseNumber++; } - return new ConvertedOatRule(rule.Id, rule) - { - Clauses = clauses, - Expression = expression.ToString() - }; } - private Clause? GenerateCondition(SearchCondition condition, int clauseNumber) + return new ConvertedOatRule(rule.Id, rule) { - if (condition.Pattern is {} conditionPattern) + Clauses = clauses, + Expression = expression.ToString() + }; + } + + private Clause? GenerateCondition(SearchCondition condition, int clauseNumber) + { + if (condition.Pattern is { } conditionPattern) + { + var subClause = GenerateClause(conditionPattern); + if (subClause is null) { - var subClause = GenerateClause(conditionPattern); - if (subClause is null) + _logger.LogWarning("SubClause for condition could not be generated"); + } + else + { + if (condition.SearchIn?.Equals("finding-only", StringComparison.InvariantCultureIgnoreCase) != false) + return new WithinClause(subClause) + { + Label = clauseNumber.ToString(CultureInfo.InvariantCulture), + FindingOnly = true, + Invert = condition.NegateFinding + }; + + if (condition.SearchIn.StartsWith("finding-region", StringComparison.InvariantCultureIgnoreCase)) { - _logger.LogWarning("SubClause for condition could not be generated"); + var argList = new List(); + var m = _searchInRegex.Match(condition.SearchIn); + if (m.Success) + for (var i = 1; i < m.Groups.Count; i++) + if (int.TryParse(m.Groups[i].Value, out var value)) + argList.Add(value); + else + break; + if (argList.Count == 2) + return new WithinClause(subClause) + { + Label = clauseNumber.ToString(CultureInfo.InvariantCulture), + FindingRegion = true, + Before = argList[0], + After = argList[1], + Invert = condition.NegateFinding + }; + } + else if (condition.SearchIn.Equals("same-line", StringComparison.InvariantCultureIgnoreCase)) + { + return new WithinClause(subClause) + { + Label = clauseNumber.ToString(CultureInfo.InvariantCulture), + SameLineOnly = true, + Invert = condition.NegateFinding + }; + } + else if (condition.SearchIn.Equals("same-file", StringComparison.InvariantCultureIgnoreCase)) + { + return new WithinClause(subClause) + { + Label = clauseNumber.ToString(CultureInfo.InvariantCulture), + SameFile = true, + Invert = condition.NegateFinding + }; + } + else if (condition.SearchIn.Equals("only-before", StringComparison.InvariantCultureIgnoreCase)) + { + return new WithinClause(subClause) + { + Label = clauseNumber.ToString(CultureInfo.InvariantCulture), + OnlyBefore = true, + Invert = condition.NegateFinding + }; + } + else if (condition.SearchIn.Equals("only-after", StringComparison.InvariantCultureIgnoreCase)) + { + return new WithinClause(subClause) + { + Label = clauseNumber.ToString(CultureInfo.InvariantCulture), + OnlyAfter = true, + Invert = condition.NegateFinding + }; } else { - if (condition.SearchIn?.Equals("finding-only", StringComparison.InvariantCultureIgnoreCase) != false) - { - return new WithinClause(subClause) - { - Label = clauseNumber.ToString(CultureInfo.InvariantCulture), - FindingOnly = true, - Invert = condition.NegateFinding - }; - } - else if (condition.SearchIn.StartsWith("finding-region", StringComparison.InvariantCultureIgnoreCase)) - { - var argList = new List(); - Match m = _searchInRegex.Match(condition.SearchIn); - if (m.Success) - { - for (int i = 1; i < m.Groups.Count; i++) - { - if (int.TryParse(m.Groups[i].Value, out int value)) - { - argList.Add(value); - } - else - { - break; - } - } - } - if (argList.Count == 2) - { - return new WithinClause(subClause) - { - Label = clauseNumber.ToString(CultureInfo.InvariantCulture), - FindingRegion = true, - Before = argList[0], - After = argList[1], - Invert = condition.NegateFinding - }; - } - } - else if (condition.SearchIn.Equals("same-line", StringComparison.InvariantCultureIgnoreCase)) - { - return new WithinClause(subClause) - { - Label = clauseNumber.ToString(CultureInfo.InvariantCulture), - SameLineOnly = true, - Invert = condition.NegateFinding - }; - } - else if (condition.SearchIn.Equals("same-file", StringComparison.InvariantCultureIgnoreCase)) - { - return new WithinClause(subClause) - { - Label = clauseNumber.ToString(CultureInfo.InvariantCulture), - SameFile = true, - Invert = condition.NegateFinding - }; - } - else if (condition.SearchIn.Equals("only-before", StringComparison.InvariantCultureIgnoreCase)) - { - return new WithinClause(subClause) - { - Label = clauseNumber.ToString(CultureInfo.InvariantCulture), - OnlyBefore = true, - Invert = condition.NegateFinding - }; - } - else if (condition.SearchIn.Equals("only-after", StringComparison.InvariantCultureIgnoreCase)) - { - return new WithinClause(subClause) - { - Label = clauseNumber.ToString(CultureInfo.InvariantCulture), - OnlyAfter = true, - Invert = condition.NegateFinding - }; - } - else - { - _logger.LogWarning("Search condition {Condition} is not one of the accepted values and this condition will be ignored", condition.SearchIn); - } - } - - } - return null; - } - - private Clause? GenerateClause(SearchPattern pattern, int clauseNumber = -1) - { - if (pattern.Pattern != null) - { - var scopes = pattern.Scopes ?? new PatternScope[] { PatternScope.All }; - var modifiers = pattern.Modifiers?.ToList() ?? new List(); - if (pattern.PatternType is PatternType.String or PatternType.Substring) - { - return new OatSubstringIndexClause(scopes, useWordBoundaries: pattern.PatternType == PatternType.String, xPaths: pattern.XPaths, jsonPaths:pattern.JsonPaths) - { - Label = clauseNumber.ToString(CultureInfo.InvariantCulture),//important to pattern index identification - Data = new List() { pattern.Pattern }, - Capture = true, - Arguments = pattern.Modifiers?.ToList() ?? new List() - }; - } - else if (pattern.PatternType == PatternType.Regex) - { - return new OatRegexWithIndexClause(scopes, null, pattern.XPaths, pattern.JsonPaths) - { - Label = clauseNumber.ToString(CultureInfo - .InvariantCulture), //important to pattern index identification - Data = new List() { pattern.Pattern }, - Capture = true, - Arguments = modifiers - }; - } - else if (pattern.PatternType == PatternType.RegexWord) - { - return new OatRegexWithIndexClause(scopes, null, pattern.XPaths, pattern.JsonPaths) - { - Label = clauseNumber.ToString(CultureInfo.InvariantCulture),//important to pattern index identification - Data = new List() { $"\\b({pattern.Pattern})\\b" }, - Capture = true, - Arguments = pattern.Modifiers?.ToList() ?? new List(), - }; + _logger.LogWarning( + "Search condition {Condition} is not one of the accepted values and this condition will be ignored", + condition.SearchIn); } } - - return null; } - /// - /// Get the OAT Rules used in this RuleSet. - /// - /// - public IEnumerable GetOatRules() => _oatRules; - - /// - /// Get the AppInspector Rules contained in this RuleSet. - /// - /// - public IEnumerable GetAppInspectorRules() => _rules; + return null; } -} + + private Clause? GenerateClause(SearchPattern pattern, int clauseNumber = -1) + { + if (pattern.Pattern != null) + { + var scopes = pattern.Scopes ?? new[] { PatternScope.All }; + var modifiers = pattern.Modifiers?.ToList() ?? new List(); + if (pattern.PatternType is PatternType.String or PatternType.Substring) + return new OatSubstringIndexClause(scopes, useWordBoundaries: pattern.PatternType == PatternType.String, + xPaths: pattern.XPaths, jsonPaths: pattern.JsonPaths) + { + Label = clauseNumber.ToString(CultureInfo + .InvariantCulture), //important to pattern index identification + Data = new List { pattern.Pattern }, + Capture = true, + Arguments = pattern.Modifiers?.ToList() ?? new List() + }; + if (pattern.PatternType == PatternType.Regex) + return new OatRegexWithIndexClause(scopes, null, pattern.XPaths, pattern.JsonPaths) + { + Label = clauseNumber.ToString(CultureInfo + .InvariantCulture), //important to pattern index identification + Data = new List { pattern.Pattern }, + Capture = true, + Arguments = modifiers + }; + if (pattern.PatternType == PatternType.RegexWord) + return new OatRegexWithIndexClause(scopes, null, pattern.XPaths, pattern.JsonPaths) + { + Label = clauseNumber.ToString(CultureInfo + .InvariantCulture), //important to pattern index identification + Data = new List { $"\\b({pattern.Pattern})\\b" }, + Capture = true, + Arguments = pattern.Modifiers?.ToList() ?? new List() + }; + } + + return null; + } + + /// + /// Get the OAT Rules used in this RuleSet. + /// + /// + public IEnumerable GetOatRules() + { + return _oatRules; + } + + /// + /// Get the AppInspector Rules contained in this RuleSet. + /// + /// + public IEnumerable GetAppInspectorRules() + { + return _rules; + } +} \ No newline at end of file diff --git a/AppInspector.RulesEngine/AppInspector.RulesEngine.csproj b/AppInspector.RulesEngine/AppInspector.RulesEngine.csproj index 4e1e39a..83077a1 100644 --- a/AppInspector.RulesEngine/AppInspector.RulesEngine.csproj +++ b/AppInspector.RulesEngine/AppInspector.RulesEngine.csproj @@ -1,60 +1,60 @@  - - Library - Microsoft.CST.ApplicationInspector.RulesEngine - 0.0.0-placeholder - https://github.com/microsoft/ApplicationInspector - https://github.com/microsoft/ApplicationInspector - Security Static Analyzer - Microsoft Application Inspector is a software source code analysis tool that helps identify and surface well-known features and other interesting characteristics of source code to aid in determining what the software is or what it does. - © Microsoft Corporation. All rights reserved. - true - Application Inspector - Microsoft - 0.0.0-placeholder - ApplicationInspector.RulesEngine - Microsoft.ApplicationInspector.RulesEngine - Microsoft - true - 0.0.0 - 0.0.0 - false - LICENSE.txt - icon-128.png - true - snupkg - enable - 10.0 - net6.0;netstandard2.1 - + + Library + Microsoft.CST.ApplicationInspector.RulesEngine + 0.0.0-placeholder + https://github.com/microsoft/ApplicationInspector + https://github.com/microsoft/ApplicationInspector + Security Static Analyzer + Microsoft Application Inspector is a software source code analysis tool that helps identify and surface well-known features and other interesting characteristics of source code to aid in determining what the software is or what it does. + © Microsoft Corporation. All rights reserved. + true + Application Inspector + Microsoft + 0.0.0-placeholder + ApplicationInspector.RulesEngine + Microsoft.ApplicationInspector.RulesEngine + Microsoft + true + 0.0.0 + 0.0.0 + false + LICENSE.txt + icon-128.png + true + snupkg + enable + 10.0 + net6.0;netstandard2.1 + - - - - - - - - + + + + + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/AppInspector.RulesEngine/Boundary.cs b/AppInspector.RulesEngine/Boundary.cs index a6f3a1c..dbe336d 100644 --- a/AppInspector.RulesEngine/Boundary.cs +++ b/AppInspector.RulesEngine/Boundary.cs @@ -1,21 +1,20 @@ // Copyright (C) Microsoft. All rights reserved. // Licensed under the MIT License. See LICENSE.txt in the project root for license information. -namespace Microsoft.ApplicationInspector.RulesEngine +namespace Microsoft.ApplicationInspector.RulesEngine; + +/// +/// Class represents boundary in text +/// +public class Boundary { /// - /// Class represents boundary in text + /// Starting position /// - public class Boundary - { - /// - /// Starting position - /// - public int Index { get; set; } + public int Index { get; set; } - /// - /// Length of boundary - /// - public int Length { get; set; } - } + /// + /// Length of boundary + /// + public int Length { get; set; } } \ No newline at end of file diff --git a/AppInspector.RulesEngine/Comment.cs b/AppInspector.RulesEngine/Comment.cs index 3c83c44..2e08a05 100644 --- a/AppInspector.RulesEngine/Comment.cs +++ b/AppInspector.RulesEngine/Comment.cs @@ -3,23 +3,22 @@ using Newtonsoft.Json; -namespace Microsoft.ApplicationInspector.RulesEngine +namespace Microsoft.ApplicationInspector.RulesEngine; + +/// +/// Comment class to hold information about comment for each language +/// +internal class Comment { - /// - /// Comment class to hold information about comment for each language - /// - internal class Comment - { - [JsonProperty(PropertyName ="language")] - public string[]? Languages { get; set; } + [JsonProperty(PropertyName = "language")] + public string[]? Languages { get; set; } - [JsonProperty(PropertyName ="inline")] - public string? Inline { get; set; } + [JsonProperty(PropertyName = "inline")] + public string? Inline { get; set; } - [JsonProperty(PropertyName ="prefix")] - public string? Prefix { get; set; } + [JsonProperty(PropertyName = "prefix")] + public string? Prefix { get; set; } - [JsonProperty(PropertyName ="suffix")] - public string? Suffix { get; set; } - } + [JsonProperty(PropertyName = "suffix")] + public string? Suffix { get; set; } } \ No newline at end of file diff --git a/AppInspector.RulesEngine/Confidence.cs b/AppInspector.RulesEngine/Confidence.cs index 46cbff9..654aeab 100644 --- a/AppInspector.RulesEngine/Confidence.cs +++ b/AppInspector.RulesEngine/Confidence.cs @@ -1,11 +1,15 @@ -using Newtonsoft.Json; +using System; +using Newtonsoft.Json; using Newtonsoft.Json.Converters; -namespace Microsoft.ApplicationInspector.RulesEngine -{ - using System; +namespace Microsoft.ApplicationInspector.RulesEngine; - [Flags] - [JsonConverter(typeof(StringEnumConverter))] - public enum Confidence { Unspecified = 0, Low = 1, Medium = 2, High = 4 } +[Flags] +[JsonConverter(typeof(StringEnumConverter))] +public enum Confidence +{ + Unspecified = 0, + Low = 1, + Medium = 2, + High = 4 } \ No newline at end of file diff --git a/AppInspector.RulesEngine/LanguageInfo.cs b/AppInspector.RulesEngine/LanguageInfo.cs index b0bb738..82b449f 100644 --- a/AppInspector.RulesEngine/LanguageInfo.cs +++ b/AppInspector.RulesEngine/LanguageInfo.cs @@ -4,26 +4,28 @@ using Newtonsoft.Json; using Newtonsoft.Json.Converters; -namespace Microsoft.ApplicationInspector.RulesEngine +namespace Microsoft.ApplicationInspector.RulesEngine; + +/// +/// Content Type class +/// +public class LanguageInfo { - /// - /// Content Type class - /// - public class LanguageInfo + public enum LangFileType { - public enum LangFileType { Code, Build }; - - [JsonProperty(PropertyName ="name")] - public string Name { get; set; } = ""; - - [JsonProperty(PropertyName ="extensions")] - public string[]? Extensions { get; set; } - - [JsonProperty(PropertyName ="file-names")] - public string[]? FileNames { get; set; } - - [JsonProperty(PropertyName ="type")] - [JsonConverter(typeof(StringEnumConverter))] - public LangFileType Type { get; set; } = LangFileType.Code; + Code, + Build } + + [JsonProperty(PropertyName = "name")] public string Name { get; set; } = ""; + + [JsonProperty(PropertyName = "extensions")] + public string[]? Extensions { get; set; } + + [JsonProperty(PropertyName = "file-names")] + public string[]? FileNames { get; set; } + + [JsonProperty(PropertyName = "type")] + [JsonConverter(typeof(StringEnumConverter))] + public LangFileType Type { get; set; } = LangFileType.Code; } \ No newline at end of file diff --git a/AppInspector.RulesEngine/Languages.cs b/AppInspector.RulesEngine/Languages.cs index ca2cdc3..948edbb 100644 --- a/AppInspector.RulesEngine/Languages.cs +++ b/AppInspector.RulesEngine/Languages.cs @@ -1,201 +1,193 @@ // Copyright (C) Microsoft. All rights reserved. Licensed under the MIT License. +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Newtonsoft.Json; -namespace Microsoft.ApplicationInspector.RulesEngine +namespace Microsoft.ApplicationInspector.RulesEngine; + +/// +/// Helper class for language based commenting +/// +public sealed class Languages { - using Microsoft.Extensions.Logging; - using Microsoft.Extensions.Logging.Abstractions; - using System; - using System.Collections.Generic; - using System.Globalization; - using System.IO; - using System.Linq; - using System.Reflection; - + private const string CommentResourcePath = "Microsoft.ApplicationInspector.RulesEngine.Resources.comments.json"; + private const string LanguagesResourcePath = "Microsoft.ApplicationInspector.RulesEngine.Resources.languages.json"; + private readonly List _comments; + private readonly List _languageInfos; + private readonly ILogger _logger; - /// - /// Helper class for language based commenting - /// - public sealed class Languages + public Languages(ILoggerFactory? loggerFactory = null, Stream? commentsStream = null, + Stream? languagesStream = null) { - private readonly List _comments; - private readonly List _languageInfos; - private readonly ILogger _logger; + _logger = loggerFactory?.CreateLogger() ?? new NullLogger(); + var assembly = typeof(Languages).Assembly; + var commentResource = commentsStream ?? assembly.GetManifestResourceStream(CommentResourcePath); - private const string CommentResourcePath = "Microsoft.ApplicationInspector.RulesEngine.Resources.comments.json"; - private const string LanguagesResourcePath = "Microsoft.ApplicationInspector.RulesEngine.Resources.languages.json"; - public Languages(ILoggerFactory? loggerFactory = null, Stream? commentsStream = null, Stream? languagesStream = null) + if (commentResource is null) { - _logger = loggerFactory?.CreateLogger() ?? new NullLogger(); - Assembly assembly = typeof(Languages).Assembly; - Stream? commentResource = commentsStream ?? assembly.GetManifestResourceStream(CommentResourcePath); - - if (commentResource is null) - { - _logger.LogError("Failed to load embedded comments configuration from {CommentResourcePath}", CommentResourcePath); - _comments = new List(); - } - else - { - using StreamReader commentStreamReader = new(commentResource); - using JsonReader commentJsonReader = new JsonTextReader(commentStreamReader); - JsonSerializer jsonSerializer = new(); - _comments = jsonSerializer.Deserialize>(commentJsonReader) ?? new List(); - } - - Stream? languagesResource = languagesStream ?? assembly.GetManifestResourceStream(LanguagesResourcePath); - if (languagesResource is null) - { - _logger.LogError("Failed to load embedded languages configuration from {LanguagesResourcePath}", LanguagesResourcePath); - _languageInfos = new List(); - } - else - { - using StreamReader languagesStreamReader = new(languagesResource); - using JsonReader languagesJsonReader = new JsonTextReader(languagesStreamReader); - JsonSerializer jsonSerializer = new(); - _languageInfos = jsonSerializer.Deserialize>(languagesJsonReader) ?? new List(); - } + _logger.LogError("Failed to load embedded comments configuration from {CommentResourcePath}", + CommentResourcePath); + _comments = new List(); + } + else + { + using StreamReader commentStreamReader = new(commentResource); + using JsonReader commentJsonReader = new JsonTextReader(commentStreamReader); + JsonSerializer jsonSerializer = new(); + _comments = jsonSerializer.Deserialize>(commentJsonReader) ?? new List(); } - public static Languages FromConfigurationFiles(ILoggerFactory? loggerFactory = null, string? commentsPath = null, string? languagesPath = null) + var languagesResource = languagesStream ?? assembly.GetManifestResourceStream(LanguagesResourcePath); + if (languagesResource is null) { - Stream? commentsStream = commentsPath is null ? null : File.OpenRead(commentsPath); - Stream? languagesStream = languagesPath is null ? null : File.OpenRead(languagesPath); - try - { - return new Languages(loggerFactory, commentsStream, languagesStream); - } - finally - { - commentsStream?.Dispose(); - languagesStream?.Dispose(); - } + _logger.LogError("Failed to load embedded languages configuration from {LanguagesResourcePath}", + LanguagesResourcePath); + _languageInfos = new List(); } - - /// - /// Returns the language for a given file name if detected. - /// - /// The FileName to check. - /// If this returns true, a valid LanguageInfo object. If false, undefined. - /// True if the language could be detected based on the filename. - public bool FromFileNameOut(string fileName, out LanguageInfo info) + else { - info = new LanguageInfo(); - - return FromFileName(fileName, ref info); - } - - /// - /// Returns for a given filename. It is recommended to use instead. - /// - /// File name - /// Ref to an existing LanguageInfo object to assign the result to, if true is returned. - /// True if the language could be detected based on the filename - public bool FromFileName(string fileName, ref LanguageInfo info) - { - if (fileName == null) - { - return false; - } - - string file = Path.GetFileName(fileName).ToLower(CultureInfo.CurrentCulture); - string ext = Path.GetExtension(file); - - // Look for whole filename first - if (_languageInfos.FirstOrDefault(item => item.FileNames?.Contains(file,StringComparer.InvariantCultureIgnoreCase) ?? false) is LanguageInfo langInfo) - { - info = langInfo; - return true; - } - - // Look for extension only ext is defined - if (!string.IsNullOrEmpty(ext)) - { - if (_languageInfos.FirstOrDefault(item => item.Extensions?.Contains(ext,StringComparer.InvariantCultureIgnoreCase) ?? false) is LanguageInfo extLangInfo) - { - info = extLangInfo; - return true; - } - } - - return false; - } - - /// - /// Gets comment inline for given language - /// - /// Language - /// Commented string - public string GetCommentInline(string language) - { - string result = string.Empty; - - if (language != null) - { - foreach (Comment comment in _comments) - { - if (Array.Exists(comment.Languages ?? new string[] { "" }, x => x.Equals(language, StringComparison.InvariantCultureIgnoreCase)) && comment.Inline is { }) - return comment.Inline; - } - } - - return result; - } - - /// - /// Gets comment preffix for given language - /// - /// Language - /// Commented string - public string GetCommentPrefix(string language) - { - string result = string.Empty; - - if (language != null) - { - foreach (Comment comment in _comments) - { - if ((comment.Languages?.Contains(language.ToLower(CultureInfo.InvariantCulture)) ?? false) && comment.Prefix is { }) - return comment.Prefix; - } - } - - return result; - } - - /// - /// Gets comment suffix for given language - /// - /// Language - /// Commented string - public string GetCommentSuffix(string language) - { - string result = string.Empty; - - if (language != null) - { - foreach (Comment comment in _comments) - { - if (Array.Exists(comment.Languages ?? new string[] { "" }, x => x.Equals(language, StringComparison.InvariantCultureIgnoreCase)) && comment.Suffix is { }) - return comment.Suffix; - } - } - - return result; - } - - /// - /// Get names of all known languages - /// - /// Returns list of names - public string[] GetNames() - { - var names = from x in _languageInfos - select x.Name; - - return names.ToArray(); + using StreamReader languagesStreamReader = new(languagesResource); + using JsonReader languagesJsonReader = new JsonTextReader(languagesStreamReader); + JsonSerializer jsonSerializer = new(); + _languageInfos = jsonSerializer.Deserialize>(languagesJsonReader) ?? + new List(); } } + + public static Languages FromConfigurationFiles(ILoggerFactory? loggerFactory = null, string? commentsPath = null, + string? languagesPath = null) + { + Stream? commentsStream = commentsPath is null ? null : File.OpenRead(commentsPath); + Stream? languagesStream = languagesPath is null ? null : File.OpenRead(languagesPath); + try + { + return new Languages(loggerFactory, commentsStream, languagesStream); + } + finally + { + commentsStream?.Dispose(); + languagesStream?.Dispose(); + } + } + + /// + /// Returns the language for a given file name if detected. + /// + /// The FileName to check. + /// If this returns true, a valid LanguageInfo object. If false, undefined. + /// True if the language could be detected based on the filename. + public bool FromFileNameOut(string fileName, out LanguageInfo info) + { + info = new LanguageInfo(); + + return FromFileName(fileName, ref info); + } + + /// + /// Returns for a given filename. It is recommended to use + /// instead. + /// + /// File name + /// Ref to an existing LanguageInfo object to assign the result to, if true is returned. + /// True if the language could be detected based on the filename + public bool FromFileName(string fileName, ref LanguageInfo info) + { + if (fileName == null) return false; + + var file = Path.GetFileName(fileName).ToLower(CultureInfo.CurrentCulture); + var ext = Path.GetExtension(file); + + // Look for whole filename first + if (_languageInfos.FirstOrDefault(item => + item.FileNames?.Contains(file, StringComparer.InvariantCultureIgnoreCase) ?? false) is LanguageInfo + langInfo) + { + info = langInfo; + return true; + } + + // Look for extension only ext is defined + if (!string.IsNullOrEmpty(ext)) + if (_languageInfos.FirstOrDefault(item => + item.Extensions?.Contains(ext, StringComparer.InvariantCultureIgnoreCase) ?? false) is LanguageInfo + extLangInfo) + { + info = extLangInfo; + return true; + } + + return false; + } + + /// + /// Gets comment inline for given language + /// + /// Language + /// Commented string + public string GetCommentInline(string language) + { + var result = string.Empty; + + if (language != null) + foreach (var comment in _comments) + if (Array.Exists(comment.Languages ?? new[] { "" }, + x => x.Equals(language, StringComparison.InvariantCultureIgnoreCase)) && comment.Inline is { }) + return comment.Inline; + + return result; + } + + /// + /// Gets comment preffix for given language + /// + /// Language + /// Commented string + public string GetCommentPrefix(string language) + { + var result = string.Empty; + + if (language != null) + foreach (var comment in _comments) + if ((comment.Languages?.Contains(language.ToLower(CultureInfo.InvariantCulture)) ?? false) && + comment.Prefix is { }) + return comment.Prefix; + + return result; + } + + /// + /// Gets comment suffix for given language + /// + /// Language + /// Commented string + public string GetCommentSuffix(string language) + { + var result = string.Empty; + + if (language != null) + foreach (var comment in _comments) + if (Array.Exists(comment.Languages ?? new[] { "" }, + x => x.Equals(language, StringComparison.InvariantCultureIgnoreCase)) && comment.Suffix is { }) + return comment.Suffix; + + return result; + } + + /// + /// Get names of all known languages + /// + /// Returns list of names + public string[] GetNames() + { + var names = from x in _languageInfos + select x.Name; + + return names.ToArray(); + } } \ No newline at end of file diff --git a/AppInspector.RulesEngine/Location.cs b/AppInspector.RulesEngine/Location.cs index bf5f827..e22e0f4 100644 --- a/AppInspector.RulesEngine/Location.cs +++ b/AppInspector.RulesEngine/Location.cs @@ -1,11 +1,10 @@ // Copyright (C) Microsoft. All rights reserved. // Licensed under the MIT License. See LICENSE.txt in the project root for license information. -namespace Microsoft.ApplicationInspector.RulesEngine +namespace Microsoft.ApplicationInspector.RulesEngine; + +public class Location { - public class Location - { - public int Line { get; set; } = 0; - public int Column { get; set; } = 0; - } + public int Line { get; set; } = 0; + public int Column { get; set; } = 0; } \ No newline at end of file diff --git a/AppInspector.RulesEngine/MatchRecord.cs b/AppInspector.RulesEngine/MatchRecord.cs index 666f25f..ad834c5 100644 --- a/AppInspector.RulesEngine/MatchRecord.cs +++ b/AppInspector.RulesEngine/MatchRecord.cs @@ -4,166 +4,158 @@ using System.Diagnostics.CodeAnalysis; using Newtonsoft.Json; -namespace Microsoft.ApplicationInspector.RulesEngine +namespace Microsoft.ApplicationInspector.RulesEngine; + +/// +/// Represents augmented record of result issue from rules engine +/// +public class MatchRecord { /// - /// Represents augmented record of result issue from rules engine + /// Force required arg to avoid null rule /// - public class MatchRecord + /// + public MatchRecord(Rule rule) { - /// - /// Force required arg to avoid null rule - /// - /// - public MatchRecord(Rule rule) - { - Rule = rule; - RuleId = rule.Id; - RuleName = rule.Name; - RuleDescription = rule.Description; - Tags = rule.Tags; - Severity = rule.Severity; - } - - [JsonConstructor] - public MatchRecord(string ruleId, string ruleName) - { - RuleId = ruleId; - RuleName = ruleName; - } - - [JsonIgnore] - [ExcludeFromCodeCoverage] - public Rule? Rule { get; } - - /// - /// Rule Id found in matching rule - /// - [JsonProperty(PropertyName ="ruleId")] - [ExcludeFromCodeCoverage] - public string RuleId { get; set; } - - /// - /// Rule name found in matching rule - /// - [JsonProperty(PropertyName ="ruleName")] - [ExcludeFromCodeCoverage] - public string RuleName { get; set; } - - /// - /// Rule description found in matching rule - /// - [JsonProperty(PropertyName ="ruleDescription")] - [ExcludeFromCodeCoverage] - public string? RuleDescription { get; set; } - - /// - /// Tags in matching rule - /// - [JsonProperty(PropertyName ="tags")] - [ExcludeFromCodeCoverage] - public string[]? Tags { get; set; } - - /// - /// Rule severity - /// _rule - [JsonProperty(PropertyName ="severity")] - [ExcludeFromCodeCoverage] - public Severity Severity { get; set; } - - [JsonIgnore] - [ExcludeFromCodeCoverage] - public SearchPattern? MatchingPattern { get; set; } - - /// - /// Matching pattern found in matching rule - /// - [JsonProperty(PropertyName ="pattern")] - [ExcludeFromCodeCoverage] - public string? Pattern => MatchingPattern?.Pattern; - - /// - /// Pattern confidence in matching rule pattern - /// - [JsonProperty(PropertyName ="confidence")] - [ExcludeFromCodeCoverage] - public Confidence Confidence => MatchingPattern?.Confidence ?? Confidence.Unspecified; - - /// - /// Pattern type of matching pattern - /// - [JsonProperty(PropertyName ="type")] - [ExcludeFromCodeCoverage] - public string? PatternType => MatchingPattern?.PatternType.ToString(); - - [JsonIgnore] - [ExcludeFromCodeCoverage] - public TextContainer? FullTextContainer { get; set; } - - /// - /// Internal to namespace only - /// - [JsonIgnore] - [ExcludeFromCodeCoverage] - public LanguageInfo LanguageInfo { get; set; } = new LanguageInfo(); - - /// - /// Friendly source type - /// - [JsonProperty(PropertyName ="language")] - public string? Language => LanguageInfo?.Name; - - /// - /// Filename of this match - /// - [JsonProperty(PropertyName ="fileName")] - [ExcludeFromCodeCoverage] - public string? FileName { get; set; } - - /// - /// Matching text for this record - /// - [JsonProperty(PropertyName ="sample")] - [ExcludeFromCodeCoverage] - public string Sample { get; set; } = ""; - - /// - /// Matching surrounding context text for sample in this record - /// - [JsonProperty(PropertyName ="excerpt")] - [ExcludeFromCodeCoverage] - public string Excerpt { get; set; } = ""; - - [JsonIgnore] - [ExcludeFromCodeCoverage] - public Boundary Boundary { get; set; } = new Boundary(); - - /// - /// Starting line location of the matching text - /// - [JsonProperty(PropertyName ="startLocationLine")] - [ExcludeFromCodeCoverage] - public int StartLocationLine { get; set; } - - /// - /// Starting column location of the matching text - /// - [JsonProperty(PropertyName ="startLocationColumn")] - [ExcludeFromCodeCoverage] - public int StartLocationColumn { get; set; } - - /// - /// Ending line location of the matching text - /// - [JsonProperty(PropertyName ="endLocationLine")] - [ExcludeFromCodeCoverage] - public int EndLocationLine { get; set; } - - /// - /// Ending column of the matching text - /// - [JsonProperty(PropertyName ="endLocationColumn")] - [ExcludeFromCodeCoverage] - public int EndLocationColumn { get; set; } + Rule = rule; + RuleId = rule.Id; + RuleName = rule.Name; + RuleDescription = rule.Description; + Tags = rule.Tags; + Severity = rule.Severity; } + + [JsonConstructor] + public MatchRecord(string ruleId, string ruleName) + { + RuleId = ruleId; + RuleName = ruleName; + } + + [JsonIgnore] [ExcludeFromCodeCoverage] public Rule? Rule { get; } + + /// + /// Rule Id found in matching rule + /// + [JsonProperty(PropertyName = "ruleId")] + [ExcludeFromCodeCoverage] + public string RuleId { get; set; } + + /// + /// Rule name found in matching rule + /// + [JsonProperty(PropertyName = "ruleName")] + [ExcludeFromCodeCoverage] + public string RuleName { get; set; } + + /// + /// Rule description found in matching rule + /// + [JsonProperty(PropertyName = "ruleDescription")] + [ExcludeFromCodeCoverage] + public string? RuleDescription { get; set; } + + /// + /// Tags in matching rule + /// + [JsonProperty(PropertyName = "tags")] + [ExcludeFromCodeCoverage] + public string[]? Tags { get; set; } + + /// + /// Rule severity + /// + /// _rule + [JsonProperty(PropertyName = "severity")] + [ExcludeFromCodeCoverage] + public Severity Severity { get; set; } + + [JsonIgnore] [ExcludeFromCodeCoverage] public SearchPattern? MatchingPattern { get; set; } + + /// + /// Matching pattern found in matching rule + /// + [JsonProperty(PropertyName = "pattern")] + [ExcludeFromCodeCoverage] + public string? Pattern => MatchingPattern?.Pattern; + + /// + /// Pattern confidence in matching rule pattern + /// + [JsonProperty(PropertyName = "confidence")] + [ExcludeFromCodeCoverage] + public Confidence Confidence => MatchingPattern?.Confidence ?? Confidence.Unspecified; + + /// + /// Pattern type of matching pattern + /// + [JsonProperty(PropertyName = "type")] + [ExcludeFromCodeCoverage] + public string? PatternType => MatchingPattern?.PatternType.ToString(); + + [JsonIgnore] [ExcludeFromCodeCoverage] public TextContainer? FullTextContainer { get; set; } + + /// + /// Internal to namespace only + /// + [JsonIgnore] + [ExcludeFromCodeCoverage] + public LanguageInfo LanguageInfo { get; set; } = new(); + + /// + /// Friendly source type + /// + [JsonProperty(PropertyName = "language")] + public string? Language => LanguageInfo?.Name; + + /// + /// Filename of this match + /// + [JsonProperty(PropertyName = "fileName")] + [ExcludeFromCodeCoverage] + public string? FileName { get; set; } + + /// + /// Matching text for this record + /// + [JsonProperty(PropertyName = "sample")] + [ExcludeFromCodeCoverage] + public string Sample { get; set; } = ""; + + /// + /// Matching surrounding context text for sample in this record + /// + [JsonProperty(PropertyName = "excerpt")] + [ExcludeFromCodeCoverage] + public string Excerpt { get; set; } = ""; + + [JsonIgnore] [ExcludeFromCodeCoverage] public Boundary Boundary { get; set; } = new(); + + /// + /// Starting line location of the matching text + /// + [JsonProperty(PropertyName = "startLocationLine")] + [ExcludeFromCodeCoverage] + public int StartLocationLine { get; set; } + + /// + /// Starting column location of the matching text + /// + [JsonProperty(PropertyName = "startLocationColumn")] + [ExcludeFromCodeCoverage] + public int StartLocationColumn { get; set; } + + /// + /// Ending line location of the matching text + /// + [JsonProperty(PropertyName = "endLocationLine")] + [ExcludeFromCodeCoverage] + public int EndLocationLine { get; set; } + + /// + /// Ending column of the matching text + /// + [JsonProperty(PropertyName = "endLocationColumn")] + [ExcludeFromCodeCoverage] + public int EndLocationColumn { get; set; } } \ No newline at end of file diff --git a/AppInspector.RulesEngine/OatExtensions/ApplicationInspectorAnalyzer.cs b/AppInspector.RulesEngine/OatExtensions/ApplicationInspectorAnalyzer.cs index 57365e3..6f72acd 100644 --- a/AppInspector.RulesEngine/OatExtensions/ApplicationInspectorAnalyzer.cs +++ b/AppInspector.RulesEngine/OatExtensions/ApplicationInspectorAnalyzer.cs @@ -1,15 +1,14 @@ using Microsoft.CST.OAT; using Microsoft.Extensions.Logging; -namespace Microsoft.ApplicationInspector.RulesEngine.OatExtensions +namespace Microsoft.ApplicationInspector.RulesEngine.OatExtensions; + +public class ApplicationInspectorAnalyzer : Analyzer { - public class ApplicationInspectorAnalyzer : Analyzer + public ApplicationInspectorAnalyzer(ILoggerFactory? loggerFactory = null) { - public ApplicationInspectorAnalyzer(ILoggerFactory? loggerFactory = null) - { - SetOperation(new WithinOperation(this, loggerFactory)); - SetOperation(new OatRegexWithIndexOperation(this, loggerFactory)); - SetOperation(new OatSubstringIndexOperation(this, loggerFactory)); - } + SetOperation(new WithinOperation(this, loggerFactory)); + SetOperation(new OatRegexWithIndexOperation(this, loggerFactory)); + SetOperation(new OatSubstringIndexOperation(this, loggerFactory)); } -} +} \ No newline at end of file diff --git a/AppInspector.RulesEngine/OatExtensions/ConvertedOatRule.cs b/AppInspector.RulesEngine/OatExtensions/ConvertedOatRule.cs index d8e662e..8f57cf6 100644 --- a/AppInspector.RulesEngine/OatExtensions/ConvertedOatRule.cs +++ b/AppInspector.RulesEngine/OatExtensions/ConvertedOatRule.cs @@ -1,18 +1,17 @@ -namespace Microsoft.ApplicationInspector.RulesEngine.OatExtensions -{ - /// - /// Wrapper for OAT based processing - /// - public class ConvertedOatRule : CST.OAT.Rule - { - public ConvertedOatRule(string name, Rule rule) : base(name) - { - AppInspectorRule = rule; - } +namespace Microsoft.ApplicationInspector.RulesEngine.OatExtensions; - /// - /// Native Application Inspector Rule to preserve format and instance data - /// - public Rule AppInspectorRule { get; } +/// +/// Wrapper for OAT based processing +/// +public class ConvertedOatRule : CST.OAT.Rule +{ + public ConvertedOatRule(string name, Rule rule) : base(name) + { + AppInspectorRule = rule; } -} + + /// + /// Native Application Inspector Rule to preserve format and instance data + /// + public Rule AppInspectorRule { get; } +} \ No newline at end of file diff --git a/AppInspector.RulesEngine/OatExtensions/OatRegexWithIndexClause.cs b/AppInspector.RulesEngine/OatExtensions/OatRegexWithIndexClause.cs index d7ac43f..56bf25b 100644 --- a/AppInspector.RulesEngine/OatExtensions/OatRegexWithIndexClause.cs +++ b/AppInspector.RulesEngine/OatExtensions/OatRegexWithIndexClause.cs @@ -2,22 +2,22 @@ using Microsoft.CST.OAT; -namespace Microsoft.ApplicationInspector.RulesEngine.OatExtensions +namespace Microsoft.ApplicationInspector.RulesEngine.OatExtensions; + +public class OatRegexWithIndexClause : Clause { - public class OatRegexWithIndexClause : Clause + public OatRegexWithIndexClause(PatternScope[] scopes, string? field = null, string[]? xPaths = null, + string[]? jsonPaths = null) : base(Operation.Custom, field) { - public OatRegexWithIndexClause(PatternScope[] scopes, string? field = null, string[]? xPaths = null, string[]? jsonPaths = null) : base(Operation.Custom, field) - { - Scopes = scopes; - CustomOperation = "RegexWithIndex"; - XPaths = xPaths; - JsonPaths = jsonPaths; - } - - public string[]? JsonPaths { get; } - - public string[]? XPaths { get; } - - public PatternScope[] Scopes { get; } + Scopes = scopes; + CustomOperation = "RegexWithIndex"; + XPaths = xPaths; + JsonPaths = jsonPaths; } + + public string[]? JsonPaths { get; } + + public string[]? XPaths { get; } + + public PatternScope[] Scopes { get; } } \ No newline at end of file diff --git a/AppInspector.RulesEngine/OatExtensions/OatRegexWithIndexOperation.cs b/AppInspector.RulesEngine/OatExtensions/OatRegexWithIndexOperation.cs index e4f923b..1b372af 100644 --- a/AppInspector.RulesEngine/OatExtensions/OatRegexWithIndexOperation.cs +++ b/AppInspector.RulesEngine/OatExtensions/OatRegexWithIndexOperation.cs @@ -9,180 +9,162 @@ using Microsoft.CST.OAT.Utils; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; -namespace Microsoft.ApplicationInspector.RulesEngine.OatExtensions +namespace Microsoft.ApplicationInspector.RulesEngine.OatExtensions; + +/// +/// The Custom Operation to enable identification of pattern index in result used by Application Inspector to report +/// why a given +/// result was matched and to retrieve other pattern level meta-data +/// +public class OatRegexWithIndexOperation : OatOperation { + private readonly ILogger _logger; + private readonly ILoggerFactory _loggerFactory; + private readonly ConcurrentDictionary<(string, RegexOptions), Regex?> RegexCache = new(); + /// - /// The Custom Operation to enable identification of pattern index in result used by Application Inspector to report why a given - /// result was matched and to retrieve other pattern level meta-data + /// Create an OatOperation given an analyzer /// - public class OatRegexWithIndexOperation : OatOperation + /// The analyzer context to work with + /// Logger Factory to use + public OatRegexWithIndexOperation(Analyzer analyzer, ILoggerFactory? loggerFactory = null) : base(Operation.Custom, + analyzer) { - private readonly ConcurrentDictionary<(string, RegexOptions), Regex?> RegexCache = new(); - private readonly ILoggerFactory _loggerFactory; - private readonly ILogger _logger; + _loggerFactory = loggerFactory ?? new NullLoggerFactory(); + _logger = _loggerFactory.CreateLogger(); + CustomOperation = "RegexWithIndex"; + OperationDelegate = RegexWithIndexOperationDelegate; + ValidationDelegate = RegexWithIndexValidationDelegate; + } - /// - /// Create an OatOperation given an analyzer - /// - /// The analyzer context to work with - /// Logger Factory to use - public OatRegexWithIndexOperation(Analyzer analyzer, ILoggerFactory? loggerFactory = null) : base(Operation.Custom, analyzer) + private static IEnumerable RegexWithIndexValidationDelegate(CST.OAT.Rule rule, Clause clause) + { + if (clause.Data?.Count is null or 0) + yield return new Violation( + string.Format(Strings.Get("Err_ClauseNoData"), rule.Name, + clause.Label ?? rule.Clauses.IndexOf(clause).ToString(CultureInfo.InvariantCulture)), rule, clause); + else if (clause.Data is List regexList) + foreach (var regex in regexList) + if (!Helpers.IsValidRegex(regex)) + yield return new Violation( + string.Format(Strings.Get("Err_ClauseInvalidRegex"), rule.Name, + clause.Label ?? rule.Clauses.IndexOf(clause).ToString(CultureInfo.InvariantCulture), regex), + rule, clause); + if (clause.DictData?.Count > 0) + yield return new Violation( + string.Format(Strings.Get("Err_ClauseDictDataUnexpected"), rule.Name, + clause.Label ?? rule.Clauses.IndexOf(clause).ToString(CultureInfo.InvariantCulture), + clause.Operation.ToString()), rule, clause); + } + + /// + /// Returns results with pattern index and Boundary as a tuple to enable retrieval of Rule pattern level meta-data like + /// Confidence and report the + /// pattern that was responsible for the match + /// + /// + /// + /// + /// + /// + private OperationResult RegexWithIndexOperationDelegate(Clause clause, object? state1, object? state2, + IEnumerable? captures) + { + if (state1 is TextContainer tc && clause is OatRegexWithIndexClause src && + clause.Data is List RegexList && RegexList.Count > 0) { - _loggerFactory = loggerFactory ?? new NullLoggerFactory(); - _logger = _loggerFactory.CreateLogger(); - CustomOperation = "RegexWithIndex"; - OperationDelegate = RegexWithIndexOperationDelegate; - ValidationDelegate = RegexWithIndexValidationDelegate; - } - - private static IEnumerable RegexWithIndexValidationDelegate(CST.OAT.Rule rule, Clause clause) - { - if (clause.Data?.Count is null or 0) + RegexOptions regexOpts = new(); + var subBoundary = state2 is Boundary s2 ? s2 : null; + + if (src.Arguments.Contains("i")) regexOpts |= RegexOptions.IgnoreCase; + if (src.Arguments.Contains("m")) regexOpts |= RegexOptions.Multiline; + + List<(int, Boundary)> outmatches = new(); //tuple results i.e. pattern index and where + + if (Analyzer != null) { - yield return new Violation(string.Format(Strings.Get("Err_ClauseNoData"), rule.Name, clause.Label ?? rule.Clauses.IndexOf(clause).ToString(CultureInfo.InvariantCulture)), rule, clause); - } - else if (clause.Data is List regexList) - { - foreach (var regex in regexList) - { - if (!Helpers.IsValidRegex(regex)) + foreach (var regexString in RegexList) + if (StringToRegex(regexString, regexOpts) is { } regex) { - yield return new Violation(string.Format(Strings.Get("Err_ClauseInvalidRegex"), rule.Name, clause.Label ?? rule.Clauses.IndexOf(clause).ToString(CultureInfo.InvariantCulture), regex), rule, clause); - } - } - } - if (clause.DictData?.Count > 0) - { - yield return new Violation(string.Format(Strings.Get("Err_ClauseDictDataUnexpected"), rule.Name, clause.Label ?? rule.Clauses.IndexOf(clause).ToString(CultureInfo.InvariantCulture), clause.Operation.ToString()), rule, clause); - } - } + if (src.XPaths is not null) + foreach (var xmlPath in src.XPaths) + { + var targets = tc.GetStringFromXPath(xmlPath); + foreach (var target in targets) + { + var matches = GetMatches(regex, tc, clause, src.Scopes, target.Item2); + foreach (var match in matches) outmatches.Add(match); + } + } - /// - /// Returns results with pattern index and Boundary as a tuple to enable retrieval of Rule pattern level meta-data like Confidence and report the - /// pattern that was responsible for the match - /// - /// - /// - /// - /// - /// - private OperationResult RegexWithIndexOperationDelegate(Clause clause, object? state1, object? state2, IEnumerable? captures) - { - if (state1 is TextContainer tc && clause is OatRegexWithIndexClause src && clause.Data is List RegexList && RegexList.Count > 0) - { - RegexOptions regexOpts = new(); - Boundary? subBoundary = state2 is Boundary s2 ? s2 : null; + if (src.JsonPaths is not null) + foreach (var jsonPath in src.JsonPaths) + { + var targets = tc.GetStringFromJsonPath(jsonPath); + foreach (var target in targets) + outmatches.AddRange(GetMatches(regex, tc, clause, src.Scopes, target.Item2)); + } - if (src.Arguments.Contains("i")) - { - regexOpts |= RegexOptions.IgnoreCase; - } - if (src.Arguments.Contains("m")) - { - regexOpts |= RegexOptions.Multiline; - } - - List<(int, Boundary)> outmatches = new();//tuple results i.e. pattern index and where - - if (Analyzer != null) - { - foreach (var regexString in RegexList) - { - if (StringToRegex(regexString, regexOpts) is { } regex) + if (src.JsonPaths is null && src.XPaths is null) { - if (src.XPaths is not null) - { - foreach (var xmlPath in src.XPaths) - { - var targets = tc.GetStringFromXPath(xmlPath); - foreach (var target in targets) - { - var matches = GetMatches(regex, tc, clause, src.Scopes, target.Item2); - foreach (var match in matches) - { - outmatches.Add(match); - } - } - } - } - if (src.JsonPaths is not null) - { - foreach (var jsonPath in src.JsonPaths) - { - var targets = tc.GetStringFromJsonPath(jsonPath); - foreach (var target in targets) - { - outmatches.AddRange(GetMatches(regex, tc, clause, src.Scopes, target.Item2)); - } - } - } - if (src.JsonPaths is null && src.XPaths is null) - { - if (subBoundary is not null) - { - outmatches.AddRange(GetMatches(regex, tc, clause, src.Scopes, subBoundary)); - } - else - { - outmatches.AddRange(GetMatches(regex, tc, clause, src.Scopes, null)); - } - } - } + if (subBoundary is not null) + outmatches.AddRange(GetMatches(regex, tc, clause, src.Scopes, subBoundary)); + else + outmatches.AddRange(GetMatches(regex, tc, clause, src.Scopes, null)); + } } - var result = src.Invert ? outmatches.Count == 0 : outmatches.Count > 0; - return new OperationResult(result, result && src.Capture ? new TypedClauseCapture>(clause, outmatches, state1) : null); - } + var result = src.Invert ? outmatches.Count == 0 : outmatches.Count > 0; + return new OperationResult(result, + result && src.Capture + ? new TypedClauseCapture>(clause, outmatches, state1) + : null); } - return new OperationResult(false, null); } - - private IEnumerable<(int, Boundary)> GetMatches(Regex regex, TextContainer tc, Clause clause, PatternScope[] scopes, Boundary? boundary) - { - foreach (var match in regex.Matches(boundary is null ? tc.FullContent : tc.GetBoundaryText(boundary))) - { - if (match is Match m) - { - Boundary translatedBoundary = new() - { - Length = m.Length, - Index = m.Index + (boundary?.Index ?? 0) - }; - //regex patterns will be indexed off data while string patterns result in N clauses - int patternIndex = Convert.ToInt32(clause.Label); + return new OperationResult(false); + } - // Should return only scoped matches - if (tc.ScopeMatch(scopes, translatedBoundary)) - { - yield return (patternIndex, translatedBoundary); - } - } - } - } - - /// - /// Converts a strings to a compiled regex. - /// Uses an internal cache. - /// - /// The regex to build - /// The options to use. - /// The built Regex - private Regex? StringToRegex(string built, RegexOptions regexOptions) - { - if (!RegexCache.ContainsKey((built, regexOptions))) + private IEnumerable<(int, Boundary)> GetMatches(Regex regex, TextContainer tc, Clause clause, PatternScope[] scopes, + Boundary? boundary) + { + foreach (var match in regex.Matches(boundary is null ? tc.FullContent : tc.GetBoundaryText(boundary))) + if (match is Match m) { - try + Boundary translatedBoundary = new() { - RegexCache.TryAdd((built, regexOptions), new Regex(built, regexOptions)); - } - catch (ArgumentException) - { - _logger.LogWarning("Provided regex {Regex} was not valid and could not be used", built); - RegexCache.TryAdd((built, regexOptions), null); - } + Length = m.Length, + Index = m.Index + (boundary?.Index ?? 0) + }; + + //regex patterns will be indexed off data while string patterns result in N clauses + var patternIndex = Convert.ToInt32(clause.Label); + + // Should return only scoped matches + if (tc.ScopeMatch(scopes, translatedBoundary)) yield return (patternIndex, translatedBoundary); } - return RegexCache[(built, regexOptions)]; - } + } + + /// + /// Converts a strings to a compiled regex. + /// Uses an internal cache. + /// + /// The regex to build + /// The options to use. + /// The built Regex + private Regex? StringToRegex(string built, RegexOptions regexOptions) + { + if (!RegexCache.ContainsKey((built, regexOptions))) + try + { + RegexCache.TryAdd((built, regexOptions), new Regex(built, regexOptions)); + } + catch (ArgumentException) + { + _logger.LogWarning("Provided regex {Regex} was not valid and could not be used", built); + RegexCache.TryAdd((built, regexOptions), null); + } + + return RegexCache[(built, regexOptions)]; } } \ No newline at end of file diff --git a/AppInspector.RulesEngine/OatExtensions/OatSubstringIndexClause.cs b/AppInspector.RulesEngine/OatExtensions/OatSubstringIndexClause.cs index 298f3c4..197d25d 100644 --- a/AppInspector.RulesEngine/OatExtensions/OatSubstringIndexClause.cs +++ b/AppInspector.RulesEngine/OatExtensions/OatSubstringIndexClause.cs @@ -2,25 +2,25 @@ using Microsoft.CST.OAT; -namespace Microsoft.ApplicationInspector.RulesEngine.OatExtensions +namespace Microsoft.ApplicationInspector.RulesEngine.OatExtensions; + +public class OatSubstringIndexClause : Clause { - public class OatSubstringIndexClause : Clause + public OatSubstringIndexClause(PatternScope[] scopes, string? field = null, bool useWordBoundaries = false, + string[]? xPaths = null, string[]? jsonPaths = null) : base(Operation.Custom, field) { - public OatSubstringIndexClause(PatternScope[] scopes, string? field = null, bool useWordBoundaries = false, string[]? xPaths = null, string[]? jsonPaths = null) : base(Operation.Custom, field) - { - Scopes = scopes; - CustomOperation = "SubstringIndex"; - UseWordBoundaries = useWordBoundaries; - XPaths = xPaths; - JsonPaths = jsonPaths; - } - - public string[]? JsonPaths { get; } - - public string[]? XPaths { get; } - - public PatternScope[] Scopes { get; } - - public bool UseWordBoundaries {get;} + Scopes = scopes; + CustomOperation = "SubstringIndex"; + UseWordBoundaries = useWordBoundaries; + XPaths = xPaths; + JsonPaths = jsonPaths; } + + public string[]? JsonPaths { get; } + + public string[]? XPaths { get; } + + public PatternScope[] Scopes { get; } + + public bool UseWordBoundaries { get; } } \ No newline at end of file diff --git a/AppInspector.RulesEngine/OatExtensions/OatSubstringIndexOperation.cs b/AppInspector.RulesEngine/OatExtensions/OatSubstringIndexOperation.cs index 9f1a787..ce22eb6 100644 --- a/AppInspector.RulesEngine/OatExtensions/OatSubstringIndexOperation.cs +++ b/AppInspector.RulesEngine/OatExtensions/OatSubstringIndexOperation.cs @@ -8,146 +8,148 @@ using Microsoft.CST.OAT.Utils; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; -namespace Microsoft.ApplicationInspector.RulesEngine.OatExtensions +namespace Microsoft.ApplicationInspector.RulesEngine.OatExtensions; + +/// +/// The Custom Operation to enable identification of pattern index in result used by Application Inspector to report +/// why a given +/// result was matched and to retrieve other pattern level meta-data +/// +public class OatSubstringIndexOperation : OatOperation { + private readonly ILoggerFactory _loggerFactory; + /// - /// The Custom Operation to enable identification of pattern index in result used by Application Inspector to report why a given - /// result was matched and to retrieve other pattern level meta-data + /// Create an OatOperation given an analyzer /// - public class OatSubstringIndexOperation : OatOperation + /// The analyzer context to work with + /// LoggerFactory to use + public OatSubstringIndexOperation(Analyzer analyzer, ILoggerFactory? loggerFactory = null) : base(Operation.Custom, + analyzer) { - private readonly ILoggerFactory _loggerFactory; + _loggerFactory = loggerFactory ?? new NullLoggerFactory(); + CustomOperation = "SubstringIndex"; + OperationDelegate = SubstringIndexOperationDelegate; + ValidationDelegate = SubstringIndexValidationDelegate; + } - /// - /// Create an OatOperation given an analyzer - /// - /// The analyzer context to work with - /// LoggerFactory to use - public OatSubstringIndexOperation(Analyzer analyzer, ILoggerFactory? loggerFactory = null) : base(Operation.Custom, analyzer) - { - _loggerFactory = loggerFactory ?? new NullLoggerFactory(); - CustomOperation = "SubstringIndex"; - OperationDelegate = SubstringIndexOperationDelegate; - ValidationDelegate = SubstringIndexValidationDelegate; - } - - public static IEnumerable SubstringIndexValidationDelegate(CST.OAT.Rule rule, Clause clause) - { - if (clause.Data?.Count is null or 0) - { - yield return new Violation(string.Format(Strings.Get("Err_ClauseNoData"), rule.Name, clause.Label ?? rule.Clauses.IndexOf(clause).ToString(CultureInfo.InvariantCulture)), rule, clause); - } - if (clause.DictData?.Count is not null && clause.DictData.Count > 0) - { - yield return new Violation(string.Format(Strings.Get("Err_ClauseDictDataUnexpected"), rule.Name, clause.Label ?? rule.Clauses.IndexOf(clause).ToString(CultureInfo.InvariantCulture), clause.Operation.ToString()), rule, clause); - } - } + public static IEnumerable SubstringIndexValidationDelegate(CST.OAT.Rule rule, Clause clause) + { + if (clause.Data?.Count is null or 0) + yield return new Violation( + string.Format(Strings.Get("Err_ClauseNoData"), rule.Name, + clause.Label ?? rule.Clauses.IndexOf(clause).ToString(CultureInfo.InvariantCulture)), rule, clause); + if (clause.DictData?.Count is not null && clause.DictData.Count > 0) + yield return new Violation( + string.Format(Strings.Get("Err_ClauseDictDataUnexpected"), rule.Name, + clause.Label ?? rule.Clauses.IndexOf(clause).ToString(CultureInfo.InvariantCulture), + clause.Operation.ToString()), rule, clause); + } - /// - /// Returns results with pattern index and Boundary as a tuple to enable retrieval of Rule pattern level meta-data like Confidence and report the - /// pattern that was responsible for the match - /// - /// - /// - /// - /// - /// - private OperationResult SubstringIndexOperationDelegate(Clause clause, object? state1, object? state2, IEnumerable? captures) - { - var comparisonType = clause.Arguments.Contains("i") ? StringComparison.InvariantCultureIgnoreCase : StringComparison.InvariantCulture; - if (state1 is TextContainer tc && clause is OatSubstringIndexClause src) + /// + /// Returns results with pattern index and Boundary as a tuple to enable retrieval of Rule pattern level meta-data like + /// Confidence and report the + /// pattern that was responsible for the match + /// + /// + /// + /// + /// + /// + private OperationResult SubstringIndexOperationDelegate(Clause clause, object? state1, object? state2, + IEnumerable? captures) + { + var comparisonType = clause.Arguments.Contains("i") + ? StringComparison.InvariantCultureIgnoreCase + : StringComparison.InvariantCulture; + if (state1 is TextContainer tc && clause is OatSubstringIndexClause src) + if (clause.Data is { Count: > 0 } stringList) { - if (clause.Data is { Count: > 0 } stringList) + var outmatches = new List<(int, Boundary)>(); //tuple results i.e. pattern index and where + + for (var i = 0; i < stringList.Count; i++) { - var outmatches = new List<(int, Boundary)>();//tuple results i.e. pattern index and where - - for (int i = 0; i < stringList.Count; i++) - { - if (src.XPaths is not null) + if (src.XPaths is not null) + foreach (var xmlPath in src.XPaths) { - foreach (var xmlPath in src.XPaths) + var targets = tc.GetStringFromXPath(xmlPath); + foreach (var target in targets) { - var targets = tc.GetStringFromXPath(xmlPath); - foreach (var target in targets) + var matches = GetMatches(target.Item1, stringList[i], comparisonType, tc, src); + foreach (var match in matches) { - var matches = GetMatches(target.Item1, stringList[i], comparisonType, tc, src); - foreach (var match in matches) - { - match.Index += target.Item2.Index; - outmatches.Add((i,match)); - } + match.Index += target.Item2.Index; + outmatches.Add((i, match)); } } } - if (src.JsonPaths is not null) + + if (src.JsonPaths is not null) + foreach (var jsonPath in src.JsonPaths) { - foreach (var jsonPath in src.JsonPaths) + var targets = tc.GetStringFromJsonPath(jsonPath); + foreach (var target in targets) { - var targets = tc.GetStringFromJsonPath(jsonPath); - foreach (var target in targets) + var matches = GetMatches(target.Item1, stringList[i], comparisonType, tc, src); + foreach (var match in matches) { - var matches = GetMatches(target.Item1, stringList[i], comparisonType, tc, src); - foreach (var match in matches) - { - match.Index += target.Item2.Index; - outmatches.Add((i,match)); - } + match.Index += target.Item2.Index; + outmatches.Add((i, match)); } } } - if (src.JsonPaths is null && src.XPaths is null) - { - // If state 2 is a boundary, restrict the text provided to check to match the boundary - if (state2 is Boundary boundary) - { - var matches = GetMatches(tc.GetBoundaryText(boundary), stringList[i], comparisonType, tc, src); - outmatches.AddRange(matches.Select(x => (i, x))); } - else - { - var matches = GetMatches(tc.FullContent, stringList[i], comparisonType, tc, src); - outmatches.AddRange(matches.Select(x => (i, x))); } + if (src.JsonPaths is null && src.XPaths is null) + { + // If state 2 is a boundary, restrict the text provided to check to match the boundary + if (state2 is Boundary boundary) + { + var matches = GetMatches(tc.GetBoundaryText(boundary), stringList[i], comparisonType, tc, + src); + outmatches.AddRange(matches.Select(x => (i, x))); + } + else + { + var matches = GetMatches(tc.FullContent, stringList[i], comparisonType, tc, src); + outmatches.AddRange(matches.Select(x => (i, x))); } } - - var result = src.Invert ? outmatches.Count == 0 : outmatches.Count > 0; - return new OperationResult(result, result && src.Capture ? new TypedClauseCapture>(clause, outmatches, state1) : null); } - } - return new OperationResult(false, null); - } - private static IEnumerable GetMatches(string target, string query, StringComparison comparisonType, TextContainer tc, OatSubstringIndexClause src) + var result = src.Invert ? outmatches.Count == 0 : outmatches.Count > 0; + return new OperationResult(result, + result && src.Capture + ? new TypedClauseCapture>(clause, outmatches, state1) + : null); + } + + return new OperationResult(false); + } + + private static IEnumerable GetMatches(string target, string query, StringComparison comparisonType, + TextContainer tc, OatSubstringIndexClause src) + { + var idx = target.IndexOf(query, comparisonType); + while (idx != -1) { - var idx = target.IndexOf(query, comparisonType); - while (idx != -1) + var skip = false; + if (src.UseWordBoundaries) { - bool skip = false; - if (src.UseWordBoundaries) - { - if (idx > 0 && char.IsLetterOrDigit(target[idx - 1])) - { - skip = true; - } - if (idx + query.Length < target.Length && char.IsLetterOrDigit(target[idx + query.Length])) - { - skip = true; - } - } - if (!skip) - { - Boundary newBoundary = new() - { - Length = query.Length, - Index = idx - }; - if (tc.ScopeMatch(src.Scopes, newBoundary)) - { - yield return newBoundary; - } - } - idx = target.IndexOf(query, idx + query.Length, comparisonType); + if (idx > 0 && char.IsLetterOrDigit(target[idx - 1])) skip = true; + if (idx + query.Length < target.Length && char.IsLetterOrDigit(target[idx + query.Length])) skip = true; } + + if (!skip) + { + Boundary newBoundary = new() + { + Length = query.Length, + Index = idx + }; + if (tc.ScopeMatch(src.Scopes, newBoundary)) yield return newBoundary; + } + + idx = target.IndexOf(query, idx + query.Length, comparisonType); } } } \ No newline at end of file diff --git a/AppInspector.RulesEngine/OatExtensions/WithinClause.cs b/AppInspector.RulesEngine/OatExtensions/WithinClause.cs index c58d921..adbf9bb 100644 --- a/AppInspector.RulesEngine/OatExtensions/WithinClause.cs +++ b/AppInspector.RulesEngine/OatExtensions/WithinClause.cs @@ -2,24 +2,23 @@ using Microsoft.CST.OAT; -namespace Microsoft.ApplicationInspector.RulesEngine.OatExtensions -{ - public class WithinClause : Clause - { - public WithinClause(Clause subClause, string? field = null) : base(Operation.Custom, field) - { - SubClause = subClause; - CustomOperation = "Within"; - } +namespace Microsoft.ApplicationInspector.RulesEngine.OatExtensions; - public int After { get; set; } - public int Before { get; set; } - public bool OnlyBefore { get; set; } - public bool OnlyAfter { get; set; } - public bool SameFile { get; set; } - public bool FindingOnly { get; set; } - public bool SameLineOnly { get; set; } - public bool FindingRegion { get; set; } - public Clause SubClause { get; } +public class WithinClause : Clause +{ + public WithinClause(Clause subClause, string? field = null) : base(Operation.Custom, field) + { + SubClause = subClause; + CustomOperation = "Within"; } + + public int After { get; set; } + public int Before { get; set; } + public bool OnlyBefore { get; set; } + public bool OnlyAfter { get; set; } + public bool SameFile { get; set; } + public bool FindingOnly { get; set; } + public bool SameLineOnly { get; set; } + public bool FindingRegion { get; set; } + public Clause SubClause { get; } } \ No newline at end of file diff --git a/AppInspector.RulesEngine/OatExtensions/WithinOperation.cs b/AppInspector.RulesEngine/OatExtensions/WithinOperation.cs index 07acf3b..f054fb0 100644 --- a/AppInspector.RulesEngine/OatExtensions/WithinOperation.cs +++ b/AppInspector.RulesEngine/OatExtensions/WithinOperation.cs @@ -7,211 +7,192 @@ using Microsoft.CST.OAT.Operations; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; -namespace Microsoft.ApplicationInspector.RulesEngine.OatExtensions +namespace Microsoft.ApplicationInspector.RulesEngine.OatExtensions; + +public class WithinOperation : OatOperation { - public class WithinOperation : OatOperation + private readonly Analyzer _analyzer; + + private readonly ILoggerFactory _loggerFactory; + + public WithinOperation(Analyzer analyzer, ILoggerFactory? loggerFactory = null) : base(Operation.Custom, analyzer) { - public WithinOperation(Analyzer analyzer, ILoggerFactory? loggerFactory = null) : base(Operation.Custom, analyzer) - { - _loggerFactory = loggerFactory ?? NullLoggerFactory.Instance; - _analyzer = analyzer; - CustomOperation = "Within"; - OperationDelegate = WithinOperationDelegate; - ValidationDelegate = WithinValidationDelegate; - } + _loggerFactory = loggerFactory ?? NullLoggerFactory.Instance; + _analyzer = analyzer; + CustomOperation = "Within"; + OperationDelegate = WithinOperationDelegate; + ValidationDelegate = WithinValidationDelegate; + } - public OperationResult WithinOperationDelegate(Clause c, object? state1, object? _, IEnumerable? captures) + public OperationResult WithinOperationDelegate(Clause c, object? state1, object? _, + IEnumerable? captures) + { + if (c is WithinClause wc && state1 is TextContainer tc) { - if (c is WithinClause wc && state1 is TextContainer tc) + var passed = + new List<(int, Boundary)>(); + var failed = + new List<(int, Boundary)>(); + + foreach (var capture in captures ?? Array.Empty()) { - List<(int, Boundary)> passed = - new List<(int, Boundary)>(); - List<(int, Boundary)> failed = - new List<(int, Boundary)>(); - - foreach (var capture in captures ?? Array.Empty()) - { - if (capture is TypedClauseCapture> tcc) + if (capture is TypedClauseCapture> tcc) + foreach ((var clauseNum, var boundary) in tcc.Result) { - foreach ((int clauseNum, Boundary boundary) in tcc.Result) + var boundaryToCheck = GetBoundaryToCheck(); + if (boundaryToCheck is not null) { - var boundaryToCheck = GetBoundaryToCheck(); - if (boundaryToCheck is not null) + var operationResult = ProcessLambda(boundaryToCheck); + if (operationResult.Result) + passed.Add((clauseNum, boundary)); + else + failed.Add((clauseNum, boundary)); + } + + Boundary? GetBoundaryToCheck() + { + if (wc.FindingOnly) return boundary; + if (wc.SameLineOnly) { - var operationResult = ProcessLambda(boundaryToCheck); - if (operationResult.Result) + var startInner = tc.LineStarts[tc.GetLocation(boundary.Index).Line]; + var endInner = tc.LineEnds[tc.GetLocation(startInner + (boundary.Length - 1)).Line]; + return new Boundary { - passed.Add((clauseNum, boundary)); - } - else - { - failed.Add((clauseNum, boundary)); - } + Index = startInner, + Length = endInner - startInner + 1 + }; } - Boundary? GetBoundaryToCheck() + if (wc.FindingRegion) { - if (wc.FindingOnly) + var startLine = tc.GetLocation(boundary.Index).Line; + // Before is already a negative number + var startInner = tc.LineStarts[Math.Max(1, startLine + wc.Before)]; + var endInner = tc.LineEnds[Math.Min(tc.LineEnds.Count - 1, startLine + wc.After)]; + return new Boundary { - return boundary; - } - if (wc.SameLineOnly) - { - var startInner = tc.LineStarts[tc.GetLocation(boundary.Index).Line]; - var endInner = tc.LineEnds[tc.GetLocation(startInner + (boundary.Length - 1)).Line]; - return new Boundary() - { - Index = startInner, - Length = (endInner - startInner) + 1 - }; - } - - if (wc.FindingRegion) - { - var startLine = tc.GetLocation(boundary.Index).Line; - // Before is already a negative number - var startInner = tc.LineStarts[Math.Max(1, startLine + wc.Before)]; - var endInner = tc.LineEnds[Math.Min(tc.LineEnds.Count - 1, startLine + wc.After)]; - return new Boundary() - { - Index = startInner, - Length = (endInner - startInner) + 1 - }; - } - - if (wc.SameFile) - { - var startInner = tc.LineStarts[0]; - var endInner = tc.LineEnds[^1]; - return new Boundary() - { - Index = startInner, - Length = (endInner - startInner) + 1 - }; - } - - if (wc.OnlyBefore) - { - var startInner = tc.LineStarts[0]; - var endInner = boundary.Index; - return new Boundary() - { - Index = startInner, - Length = (endInner - startInner) + 1 - }; - } - - if (wc.OnlyAfter) - { - var startInner = boundary.Index + boundary.Length; - var endInner = tc.LineEnds[^1]; - return new Boundary() - { - Index = startInner, - Length = (endInner - startInner) + 1 - }; - } - - return null; + Index = startInner, + Length = endInner - startInner + 1 + }; } + + if (wc.SameFile) + { + var startInner = tc.LineStarts[0]; + var endInner = tc.LineEnds[^1]; + return new Boundary + { + Index = startInner, + Length = endInner - startInner + 1 + }; + } + + if (wc.OnlyBefore) + { + var startInner = tc.LineStarts[0]; + var endInner = boundary.Index; + return new Boundary + { + Index = startInner, + Length = endInner - startInner + 1 + }; + } + + if (wc.OnlyAfter) + { + var startInner = boundary.Index + boundary.Length; + var endInner = tc.LineEnds[^1]; + return new Boundary + { + Index = startInner, + Length = endInner - startInner + 1 + }; + } + + return null; } } - var passedOrFailed = wc.Invert ? failed : passed; - return new OperationResult(passedOrFailed.Any(), passedOrFailed.Any() ? new TypedClauseCapture>(wc, passedOrFailed.ToList()) : null); - } - - OperationResult ProcessLambda(Boundary target) - { - return _analyzer.GetClauseCapture(wc.SubClause, tc, target, captures); - } + var passedOrFailed = wc.Invert ? failed : passed; + return new OperationResult(passedOrFailed.Any(), + passedOrFailed.Any() + ? new TypedClauseCapture>(wc, passedOrFailed.ToList()) + : null); } - return new OperationResult(false); - } - - public IEnumerable WithinValidationDelegate(CST.OAT.Rule rule, Clause clause) - { - if (clause is WithinClause wc) + + OperationResult ProcessLambda(Boundary target) { - if (new bool[] {wc.FindingOnly, wc.SameLineOnly, wc.FindingRegion, wc.OnlyAfter, wc.OnlyBefore, wc.SameFile}.Count(x => x) != 1) - { - yield return new Violation($"Exactly one of: FindingOnly, SameLineOnly, OnlyAfter, OnlyBefore, SameFile or FindingRegion must be set", rule, clause); - } + return _analyzer.GetClauseCapture(wc.SubClause, tc, target, captures); + } + } - if (wc.FindingRegion) - { - if (wc.Before == 0 && wc.After == 0) - { - yield return new Violation( - $"Both parameters for finding-region may not be 0. Use same-line to only analyze the same line.", - rule, clause); - } + return new OperationResult(false); + } - if (wc.Before > 0) - { - yield return new Violation( - $"The first parameter for finding region, representing number of lines before, must be 0 or negative", - rule, clause); - } + public IEnumerable WithinValidationDelegate(CST.OAT.Rule rule, Clause clause) + { + if (clause is WithinClause wc) + { + if (new[] { wc.FindingOnly, wc.SameLineOnly, wc.FindingRegion, wc.OnlyAfter, wc.OnlyBefore, wc.SameFile } + .Count(x => x) != + 1) + yield return new Violation( + "Exactly one of: FindingOnly, SameLineOnly, OnlyAfter, OnlyBefore, SameFile or FindingRegion must be set", + rule, clause); - if (wc.After < 0) - { - yield return new Violation( - $"The second parameter for finding region, representing number of lines after, must be 0 or positive", - rule, clause); - } - } - if (wc.Data.Any()) - { - yield return new Violation($"Don't provide data directly. Instead use SubClause.", rule, clause); - } - - var subOp = _analyzer - .GetOperation(wc.SubClause.Key.Operation, wc.SubClause.Key.CustomOperation); + if (wc.FindingRegion) + { + if (wc.Before == 0 && wc.After == 0) + yield return new Violation( + "Both parameters for finding-region may not be 0. Use same-line to only analyze the same line.", + rule, clause); - if (subOp is null) - { - yield return new Violation($"SubClause in Rule {rule.Name} Clause {clause.Label ?? rule.Clauses.IndexOf(clause).ToString(CultureInfo.InvariantCulture)} is of type '{wc.SubClause.Key.Operation},{wc.SubClause.Key.CustomOperation}' is not present in the analyzer.", rule, clause); - } - else - { - foreach (var violation in subOp.ValidationDelegate.Invoke(rule, wc.SubClause)) - { - yield return violation; - } + if (wc.Before > 0) + yield return new Violation( + "The first parameter for finding region, representing number of lines before, must be 0 or negative", + rule, clause); - if (wc.SubClause is OatRegexWithIndexClause oatRegexWithIndexClause) - { - if ((oatRegexWithIndexClause.JsonPaths?.Any() ?? false) || - (oatRegexWithIndexClause.XPaths?.Any() ?? false)) - { - if (wc.FindingOnly || wc.SameLineOnly || wc.FindingRegion || wc.OnlyAfter || wc.OnlyBefore) - { - yield return new Violation($"When providing JSONPaths or XPaths must use same-file region.", rule, clause); - } - } - } - if (wc.SubClause is OatSubstringIndexClause oatSubstringIndexClause) - { - if ((oatSubstringIndexClause.JsonPaths?.Any() ?? false) || - (oatSubstringIndexClause.XPaths?.Any() ?? false)) - { - if (wc.FindingOnly || wc.SameLineOnly || wc.FindingRegion || wc.OnlyAfter || wc.OnlyBefore) - { - yield return new Violation($"When providing JSONPaths or XPaths must use same-file region.", rule, clause); - } - } - } - } - + if (wc.After < 0) + yield return new Violation( + "The second parameter for finding region, representing number of lines after, must be 0 or positive", + rule, clause); + } + + if (wc.Data.Any()) + yield return new Violation("Don't provide data directly. Instead use SubClause.", rule, clause); + + var subOp = _analyzer + .GetOperation(wc.SubClause.Key.Operation, wc.SubClause.Key.CustomOperation); + + if (subOp is null) + { + yield return new Violation( + $"SubClause in Rule {rule.Name} Clause {clause.Label ?? rule.Clauses.IndexOf(clause).ToString(CultureInfo.InvariantCulture)} is of type '{wc.SubClause.Key.Operation},{wc.SubClause.Key.CustomOperation}' is not present in the analyzer.", + rule, clause); } else { - yield return new Violation($"Rule {rule.Name} clause {clause.Label ?? rule.Clauses.IndexOf(clause).ToString(CultureInfo.InvariantCulture)} is not a WithinClause", rule, clause); + foreach (var violation in subOp.ValidationDelegate.Invoke(rule, wc.SubClause)) yield return violation; + + if (wc.SubClause is OatRegexWithIndexClause oatRegexWithIndexClause) + if ((oatRegexWithIndexClause.JsonPaths?.Any() ?? false) || + (oatRegexWithIndexClause.XPaths?.Any() ?? false)) + if (wc.FindingOnly || wc.SameLineOnly || wc.FindingRegion || wc.OnlyAfter || wc.OnlyBefore) + yield return new Violation("When providing JSONPaths or XPaths must use same-file region.", + rule, clause); + if (wc.SubClause is OatSubstringIndexClause oatSubstringIndexClause) + if ((oatSubstringIndexClause.JsonPaths?.Any() ?? false) || + (oatSubstringIndexClause.XPaths?.Any() ?? false)) + if (wc.FindingOnly || wc.SameLineOnly || wc.FindingRegion || wc.OnlyAfter || wc.OnlyBefore) + yield return new Violation("When providing JSONPaths or XPaths must use same-file region.", + rule, clause); } } - - private readonly ILoggerFactory _loggerFactory; - private readonly Analyzer _analyzer; + else + { + yield return new Violation( + $"Rule {rule.Name} clause {clause.Label ?? rule.Clauses.IndexOf(clause).ToString(CultureInfo.InvariantCulture)} is not a WithinClause", + rule, clause); + } } } \ No newline at end of file diff --git a/AppInspector.RulesEngine/PatternScope.cs b/AppInspector.RulesEngine/PatternScope.cs index cbfcb67..b79479c 100644 --- a/AppInspector.RulesEngine/PatternScope.cs +++ b/AppInspector.RulesEngine/PatternScope.cs @@ -3,14 +3,13 @@ using Newtonsoft.Json; using Newtonsoft.Json.Converters; -namespace Microsoft.ApplicationInspector.RulesEngine +namespace Microsoft.ApplicationInspector.RulesEngine; + +[JsonConverter(typeof(StringEnumConverter))] +public enum PatternScope { - [JsonConverter(typeof(StringEnumConverter))] - public enum PatternScope - { - All, - Code, - Comment, - Html - } + All, + Code, + Comment, + Html } \ No newline at end of file diff --git a/AppInspector.RulesEngine/PatternType.cs b/AppInspector.RulesEngine/PatternType.cs index 3dc9ef1..d428050 100644 --- a/AppInspector.RulesEngine/PatternType.cs +++ b/AppInspector.RulesEngine/PatternType.cs @@ -3,17 +3,16 @@ using Newtonsoft.Json; using Newtonsoft.Json.Converters; -namespace Microsoft.ApplicationInspector.RulesEngine +namespace Microsoft.ApplicationInspector.RulesEngine; + +/// +/// Pattern Type for search pattern +/// +[JsonConverter(typeof(StringEnumConverter))] +public enum PatternType { - /// - /// Pattern Type for search pattern - /// - [JsonConverter(typeof(StringEnumConverter))] - public enum PatternType - { - Regex, - RegexWord, - String, - Substring - } + Regex, + RegexWord, + String, + Substring } \ No newline at end of file diff --git a/AppInspector.RulesEngine/Resources/languages.json b/AppInspector.RulesEngine/Resources/languages.json index 58d8785..8f67f79 100644 --- a/AppInspector.RulesEngine/Resources/languages.json +++ b/AppInspector.RulesEngine/Resources/languages.json @@ -1,217 +1,327 @@ [ { "name": "c", - "extensions": [ ".c", ".h" ], + "extensions": [ + ".c", + ".h" + ], "type": "code" }, { "name": "cpp", - "extensions": [ ".cpp", ".hpp", ".cxx" ], + "extensions": [ + ".cpp", + ".hpp", + ".cxx" + ], "type": "code" }, { "name": "csharp", - "extensions": [ ".cs" ], + "extensions": [ + ".cs" + ], "type": "code" }, { "name": "fsharp", - "extensions": [ ".fs" ], + "extensions": [ + ".fs" + ], "type": "code" }, { "name": "vb", - "extensions": [ ".vb" ], + "extensions": [ + ".vb" + ], "type": "code" }, { "name": "python", - "extensions": [ ".py", ".py3", ".pyw" ], + "extensions": [ + ".py", + ".py3", + ".pyw" + ], "type": "code" }, { "name": "html", - "extensions": [ ".html", ".htm", ".cshtml", ".tmpl" ], + "extensions": [ + ".html", + ".htm", + ".cshtml", + ".tmpl" + ], "type": "code" }, { "name": "javascript", - "extensions": [ ".js" ], + "extensions": [ + ".js" + ], "type": "code" }, { "name": "javascriptreact", - "extensions": [ ".jsx" ], + "extensions": [ + ".jsx" + ], "type": "code" }, { "name": "typescript", - "extensions": [ ".ts" ], + "extensions": [ + ".ts" + ], "type": "code" }, { "name": "typescriptreact", - "extensions": [ ".tsx" ], + "extensions": [ + ".tsx" + ], "type": "code" }, { "name": "coffeescript", - "extensions": [ ".coffee" ], + "extensions": [ + ".coffee" + ], "type": "code" }, { "name": "dart", - "extensions": [ ".dart" ], + "extensions": [ + ".dart" + ], "type": "code" }, { "name": "java", - "extensions": [ ".java" ], + "extensions": [ + ".java" + ], "type": "code" }, { "name": "kotlin", - "extensions": [ ".kts" ], + "extensions": [ + ".kts" + ], "type": "code" }, { "name": "scala", - "extensions": [ ".scala" ], + "extensions": [ + ".scala" + ], "type": "code" }, { "name": "objective-c", - "extensions": [ ".m", ".mm" ], + "extensions": [ + ".m", + ".mm" + ], "type": "code" }, { "name": "swift", - "extensions": [ ".swift" ], + "extensions": [ + ".swift" + ], "type": "code" }, { "name": "perl", - "extensions": [ ".pl", ".pm", ".t", ".pod" ], + "extensions": [ + ".pl", + ".pm", + ".t", + ".pod" + ], "type": "code" }, { "name": "perl6", - "extensions": [ ".pl6", ".p6", ".pm6" ], + "extensions": [ + ".pl6", + ".p6", + ".pm6" + ], "type": "code" }, { "name": "ruby", - "extensions": [ ".rb" ], + "extensions": [ + ".rb" + ], "type": "code" }, { "name": "lua", - "extensions": [ ".lua" ], + "extensions": [ + ".lua" + ], "type": "code" }, { "name": "groovy", - "extensions": [ ".groovy" ], + "extensions": [ + ".groovy" + ], "type": "code" }, { "name": "go", - "extensions": [ ".go" ], + "extensions": [ + ".go" + ], "type": "code" }, { "name": "rust", - "extensions": [ ".rs" ], + "extensions": [ + ".rs" + ], "type": "code" }, { "name": "jade", - "extensions": [ ".jade" ], + "extensions": [ + ".jade" + ], "type": "code" }, { "name": "clojure", - "extensions": [ ".clj", ".cljs", ".cljc", ".edn" ], + "extensions": [ + ".clj", + ".cljs", + ".cljc", + ".edn" + ], "type": "code" }, { "name": "r", - "extensions": [ ".r" ], + "extensions": [ + ".r" + ], "type": "code" }, { "name": "php", - "extensions": [ ".php" ], + "extensions": [ + ".php" + ], "type": "code" }, { "name": "powershell", - "extensions": [ ".ps1", ".psm1", ".psd1" ], + "extensions": [ + ".ps1", + ".psm1", + ".psd1" + ], "type": "code" }, { "name": "shellscript", - "extensions": [ ".sh" ], + "extensions": [ + ".sh" + ], "type": "code" }, { "name": "wincmdscript", - "extensions": [ ".bat" ], + "extensions": [ + ".bat" + ], "type": "code" }, { "name": "sql", - "extensions": [ ".sql" ], + "extensions": [ + ".sql" + ], "type": "code" }, { "name": "yaml", - "extensions": [ ".yaml", ".yml" ], + "extensions": [ + ".yaml", + ".yml" + ], "type": "build" }, { "name": "package.json", - "file-names": ["package.json"], + "file-names": [ + "package.json" + ], "type": "build" }, { "name": "nugetpkg", - "file-names": [ "packages.config" ], + "file-names": [ + "packages.config" + ], "type": "build" }, { "name": "VSSolution", - "extensions": [ ".sln" ], + "extensions": [ + ".sln" + ], "type": "build" }, { "name": "VSProject", - "extensions": [ ".vcxproj", ".ccproj", ".csproj", ".njsproj", ".vbproj" ], + "extensions": [ + ".vcxproj", + ".ccproj", + ".csproj", + ".njsproj", + ".vbproj" + ], "type": "build" }, { "name": "pom.xml", - "file-names": [ "pom.xml" ], + "file-names": [ + "pom.xml" + ], "type": "build" }, { "name": "build.xml", - "file-names": [ "build.xml" ], + "file-names": [ + "build.xml" + ], "type": "build" }, { "name": "build.gradle", - "file-names": [ "build.gradle" ], + "file-names": [ + "build.gradle" + ], "type": "build" }, { "name": "build.make.xml", - "file-names": [ "build.make.xml" ], + "file-names": [ + "build.make.xml" + ], "type": "build" }, { "name": "jenkins", - "extensions": [ ".hpi" ], + "extensions": [ + ".hpi" + ], "type": "build" }, { @@ -221,7 +331,9 @@ }, { "name": "sbt", - "extensions": [ ".sbt" ], + "extensions": [ + ".sbt" + ], "type": "build" }, { @@ -231,32 +343,45 @@ }, { "name": "typescript-config", - "file-names": [ "tsconfig.json", "typings.json" ], + "file-names": [ + "tsconfig.json", + "typings.json" + ], "type": "build" }, { "name": "json", - "extensions": [ ".json" ], + "extensions": [ + ".json" + ], "type": "build" }, { "name": "terraform", - "extensions": [ ".tf" ], + "extensions": [ + ".tf" + ], "type": "code" }, { "name": ".config", - "extensions": [ ".config" ], + "extensions": [ + ".config" + ], "type": "build" }, { "name": "Package.appxmanifest", - "extensions": [ ".appxmanifest" ], + "extensions": [ + ".appxmanifest" + ], "type": "code" }, { "name": "XML", - "extensions": [ ".xml" ], + "extensions": [ + ".xml" + ], "type": "build" } ] \ No newline at end of file diff --git a/AppInspector.RulesEngine/Rule.cs b/AppInspector.RulesEngine/Rule.cs index 06bef24..cb13591 100644 --- a/AppInspector.RulesEngine/Rule.cs +++ b/AppInspector.RulesEngine/Rule.cs @@ -1,106 +1,101 @@ // Copyright (C) Microsoft. All rights reserved. // Licensed under the MIT License. See LICENSE.txt in the project root for license information. +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.RegularExpressions; using Newtonsoft.Json; using Newtonsoft.Json.Converters; -namespace Microsoft.ApplicationInspector.RulesEngine +namespace Microsoft.ApplicationInspector.RulesEngine; + +/// +/// Class to hold the Rule +/// +public class Rule { - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text.RegularExpressions; + private IEnumerable _compiled = Array.Empty(); + + private string[]? _fileRegexes; + private bool _updateCompiledFileRegex; /// - /// Class to hold the Rule + /// Name of the source where the rule definition came from. + /// Typically file, database or other storage. /// - public class Rule + [JsonIgnore] + public string? Source { get; set; } + + /// + /// Optional tag assigned to the rule during runtime + /// + [JsonIgnore] + public string? RuntimeTag { get; set; } + + /// + /// Runtime flag to disable the rule + /// + [JsonIgnore] + public bool Disabled { get; set; } + + [JsonProperty(PropertyName = "name")] public string Name { get; set; } = ""; + + [JsonProperty(PropertyName = "id")] public string Id { get; set; } = ""; + + [JsonProperty(PropertyName = "description")] + public string? Description { get; set; } = ""; + + [JsonProperty(PropertyName = "does_not_apply_to")] + public List? DoesNotApplyTo { get; set; } + + [JsonProperty(PropertyName = "applies_to")] + public string[]? AppliesTo { get; set; } + + [JsonProperty(PropertyName = "applies_to_file_regex")] + public string[]? FileRegexes { - /// - /// Name of the source where the rule definition came from. - /// Typically file, database or other storage. - /// - [JsonIgnore] - public string? Source { get; set; } - - /// - /// Optional tag assigned to the rule during runtime - /// - [JsonIgnore] - public string? RuntimeTag { get; set; } - - /// - /// Runtime flag to disable the rule - /// - [JsonIgnore] - public bool Disabled { get; set; } - - [JsonProperty(PropertyName = "name")] - public string Name { get; set; } = ""; - - [JsonProperty(PropertyName = "id")] - public string Id { get; set; } = ""; - - [JsonProperty(PropertyName = "description")] - public string? Description { get; set; } = ""; - - [JsonProperty(PropertyName = "does_not_apply_to")] - public List? DoesNotApplyTo { get; set; } - - [JsonProperty(PropertyName = "applies_to")] - public string[]? AppliesTo { get; set; } - - [JsonProperty(PropertyName = "applies_to_file_regex")] - public string[]? FileRegexes + get => _fileRegexes; + set { - get => _fileRegexes; - set - { - _fileRegexes = value; - _updateCompiledFileRegex = true; - } + _fileRegexes = value; + _updateCompiledFileRegex = true; } - - private string[]? _fileRegexes; - - [JsonIgnore] - public IEnumerable CompiledFileRegexes - { - get - { - if (_updateCompiledFileRegex) - { - _compiled = FileRegexes?.Select(x => new Regex(x, RegexOptions.Compiled)) ?? Array.Empty(); - _updateCompiledFileRegex = false; - } - - return _compiled; - } - } - - private IEnumerable _compiled = Array.Empty(); - private bool _updateCompiledFileRegex = false; - - [JsonProperty(PropertyName = "tags")] - public string[]? Tags { get; set; } - - [JsonProperty(PropertyName = "severity")] - [JsonConverter(typeof(StringEnumConverter))] - public Severity Severity { get; set; } = Severity.Moderate; - - [JsonProperty(PropertyName = "overrides")] - public string[]? Overrides { get; set; } - - [JsonProperty(PropertyName = "patterns")] - public SearchPattern[] Patterns { get; set; } = Array.Empty(); - - [JsonProperty(PropertyName = "conditions")] - public SearchCondition[]? Conditions { get; set; } - - [JsonProperty(PropertyName = "must-match")] - public string[]? MustMatch { get; set; } - - [JsonProperty(PropertyName = "must-not-match")] - public string[]? MustNotMatch { get; set; } } + + [JsonIgnore] + public IEnumerable CompiledFileRegexes + { + get + { + if (_updateCompiledFileRegex) + { + _compiled = FileRegexes?.Select(x => new Regex(x, RegexOptions.Compiled)) ?? Array.Empty(); + _updateCompiledFileRegex = false; + } + + return _compiled; + } + } + + [JsonProperty(PropertyName = "tags")] public string[]? Tags { get; set; } + + [JsonProperty(PropertyName = "severity")] + [JsonConverter(typeof(StringEnumConverter))] + public Severity Severity { get; set; } = Severity.Moderate; + + [JsonProperty(PropertyName = "overrides")] + public string[]? Overrides { get; set; } + + [JsonProperty(PropertyName = "patterns")] + public SearchPattern[] Patterns { get; set; } = Array.Empty(); + + [JsonProperty(PropertyName = "conditions")] + public SearchCondition[]? Conditions { get; set; } + + [JsonProperty(PropertyName = "must-match")] + public string[]? MustMatch { get; set; } + + [JsonProperty(PropertyName = "must-not-match")] + public string[]? MustNotMatch { get; set; } } \ No newline at end of file diff --git a/AppInspector.RulesEngine/RuleProcessor.cs b/AppInspector.RulesEngine/RuleProcessor.cs index da4c933..7e3a294 100644 --- a/AppInspector.RulesEngine/RuleProcessor.cs +++ b/AppInspector.RulesEngine/RuleProcessor.cs @@ -1,535 +1,491 @@ // Copyright (C) Microsoft. All rights reserved. // Licensed under the MIT License. See LICENSE.txt in the project root for license information. +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; +using System.Net; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; using Microsoft.ApplicationInspector.RulesEngine.OatExtensions; +using Microsoft.CST.OAT; +using Microsoft.CST.RecursiveExtractor; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; -namespace Microsoft.ApplicationInspector.RulesEngine +namespace Microsoft.ApplicationInspector.RulesEngine; + +[ExcludeFromCodeCoverage] +public class RuleProcessorOptions { - using Microsoft.CST.OAT; - using Microsoft.CST.RecursiveExtractor; - using Microsoft.Extensions.Logging; - using Microsoft.Extensions.Logging.Abstractions; - using System; - using System.Collections.Concurrent; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Text.RegularExpressions; - using System.Threading; - using System.Threading.Tasks; + public bool Parallel { get; set; } = true; - [ExcludeFromCodeCoverage] - public class RuleProcessorOptions + public Confidence ConfidenceFilter { get; set; } = + Confidence.Unspecified | Confidence.Low | Confidence.Medium | Confidence.High; + + public Severity SeverityFilter { get; set; } = + Severity.Critical | Severity.Important | Severity.Moderate | Severity.BestPractice; + + public ILoggerFactory? LoggerFactory { get; set; } + public bool AllowAllTagsInBuildFiles { get; set; } + public bool EnableCache { get; set; } = true; + public Languages Languages { get; set; } = new(); +} + +/// +/// Heart of RulesEngine. Parses code applies rules +/// +public class RuleProcessor +{ + private readonly Analyzer _analyzer; + private readonly ConcurrentDictionary> _fileRulesCache = new(); + private readonly ConcurrentDictionary> _languageRulesCache = new(); + private readonly Languages _languages; + private readonly ILogger _logger; + + private readonly RuleProcessorOptions _opts; + private readonly AbstractRuleSet _ruleset; + private readonly int MAX_TEXT_SAMPLE_LENGTH = 200; //char bytes + private IEnumerable? _universalRulesCache; + + /// + /// Creates instance of RuleProcessor + /// + public RuleProcessor(AbstractRuleSet rules, RuleProcessorOptions opts) { - public RuleProcessorOptions() - { - } + _opts = opts; + _logger = opts.LoggerFactory?.CreateLogger() ?? NullLogger.Instance; + _languages = opts.Languages; + _ruleset = rules; + EnableCache = true; - public bool Parallel { get; set; } = true; - public Confidence ConfidenceFilter { get; set; } = Confidence.Unspecified | Confidence.Low | Confidence.Medium | Confidence.High; - public Severity SeverityFilter { get; set; } = Severity.Critical | Severity.Important | Severity.Moderate | Severity.BestPractice; - public ILoggerFactory? LoggerFactory { get; set; } - public bool AllowAllTagsInBuildFiles { get; set; } - public bool EnableCache { get; set; } = true; - public Languages Languages { get; set; } = new(); + _analyzer = new ApplicationInspectorAnalyzer(_opts.LoggerFactory); } /// - /// Heart of RulesEngine. Parses code applies rules + /// Sets severity levels for analysis /// - public class RuleProcessor + private Severity SeverityLevel => _opts.SeverityFilter; + + /// + /// Enables caching of rules queries if multiple reuses per instance + /// + private bool EnableCache { get; } + + private static string ExtractDependency(TextContainer? text, int startIndex, string? pattern, string? language) { - private readonly int MAX_TEXT_SAMPLE_LENGTH = 200;//char bytes + if (text is null || string.IsNullOrEmpty(text.FullContent) || string.IsNullOrEmpty(language) || + string.IsNullOrEmpty(pattern)) return string.Empty; - private readonly RuleProcessorOptions _opts; - private readonly ILogger _logger; - private readonly Analyzer _analyzer; - private readonly AbstractRuleSet _ruleset; - private readonly Languages _languages; - private readonly ConcurrentDictionary> _fileRulesCache = new(); - private readonly ConcurrentDictionary> _languageRulesCache = new(); - private IEnumerable? _universalRulesCache; - - /// - /// Sets severity levels for analysis - /// - private Severity SeverityLevel => _opts.SeverityFilter; - - /// - /// Enables caching of rules queries if multiple reuses per instance - /// - private bool EnableCache { get; } - - /// - /// Creates instance of RuleProcessor - /// - public RuleProcessor(AbstractRuleSet rules, RuleProcessorOptions opts) + var rawResult = string.Empty; + var endIndex = text.FullContent.IndexOfAny(new[] { '\n', '\r' }, startIndex); + if (-1 != startIndex && -1 != endIndex) { - _opts = opts; - _logger = opts.LoggerFactory?.CreateLogger() ?? NullLogger.Instance; - _languages = opts.Languages; - _ruleset = rules; - EnableCache = true; + rawResult = text.FullContent[startIndex..endIndex].Trim(); + Regex regex = new(pattern); + var matches = regex.Matches(rawResult); - _analyzer = new ApplicationInspectorAnalyzer(_opts.LoggerFactory); - } - - private static string ExtractDependency(TextContainer? text, int startIndex, string? pattern, string? language) - { - if (text is null || string.IsNullOrEmpty(text.FullContent) || string.IsNullOrEmpty(language) || string.IsNullOrEmpty(pattern)) - { - return string.Empty; - } - - string rawResult = string.Empty; - int endIndex = text.FullContent.IndexOfAny(new char[] { '\n', '\r' }, startIndex); - if (-1 != startIndex && -1 != endIndex) - { - rawResult = text.FullContent[startIndex..endIndex].Trim(); - Regex regex = new(pattern); - MatchCollection matches = regex.Matches(rawResult); - - //remove surrounding import or trailing comments - if (matches.Any()) + //remove surrounding import or trailing comments + if (matches.Any()) + foreach (Match? match in matches) { - foreach (Match? match in matches) + if (match?.Groups.Count == 1) //handles cases like "using Newtonsoft.Json" { - if (match?.Groups.Count == 1)//handles cases like "using Newtonsoft.Json" - { - string[] parseValues = match.Groups[0].Value.Split(' '); - if (parseValues.Length == 1) - { - rawResult = parseValues[0].Trim(); - } - else if (parseValues.Length > 1) - { - rawResult = parseValues[1].Trim(); - } - } - else if (match?.Groups.Count > 1)//handles cases like include - { - rawResult = match.Groups[1].Value.Trim(); - } - //else if > 2 too hard to match; do nothing - - break;//only designed to expect one match per line i.e. not include value include value + string[] parseValues = match.Groups[0].Value.Split(' '); + if (parseValues.Length == 1) + rawResult = parseValues[0].Trim(); + else if (parseValues.Length > 1) rawResult = parseValues[1].Trim(); } + else if (match?.Groups.Count > 1) //handles cases like include + { + rawResult = match.Groups[1].Value.Trim(); + } + + //else if > 2 too hard to match; do nothing + break; //only designed to expect one match per line i.e. not include value include value } - string finalResult = rawResult.Replace(";", ""); + var finalResult = rawResult.Replace(";", ""); - return System.Net.WebUtility.HtmlEncode(finalResult); - } - - return rawResult; + return WebUtility.HtmlEncode(finalResult); } - /// - /// Analyzes a file and returns a list of - /// - /// TextContainer which holds the text to analyze - /// FileEntry which has the name of the file being analyzed. - /// The LanguageInfo for the file - /// Ignore rules that match tags that are only in the tags to ignore list - /// Number of lines of text to extract for the sample. Set to 0 to disable context gathering. Set to -1 to also disable sampling the match. - /// A List of the matches against the Rules the processor is configured with. - public List AnalyzeFile(TextContainer textContainer, FileEntry fileEntry, - LanguageInfo languageInfo, IEnumerable? tagsToIgnore = null, int numLinesContext = 3) - { - var rules = GetRulesForFile(languageInfo, fileEntry, tagsToIgnore); - List resultsList = new(); + return rawResult; + } - var caps = _analyzer.GetCaptures(rules, textContainer); - foreach (var ruleCapture in caps) + /// + /// Analyzes a file and returns a list of + /// + /// TextContainer which holds the text to analyze + /// FileEntry which has the name of the file being analyzed. + /// The LanguageInfo for the file + /// Ignore rules that match tags that are only in the tags to ignore list + /// + /// Number of lines of text to extract for the sample. Set to 0 to disable context gathering. + /// Set to -1 to also disable sampling the match. + /// + /// A List of the matches against the Rules the processor is configured with. + public List AnalyzeFile(TextContainer textContainer, FileEntry fileEntry, + LanguageInfo languageInfo, IEnumerable? tagsToIgnore = null, int numLinesContext = 3) + { + var rules = GetRulesForFile(languageInfo, fileEntry, tagsToIgnore); + List resultsList = new(); + + var caps = _analyzer.GetCaptures(rules, textContainer); + foreach (var ruleCapture in caps) + { + var netCaptures = FilterCaptures(ruleCapture.Captures); + var oatRule = ruleCapture.Rule as ConvertedOatRule; + foreach (var match in netCaptures) { - List<(int, Boundary)> netCaptures = FilterCaptures(ruleCapture.Captures); - var oatRule = ruleCapture.Rule as ConvertedOatRule; - foreach (var match in netCaptures) + var patternIndex = match.Item1; + var boundary = match.Item2; + //restrict adds from build files to tags with "metadata" only to avoid false feature positives that are not part of executable code + if (!_opts.AllowAllTagsInBuildFiles && languageInfo.Type == LanguageInfo.LangFileType.Build && + (oatRule.AppInspectorRule.Tags?.Any(v => !v.Contains("Metadata")) ?? false)) continue; + if (!_opts.ConfidenceFilter.HasFlag(oatRule.AppInspectorRule.Patterns[patternIndex].Confidence)) + continue; + + var startLocation = textContainer.GetLocation(boundary.Index); + var endLocation = textContainer.GetLocation(boundary.Index + boundary.Length); + MatchRecord newMatch = new(oatRule.AppInspectorRule) { - var patternIndex = match.Item1; - var boundary = match.Item2; - //restrict adds from build files to tags with "metadata" only to avoid false feature positives that are not part of executable code - if (!_opts.AllowAllTagsInBuildFiles && languageInfo.Type == LanguageInfo.LangFileType.Build && (oatRule.AppInspectorRule.Tags?.Any(v => !v.Contains("Metadata")) ?? false)) - { - continue; - } - if (!_opts.ConfidenceFilter.HasFlag(oatRule.AppInspectorRule.Patterns[patternIndex].Confidence)) - { - continue; - } + FileName = fileEntry.FullPath, + FullTextContainer = textContainer, + LanguageInfo = languageInfo, + Boundary = boundary, + StartLocationLine = startLocation.Line, + StartLocationColumn = startLocation.Column, + EndLocationLine = + endLocation.Line != 0 ? endLocation.Line : startLocation.Line + 1, //match is on last line + EndLocationColumn = endLocation.Column, + MatchingPattern = oatRule.AppInspectorRule.Patterns[patternIndex], + Excerpt = numLinesContext > 0 + ? ExtractExcerpt(textContainer, startLocation.Line, numLinesContext) + : string.Empty, + Sample = numLinesContext > -1 + ? ExtractTextSample(textContainer.FullContent, boundary.Index, boundary.Length) + : string.Empty + }; - Location startLocation = textContainer.GetLocation(boundary.Index); - Location endLocation = textContainer.GetLocation(boundary.Index + boundary.Length); - MatchRecord newMatch = new(oatRule.AppInspectorRule) - { - FileName = fileEntry.FullPath, - FullTextContainer = textContainer, - LanguageInfo = languageInfo, - Boundary = boundary, - StartLocationLine = startLocation.Line, - StartLocationColumn = startLocation.Column, - EndLocationLine = endLocation.Line != 0 ? endLocation.Line : startLocation.Line + 1, //match is on last line - EndLocationColumn = endLocation.Column, - MatchingPattern = oatRule.AppInspectorRule.Patterns[patternIndex], - Excerpt = numLinesContext > 0 ? ExtractExcerpt(textContainer, startLocation.Line, numLinesContext) : string.Empty, - Sample = numLinesContext > -1 ? ExtractTextSample(textContainer.FullContent, boundary.Index, boundary.Length) : string.Empty - }; + if (oatRule.AppInspectorRule.Tags?.Contains("Dependency.SourceInclude") ?? false) + newMatch.Sample = ExtractDependency(newMatch.FullTextContainer, newMatch.Boundary.Index, + newMatch.Pattern, newMatch.LanguageInfo.Name); - if (oatRule.AppInspectorRule.Tags?.Contains("Dependency.SourceInclude") ?? false) - { - newMatch.Sample = ExtractDependency(newMatch.FullTextContainer, newMatch.Boundary.Index, newMatch.Pattern, newMatch.LanguageInfo.Name); - } - - resultsList.Add(newMatch); - } - - // If a WithinClause capture is present, use only within captures, otherwise just flattens the list of results from the non-within clause. - List<(int, Boundary)> FilterCaptures(List captures) - { - // If we had a WithinClause we only want the captures that passed the within filter. - if (captures.Any(x => x.Clause is WithinClause)) - { - var onlyWithinCaptures = captures.Where(x => x.Clause is WithinClause).Cast>>().ToList(); - var allCaptured = onlyWithinCaptures.SelectMany(x => x.Result); - ConcurrentDictionary<(int, Boundary), int> numberOfInstances = new(); - // If there are multiple within clauses ensure that we only return matches which passed all clauses - // WithinClauses are always ANDed, but each contains all the captures that passed *that* clause. - // We need the captures that passed every clause. - foreach (var aCapture in allCaptured) - { - numberOfInstances.AddOrUpdate(aCapture, 1, (tuple, i) => i + 1); - } - return numberOfInstances.Where(x => x.Value == onlyWithinCaptures.Count).Select(x => x.Key).ToList(); - } - else - { - var outList = new List<(int, Boundary)>(); - foreach (var cap in captures) - { - if (cap is TypedClauseCapture> tcc) - { - outList.AddRange(tcc.Result); - } - } - - return outList; - } - } + resultsList.Add(newMatch); } - List removes = new(); - - foreach (MatchRecord m in resultsList.Where(x => x.Rule?.Overrides?.Length > 0)) - { - foreach (string idsToOverride in m.Rule?.Overrides ?? Array.Empty()) - { - // Find all overriden rules and mark them for removal from issues list - foreach (MatchRecord om in resultsList.FindAll(x => x.Rule?.Id == idsToOverride)) - { - // If the overridden match is a subset of the overriding match - if (om.Boundary.Index >= m.Boundary.Index && - om.Boundary.Index <= m.Boundary.Index + m.Boundary.Length) - { - removes.Add(om); - } - } - } - } - - // Remove overriden rules - resultsList.RemoveAll(x => removes.Contains(x)); - - return resultsList; - } - - /// - /// Analyzes a file and returns a list of - /// - /// A string containing the text to analyze - /// FileEntry which has the name of the file being analyzed - /// The LanguageInfo for the file - /// Ignore rules that match tags that are only in the tags to ignore list - /// Number of lines of text to extract for the sample. Set to 0 to disable context gathering. Set to -1 to also disable sampling the match. - /// A List of the matches against the Rules the processor is configured with. - public List AnalyzeFile(string contents, FileEntry fileEntry, LanguageInfo languageInfo, IEnumerable? tagsToIgnore = null, int numLinesContext = 3) - { - TextContainer textContainer = new(contents, languageInfo.Name, _languages, _opts.LoggerFactory ?? NullLoggerFactory.Instance); - return AnalyzeFile(textContainer, fileEntry, languageInfo, tagsToIgnore, numLinesContext); - } - - /// - /// Get the Rules which apply to the FileName of the FileEntry provided. - /// - /// - /// - /// - /// - public IEnumerable GetRulesForFile(LanguageInfo languageInfo, FileEntry fileEntry, IEnumerable? tagsToIgnore) - { - return GetRulesByLanguage(languageInfo.Name) - .Union(GetRulesByFileName(fileEntry.FullPath)) - .Union(GetUniversalRules().Where(x => !x.AppInspectorRule.DoesNotApplyTo?.Contains(languageInfo.Name) ?? true)) - .Where(x => !x.AppInspectorRule.Tags?.Any(y => tagsToIgnore?.Contains(y) ?? false) ?? true) - .Where(x => !x.AppInspectorRule.Disabled && SeverityLevel.HasFlag(x.AppInspectorRule.Severity)); - } - - /// - /// Analyzes a file and returns a list of - /// - /// FileEntry which holds the name of the file being analyzed as well as a Stream containing the contents to analyze - /// The LanguageInfo for the file - /// Ignore rules that match tags that are only in the tags to ignore list - /// Number of lines of text to extract for the sample. Set to 0 to disable context gathering. Set to -1 to also disable sampling the match. - /// A List of the matches against the Rules the processor is configured with. - public List AnalyzeFile(FileEntry fileEntry, LanguageInfo languageInfo, IEnumerable? tagsToIgnore = null, int numLinesContext = 3) - { - using var sr = new StreamReader(fileEntry.Content); - var contents = string.Empty; - try - { - contents = sr.ReadToEnd(); - } - catch(Exception e) - { - _logger.LogDebug("Failed to analyze file {path}. {type}:{message}. ({stackTrace}), fileRecord.FileName", fileEntry.FullPath, e.GetType(), e.Message, e.StackTrace); - } - return AnalyzeFile(contents, fileEntry, languageInfo, tagsToIgnore, numLinesContext); - } - - public async Task> AnalyzeFileAsync(FileEntry fileEntry, LanguageInfo languageInfo, CancellationToken? cancellationToken = null, IEnumerable? tagsToIgnore = null, int numLinesContext = 3) - { - var rules = GetRulesForFile(languageInfo, fileEntry, tagsToIgnore); - - List resultsList = new(); - - using var sr = new StreamReader(fileEntry.Content); - - TextContainer textContainer = new(await sr.ReadToEndAsync().ConfigureAwait(false), languageInfo.Name, _languages, _opts.LoggerFactory ?? NullLoggerFactory.Instance); - foreach (var ruleCapture in _analyzer.GetCaptures(rules, textContainer)) + // If a WithinClause capture is present, use only within captures, otherwise just flattens the list of results from the non-within clause. + List<(int, Boundary)> FilterCaptures(List captures) { // If we had a WithinClause we only want the captures that passed the within filter. - var filteredCaptures = ruleCapture.Captures.Any(x => x.Clause is WithinClause) - ? ruleCapture.Captures.Where(x => x.Clause is WithinClause) : ruleCapture.Captures; - if (cancellationToken?.IsCancellationRequested is true) + if (captures.Any(x => x.Clause is WithinClause)) { - return resultsList; - } - foreach (var cap in filteredCaptures) - { - resultsList.AddRange(ProcessBoundary(cap)); + var onlyWithinCaptures = captures.Where(x => x.Clause is WithinClause) + .Cast>>().ToList(); + var allCaptured = onlyWithinCaptures.SelectMany(x => x.Result); + ConcurrentDictionary<(int, Boundary), int> numberOfInstances = new(); + // If there are multiple within clauses ensure that we only return matches which passed all clauses + // WithinClauses are always ANDed, but each contains all the captures that passed *that* clause. + // We need the captures that passed every clause. + foreach (var aCapture in allCaptured) + numberOfInstances.AddOrUpdate(aCapture, 1, (tuple, i) => i + 1); + return numberOfInstances.Where(x => x.Value == onlyWithinCaptures.Count).Select(x => x.Key) + .ToList(); } - List ProcessBoundary(ClauseCapture cap) - { - List newMatches = new();//matches for this rule clause only - + var outList = new List<(int, Boundary)>(); + foreach (var cap in captures) if (cap is TypedClauseCapture> tcc) - { - if (ruleCapture.Rule is ConvertedOatRule oatRule) - { - if (tcc.Result is { } captureResults) + outList.AddRange(tcc.Result); + + return outList; + } + } + + List removes = new(); + + foreach (var m in resultsList.Where(x => x.Rule?.Overrides?.Length > 0)) + foreach (var idsToOverride in m.Rule?.Overrides ?? Array.Empty()) + // Find all overriden rules and mark them for removal from issues list + foreach (var om in resultsList.FindAll(x => x.Rule?.Id == idsToOverride)) + // If the overridden match is a subset of the overriding match + if (om.Boundary.Index >= m.Boundary.Index && + om.Boundary.Index <= m.Boundary.Index + m.Boundary.Length) + removes.Add(om); + + // Remove overriden rules + resultsList.RemoveAll(x => removes.Contains(x)); + + return resultsList; + } + + /// + /// Analyzes a file and returns a list of + /// + /// A string containing the text to analyze + /// FileEntry which has the name of the file being analyzed + /// The LanguageInfo for the file + /// Ignore rules that match tags that are only in the tags to ignore list + /// + /// Number of lines of text to extract for the sample. Set to 0 to disable context gathering. + /// Set to -1 to also disable sampling the match. + /// + /// A List of the matches against the Rules the processor is configured with. + public List AnalyzeFile(string contents, FileEntry fileEntry, LanguageInfo languageInfo, + IEnumerable? tagsToIgnore = null, int numLinesContext = 3) + { + TextContainer textContainer = new(contents, languageInfo.Name, _languages, + _opts.LoggerFactory ?? NullLoggerFactory.Instance); + return AnalyzeFile(textContainer, fileEntry, languageInfo, tagsToIgnore, numLinesContext); + } + + /// + /// Get the Rules which apply to the FileName of the FileEntry provided. + /// + /// + /// + /// + /// + public IEnumerable GetRulesForFile(LanguageInfo languageInfo, FileEntry fileEntry, + IEnumerable? tagsToIgnore) + { + return GetRulesByLanguage(languageInfo.Name) + .Union(GetRulesByFileName(fileEntry.FullPath)) + .Union(GetUniversalRules() + .Where(x => !x.AppInspectorRule.DoesNotApplyTo?.Contains(languageInfo.Name) ?? true)) + .Where(x => !x.AppInspectorRule.Tags?.Any(y => tagsToIgnore?.Contains(y) ?? false) ?? true) + .Where(x => !x.AppInspectorRule.Disabled && SeverityLevel.HasFlag(x.AppInspectorRule.Severity)); + } + + /// + /// Analyzes a file and returns a list of + /// + /// + /// FileEntry which holds the name of the file being analyzed as well as a Stream containing the + /// contents to analyze + /// + /// The LanguageInfo for the file + /// Ignore rules that match tags that are only in the tags to ignore list + /// + /// Number of lines of text to extract for the sample. Set to 0 to disable context gathering. + /// Set to -1 to also disable sampling the match. + /// + /// A List of the matches against the Rules the processor is configured with. + public List AnalyzeFile(FileEntry fileEntry, LanguageInfo languageInfo, + IEnumerable? tagsToIgnore = null, int numLinesContext = 3) + { + using var sr = new StreamReader(fileEntry.Content); + var contents = string.Empty; + try + { + contents = sr.ReadToEnd(); + } + catch (Exception e) + { + _logger.LogDebug("Failed to analyze file {path}. {type}:{message}. ({stackTrace}), fileRecord.FileName", + fileEntry.FullPath, e.GetType(), e.Message, e.StackTrace); + } + + return AnalyzeFile(contents, fileEntry, languageInfo, tagsToIgnore, numLinesContext); + } + + public async Task> AnalyzeFileAsync(FileEntry fileEntry, LanguageInfo languageInfo, + CancellationToken? cancellationToken = null, IEnumerable? tagsToIgnore = null, int numLinesContext = 3) + { + var rules = GetRulesForFile(languageInfo, fileEntry, tagsToIgnore); + + List resultsList = new(); + + using var sr = new StreamReader(fileEntry.Content); + + TextContainer textContainer = new(await sr.ReadToEndAsync().ConfigureAwait(false), languageInfo.Name, + _languages, _opts.LoggerFactory ?? NullLoggerFactory.Instance); + foreach (var ruleCapture in _analyzer.GetCaptures(rules, textContainer)) + { + // If we had a WithinClause we only want the captures that passed the within filter. + var filteredCaptures = ruleCapture.Captures.Any(x => x.Clause is WithinClause) + ? ruleCapture.Captures.Where(x => x.Clause is WithinClause) + : ruleCapture.Captures; + if (cancellationToken?.IsCancellationRequested is true) return resultsList; + foreach (var cap in filteredCaptures) resultsList.AddRange(ProcessBoundary(cap)); + + List ProcessBoundary(ClauseCapture cap) + { + List newMatches = new(); //matches for this rule clause only + + if (cap is TypedClauseCapture> tcc) + if (ruleCapture.Rule is ConvertedOatRule oatRule) + if (tcc.Result is { } captureResults) + foreach (var match in captureResults) { - foreach (var match in captureResults) + var patternIndex = match.Item1; + var boundary = match.Item2; + + //restrict adds from build files to tags with "metadata" only to avoid false feature positives that are not part of executable code + if (!_opts.AllowAllTagsInBuildFiles && + languageInfo.Type == LanguageInfo.LangFileType.Build && + (oatRule.AppInspectorRule.Tags?.Any(v => !v.Contains("Metadata")) ?? false)) + continue; + + if (patternIndex < 0 || patternIndex > oatRule.AppInspectorRule.Patterns.Length) { - var patternIndex = match.Item1; - var boundary = match.Item2; - - //restrict adds from build files to tags with "metadata" only to avoid false feature positives that are not part of executable code - if (!_opts.AllowAllTagsInBuildFiles && languageInfo.Type == LanguageInfo.LangFileType.Build && (oatRule.AppInspectorRule.Tags?.Any(v => !v.Contains("Metadata")) ?? false)) - { - continue; - } - - if (patternIndex < 0 || patternIndex > oatRule.AppInspectorRule.Patterns.Length) - { - _logger.LogError("Index out of range for patterns for rule: {ruleName}", oatRule.AppInspectorRule.Name); - continue; - } - - if (!_opts.ConfidenceFilter.HasFlag(oatRule.AppInspectorRule.Patterns[patternIndex].Confidence)) - { - continue; - } - - Location startLocation = textContainer.GetLocation(boundary.Index); - Location endLocation = textContainer.GetLocation(boundary.Index + boundary.Length); - MatchRecord newMatch = new(oatRule.AppInspectorRule) - { - FileName = fileEntry.FullPath, - FullTextContainer = textContainer, - LanguageInfo = languageInfo, - Boundary = boundary, - StartLocationLine = startLocation.Line, - EndLocationLine = endLocation.Line != 0 ? endLocation.Line : startLocation.Line + 1, //match is on last line - MatchingPattern = oatRule.AppInspectorRule.Patterns[patternIndex], - Excerpt = numLinesContext > 0 ? ExtractExcerpt(textContainer, startLocation.Line, numLinesContext) : string.Empty, - Sample = numLinesContext > -1 ? ExtractTextSample(textContainer.FullContent, boundary.Index, boundary.Length) : string.Empty - }; - - if (oatRule.AppInspectorRule.Tags?.Contains("Dependency.SourceInclude") ?? false) - { - newMatch.Sample = ExtractDependency(newMatch.FullTextContainer, newMatch.Boundary.Index, newMatch.Pattern, newMatch.LanguageInfo.Name); - } - - newMatches.Add(newMatch); + _logger.LogError("Index out of range for patterns for rule: {ruleName}", + oatRule.AppInspectorRule.Name); + continue; } + + if (!_opts.ConfidenceFilter.HasFlag(oatRule.AppInspectorRule.Patterns[patternIndex] + .Confidence)) continue; + + var startLocation = textContainer.GetLocation(boundary.Index); + var endLocation = textContainer.GetLocation(boundary.Index + boundary.Length); + MatchRecord newMatch = new(oatRule.AppInspectorRule) + { + FileName = fileEntry.FullPath, + FullTextContainer = textContainer, + LanguageInfo = languageInfo, + Boundary = boundary, + StartLocationLine = startLocation.Line, + EndLocationLine = + endLocation.Line != 0 + ? endLocation.Line + : startLocation.Line + 1, //match is on last line + MatchingPattern = oatRule.AppInspectorRule.Patterns[patternIndex], + Excerpt = numLinesContext > 0 + ? ExtractExcerpt(textContainer, startLocation.Line, numLinesContext) + : string.Empty, + Sample = numLinesContext > -1 + ? ExtractTextSample(textContainer.FullContent, boundary.Index, boundary.Length) + : string.Empty + }; + + if (oatRule.AppInspectorRule.Tags?.Contains("Dependency.SourceInclude") ?? false) + newMatch.Sample = ExtractDependency(newMatch.FullTextContainer, + newMatch.Boundary.Index, newMatch.Pattern, newMatch.LanguageInfo.Name); + + newMatches.Add(newMatch); } - } - } - return newMatches; - } + + return newMatches; } - - List removes = new(); - - foreach (MatchRecord m in resultsList.Where(x => x.Rule?.Overrides?.Length > 0)) - { - if (cancellationToken?.IsCancellationRequested is true) - { - return resultsList; - } - foreach (string ovrd in m.Rule?.Overrides ?? Array.Empty()) - { - // Find all overriden rules and mark them for removal from issues list - foreach (MatchRecord om in resultsList.FindAll(x => x.Rule?.Id == ovrd)) - { - if (om.Boundary.Index >= m.Boundary.Index && - om.Boundary.Index <= m.Boundary.Index + m.Boundary.Length) - { - removes.Add(om); - } - } - } - } - - // Remove overriden rules - resultsList.RemoveAll(x => removes.Contains(x)); - - return resultsList; } - - /// - /// Filters the rules for those matching the specified language. - /// - /// Language to filter rules for - /// List of rules - private IEnumerable GetRulesByLanguage(string language) + List removes = new(); + + foreach (var m in resultsList.Where(x => x.Rule?.Overrides?.Length > 0)) + { + if (cancellationToken?.IsCancellationRequested is true) return resultsList; + foreach (var ovrd in m.Rule?.Overrides ?? Array.Empty()) + // Find all overriden rules and mark them for removal from issues list + foreach (var om in resultsList.FindAll(x => x.Rule?.Id == ovrd)) + if (om.Boundary.Index >= m.Boundary.Index && + om.Boundary.Index <= m.Boundary.Index + m.Boundary.Length) + removes.Add(om); + } + + // Remove overriden rules + resultsList.RemoveAll(x => removes.Contains(x)); + + return resultsList; + } + + + /// + /// Filters the rules for those matching the specified language. + /// + /// Language to filter rules for + /// List of rules + private IEnumerable GetRulesByLanguage(string language) + { + if (EnableCache) + if (_languageRulesCache.ContainsKey(language)) + return _languageRulesCache[language]; + + IEnumerable filteredRules = _ruleset.ByLanguage(language).ToArray(); + + if (EnableCache) _languageRulesCache.TryAdd(language, filteredRules); + + return filteredRules; + } + + /// + /// Get all rules that apply to all files. + /// + /// List of rules + private IEnumerable GetUniversalRules() + { + if (_universalRulesCache is null) { if (EnableCache) - { - if (_languageRulesCache.ContainsKey(language)) - return _languageRulesCache[language]; - } - - IEnumerable filteredRules = _ruleset.ByLanguage(language).ToArray(); - - if (EnableCache) - { - _languageRulesCache.TryAdd(language, filteredRules); - } - - return filteredRules; + _universalRulesCache = _ruleset.GetUniversalRules(); + else + return _ruleset.GetUniversalRules(); } - /// - /// Get all rules that apply to all files. - /// - /// List of rules - private IEnumerable GetUniversalRules() - { - if (_universalRulesCache is null) - { - if (EnableCache) - { - _universalRulesCache = _ruleset.GetUniversalRules(); - } - else - { - return _ruleset.GetUniversalRules(); - } - } + return _universalRulesCache; + } - return _universalRulesCache; - } + /// + /// Filters the rules for those matching the filename. + /// + /// Filename to filter for + /// List of rules + private IEnumerable GetRulesByFileName(string fileName) + { + if (EnableCache) + if (_fileRulesCache.ContainsKey(fileName)) + return _fileRulesCache[fileName]; - /// - /// Filters the rules for those matching the filename. - /// - /// Filename to filter for - /// List of rules - private IEnumerable GetRulesByFileName(string fileName) - { - if (EnableCache) - { - if (_fileRulesCache.ContainsKey(fileName)) - return _fileRulesCache[fileName]; - } + IEnumerable filteredRules = _ruleset.ByFilename(fileName).ToArray(); - IEnumerable filteredRules = _ruleset.ByFilename(fileName).ToArray(); + if (EnableCache) _fileRulesCache.TryAdd(fileName, filteredRules); - if (EnableCache) - { - _fileRulesCache.TryAdd(fileName, filteredRules); - } + return filteredRules; + } - return filteredRules; - } + /// + /// Simple wrapper but keeps calling code consistent + /// Do not html code result which is accomplished later before out put to report + /// + private string ExtractTextSample(string fileText, int index, int length) + { + if (index < 0 || length < 0) return fileText; - /// - /// Simple wrapper but keeps calling code consistent - /// Do not html code result which is accomplished later before out put to report - /// - private string ExtractTextSample(string fileText, int index, int length) - { - if (index < 0 || length < 0) { return fileText; } + length = Math.Min(Math.Min(length, MAX_TEXT_SAMPLE_LENGTH), fileText.Length - index); - length = Math.Min(Math.Min(length, MAX_TEXT_SAMPLE_LENGTH), fileText.Length - index); + if (length == 0) return string.Empty; - if (length == 0) { return string.Empty; } + return fileText[index..(index + length)].Trim(); + } - return fileText[index..(index + length)].Trim(); - } + /// + /// Located here to include during Match creation to avoid a call later or putting in constructor + /// Needed in match ensuring value exists at time of report writing rather than expecting a callback + /// from the template + /// + /// + private static string ExtractExcerpt(TextContainer text, int startLineNumber, int context = 3) + { + if (context == 0) return string.Empty; + if (startLineNumber < 0) startLineNumber = 0; - /// - /// Located here to include during Match creation to avoid a call later or putting in constructor - /// Needed in match ensuring value exists at time of report writing rather than expecting a callback - /// from the template - /// - /// - private static string ExtractExcerpt(TextContainer text, int startLineNumber, int context = 3) - { - if (context == 0) - { - return string.Empty; - } - if (startLineNumber < 0) - { - startLineNumber = 0; - } + if (startLineNumber >= text.LineEnds.Count) startLineNumber = text.LineEnds.Count - 1; - if (startLineNumber >= text.LineEnds.Count) - { - startLineNumber = text.LineEnds.Count - 1; - } - - var excerptStartLine = Math.Max(0, startLineNumber - context); - var excerptEndLine = Math.Min(text.LineEnds.Count - 1, startLineNumber + context); - var startIndex = text.LineStarts[excerptStartLine]; - var endIndex = text.LineEnds[excerptEndLine] + 1; - var maxCharacterContext = context * 100; - // Only gather 100*lines context characters to avoid gathering super long lines - if (text.LineStarts[startLineNumber] - startIndex > maxCharacterContext) - { - startIndex = Math.Max(0, startIndex - maxCharacterContext); - } - if (endIndex - text.LineEnds[startLineNumber] > maxCharacterContext) - { - endIndex = Math.Min(text.FullContent.Length - 1, endIndex + maxCharacterContext); - } - return text.FullContent[startIndex..endIndex]; - } - - } -} + var excerptStartLine = Math.Max(0, startLineNumber - context); + var excerptEndLine = Math.Min(text.LineEnds.Count - 1, startLineNumber + context); + var startIndex = text.LineStarts[excerptStartLine]; + var endIndex = text.LineEnds[excerptEndLine] + 1; + var maxCharacterContext = context * 100; + // Only gather 100*lines context characters to avoid gathering super long lines + if (text.LineStarts[startLineNumber] - startIndex > maxCharacterContext) + startIndex = Math.Max(0, startIndex - maxCharacterContext); + if (endIndex - text.LineEnds[startLineNumber] > maxCharacterContext) + endIndex = Math.Min(text.FullContent.Length - 1, endIndex + maxCharacterContext); + return text.FullContent[startIndex..endIndex]; + } +} \ No newline at end of file diff --git a/AppInspector.RulesEngine/RulesVerifier.cs b/AppInspector.RulesEngine/RulesVerifier.cs index 3119705..8648ccd 100644 --- a/AppInspector.RulesEngine/RulesVerifier.cs +++ b/AppInspector.RulesEngine/RulesVerifier.cs @@ -14,211 +14,191 @@ using Microsoft.CST.OAT; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; -namespace Microsoft.ApplicationInspector.RulesEngine +namespace Microsoft.ApplicationInspector.RulesEngine; + +/// +/// Common helper used by VerifyRulesCommand and PackRulesCommand classes to reduce duplication +/// +public class RulesVerifier { - /// - /// Common helper used by VerifyRulesCommand and PackRulesCommand classes to reduce duplication - /// - public class RulesVerifier + private readonly Analyzer _analyzer; + private readonly ILogger _logger; + private readonly RulesVerifierOptions _options; + + public RulesVerifier(RulesVerifierOptions options) { - private readonly ILogger _logger; - private readonly RulesVerifierOptions _options; - private ILoggerFactory? _loggerFactory => _options.LoggerFactory; - private readonly Analyzer _analyzer; - public RulesVerifier(RulesVerifierOptions options) + _options = options; + _logger = _options.LoggerFactory?.CreateLogger() ?? NullLogger.Instance; + _analyzer = _options.Analyzer ?? new ApplicationInspectorAnalyzer(_options.LoggerFactory); + } + + private ILoggerFactory? _loggerFactory => _options.LoggerFactory; + + /// + /// Compile ruleset from a path to a directory or file containing a rule.json file and verify the status of the rules. + /// + /// Path to rules. + /// + /// + public RulesVerifierResult Verify(string rulesPath) + { + RuleSet CompiledRuleset = new(_loggerFactory); + + if (!string.IsNullOrEmpty(rulesPath)) { - _options = options; - _logger = _options.LoggerFactory?.CreateLogger() ?? NullLogger.Instance; - _analyzer = _options.Analyzer ?? new ApplicationInspectorAnalyzer(_options.LoggerFactory); + if (Directory.Exists(rulesPath)) + CompiledRuleset.AddDirectory(rulesPath); + else if (File.Exists(rulesPath)) + CompiledRuleset.AddFile(rulesPath); + else + throw new OpException(MsgHelp.FormatString(MsgHelp.ID.CMD_INVALID_RULE_PATH, rulesPath)); } - /// - /// Compile ruleset from a path to a directory or file containing a rule.json file and verify the status of the rules. - /// - /// Path to rules. - /// - /// - public RulesVerifierResult Verify(string rulesPath) - { - RuleSet CompiledRuleset = new(_loggerFactory); + return Verify(CompiledRuleset); + } - if (!string.IsNullOrEmpty(rulesPath)) + public RulesVerifierResult Verify(AbstractRuleSet ruleset) + { + return new RulesVerifierResult(CheckIntegrity(ruleset), ruleset); + } + + public List CheckIntegrity(AbstractRuleSet ruleSet) + { + List ruleStatuses = new(); + foreach (var rule in ruleSet.GetOatRules()) + { + var ruleVerified = CheckIntegrity(rule); + + ruleStatuses.Add(ruleVerified); + } + + if (!_options.DisableRequireUniqueIds) + { + var duplicatedRules = ruleSet.GetAppInspectorRules().GroupBy(x => x.Id).Where(y => y.Count() > 1); + foreach (var rule in duplicatedRules) { - if (Directory.Exists(rulesPath)) - { - CompiledRuleset.AddDirectory(rulesPath); - } - else if (File.Exists(rulesPath)) - { - CompiledRuleset.AddFile(rulesPath); - } - else - { - throw new OpException(MsgHelp.FormatString(MsgHelp.ID.CMD_INVALID_RULE_PATH, rulesPath)); - } + _logger.LogError(MsgHelp.GetString(MsgHelp.ID.VERIFY_RULES_DUPLICATEID_FAIL), rule.Key); + var relevantStati = ruleStatuses.Where(x => x.RulesId == rule.Key); + foreach (var status in relevantStati) + status.Errors = + status.Errors.Append(MsgHelp.FormatString(MsgHelp.ID.VERIFY_RULES_DUPLICATEID_FAIL, rule.Key)); } - return Verify(CompiledRuleset); } - public RulesVerifierResult Verify(AbstractRuleSet ruleset) + return ruleStatuses; + } + + public RuleStatus CheckIntegrity(ConvertedOatRule convertedOatRule) + { + List errors = new(); + + // App Inspector checks + var rule = convertedOatRule.AppInspectorRule; + // Check for null Id + if (string.IsNullOrEmpty(rule.Id)) { - return new RulesVerifierResult(CheckIntegrity(ruleset), ruleset); + _logger.LogError(MsgHelp.GetString(MsgHelp.ID.VERIFY_RULES_NULLID_FAIL), rule.Name); + errors.Add(MsgHelp.FormatString(MsgHelp.ID.VERIFY_RULES_NULLID_FAIL, rule.Name)); } - public List CheckIntegrity(AbstractRuleSet ruleSet) + //applicability + if (rule.AppliesTo != null) { - List ruleStatuses = new(); - foreach (ConvertedOatRule rule in ruleSet.GetOatRules()) - { - RuleStatus ruleVerified = CheckIntegrity(rule); - - ruleStatuses.Add(ruleVerified); - } - - if (!_options.DisableRequireUniqueIds) - { - var duplicatedRules = ruleSet.GetAppInspectorRules().GroupBy(x => x.Id).Where(y => y.Count() > 1); - foreach (var rule in duplicatedRules) - { - _logger.LogError(MsgHelp.GetString(MsgHelp.ID.VERIFY_RULES_DUPLICATEID_FAIL), rule.Key); - var relevantStati = ruleStatuses.Where(x => x.RulesId == rule.Key); - foreach(var status in relevantStati) + var languages = _options.LanguageSpecs.GetNames(); + // Check for unknown language + foreach (var lang in rule.AppliesTo) + if (!string.IsNullOrEmpty(lang)) + if (!languages.Any(x => x.Equals(lang, StringComparison.CurrentCultureIgnoreCase))) { - status.Errors = status.Errors.Append(MsgHelp.FormatString(MsgHelp.ID.VERIFY_RULES_DUPLICATEID_FAIL, rule.Key)); + _logger.LogError(MsgHelp.GetString(MsgHelp.ID.VERIFY_RULES_LANGUAGE_FAIL), rule.Id ?? "", lang); + errors.Add(MsgHelp.FormatString(MsgHelp.ID.VERIFY_RULES_LANGUAGE_FAIL, rule.Id ?? "", lang)); } - } - } - - return ruleStatuses; } - public RuleStatus CheckIntegrity(ConvertedOatRule convertedOatRule) + + foreach (var pattern in rule.FileRegexes ?? Array.Empty()) + try + { + _ = new Regex(pattern, RegexOptions.Compiled); + } + catch (Exception e) + { + _logger?.LogError(MsgHelp.GetString(MsgHelp.ID.VERIFY_RULES_REGEX_FAIL), rule.Id ?? "", pattern ?? "", + e.Message); + errors.Add(MsgHelp.FormatString(MsgHelp.ID.VERIFY_RULES_REGEX_FAIL, rule.Id ?? "", pattern ?? "", + e.Message)); + } + + //valid search pattern + foreach (var searchPattern in rule.Patterns ?? Array.Empty()) { - List errors = new(); - - // App Inspector checks - var rule = convertedOatRule.AppInspectorRule; - // Check for null Id - if (string.IsNullOrEmpty(rule.Id)) - { - _logger.LogError(MsgHelp.GetString(MsgHelp.ID.VERIFY_RULES_NULLID_FAIL), rule.Name); - errors.Add(MsgHelp.FormatString(MsgHelp.ID.VERIFY_RULES_NULLID_FAIL, rule.Name)); - } - - //applicability - if (rule.AppliesTo != null) - { - string[] languages = _options.LanguageSpecs.GetNames(); - // Check for unknown language - foreach (string lang in rule.AppliesTo) - { - if (!string.IsNullOrEmpty(lang)) - { - if (!languages.Any(x => x.Equals(lang, StringComparison.CurrentCultureIgnoreCase))) - { - _logger.LogError(MsgHelp.GetString(MsgHelp.ID.VERIFY_RULES_LANGUAGE_FAIL), rule.Id ?? "", lang); - errors.Add(MsgHelp.FormatString(MsgHelp.ID.VERIFY_RULES_LANGUAGE_FAIL, rule.Id ?? "", lang)); - } - } - } - } - - foreach (var pattern in rule.FileRegexes ?? Array.Empty()) - { + if (searchPattern.PatternType == PatternType.RegexWord || searchPattern.PatternType == PatternType.Regex) try { - _ = new Regex(pattern, RegexOptions.Compiled); + if (string.IsNullOrEmpty(searchPattern.Pattern)) throw new ArgumentException(); + _ = new Regex(searchPattern.Pattern); } catch (Exception e) { - _logger?.LogError(MsgHelp.GetString(MsgHelp.ID.VERIFY_RULES_REGEX_FAIL), rule.Id ?? "", pattern ?? "", e.Message); - errors.Add(MsgHelp.FormatString(MsgHelp.ID.VERIFY_RULES_REGEX_FAIL, rule.Id ?? "", pattern ?? "", e.Message)); + _logger?.LogError(MsgHelp.GetString(MsgHelp.ID.VERIFY_RULES_REGEX_FAIL), rule.Id ?? "", + searchPattern.Pattern ?? "", e.Message); + errors.Add(MsgHelp.FormatString(MsgHelp.ID.VERIFY_RULES_REGEX_FAIL, rule.Id ?? "", + searchPattern.Pattern ?? "", e.Message)); } - } - //valid search pattern - foreach (SearchPattern searchPattern in rule.Patterns ?? Array.Empty()) - { - if (searchPattern.PatternType == PatternType.RegexWord || searchPattern.PatternType == PatternType.Regex) - { + if (searchPattern.JsonPaths is not null) + foreach (var jsonPath in searchPattern.JsonPaths) try { - if (string.IsNullOrEmpty(searchPattern.Pattern)) - { - throw new ArgumentException(); - } - _ = new Regex(searchPattern.Pattern); + _ = JsonSelector.Parse(jsonPath); } catch (Exception e) { - _logger?.LogError(MsgHelp.GetString(MsgHelp.ID.VERIFY_RULES_REGEX_FAIL), rule.Id ?? "", searchPattern.Pattern ?? "", e.Message); - errors.Add(MsgHelp.FormatString(MsgHelp.ID.VERIFY_RULES_REGEX_FAIL, rule.Id ?? "", searchPattern.Pattern ?? "", e.Message)); + _logger?.LogError( + "The provided JsonPath '{JsonPath}' value was not valid in Rule {Id} : {message}", + searchPattern.JsonPaths, rule.Id, e.Message); + errors.Add(string.Format("The provided JsonPath '{0}' value was not valid in Rule {1} : {2}", + searchPattern.JsonPaths, rule.Id, e.Message)); } - } - if (searchPattern.JsonPaths is not null) - { - foreach (var jsonPath in searchPattern.JsonPaths) + if (searchPattern.XPaths is not null) + foreach (var xpath in searchPattern.XPaths) + try { - try - { - _ = JsonSelector.Parse(jsonPath); - } - catch (Exception e) - { - _logger?.LogError("The provided JsonPath '{JsonPath}' value was not valid in Rule {Id} : {message}", searchPattern.JsonPaths, rule.Id, e.Message); - errors.Add(string.Format("The provided JsonPath '{0}' value was not valid in Rule {1} : {2}", searchPattern.JsonPaths, rule.Id, e.Message)); - } + XPathExpression.Compile(xpath); } - } - - if (searchPattern.XPaths is not null) - { - foreach (var xpath in searchPattern.XPaths) + catch (Exception e) { - try - { - XPathExpression.Compile(xpath); - } - catch (Exception e) - { - _logger?.LogError("The provided XPath '{XPath}' value was not valid in Rule {Id} : {message}", searchPattern.XPaths, rule.Id, e.Message); - errors.Add(string.Format("The provided XPath '{0}' value was not valid in Rule {1} : {2}", searchPattern.JsonPaths, rule.Id, e.Message)); - } + _logger?.LogError("The provided XPath '{XPath}' value was not valid in Rule {Id} : {message}", + searchPattern.XPaths, rule.Id, e.Message); + errors.Add(string.Format("The provided XPath '{0}' value was not valid in Rule {1} : {2}", + searchPattern.JsonPaths, rule.Id, e.Message)); } - } - } + } - // validate conditions - foreach(var condition in rule.Conditions ?? Array.Empty()) + // validate conditions + foreach (var condition in rule.Conditions ?? Array.Empty()) + if (condition.SearchIn is null) { - if (condition.SearchIn is null) + _logger?.LogError("SearchIn is null in {ruleId}", rule.Id); + errors.Add($"SearchIn is null in {rule.Id}"); + } + else if (condition.SearchIn.StartsWith("finding-region")) + { + var parSplits = condition.SearchIn.Split(')', '('); + if (parSplits.Length == 3) { - _logger?.LogError("SearchIn is null in {ruleId}",rule.Id); - errors.Add($"SearchIn is null in {rule.Id}"); - } - else if (condition.SearchIn.StartsWith("finding-region")) - { - var parSplits = condition.SearchIn.Split(new char[] { ')', '(' }); - if (parSplits.Length == 3) + var splits = parSplits[1].Split(','); + if (splits.Length == 2) { - var splits = parSplits[1].Split(','); - if (splits.Length == 2) - { - if (int.TryParse(splits[0], out int int1) && int.TryParse(splits[1], out int int2)) + if (int.TryParse(splits[0], out var int1) && int.TryParse(splits[1], out var int2)) + if (int1 > 0 && int2 < 0) { - if (int1 > 0 && int2 < 0) - { - _logger?.LogError("The finding region must have a negative number or 0 for the lines before and a positive number or 0 for lines after. {0}", rule.Id); - errors.Add( - $"The finding region must have a negative number or 0 for the lines before and a positive number or 0 for lines after. {rule.Id}"); - } + _logger?.LogError( + "The finding region must have a negative number or 0 for the lines before and a positive number or 0 for lines after. {0}", + rule.Id); + errors.Add( + $"The finding region must have a negative number or 0 for the lines before and a positive number or 0 for lines after. {rule.Id}"); } - } - else - { - _logger?.LogError("Improperly specified finding region. {id}", rule.Id); - errors.Add($"Improperly specified finding region. {rule.Id}"); - } } else { @@ -226,70 +206,75 @@ namespace Microsoft.ApplicationInspector.RulesEngine errors.Add($"Improperly specified finding region. {rule.Id}"); } } - } - - var singleList = new [] {convertedOatRule}; - - // We need to provide a language for the TextContainer, which will later be referenced by the Rule when executed. - // We can grab any Language that the rule applies to, if there are none, it means it applies to all languages, except any in DoesNotApplyTo. - // Then we fall back to grab any language from the languages configuration that isn't DoesNotApplyTo for this rule. - var language = convertedOatRule.AppInspectorRule.AppliesTo?.FirstOrDefault() ?? - _options.LanguageSpecs.GetNames().FirstOrDefault(x => !convertedOatRule.AppInspectorRule.DoesNotApplyTo?.Contains(x, StringComparer.InvariantCultureIgnoreCase) ?? true) ?? "csharp"; - - // validate all must match samples are matched - foreach (var mustMatchElement in rule.MustMatch ?? Array.Empty()) - { - var tc = new TextContainer(mustMatchElement, language, _options.LanguageSpecs); - if (!_analyzer.Analyze(singleList, tc).Any()) + else { - _logger?.LogError("Rule {ID} does not match the 'MustMatch' test {MustMatch}. ", rule.Id, mustMatchElement); - errors.Add($"Rule {rule.Id} does not match the 'MustMatch' test {mustMatchElement}. "); + _logger?.LogError("Improperly specified finding region. {id}", rule.Id); + errors.Add($"Improperly specified finding region. {rule.Id}"); } } - // validate no must not match conditions are matched - foreach (var mustNotMatchElement in rule.MustNotMatch ?? Array.Empty()) - { - var tc = new TextContainer(mustNotMatchElement, language, _options.LanguageSpecs); - if (_analyzer.Analyze(singleList, tc).Any()) - { - _logger?.LogError("Rule {ID} matches the 'MustNotMatch' test '{MustNotMatch}'. ", rule.Id, mustNotMatchElement); - errors.Add($"Rule {rule.Id} matches the 'MustNotMatch' test '{mustNotMatchElement}'."); - } - } + var singleList = new[] { convertedOatRule }; - if (rule.Tags?.Length == 0) - { - _logger?.LogError("Rule must specify tags. {0}", rule.Id); - errors.Add($"Rule must specify tags. {rule.Id}"); - } + // We need to provide a language for the TextContainer, which will later be referenced by the Rule when executed. + // We can grab any Language that the rule applies to, if there are none, it means it applies to all languages, except any in DoesNotApplyTo. + // Then we fall back to grab any language from the languages configuration that isn't DoesNotApplyTo for this rule. + var language = convertedOatRule.AppInspectorRule.AppliesTo?.FirstOrDefault() ?? + _options.LanguageSpecs.GetNames().FirstOrDefault(x => + !convertedOatRule.AppInspectorRule.DoesNotApplyTo?.Contains(x, + StringComparer.InvariantCultureIgnoreCase) ?? true) ?? "csharp"; - if (_options.RequireMustMatch) + // validate all must match samples are matched + foreach (var mustMatchElement in rule.MustMatch ?? Array.Empty()) + { + var tc = new TextContainer(mustMatchElement, language, _options.LanguageSpecs); + if (!_analyzer.Analyze(singleList, tc).Any()) { - if (rule.MustMatch?.Any() is not true) - { - _logger?.LogError("Rule must specify MustMatch when `RequireMustMatch` is set. {0}", rule.Id); - errors.Add($"Rule must specify MustMatch when `RequireMustMatch` is set. {rule.Id}"); - } + _logger?.LogError("Rule {ID} does not match the 'MustMatch' test {MustMatch}. ", rule.Id, + mustMatchElement); + errors.Add($"Rule {rule.Id} does not match the 'MustMatch' test {mustMatchElement}. "); } - if (_options.RequireMustNotMatch) - { - if (rule.MustNotMatch?.Any() is not true) - { - _logger?.LogError("Rule must specify MustNotMatch when `RequireMustNotMatch` is set. {0}", rule.Id); - errors.Add($"Rule must specify MustNotMatch when `RequireMustNotMatch` is set. {rule.Id}"); - } - } - - return new RuleStatus() - { - RulesId = rule.Id, - RulesName = rule.Name, - Errors = errors, - OatIssues = _analyzer.EnumerateRuleIssues(convertedOatRule), - HasPositiveSelfTests = rule.MustMatch?.Length > 0, - HasNegativeSelfTests = rule.MustNotMatch?.Length > 0 - }; } + + // validate no must not match conditions are matched + foreach (var mustNotMatchElement in rule.MustNotMatch ?? Array.Empty()) + { + var tc = new TextContainer(mustNotMatchElement, language, _options.LanguageSpecs); + if (_analyzer.Analyze(singleList, tc).Any()) + { + _logger?.LogError("Rule {ID} matches the 'MustNotMatch' test '{MustNotMatch}'. ", rule.Id, + mustNotMatchElement); + errors.Add($"Rule {rule.Id} matches the 'MustNotMatch' test '{mustNotMatchElement}'."); + } + } + + if (rule.Tags?.Length == 0) + { + _logger?.LogError("Rule must specify tags. {0}", rule.Id); + errors.Add($"Rule must specify tags. {rule.Id}"); + } + + if (_options.RequireMustMatch) + if (rule.MustMatch?.Any() is not true) + { + _logger?.LogError("Rule must specify MustMatch when `RequireMustMatch` is set. {0}", rule.Id); + errors.Add($"Rule must specify MustMatch when `RequireMustMatch` is set. {rule.Id}"); + } + + if (_options.RequireMustNotMatch) + if (rule.MustNotMatch?.Any() is not true) + { + _logger?.LogError("Rule must specify MustNotMatch when `RequireMustNotMatch` is set. {0}", rule.Id); + errors.Add($"Rule must specify MustNotMatch when `RequireMustNotMatch` is set. {rule.Id}"); + } + + return new RuleStatus + { + RulesId = rule.Id, + RulesName = rule.Name, + Errors = errors, + OatIssues = _analyzer.EnumerateRuleIssues(convertedOatRule), + HasPositiveSelfTests = rule.MustMatch?.Length > 0, + HasNegativeSelfTests = rule.MustNotMatch?.Length > 0 + }; } } \ No newline at end of file diff --git a/AppInspector.RulesEngine/RulesVerifierOptions.cs b/AppInspector.RulesEngine/RulesVerifierOptions.cs index f05dd0e..92ff385 100644 --- a/AppInspector.RulesEngine/RulesVerifierOptions.cs +++ b/AppInspector.RulesEngine/RulesVerifierOptions.cs @@ -6,30 +6,34 @@ namespace Microsoft.ApplicationInspector.RulesEngine; public class RulesVerifierOptions { /// - /// If desired you may provide the analyzer to use. An analyzer with AI defaults will be created to use for validation. + /// If desired you may provide the analyzer to use. An analyzer with AI defaults will be created to use for validation. /// public Analyzer? Analyzer { get; set; } - /// - /// To receive log messages, provide a LoggerFactory with your preferred configuration. - /// - public ILoggerFactory? LoggerFactory { get; set; } - /// - /// The language specifications to use - /// - public Languages LanguageSpecs { get; set; } = new Languages(); /// - /// By default rules must have unique IDs, this disables that validation check + /// To receive log messages, provide a LoggerFactory with your preferred configuration. + /// + public ILoggerFactory? LoggerFactory { get; set; } + + /// + /// The language specifications to use + /// + public Languages LanguageSpecs { get; set; } = new(); + + /// + /// By default rules must have unique IDs, this disables that validation check /// public bool DisableRequireUniqueIds { get; set; } - + /// - /// By default, the property informs if is populated. Enabling this will cause an error to be raised during rule validation if it is not populated. + /// By default, the property informs if is + /// populated. Enabling this will cause an error to be raised during rule validation if it is not populated. /// public bool RequireMustMatch { get; set; } - + /// - /// By default, the property informs if is populated. Enabling this will cause an error to be raised during rule validation if it is not populated. + /// By default, the property informs if + /// is populated. Enabling this will cause an error to be raised during rule validation if it is not populated. /// public bool RequireMustNotMatch { get; set; } } \ No newline at end of file diff --git a/AppInspector.RulesEngine/RulesVerifierResult.cs b/AppInspector.RulesEngine/RulesVerifierResult.cs index bbd5c92..1541d4d 100644 --- a/AppInspector.RulesEngine/RulesVerifierResult.cs +++ b/AppInspector.RulesEngine/RulesVerifierResult.cs @@ -10,7 +10,8 @@ public class RulesVerifierResult RuleStatuses = ruleStatuses; CompiledRuleSet = compiledRuleSets; } + public List RuleStatuses { get; } public AbstractRuleSet CompiledRuleSet { get; } - public bool Verified => RuleStatuses.All(x => x.Verified); + public bool Verified => RuleStatuses.All(x => x.Verified); } \ No newline at end of file diff --git a/AppInspector.RulesEngine/Ruleset.cs b/AppInspector.RulesEngine/Ruleset.cs index eb7c891..3cbe542 100644 --- a/AppInspector.RulesEngine/Ruleset.cs +++ b/AppInspector.RulesEngine/Ruleset.cs @@ -1,22 +1,20 @@ // Copyright (C) Microsoft. All rights reserved. Licensed under the MIT License. -namespace Microsoft.ApplicationInspector.RulesEngine +using Microsoft.Extensions.Logging; + +namespace Microsoft.ApplicationInspector.RulesEngine; + +/// +/// Default class to use to store Application Inspector objects. +/// +public class RuleSet : TypedRuleSet { - using Microsoft.Extensions.Logging; - /// - /// Default class to use to store Application Inspector objects. + /// Create a ruleset using the given (optional) logger. /// - public class RuleSet : TypedRuleSet + /// + public RuleSet(ILoggerFactory? loggerFactory = null) : base(loggerFactory) { - - /// - /// Create a ruleset using the given (optional) logger. - /// - /// - public RuleSet(ILoggerFactory? loggerFactory = null) : base(loggerFactory) - { - } } -} +} \ No newline at end of file diff --git a/AppInspector.RulesEngine/SearchCondition.cs b/AppInspector.RulesEngine/SearchCondition.cs index a98b1dd..ca8b073 100644 --- a/AppInspector.RulesEngine/SearchCondition.cs +++ b/AppInspector.RulesEngine/SearchCondition.cs @@ -2,18 +2,16 @@ using Newtonsoft.Json; -namespace Microsoft.ApplicationInspector.RulesEngine +namespace Microsoft.ApplicationInspector.RulesEngine; + +public class SearchCondition { + [JsonProperty(PropertyName = "negate_finding")] + public bool NegateFinding { get; set; } - public class SearchCondition - { - [JsonProperty(PropertyName ="negate_finding")] - public bool NegateFinding { get; set; } + [JsonProperty(PropertyName = "pattern")] + public SearchPattern? Pattern { get; set; } - [JsonProperty(PropertyName ="pattern")] - public SearchPattern? Pattern { get; set; } - - [JsonProperty(PropertyName ="search_in")] - public string? SearchIn { get; set; } - } + [JsonProperty(PropertyName = "search_in")] + public string? SearchIn { get; set; } } \ No newline at end of file diff --git a/AppInspector.RulesEngine/SearchPattern.cs b/AppInspector.RulesEngine/SearchPattern.cs index 256d021..cf0f8c4 100644 --- a/AppInspector.RulesEngine/SearchPattern.cs +++ b/AppInspector.RulesEngine/SearchPattern.cs @@ -1,61 +1,56 @@ // Copyright (C) Microsoft. All rights reserved. Licensed under the MIT License. +using System.Collections.Generic; +using System.Text.RegularExpressions; using Newtonsoft.Json; using Newtonsoft.Json.Converters; -namespace Microsoft.ApplicationInspector.RulesEngine +namespace Microsoft.ApplicationInspector.RulesEngine; + +/// +/// Class to hold search pattern +/// +public class SearchPattern { - using System.Collections.Generic; - using System.Text.RegularExpressions; + private readonly Dictionary _compiled = new(); + private string? _pattern; + + [JsonProperty(PropertyName = "confidence")] + [JsonConverter(typeof(StringEnumConverter))] + public Confidence Confidence { get; set; } + + [JsonProperty(PropertyName = "modifiers")] + public string[]? Modifiers { get; set; } + + [JsonProperty(PropertyName = "pattern")] + public string? Pattern + { + get => _pattern; + set + { + _compiled.Clear(); + _pattern = value; + } + } + + [JsonProperty(PropertyName = "type")] + [JsonConverter(typeof(StringEnumConverter))] + public PatternType? PatternType { get; set; } + + [JsonProperty(PropertyName = "scopes")] + public PatternScope[]? Scopes { get; set; } /// - /// Class to hold search pattern + /// If set, attempt to parse the file as XML and if that is possible, + /// before running the pattern, select down to the XPath provided /// - public class SearchPattern - { - private Dictionary _compiled = new(); - private string? _pattern; + [JsonProperty(PropertyName = "xpaths")] + public string[]? XPaths { get; set; } - [JsonProperty(PropertyName ="confidence")] - [JsonConverter(typeof(StringEnumConverter))] - public Confidence Confidence { get; set; } - - [JsonProperty(PropertyName ="modifiers")] - public string[]? Modifiers { get; set; } - - [JsonProperty(PropertyName ="pattern")] - public string? Pattern - { - get - { - return _pattern; - } - set - { - _compiled.Clear(); - _pattern = value; - } - } - - [JsonProperty(PropertyName ="type")] - [JsonConverter(typeof(StringEnumConverter))] - public PatternType? PatternType { get; set; } - - [JsonProperty(PropertyName ="scopes")] - public PatternScope[]? Scopes { get; set; } - - /// - /// If set, attempt to parse the file as XML and if that is possible, - /// before running the pattern, select down to the XPath provided - /// - [JsonProperty(PropertyName ="xpaths")] - public string[]? XPaths { get; set; } - - /// - /// If set, attempt to parse the file as JSON and if that is possible, - /// before running the pattern, select down to the JsonPath provided - /// - [JsonProperty(PropertyName = "jsonpaths")] - public string[]? JsonPaths { get; set; } - } + /// + /// If set, attempt to parse the file as JSON and if that is possible, + /// before running the pattern, select down to the JsonPath provided + /// + [JsonProperty(PropertyName = "jsonpaths")] + public string[]? JsonPaths { get; set; } } \ No newline at end of file diff --git a/AppInspector.RulesEngine/Severity.cs b/AppInspector.RulesEngine/Severity.cs index 8f7b727..b008398 100644 --- a/AppInspector.RulesEngine/Severity.cs +++ b/AppInspector.RulesEngine/Severity.cs @@ -1,46 +1,45 @@ // Copyright (C) Microsoft. All rights reserved. Licensed under the MIT License. +using System; using Newtonsoft.Json; using Newtonsoft.Json.Converters; -namespace Microsoft.ApplicationInspector.RulesEngine +namespace Microsoft.ApplicationInspector.RulesEngine; + +/// +/// Issue severity +/// +[Flags] +[JsonConverter(typeof(StringEnumConverter))] +public enum Severity { - using System; + /// + /// Has not been specified + /// + Unspecified = 0, /// - /// Issue severity + /// Critical issues /// - [Flags] - [JsonConverter(typeof(StringEnumConverter))] - public enum Severity - { - /// - /// Has not been specified - /// - Unspecified = 0, - /// - /// Critical issues - /// - Critical = 1, + Critical = 1, - /// - /// Important issues - /// - Important = 2, + /// + /// Important issues + /// + Important = 2, - /// - /// Moderate issues - /// - Moderate = 4, + /// + /// Moderate issues + /// + Moderate = 4, - /// - /// Best Practice - /// - BestPractice = 8, + /// + /// Best Practice + /// + BestPractice = 8, - /// - /// Issues that require manual review - /// - ManualReview = 16 - } + /// + /// Issues that require manual review + /// + ManualReview = 16 } \ No newline at end of file diff --git a/AppInspector.RulesEngine/TextContainer.cs b/AppInspector.RulesEngine/TextContainer.cs index 8930c54..8ca490c 100644 --- a/AppInspector.RulesEngine/TextContainer.cs +++ b/AppInspector.RulesEngine/TextContainer.cs @@ -1,370 +1,319 @@ // Copyright (C) Microsoft. All rights reserved. Licensed under the MIT License. +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; using System.IO; +using System.Linq; using System.Reflection; using System.Text.Json; +using System.Xml.XPath; using JsonCons.JsonPath; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; -namespace Microsoft.ApplicationInspector.RulesEngine +namespace Microsoft.ApplicationInspector.RulesEngine; + +/// +/// Class to handle text as a searchable container +/// +public class TextContainer { - using System; - using System.Collections.Concurrent; - using System.Collections.Generic; - using System.Linq; - using System.Xml.XPath; + private readonly ILogger _logger; + + private readonly string inline; + private readonly string prefix; + private readonly string suffix; + private JsonDocument? _jsonDocument; + + private bool _triedToConstructJsonDocument; + + private bool _triedToConstructXPathDocument; + private XPathDocument? _xmlDoc; + /// - /// Class to handle text as a searchable container + /// Creates new instance /// - public class TextContainer + /// Text to work with + /// The language of the test + /// + /// An instance of the class containing the information for language + /// mapping to use. + /// + public TextContainer(string content, string language, Languages languages, ILoggerFactory? loggerFactory = null) { - /// - /// Creates new instance - /// - /// Text to work with - /// The language of the test - /// An instance of the class containing the information for language mapping to use. - public TextContainer(string content, string language, Languages languages, ILoggerFactory? loggerFactory = null) + _logger = loggerFactory?.CreateLogger() ?? NullLogger.Instance; + Language = language; + FullContent = content; + LineEnds = new List { 0 }; + LineStarts = new List { 0, 0 }; + + // Find line end in the text + var pos = FullContent.IndexOf('\n'); + while (pos > -1) { - _logger = loggerFactory?.CreateLogger() ?? NullLogger.Instance; - Language = language; - FullContent = content; - LineEnds = new List() { 0 }; - LineStarts = new List() { 0, 0 }; + LineEnds.Add(pos); - // Find line end in the text - int pos = FullContent.IndexOf('\n'); - while (pos > -1) - { - LineEnds.Add(pos); + if (pos + 1 < FullContent.Length) LineStarts.Add(pos + 1); - if (pos + 1 < FullContent.Length) - { - LineStarts.Add(pos + 1); - } - - pos = FullContent.IndexOf('\n', pos + 1); - } - - if (LineEnds.Count < LineStarts.Count) - { - LineEnds.Add(FullContent.Length - 1); - } - - prefix = languages.GetCommentPrefix(Language); - suffix = languages.GetCommentSuffix(Language); - inline = languages.GetCommentInline(Language); - Languages = languages; + pos = FullContent.IndexOf('\n', pos + 1); } - public Languages Languages { get; set; } + if (LineEnds.Count < LineStarts.Count) LineEnds.Add(FullContent.Length - 1); - private bool _triedToConstructJsonDocument; - private JsonDocument? _jsonDocument; - internal IEnumerable<(string, Boundary)> GetStringFromJsonPath(string Path) - { - if (!_triedToConstructJsonDocument) + prefix = languages.GetCommentPrefix(Language); + suffix = languages.GetCommentSuffix(Language); + inline = languages.GetCommentInline(Language); + Languages = languages; + } + + public Languages Languages { get; set; } + + /// + /// The full string of the TextContainer represents. + /// + public string FullContent { get; } + + /// + /// The code language of the file + /// + public string Language { get; } + + /// + /// One-indexed array of the character indexes of the ends of the lines in FullContent. + /// + public List LineEnds { get; } + + /// + /// One-indexed array of the character indexes of the starts of the lines in FullContent. + /// + public List LineStarts { get; } + + /// + /// A dictionary mapping character index in FullContent to if a specific character is commented. See IsCommented to + /// use. + /// + private ConcurrentDictionary CommentedStates { get; } = new(); + + internal IEnumerable<(string, Boundary)> GetStringFromJsonPath(string Path) + { + if (!_triedToConstructJsonDocument) + try { - try - { - _triedToConstructJsonDocument = true; - _jsonDocument = JsonDocument.Parse(FullContent); - } - catch (Exception e) - { - _logger.LogError("Failed to parse as a JSON document: {0}", e.Message); - _jsonDocument = null; - } + _triedToConstructJsonDocument = true; + _jsonDocument = JsonDocument.Parse(FullContent); + } + catch (Exception e) + { + _logger.LogError("Failed to parse as a JSON document: {0}", e.Message); + _jsonDocument = null; } - if (_jsonDocument is not null) - { - var selector = JsonSelector.Parse(Path); - - IList values = selector.Select(_jsonDocument.RootElement); + if (_jsonDocument is not null) + { + var selector = JsonSelector.Parse(Path); - var field = typeof(JsonElement).GetField("_idx", BindingFlags.NonPublic | BindingFlags.Instance); - if (field is null) - { - _logger.LogWarning("Failed to access _idx field of JsonElement."); - } - else - { - foreach (JsonElement ele in values) + var values = selector.Select(_jsonDocument.RootElement); + + var field = typeof(JsonElement).GetField("_idx", BindingFlags.NonPublic | BindingFlags.Instance); + if (field is null) + _logger.LogWarning("Failed to access _idx field of JsonElement."); + else + foreach (var ele in values) + // Private access hack + // The idx field is the start of the JSON element, including markup that isn't directly part of the element itself + if (field.GetValue(ele) is int idx) { - // Private access hack - // The idx field is the start of the JSON element, including markup that isn't directly part of the element itself - if (field.GetValue(ele) is int idx) + var eleString = ele.ToString(); + if (eleString is { } denulledString) { - var eleString = ele.ToString(); - if (eleString is { } denulledString) + var location = new Boundary { - var location = new Boundary() - { - // Adjust the index to the start of the actual element - Index = FullContent[idx..].IndexOf(denulledString, StringComparison.Ordinal) + idx, - Length = eleString.Length - }; - yield return (eleString, location); - } + // Adjust the index to the start of the actual element + Index = FullContent[idx..].IndexOf(denulledString, StringComparison.Ordinal) + idx, + Length = eleString.Length + }; + yield return (eleString, location); } - } - } - } - } - - private bool _triedToConstructXPathDocument; - private XPathDocument? _xmlDoc; - - /// - /// If this file is a JSON, XML or YML file, returns the string contents of the specified path. - /// If the path does not exist, or the file is not JSON, XML or YML returns null. - /// - /// - /// - internal IEnumerable<(string, Boundary)> GetStringFromXPath(string Path) - { - if (!_triedToConstructXPathDocument) - { - try - { - _triedToConstructXPathDocument = true; - _xmlDoc = new XPathDocument(new StringReader(FullContent)); - } - catch (Exception e) - { - _logger.LogError("Failed to parse as an XML document: {0}", e.Message); - _xmlDoc = null; - } - } - - if (_xmlDoc is not null) - { - var navigator = _xmlDoc.CreateNavigator(); - var nodeIter = navigator.Select(Path); - int minIndex = 0; - while (nodeIter.MoveNext()) - { - if (nodeIter.Current is not null) - { - // First we find the name - var nameIndex = FullContent[minIndex..].IndexOf(nodeIter.Current.Name); - // Then we grab the index of the end of this tag. - // We can't use OuterXML because the parser will inject the namespace if present into the OuterXML so it doesn't match the original text. - var endTagIndex = FullContent[nameIndex..].IndexOf('>'); - var totalOffset = nameIndex + endTagIndex + minIndex; - var offset = FullContent[totalOffset..].IndexOf(nodeIter.Current.InnerXml) + totalOffset; - // Move the minimum index up in case there are multiple instances of identical OuterXML - // This ensures we won't re-find the same one - minIndex = offset; - var location = new Boundary() - { - Index = offset, - Length = nodeIter.Current.InnerXml.Length - }; - yield return (nodeIter.Current.Value, location); } - } - } } - - /// - /// The full string of the TextContainer represents. - /// - public string FullContent { get; } - /// - /// The code language of the file - /// - public string Language { get; } - /// - /// One-indexed array of the character indexes of the ends of the lines in FullContent. - /// - public List LineEnds { get; } - /// - /// One-indexed array of the character indexes of the starts of the lines in FullContent. - /// - public List LineStarts { get; } + } - /// - /// A dictionary mapping character index in FullContent to if a specific character is commented. See IsCommented to use. - /// - private ConcurrentDictionary CommentedStates { get; } = new(); - - /// - /// Populates the CommentedStates Dictionary based on the index and the provided comment prefix and suffix - /// - /// The character index in FullContent - /// The comment prefix - /// The comment suffix - private void PopulateCommentedStatesInternal(int index, string prefix, string suffix) - { - var prefixLoc = FullContent.LastIndexOf(prefix, index, StringComparison.Ordinal); - if (prefixLoc != -1) + /// + /// If this file is a JSON, XML or YML file, returns the string contents of the specified path. + /// If the path does not exist, or the file is not JSON, XML or YML returns null. + /// + /// + /// + internal IEnumerable<(string, Boundary)> GetStringFromXPath(string Path) + { + if (!_triedToConstructXPathDocument) + try { - if (!CommentedStates.ContainsKey(prefixLoc)) + _triedToConstructXPathDocument = true; + _xmlDoc = new XPathDocument(new StringReader(FullContent)); + } + catch (Exception e) + { + _logger.LogError("Failed to parse as an XML document: {0}", e.Message); + _xmlDoc = null; + } + + if (_xmlDoc is not null) + { + var navigator = _xmlDoc.CreateNavigator(); + var nodeIter = navigator.Select(Path); + var minIndex = 0; + while (nodeIter.MoveNext()) + if (nodeIter.Current is not null) { - var suffixLoc = FullContent.IndexOf(suffix, prefixLoc, StringComparison.Ordinal); - if (suffixLoc == -1) + // First we find the name + var nameIndex = FullContent[minIndex..].IndexOf(nodeIter.Current.Name); + // Then we grab the index of the end of this tag. + // We can't use OuterXML because the parser will inject the namespace if present into the OuterXML so it doesn't match the original text. + var endTagIndex = FullContent[nameIndex..].IndexOf('>'); + var totalOffset = nameIndex + endTagIndex + minIndex; + var offset = FullContent[totalOffset..].IndexOf(nodeIter.Current.InnerXml) + totalOffset; + // Move the minimum index up in case there are multiple instances of identical OuterXML + // This ensures we won't re-find the same one + minIndex = offset; + var location = new Boundary { - suffixLoc = FullContent.Length - 1; - } - for (int i = prefixLoc; i <= suffixLoc; i++) - { - CommentedStates[i] = true; - } - } - } - } - - /// - /// Populate the CommentedStates Dictionary based on the provided index. - /// - /// The character index in FullContent to work based on. - public void PopulateCommentedState(int index) - { - var inIndex = index; - if (index >= FullContent.Length) - { - index = FullContent.Length - 1; - } - if (index < 0) - { - index = 0; - } - // Populate true for the indexes of the most immediately preceding instance of the multiline comment type if found - if (!string.IsNullOrEmpty(prefix) && !string.IsNullOrEmpty(suffix)) - { - PopulateCommentedStatesInternal(index, prefix, suffix); - } - // Populate true for indexes of the most immediately preceding instance of the single-line comment type if found - if (!CommentedStates.ContainsKey(index) && !string.IsNullOrEmpty(inline)) - { - PopulateCommentedStatesInternal(index, inline, "\n"); - } - var i = index; - // Everything preceding this, including this, which doesn't have a commented state is - // therefore not commented so we backfill - while (!CommentedStates.ContainsKey(i) && i >= 0) - { - CommentedStates[i--] = false; - } - if (inIndex != index) - { - CommentedStates[inIndex] = CommentedStates[index]; - } - } - - /// - /// Gets the text for a given boundary - /// - /// The boundary to get text for. - /// - public string GetBoundaryText(Boundary boundary) - { - if (boundary is null) - { - return string.Empty; - } - var start = Math.Max(boundary.Index, 0); - var end = start + boundary.Length; - start = Math.Min(FullContent.Length, start); - end = Math.Min(FullContent.Length, end); - return FullContent[start..end]; - } - - /// - /// Returns boundary for a given index in text - /// - /// Position in text - /// Boundary - public Boundary GetLineBoundary(int index) - { - Boundary result = new(); - - for (int i = 0; i < LineEnds.Count; i++) - { - if (LineEnds[i] >= index) - { - result.Index = LineStarts[i]; - result.Length = LineEnds[i] - LineStarts[i] + 1; - break; - } - } - - return result; - } - - /// - /// Return content of the line - /// - /// Line number (one-indexed) - /// Text - public string GetLineContent(int line) - { - if (line >= LineEnds.Count) - { - line = LineEnds.Count - 1; - } - int index = LineEnds[line]; - Boundary bound = GetLineBoundary(index); - return FullContent.Substring(bound.Index, bound.Length); - } - - /// - /// Returns location (Line, Column) for given index in text - /// - /// Position in text (line is one-indexed) - /// Location - public Location GetLocation(int index) - { - for (int i = 1; i < LineEnds.Count; i++) - { - if (LineEnds[i] >= index) - { - return new() - { - Column = index - LineStarts[i], - Line = i + Index = offset, + Length = nodeIter.Current.InnerXml.Length }; + yield return (nodeIter.Current.Value, location); } - } - - return new(); } + } - public bool IsCommented(int index) - { - if (!CommentedStates.ContainsKey(index)) + /// + /// Populates the CommentedStates Dictionary based on the index and the provided comment prefix and suffix + /// + /// The character index in FullContent + /// The comment prefix + /// The comment suffix + private void PopulateCommentedStatesInternal(int index, string prefix, string suffix) + { + var prefixLoc = FullContent.LastIndexOf(prefix, index, StringComparison.Ordinal); + if (prefixLoc != -1) + if (!CommentedStates.ContainsKey(prefixLoc)) { - PopulateCommentedState(index); + var suffixLoc = FullContent.IndexOf(suffix, prefixLoc, StringComparison.Ordinal); + if (suffixLoc == -1) suffixLoc = FullContent.Length - 1; + for (var i = prefixLoc; i <= suffixLoc; i++) CommentedStates[i] = true; } - return CommentedStates[index]; - } + } - /// - /// Check whether the boundary in a text matches the scope of a search pattern (code, comment etc.) - /// - /// The scopes to check - /// Boundary in the text - /// Text - /// True if boundary is in a provided scope - public bool ScopeMatch(IEnumerable scopes, Boundary boundary) - { - if (scopes is null || !scopes.Any() || scopes.Contains(PatternScope.All)) + /// + /// Populate the CommentedStates Dictionary based on the provided index. + /// + /// The character index in FullContent to work based on. + public void PopulateCommentedState(int index) + { + var inIndex = index; + if (index >= FullContent.Length) index = FullContent.Length - 1; + if (index < 0) index = 0; + // Populate true for the indexes of the most immediately preceding instance of the multiline comment type if found + if (!string.IsNullOrEmpty(prefix) && !string.IsNullOrEmpty(suffix)) + PopulateCommentedStatesInternal(index, prefix, suffix); + // Populate true for indexes of the most immediately preceding instance of the single-line comment type if found + if (!CommentedStates.ContainsKey(index) && !string.IsNullOrEmpty(inline)) + PopulateCommentedStatesInternal(index, inline, "\n"); + var i = index; + // Everything preceding this, including this, which doesn't have a commented state is + // therefore not commented so we backfill + while (!CommentedStates.ContainsKey(i) && i >= 0) CommentedStates[i--] = false; + if (inIndex != index) CommentedStates[inIndex] = CommentedStates[index]; + } + + /// + /// Gets the text for a given boundary + /// + /// The boundary to get text for. + /// + public string GetBoundaryText(Boundary boundary) + { + if (boundary is null) return string.Empty; + var start = Math.Max(boundary.Index, 0); + var end = start + boundary.Length; + start = Math.Min(FullContent.Length, start); + end = Math.Min(FullContent.Length, end); + return FullContent[start..end]; + } + + /// + /// Returns boundary for a given index in text + /// + /// Position in text + /// Boundary + public Boundary GetLineBoundary(int index) + { + Boundary result = new(); + + for (var i = 0; i < LineEnds.Count; i++) + if (LineEnds[i] >= index) { - return true; + result.Index = LineStarts[i]; + result.Length = LineEnds[i] - LineStarts[i] + 1; + break; } - if (scopes.Contains(PatternScope.All) || string.IsNullOrEmpty(prefix)) - return true; - bool isInComment = IsCommented(boundary.Index); - return (!isInComment && scopes.Contains(PatternScope.Code)) || (isInComment && scopes.Contains(PatternScope.Comment)); - } + return result; + } - private readonly string inline; - private readonly string prefix; - private readonly string suffix; - private readonly ILogger _logger; + /// + /// Return content of the line + /// + /// Line number (one-indexed) + /// Text + public string GetLineContent(int line) + { + if (line >= LineEnds.Count) line = LineEnds.Count - 1; + var index = LineEnds[line]; + var bound = GetLineBoundary(index); + return FullContent.Substring(bound.Index, bound.Length); + } + + /// + /// Returns location (Line, Column) for given index in text + /// + /// Position in text (line is one-indexed) + /// Location + public Location GetLocation(int index) + { + for (var i = 1; i < LineEnds.Count; i++) + if (LineEnds[i] >= index) + return new Location + { + Column = index - LineStarts[i], + Line = i + }; + + return new Location(); + } + + public bool IsCommented(int index) + { + if (!CommentedStates.ContainsKey(index)) PopulateCommentedState(index); + return CommentedStates[index]; + } + + /// + /// Check whether the boundary in a text matches the scope of a search pattern (code, comment etc.) + /// + /// The scopes to check + /// Boundary in the text + /// Text + /// True if boundary is in a provided scope + public bool ScopeMatch(IEnumerable scopes, Boundary boundary) + { + if (scopes is null || !scopes.Any() || scopes.Contains(PatternScope.All)) return true; + if (scopes.Contains(PatternScope.All) || string.IsNullOrEmpty(prefix)) + return true; + var isInComment = IsCommented(boundary.Index); + + return (!isInComment && scopes.Contains(PatternScope.Code)) || + (isInComment && scopes.Contains(PatternScope.Comment)); } } \ No newline at end of file diff --git a/AppInspector.RulesEngine/TypedRuleSet.cs b/AppInspector.RulesEngine/TypedRuleSet.cs index 26af87b..10ba2b2 100644 --- a/AppInspector.RulesEngine/TypedRuleSet.cs +++ b/AppInspector.RulesEngine/TypedRuleSet.cs @@ -9,10 +9,11 @@ using Newtonsoft.Json; namespace Microsoft.ApplicationInspector.RulesEngine; /// -/// The TypedRuleSet allows you to extend the Application Inspector Rule format with your own custom fields and have them be deserialized. They won't be used in processing, but may be used for additional follow up actions. +/// The TypedRuleSet allows you to extend the Application Inspector Rule format with your own custom fields and have +/// them be deserialized. They won't be used in processing, but may be used for additional follow up actions. /// -/// The Type of the Rule this set holds. It must inherit from -public class TypedRuleSet : AbstractRuleSet, IEnumerable where T : Rule +/// The Type of the Rule this set holds. It must inherit from +public class TypedRuleSet : AbstractRuleSet, IEnumerable where T : Rule { /// /// Creates instance of TypedRuleSet @@ -23,33 +24,35 @@ public class TypedRuleSet : AbstractRuleSet, IEnumerable where T : Rule } /// - /// Returns an enumerator that iterates through the AI Formatted . + /// Returns an enumerator that iterates through the AI Formatted . /// /// Enumerator - IEnumerator IEnumerable.GetEnumerator() => AppInspectorRulesAsEnumerableT().GetEnumerator(); - - /// - /// Returns an enumerator that iterates through the AI Formatted . - /// - /// Enumerator - public IEnumerator GetEnumerator() => AppInspectorRulesAsEnumerableT().GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() + { + return AppInspectorRulesAsEnumerableT().GetEnumerator(); + } /// - /// Returns the set of rules as an + /// Returns an enumerator that iterates through the AI Formatted . + /// + /// Enumerator + public IEnumerator GetEnumerator() + { + return AppInspectorRulesAsEnumerableT().GetEnumerator(); + } + + /// + /// Returns the set of rules as an /// /// private IEnumerable AppInspectorRulesAsEnumerableT() { foreach (var rule in _rules) - { if (rule is T ruleAsT) - { yield return ruleAsT; - } - } } - - + + /// /// Load rules from a file or directory /// @@ -57,21 +60,18 @@ public class TypedRuleSet : AbstractRuleSet, IEnumerable where T : Rule /// Tag for the rules /// Thrown if the filename is null or empty /// Thrown if the specified file cannot be found on the file system - /// Thrown if the specified file cannot be deserialized as a + /// + /// Thrown if the specified file cannot be deserialized as a + /// + /// public void AddPath(string path, string? tag = null) { if (Directory.Exists(path)) - { AddDirectory(path, tag); - } else if (File.Exists(path)) - { AddFile(path, tag); - } else - { throw new ArgumentException("The path must exist.", nameof(path)); - } } /// @@ -81,16 +81,17 @@ public class TypedRuleSet : AbstractRuleSet, IEnumerable where T : Rule /// Tag for the rules /// Thrown if the filename is null or empty /// Thrown if the specified file cannot be found on the file system - /// Thrown if the specified file cannot be deserialized as a + /// + /// Thrown if the specified file cannot be deserialized as a + /// + /// public void AddDirectory(string path, string? tag = null) { if (!Directory.Exists(path)) throw new DirectoryNotFoundException(); - foreach (string filename in Directory.EnumerateFileSystemEntries(path, "*.json", SearchOption.AllDirectories)) - { + foreach (var filename in Directory.EnumerateFileSystemEntries(path, "*.json", SearchOption.AllDirectories)) AddFile(filename, tag); - } } /// @@ -100,7 +101,10 @@ public class TypedRuleSet : AbstractRuleSet, IEnumerable where T : Rule /// Tag for the rules /// Thrown if the filename is null or empty /// Thrown if the specified file cannot be found on the file system - /// Thrown if the specified file cannot be deserialized as a + /// + /// Thrown if the specified file cannot be deserialized as a + /// + /// public void AddFile(string? filename, string? tag = null) { if (string.IsNullOrEmpty(filename)) @@ -109,7 +113,7 @@ public class TypedRuleSet : AbstractRuleSet, IEnumerable where T : Rule if (!File.Exists(filename)) throw new FileNotFoundException(); - using StreamReader file = File.OpenText(filename); + using var file = File.OpenText(filename); AddString(file.ReadToEnd(), filename, tag); } @@ -123,21 +127,16 @@ public class TypedRuleSet : AbstractRuleSet, IEnumerable where T : Rule public void AddString(string jsonString, string sourceName, string? tag = null) { if (StringToRules(jsonString ?? string.Empty, sourceName ?? string.Empty, tag) is { } deserializedList) - { AddRange(deserializedList); - } } - + /// /// Adds the elements of the collection to the Ruleset /// /// Collection of rules public void AddRange(IEnumerable? collection) { - foreach (T rule in collection ?? Array.Empty()) - { - AddRule(rule); - } + foreach (var rule in collection ?? Array.Empty()) AddRule(rule); } /// @@ -153,12 +152,14 @@ public class TypedRuleSet : AbstractRuleSet, IEnumerable where T : Rule } else { - _logger.LogError("Rule '{RuleId}:{RuleName}' could not be converted into an OAT rule. There may be message in the logs indicating why. You can run rule verification to identify the issue", rule.Id, rule.Name); + _logger.LogError( + "Rule '{RuleId}:{RuleName}' could not be converted into an OAT rule. There may be message in the logs indicating why. You can run rule verification to identify the issue", + rule.Id, rule.Name); } } - + /// - /// Deserialize a string into rules and enumerate them. + /// Deserialize a string into rules and enumerate them. /// /// /// @@ -173,17 +174,17 @@ public class TypedRuleSet : AbstractRuleSet, IEnumerable where T : Rule } catch (JsonSerializationException jsonSerializationException) { - _logger.LogError("Failed to deserialize '{0}' at Line {1} Column {2}", sourceName, jsonSerializationException.LineNumber, jsonSerializationException.LinePosition); + _logger.LogError("Failed to deserialize '{0}' at Line {1} Column {2}", sourceName, + jsonSerializationException.LineNumber, jsonSerializationException.LinePosition); throw; } + if (ruleList is not null) - { - foreach (T r in ruleList) + foreach (var r in ruleList) { r.Source = sourceName; r.RuntimeTag = tag; yield return r; } - } } } \ No newline at end of file diff --git a/AppInspector.Tests/AppInspector.Tests.csproj b/AppInspector.Tests/AppInspector.Tests.csproj index d9657c0..c4cc88e 100644 --- a/AppInspector.Tests/AppInspector.Tests.csproj +++ b/AppInspector.Tests/AppInspector.Tests.csproj @@ -1,34 +1,34 @@  - - Library - enable - 10.0 - net6.0;netcoreapp3.1 - + + Library + enable + 10.0 + net6.0;netcoreapp3.1 + - - - - - - Always - - + + + + + + Always + + - - - - - - - - - - + + + + + + + + - - - - - + + + + + + + diff --git a/AppInspector.Tests/Commands/TestAnalyzeCmd.cs b/AppInspector.Tests/Commands/TestAnalyzeCmd.cs index 97dede3..a9c3f51 100644 --- a/AppInspector.Tests/Commands/TestAnalyzeCmd.cs +++ b/AppInspector.Tests/Commands/TestAnalyzeCmd.cs @@ -12,73 +12,28 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.VisualStudio.TestTools.UnitTesting; -namespace AppInspector.Tests.Commands +namespace AppInspector.Tests.Commands; + +/// +/// Test class for the Analyze Command +/// +[TestClass] +[ExcludeFromCodeCoverage] +public class TestAnalyzeCmd { - /// - /// Test class for the Analyze Command - /// - [TestClass] - [ExcludeFromCodeCoverage] - public class TestAnalyzeCmd - { - private static string testFilePath = string.Empty; - private static string testRulesPath = string.Empty; - private static string appliesToTestRulePath = string.Empty; - private static string doesNotApplyToTestRulePath = string.Empty; + private const int numTimeOutFiles = 25; + private const int numTimesContent = 25; - // Test files for timeout tests - private static List enumeratingTimeOutTestsFiles = new(); - private const int numTimeOutFiles = 25; - private const int numTimesContent = 25; - - private ILoggerFactory factory = new NullLoggerFactory(); - - [ClassInitialize] - public static void ClassInit(TestContext context) - { - Directory.CreateDirectory(TestHelpers.GetPath(TestHelpers.AppPath.testOutput)); - testFilePath = Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput),"TestFile.js"); - testRulesPath = Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), "TestRules.json"); - heavyRulePath = Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), "HeavyRule.json"); - appliesToTestRulePath = Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), "AppliesToTestRules.json"); - doesNotApplyToTestRulePath = Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), "DoesNotApplyToTestRules.json"); - - File.WriteAllText(heavyRulePath, heavyRule); - File.WriteAllText(testFilePath, fourWindowsOneLinux); - File.WriteAllText(testRulesPath, findWindows); - File.WriteAllText(appliesToTestRulePath, findWindowsWithAppliesTo); - File.WriteAllText(doesNotApplyToTestRulePath, findWindowsWithDoesNotApplyTo); - - for (int i = 0; i < numTimeOutFiles; i++) - { - string newPath = Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput),$"TestFile-{i}.js"); - File.WriteAllText(newPath, string.Join('\n',Enumerable.Repeat(hardToFindContent, numTimesContent))); - enumeratingTimeOutTestsFiles.Add(newPath); - } - } - - private static string heavyRulePath = string.Empty; - - [TestInitialize] - public void InitOutput() - { - factory = new LogOptions().GetLoggerFactory(); - } - - [ClassCleanup] - public static void CleanUp() - { - Directory.Delete(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), true); - } - - private const string hardToFindContent = @" + private const string hardToFindContent = @" asefljkajsdfklasjdfklasjdfklasdfjklasdjfaklsdfjaklsdjfaklsfaksdjfkasdasdklfalskdfjalskdjfalskdjflaksdjflaskjdflaksjdflaksjdfljaskldfjjdkfaklsdfjlakasefljkajsdfklasjdfklasjdfklasdfjklasdjfaklsdfjaklsdjfaklsfaksdjfkasdasdklfalskdfjalskdjfalskdjflaksdjflaskjdflaksjdflaksjdfljaskldfjjdkfaklsdfjlakasefljkajsdfklasjdfklasjdfklasdfjklasdjfaklsdfjaklsdjfaklsfaksdjfkasdasdklfalskdfjalskdjfalskdjflaksdjflaskjdflaksjdflaksjdfljaskldfjjdkfaklsdfjlakasefljkajsdfklasjdfklasjdfklasdfjklasdjfaklsdfjaklsdjfaklsfaksdjfkasdasdklfalskdfjalskdjfalskdjflaksdjflaskjdflaksjdflaksjdfljaskldfjjdkfaklsdfjlakasefljkajsdfklasjdfklasjdfklasdfjklasdjfaklsdfjaklsdjfaklsfaksdjfkasdasdklfalskdfjalskdjfalskdjflaksdjflaskjdflaksjdflaksjdfljaskldfjjdkfaklsdfjlakasefljkajsdfklasjdfklasjdfklasdfjklasdjfaklsdfjaklsdjfaklsfaksdjfkasdasdklfalskdfjalskdjfalskdjflaksdjflaskjdflaksjdflaksjdfljaskldfjjdkfaklsdfjlakasefljkajsdfklasjdfklasjdfklasdfjklasdjfaklsdfjaklsdjfaklsfaksdjfkasdasdklfalskdfjalskdjfalskdjflaksdjflaskjdflaksjdflaksjdfljaskldfjjdkfaklsdfjlakasefljkajsdfklasjdfklasjdfklasdfjklasdjfaklsdfjaklsdjfaklsfaksdjfkasdasdklfalskdfjalskdjfalskdjflaksdjflaskjdflaksjdflaksjdfljaskldfjjdkfaklsdfjlakasefljkajsdfklasjdfklasjdfklasdfjklasdjfaklsdfjaklsdjfaklsfaksdjfkasdasdklfalskdfjalskdjfalskdjflaksdjflaskjdflaksjdflaksjdfljaskldfjjdkfaklsdfjlakasefljkajsdfklasjdfklasjdfklasdfjklasdjfaklsdfjaklsdjfaklsfaksdjfkasdasdklfalskdfjalskdjfalskdjflaksdjflaskjdflaksjdflaksjdfljaskldfjjdkfaklsdfjlakasefljkajsdfklasjdfklasjdfklasdfjklasdjfaklsdfjaklsdjfaklsfaksdjfkasdasdklfalskdfjalskdjfalskdjflaksdjflaskjdflaksjdflaksjdfljaskldfjjdkfaklsdfjlakasefljkajsdfklasjdfklasjdfklasdfjklasdjfaklsdfjaklsdjfaklsfaksdjfkasdasdklfalskdfjalskdjfalskdjflaksdjflaskjdflaksjdflaksjdfljaskldfjjdkfaklsdfjlakasefljkajsdfklasjdfklasjdfklasdfjklasdjfaklsdfjaklsdjfaklsfaksdjfkasdasdklfalskdfjalskdjfalskdjflaksdjflaskjdflaksjdflaksjdfljaskldfjjdkfaklsdfjlakasefljkajsdfklasjdfklasjdfklasdfjklasdjfaklsdfjaklsdjfaklsfaksdjfkasdasdklfalskdfjalskdjfalskdjflaksdjflaskjdflaksjdflaksjdfljaskldfjjdkfaklsdfjlakasefljkajsdfklasjdfklasjdfklasdfjklasdjfaklsdfjaklsdjfaklsfaksdjfkasdasdklfalskdfjalskdjfalskdjflaksdjflaskjdflaksjdflaksjdfljaskldfjjdkfaklsdfjlakasefljkajsdfklasjdfklasjdfklasdfjklasdjfaklsdfjaklsdjfaklsfaksdjfkasdasdklfalskdfjalskdjfalskdjflaksdjflaskjdflaksjdflaksjdfljaskldfjjdkfaklsdfjlakasefljkajsdfklasjdfklasjdfklasdfjklasdjfaklsdfjaklsdjfaklsfaksdjfkasdasdklfalskdfjalskdjfalskdjflaksdjflaskjdflaksjdflaksjdfljaskldfjjdkfaklsdfjlakasefljkajsdfklasjdfklasjdfklasdfjklasdjfaklsdfjaklsdjfaklsfaksdjfkasdasdklfalskdfjalskdjfalskdjflaksdjflaskjdflaksjdflaksjdfljaskldfjjdkfaklsdfjlakasefljkajsdfklasjdfklasjdfklasdfjklasdjfaklsdfjaklsdjfaklsfaksdjfkasdasdklfalskdfjalskdjfalskdjflaksdjflaskjdflaksjdflaksjdfljaskldfjjdkfaklsdfjlakasefljkajsdfklasjdfklasjdfklasdfjklasdjfaklsdfjaklsdjfaklsfaksdjfkasdasdklfalskdfjalskdjfalskdjflaksdjflaskjdflaksjdflaksjdfljaskldfjjdkfaklsdfjlakasefljkajsdfklasjdfklasjdfklasdfjklasdjfaklsdfjaklsdjfaklsfaksdjfkasdasdklfalskdfjalskdjfalskdjflaksdjflaskjdflaksjdflaksjdfljaskldfjjdkfaklsdfjlakasefljkajsdfklasjdfklasjdfklasdfjklasdjfaklsdfjaklsdjfaklsfaksdjfkasdasdklfalskdfjalskdjfalskdjflaksdjflaskjdflaksjdflaksjdfljaskldfjjdkfaklsdfjlakasefljkajsdfklasjdfklasjdfklasdfjklasdjfaklsdfjaklsdjfaklsfaksdjfkasdasdklfalskdfjalskdjfalskdjflaksdjflaskjdflaksjdflaksjdfljaskldfjjdkfaklsdfjlakasefljkajsdfklasjdfklasjdfklasdfjklasdjfaklsdfjaklsdjfaklsfaksdjfkasdasdklfalskdfjalskdjfalskdjflaksdjflaskjdflaksjdflaksjdfljaskldfjjdkfaklsdfjlakasefljkajsdfklasjdfklasjdfklasdfjklasdjfaklsdfjaklsdjfaklsfaksdjfkasdasdklfalskdfjalskdjfalskdjflaksdjflaskjdflaksjdflaksjdfljaskldfjjdkfaklsdfjlakasefljkajsdfklasjdfklasjdfklasdfjklasdjfaklsdfjaklsdjfaklsfaksdjfkasdasdklfalskdfjalskdjfalskdjflaksdjflaskjdflaksjdflaksjdfljaskldfjjdkfaklsdfjlakasefljkajsdfklasjdfklasjdfklasdfjklasdjfaklsdfjaklsdjfaklsfaksdjfkasdasdklfalskdfjalskdjfalskdjflaksdjflaskjdflaksjdflaksjdfljaskldfjjdkfaklsdfjlak@company.com1 buy@tacos.com "; - /// - /// This rule contains an intentionally catastrophic backtracking regex in order to trigger the timeout when running tests. - /// - const string heavyRule = @"[ + + /// + /// This rule contains an intentionally catastrophic backtracking regex in order to trigger the timeout when running + /// tests. + /// + private const string heavyRule = @"[ { ""name"": ""Runaway CSV Regex"", ""id"": ""AI_TEST_WINDOWS"", @@ -99,7 +54,7 @@ buy@tacos.com ] }]"; - private const string findWindowsWithAppliesTo = @"[ + private const string findWindowsWithAppliesTo = @"[ { ""name"": ""Platform: Microsoft Windows"", ""id"": ""AI_TEST_WINDOWS"", @@ -120,8 +75,8 @@ buy@tacos.com } ] }]"; - - private const string findWindowsWithDoesNotApplyTo = @"[ + + private const string findWindowsWithDoesNotApplyTo = @"[ { ""name"": ""Platform: Microsoft Windows"", ""id"": ""AI_TEST_WINDOWS"", @@ -142,10 +97,10 @@ buy@tacos.com } ] }]"; - - - // These simple test rules rules look for the string "windows" and "linux" - const string findWindows = @"[ + + + // These simple test rules rules look for the string "windows" and "linux" + private const string findWindows = @"[ { ""name"": ""Platform: Microsoft Windows"", ""id"": ""AI_TEST_WINDOWS"", @@ -185,7 +140,8 @@ buy@tacos.com ] } ]"; - const string findWindowsWithOverride = @"[ + + private const string findWindowsWithOverride = @"[ { ""name"": ""Platform: Microsoft Windows"", ""id"": ""AI_TEST_WINDOWS"", @@ -226,7 +182,8 @@ buy@tacos.com ] } ]"; - const string findWindowsWithOverrideRuleWithoutOverride = @"[ + + private const string findWindowsWithOverrideRuleWithoutOverride = @"[ { ""name"": ""Platform: Microsoft Windows"", ""id"": ""AI_TEST_WINDOWS"", @@ -266,7 +223,8 @@ buy@tacos.com ] } ]"; - const string findWindowsWithFileRegex = @"[ + + private const string findWindowsWithFileRegex = @"[ { ""name"": ""Platform: Microsoft Windows"", ""id"": ""AI_TEST_WINDOWS"", @@ -308,713 +266,774 @@ buy@tacos.com ] } ]"; - // This string contains windows four times and linux once. - const string fourWindowsOneLinux = -@"windows + + // This string contains windows four times and linux once. + private const string fourWindowsOneLinux = + @"windows windows linux windows windows "; - // This string contains windows four times and linux once. - const string threeWindowsOneWindows2000 = -@"windows + // This string contains windows four times and linux once. + private const string threeWindowsOneWindows2000 = + @"windows windows windows 2000 windows "; - [DataTestMethod] - public void Overrides() + + private static string testFilePath = string.Empty; + private static string testRulesPath = string.Empty; + private static string appliesToTestRulePath = string.Empty; + private static string doesNotApplyToTestRulePath = string.Empty; + + // Test files for timeout tests + private static readonly List enumeratingTimeOutTestsFiles = new(); + + private static string heavyRulePath = string.Empty; + + private ILoggerFactory factory = new NullLoggerFactory(); + + [ClassInitialize] + public static void ClassInit(TestContext context) + { + Directory.CreateDirectory(TestHelpers.GetPath(TestHelpers.AppPath.testOutput)); + testFilePath = Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), "TestFile.js"); + testRulesPath = Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), "TestRules.json"); + heavyRulePath = Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), "HeavyRule.json"); + appliesToTestRulePath = + Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), "AppliesToTestRules.json"); + doesNotApplyToTestRulePath = Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), + "DoesNotApplyToTestRules.json"); + + File.WriteAllText(heavyRulePath, heavyRule); + File.WriteAllText(testFilePath, fourWindowsOneLinux); + File.WriteAllText(testRulesPath, findWindows); + File.WriteAllText(appliesToTestRulePath, findWindowsWithAppliesTo); + File.WriteAllText(doesNotApplyToTestRulePath, findWindowsWithDoesNotApplyTo); + + for (var i = 0; i < numTimeOutFiles; i++) { - var overridesTestRulePath = Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), "OverrideTestRule.json"); - var overridesWithoutOverrideTestRulePath = Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), "OverrideTestRuleWithoutOverride.json"); - var fourWindowsOne2000Path = Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), "FourWindowsOne2000.cs"); - - File.WriteAllText(fourWindowsOne2000Path, threeWindowsOneWindows2000); - File.WriteAllText(overridesTestRulePath, findWindowsWithOverride); - File.WriteAllText(overridesWithoutOverrideTestRulePath, findWindowsWithOverrideRuleWithoutOverride); - - AnalyzeOptions options = new() - { - SourcePath = new string[1] { fourWindowsOne2000Path }, - CustomRulesPath = overridesTestRulePath, - IgnoreDefaultRules = true - }; - - AnalyzeCommand command = new(options, factory); - AnalyzeResult result = command.GetResult(); - Assert.AreEqual(AnalyzeResult.ExitCode.Success, result.ResultCode); - Assert.AreEqual(4, result.Metadata.TotalMatchesCount); - Assert.AreEqual(2, result.Metadata.UniqueMatchesCount); - - options = new() - { - SourcePath = new string[1] { fourWindowsOne2000Path }, - CustomRulesPath = overridesWithoutOverrideTestRulePath, - IgnoreDefaultRules = true - }; - - command = new(options, factory); - result = command.GetResult(); - Assert.AreEqual(AnalyzeResult.ExitCode.Success, result.ResultCode); - // This has one additional result for the same file because the match is not being overridden. - Assert.AreEqual(5, result.Metadata.TotalMatchesCount); - Assert.AreEqual(2, result.Metadata.UniqueMatchesCount); - } - - [DataTestMethod] - public async Task OverridesAsync() - { - var overridesTestRulePath = Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), "OverrideTestRule.json"); - var overridesWithoutOverrideTestRulePath = Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), "OverrideTestRuleWithoutOverride.json"); - var fourWindowsOne2000Path = Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), "FourWindowsOne2000.cs"); - - File.WriteAllText(fourWindowsOne2000Path, threeWindowsOneWindows2000); - File.WriteAllText(overridesTestRulePath, findWindowsWithOverride); - File.WriteAllText(overridesWithoutOverrideTestRulePath, findWindowsWithOverrideRuleWithoutOverride); - - AnalyzeOptions options = new() - { - SourcePath = new string[1] { fourWindowsOne2000Path }, - CustomRulesPath = overridesTestRulePath, - IgnoreDefaultRules = true - }; - - AnalyzeCommand command = new(options, factory); - AnalyzeResult result = await command.GetResultAsync(); - Assert.AreEqual(AnalyzeResult.ExitCode.Success, result.ResultCode); - Assert.AreEqual(4, result.Metadata.TotalMatchesCount); - Assert.AreEqual(2, result.Metadata.UniqueMatchesCount); - - options = new() - { - SourcePath = new string[1] { fourWindowsOne2000Path }, - CustomRulesPath = overridesWithoutOverrideTestRulePath, - IgnoreDefaultRules = true - }; - - command = new(options, factory); - result = await command.GetResultAsync(); - Assert.AreEqual(AnalyzeResult.ExitCode.Success, result.ResultCode); - // This has one additional result for the same file because the match is not being overridden. - Assert.AreEqual(5, result.Metadata.TotalMatchesCount); - Assert.AreEqual(2, result.Metadata.UniqueMatchesCount); - } - - private void DeleteTestFiles(IEnumerable pathsToDelete) - { - foreach (var path in pathsToDelete) - { - File.Delete(path); - } - } - - /// - /// Checks that the enumeration timeout works - /// - [DataRow(true, true)] - [DataRow(true, false)] - [DataRow(false, true)] - [DataRow(false, false)] - [DataTestMethod] - public void EnumeratingTimeoutTimesOut(bool singleThread, bool noShowProgress) - { - AnalyzeOptions options = new() - { - SourcePath = new []{enumeratingTimeOutTestsFiles[0]}, - CustomRulesPath = testRulesPath, - IgnoreDefaultRules = true, - EnumeratingTimeout = 1, - SingleThread = singleThread, - NoShowProgress = noShowProgress - }; - - AnalyzeCommand command = new(options, factory); - AnalyzeResult result = command.GetResult(); - - Assert.AreNotEqual(numTimeOutFiles, result.Metadata.TotalFiles); - } - - /// - /// Checks that the overall processing timeout works - /// - [DataRow(true, true)] - [DataRow(true, false)] - [DataRow(false, true)] - [DataRow(false, false)] - [DataTestMethod] - public void ProcessingTimeoutTimesOut(bool singleThread, bool noShowProgress) - { - AnalyzeOptions options = new() - { - SourcePath = new []{enumeratingTimeOutTestsFiles[0]}, - CustomRulesPath = heavyRulePath, - IgnoreDefaultRules = true, - ProcessingTimeOut = 1, - SingleThread = singleThread, - NoShowProgress = noShowProgress - }; - - AnalyzeCommand command = new(options, factory); - AnalyzeResult result = command.GetResult(); - - Assert.AreEqual(result.Metadata.FilesTimeOutSkipped,1); - } - - /// - /// Checks that the individual file timeout times out - /// - [DataRow(true, true)] - [DataRow(true, false)] - [DataRow(false, true)] - [DataRow(false, false)] - [DataTestMethod] - public void FileTimeoutTimesOut(bool singleThread, bool noShowProgress) - { - AnalyzeOptions options = new() - { - SourcePath = new []{enumeratingTimeOutTestsFiles[0]}, - CustomRulesPath = heavyRulePath, - IgnoreDefaultRules = true, - FileTimeOut = 1, - SingleThread = singleThread, - NoShowProgress = noShowProgress - }; - - AnalyzeCommand command = new(options, factory); - AnalyzeResult result = command.GetResult(); - - Assert.AreEqual(1, result.Metadata.FilesTimedOut); - } - - /// - /// Checks that the parameter to restrict the maximum number of matches for a tag works - /// - /// - /// - [DataRow(0, 4)] // 0 is the default value and indicates disabled. So we should find all 4. - [DataRow(1, 1)] - [DataRow(2, 2)] - [DataRow(3, 3)] - [DataRow(4, 4)] - [DataRow(9999, 4)] // 9999 is larger than 4, but there are only 4 to find. - [DataTestMethod] - public void MaxNumMatches(int MaxNumberOfMatchesParameter, int ActualExpectedNumberOfMatches) - { - AnalyzeOptions options = new() - { - SourcePath = new string[1] { testFilePath }, - MaxNumMatchesPerTag = MaxNumberOfMatchesParameter, - CustomRulesPath = testRulesPath, - IgnoreDefaultRules = true - }; - - AnalyzeCommand command = new(options, factory); - AnalyzeResult result = command.GetResult(); - - Assert.AreEqual(AnalyzeResult.ExitCode.Success, result.ResultCode); - Assert.AreEqual(ActualExpectedNumberOfMatches, result.Metadata.Matches.Count(x => x.Tags?.Contains("Test.Tags.Windows") ?? false)); - } - - /// - /// Checks that the parameter to restrict the maximum number of matches for a tag works using asynchronous. - /// - /// - /// - [DataRow(0, 4)] // 0 is the default value and indicates disabled. So we should find all 4. - [DataRow(1, 1)] - [DataRow(2, 2)] - [DataRow(3, 3)] - [DataRow(4, 4)] - [DataRow(9999, 4)] // 9999 is larger than 4, but there are only 4 to find. - [DataTestMethod] - public async Task MaxNumMatchesAsync(int MaxNumberOfMatchesParameter, int ActualExpectedNumberOfMatches) - { - AnalyzeOptions options = new() - { - SourcePath = new string[1] { testFilePath }, - MaxNumMatchesPerTag = MaxNumberOfMatchesParameter, - CustomRulesPath = testRulesPath, - IgnoreDefaultRules = true - }; - - AnalyzeCommand command = new(options, factory); - AnalyzeResult result = await command.GetResultAsync(new CancellationToken()); - - Assert.AreEqual(AnalyzeResult.ExitCode.Success, result.ResultCode); - Assert.AreEqual(ActualExpectedNumberOfMatches, result.Metadata.Matches.Count(x => x.Tags?.Contains("Test.Tags.Windows") ?? false)); - } - - /// - /// Ensure that an exception is thrown when a source file does not exist - /// - [TestMethod] - public void DetectMissingSourcePath() - { - // This will cause an exception when we try to scan a path to a non-extant file. - AnalyzeOptions options = new() - { - SourcePath = new string[1] { Path.Combine(testFilePath, ".not.a.real.file") }, - }; - - Assert.ThrowsException(() => new AnalyzeCommand(options)); - } - - /// - /// Ensure that an exception is thrown when the rules file which is specified does not exist. - /// - [TestMethod] - public void DetectMissingRulesPath() - { - // We need to ensure the test file exists, it doesn't matter what is in it. - File.WriteAllText(testFilePath, fourWindowsOneLinux); - AnalyzeOptions options = new() - { - SourcePath = new string[1] { testFilePath }, - CustomRulesPath = Path.Combine(testRulesPath, ".not.a.real.file"), - IgnoreDefaultRules = true - }; - - Assert.ThrowsException(() => new AnalyzeCommand(options)); - } - - /// - /// Ensure that an exception is thrown when no rules are specified and default rules are disabled. - /// - [TestMethod] - public void NoRulesSpecified() - { - AnalyzeOptions options = new() - { - SourcePath = new string[1] { testFilePath }, - IgnoreDefaultRules = true, - }; - - Assert.ThrowsException(() => new AnalyzeCommand(options)); - } - - /// - /// Test that the exclusion globs work - /// - [TestMethod] - public void TestExclusionFilter() - { - AnalyzeOptions options = new() - { - SourcePath = new string[1] { testFilePath }, - FilePathExclusions = new[] { "**/TestFile.js" }, - CustomRulesPath = testRulesPath, - IgnoreDefaultRules = true - }; - - AnalyzeCommand command = new(options, factory); - AnalyzeResult result = command.GetResult(); - Assert.AreEqual(AnalyzeResult.ExitCode.NoMatches, result.ResultCode); - Assert.AreEqual(0, result.Metadata.TotalMatchesCount); - } - - [TestMethod] - public void TestNoMatchesOkay() - { - AnalyzeOptions options = new() - { - SourcePath = new string[1] { testFilePath }, - FilePathExclusions = new[] { "**/TestFile.js" }, - CustomRulesPath = testRulesPath, - IgnoreDefaultRules = true, - SuccessErrorCodeOnNoMatches = true - }; - - AnalyzeCommand command = new(options, factory); - AnalyzeResult result = command.GetResult(); - Assert.AreEqual(AnalyzeResult.ExitCode.Success, result.ResultCode); - Assert.AreEqual(0, result.Metadata.TotalMatchesCount); - } - - [TestMethod] - public async Task TestNoMatchesOkayAsync() - { - AnalyzeOptions options = new() - { - SourcePath = new string[1] { testFilePath }, - FilePathExclusions = new[] { "**/TestFile.js" }, - CustomRulesPath = testRulesPath, - IgnoreDefaultRules = true, - SuccessErrorCodeOnNoMatches = true - }; - - AnalyzeCommand command = new(options, factory); - AnalyzeResult result = await command.GetResultAsync(); - Assert.AreEqual(AnalyzeResult.ExitCode.Success, result.ResultCode); - Assert.AreEqual(0, result.Metadata.TotalMatchesCount); - } - - [TestMethod] - public async Task ExpectedResultCountsAsync() - { - AnalyzeOptions options = new() - { - SourcePath = new string[1] { testFilePath }, - CustomRulesPath = testRulesPath, - IgnoreDefaultRules = true - }; - - AnalyzeCommand command = new(options, factory); - AnalyzeResult result = await command.GetResultAsync(); - Assert.AreEqual(AnalyzeResult.ExitCode.Success, result.ResultCode); - Assert.AreEqual(5, result.Metadata.TotalMatchesCount); - Assert.AreEqual(2, result.Metadata.UniqueMatchesCount); - } - - [DataTestMethod] - [DataRow(true, 0, 0)] - [DataRow(false, 2, 5)] - public void ExpectedResultCounts(bool disableArchive, int expectedUniqueCount, int expectedCount) - { - AnalyzeOptions options = new() - { - // This file is in the repo under test data and should be placed in the working directory by the build - SourcePath = new string[1] { Path.Combine("TestData","FourWindowsOneLinux.zip") }, - CustomRulesPath = testRulesPath, - IgnoreDefaultRules = true, - DisableCrawlArchives = disableArchive - }; - - AnalyzeCommand command = new(options, factory); - AnalyzeResult result = command.GetResult(); - Assert.AreEqual(disableArchive ? AnalyzeResult.ExitCode.NoMatches : AnalyzeResult.ExitCode.Success, result.ResultCode); - Assert.AreEqual(expectedCount, result.Metadata.TotalMatchesCount); - Assert.AreEqual(expectedUniqueCount, result.Metadata.UniqueMatchesCount); - } - - [TestMethod] - public void ExpectedResultCounts() - { - AnalyzeOptions options = new() - { - SourcePath = new string[1] { testFilePath }, - CustomRulesPath = testRulesPath, - IgnoreDefaultRules = true - }; - - AnalyzeCommand command = new(options, factory); - AnalyzeResult result = command.GetResult(); - Assert.AreEqual(AnalyzeResult.ExitCode.Success, result.ResultCode); - Assert.AreEqual(5, result.Metadata.TotalMatchesCount); - Assert.AreEqual(2, result.Metadata.UniqueMatchesCount); - } - - [DataRow("afile.js", AnalyzeResult.ExitCode.Success, 4, 1)] - [DataRow("adifferentfile.js", AnalyzeResult.ExitCode.Success, 1, 1)] - [DataTestMethod] - public void AppliesToFileName(string testFileName, AnalyzeResult.ExitCode expectedExitCode, int expectedTotalMatches, int expectedUniqueMatches) - { - var appliesToTestRulePath = Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), "AppliesToFileNameTestRule.json"); - var appliesToTestFilePath = Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), testFileName); - File.WriteAllText(appliesToTestFilePath, fourWindowsOneLinux); - File.WriteAllText(appliesToTestRulePath, findWindowsWithFileRegex); - AnalyzeOptions options = new() - { - SourcePath = new string[1] { appliesToTestFilePath }, - CustomRulesPath = appliesToTestRulePath, - IgnoreDefaultRules = true - }; - - AnalyzeCommand command = new(options, factory); - AnalyzeResult result = command.GetResult(); - Assert.AreEqual(expectedExitCode, result.ResultCode); - Assert.AreEqual(expectedTotalMatches, result.Metadata.TotalMatchesCount); - Assert.AreEqual(expectedUniqueMatches, result.Metadata.UniqueMatchesCount); - } - - - [TestMethod] - public void ScanUnknownFileTypes() - { - string scanPath = Path.GetTempFileName(); - - File.WriteAllText(scanPath, fourWindowsOneLinux); - - AnalyzeOptions options = new() - { - SourcePath = new string[1] { scanPath }, - CustomRulesPath = testRulesPath, - ScanUnknownTypes = true, - IgnoreDefaultRules= true - }; - AnalyzeCommand command = new(options, factory); - AnalyzeResult result = command.GetResult(); - - File.Delete(scanPath); - - Assert.AreEqual(AnalyzeResult.ExitCode.Success, result.ResultCode); - Assert.AreEqual(1, result.Metadata.TotalFiles); - Assert.AreEqual(0, result.Metadata.FilesSkipped); - Assert.AreEqual(1, result.Metadata.FilesAffected); - Assert.AreEqual(5, result.Metadata.TotalMatchesCount); - Assert.AreEqual(2, result.Metadata.UniqueMatchesCount); - } - - [TestMethod] - public void MultiPath_Pass() - { - string scanPath = Path.GetTempFileName(); - - File.WriteAllText(scanPath, fourWindowsOneLinux); - - AnalyzeOptions options = new() - { - SourcePath = new string[2] - { - scanPath, - testFilePath - }, - CustomRulesPath = testRulesPath, - ScanUnknownTypes = true, - IgnoreDefaultRules = true - }; - - AnalyzeCommand command = new(options, factory); - AnalyzeResult result = command.GetResult(); - Assert.AreEqual(AnalyzeResult.ExitCode.Success, result.ResultCode); - Assert.AreEqual(5 * 2, result.Metadata.TotalMatchesCount); - Assert.AreEqual(2, result.Metadata.UniqueMatchesCount); - } - - [TestMethod] - public void TagsOnly() - { - AnalyzeOptions options = new() - { - SourcePath = new string[1] { testFilePath }, - CustomRulesPath = testRulesPath, - IgnoreDefaultRules = true, - TagsOnly = true - }; - - AnalyzeCommand command = new(options, factory); - AnalyzeResult result = command.GetResult(); - - Assert.AreEqual(AnalyzeResult.ExitCode.Success, result.ResultCode); - Assert.AreEqual(0, result.Metadata.Matches.Count); - Assert.AreEqual(2, result.Metadata.UniqueTags.Count); - } - - [TestMethod] - public void SingleVsMultiThread() - { - List testFiles = new(); - - int iterations = 100; - for (int i = 0; i < iterations; i++) - { - string testFileName = Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), - $"SingleVsMultiThread-TestFile-{i}.js"); - File.WriteAllText(testFileName, fourWindowsOneLinux); - testFiles.Add(testFileName); - } - - AnalyzeOptions optionsSingle = new() - { - SourcePath = testFiles, - CustomRulesPath = testRulesPath, - IgnoreDefaultRules = true, - SingleThread = true - }; - - AnalyzeOptions optionsMulti = new() - { - SourcePath = testFiles, - CustomRulesPath = testRulesPath, - IgnoreDefaultRules = true, - SingleThread = false - }; - - AnalyzeCommand commandSingle = new(optionsSingle, factory); - AnalyzeResult resultSingle = commandSingle.GetResult(); - - AnalyzeCommand commandMulti = new(optionsMulti, factory); - AnalyzeResult resultMulti = commandMulti.GetResult(); - - Assert.AreEqual(AnalyzeResult.ExitCode.Success, resultSingle.ResultCode); - Assert.AreEqual(AnalyzeResult.ExitCode.Success, resultMulti.ResultCode); - Assert.AreEqual(5 * iterations, resultSingle.Metadata.TotalMatchesCount); - Assert.AreEqual(5 * iterations, resultMulti.Metadata.TotalMatchesCount); - Assert.IsTrue(resultSingle.Metadata.Matches.All(x => resultMulti.Metadata.Matches.Any(y => y.Tags?.All(z => x.Tags?.All(w => w.Contains(z)) ?? false) ?? false))); - } - - [DataRow(new[] { Severity.Moderate }, 1)] - [DataRow(new[] { Severity.Important }, 4)] - [DataRow(new[] { Severity.Important | Severity.Moderate }, 5)] - - [DataTestMethod] - public void SeverityFilters(Severity[] severityFilter, int ActualExpectedNumberOfMatches) - { - AnalyzeOptions options = new() - { - SourcePath = new string[1] { testFilePath }, - CustomRulesPath = testRulesPath, - SeverityFilters = severityFilter, - IgnoreDefaultRules = true - }; - - AnalyzeCommand command = new(options, factory); - AnalyzeResult result = command.GetResult(); - - Assert.AreEqual(AnalyzeResult.ExitCode.Success, result.ResultCode); - Assert.AreEqual(ActualExpectedNumberOfMatches, result.Metadata.Matches.Count); - } - - [DataRow(new[] { Confidence.High }, 1)] - [DataRow(new[] { Confidence.Medium }, 4)] - [DataRow(new[] { Confidence.Medium | Confidence.High }, 5)] - [DataTestMethod] - public void ConfidenceFilters(Confidence[] confidenceFilter, int ActualExpectedNumberOfMatches) - { - AnalyzeOptions options = new() - { - SourcePath = new string[1] { testFilePath }, - CustomRulesPath = testRulesPath, - ConfidenceFilters = confidenceFilter, - IgnoreDefaultRules = true - }; - - AnalyzeCommand command = new(options, factory); - AnalyzeResult result = command.GetResult(); - - Assert.AreEqual(AnalyzeResult.ExitCode.Success, result.ResultCode); - Assert.AreEqual(ActualExpectedNumberOfMatches, result.Metadata.Matches.Count); - } - - [TestMethod] - public void TagsInBuildFiles() - { - string innerTestFilePath = $"{Path.GetTempFileName()}.json"; // JSON is considered a build file, it does not have code/comment sections. - File.WriteAllText(innerTestFilePath, fourWindowsOneLinux); - - AnalyzeOptions options = new() - { - SourcePath = new string[1] { innerTestFilePath }, - CustomRulesPath = testRulesPath, - IgnoreDefaultRules = true, - AllowAllTagsInBuildFiles = true - }; - - AnalyzeCommand command = new(options, factory); - AnalyzeResult result = command.GetResult(); - - Assert.AreEqual(AnalyzeResult.ExitCode.Success, result.ResultCode); - Assert.AreEqual(5, result.Metadata.Matches.Count); - Assert.AreEqual(2, result.Metadata.UniqueMatchesCount); - - AnalyzeOptions dontAllowAllTagsOptions = new() - { - SourcePath = new string[1] { innerTestFilePath }, - CustomRulesPath = testRulesPath, - IgnoreDefaultRules = true, - AllowAllTagsInBuildFiles = false - }; - - AnalyzeCommand dontAllowAllTagsCommand = new(dontAllowAllTagsOptions); - AnalyzeResult dontAllowAllTagsResult = dontAllowAllTagsCommand.GetResult(); - - File.Delete(innerTestFilePath); - - Assert.AreEqual(AnalyzeResult.ExitCode.NoMatches, dontAllowAllTagsResult.ResultCode); - Assert.AreEqual(0, dontAllowAllTagsResult.Metadata.Matches.Count); - Assert.AreEqual(0, dontAllowAllTagsResult.Metadata.Matches.Count); - } - - [DataRow(1, 3)] - [DataRow(2, 5)] - [DataRow(3, 5)] - [DataRow(50, 5)] - [DataRow(0, 0)] - [DataTestMethod] - public void ContextLines(int numLinesContextArgument, int expectedNewLinesInResult) - { - AnalyzeOptions options = new() - { - SourcePath = new string[1] { testFilePath }, - CustomRulesPath = testRulesPath, - IgnoreDefaultRules = true, - ContextLines = numLinesContextArgument - }; - - AnalyzeCommand command = new(options, factory); - AnalyzeResult result = command.GetResult(); - - Assert.AreEqual(AnalyzeResult.ExitCode.Success, result.ResultCode); - var linuxResult = result.Metadata.Matches.Where(x => x.RuleId == "AI_TEST_LINUX"); - Assert.AreEqual(expectedNewLinesInResult, linuxResult.First().Excerpt.Count(x => x == '\n')); - } - - [TestMethod] - public void FileMetadata() - { - AnalyzeOptions optionsWithoutMetadata = new() - { - SourcePath = new string[1] { testFilePath }, - CustomRulesPath = testRulesPath, - IgnoreDefaultRules = true, - NoFileMetadata = true - }; - - AnalyzeOptions optionsWithMetadata = new() - { - SourcePath = new string[1] { testFilePath }, - CustomRulesPath = testRulesPath, - IgnoreDefaultRules = true, - NoFileMetadata = false - }; - - AnalyzeCommand commandWithoutMetadata = new(optionsWithoutMetadata, factory); - AnalyzeResult resultWithoutMetadata = commandWithoutMetadata.GetResult(); - - AnalyzeCommand commandWithMetadata = new(optionsWithMetadata, factory); - AnalyzeResult resultWithMetadata = commandWithMetadata.GetResult(); - - Assert.AreEqual(1, resultWithMetadata.Metadata.TotalFiles); - Assert.AreEqual(0, resultWithoutMetadata.Metadata.TotalFiles); - } - - /// - /// Test that the applies_to parameter allows the specified types - /// - [TestMethod] - public void TestAppliesTo() - { - AnalyzeOptions options = new() - { - SourcePath = new string[1] { testFilePath }, - CustomRulesPath = appliesToTestRulePath, - IgnoreDefaultRules = true - }; - - AnalyzeCommand command = new(options, factory); - AnalyzeResult result = command.GetResult(); - Assert.AreEqual(AnalyzeResult.ExitCode.Success, result.ResultCode); - Assert.AreEqual(4, result.Metadata.TotalMatchesCount); - } - - /// - /// Test that the does_not_apply_to parameter excludes the specified types - /// - [TestMethod] - public void TestDoesNotApplyTo() - { - AnalyzeOptions options = new() - { - SourcePath = new string[1] { testFilePath }, - CustomRulesPath = doesNotApplyToTestRulePath, - IgnoreDefaultRules = true - }; - - AnalyzeCommand command = new(options, factory); - AnalyzeResult result = command.GetResult(); - Assert.AreEqual(AnalyzeResult.ExitCode.NoMatches, result.ResultCode); - Assert.AreEqual(0, result.Metadata.TotalMatchesCount); + var newPath = Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), $"TestFile-{i}.js"); + File.WriteAllText(newPath, string.Join('\n', Enumerable.Repeat(hardToFindContent, numTimesContent))); + enumeratingTimeOutTestsFiles.Add(newPath); } } + + [TestInitialize] + public void InitOutput() + { + factory = new LogOptions().GetLoggerFactory(); + } + + [ClassCleanup] + public static void CleanUp() + { + Directory.Delete(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), true); + } + + [DataTestMethod] + public void Overrides() + { + var overridesTestRulePath = + Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), "OverrideTestRule.json"); + var overridesWithoutOverrideTestRulePath = Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), + "OverrideTestRuleWithoutOverride.json"); + var fourWindowsOne2000Path = + Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), "FourWindowsOne2000.cs"); + + File.WriteAllText(fourWindowsOne2000Path, threeWindowsOneWindows2000); + File.WriteAllText(overridesTestRulePath, findWindowsWithOverride); + File.WriteAllText(overridesWithoutOverrideTestRulePath, findWindowsWithOverrideRuleWithoutOverride); + + AnalyzeOptions options = new() + { + SourcePath = new string[1] { fourWindowsOne2000Path }, + CustomRulesPath = overridesTestRulePath, + IgnoreDefaultRules = true + }; + + AnalyzeCommand command = new(options, factory); + var result = command.GetResult(); + Assert.AreEqual(AnalyzeResult.ExitCode.Success, result.ResultCode); + Assert.AreEqual(4, result.Metadata.TotalMatchesCount); + Assert.AreEqual(2, result.Metadata.UniqueMatchesCount); + + options = new AnalyzeOptions + { + SourcePath = new string[1] { fourWindowsOne2000Path }, + CustomRulesPath = overridesWithoutOverrideTestRulePath, + IgnoreDefaultRules = true + }; + + command = new AnalyzeCommand(options, factory); + result = command.GetResult(); + Assert.AreEqual(AnalyzeResult.ExitCode.Success, result.ResultCode); + // This has one additional result for the same file because the match is not being overridden. + Assert.AreEqual(5, result.Metadata.TotalMatchesCount); + Assert.AreEqual(2, result.Metadata.UniqueMatchesCount); + } + + [DataTestMethod] + public async Task OverridesAsync() + { + var overridesTestRulePath = + Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), "OverrideTestRule.json"); + var overridesWithoutOverrideTestRulePath = Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), + "OverrideTestRuleWithoutOverride.json"); + var fourWindowsOne2000Path = + Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), "FourWindowsOne2000.cs"); + + File.WriteAllText(fourWindowsOne2000Path, threeWindowsOneWindows2000); + File.WriteAllText(overridesTestRulePath, findWindowsWithOverride); + File.WriteAllText(overridesWithoutOverrideTestRulePath, findWindowsWithOverrideRuleWithoutOverride); + + AnalyzeOptions options = new() + { + SourcePath = new string[1] { fourWindowsOne2000Path }, + CustomRulesPath = overridesTestRulePath, + IgnoreDefaultRules = true + }; + + AnalyzeCommand command = new(options, factory); + var result = await command.GetResultAsync(); + Assert.AreEqual(AnalyzeResult.ExitCode.Success, result.ResultCode); + Assert.AreEqual(4, result.Metadata.TotalMatchesCount); + Assert.AreEqual(2, result.Metadata.UniqueMatchesCount); + + options = new AnalyzeOptions + { + SourcePath = new string[1] { fourWindowsOne2000Path }, + CustomRulesPath = overridesWithoutOverrideTestRulePath, + IgnoreDefaultRules = true + }; + + command = new AnalyzeCommand(options, factory); + result = await command.GetResultAsync(); + Assert.AreEqual(AnalyzeResult.ExitCode.Success, result.ResultCode); + // This has one additional result for the same file because the match is not being overridden. + Assert.AreEqual(5, result.Metadata.TotalMatchesCount); + Assert.AreEqual(2, result.Metadata.UniqueMatchesCount); + } + + private void DeleteTestFiles(IEnumerable pathsToDelete) + { + foreach (var path in pathsToDelete) File.Delete(path); + } + + /// + /// Checks that the enumeration timeout works + /// + [DataRow(true, true)] + [DataRow(true, false)] + [DataRow(false, true)] + [DataRow(false, false)] + [DataTestMethod] + public void EnumeratingTimeoutTimesOut(bool singleThread, bool noShowProgress) + { + AnalyzeOptions options = new() + { + SourcePath = new[] { enumeratingTimeOutTestsFiles[0] }, + CustomRulesPath = testRulesPath, + IgnoreDefaultRules = true, + EnumeratingTimeout = 1, + SingleThread = singleThread, + NoShowProgress = noShowProgress + }; + + AnalyzeCommand command = new(options, factory); + var result = command.GetResult(); + + Assert.AreNotEqual(numTimeOutFiles, result.Metadata.TotalFiles); + } + + /// + /// Checks that the overall processing timeout works + /// + [DataRow(true, true)] + [DataRow(true, false)] + [DataRow(false, true)] + [DataRow(false, false)] + [DataTestMethod] + public void ProcessingTimeoutTimesOut(bool singleThread, bool noShowProgress) + { + AnalyzeOptions options = new() + { + SourcePath = new[] { enumeratingTimeOutTestsFiles[0] }, + CustomRulesPath = heavyRulePath, + IgnoreDefaultRules = true, + ProcessingTimeOut = 1, + SingleThread = singleThread, + NoShowProgress = noShowProgress + }; + + AnalyzeCommand command = new(options, factory); + var result = command.GetResult(); + + Assert.AreEqual(result.Metadata.FilesTimeOutSkipped, 1); + } + + /// + /// Checks that the individual file timeout times out + /// + [DataRow(true, true)] + [DataRow(true, false)] + [DataRow(false, true)] + [DataRow(false, false)] + [DataTestMethod] + public void FileTimeoutTimesOut(bool singleThread, bool noShowProgress) + { + AnalyzeOptions options = new() + { + SourcePath = new[] { enumeratingTimeOutTestsFiles[0] }, + CustomRulesPath = heavyRulePath, + IgnoreDefaultRules = true, + FileTimeOut = 1, + SingleThread = singleThread, + NoShowProgress = noShowProgress + }; + + AnalyzeCommand command = new(options, factory); + var result = command.GetResult(); + + Assert.AreEqual(1, result.Metadata.FilesTimedOut); + } + + /// + /// Checks that the parameter to restrict the maximum number of matches for a tag works + /// + /// + /// + [DataRow(0, 4)] // 0 is the default value and indicates disabled. So we should find all 4. + [DataRow(1, 1)] + [DataRow(2, 2)] + [DataRow(3, 3)] + [DataRow(4, 4)] + [DataRow(9999, 4)] // 9999 is larger than 4, but there are only 4 to find. + [DataTestMethod] + public void MaxNumMatches(int MaxNumberOfMatchesParameter, int ActualExpectedNumberOfMatches) + { + AnalyzeOptions options = new() + { + SourcePath = new string[1] { testFilePath }, + MaxNumMatchesPerTag = MaxNumberOfMatchesParameter, + CustomRulesPath = testRulesPath, + IgnoreDefaultRules = true + }; + + AnalyzeCommand command = new(options, factory); + var result = command.GetResult(); + + Assert.AreEqual(AnalyzeResult.ExitCode.Success, result.ResultCode); + Assert.AreEqual(ActualExpectedNumberOfMatches, + result.Metadata.Matches.Count(x => x.Tags?.Contains("Test.Tags.Windows") ?? false)); + } + + /// + /// Checks that the parameter to restrict the maximum number of matches for a tag works using asynchronous. + /// + /// + /// + [DataRow(0, 4)] // 0 is the default value and indicates disabled. So we should find all 4. + [DataRow(1, 1)] + [DataRow(2, 2)] + [DataRow(3, 3)] + [DataRow(4, 4)] + [DataRow(9999, 4)] // 9999 is larger than 4, but there are only 4 to find. + [DataTestMethod] + public async Task MaxNumMatchesAsync(int MaxNumberOfMatchesParameter, int ActualExpectedNumberOfMatches) + { + AnalyzeOptions options = new() + { + SourcePath = new string[1] { testFilePath }, + MaxNumMatchesPerTag = MaxNumberOfMatchesParameter, + CustomRulesPath = testRulesPath, + IgnoreDefaultRules = true + }; + + AnalyzeCommand command = new(options, factory); + var result = await command.GetResultAsync(new CancellationToken()); + + Assert.AreEqual(AnalyzeResult.ExitCode.Success, result.ResultCode); + Assert.AreEqual(ActualExpectedNumberOfMatches, + result.Metadata.Matches.Count(x => x.Tags?.Contains("Test.Tags.Windows") ?? false)); + } + + /// + /// Ensure that an exception is thrown when a source file does not exist + /// + [TestMethod] + public void DetectMissingSourcePath() + { + // This will cause an exception when we try to scan a path to a non-extant file. + AnalyzeOptions options = new() + { + SourcePath = new string[1] { Path.Combine(testFilePath, ".not.a.real.file") } + }; + + Assert.ThrowsException(() => new AnalyzeCommand(options)); + } + + /// + /// Ensure that an exception is thrown when the rules file which is specified does not exist. + /// + [TestMethod] + public void DetectMissingRulesPath() + { + // We need to ensure the test file exists, it doesn't matter what is in it. + File.WriteAllText(testFilePath, fourWindowsOneLinux); + AnalyzeOptions options = new() + { + SourcePath = new string[1] { testFilePath }, + CustomRulesPath = Path.Combine(testRulesPath, ".not.a.real.file"), + IgnoreDefaultRules = true + }; + + Assert.ThrowsException(() => new AnalyzeCommand(options)); + } + + /// + /// Ensure that an exception is thrown when no rules are specified and default rules are disabled. + /// + [TestMethod] + public void NoRulesSpecified() + { + AnalyzeOptions options = new() + { + SourcePath = new string[1] { testFilePath }, + IgnoreDefaultRules = true + }; + + Assert.ThrowsException(() => new AnalyzeCommand(options)); + } + + /// + /// Test that the exclusion globs work + /// + [TestMethod] + public void TestExclusionFilter() + { + AnalyzeOptions options = new() + { + SourcePath = new string[1] { testFilePath }, + FilePathExclusions = new[] { "**/TestFile.js" }, + CustomRulesPath = testRulesPath, + IgnoreDefaultRules = true + }; + + AnalyzeCommand command = new(options, factory); + var result = command.GetResult(); + Assert.AreEqual(AnalyzeResult.ExitCode.NoMatches, result.ResultCode); + Assert.AreEqual(0, result.Metadata.TotalMatchesCount); + } + + [TestMethod] + public void TestNoMatchesOkay() + { + AnalyzeOptions options = new() + { + SourcePath = new string[1] { testFilePath }, + FilePathExclusions = new[] { "**/TestFile.js" }, + CustomRulesPath = testRulesPath, + IgnoreDefaultRules = true, + SuccessErrorCodeOnNoMatches = true + }; + + AnalyzeCommand command = new(options, factory); + var result = command.GetResult(); + Assert.AreEqual(AnalyzeResult.ExitCode.Success, result.ResultCode); + Assert.AreEqual(0, result.Metadata.TotalMatchesCount); + } + + [TestMethod] + public async Task TestNoMatchesOkayAsync() + { + AnalyzeOptions options = new() + { + SourcePath = new string[1] { testFilePath }, + FilePathExclusions = new[] { "**/TestFile.js" }, + CustomRulesPath = testRulesPath, + IgnoreDefaultRules = true, + SuccessErrorCodeOnNoMatches = true + }; + + AnalyzeCommand command = new(options, factory); + var result = await command.GetResultAsync(); + Assert.AreEqual(AnalyzeResult.ExitCode.Success, result.ResultCode); + Assert.AreEqual(0, result.Metadata.TotalMatchesCount); + } + + [TestMethod] + public async Task ExpectedResultCountsAsync() + { + AnalyzeOptions options = new() + { + SourcePath = new string[1] { testFilePath }, + CustomRulesPath = testRulesPath, + IgnoreDefaultRules = true + }; + + AnalyzeCommand command = new(options, factory); + var result = await command.GetResultAsync(); + Assert.AreEqual(AnalyzeResult.ExitCode.Success, result.ResultCode); + Assert.AreEqual(5, result.Metadata.TotalMatchesCount); + Assert.AreEqual(2, result.Metadata.UniqueMatchesCount); + } + + [DataTestMethod] + [DataRow(true, 0, 0)] + [DataRow(false, 2, 5)] + public void ExpectedResultCounts(bool disableArchive, int expectedUniqueCount, int expectedCount) + { + AnalyzeOptions options = new() + { + // This file is in the repo under test data and should be placed in the working directory by the build + SourcePath = new string[1] { Path.Combine("TestData", "FourWindowsOneLinux.zip") }, + CustomRulesPath = testRulesPath, + IgnoreDefaultRules = true, + DisableCrawlArchives = disableArchive + }; + + AnalyzeCommand command = new(options, factory); + var result = command.GetResult(); + Assert.AreEqual(disableArchive ? AnalyzeResult.ExitCode.NoMatches : AnalyzeResult.ExitCode.Success, + result.ResultCode); + Assert.AreEqual(expectedCount, result.Metadata.TotalMatchesCount); + Assert.AreEqual(expectedUniqueCount, result.Metadata.UniqueMatchesCount); + } + + [TestMethod] + public void ExpectedResultCounts() + { + AnalyzeOptions options = new() + { + SourcePath = new string[1] { testFilePath }, + CustomRulesPath = testRulesPath, + IgnoreDefaultRules = true + }; + + AnalyzeCommand command = new(options, factory); + var result = command.GetResult(); + Assert.AreEqual(AnalyzeResult.ExitCode.Success, result.ResultCode); + Assert.AreEqual(5, result.Metadata.TotalMatchesCount); + Assert.AreEqual(2, result.Metadata.UniqueMatchesCount); + } + + [DataRow("afile.js", AnalyzeResult.ExitCode.Success, 4, 1)] + [DataRow("adifferentfile.js", AnalyzeResult.ExitCode.Success, 1, 1)] + [DataTestMethod] + public void AppliesToFileName(string testFileName, AnalyzeResult.ExitCode expectedExitCode, + int expectedTotalMatches, int expectedUniqueMatches) + { + var appliesToTestRulePath = Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), + "AppliesToFileNameTestRule.json"); + var appliesToTestFilePath = Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), testFileName); + File.WriteAllText(appliesToTestFilePath, fourWindowsOneLinux); + File.WriteAllText(appliesToTestRulePath, findWindowsWithFileRegex); + AnalyzeOptions options = new() + { + SourcePath = new string[1] { appliesToTestFilePath }, + CustomRulesPath = appliesToTestRulePath, + IgnoreDefaultRules = true + }; + + AnalyzeCommand command = new(options, factory); + var result = command.GetResult(); + Assert.AreEqual(expectedExitCode, result.ResultCode); + Assert.AreEqual(expectedTotalMatches, result.Metadata.TotalMatchesCount); + Assert.AreEqual(expectedUniqueMatches, result.Metadata.UniqueMatchesCount); + } + + + [TestMethod] + public void ScanUnknownFileTypes() + { + var scanPath = Path.GetTempFileName(); + + File.WriteAllText(scanPath, fourWindowsOneLinux); + + AnalyzeOptions options = new() + { + SourcePath = new string[1] { scanPath }, + CustomRulesPath = testRulesPath, + ScanUnknownTypes = true, + IgnoreDefaultRules = true + }; + AnalyzeCommand command = new(options, factory); + var result = command.GetResult(); + + File.Delete(scanPath); + + Assert.AreEqual(AnalyzeResult.ExitCode.Success, result.ResultCode); + Assert.AreEqual(1, result.Metadata.TotalFiles); + Assert.AreEqual(0, result.Metadata.FilesSkipped); + Assert.AreEqual(1, result.Metadata.FilesAffected); + Assert.AreEqual(5, result.Metadata.TotalMatchesCount); + Assert.AreEqual(2, result.Metadata.UniqueMatchesCount); + } + + [TestMethod] + public void MultiPath_Pass() + { + var scanPath = Path.GetTempFileName(); + + File.WriteAllText(scanPath, fourWindowsOneLinux); + + AnalyzeOptions options = new() + { + SourcePath = new string[2] + { + scanPath, + testFilePath + }, + CustomRulesPath = testRulesPath, + ScanUnknownTypes = true, + IgnoreDefaultRules = true + }; + + AnalyzeCommand command = new(options, factory); + var result = command.GetResult(); + Assert.AreEqual(AnalyzeResult.ExitCode.Success, result.ResultCode); + Assert.AreEqual(5 * 2, result.Metadata.TotalMatchesCount); + Assert.AreEqual(2, result.Metadata.UniqueMatchesCount); + } + + [TestMethod] + public void TagsOnly() + { + AnalyzeOptions options = new() + { + SourcePath = new string[1] { testFilePath }, + CustomRulesPath = testRulesPath, + IgnoreDefaultRules = true, + TagsOnly = true + }; + + AnalyzeCommand command = new(options, factory); + var result = command.GetResult(); + + Assert.AreEqual(AnalyzeResult.ExitCode.Success, result.ResultCode); + Assert.AreEqual(0, result.Metadata.Matches.Count); + Assert.AreEqual(2, result.Metadata.UniqueTags.Count); + } + + [TestMethod] + public void SingleVsMultiThread() + { + List testFiles = new(); + + var iterations = 100; + for (var i = 0; i < iterations; i++) + { + var testFileName = Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), + $"SingleVsMultiThread-TestFile-{i}.js"); + File.WriteAllText(testFileName, fourWindowsOneLinux); + testFiles.Add(testFileName); + } + + AnalyzeOptions optionsSingle = new() + { + SourcePath = testFiles, + CustomRulesPath = testRulesPath, + IgnoreDefaultRules = true, + SingleThread = true + }; + + AnalyzeOptions optionsMulti = new() + { + SourcePath = testFiles, + CustomRulesPath = testRulesPath, + IgnoreDefaultRules = true, + SingleThread = false + }; + + AnalyzeCommand commandSingle = new(optionsSingle, factory); + var resultSingle = commandSingle.GetResult(); + + AnalyzeCommand commandMulti = new(optionsMulti, factory); + var resultMulti = commandMulti.GetResult(); + + Assert.AreEqual(AnalyzeResult.ExitCode.Success, resultSingle.ResultCode); + Assert.AreEqual(AnalyzeResult.ExitCode.Success, resultMulti.ResultCode); + Assert.AreEqual(5 * iterations, resultSingle.Metadata.TotalMatchesCount); + Assert.AreEqual(5 * iterations, resultMulti.Metadata.TotalMatchesCount); + Assert.IsTrue(resultSingle.Metadata.Matches.All(x => + resultMulti.Metadata.Matches.Any(y => + y.Tags?.All(z => x.Tags?.All(w => w.Contains(z)) ?? false) ?? false))); + } + + [DataRow(new[] { Severity.Moderate }, 1)] + [DataRow(new[] { Severity.Important }, 4)] + [DataRow(new[] { Severity.Important | Severity.Moderate }, 5)] + [DataTestMethod] + public void SeverityFilters(Severity[] severityFilter, int ActualExpectedNumberOfMatches) + { + AnalyzeOptions options = new() + { + SourcePath = new string[1] { testFilePath }, + CustomRulesPath = testRulesPath, + SeverityFilters = severityFilter, + IgnoreDefaultRules = true + }; + + AnalyzeCommand command = new(options, factory); + var result = command.GetResult(); + + Assert.AreEqual(AnalyzeResult.ExitCode.Success, result.ResultCode); + Assert.AreEqual(ActualExpectedNumberOfMatches, result.Metadata.Matches.Count); + } + + [DataRow(new[] { Confidence.High }, 1)] + [DataRow(new[] { Confidence.Medium }, 4)] + [DataRow(new[] { Confidence.Medium | Confidence.High }, 5)] + [DataTestMethod] + public void ConfidenceFilters(Confidence[] confidenceFilter, int ActualExpectedNumberOfMatches) + { + AnalyzeOptions options = new() + { + SourcePath = new string[1] { testFilePath }, + CustomRulesPath = testRulesPath, + ConfidenceFilters = confidenceFilter, + IgnoreDefaultRules = true + }; + + AnalyzeCommand command = new(options, factory); + var result = command.GetResult(); + + Assert.AreEqual(AnalyzeResult.ExitCode.Success, result.ResultCode); + Assert.AreEqual(ActualExpectedNumberOfMatches, result.Metadata.Matches.Count); + } + + [TestMethod] + public void TagsInBuildFiles() + { + var innerTestFilePath = + $"{Path.GetTempFileName()}.json"; // JSON is considered a build file, it does not have code/comment sections. + File.WriteAllText(innerTestFilePath, fourWindowsOneLinux); + + AnalyzeOptions options = new() + { + SourcePath = new string[1] { innerTestFilePath }, + CustomRulesPath = testRulesPath, + IgnoreDefaultRules = true, + AllowAllTagsInBuildFiles = true + }; + + AnalyzeCommand command = new(options, factory); + var result = command.GetResult(); + + Assert.AreEqual(AnalyzeResult.ExitCode.Success, result.ResultCode); + Assert.AreEqual(5, result.Metadata.Matches.Count); + Assert.AreEqual(2, result.Metadata.UniqueMatchesCount); + + AnalyzeOptions dontAllowAllTagsOptions = new() + { + SourcePath = new string[1] { innerTestFilePath }, + CustomRulesPath = testRulesPath, + IgnoreDefaultRules = true, + AllowAllTagsInBuildFiles = false + }; + + AnalyzeCommand dontAllowAllTagsCommand = new(dontAllowAllTagsOptions); + var dontAllowAllTagsResult = dontAllowAllTagsCommand.GetResult(); + + File.Delete(innerTestFilePath); + + Assert.AreEqual(AnalyzeResult.ExitCode.NoMatches, dontAllowAllTagsResult.ResultCode); + Assert.AreEqual(0, dontAllowAllTagsResult.Metadata.Matches.Count); + Assert.AreEqual(0, dontAllowAllTagsResult.Metadata.Matches.Count); + } + + [DataRow(1, 3)] + [DataRow(2, 5)] + [DataRow(3, 5)] + [DataRow(50, 5)] + [DataRow(0, 0)] + [DataTestMethod] + public void ContextLines(int numLinesContextArgument, int expectedNewLinesInResult) + { + AnalyzeOptions options = new() + { + SourcePath = new string[1] { testFilePath }, + CustomRulesPath = testRulesPath, + IgnoreDefaultRules = true, + ContextLines = numLinesContextArgument + }; + + AnalyzeCommand command = new(options, factory); + var result = command.GetResult(); + + Assert.AreEqual(AnalyzeResult.ExitCode.Success, result.ResultCode); + var linuxResult = result.Metadata.Matches.Where(x => x.RuleId == "AI_TEST_LINUX"); + Assert.AreEqual(expectedNewLinesInResult, linuxResult.First().Excerpt.Count(x => x == '\n')); + } + + [TestMethod] + public void FileMetadata() + { + AnalyzeOptions optionsWithoutMetadata = new() + { + SourcePath = new string[1] { testFilePath }, + CustomRulesPath = testRulesPath, + IgnoreDefaultRules = true, + NoFileMetadata = true + }; + + AnalyzeOptions optionsWithMetadata = new() + { + SourcePath = new string[1] { testFilePath }, + CustomRulesPath = testRulesPath, + IgnoreDefaultRules = true, + NoFileMetadata = false + }; + + AnalyzeCommand commandWithoutMetadata = new(optionsWithoutMetadata, factory); + var resultWithoutMetadata = commandWithoutMetadata.GetResult(); + + AnalyzeCommand commandWithMetadata = new(optionsWithMetadata, factory); + var resultWithMetadata = commandWithMetadata.GetResult(); + + Assert.AreEqual(1, resultWithMetadata.Metadata.TotalFiles); + Assert.AreEqual(0, resultWithoutMetadata.Metadata.TotalFiles); + } + + /// + /// Test that the applies_to parameter allows the specified types + /// + [TestMethod] + public void TestAppliesTo() + { + AnalyzeOptions options = new() + { + SourcePath = new string[1] { testFilePath }, + CustomRulesPath = appliesToTestRulePath, + IgnoreDefaultRules = true + }; + + AnalyzeCommand command = new(options, factory); + var result = command.GetResult(); + Assert.AreEqual(AnalyzeResult.ExitCode.Success, result.ResultCode); + Assert.AreEqual(4, result.Metadata.TotalMatchesCount); + } + + /// + /// Test that the does_not_apply_to parameter excludes the specified types + /// + [TestMethod] + public void TestDoesNotApplyTo() + { + AnalyzeOptions options = new() + { + SourcePath = new string[1] { testFilePath }, + CustomRulesPath = doesNotApplyToTestRulePath, + IgnoreDefaultRules = true + }; + + AnalyzeCommand command = new(options, factory); + var result = command.GetResult(); + Assert.AreEqual(AnalyzeResult.ExitCode.NoMatches, result.ResultCode); + Assert.AreEqual(0, result.Metadata.TotalMatchesCount); + } } \ No newline at end of file diff --git a/AppInspector.Tests/Commands/TestExportTagsCmd.cs b/AppInspector.Tests/Commands/TestExportTagsCmd.cs index 3dac00f..2434aa1 100644 --- a/AppInspector.Tests/Commands/TestExportTagsCmd.cs +++ b/AppInspector.Tests/Commands/TestExportTagsCmd.cs @@ -7,33 +7,15 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.VisualStudio.TestTools.UnitTesting; -namespace AppInspector.Tests.Commands +namespace AppInspector.Tests.Commands; + +[TestClass] +[ExcludeFromCodeCoverage] +public class TestExportTagsCmd { - [TestClass] - [ExcludeFromCodeCoverage] - public class TestExportTagsCmd - { - private string testRulesPath = string.Empty; + private ILoggerFactory factory = new NullLoggerFactory(); - private LogOptions logOptions = new(); - private ILoggerFactory factory = new NullLoggerFactory(); - - [TestInitialize] - public void InitOutput() - { - factory = logOptions.GetLoggerFactory(); - Directory.CreateDirectory(TestHelpers.GetPath(TestHelpers.AppPath.testOutput)); - testRulesPath = Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), "TestRules.json"); - File.WriteAllText(testRulesPath, findWindows); - } - - [ClassCleanup] - public static void CleanUp() - { - Directory.Delete(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), true); - } - - string findWindows = @"[ + private readonly string findWindows = @"[ { ""name"": ""Platform: Microsoft Windows"", ""id"": ""AI_TEST_WINDOWS"", @@ -112,42 +94,59 @@ namespace AppInspector.Tests.Commands } ]"; - [TestMethod] - public void ExportCustom() - { - ExportTagsOptions options = new() - { - IgnoreDefaultRules = true, - CustomRulesPath = testRulesPath - }; - ExportTagsCommand command = new(options, factory); - ExportTagsResult result = command.GetResult(); - Assert.IsTrue(result.TagsList.Contains("Test.Tags.Linux")); - Assert.IsTrue(result.TagsList.Contains("Test.Tags.Windows")); - Assert.AreEqual(2, result.TagsList.Count); - Assert.AreEqual(ExportTagsResult.ExitCode.Success, result.ResultCode); - } + private readonly LogOptions logOptions = new(); + private string testRulesPath = string.Empty; - [TestMethod] - public void ExportDefault() - { - ExportTagsOptions options = new() - { - IgnoreDefaultRules = false - }; - ExportTagsCommand command = new(options, factory); - ExportTagsResult result = command.GetResult(); - Assert.AreEqual(ExportTagsResult.ExitCode.Success, result.ResultCode); - } + [TestInitialize] + public void InitOutput() + { + factory = logOptions.GetLoggerFactory(); + Directory.CreateDirectory(TestHelpers.GetPath(TestHelpers.AppPath.testOutput)); + testRulesPath = Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), "TestRules.json"); + File.WriteAllText(testRulesPath, findWindows); + } - [TestMethod] - public void NoDefaultNoCustomRules() + [ClassCleanup] + public static void CleanUp() + { + Directory.Delete(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), true); + } + + [TestMethod] + public void ExportCustom() + { + ExportTagsOptions options = new() { - ExportTagsOptions options = new() - { - IgnoreDefaultRules = true - }; - Assert.ThrowsException(() => new ExportTagsCommand(options)); - } + IgnoreDefaultRules = true, + CustomRulesPath = testRulesPath + }; + ExportTagsCommand command = new(options, factory); + var result = command.GetResult(); + Assert.IsTrue(result.TagsList.Contains("Test.Tags.Linux")); + Assert.IsTrue(result.TagsList.Contains("Test.Tags.Windows")); + Assert.AreEqual(2, result.TagsList.Count); + Assert.AreEqual(ExportTagsResult.ExitCode.Success, result.ResultCode); + } + + [TestMethod] + public void ExportDefault() + { + ExportTagsOptions options = new() + { + IgnoreDefaultRules = false + }; + ExportTagsCommand command = new(options, factory); + var result = command.GetResult(); + Assert.AreEqual(ExportTagsResult.ExitCode.Success, result.ResultCode); + } + + [TestMethod] + public void NoDefaultNoCustomRules() + { + ExportTagsOptions options = new() + { + IgnoreDefaultRules = true + }; + Assert.ThrowsException(() => new ExportTagsCommand(options)); } } \ No newline at end of file diff --git a/AppInspector.Tests/Commands/TestPackRulesCmd.cs b/AppInspector.Tests/Commands/TestPackRulesCmd.cs index 52e17dd..f5c1144 100644 --- a/AppInspector.Tests/Commands/TestPackRulesCmd.cs +++ b/AppInspector.Tests/Commands/TestPackRulesCmd.cs @@ -4,43 +4,42 @@ using Microsoft.ApplicationInspector.Commands; using Microsoft.ApplicationInspector.Common; using Microsoft.VisualStudio.TestTools.UnitTesting; -namespace AppInspector.Tests.Commands +namespace AppInspector.Tests.Commands; + +[TestClass] +[ExcludeFromCodeCoverage] +public class TestPackRulesCmd { - [TestClass] - [ExcludeFromCodeCoverage] - public class TestPackRulesCmd + [TestInitialize] + public void InitOutput() { - [TestInitialize] - public void InitOutput() - { - Directory.CreateDirectory(TestHelpers.GetPath(TestHelpers.AppPath.testOutput)); - } + Directory.CreateDirectory(TestHelpers.GetPath(TestHelpers.AppPath.testOutput)); + } - [TestCleanup] - public void CleanUp() + [TestCleanup] + public void CleanUp() + { + try { - try - { - Directory.Delete(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), true); - } - catch - { - } + Directory.Delete(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), true); } - - [TestMethod] - public void NoCustomNoEmbeddedRules() + catch { - Assert.ThrowsException(() => new PackRulesCommand(new())); - } - - [TestMethod] - public void PackEmbeddedRules() - { - PackRulesOptions options = new() { PackEmbeddedRules = true }; - PackRulesCommand command = new(options); - PackRulesResult result = command.GetResult(); - Assert.AreEqual(PackRulesResult.ExitCode.Success, result.ResultCode); } } + + [TestMethod] + public void NoCustomNoEmbeddedRules() + { + Assert.ThrowsException(() => new PackRulesCommand(new PackRulesOptions())); + } + + [TestMethod] + public void PackEmbeddedRules() + { + PackRulesOptions options = new() { PackEmbeddedRules = true }; + PackRulesCommand command = new(options); + var result = command.GetResult(); + Assert.AreEqual(PackRulesResult.ExitCode.Success, result.ResultCode); + } } \ No newline at end of file diff --git a/AppInspector.Tests/Commands/TestTagDiffCmd.cs b/AppInspector.Tests/Commands/TestTagDiffCmd.cs index bc1b6f1..dbae127 100644 --- a/AppInspector.Tests/Commands/TestTagDiffCmd.cs +++ b/AppInspector.Tests/Commands/TestTagDiffCmd.cs @@ -8,49 +8,17 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.VisualStudio.TestTools.UnitTesting; -namespace AppInspector.Tests.Commands +namespace AppInspector.Tests.Commands; + +/// +/// Test class for TagDiff Command +/// +[TestClass] +[ExcludeFromCodeCoverage] +public class TestTagDiffCmd { - /// - /// Test class for TagDiff Command - /// - [TestClass] - [ExcludeFromCodeCoverage] - public class TestTagDiffCmd - { - private string testFileFourWindowsOneLinuxPath = string.Empty; - private string testFileFourWindowsOneLinuxCopyPath = string.Empty; - private string testFileFourWindowsNoLinuxPath = string.Empty; - private string testRulesPath = string.Empty; - - private LogOptions logOptions = new(); - private ILoggerFactory loggerFactory = new NullLoggerFactory(); - - [TestInitialize] - public void InitOutput() - { - loggerFactory = logOptions.GetLoggerFactory(); - Directory.CreateDirectory(TestHelpers.GetPath(TestHelpers.AppPath.testOutput)); - testFileFourWindowsOneLinuxPath = Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), "TestFile.js"); - File.WriteAllText(testFileFourWindowsOneLinuxPath, fourWindowsOneLinux); - - testFileFourWindowsOneLinuxCopyPath = Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), "TestFileCopy.js"); - File.WriteAllText(testFileFourWindowsOneLinuxCopyPath, fourWindowsOneLinux); - - testFileFourWindowsNoLinuxPath = Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), "TestFileNoLinux.js"); - File.WriteAllText(testFileFourWindowsNoLinuxPath, fourWindowsNoLinux); - - testRulesPath = Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), "TestRules.json"); - File.WriteAllText(testRulesPath, findWindows); - } - - [ClassCleanup] - public static void CleanUp() - { - Directory.Delete(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), true); - } - - // These simple test rules rules look for the string "windows" and "linux" - string findWindows = @"[ + // These simple test rules rules look for the string "windows" and "linux" + private readonly string findWindows = @"[ { ""name"": ""Platform: Microsoft Windows"", ""id"": ""AI_TEST_WINDOWS"", @@ -90,89 +58,125 @@ namespace AppInspector.Tests.Commands ] } ]"; - // This string contains windows four times and linux once. - string fourWindowsOneLinux = -@"windows + + private readonly string fourWindowsNoLinux = + @"windows +windows +windows +windows +"; + + // This string contains windows four times and linux once. + private readonly string fourWindowsOneLinux = + @"windows windows linux windows windows -"; - string fourWindowsNoLinux = -@"windows -windows -windows -windows "; - [DataRow(TagTestType.Equality, TagDiffResult.ExitCode.TestPassed)] - [DataRow(TagTestType.Inequality, TagDiffResult.ExitCode.TestFailed)] - [TestMethod] - public void Equality(TagTestType tagTestType, TagDiffResult.ExitCode expectedExitCode) + private ILoggerFactory loggerFactory = new NullLoggerFactory(); + + private readonly LogOptions logOptions = new(); + private string testFileFourWindowsNoLinuxPath = string.Empty; + private string testFileFourWindowsOneLinuxCopyPath = string.Empty; + private string testFileFourWindowsOneLinuxPath = string.Empty; + private string testRulesPath = string.Empty; + + [TestInitialize] + public void InitOutput() + { + loggerFactory = logOptions.GetLoggerFactory(); + Directory.CreateDirectory(TestHelpers.GetPath(TestHelpers.AppPath.testOutput)); + testFileFourWindowsOneLinuxPath = + Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), "TestFile.js"); + File.WriteAllText(testFileFourWindowsOneLinuxPath, fourWindowsOneLinux); + + testFileFourWindowsOneLinuxCopyPath = + Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), "TestFileCopy.js"); + File.WriteAllText(testFileFourWindowsOneLinuxCopyPath, fourWindowsOneLinux); + + testFileFourWindowsNoLinuxPath = + Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), "TestFileNoLinux.js"); + File.WriteAllText(testFileFourWindowsNoLinuxPath, fourWindowsNoLinux); + + testRulesPath = Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), "TestRules.json"); + File.WriteAllText(testRulesPath, findWindows); + } + + [ClassCleanup] + public static void CleanUp() + { + Directory.Delete(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), true); + } + + [DataRow(TagTestType.Equality, TagDiffResult.ExitCode.TestPassed)] + [DataRow(TagTestType.Inequality, TagDiffResult.ExitCode.TestFailed)] + [TestMethod] + public void Equality(TagTestType tagTestType, TagDiffResult.ExitCode expectedExitCode) + { + TagDiffOptions options = new() { - TagDiffOptions options = new() - { - SourcePath1 = new[] { testFileFourWindowsOneLinuxPath }, - SourcePath2 = new[] { testFileFourWindowsOneLinuxCopyPath }, - FilePathExclusions = Array.Empty(), //allow source under unittest path - IgnoreDefaultRules = true, - TestType = tagTestType, - CustomRulesPath = testRulesPath - }; + SourcePath1 = new[] { testFileFourWindowsOneLinuxPath }, + SourcePath2 = new[] { testFileFourWindowsOneLinuxCopyPath }, + FilePathExclusions = Array.Empty(), //allow source under unittest path + IgnoreDefaultRules = true, + TestType = tagTestType, + CustomRulesPath = testRulesPath + }; - TagDiffCommand command = new(options, loggerFactory); - TagDiffResult result = command.GetResult(); + TagDiffCommand command = new(options, loggerFactory); + var result = command.GetResult(); - Assert.AreEqual(expectedExitCode, result.ResultCode); - } + Assert.AreEqual(expectedExitCode, result.ResultCode); + } - [DataRow(TagTestType.Equality, TagDiffResult.ExitCode.TestFailed)] - [DataRow(TagTestType.Inequality, TagDiffResult.ExitCode.TestPassed)] - [TestMethod] - public void Inequality(TagTestType tagTestType, TagDiffResult.ExitCode expectedExitCode) + [DataRow(TagTestType.Equality, TagDiffResult.ExitCode.TestFailed)] + [DataRow(TagTestType.Inequality, TagDiffResult.ExitCode.TestPassed)] + [TestMethod] + public void Inequality(TagTestType tagTestType, TagDiffResult.ExitCode expectedExitCode) + { + TagDiffOptions options = new() { - TagDiffOptions options = new() - { - SourcePath1 = new[] { testFileFourWindowsOneLinuxPath }, - SourcePath2 = new[] { testFileFourWindowsNoLinuxPath }, - FilePathExclusions = Array.Empty(), //allow source under unittest path - IgnoreDefaultRules = true, - TestType = tagTestType, - CustomRulesPath = testRulesPath - }; + SourcePath1 = new[] { testFileFourWindowsOneLinuxPath }, + SourcePath2 = new[] { testFileFourWindowsNoLinuxPath }, + FilePathExclusions = Array.Empty(), //allow source under unittest path + IgnoreDefaultRules = true, + TestType = tagTestType, + CustomRulesPath = testRulesPath + }; - TagDiffCommand command = new(options, loggerFactory); - TagDiffResult result = command.GetResult(); + TagDiffCommand command = new(options, loggerFactory); + var result = command.GetResult(); - Assert.AreEqual(expectedExitCode, result.ResultCode); - } + Assert.AreEqual(expectedExitCode, result.ResultCode); + } - [TestMethod] - public void InvalidSourcePath_Fail() + [TestMethod] + public void InvalidSourcePath_Fail() + { + TagDiffOptions options = new() { - TagDiffOptions options = new() - { - SourcePath1 = new[] { $"{testFileFourWindowsOneLinuxPath}.not.a.path" }, - SourcePath2 = new[] { testFileFourWindowsOneLinuxPath }, - FilePathExclusions = Array.Empty(), //allow source under unittest path - }; - var cmd = new TagDiffCommand(options, loggerFactory); - Assert.ThrowsException(() => cmd.GetResult()); - } + SourcePath1 = new[] { $"{testFileFourWindowsOneLinuxPath}.not.a.path" }, + SourcePath2 = new[] { testFileFourWindowsOneLinuxPath }, + FilePathExclusions = Array.Empty() //allow source under unittest path + }; + var cmd = new TagDiffCommand(options, loggerFactory); + Assert.ThrowsException(() => cmd.GetResult()); + } - [TestMethod] - public void NoDefaultNoCustomRules_Fail() + [TestMethod] + public void NoDefaultNoCustomRules_Fail() + { + TagDiffOptions options = new() { - TagDiffOptions options = new() - { - SourcePath1 = new[] { testFileFourWindowsOneLinuxPath }, - SourcePath2 = new[] { testFileFourWindowsOneLinuxCopyPath }, - FilePathExclusions = Array.Empty(), //allow source under unittest path - IgnoreDefaultRules = true - }; + SourcePath1 = new[] { testFileFourWindowsOneLinuxPath }, + SourcePath2 = new[] { testFileFourWindowsOneLinuxCopyPath }, + FilePathExclusions = Array.Empty(), //allow source under unittest path + IgnoreDefaultRules = true + }; - TagDiffCommand command = new TagDiffCommand(options, loggerFactory); - Assert.ThrowsException(() => command.GetResult()); - } + var command = new TagDiffCommand(options, loggerFactory); + Assert.ThrowsException(() => command.GetResult()); } } \ No newline at end of file diff --git a/AppInspector.Tests/Commands/TestVerifyRulesCmd.cs b/AppInspector.Tests/Commands/TestVerifyRulesCmd.cs index 4626c51..10e75e1 100644 --- a/AppInspector.Tests/Commands/TestVerifyRulesCmd.cs +++ b/AppInspector.Tests/Commands/TestVerifyRulesCmd.cs @@ -8,34 +8,247 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.VisualStudio.TestTools.UnitTesting; -namespace AppInspector.Tests.Commands +namespace AppInspector.Tests.Commands; + +// TODO: This does not intentionally try to make the OAT rule maker fail +// The OAT rules are being validated but there aren't test cases that intentionally try to break it. +[TestClass] +[ExcludeFromCodeCoverage] +public class TestVerifyRulesCmd { - // TODO: This does not intentionally try to make the OAT rule maker fail - // The OAT rules are being validated but there aren't test cases that intentionally try to break it. - [TestClass] - [ExcludeFromCodeCoverage] - public class TestVerifyRulesCmd - { - private string _validRulesPath = string.Empty; - private LogOptions _logOptions = new(); - private ILoggerFactory _factory = new NullLoggerFactory(); - - [TestInitialize] - public void InitOutput() - { - _factory = _logOptions.GetLoggerFactory(); - Directory.CreateDirectory(TestHelpers.GetPath(TestHelpers.AppPath.testOutput)); - _validRulesPath = Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), "TestRules.json"); - File.WriteAllText(_validRulesPath, _validRules); - } + // FileRegexes if specified must be valid + private readonly string _invalidFileRegexes = @"[ +{ + ""name"": ""Platform: Microsoft Windows"", + ""id"": ""AI_TEST_WINDOWS"", + ""description"": ""This rule checks for the string 'windows'"", + ""tags"": [ + ""Test.Tags.Windows"" + ], + ""applies_to_file_regex"": [ ""$(^""], + ""severity"": ""Important"", + ""patterns"": [ + { + ""confidence"": ""Medium"", + ""modifiers"": [ + ""i"" + ], + ""pattern"": ""windows"", + ""type"": ""String"", + } + ] +} +]"; - [ClassCleanup] - public static void CleanUp() - { - Directory.Delete(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), true); - } + // Rules are a List so they must be contained in [] + private readonly string _invalidJsonValidRule = @" +{ + ""name"": ""Platform: Microsoft Windows"", + ""id"": ""AI_TEST_WINDOWS"", + ""description"": ""This rule checks for the string 'windows'"", + ""tags"": [ + ""Test.Tags.Windows"" + ], + ""severity"": ""Important"", + ""patterns"": [ + { + ""confidence"": ""Medium"", + ""modifiers"": [ + ""i"" + ], + ""pattern"": ""windows"", + ""type"": ""String"", + } + ] +}"; - readonly string _validRules = @"[ + // Languages if specified must be known + private readonly string _knownLanguages = @"[ +{ + ""name"": ""Platform: Microsoft Windows"", + ""id"": ""AI_TEST_WINDOWS"", + ""description"": ""This rule checks for the string 'windows'"", + ""tags"": [ + ""Test.Tags.Windows"" + ], + ""applies_to"": [ ""malboge""], + ""severity"": ""Important"", + ""patterns"": [ + { + ""confidence"": ""Medium"", + ""modifiers"": [ + ""i"" + ], + ""pattern"": ""windows"", + ""type"": ""String"", + } + ] +} +]"; + + // MustMatch if specified must be matched + private readonly string _mustMatchRule = @"[ +{ + ""name"": ""Platform: Microsoft Windows"", + ""id"": ""AI_TEST_WINDOWS"", + ""description"": ""This rule checks for the string 'windows'"", + ""tags"": [ + ""Test.Tags.Windows"" + ], + ""applies_to"": [ ""csharp""], + ""severity"": ""Important"", + ""patterns"": [ + { + ""confidence"": ""Medium"", + ""modifiers"": [ + ""i"" + ], + ""pattern"": ""windows"", + ""type"": ""String"", + } + ], + ""must-match"" : [ ""windows 2000""] +} +]"; + + // MustMatch if specified must not fail to match + private readonly string _mustMatchRuleFail = @"[ +{ + ""name"": ""Platform: Microsoft Windows"", + ""id"": ""AI_TEST_WINDOWS"", + ""description"": ""This rule checks for the string 'windows'"", + ""tags"": [ + ""Test.Tags.Windows"" + ], + ""applies_to"": [ ""csharp""], + ""severity"": ""Important"", + ""patterns"": [ + { + ""confidence"": ""Medium"", + ""modifiers"": [ + ""i"" + ], + ""pattern"": ""windows"", + ""type"": ""String"", + } + ], + ""must-match"" : [ ""wimdoos""] +} +]"; + + // MustNotMatch if specified must not be matched + private readonly string _mustNotMatchRule = @"[ +{ + ""name"": ""Platform: Microsoft Windows"", + ""id"": ""AI_TEST_WINDOWS"", + ""description"": ""This rule checks for the string 'windows'"", + ""tags"": [ + ""Test.Tags.Windows"" + ], + ""applies_to"": [ ""csharp""], + ""severity"": ""Important"", + ""patterns"": [ + { + ""confidence"": ""Medium"", + ""modifiers"": [ + ""i"" + ], + ""pattern"": ""windows"", + ""type"": ""String"", + } + ], + ""must-not-match"" : [ ""linux""] +} +]"; + + // MustNotMatch if specified must not fail to not be matched + private readonly string _mustNotMatchRuleFail = @"[ +{ + ""name"": ""Platform: Microsoft Windows"", + ""id"": ""AI_TEST_WINDOWS"", + ""description"": ""This rule checks for the string 'windows'"", + ""tags"": [ + ""Test.Tags.Windows"" + ], + ""applies_to"": [ ""csharp""], + ""severity"": ""Important"", + ""patterns"": [ + { + ""confidence"": ""Medium"", + ""modifiers"": [ + ""i"" + ], + ""pattern"": ""windows"", + ""type"": ""String"", + } + ], + ""must-not-match"" : [ ""windows""] +} +]"; + + // Two rules may not have the same id + private readonly string _sameId = @"[ +{ + ""name"": ""Platform: Microsoft Windows"", + ""id"": ""AI_TEST_WINDOWS"", + ""description"": ""This rule checks for the string 'windows'"", + ""tags"": [ + ""Test.Tags.Windows"" + ], + ""severity"": ""Important"", + ""patterns"": [ + { + ""confidence"": ""Medium"", + ""modifiers"": [ + ""i"" + ], + ""pattern"": ""windows"", + ""type"": ""String"", + } + ] +}, +{ + ""name"": ""Platform: Linux"", + ""id"": ""AI_TEST_WINDOWS"", + ""description"": ""This rule checks for the string 'linux'"", + ""tags"": [ + ""Test.Tags.Linux"" + ], + ""severity"": ""Moderate"", + ""patterns"": [ + { + ""confidence"": ""High"", + ""modifiers"": [ + ""i"" + ], + ""pattern"": ""linux"", + ""type"": ""String"", + } + ] +} +]"; + + // Rules must contain an id + private readonly string _validJsonInvalidRuleNoId = @"[{ + ""name"": ""Platform: Microsoft Windows"", + ""description"": ""This rule checks for the string 'windows'"", + ""tags"": [ + ""Test.Tags.Windows"" + ], + ""severity"": ""Important"", + ""patterns"": [ + { + ""confidence"": ""Medium"", + ""modifiers"": [ + ""i"" + ], + ""pattern"": ""windows"", + ""type"": ""String"", + } + ] +}]"; + + private readonly string _validRules = @"[ { ""name"": ""Platform: Microsoft Windows"", ""id"": ""AI_TEST_WINDOWS"", @@ -74,414 +287,205 @@ namespace AppInspector.Tests.Commands } ] } -]"; - // Rules are a List so they must be contained in [] - readonly string _invalidJsonValidRule = @" -{ - ""name"": ""Platform: Microsoft Windows"", - ""id"": ""AI_TEST_WINDOWS"", - ""description"": ""This rule checks for the string 'windows'"", - ""tags"": [ - ""Test.Tags.Windows"" - ], - ""severity"": ""Important"", - ""patterns"": [ - { - ""confidence"": ""Medium"", - ""modifiers"": [ - ""i"" - ], - ""pattern"": ""windows"", - ""type"": ""String"", - } - ] -}"; - // Rules must contain an id - readonly string _validJsonInvalidRuleNoId = @"[{ - ""name"": ""Platform: Microsoft Windows"", - ""description"": ""This rule checks for the string 'windows'"", - ""tags"": [ - ""Test.Tags.Windows"" - ], - ""severity"": ""Important"", - ""patterns"": [ - { - ""confidence"": ""Medium"", - ""modifiers"": [ - ""i"" - ], - ""pattern"": ""windows"", - ""type"": ""String"", - } - ] -}]"; - // Two rules may not have the same id - readonly string _sameId = @"[ -{ - ""name"": ""Platform: Microsoft Windows"", - ""id"": ""AI_TEST_WINDOWS"", - ""description"": ""This rule checks for the string 'windows'"", - ""tags"": [ - ""Test.Tags.Windows"" - ], - ""severity"": ""Important"", - ""patterns"": [ - { - ""confidence"": ""Medium"", - ""modifiers"": [ - ""i"" - ], - ""pattern"": ""windows"", - ""type"": ""String"", - } - ] -}, -{ - ""name"": ""Platform: Linux"", - ""id"": ""AI_TEST_WINDOWS"", - ""description"": ""This rule checks for the string 'linux'"", - ""tags"": [ - ""Test.Tags.Linux"" - ], - ""severity"": ""Moderate"", - ""patterns"": [ - { - ""confidence"": ""High"", - ""modifiers"": [ - ""i"" - ], - ""pattern"": ""linux"", - ""type"": ""String"", - } - ] -} ]"; - // Languages if specified must be known - readonly string _knownLanguages = @"[ -{ - ""name"": ""Platform: Microsoft Windows"", - ""id"": ""AI_TEST_WINDOWS"", - ""description"": ""This rule checks for the string 'windows'"", - ""tags"": [ - ""Test.Tags.Windows"" - ], - ""applies_to"": [ ""malboge""], - ""severity"": ""Important"", - ""patterns"": [ - { - ""confidence"": ""Medium"", - ""modifiers"": [ - ""i"" - ], - ""pattern"": ""windows"", - ""type"": ""String"", - } - ] -} -]"; - - // MustMatch if specified must be matched - readonly string _mustMatchRule = @"[ -{ - ""name"": ""Platform: Microsoft Windows"", - ""id"": ""AI_TEST_WINDOWS"", - ""description"": ""This rule checks for the string 'windows'"", - ""tags"": [ - ""Test.Tags.Windows"" - ], - ""applies_to"": [ ""csharp""], - ""severity"": ""Important"", - ""patterns"": [ - { - ""confidence"": ""Medium"", - ""modifiers"": [ - ""i"" - ], - ""pattern"": ""windows"", - ""type"": ""String"", - } - ], - ""must-match"" : [ ""windows 2000""] -} -]"; - // MustMatch if specified must not fail to match - readonly string _mustMatchRuleFail = @"[ -{ - ""name"": ""Platform: Microsoft Windows"", - ""id"": ""AI_TEST_WINDOWS"", - ""description"": ""This rule checks for the string 'windows'"", - ""tags"": [ - ""Test.Tags.Windows"" - ], - ""applies_to"": [ ""csharp""], - ""severity"": ""Important"", - ""patterns"": [ - { - ""confidence"": ""Medium"", - ""modifiers"": [ - ""i"" - ], - ""pattern"": ""windows"", - ""type"": ""String"", - } - ], - ""must-match"" : [ ""wimdoos""] -} -]"; - - // MustNotMatch if specified must not be matched - readonly string _mustNotMatchRule = @"[ -{ - ""name"": ""Platform: Microsoft Windows"", - ""id"": ""AI_TEST_WINDOWS"", - ""description"": ""This rule checks for the string 'windows'"", - ""tags"": [ - ""Test.Tags.Windows"" - ], - ""applies_to"": [ ""csharp""], - ""severity"": ""Important"", - ""patterns"": [ - { - ""confidence"": ""Medium"", - ""modifiers"": [ - ""i"" - ], - ""pattern"": ""windows"", - ""type"": ""String"", - } - ], - ""must-not-match"" : [ ""linux""] -} -]"; - - // MustNotMatch if specified must not fail to not be matched - readonly string _mustNotMatchRuleFail = @"[ -{ - ""name"": ""Platform: Microsoft Windows"", - ""id"": ""AI_TEST_WINDOWS"", - ""description"": ""This rule checks for the string 'windows'"", - ""tags"": [ - ""Test.Tags.Windows"" - ], - ""applies_to"": [ ""csharp""], - ""severity"": ""Important"", - ""patterns"": [ - { - ""confidence"": ""Medium"", - ""modifiers"": [ - ""i"" - ], - ""pattern"": ""windows"", - ""type"": ""String"", - } - ], - ""must-not-match"" : [ ""windows""] -} -]"; + private ILoggerFactory _factory = new NullLoggerFactory(); + private readonly LogOptions _logOptions = new(); + private string _validRulesPath = string.Empty; - // FileRegexes if specified must be valid - readonly string _invalidFileRegexes = @"[ -{ - ""name"": ""Platform: Microsoft Windows"", - ""id"": ""AI_TEST_WINDOWS"", - ""description"": ""This rule checks for the string 'windows'"", - ""tags"": [ - ""Test.Tags.Windows"" - ], - ""applies_to_file_regex"": [ ""$(^""], - ""severity"": ""Important"", - ""patterns"": [ - { - ""confidence"": ""Medium"", - ""modifiers"": [ - ""i"" - ], - ""pattern"": ""windows"", - ""type"": ""String"", - } - ] -} -]"; - /// - /// Ensure an exception is thrown if you don't specify any rules to verify - /// - [TestMethod] - public void NoDefaultNoCustomRules() + [TestInitialize] + public void InitOutput() + { + _factory = _logOptions.GetLoggerFactory(); + Directory.CreateDirectory(TestHelpers.GetPath(TestHelpers.AppPath.testOutput)); + _validRulesPath = Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), "TestRules.json"); + File.WriteAllText(_validRulesPath, _validRules); + } + + [ClassCleanup] + public static void CleanUp() + { + Directory.Delete(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), true); + } + + /// + /// Ensure an exception is thrown if you don't specify any rules to verify + /// + [TestMethod] + public void NoDefaultNoCustomRules() + { + Assert.ThrowsException(() => new VerifyRulesCommand(new VerifyRulesOptions())); + } + + [TestMethod] + public void CustomRules() + { + VerifyRulesOptions options = new() { - Assert.ThrowsException(() => new VerifyRulesCommand(new())); - } + CustomRulesPath = _validRulesPath + }; - [TestMethod] - public void CustomRules() + VerifyRulesCommand command = new(options, _factory); + var result = command.GetResult(); + + Assert.AreEqual(VerifyRulesResult.ExitCode.Verified, result.ResultCode); + } + + [TestMethod] + public void UnclosedJson() + { + var path = Path.GetTempFileName(); + File.WriteAllText(path, _invalidJsonValidRule); + VerifyRulesOptions options = new() { - VerifyRulesOptions options = new() - { - CustomRulesPath = _validRulesPath, - }; + CustomRulesPath = path + }; - VerifyRulesCommand command = new(options, _factory); - VerifyRulesResult result = command.GetResult(); + VerifyRulesCommand command = new(options, _factory); + var result = command.GetResult(); + File.Delete(path); + Assert.AreEqual(VerifyRulesResult.ExitCode.CriticalError, result.ResultCode); + } - Assert.AreEqual(VerifyRulesResult.ExitCode.Verified, result.ResultCode); - } - - [TestMethod] - public void UnclosedJson() + [TestMethod] + public void NullId() + { + var set = new RuleSet(); + set.AddString(_validJsonInvalidRuleNoId, "NoIdTest"); + RulesVerifierOptions options = new() { - string path = Path.GetTempFileName(); - File.WriteAllText(path, _invalidJsonValidRule); - VerifyRulesOptions options = new() - { - CustomRulesPath = path, - }; + LoggerFactory = _factory + }; + var rulesVerifier = new RulesVerifier(options); + Assert.IsFalse(rulesVerifier.Verify(set).Verified); + } - VerifyRulesCommand command = new(options, _factory); - VerifyRulesResult result = command.GetResult(); - File.Delete(path); - Assert.AreEqual(VerifyRulesResult.ExitCode.CriticalError, result.ResultCode); - } - - [TestMethod] - public void NullId() + [TestMethod] + public void DuplicateId() + { + var path = Path.GetTempFileName(); + File.WriteAllText(path, _sameId); + VerifyRulesOptions options = new() { - RuleSet set = new RuleSet(); - set.AddString(_validJsonInvalidRuleNoId, "NoIdTest"); - RulesVerifierOptions options = new() - { - LoggerFactory = _factory - }; - RulesVerifier rulesVerifier = new RulesVerifier(options); - Assert.IsFalse(rulesVerifier.Verify(set).Verified); - } + CustomRulesPath = path + }; - [TestMethod] - public void DuplicateId() + VerifyRulesCommand command = new(options, _factory); + var result = command.GetResult(); + Assert.AreEqual(VerifyRulesResult.ExitCode.NotVerified, result.ResultCode); + File.Delete(path); + } + + [TestMethod] + public void DuplicateIdCheckDisabled() + { + var path = Path.GetTempFileName(); + File.WriteAllText(path, _sameId); + VerifyRulesOptions options = new() { - string path = Path.GetTempFileName(); - File.WriteAllText(path, _sameId); - VerifyRulesOptions options = new() - { - CustomRulesPath = path, - }; + CustomRulesPath = path, + DisableRequireUniqueIds = true + }; - VerifyRulesCommand command = new(options, _factory); - VerifyRulesResult result = command.GetResult(); - Assert.AreEqual(VerifyRulesResult.ExitCode.NotVerified, result.ResultCode); - File.Delete(path); - } - - [TestMethod] - public void DuplicateIdCheckDisabled() + VerifyRulesCommand command = new(options, _factory); + var result = command.GetResult(); + Assert.AreEqual(VerifyRulesResult.ExitCode.Verified, result.ResultCode); + File.Delete(path); + } + + [TestMethod] + public void InvalidRegex() + { + var path = Path.GetTempFileName(); + File.WriteAllText(path, _invalidFileRegexes); + VerifyRulesOptions options = new() { - string path = Path.GetTempFileName(); - File.WriteAllText(path, _sameId); - VerifyRulesOptions options = new() - { - CustomRulesPath = path, - DisableRequireUniqueIds = true - }; + CustomRulesPath = path + }; - VerifyRulesCommand command = new(options, _factory); - VerifyRulesResult result = command.GetResult(); - Assert.AreEqual(VerifyRulesResult.ExitCode.Verified, result.ResultCode); - File.Delete(path); - } + VerifyRulesCommand command = new(options, _factory); + var result = command.GetResult(); + File.Delete(path); + Assert.AreEqual(VerifyRulesResult.ExitCode.NotVerified, result.ResultCode); + } - [TestMethod] - public void InvalidRegex() + [TestMethod] + public void UnknownLanguage() + { + var path = Path.GetTempFileName(); + File.WriteAllText(path, _knownLanguages); + VerifyRulesOptions options = new() { - string path = Path.GetTempFileName(); - File.WriteAllText(path, _invalidFileRegexes); - VerifyRulesOptions options = new() - { - CustomRulesPath = path, - }; + CustomRulesPath = path + }; - VerifyRulesCommand command = new(options, _factory); - VerifyRulesResult result = command.GetResult(); - File.Delete(path); - Assert.AreEqual(VerifyRulesResult.ExitCode.NotVerified, result.ResultCode); - } + VerifyRulesCommand command = new(options, _factory); + var result = command.GetResult(); + File.Delete(path); + Assert.AreEqual(VerifyRulesResult.ExitCode.NotVerified, result.ResultCode); + } - [TestMethod] - public void UnknownLanguage() + [TestMethod] + public void MustMatch() + { + var path = Path.GetTempFileName(); + File.WriteAllText(path, _mustMatchRule); + VerifyRulesOptions options = new() { - string path = Path.GetTempFileName(); - File.WriteAllText(path, _knownLanguages); - VerifyRulesOptions options = new() - { - CustomRulesPath = path, - }; + CustomRulesPath = path + }; - VerifyRulesCommand command = new(options, _factory); - VerifyRulesResult result = command.GetResult(); - File.Delete(path); - Assert.AreEqual(VerifyRulesResult.ExitCode.NotVerified, result.ResultCode); - } - - [TestMethod] - public void MustMatch() + VerifyRulesCommand command = new(options, _factory); + var result = command.GetResult(); + File.Delete(path); + Assert.AreEqual(VerifyRulesResult.ExitCode.Verified, result.ResultCode); + } + + [TestMethod] + public void MustMatchDetectIncorrect() + { + var path = Path.GetTempFileName(); + File.WriteAllText(path, _mustMatchRuleFail); + VerifyRulesOptions options = new() { - string path = Path.GetTempFileName(); - File.WriteAllText(path, _mustMatchRule); - VerifyRulesOptions options = new() - { - CustomRulesPath = path, - }; + CustomRulesPath = path + }; - VerifyRulesCommand command = new(options, _factory); - VerifyRulesResult result = command.GetResult(); - File.Delete(path); - Assert.AreEqual(VerifyRulesResult.ExitCode.Verified, result.ResultCode); - } - - [TestMethod] - public void MustMatchDetectIncorrect() + VerifyRulesCommand command = new(options, _factory); + var result = command.GetResult(); + File.Delete(path); + Assert.AreEqual(VerifyRulesResult.ExitCode.NotVerified, result.ResultCode); + } + + [TestMethod] + public void MustNotMatch() + { + var path = Path.GetTempFileName(); + File.WriteAllText(path, _mustNotMatchRule); + VerifyRulesOptions options = new() { - string path = Path.GetTempFileName(); - File.WriteAllText(path, _mustMatchRuleFail); - VerifyRulesOptions options = new() - { - CustomRulesPath = path, - }; + CustomRulesPath = path + }; - VerifyRulesCommand command = new(options, _factory); - VerifyRulesResult result = command.GetResult(); - File.Delete(path); - Assert.AreEqual(VerifyRulesResult.ExitCode.NotVerified, result.ResultCode); - } - - [TestMethod] - public void MustNotMatch() + VerifyRulesCommand command = new(options, _factory); + var result = command.GetResult(); + File.Delete(path); + Assert.AreEqual(VerifyRulesResult.ExitCode.Verified, result.ResultCode); + } + + [TestMethod] + public void MustNotMatchDetectIncorrect() + { + var path = Path.GetTempFileName(); + File.WriteAllText(path, _mustNotMatchRuleFail); + VerifyRulesOptions options = new() { - string path = Path.GetTempFileName(); - File.WriteAllText(path, _mustNotMatchRule); - VerifyRulesOptions options = new() - { - CustomRulesPath = path, - }; + CustomRulesPath = path + }; - VerifyRulesCommand command = new(options, _factory); - VerifyRulesResult result = command.GetResult(); - File.Delete(path); - Assert.AreEqual(VerifyRulesResult.ExitCode.Verified, result.ResultCode); - } - - [TestMethod] - public void MustNotMatchDetectIncorrect() - { - string path = Path.GetTempFileName(); - File.WriteAllText(path, _mustNotMatchRuleFail); - VerifyRulesOptions options = new() - { - CustomRulesPath = path, - }; - - VerifyRulesCommand command = new(options, _factory); - VerifyRulesResult result = command.GetResult(); - File.Delete(path); - Assert.AreEqual(VerifyRulesResult.ExitCode.NotVerified, result.ResultCode); - } + VerifyRulesCommand command = new(options, _factory); + var result = command.GetResult(); + File.Delete(path); + Assert.AreEqual(VerifyRulesResult.ExitCode.NotVerified, result.ResultCode); } } \ No newline at end of file diff --git a/AppInspector.Tests/DefaultRules/TestDefaultRules.cs b/AppInspector.Tests/DefaultRules/TestDefaultRules.cs index 59c1584..66127f2 100644 --- a/AppInspector.Tests/DefaultRules/TestDefaultRules.cs +++ b/AppInspector.Tests/DefaultRules/TestDefaultRules.cs @@ -1,5 +1,4 @@ -using System; -using System.Diagnostics.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; using System.Linq; using Microsoft.ApplicationInspector.Commands; using Microsoft.ApplicationInspector.Logging; @@ -8,79 +7,67 @@ using Microsoft.Extensions.Logging; using Microsoft.VisualStudio.TestTools.UnitTesting; using Serilog.Events; -namespace AppInspector.Tests.DefaultRules +namespace AppInspector.Tests.DefaultRules; + +/// +/// Tests for the default set of rules which are embedded in the executable. +/// +[TestClass] +[ExcludeFromCodeCoverage] +public class TestDefaultRules { - /// - /// Tests for the default set of rules which are embedded in the executable. - /// - [TestClass] - [ExcludeFromCodeCoverage] - public class TestDefaultRules + // This test ensures that the rules that are bundled with Application Inspector are valid. + [TestMethod] + public void VerifyDefaultRulesAreValid() { - // This test ensures that the rules that are bundled with Application Inspector are valid. - [TestMethod] - public void VerifyDefaultRules() + VerifyRulesOptions options = new() { - VerifyRulesOptions options = new() - { - VerifyDefaultRules = true, - }; - var loggerFactory = new LogOptions() {ConsoleVerbosityLevel = LogEventLevel.Debug}.GetLoggerFactory(); - var logger = loggerFactory.CreateLogger("Tests"); - VerifyRulesCommand command = new(options, loggerFactory); - VerifyRulesResult result = command.GetResult(); - - if (result.Unverified.Any()) - { - logger.Log(LogLevel.Error, "{0} of {1} rules failed verification. Errors are as follows:", result.Unverified.Count(), result.RuleStatusList.Count); - } - else - { - logger.Log(LogLevel.Information, "All {0} rules passed validation.", result.RuleStatusList.Count); - } + VerifyDefaultRules = true + }; + var loggerFactory = new LogOptions { ConsoleVerbosityLevel = LogEventLevel.Debug }.GetLoggerFactory(); + var logger = loggerFactory.CreateLogger("Tests"); + VerifyRulesCommand command = new(options, loggerFactory); + var result = command.GetResult(); - foreach (var unverified in result.Unverified) - { - logger.Log(LogLevel.Error, "Failed to validate {0}",unverified.RulesId); - foreach (var error in unverified.Errors) - { - logger.Log(LogLevel.Error, error); - } + if (result.Unverified.Any()) + logger.Log(LogLevel.Error, "{0} of {1} rules failed verification. Errors are as follows:", + result.Unverified.Count(), result.RuleStatusList.Count); + else + logger.Log(LogLevel.Information, "All {0} rules passed validation.", result.RuleStatusList.Count); - foreach (var oatError in unverified.OatIssues) - { - logger.Log(LogLevel.Error, oatError.Description); - } - } - - logger.Log(LogLevel.Information, "{0} of {1} rules have positive self tests.",result.RuleStatusList.Count(x => x.HasPositiveSelfTests),result.RuleStatusList.Count); - logger.Log(LogLevel.Information, "{0} of {1} rules have negative self tests.",result.RuleStatusList.Count(x => x.HasNegativeSelfTests),result.RuleStatusList.Count); + foreach (var unverified in result.Unverified) + { + logger.Log(LogLevel.Error, "Failed to validate {0}", unverified.RulesId); + foreach (var error in unverified.Errors) logger.Log(LogLevel.Error, error); - foreach (var rule in result.RuleStatusList.Where(x => !x.HasPositiveSelfTests)) - { - logger.Log(LogLevel.Warning, "Rule {0} does not have any positive test cases", rule.RulesId); - } - foreach (var rule in result.RuleStatusList.Where(x => !x.HasNegativeSelfTests)) - { - logger.Log(LogLevel.Warning, "Rule {0} does not have any negative test cases", rule.RulesId); - } - - Assert.AreEqual(VerifyRulesResult.ExitCode.Verified, result.ResultCode); - Assert.AreNotEqual(0, result.RuleStatusList.Count); + foreach (var oatError in unverified.OatIssues) logger.Log(LogLevel.Error, oatError.Description); } - [TestMethod] - public void VerifyNonZeroDefaultRules() - { - RuleSet set = RuleSetUtils.GetDefaultRuleSet(); - Assert.IsTrue(set.Any()); + logger.Log(LogLevel.Information, "{0} of {1} rules have positive self tests.", + result.RuleStatusList.Count(x => x.HasPositiveSelfTests), result.RuleStatusList.Count); + logger.Log(LogLevel.Information, "{0} of {1} rules have negative self tests.", + result.RuleStatusList.Count(x => x.HasNegativeSelfTests), result.RuleStatusList.Count); - RulesVerifier verifier = new(new RulesVerifierOptions()); - RulesVerifierResult result = verifier.Verify(set); - - Assert.IsTrue(result.Verified); - Assert.AreNotEqual(0, result.RuleStatuses.Count); - Assert.AreNotEqual(0, result.CompiledRuleSet.GetAppInspectorRules().Count()); - } + foreach (var rule in result.RuleStatusList.Where(x => !x.HasPositiveSelfTests)) + logger.Log(LogLevel.Warning, "Rule {0} does not have any positive test cases", rule.RulesId); + foreach (var rule in result.RuleStatusList.Where(x => !x.HasNegativeSelfTests)) + logger.Log(LogLevel.Warning, "Rule {0} does not have any negative test cases", rule.RulesId); + + Assert.AreEqual(VerifyRulesResult.ExitCode.Verified, result.ResultCode); + Assert.AreNotEqual(0, result.RuleStatusList.Count); } -} + + [TestMethod] + public void VerifyNonZeroDefaultRules() + { + var set = RuleSetUtils.GetDefaultRuleSet(); + Assert.IsTrue(set.Any()); + + RulesVerifier verifier = new(new RulesVerifierOptions()); + var result = verifier.Verify(set); + + Assert.IsTrue(result.Verified); + Assert.AreNotEqual(0, result.RuleStatuses.Count); + Assert.AreNotEqual(0, result.CompiledRuleSet.GetAppInspectorRules().Count()); + } +} \ No newline at end of file diff --git a/AppInspector.Tests/Languages/LanguagesTests.cs b/AppInspector.Tests/Languages/LanguagesTests.cs index 8026aa9..d7835d9 100644 --- a/AppInspector.Tests/Languages/LanguagesTests.cs +++ b/AppInspector.Tests/Languages/LanguagesTests.cs @@ -1,6 +1,5 @@ using System.Diagnostics.CodeAnalysis; using System.IO; - using Microsoft.ApplicationInspector.Logging; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; @@ -8,13 +7,13 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Newtonsoft.Json; using Serilog.Events; -namespace AppInspector.Tests.Languages +namespace AppInspector.Tests.Languages; + +[TestClass] +[ExcludeFromCodeCoverage] +public class LanguagesTests { - [TestClass] - [ExcludeFromCodeCoverage] - public class LanguagesTests - { - readonly string comments_z = @" + private readonly string comments_z = @" [ { ""language"": [ @@ -25,7 +24,8 @@ namespace AppInspector.Tests.Languages ""suffix"": ""*/"" } ]"; - readonly string languages_z = @" + + private readonly string languages_z = @" [ { ""name"": ""z"", @@ -34,66 +34,78 @@ namespace AppInspector.Tests.Languages } ]"; - private string testLanguagesPath = string.Empty; - private string testCommentsPath = string.Empty; - private string invalidTestLanguagesPath = string.Empty; - private string invalidTestCommentsPath = string.Empty; + private ILoggerFactory _factory = new NullLoggerFactory(); + private string invalidTestCommentsPath = string.Empty; + private string invalidTestLanguagesPath = string.Empty; + private string testCommentsPath = string.Empty; - private ILoggerFactory _factory = new NullLoggerFactory(); - - [TestInitialize] - public void InitOutput() - { - Directory.CreateDirectory(TestHelpers.GetPath(TestHelpers.AppPath.testOutput)); - testLanguagesPath = Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), "test_languages.json"); - testCommentsPath = Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), "test_comments.json"); - File.WriteAllText(testLanguagesPath, languages_z); - File.WriteAllText(testCommentsPath, comments_z); - invalidTestLanguagesPath = Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), "test_languages_invalid.json"); - invalidTestCommentsPath = Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), "test_comments_invalid.json"); - File.WriteAllText(invalidTestLanguagesPath, languages_z.Trim().Substring(1)); // Not a valid json array, should be missing the opening [ - File.WriteAllText(invalidTestCommentsPath, comments_z.Trim().Substring(1)); // Not a valid json, should be missing the opening [ - _factory = new LogOptions() {ConsoleVerbosityLevel = LogEventLevel.Verbose}.GetLoggerFactory(); - } + private string testLanguagesPath = string.Empty; - [ClassCleanup] - public static void CleanUp() - { - Directory.Delete(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), true); - } - [TestMethod] - public void DetectCustomLanguage() - { - var languages = Microsoft.ApplicationInspector.RulesEngine.Languages.FromConfigurationFiles(_factory, testCommentsPath, testLanguagesPath); - Assert.IsTrue(languages.FromFileNameOut("afilename.z", out var language)); - Assert.AreEqual("z", language.Name); - Assert.IsFalse(languages.FromFileNameOut("afilename.c", out var _)); - } - - [TestMethod] - public void EmptyLanguagesOnInvalidCommentsAndLanguages() - { - Assert.ThrowsException(() => Microsoft.ApplicationInspector.RulesEngine.Languages.FromConfigurationFiles(_factory, invalidTestCommentsPath, null)); - Assert.ThrowsException(() => Microsoft.ApplicationInspector.RulesEngine.Languages.FromConfigurationFiles(_factory, null, invalidTestLanguagesPath)); - } + [TestInitialize] + public void InitOutput() + { + Directory.CreateDirectory(TestHelpers.GetPath(TestHelpers.AppPath.testOutput)); + testLanguagesPath = Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), "test_languages.json"); + testCommentsPath = Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), "test_comments.json"); + File.WriteAllText(testLanguagesPath, languages_z); + File.WriteAllText(testCommentsPath, comments_z); + invalidTestLanguagesPath = Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), + "test_languages_invalid.json"); + invalidTestCommentsPath = + Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), "test_comments_invalid.json"); + File.WriteAllText(invalidTestLanguagesPath, + languages_z.Trim().Substring(1)); // Not a valid json array, should be missing the opening [ + File.WriteAllText(invalidTestCommentsPath, + comments_z.Trim().Substring(1)); // Not a valid json, should be missing the opening [ + _factory = new LogOptions { ConsoleVerbosityLevel = LogEventLevel.Verbose }.GetLoggerFactory(); + } - [TestMethod] - public void DetectLanguageAsFileNameLanguage() - { - Microsoft.ApplicationInspector.RulesEngine.Languages languages = new(_factory); - Assert.IsTrue(languages.FromFileNameOut("package.json", out var language)); - Assert.AreEqual("package.json",language.Name); - } - - [DataRow(null, false)] // No way to determine language - [DataRow("", false)] // No way to determine language - [DataRow("validfilename.json", false)] //This test uses the .z test comments and languages from this file. - [DataRow("validfilename.z", true)] - [TestMethod] - public void ReturnFalseWithInvalidFilename(string? filename, bool expected) - { - var languages = Microsoft.ApplicationInspector.RulesEngine.Languages.FromConfigurationFiles(_factory, testCommentsPath, testLanguagesPath); - Assert.AreEqual(expected,languages.FromFileNameOut(filename!, out _)); - } + [ClassCleanup] + public static void CleanUp() + { + Directory.Delete(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), true); + } + + [TestMethod] + public void DetectCustomLanguage() + { + var languages = + Microsoft.ApplicationInspector.RulesEngine.Languages.FromConfigurationFiles(_factory, testCommentsPath, + testLanguagesPath); + Assert.IsTrue(languages.FromFileNameOut("afilename.z", out var language)); + Assert.AreEqual("z", language.Name); + Assert.IsFalse(languages.FromFileNameOut("afilename.c", out var _)); + } + + [TestMethod] + public void EmptyLanguagesOnInvalidCommentsAndLanguages() + { + Assert.ThrowsException(() => + Microsoft.ApplicationInspector.RulesEngine.Languages.FromConfigurationFiles(_factory, + invalidTestCommentsPath)); + Assert.ThrowsException(() => + Microsoft.ApplicationInspector.RulesEngine.Languages.FromConfigurationFiles(_factory, null, + invalidTestLanguagesPath)); + } + + [TestMethod] + public void DetectLanguageAsFileNameLanguage() + { + Microsoft.ApplicationInspector.RulesEngine.Languages languages = new(_factory); + Assert.IsTrue(languages.FromFileNameOut("package.json", out var language)); + Assert.AreEqual("package.json", language.Name); + } + + [DataRow(null, false)] // No way to determine language + [DataRow("", false)] // No way to determine language + [DataRow("validfilename.json", false)] //This test uses the .z test comments and languages from this file. + [DataRow("validfilename.z", true)] + [TestMethod] + public void ReturnFalseWithInvalidFilename(string? filename, bool expected) + { + var languages = + Microsoft.ApplicationInspector.RulesEngine.Languages.FromConfigurationFiles(_factory, testCommentsPath, + testLanguagesPath); + Assert.AreEqual(expected, languages.FromFileNameOut(filename!, out _)); } } \ No newline at end of file diff --git a/AppInspector.Tests/RuleProcessor/LoadRulesTests.cs b/AppInspector.Tests/RuleProcessor/LoadRulesTests.cs index 240cf06..f3eb4ef 100644 --- a/AppInspector.Tests/RuleProcessor/LoadRulesTests.cs +++ b/AppInspector.Tests/RuleProcessor/LoadRulesTests.cs @@ -13,52 +13,6 @@ namespace AppInspector.Tests.RuleProcessor; [TestClass] public class LoadRulesTests { - [TestInitialize] - public void TestInit() - { - _logger = _loggerFactory.CreateLogger(); - Directory.CreateDirectory(Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), "LoadRuleTests")); - } - - [TestCleanup] - public void TestCleanup() - { - Directory.Delete(Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), "LoadRuleTests"), true); - } - - private readonly ILoggerFactory _loggerFactory = new LogOptions(){ ConsoleVerbosityLevel = LogEventLevel.Verbose }.GetLoggerFactory(); - private ILogger _logger = new NullLogger(); - - [TestMethod] - public void AddFileByPath() - { - RuleSet rules = new(_loggerFactory); - string multiLineRuleLoc = Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), "LoadRuleTests", "multi_line_rule_file.json"); - File.WriteAllText(multiLineRuleLoc, multiLineRule); - rules.AddPath(multiLineRuleLoc, "multiline-tests"); - Assert.AreEqual(1, rules.Count()); - } - - [TestMethod] - public void AddDirectoryByPath() - { - RuleSet rules = new(_loggerFactory); - string multiLineRuleLoc = Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), "LoadRuleTests", "multi_line_rule_file.json"); - File.WriteAllText(multiLineRuleLoc, multiLineRule); - string rule2Loc = Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), "LoadRuleTests", "rule2_file.json"); - File.WriteAllText(rule2Loc, rule2); - rules.AddPath(Path.GetDirectoryName(multiLineRuleLoc) ?? throw new ArgumentNullException(nameof(multiLineRuleLoc)), "multiline-tests"); - Assert.AreEqual(2, rules.Count()); - } - - [TestMethod] - public void AddInvalidPath() - { - RuleSet rules = new(_loggerFactory); - string multiLineRuleLoc = Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), "ThisIsDefinitelyNotADirectoryThatExists"); - Assert.ThrowsException(() => rules.AddPath(multiLineRuleLoc, "multiline-tests")); - } - private const string multiLineRule = @"[ { ""id"": ""SA000005"", @@ -84,7 +38,7 @@ public class LoadRulesTests ""_comment"": """" } ]"; - + // Same rule as above but different id so both can be added private const string rule2 = @"[ { @@ -111,4 +65,58 @@ public class LoadRulesTests ""_comment"": """" } ]"; + + private readonly ILoggerFactory _loggerFactory = + new LogOptions { ConsoleVerbosityLevel = LogEventLevel.Verbose }.GetLoggerFactory(); + + private ILogger _logger = new NullLogger(); + + [TestInitialize] + public void TestInit() + { + _logger = _loggerFactory.CreateLogger(); + Directory.CreateDirectory(Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), "LoadRuleTests")); + } + + [TestCleanup] + public void TestCleanup() + { + Directory.Delete(Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), "LoadRuleTests"), true); + } + + [TestMethod] + public void AddFileByPath() + { + RuleSet rules = new(_loggerFactory); + var multiLineRuleLoc = Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), "LoadRuleTests", + "multi_line_rule_file.json"); + File.WriteAllText(multiLineRuleLoc, multiLineRule); + rules.AddPath(multiLineRuleLoc, "multiline-tests"); + Assert.AreEqual(1, rules.Count()); + } + + [TestMethod] + public void AddDirectoryByPath() + { + RuleSet rules = new(_loggerFactory); + var multiLineRuleLoc = Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), "LoadRuleTests", + "multi_line_rule_file.json"); + File.WriteAllText(multiLineRuleLoc, multiLineRule); + var rule2Loc = Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), "LoadRuleTests", + "rule2_file.json"); + File.WriteAllText(rule2Loc, rule2); + rules.AddPath( + Path.GetDirectoryName(multiLineRuleLoc) ?? throw new ArgumentNullException(nameof(multiLineRuleLoc)), + "multiline-tests"); + Assert.AreEqual(2, rules.Count()); + } + + [TestMethod] + public void AddInvalidPath() + { + RuleSet rules = new(_loggerFactory); + var multiLineRuleLoc = Path.Combine(TestHelpers.GetPath(TestHelpers.AppPath.testOutput), + "ThisIsDefinitelyNotADirectoryThatExists"); + Assert.ThrowsException(() => rules.AddPath(multiLineRuleLoc, "multiline-tests")); + } } \ No newline at end of file diff --git a/AppInspector.Tests/RuleProcessor/RegexWithIndexTests.cs b/AppInspector.Tests/RuleProcessor/RegexWithIndexTests.cs index d1cc9a2..348db66 100644 --- a/AppInspector.Tests/RuleProcessor/RegexWithIndexTests.cs +++ b/AppInspector.Tests/RuleProcessor/RegexWithIndexTests.cs @@ -5,181 +5,16 @@ using System.Linq; using Microsoft.ApplicationInspector.RulesEngine; using Microsoft.ApplicationInspector.RulesEngine.OatExtensions; using Microsoft.CST.OAT; +using Microsoft.CST.RecursiveExtractor; using Microsoft.VisualStudio.TestTools.UnitTesting; -namespace AppInspector.Tests.RuleProcessor +namespace AppInspector.Tests.RuleProcessor; + +[TestClass] +[ExcludeFromCodeCoverage] +public class RegexWithIndexTests { - [TestClass] - [ExcludeFromCodeCoverage] - public class RegexWithIndexTests - { - private readonly Microsoft.ApplicationInspector.RulesEngine.Languages _languages = new(); - - - [TestMethod] - public void NoDictDataAllowed() - { - RuleSet rules = new(null); - rules.AddString(multiLineRule, "TestRules"); - var theRule = rules.GetOatRules().First(); - theRule.Clauses.First().DictData = new() { new KeyValuePair("test","test") }; - - Analyzer analyzer = new ApplicationInspectorAnalyzer(); - var issues = analyzer.EnumerateRuleIssues(theRule); - - Assert.AreEqual(1, issues.Count()); - } - - [TestMethod] - public void NoData() - { - RuleSet rules = new(null); - rules.AddString(multiLineRule, "TestRules"); - var theRule = rules.GetOatRules().First(); - theRule.Clauses.First().Data = new(); - - Analyzer analyzer = new ApplicationInspectorAnalyzer(); - var issues = analyzer.EnumerateRuleIssues(theRule); - - Assert.AreEqual(1, issues.Count()); - } - - [TestMethod] - public void InvalidRegex() - { - RuleSet rules = new(null); - rules.AddString(multiLineRule, "TestRules"); - var theRule = rules.GetOatRules().First(); - theRule.Clauses.First().Data = new() { "^($" }; - - Analyzer analyzer = new ApplicationInspectorAnalyzer(); - var issues = analyzer.EnumerateRuleIssues(theRule); - - Assert.AreEqual(1, issues.Count()); - } - - [TestMethod] - public void InvalidRegexWhenAnalyzing() - { - RuleSet rules = new(null); - rules.AddString(multiLineRule, "TestRules"); - var theRule = rules.GetOatRules().First(); - theRule.Clauses.First().Data = new() { "^($" }; - - Analyzer analyzer = new ApplicationInspectorAnalyzer(); - var issues = analyzer.Analyze(rules.GetOatRules(), - new TextContainer("TestContent", "csharp", new Microsoft.ApplicationInspector.RulesEngine.Languages())); - - Assert.AreEqual(0, issues.Count()); - } - - [TestMethod] - public void MultiLine() - { - RuleSet rules = new(null); - rules.AddString(multiLineRule, "TestRules"); - Microsoft.ApplicationInspector.RulesEngine.RuleProcessor processor = new(rules, new RuleProcessorOptions()); - if (_languages.FromFileNameOut("test.c", out LanguageInfo info)) - { - List matches = processor.AnalyzeFile(multiLineData, new Microsoft.CST.RecursiveExtractor.FileEntry("test.cs", new MemoryStream()), info); - Assert.AreEqual(1, matches.Count); - } - else - { - Assert.Fail(); - } - } - - [TestMethod] - public void MultiLineCaseInsensitive() - { - RuleSet rules = new(null); - rules.AddString(multiLineCaseInsensitiveRule, "TestRules"); - Microsoft.ApplicationInspector.RulesEngine.RuleProcessor processor = new(rules, new RuleProcessorOptions()); - if (_languages.FromFileNameOut("test.c", out LanguageInfo info)) - { - List matches = processor.AnalyzeFile(multiLineData, new Microsoft.CST.RecursiveExtractor.FileEntry("test.cs", new MemoryStream()), info); - Assert.AreEqual(2, matches.Count); - } - else - { - Assert.Fail(); - } - } - - [TestMethod] - public void MultiLineRuleWithSingleLineData() - { - RuleSet rules = new(null); - rules.AddString(multiLineRule, "TestRules"); - Microsoft.ApplicationInspector.RulesEngine.RuleProcessor processor = new(rules, new RuleProcessorOptions()); - if (_languages.FromFileNameOut("test.c", out LanguageInfo info)) - { - List matches = processor.AnalyzeFile(singleLineData, new Microsoft.CST.RecursiveExtractor.FileEntry("test.cs", new MemoryStream()), info); - Assert.AreEqual(0, matches.Count); - } - else - { - Assert.Fail(); - } - } - - [TestMethod] - public void MultiLineRuleWithoutOptionSet() - { - RuleSet rules = new(null); - rules.AddString(multiLineRuleWithoutMultiLine, "TestRules"); - Microsoft.ApplicationInspector.RulesEngine.RuleProcessor processor = new(rules, new RuleProcessorOptions()); - if (_languages.FromFileNameOut("test.c", out LanguageInfo info)) - { - List matches = processor.AnalyzeFile(singleLineData, new Microsoft.CST.RecursiveExtractor.FileEntry("test.cs", new MemoryStream()), info); - Assert.AreEqual(0, matches.Count); - } - else - { - Assert.Fail(); - } - } - - [DataRow(jsonRule)] - [DataRow(jsonAndXmlRule)] - [DataTestMethod] - public void JsonRule(string rule) - { - RuleSet rules = new(null); - rules.AddString(rule, "JsonTestRules"); - Microsoft.ApplicationInspector.RulesEngine.RuleProcessor processor = new(rules, new RuleProcessorOptions(){AllowAllTagsInBuildFiles = true}); - if (_languages.FromFileNameOut("test.json", out LanguageInfo info)) - { - List matches = processor.AnalyzeFile(jsonData, new Microsoft.CST.RecursiveExtractor.FileEntry("test.json", new MemoryStream()), info); - Assert.AreEqual(1, matches.Count); - } - else - { - Assert.Fail(); - } - } - - [DataRow(xmlRule)] - [DataRow(jsonAndXmlRule)] - [DataTestMethod] - public void XmlRule(string rule) - { - RuleSet rules = new(null); - rules.AddString(rule, "XmlTestRules"); - Microsoft.ApplicationInspector.RulesEngine.RuleProcessor processor = new(rules, new RuleProcessorOptions(){AllowAllTagsInBuildFiles = true}); - if (_languages.FromFileNameOut("test.xml", out LanguageInfo info)) - { - List matches = processor.AnalyzeFile(xmlData, new Microsoft.CST.RecursiveExtractor.FileEntry("test.xml", new MemoryStream()), info); - Assert.AreEqual(1, matches.Count); - } - else - { - Assert.Fail(); - } - } - - private const string jsonAndXmlRule = @"[ + private const string jsonAndXmlRule = @"[ { ""id"": ""SA000005"", ""name"": ""Testing.Rules.JSONandXML"", @@ -203,8 +38,8 @@ namespace AppInspector.Tests.RuleProcessor ""_comment"": """" } ]"; - - private const string jsonRule = @"[ + + private const string jsonRule = @"[ { ""id"": ""SA000005"", ""name"": ""Testing.Rules.JSON"", @@ -227,8 +62,8 @@ namespace AppInspector.Tests.RuleProcessor ""_comment"": """" } ]"; - - private const string xmlRule = @"[ + + private const string xmlRule = @"[ { ""id"": ""SA000005"", ""name"": ""Testing.Rules.XML"", @@ -252,8 +87,8 @@ namespace AppInspector.Tests.RuleProcessor } ]"; - private const string jsonData = -@"{ + private const string jsonData = + @"{ ""books"": [ { @@ -289,9 +124,9 @@ namespace AppInspector.Tests.RuleProcessor ] } "; - - private const string xmlData = -@" + + private const string xmlData = + @" The Autobiography of Benjamin Franklin @@ -319,7 +154,7 @@ namespace AppInspector.Tests.RuleProcessor "; - private const string multiLineRuleWithoutMultiLine = @"[ + private const string multiLineRuleWithoutMultiLine = @"[ { ""id"": ""SA000005"", ""name"": ""Testing.Rules.MultiLine"", @@ -342,7 +177,7 @@ namespace AppInspector.Tests.RuleProcessor } ]"; - private const string multiLineRule = @"[ + private const string multiLineRule = @"[ { ""id"": ""SA000005"", ""name"": ""Testing.Rules.MultiLine"", @@ -368,7 +203,7 @@ namespace AppInspector.Tests.RuleProcessor } ]"; - private const string multiLineCaseInsensitiveRule = @"[ + private const string multiLineCaseInsensitiveRule = @"[ { ""id"": ""SA000005"", ""name"": ""Testing.Rules.MultiLine"", @@ -395,14 +230,182 @@ namespace AppInspector.Tests.RuleProcessor } ]"; - const string multiLineData = @" + private const string multiLineData = @" race CAR race car"; - const string singleLineData = @" + private const string singleLineData = @" raceCAR racecar"; + + private readonly Microsoft.ApplicationInspector.RulesEngine.Languages _languages = new(); + + + [TestMethod] + public void NoDictDataAllowed() + { + RuleSet rules = new(); + rules.AddString(multiLineRule, "TestRules"); + var theRule = rules.GetOatRules().First(); + theRule.Clauses.First().DictData = new List> + { new KeyValuePair("test", "test") }; + + Analyzer analyzer = new ApplicationInspectorAnalyzer(); + var issues = analyzer.EnumerateRuleIssues(theRule); + + Assert.AreEqual(1, issues.Count()); + } + + [TestMethod] + public void NoData() + { + RuleSet rules = new(); + rules.AddString(multiLineRule, "TestRules"); + var theRule = rules.GetOatRules().First(); + theRule.Clauses.First().Data = new List(); + + Analyzer analyzer = new ApplicationInspectorAnalyzer(); + var issues = analyzer.EnumerateRuleIssues(theRule); + + Assert.AreEqual(1, issues.Count()); + } + + [TestMethod] + public void InvalidRegex() + { + RuleSet rules = new(); + rules.AddString(multiLineRule, "TestRules"); + var theRule = rules.GetOatRules().First(); + theRule.Clauses.First().Data = new List { "^($" }; + + Analyzer analyzer = new ApplicationInspectorAnalyzer(); + var issues = analyzer.EnumerateRuleIssues(theRule); + + Assert.AreEqual(1, issues.Count()); + } + + [TestMethod] + public void InvalidRegexWhenAnalyzing() + { + RuleSet rules = new(); + rules.AddString(multiLineRule, "TestRules"); + var theRule = rules.GetOatRules().First(); + theRule.Clauses.First().Data = new List { "^($" }; + + Analyzer analyzer = new ApplicationInspectorAnalyzer(); + var issues = analyzer.Analyze(rules.GetOatRules(), + new TextContainer("TestContent", "csharp", new Microsoft.ApplicationInspector.RulesEngine.Languages())); + + Assert.AreEqual(0, issues.Count()); + } + + [TestMethod] + public void MultiLine() + { + RuleSet rules = new(); + rules.AddString(multiLineRule, "TestRules"); + Microsoft.ApplicationInspector.RulesEngine.RuleProcessor processor = new(rules, new RuleProcessorOptions()); + if (_languages.FromFileNameOut("test.c", out var info)) + { + var matches = processor.AnalyzeFile(multiLineData, new FileEntry("test.cs", new MemoryStream()), info); + Assert.AreEqual(1, matches.Count); + } + else + { + Assert.Fail(); + } + } + + [TestMethod] + public void MultiLineCaseInsensitive() + { + RuleSet rules = new(); + rules.AddString(multiLineCaseInsensitiveRule, "TestRules"); + Microsoft.ApplicationInspector.RulesEngine.RuleProcessor processor = new(rules, new RuleProcessorOptions()); + if (_languages.FromFileNameOut("test.c", out var info)) + { + var matches = processor.AnalyzeFile(multiLineData, new FileEntry("test.cs", new MemoryStream()), info); + Assert.AreEqual(2, matches.Count); + } + else + { + Assert.Fail(); + } + } + + [TestMethod] + public void MultiLineRuleWithSingleLineData() + { + RuleSet rules = new(); + rules.AddString(multiLineRule, "TestRules"); + Microsoft.ApplicationInspector.RulesEngine.RuleProcessor processor = new(rules, new RuleProcessorOptions()); + if (_languages.FromFileNameOut("test.c", out var info)) + { + var matches = processor.AnalyzeFile(singleLineData, new FileEntry("test.cs", new MemoryStream()), info); + Assert.AreEqual(0, matches.Count); + } + else + { + Assert.Fail(); + } + } + + [TestMethod] + public void MultiLineRuleWithoutOptionSet() + { + RuleSet rules = new(); + rules.AddString(multiLineRuleWithoutMultiLine, "TestRules"); + Microsoft.ApplicationInspector.RulesEngine.RuleProcessor processor = new(rules, new RuleProcessorOptions()); + if (_languages.FromFileNameOut("test.c", out var info)) + { + var matches = processor.AnalyzeFile(singleLineData, new FileEntry("test.cs", new MemoryStream()), info); + Assert.AreEqual(0, matches.Count); + } + else + { + Assert.Fail(); + } + } + + [DataRow(jsonRule)] + [DataRow(jsonAndXmlRule)] + [DataTestMethod] + public void JsonRule(string rule) + { + RuleSet rules = new(); + rules.AddString(rule, "JsonTestRules"); + Microsoft.ApplicationInspector.RulesEngine.RuleProcessor processor = new(rules, + new RuleProcessorOptions { AllowAllTagsInBuildFiles = true }); + if (_languages.FromFileNameOut("test.json", out var info)) + { + var matches = processor.AnalyzeFile(jsonData, new FileEntry("test.json", new MemoryStream()), info); + Assert.AreEqual(1, matches.Count); + } + else + { + Assert.Fail(); + } + } + + [DataRow(xmlRule)] + [DataRow(jsonAndXmlRule)] + [DataTestMethod] + public void XmlRule(string rule) + { + RuleSet rules = new(); + rules.AddString(rule, "XmlTestRules"); + Microsoft.ApplicationInspector.RulesEngine.RuleProcessor processor = new(rules, + new RuleProcessorOptions { AllowAllTagsInBuildFiles = true }); + if (_languages.FromFileNameOut("test.xml", out var info)) + { + var matches = processor.AnalyzeFile(xmlData, new FileEntry("test.xml", new MemoryStream()), info); + Assert.AreEqual(1, matches.Count); + } + else + { + Assert.Fail(); + } } } \ No newline at end of file diff --git a/AppInspector.Tests/RuleProcessor/RuleTests.cs b/AppInspector.Tests/RuleProcessor/RuleTests.cs index 6e2e76d..9e8409f 100644 --- a/AppInspector.Tests/RuleProcessor/RuleTests.cs +++ b/AppInspector.Tests/RuleProcessor/RuleTests.cs @@ -7,42 +7,6 @@ namespace AppInspector.Tests.RuleProcessor; [TestClass] public class RuleTests { - [TestMethod] - public void ModifySource() - { - RuleSet rules = new(null); - var originalSource = "TestRules"; - rules.AddString(MultiLineRuleWithoutMultiLine, originalSource); - var rule = rules.First(); - Assert.AreEqual(originalSource, rule.Source); - var newSource = "Somewhere"; - rule.Source = newSource; - Assert.AreEqual(newSource, rule.Source); - } - - [TestMethod] - public void ModifyRuntimeTag() - { - RuleSet rules = new(null); - var originalSource = "TestRules"; - rules.AddString(MultiLineRuleWithoutMultiLine, originalSource); - var rule = rules.First(); - var newTag = "SomeTag"; - rule.RuntimeTag = newTag; - Assert.AreEqual(newTag, rule.RuntimeTag); - } - - [TestMethod] - public void ModifyDisabled() - { - RuleSet rules = new(null); - var originalSource = "TestRules"; - rules.AddString(MultiLineRuleWithoutMultiLine, originalSource); - var rule = rules.First(); - rule.Disabled = true; - Assert.AreEqual(true, rule.Disabled); - } - private const string MultiLineRuleWithoutMultiLine = @"[ { ""id"": ""SA000005"", @@ -65,4 +29,40 @@ public class RuleTests ""_comment"": """" } ]"; + + [TestMethod] + public void ModifySource() + { + RuleSet rules = new(); + var originalSource = "TestRules"; + rules.AddString(MultiLineRuleWithoutMultiLine, originalSource); + var rule = rules.First(); + Assert.AreEqual(originalSource, rule.Source); + var newSource = "Somewhere"; + rule.Source = newSource; + Assert.AreEqual(newSource, rule.Source); + } + + [TestMethod] + public void ModifyRuntimeTag() + { + RuleSet rules = new(); + var originalSource = "TestRules"; + rules.AddString(MultiLineRuleWithoutMultiLine, originalSource); + var rule = rules.First(); + var newTag = "SomeTag"; + rule.RuntimeTag = newTag; + Assert.AreEqual(newTag, rule.RuntimeTag); + } + + [TestMethod] + public void ModifyDisabled() + { + RuleSet rules = new(); + var originalSource = "TestRules"; + rules.AddString(MultiLineRuleWithoutMultiLine, originalSource); + var rule = rules.First(); + rule.Disabled = true; + Assert.AreEqual(true, rule.Disabled); + } } \ No newline at end of file diff --git a/AppInspector.Tests/RuleProcessor/SubstringWithIndexTests.cs b/AppInspector.Tests/RuleProcessor/SubstringWithIndexTests.cs index 89f8bf4..cb6480b 100644 --- a/AppInspector.Tests/RuleProcessor/SubstringWithIndexTests.cs +++ b/AppInspector.Tests/RuleProcessor/SubstringWithIndexTests.cs @@ -5,291 +5,16 @@ using System.Linq; using Microsoft.ApplicationInspector.RulesEngine; using Microsoft.ApplicationInspector.RulesEngine.OatExtensions; using Microsoft.CST.OAT; +using Microsoft.CST.RecursiveExtractor; using Microsoft.VisualStudio.TestTools.UnitTesting; -namespace AppInspector.Tests.RuleProcessor +namespace AppInspector.Tests.RuleProcessor; + +[TestClass] +[ExcludeFromCodeCoverage] +public class SubstringWithIndexTests { - [TestClass] - [ExcludeFromCodeCoverage] - public class SubstringWithIndexTests - { - private readonly Microsoft.ApplicationInspector.RulesEngine.Languages _languages = new(); - - [DataRow(jsonStringRule)] - [DataRow(jsonAndXmlStringRule)] - [DataTestMethod] - public void JsonSubstringRule(string rule) - { - RuleSet rules = new(null); - rules.AddString(rule, "JsonTestRules"); - Microsoft.ApplicationInspector.RulesEngine.RuleProcessor processor = new(rules, new RuleProcessorOptions(){AllowAllTagsInBuildFiles = true}); - if (_languages.FromFileNameOut("test.json", out LanguageInfo info)) - { - List matches = processor.AnalyzeFile(jsonData, new Microsoft.CST.RecursiveExtractor.FileEntry("test.json", new MemoryStream()), info); - Assert.AreEqual(1, matches.Count); - } - else - { - Assert.Fail(); - } - } - - [DataRow(xmlStringRule)] - [DataRow(jsonAndXmlStringRule)] - [DataTestMethod] - public void XmlSubstringRule(string rule) - { - RuleSet rules = new(null); - rules.AddString(rule, "XmlTestRules"); - Microsoft.ApplicationInspector.RulesEngine.RuleProcessor processor = new(rules, new RuleProcessorOptions(){AllowAllTagsInBuildFiles = true}); - if (_languages.FromFileNameOut("test.xml", out LanguageInfo info)) - { - List matches = processor.AnalyzeFile(xmlData, new Microsoft.CST.RecursiveExtractor.FileEntry("test.xml", new MemoryStream()), info); - Assert.AreEqual(1, matches.Count); - } - else - { - Assert.Fail(); - } - } - - private const string jsonAndXmlStringRule = @"[ - { - ""id"": ""SA000005"", - ""name"": ""Testing.Rules.JSONandXML"", - ""tags"": [ - ""Testing.Rules.JSON.JSONandXML"" - ], - ""severity"": ""Critical"", - ""description"": ""This rule finds books from the JSON or XML titled with Franklin."", - ""patterns"": [ - { - ""pattern"": ""Franklin"", - ""type"": ""string"", - ""confidence"": ""High"", - ""scopes"": [ - ""code"" - ], - ""jsonpaths"" : [""$.books[*].title""], - ""xpaths"" : [""/bookstore/book/title""] - } - ], - ""_comment"": """" - } - ]"; - - private const string jsonStringRule = @"[ - { - ""id"": ""SA000005"", - ""name"": ""Testing.Rules.JSON"", - ""tags"": [ - ""Testing.Rules.JSON"" - ], - ""severity"": ""Critical"", - ""description"": ""This rule finds books from the JSON titled with Franklin."", - ""patterns"": [ - { - ""pattern"": ""Franklin"", - ""type"": ""string"", - ""confidence"": ""High"", - ""scopes"": [ - ""code"" - ], - ""jsonpaths"" : [""$.books[*].title""] - } - ], - ""_comment"": """" - } - ]"; - - private const string xmlStringRule = @"[ - { - ""id"": ""SA000005"", - ""name"": ""Testing.Rules.XML"", - ""tags"": [ - ""Testing.Rules.XML"" - ], - ""severity"": ""Critical"", - ""description"": ""This rule finds books from the XML titled with Franklin."", - ""patterns"": [ - { - ""pattern"": ""Franklin"", - ""type"": ""string"", - ""confidence"": ""High"", - ""scopes"": [ - ""code"" - ], - ""xpaths"" : [""/bookstore/book/title""] - } - ], - ""_comment"": """" - } -]"; - - private const string jsonData = -@"{ - ""books"": - [ - { - ""category"": ""fiction"", - ""title"" : ""A Wild Sheep Chase"", - ""author"" : ""Haruki Murakami"", - ""price"" : 22.72 - }, - { - ""category"": ""fiction"", - ""title"" : ""The Night Watch"", - ""author"" : ""Sergei Lukyanenko"", - ""price"" : 23.58 - }, - { - ""category"": ""fiction"", - ""title"" : ""The Comedians"", - ""author"" : ""Graham Greene"", - ""price"" : 21.99 - }, - { - ""category"": ""memoir"", - ""title"" : ""The Night Watch"", - ""author"" : ""David Atlee Phillips"", - ""price"" : 260.90 - }, - { - ""category"": ""memoir"", - ""title"" : ""The Autobiography of Benjamin Franklin"", - ""author"" : ""Benjamin Franklin"", - ""price"" : 123.45 - } - ] -} -"; - - private const string xmlData = -@" - - - The Autobiography of Benjamin Franklin - - Benjamin - Franklin - - 8.99 - - - The Confidence Man - - Herman - Melville - - 11.99 - - - The Gorgias - - Plato - - 9.99 - - -"; - - [TestMethod] - public void NoDictDataAllowed() - { - RuleSet rules = new(null); - rules.AddString(wordBoundaryEnabledCaseSensitive, "TestRules"); - var theRule = rules.GetOatRules().First(); - theRule.Clauses.First().DictData = new() { new KeyValuePair("test", "test") }; - - Analyzer analyzer = new ApplicationInspectorAnalyzer(); - var issues = analyzer.EnumerateRuleIssues(theRule); - - Assert.AreEqual(1, issues.Count()); - } - - [TestMethod] - public void NoData() - { - RuleSet rules = new(null); - rules.AddString(wordBoundaryEnabledCaseSensitive, "TestRules"); - var theRule = rules.GetOatRules().First(); - theRule.Clauses.First().Data = new(); - - Analyzer analyzer = new ApplicationInspectorAnalyzer(); - var issues = analyzer.EnumerateRuleIssues(theRule); - - Assert.AreEqual(1, issues.Count()); - } - - [TestMethod] - public void WordBoundaryEnabledCaseSensitive() - { - RuleSet rules = new(null); - rules.AddString(wordBoundaryEnabledCaseSensitive, "TestRules"); - Microsoft.ApplicationInspector.RulesEngine.RuleProcessor processor = new(rules, new RuleProcessorOptions()); - if (_languages.FromFileNameOut("test.c", out LanguageInfo info)) - { - List matches = processor.AnalyzeFile(data, new Microsoft.CST.RecursiveExtractor.FileEntry("test.cs", new MemoryStream()), info); - Assert.AreEqual(1, matches.Count); - } - else - { - Assert.Fail(); - } - } - - [TestMethod] - public void WordBoundaryDisabledCaseSensitive() - { - RuleSet rules = new(null); - rules.AddString(wordBoundaryDisabledCaseSensitive, "TestRules"); - Microsoft.ApplicationInspector.RulesEngine.RuleProcessor processor = new(rules, new RuleProcessorOptions()); - if (_languages.FromFileNameOut("test.c", out LanguageInfo info)) - { - List matches = processor.AnalyzeFile(data, new Microsoft.CST.RecursiveExtractor.FileEntry("test.cs", new MemoryStream()), info); - Assert.AreEqual(2, matches.Count); - } - else - { - Assert.Fail(); - } - } - - [TestMethod] - public void WordBoundaryEnabledCaseInsensitive() - { - RuleSet rules = new(null); - rules.AddString(wordBoundaryEnabledCaseInsensitive, "TestRules"); - Microsoft.ApplicationInspector.RulesEngine.RuleProcessor processor = new(rules, new RuleProcessorOptions()); - if (_languages.FromFileNameOut("test.c", out LanguageInfo info)) - { - List matches = processor.AnalyzeFile(data, new Microsoft.CST.RecursiveExtractor.FileEntry("test.cs", new MemoryStream()), info); - Assert.AreEqual(2, matches.Count); - } - else - { - Assert.Fail(); - } - } - - [TestMethod] - public void WordBoundaryDisabledCaseInsensitive() - { - RuleSet rules = new(null); - rules.AddString(wordBoundaryDisabledCaseInsensitive, "TestRules"); - Microsoft.ApplicationInspector.RulesEngine.RuleProcessor processor = new(rules, new RuleProcessorOptions()); - if (_languages.FromFileNameOut("test.c", out LanguageInfo info)) - { - List matches = processor.AnalyzeFile(data, new Microsoft.CST.RecursiveExtractor.FileEntry("test.cs", new MemoryStream()), info); - Assert.AreEqual(4, matches.Count); - } - else - { - Assert.Fail(); - } - } - - private const string wordBoundaryDisabledCaseSensitive = @"[ + private const string wordBoundaryDisabledCaseSensitive = @"[ { ""id"": ""SA000005"", ""name"": ""Testing.Rules.WordBoundary"", @@ -311,7 +36,8 @@ namespace AppInspector.Tests.RuleProcessor ""_comment"": """" } ]"; - private const string wordBoundaryDisabledCaseInsensitive = @"[ + + private const string wordBoundaryDisabledCaseInsensitive = @"[ { ""id"": ""SA000005"", ""name"": ""Testing.Rules.WordBoundary"", @@ -336,7 +62,8 @@ namespace AppInspector.Tests.RuleProcessor ""_comment"": """" } ]"; - private const string wordBoundaryEnabledCaseSensitive = @"[ + + private const string wordBoundaryEnabledCaseSensitive = @"[ { ""id"": ""SA000005"", ""name"": ""Testing.Rules.WordBoundary"", @@ -358,7 +85,8 @@ namespace AppInspector.Tests.RuleProcessor ""_comment"": """" } ]"; - private const string wordBoundaryEnabledCaseInsensitive = @"[ + + private const string wordBoundaryEnabledCaseInsensitive = @"[ { ""id"": ""SA000005"", ""name"": ""Testing.Rules.WordBoundary"", @@ -384,10 +112,108 @@ namespace AppInspector.Tests.RuleProcessor } ]"; - const string data = @" + private const string data = @" raceCARwithmorestuff racecarwithmorestuff raceCAR withmorestuff racecar withmorestuff"; + + private readonly Microsoft.ApplicationInspector.RulesEngine.Languages _languages = new(); + + [TestMethod] + public void NoDictDataAllowed() + { + RuleSet rules = new(); + rules.AddString(wordBoundaryEnabledCaseSensitive, "TestRules"); + var theRule = rules.GetOatRules().First(); + theRule.Clauses.First().DictData = new List> + { new KeyValuePair("test", "test") }; + + Analyzer analyzer = new ApplicationInspectorAnalyzer(); + var issues = analyzer.EnumerateRuleIssues(theRule); + + Assert.AreEqual(1, issues.Count()); + } + + [TestMethod] + public void NoData() + { + RuleSet rules = new(); + rules.AddString(wordBoundaryEnabledCaseSensitive, "TestRules"); + var theRule = rules.GetOatRules().First(); + theRule.Clauses.First().Data = new List(); + + Analyzer analyzer = new ApplicationInspectorAnalyzer(); + var issues = analyzer.EnumerateRuleIssues(theRule); + + Assert.AreEqual(1, issues.Count()); + } + + [TestMethod] + public void WordBoundaryEnabledCaseSensitive() + { + RuleSet rules = new(); + rules.AddString(wordBoundaryEnabledCaseSensitive, "TestRules"); + Microsoft.ApplicationInspector.RulesEngine.RuleProcessor processor = new(rules, new RuleProcessorOptions()); + if (_languages.FromFileNameOut("test.c", out var info)) + { + var matches = processor.AnalyzeFile(data, new FileEntry("test.cs", new MemoryStream()), info); + Assert.AreEqual(1, matches.Count); + } + else + { + Assert.Fail(); + } + } + + [TestMethod] + public void WordBoundaryDisabledCaseSensitive() + { + RuleSet rules = new(); + rules.AddString(wordBoundaryDisabledCaseSensitive, "TestRules"); + Microsoft.ApplicationInspector.RulesEngine.RuleProcessor processor = new(rules, new RuleProcessorOptions()); + if (_languages.FromFileNameOut("test.c", out var info)) + { + var matches = processor.AnalyzeFile(data, new FileEntry("test.cs", new MemoryStream()), info); + Assert.AreEqual(2, matches.Count); + } + else + { + Assert.Fail(); + } + } + + [TestMethod] + public void WordBoundaryEnabledCaseInsensitive() + { + RuleSet rules = new(); + rules.AddString(wordBoundaryEnabledCaseInsensitive, "TestRules"); + Microsoft.ApplicationInspector.RulesEngine.RuleProcessor processor = new(rules, new RuleProcessorOptions()); + if (_languages.FromFileNameOut("test.c", out var info)) + { + var matches = processor.AnalyzeFile(data, new FileEntry("test.cs", new MemoryStream()), info); + Assert.AreEqual(2, matches.Count); + } + else + { + Assert.Fail(); + } + } + + [TestMethod] + public void WordBoundaryDisabledCaseInsensitive() + { + RuleSet rules = new(); + rules.AddString(wordBoundaryDisabledCaseInsensitive, "TestRules"); + Microsoft.ApplicationInspector.RulesEngine.RuleProcessor processor = new(rules, new RuleProcessorOptions()); + if (_languages.FromFileNameOut("test.c", out var info)) + { + var matches = processor.AnalyzeFile(data, new FileEntry("test.cs", new MemoryStream()), info); + Assert.AreEqual(4, matches.Count); + } + else + { + Assert.Fail(); + } } } \ No newline at end of file diff --git a/AppInspector.Tests/RuleProcessor/WithinClauseTests.cs b/AppInspector.Tests/RuleProcessor/WithinClauseTests.cs index 15dccb2..3b5c8b1 100644 --- a/AppInspector.Tests/RuleProcessor/WithinClauseTests.cs +++ b/AppInspector.Tests/RuleProcessor/WithinClauseTests.cs @@ -1,132 +1,465 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; using Microsoft.ApplicationInspector.Logging; using Microsoft.ApplicationInspector.RulesEngine; using Microsoft.ApplicationInspector.RulesEngine.OatExtensions; +using Microsoft.CST.RecursiveExtractor; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging.Abstractions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Serilog.Events; -namespace AppInspector.Tests.RuleProcessor +namespace AppInspector.Tests.RuleProcessor; + +[TestClass] +[ExcludeFromCodeCoverage] +public class WithinClauseTests { - [TestClass] - [ExcludeFromCodeCoverage] - public class WithinClauseTests + private const string validationRule = @"[ { - private readonly Microsoft.ApplicationInspector.RulesEngine.Languages _languages = new(); - - [DataRow("WithinClauseWithInvertWithFindingRange")] - [DataRow("WithinClauseWithoutInvertWithFindingRange")] - [DataRow("WithinClauseWithInvertWithSameLine")] - [DataRow("WithinClauseWithoutInvertWithSameLine")] - [DataRow("WithinClauseWithInvertWithBeforeOnly")] - [DataRow("WithinClauseWithoutInvertWithBeforeOnly")] - [DataRow("WithinClauseWithInvertWithAfterOnly")] - [DataRow("WithinClauseWithoutInvertWithAfterOnly")] - [DataRow("WithinClauseWithInvertWithSameFile")] - [DataRow("WithinClauseWithoutInvertWithSameFile")] - - [DataTestMethod] - public void WithinClauseInvertTest(string testDataKey) - { - WithinClauseInvertTest(testData[testDataKey].testData, testData[testDataKey].conditionRegion, testData[testDataKey].negate, testData[testDataKey].expectedNumMatches, testData[testDataKey].expectedMatchesLineStarts); - } - - internal void WithinClauseInvertTest(string testDataString, string conditionRegion, bool invert, int expectedMatches, int[] expectedMatchesLineStarts) - { - RuleSet rules = new(_loggerFactory); - var newRule = baseRule.Replace("REPLACE_REGION", conditionRegion).Replace("REPLACE_NEGATE", invert.ToString().ToLowerInvariant()); - rules.AddString(newRule, "TestRules"); - Microsoft.ApplicationInspector.RulesEngine.RuleProcessor processor = new(rules, new RuleProcessorOptions()); - if (_languages.FromFileNameOut("test.c", out LanguageInfo info)) + ""id"": ""SA000005"", + ""name"": ""Testing.Rules.MallocNotFree1"", + ""tags"": [ + ""Testing.Rules.MallocNotFree1"" + ], + ""severity"": ""Critical"", + ""description"": ""this rule aims to find malloc() that does NOT have free() in 1 line range"", + ""patterns"": [ { - List matches = processor.AnalyzeFile(testDataString, new Microsoft.CST.RecursiveExtractor.FileEntry("test.cs", new MemoryStream()), info); - Assert.AreEqual(expectedMatches, matches.Count); - foreach (int expectedMatchLineStart in expectedMatchesLineStarts) - { - MatchRecord? correctLineMatch = matches.FirstOrDefault(match => match.StartLocationLine == expectedMatchLineStart); - Assert.IsNotNull(correctLineMatch); - matches.Remove(correctLineMatch); - } + ""pattern"": ""racecar"", + ""type"": ""regex"", + ""confidence"": ""High"", + ""modifiers"": [ + ""i"" + ], + ""scopes"": [ + ""code"" + ] } - else + ], + ""conditions"": [ { - Assert.Fail(); + ""pattern"": { + ""pattern"": ""car"", + ""type"": ""regex"", + ""scopes"": [ + ""code"" + ] + }, + ""search_in"": ""same-line"" + } + ] + } +]"; + + private const string findingOnlyRule = @"[ + { + ""id"": ""SA000005"", + ""name"": ""Testing.Rules.MallocNotFree1"", + ""tags"": [ + ""Testing.Rules.MallocNotFree1"" + ], + ""severity"": ""Critical"", + ""description"": ""this rule aims to find malloc() that does NOT have free() in 1 line range"", + ""patterns"": [ + { + ""pattern"": ""racecar"", + ""type"": ""regex"", + ""confidence"": ""High"", + ""modifiers"": [ + ""i"" + ], + ""scopes"": [ + ""code"" + ] + } + ], + ""conditions"": [ + { + ""pattern"": { + ""pattern"": ""car"", + ""type"": ""regex"", + ""scopes"": [ + ""code"" + ] + }, + ""search_in"": ""finding-only"", + ""negate_finding"": REPLACE_NEGATE + } + ], + ""_comment"": """" + } +]"; + + private const string findingRangeZeroRule = @"[ + { + ""id"": ""SA000005"", + ""name"": ""Testing.Rules.MallocNotFree1"", + ""tags"": [ + ""Testing.Rules.MallocNotFree1"" + ], + ""severity"": ""Critical"", + ""description"": ""this rule aims to find malloc() that does NOT have free() in 1 line range"", + ""patterns"": [ + { + ""pattern"": ""racecar"", + ""type"": ""regex"", + ""confidence"": ""High"", + ""modifiers"": [ + ""i"" + ], + ""scopes"": [ + ""code"" + ] + } + ], + ""conditions"": [ + { + ""pattern"": { + ""pattern"": ""car"", + ""type"": ""regex"", + ""scopes"": [ + ""code"" + ] + }, + ""search_in"": ""finding-region(0,0)"", + ""negate_finding"": REPLACE_NEGATE + } + ], + ""_comment"": """" + } +]"; + + private const string insideFindingData = @" +raceCAR +racecar"; + + private const string findingRangeData = @"#include +#include +#include +#include + +#define BUFSIZER1 512 +#define BUFSIZER2 ((BUFSIZER1 / 2) - 8) + +int main(int argc, char **argv) +{ + char *buf1R1; + char *buf2R1; + buf1R1 = (char *)malloc(BUFSIZER1); + buf2R1 = (char *)malloc(BUFSIZER1); + free(buf2R1); + strncpy(buf2R1, argv[1], BUFSIZER1 - 1); + free(buf1R1); +}"; + + private const string sameLineData = @"#include +#include +#include +#include + +#define BUFSIZER1 512 +#define BUFSIZER2 ((BUFSIZER1 / 2) - 8) + +int main(int argc, char **argv) +{ + char *buf1R1; + char *buf2R1; + buf1R1 = (char *)malloc(BUFSIZER1); + buf2R1 = (char *)malloc(BUFSIZER1);free(buf2R1); + + strncpy(buf2R1, argv[1], BUFSIZER1 - 1); + free(buf1R1); +}"; + + private const string beforeOnlyData = @"#include + free(buf2R1); + buf2R1 = (char *)malloc(BUFSIZER1); +"; + + private const string afterOnlyData = @"#include + buf2R1 = (char *)malloc(BUFSIZER1); + free(buf2R1);"; + + private const string sameFileData = @"#include + buf1R1 = (char *)malloc(BUFSIZER1); + free(buf1R1);"; + + private const string findingOnlyData = @"#include +#include +#include +#include + +#define BUFSIZER1 512 +#define BUFSIZER2 ((BUFSIZER1 / 2) - 8) + +int main(int argc, char **argv) +{ + char *buf1R1; + char *buf2R1; + buf1R1 = (char *)malloc(BUFSIZER1); + buf2R1 = (char *)malloc(BUFSIZER1);free(buf2R1); + + strncpy(buf2R1, argv[1], BUFSIZER1 - 1); + free(buf1R1); +}"; + + private const string multiLineData = @" + buf2R1 = (char *)malloc(BUFSIZER1); + free +();"; + + private const string baseRule = @"[ + { + ""id"": ""SA000005"", + ""name"": ""Testing.Rules.MallocNotFree1"", + ""tags"": [ + ""Testing.Rules.MallocNotFree1"" + ], + ""severity"": ""Critical"", + ""description"": ""this rule aims to find malloc() that does NOT have free() in 1 line range"", + ""patterns"": [ + { + ""pattern"": ""malloc\\("", + ""type"": ""regex"", + ""confidence"": ""High"", + ""modifiers"": [ + ""i"" + ], + ""scopes"": [ + ""code"" + ] + } + ], + ""conditions"": [ + { + ""pattern"": { + ""pattern"": ""free\\("", + ""type"": ""regex"", + ""scopes"": [ + ""code"" + ], + ""modifiers"": [ + ""i"" + ] + }, + ""search_in"": ""REPLACE_REGION"", + ""negate_finding"": REPLACE_NEGATE + } + ], + ""_comment"": """" + } +]"; + + private const string multiLineRule = @"[ + { + ""id"": ""SA000005"", + ""name"": ""Testing.Rules.MallocNotFree1"", + ""tags"": [ + ""Testing.Rules.MallocNotFree1"" + ], + ""severity"": ""Critical"", + ""description"": ""this rule aims to find malloc() that does NOT have free\\r\\n() in 1 line range"", + ""patterns"": [ + { + ""pattern"": ""malloc\\("", + ""type"": ""regex"", + ""confidence"": ""High"", + ""modifiers"": [ + ""i"" + ], + ""scopes"": [ + ""code"" + ] + } + ], + ""conditions"": [ + { + ""pattern"": { + ""pattern"": ""free\\r\\n\\("", + ""type"": ""regex"", + ""scopes"": [ + ""code"" + ], + ""modifiers"": [ + ""i"", + ""m"" + ] + }, + ""negate_finding"": true, + ""search_in"": ""finding-region(-3,3)"" + } + ], + ""_comment"": """" + } +]"; + + private readonly Microsoft.ApplicationInspector.RulesEngine.Languages _languages = new(); + + private readonly ILoggerFactory _loggerFactory = + new LogOptions { ConsoleVerbosityLevel = LogEventLevel.Verbose }.GetLoggerFactory(); + + private ILogger _logger = new NullLogger(); + + private readonly + Dictionary testData = new() + { + { + "WithinClauseWithInvertWithFindingRange", + (findingRangeData, "finding-region(0,1)", true, 1, new[] { 13 }) + }, + { + "WithinClauseWithoutInvertWithFindingRange", + (findingRangeData, "finding-region(0,1)", false, 1, new[] { 14 }) + }, + { + "WithinClauseWithInvertWithSameLine", + (sameLineData, "same-line", true, 1, new[] { 13 }) + }, + { + "WithinClauseWithoutInvertWithSameLine", + (sameLineData, "same-line", false, 1, new[] { 14 }) + }, + { + "WithinClauseWithInvertWithBeforeOnly", + (beforeOnlyData, "only-before", true, 0, new int[] { }) + }, + { + "WithinClauseWithoutInvertWithBeforeOnly", + (beforeOnlyData, "only-before", false, 1, new[] { 3 }) + }, + { + "WithinClauseWithInvertWithAfterOnly", + (afterOnlyData, "only-after", true, 0, new int[] { }) + }, + { + "WithinClauseWithoutInvertWithAfterOnly", + (afterOnlyData, "only-after", false, 1, new[] { 2 }) + }, + { + "WithinClauseWithInvertWithSameFile", + (sameFileData, "same-file", true, 0, new int[] { }) + }, + { + "WithinClauseWithoutInvertWithSameFile", + (sameFileData, "same-file", false, 1, new[] { 2 }) + } + }; + + [DataRow("WithinClauseWithInvertWithFindingRange")] + [DataRow("WithinClauseWithoutInvertWithFindingRange")] + [DataRow("WithinClauseWithInvertWithSameLine")] + [DataRow("WithinClauseWithoutInvertWithSameLine")] + [DataRow("WithinClauseWithInvertWithBeforeOnly")] + [DataRow("WithinClauseWithoutInvertWithBeforeOnly")] + [DataRow("WithinClauseWithInvertWithAfterOnly")] + [DataRow("WithinClauseWithoutInvertWithAfterOnly")] + [DataRow("WithinClauseWithInvertWithSameFile")] + [DataRow("WithinClauseWithoutInvertWithSameFile")] + [DataTestMethod] + public void WithinClauseInvertTest(string testDataKey) + { + WithinClauseInvertTest(testData[testDataKey].testData, testData[testDataKey].conditionRegion, + testData[testDataKey].negate, testData[testDataKey].expectedNumMatches, + testData[testDataKey].expectedMatchesLineStarts); + } + + internal void WithinClauseInvertTest(string testDataString, string conditionRegion, bool invert, + int expectedMatches, int[] expectedMatchesLineStarts) + { + RuleSet rules = new(_loggerFactory); + var newRule = baseRule.Replace("REPLACE_REGION", conditionRegion) + .Replace("REPLACE_NEGATE", invert.ToString().ToLowerInvariant()); + rules.AddString(newRule, "TestRules"); + Microsoft.ApplicationInspector.RulesEngine.RuleProcessor processor = new(rules, new RuleProcessorOptions()); + if (_languages.FromFileNameOut("test.c", out var info)) + { + var matches = processor.AnalyzeFile(testDataString, new FileEntry("test.cs", new MemoryStream()), info); + Assert.AreEqual(expectedMatches, matches.Count); + foreach (var expectedMatchLineStart in expectedMatchesLineStarts) + { + var correctLineMatch = + matches.FirstOrDefault(match => match.StartLocationLine == expectedMatchLineStart); + Assert.IsNotNull(correctLineMatch); + matches.Remove(correctLineMatch); } } - - [DataRow(true, 1, new [] { 2 })] - [DataRow(false, 1, new[] { 3 })] - [DataTestMethod] - public void WithinClauseInvertTestForSameLine(bool invert, int expectedMatches, int[] expectedMatchesLineStarts) + else { - RuleSet rules = new(_loggerFactory); - var newRule = findingOnlyRule.Replace("REPLACE_NEGATE", invert.ToString().ToLowerInvariant()); - rules.AddString(newRule, "TestRules"); - Microsoft.ApplicationInspector.RulesEngine.RuleProcessor processor = new(rules, new RuleProcessorOptions()); - if (_languages.FromFileNameOut("test.c", out LanguageInfo info)) + Assert.Fail(); + } + } + + [DataRow(true, 1, new[] { 2 })] + [DataRow(false, 1, new[] { 3 })] + [DataTestMethod] + public void WithinClauseInvertTestForSameLine(bool invert, int expectedMatches, int[] expectedMatchesLineStarts) + { + RuleSet rules = new(_loggerFactory); + var newRule = findingOnlyRule.Replace("REPLACE_NEGATE", invert.ToString().ToLowerInvariant()); + rules.AddString(newRule, "TestRules"); + Microsoft.ApplicationInspector.RulesEngine.RuleProcessor processor = new(rules, new RuleProcessorOptions()); + if (_languages.FromFileNameOut("test.c", out var info)) + { + var matches = processor.AnalyzeFile(insideFindingData, new FileEntry("test.cs", new MemoryStream()), info); + Assert.AreEqual(expectedMatches, matches.Count); + foreach (var expectedMatchLineStart in expectedMatchesLineStarts) { - List matches = processor.AnalyzeFile(insideFindingData, new Microsoft.CST.RecursiveExtractor.FileEntry("test.cs", new MemoryStream()), info); - Assert.AreEqual(expectedMatches, matches.Count); - foreach (int expectedMatchLineStart in expectedMatchesLineStarts) - { - MatchRecord? correctLineMatch = matches.FirstOrDefault(match => match.StartLocationLine == expectedMatchLineStart); - Assert.IsNotNull(correctLineMatch); - matches.Remove(correctLineMatch); - } - } - else - { - Assert.Fail(); + var correctLineMatch = + matches.FirstOrDefault(match => match.StartLocationLine == expectedMatchLineStart); + Assert.IsNotNull(correctLineMatch); + matches.Remove(correctLineMatch); } } - - [DataRow(true, true, true, 0, 0, 2)] - [DataRow(true, false, true, 0, 0, 1)] - [DataRow(true, true, false, 0, 0, 2)] - [DataRow(false, true, true, 0, 0, 2)] - [DataRow(true, false, false, 0, 0, 0)] - [DataRow(false, false, true, 0, 0, 0)] - [DataRow(false, true, false, 0, 0, 1)] - [DataRow(false, true, false, 0, 1, 1)] - [DataRow(false, true, false, 1, -1, 0)] - [DataRow(false, true, false, -1, 0, 1)] - - [DataTestMethod] - public void WithinClauseValidationTests(bool findingOnlySetting, bool findingRegionSetting, bool sameLineOnlySetting, int afterSetting, int beforeSetting, int expectedNumIssues) + else { - RuleSet rules = new(_loggerFactory); - rules.AddString(validationRule, "TestRules"); - IEnumerable withinClauses = rules - .GetOatRules() - .SelectMany(x => x.Clauses) - .OfType(); - foreach (WithinClause clause in withinClauses) - { - clause.FindingOnly = findingOnlySetting; - clause.FindingRegion = findingRegionSetting; - clause.SameLineOnly = sameLineOnlySetting; - clause.After = afterSetting; - clause.Before = beforeSetting; - } - RulesVerifier verifier = new(new RulesVerifierOptions() {LoggerFactory = _loggerFactory}); - var oatIssues = verifier.CheckIntegrity(rules).SelectMany(x => x.OatIssues); - foreach (var violation in oatIssues) - { - _logger.LogDebug(violation.Description); - } - Assert.AreEqual(expectedNumIssues, verifier.CheckIntegrity(rules).Sum(x => x.OatIssues.Count())); + Assert.Fail(); + } + } + + [DataRow(true, true, true, 0, 0, 2)] + [DataRow(true, false, true, 0, 0, 1)] + [DataRow(true, true, false, 0, 0, 2)] + [DataRow(false, true, true, 0, 0, 2)] + [DataRow(true, false, false, 0, 0, 0)] + [DataRow(false, false, true, 0, 0, 0)] + [DataRow(false, true, false, 0, 0, 1)] + [DataRow(false, true, false, 0, 1, 1)] + [DataRow(false, true, false, 1, -1, 0)] + [DataRow(false, true, false, -1, 0, 1)] + [DataTestMethod] + public void WithinClauseValidationTests(bool findingOnlySetting, bool findingRegionSetting, + bool sameLineOnlySetting, int afterSetting, int beforeSetting, int expectedNumIssues) + { + RuleSet rules = new(_loggerFactory); + rules.AddString(validationRule, "TestRules"); + var withinClauses = rules + .GetOatRules() + .SelectMany(x => x.Clauses) + .OfType(); + foreach (var clause in withinClauses) + { + clause.FindingOnly = findingOnlySetting; + clause.FindingRegion = findingRegionSetting; + clause.SameLineOnly = sameLineOnlySetting; + clause.After = afterSetting; + clause.Before = beforeSetting; } - [TestMethod] - public void WithinClauseWithMultipleConditions() - { - RuleSet rules = new(_loggerFactory); - var newRule = @"[{ + RulesVerifier verifier = new(new RulesVerifierOptions { LoggerFactory = _loggerFactory }); + var oatIssues = verifier.CheckIntegrity(rules).SelectMany(x => x.OatIssues); + foreach (var violation in oatIssues) _logger.LogDebug(violation.Description); + Assert.AreEqual(expectedNumIssues, verifier.CheckIntegrity(rules).Sum(x => x.OatIssues.Count())); + } + + [TestMethod] + public void WithinClauseWithMultipleConditions() + { + RuleSet rules = new(_loggerFactory); + var newRule = @"[{ ""name"": ""Insecure URL"", ""id"": ""DS137138"", ""description"": ""An HTTP-based URL without TLS was detected."", @@ -205,469 +538,74 @@ namespace AppInspector.Tests.RuleProcessor """" ] }]"; - var testData = @" + var testData = @" http:// http://localhost xmlns= http:// http:// "; - rules.AddString(newRule, "TestRules"); - Microsoft.ApplicationInspector.RulesEngine.RuleProcessor processor = new(rules, new RuleProcessorOptions(){Parallel = false}); - if (_languages.FromFileNameOut("test.c", out LanguageInfo info)) + rules.AddString(newRule, "TestRules"); + Microsoft.ApplicationInspector.RulesEngine.RuleProcessor processor = new(rules, + new RuleProcessorOptions { Parallel = false }); + if (_languages.FromFileNameOut("test.c", out var info)) + { + var matches = processor.AnalyzeFile(testData, + new FileEntry("test.cs", new MemoryStream()), info); + Assert.AreEqual(2, matches.Count); + } + } + + [DataRow(true, 1, new[] { 2 })] + [DataRow(false, 1, new[] { 3 })] + [DataTestMethod] + public void WithinClauseInvertTestForFindingRange0(bool invert, int expectedMatches, + int[] expectedMatchesLineStarts) + { + RuleSet rules = new(_loggerFactory); + var newRule = findingRangeZeroRule.Replace("REPLACE_NEGATE", invert.ToString().ToLowerInvariant()); + rules.AddString(newRule, "TestRules"); + Microsoft.ApplicationInspector.RulesEngine.RuleProcessor processor = new(rules, + new RuleProcessorOptions { Parallel = false }); + if (_languages.FromFileNameOut("test.c", out var info)) + { + var matches = processor.AnalyzeFile(insideFindingData, new FileEntry("test.cs", new MemoryStream()), info); + Assert.AreEqual(expectedMatches, matches.Count); + foreach (var expectedMatchLineStart in expectedMatchesLineStarts) { - List matches = processor.AnalyzeFile(testData, - new Microsoft.CST.RecursiveExtractor.FileEntry("test.cs", new MemoryStream()), info); - Assert.AreEqual(2, matches.Count); + var correctLineMatch = + matches.FirstOrDefault(match => match.StartLocationLine == expectedMatchLineStart); + Assert.IsNotNull(correctLineMatch); + matches.Remove(correctLineMatch); } } - - [TestMethod] - public void TestXmlWithAndWithoutNamespace() + else { - var content = @" - - 4.0.0 - - xxx - xxx - 0.1.0-SNAPSHOT - pom - - ${project.groupId}:${project.artifactId} - - - - 17 - - -"; - // The same as above but with no namespace specified - var noNamespaceContent = @" - - 4.0.0 - - xxx - xxx - 0.1.0-SNAPSHOT - pom - - ${project.groupId}:${project.artifactId} - - - - 17 - - -"; - var rule = @"[{ - ""name"": ""Source code: Java 17"", - ""id"": ""CODEJAVA000000"", - ""description"": ""Java 17 maven configuration"", - ""applies_to_file_regex"": [ - ""pom.xml"" - ], - ""tags"": [ - ""Code.Java.17"" - ], - ""severity"": ""critical"", - ""patterns"": [ - { - ""pattern"": ""17"", - ""xpaths"" : [""/*[local-name(.)='project']/*[local-name(.)='properties']/*[local-name(.)='java.version']""], - ""type"": ""regex"", - ""scopes"": [ - ""code"" - ], - ""modifiers"": [ - ""i"" - ], - ""confidence"": ""high"" - } - ] - }]"; - RuleSet rules = new(null); - var originalSource = "TestRules"; - rules.AddString(rule, originalSource); - var analyzer = new Microsoft.ApplicationInspector.RulesEngine.RuleProcessor(rules, new RuleProcessorOptions(){Parallel = false, AllowAllTagsInBuildFiles = true}); - if (_languages.FromFileNameOut("pom.xml", out LanguageInfo info)) - { - var matches = analyzer.AnalyzeFile(content, new Microsoft.CST.RecursiveExtractor.FileEntry("pom.xml", new MemoryStream()), info); - Assert.AreEqual(1, matches.Count); - matches = analyzer.AnalyzeFile(noNamespaceContent, new Microsoft.CST.RecursiveExtractor.FileEntry("pom.xml", new MemoryStream()), info); - Assert.AreEqual(1, matches.Count); - } + Assert.Fail(); } + } - [DataRow(true, 1, new[] { 2 })] - [DataRow(false, 1, new[] { 3 })] - [DataTestMethod] - public void WithinClauseInvertTestForFindingRange0(bool invert, int expectedMatches, int[] expectedMatchesLineStarts) + [TestMethod] + public void MultiLineRegexCondition() + { + RuleSet rules = new(_loggerFactory); + rules.AddString(multiLineRule, "multiline-tests"); + Microsoft.ApplicationInspector.RulesEngine.RuleProcessor processor = new(rules, + new RuleProcessorOptions { Parallel = false }); + if (_languages.FromFileNameOut("test.c", out var info)) { - RuleSet rules = new(_loggerFactory); - var newRule = findingRangeZeroRule.Replace("REPLACE_NEGATE", invert.ToString().ToLowerInvariant()); - rules.AddString(newRule, "TestRules"); - Microsoft.ApplicationInspector.RulesEngine.RuleProcessor processor = new(rules, new RuleProcessorOptions(){Parallel = false}); - if (_languages.FromFileNameOut("test.c", out LanguageInfo info)) - { - List matches = processor.AnalyzeFile(insideFindingData, new Microsoft.CST.RecursiveExtractor.FileEntry("test.cs", new MemoryStream()), info); - Assert.AreEqual(expectedMatches, matches.Count); - foreach (int expectedMatchLineStart in expectedMatchesLineStarts) - { - MatchRecord? correctLineMatch = matches.FirstOrDefault(match => match.StartLocationLine == expectedMatchLineStart); - Assert.IsNotNull(correctLineMatch); - matches.Remove(correctLineMatch); - } - } - else - { - Assert.Fail(); - } + var matches = processor.AnalyzeFile(multiLineData, new FileEntry("test.cs", new MemoryStream()), info); + Assert.AreEqual(0, matches.Count); } - - [TestMethod] - public void MultiLineRegexCondition() + else { - RuleSet rules = new(_loggerFactory); - rules.AddString(multiLineRule, "multiline-tests"); - Microsoft.ApplicationInspector.RulesEngine.RuleProcessor processor = new(rules, new RuleProcessorOptions(){Parallel = false}); - if (_languages.FromFileNameOut("test.c", out LanguageInfo info)) - { - List matches = processor.AnalyzeFile(multiLineData, new Microsoft.CST.RecursiveExtractor.FileEntry("test.cs", new MemoryStream()), info); - Assert.AreEqual(0, matches.Count); - } - else - { - Assert.Fail(); - } + Assert.Fail(); } + } - Dictionary testData = new() - { - { - "WithinClauseWithInvertWithFindingRange", - (findingRangeData, "finding-region(0,1)", true, 1, new[] { 13 }) - }, - { - "WithinClauseWithoutInvertWithFindingRange", - (findingRangeData, "finding-region(0,1)", false, 1, new[] { 14 }) - }, - { - "WithinClauseWithInvertWithSameLine", - (sameLineData, "same-line", true, 1, new[] { 13 }) - }, - { - "WithinClauseWithoutInvertWithSameLine", - (sameLineData, "same-line", false, 1, new[] { 14 }) - }, - { - "WithinClauseWithInvertWithBeforeOnly", - (beforeOnlyData, "only-before", true, 0, new int[] { }) - }, - { - "WithinClauseWithoutInvertWithBeforeOnly", - (beforeOnlyData, "only-before", false, 1, new[] { 3 }) - }, - { - "WithinClauseWithInvertWithAfterOnly", - (afterOnlyData, "only-after", true, 0, new int[] { }) - }, - { - "WithinClauseWithoutInvertWithAfterOnly", - (afterOnlyData, "only-after", false, 1, new[] { 2 }) - }, - { - "WithinClauseWithInvertWithSameFile", - (sameFileData, "same-file", true, 0, new int[] { }) - }, - { - "WithinClauseWithoutInvertWithSameFile", - (sameFileData, "same-file", false, 1, new[] { 2 }) - } - }; - - [TestInitialize] - public void TestInit() - { - _logger = _loggerFactory.CreateLogger(); - } - - private readonly ILoggerFactory _loggerFactory = new LogOptions(){ ConsoleVerbosityLevel = LogEventLevel.Verbose }.GetLoggerFactory(); - private ILogger _logger = new NullLogger(); - - private const string validationRule = @"[ + [TestInitialize] + public void TestInit() { - ""id"": ""SA000005"", - ""name"": ""Testing.Rules.MallocNotFree1"", - ""tags"": [ - ""Testing.Rules.MallocNotFree1"" - ], - ""severity"": ""Critical"", - ""description"": ""this rule aims to find malloc() that does NOT have free() in 1 line range"", - ""patterns"": [ - { - ""pattern"": ""racecar"", - ""type"": ""regex"", - ""confidence"": ""High"", - ""modifiers"": [ - ""i"" - ], - ""scopes"": [ - ""code"" - ] - } - ], - ""conditions"": [ - { - ""pattern"": { - ""pattern"": ""car"", - ""type"": ""regex"", - ""scopes"": [ - ""code"" - ] - }, - ""search_in"": ""same-line"" - } - ] + _logger = _loggerFactory.CreateLogger(); } -]"; - - private const string findingOnlyRule = @"[ - { - ""id"": ""SA000005"", - ""name"": ""Testing.Rules.MallocNotFree1"", - ""tags"": [ - ""Testing.Rules.MallocNotFree1"" - ], - ""severity"": ""Critical"", - ""description"": ""this rule aims to find malloc() that does NOT have free() in 1 line range"", - ""patterns"": [ - { - ""pattern"": ""racecar"", - ""type"": ""regex"", - ""confidence"": ""High"", - ""modifiers"": [ - ""i"" - ], - ""scopes"": [ - ""code"" - ] - } - ], - ""conditions"": [ - { - ""pattern"": { - ""pattern"": ""car"", - ""type"": ""regex"", - ""scopes"": [ - ""code"" - ] - }, - ""search_in"": ""finding-only"", - ""negate_finding"": REPLACE_NEGATE - } - ], - ""_comment"": """" - } -]"; - - private const string findingRangeZeroRule = @"[ - { - ""id"": ""SA000005"", - ""name"": ""Testing.Rules.MallocNotFree1"", - ""tags"": [ - ""Testing.Rules.MallocNotFree1"" - ], - ""severity"": ""Critical"", - ""description"": ""this rule aims to find malloc() that does NOT have free() in 1 line range"", - ""patterns"": [ - { - ""pattern"": ""racecar"", - ""type"": ""regex"", - ""confidence"": ""High"", - ""modifiers"": [ - ""i"" - ], - ""scopes"": [ - ""code"" - ] - } - ], - ""conditions"": [ - { - ""pattern"": { - ""pattern"": ""car"", - ""type"": ""regex"", - ""scopes"": [ - ""code"" - ] - }, - ""search_in"": ""finding-region(0,0)"", - ""negate_finding"": REPLACE_NEGATE - } - ], - ""_comment"": """" - } -]"; - - const string insideFindingData = @" -raceCAR -racecar"; - - const string findingRangeData = @"#include -#include -#include -#include - -#define BUFSIZER1 512 -#define BUFSIZER2 ((BUFSIZER1 / 2) - 8) - -int main(int argc, char **argv) -{ - char *buf1R1; - char *buf2R1; - buf1R1 = (char *)malloc(BUFSIZER1); - buf2R1 = (char *)malloc(BUFSIZER1); - free(buf2R1); - strncpy(buf2R1, argv[1], BUFSIZER1 - 1); - free(buf1R1); -}"; - - const string sameLineData = @"#include -#include -#include -#include - -#define BUFSIZER1 512 -#define BUFSIZER2 ((BUFSIZER1 / 2) - 8) - -int main(int argc, char **argv) -{ - char *buf1R1; - char *buf2R1; - buf1R1 = (char *)malloc(BUFSIZER1); - buf2R1 = (char *)malloc(BUFSIZER1);free(buf2R1); - - strncpy(buf2R1, argv[1], BUFSIZER1 - 1); - free(buf1R1); -}"; - const string beforeOnlyData = @"#include - free(buf2R1); - buf2R1 = (char *)malloc(BUFSIZER1); -"; - const string afterOnlyData = @"#include - buf2R1 = (char *)malloc(BUFSIZER1); - free(buf2R1);"; - - const string sameFileData = @"#include - buf1R1 = (char *)malloc(BUFSIZER1); - free(buf1R1);"; - - const string findingOnlyData = @"#include -#include -#include -#include - -#define BUFSIZER1 512 -#define BUFSIZER2 ((BUFSIZER1 / 2) - 8) - -int main(int argc, char **argv) -{ - char *buf1R1; - char *buf2R1; - buf1R1 = (char *)malloc(BUFSIZER1); - buf2R1 = (char *)malloc(BUFSIZER1);free(buf2R1); - - strncpy(buf2R1, argv[1], BUFSIZER1 - 1); - free(buf1R1); -}"; - - const string multiLineData = @" - buf2R1 = (char *)malloc(BUFSIZER1); - free -();"; - private const string baseRule = @"[ - { - ""id"": ""SA000005"", - ""name"": ""Testing.Rules.MallocNotFree1"", - ""tags"": [ - ""Testing.Rules.MallocNotFree1"" - ], - ""severity"": ""Critical"", - ""description"": ""this rule aims to find malloc() that does NOT have free() in 1 line range"", - ""patterns"": [ - { - ""pattern"": ""malloc\\("", - ""type"": ""regex"", - ""confidence"": ""High"", - ""modifiers"": [ - ""i"" - ], - ""scopes"": [ - ""code"" - ] - } - ], - ""conditions"": [ - { - ""pattern"": { - ""pattern"": ""free\\("", - ""type"": ""regex"", - ""scopes"": [ - ""code"" - ], - ""modifiers"": [ - ""i"" - ] - }, - ""search_in"": ""REPLACE_REGION"", - ""negate_finding"": REPLACE_NEGATE - } - ], - ""_comment"": """" - } -]"; - private const string multiLineRule = @"[ - { - ""id"": ""SA000005"", - ""name"": ""Testing.Rules.MallocNotFree1"", - ""tags"": [ - ""Testing.Rules.MallocNotFree1"" - ], - ""severity"": ""Critical"", - ""description"": ""this rule aims to find malloc() that does NOT have free\\r\\n() in 1 line range"", - ""patterns"": [ - { - ""pattern"": ""malloc\\("", - ""type"": ""regex"", - ""confidence"": ""High"", - ""modifiers"": [ - ""i"" - ], - ""scopes"": [ - ""code"" - ] - } - ], - ""conditions"": [ - { - ""pattern"": { - ""pattern"": ""free\\r\\n\\("", - ""type"": ""regex"", - ""scopes"": [ - ""code"" - ], - ""modifiers"": [ - ""i"", - ""m"" - ] - }, - ""negate_finding"": true, - ""search_in"": ""finding-region(-3,3)"" - } - ], - ""_comment"": """" - } -]"; - } -} +} \ No newline at end of file diff --git a/AppInspector.Tests/RuleProcessor/XmlAndJsonTests.cs b/AppInspector.Tests/RuleProcessor/XmlAndJsonTests.cs new file mode 100644 index 0000000..d27de70 --- /dev/null +++ b/AppInspector.Tests/RuleProcessor/XmlAndJsonTests.cs @@ -0,0 +1,270 @@ +using System.IO; +using Microsoft.ApplicationInspector.RulesEngine; +using Microsoft.CST.RecursiveExtractor; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace AppInspector.Tests.RuleProcessor; + +[TestClass] +public class XmlAndJsonTests +{ + private readonly Microsoft.ApplicationInspector.RulesEngine.Languages _languages = new(); + + private const string jsonAndXmlStringRule = @"[ + { + ""id"": ""SA000005"", + ""name"": ""Testing.Rules.JSONandXML"", + ""tags"": [ + ""Testing.Rules.JSON.JSONandXML"" + ], + ""severity"": ""Critical"", + ""description"": ""This rule finds books from the JSON or XML titled with Franklin."", + ""patterns"": [ + { + ""pattern"": ""Franklin"", + ""type"": ""string"", + ""confidence"": ""High"", + ""scopes"": [ + ""code"" + ], + ""jsonpaths"" : [""$.books[*].title""], + ""xpaths"" : [""/bookstore/book/title""] + } + ], + ""_comment"": """" + } + ]"; + + private const string jsonStringRule = @"[ + { + ""id"": ""SA000005"", + ""name"": ""Testing.Rules.JSON"", + ""tags"": [ + ""Testing.Rules.JSON"" + ], + ""severity"": ""Critical"", + ""description"": ""This rule finds books from the JSON titled with Franklin."", + ""patterns"": [ + { + ""pattern"": ""Franklin"", + ""type"": ""string"", + ""confidence"": ""High"", + ""scopes"": [ + ""code"" + ], + ""jsonpaths"" : [""$.books[*].title""] + } + ], + ""_comment"": """" + } + ]"; + + private const string xmlStringRule = @"[ + { + ""id"": ""SA000005"", + ""name"": ""Testing.Rules.XML"", + ""tags"": [ + ""Testing.Rules.XML"" + ], + ""severity"": ""Critical"", + ""description"": ""This rule finds books from the XML titled with Franklin."", + ""patterns"": [ + { + ""pattern"": ""Franklin"", + ""type"": ""string"", + ""confidence"": ""High"", + ""scopes"": [ + ""code"" + ], + ""xpaths"" : [""/bookstore/book/title""] + } + ], + ""_comment"": """" + } +]"; + + private const string jsonData = + @"{ + ""books"": + [ + { + ""category"": ""fiction"", + ""title"" : ""A Wild Sheep Chase"", + ""author"" : ""Haruki Murakami"", + ""price"" : 22.72 + }, + { + ""category"": ""fiction"", + ""title"" : ""The Night Watch"", + ""author"" : ""Sergei Lukyanenko"", + ""price"" : 23.58 + }, + { + ""category"": ""fiction"", + ""title"" : ""The Comedians"", + ""author"" : ""Graham Greene"", + ""price"" : 21.99 + }, + { + ""category"": ""memoir"", + ""title"" : ""The Night Watch"", + ""author"" : ""David Atlee Phillips"", + ""price"" : 260.90 + }, + { + ""category"": ""memoir"", + ""title"" : ""The Autobiography of Benjamin Franklin"", + ""author"" : ""Benjamin Franklin"", + ""price"" : 123.45 + } + ] +} +"; + + private const string xmlData = + @" + + + The Autobiography of Benjamin Franklin + + Benjamin + Franklin + + 8.99 + + + The Confidence Man + + Herman + Melville + + 11.99 + + + The Gorgias + + Plato + + 9.99 + + +"; + + [DataRow(jsonStringRule)] + [DataRow(jsonAndXmlStringRule)] + [DataTestMethod] + public void JsonStringRule(string rule) + { + RuleSet rules = new(); + rules.AddString(rule, "JsonTestRules"); + Microsoft.ApplicationInspector.RulesEngine.RuleProcessor processor = new(rules, + new RuleProcessorOptions { AllowAllTagsInBuildFiles = true }); + if (_languages.FromFileNameOut("test.json", out var info)) + { + var matches = processor.AnalyzeFile(jsonData, new FileEntry("test.json", new MemoryStream()), info); + Assert.AreEqual(1, matches.Count); + } + else + { + Assert.Fail(); + } + } + + [DataRow(xmlStringRule)] + [DataRow(jsonAndXmlStringRule)] + [DataTestMethod] + public void XmlStringRule(string rule) + { + RuleSet rules = new(); + rules.AddString(rule, "XmlTestRules"); + Microsoft.ApplicationInspector.RulesEngine.RuleProcessor processor = new(rules, + new RuleProcessorOptions { AllowAllTagsInBuildFiles = true }); + if (_languages.FromFileNameOut("test.xml", out var info)) + { + var matches = processor.AnalyzeFile(xmlData, new FileEntry("test.xml", new MemoryStream()), info); + Assert.AreEqual(1, matches.Count); + } + else + { + Assert.Fail(); + } + } + + [TestMethod] + public void TestXmlWithAndWithoutNamespace() + { + var content = @" + + 4.0.0 + + xxx + xxx + 0.1.0-SNAPSHOT + pom + + ${project.groupId}:${project.artifactId} + + + + 17 + + +"; + // The same as above but with no namespace specified + var noNamespaceContent = @" + + 4.0.0 + + xxx + xxx + 0.1.0-SNAPSHOT + pom + + ${project.groupId}:${project.artifactId} + + + + 17 + + +"; + var rule = @"[{ + ""name"": ""Source code: Java 17"", + ""id"": ""CODEJAVA000000"", + ""description"": ""Java 17 maven configuration"", + ""applies_to_file_regex"": [ + ""pom.xml"" + ], + ""tags"": [ + ""Code.Java.17"" + ], + ""severity"": ""critical"", + ""patterns"": [ + { + ""pattern"": ""17"", + ""xpaths"" : [""/*[local-name(.)='project']/*[local-name(.)='properties']/*[local-name(.)='java.version']""], + ""type"": ""regex"", + ""scopes"": [ + ""code"" + ], + ""modifiers"": [ + ""i"" + ], + ""confidence"": ""high"" + } + ] + }]"; + RuleSet rules = new(); + var originalSource = "TestRules"; + rules.AddString(rule, originalSource); + var analyzer = new Microsoft.ApplicationInspector.RulesEngine.RuleProcessor(rules, + new RuleProcessorOptions { Parallel = false, AllowAllTagsInBuildFiles = true }); + if (_languages.FromFileNameOut("pom.xml", out var info)) + { + var matches = analyzer.AnalyzeFile(content, new FileEntry("pom.xml", new MemoryStream()), info); + Assert.AreEqual(1, matches.Count); + matches = analyzer.AnalyzeFile(noNamespaceContent, new FileEntry("pom.xml", new MemoryStream()), info); + Assert.AreEqual(1, matches.Count); + } + } +} \ No newline at end of file diff --git a/AppInspector.Tests/TestHelpers.cs b/AppInspector.Tests/TestHelpers.cs index c468564..5611957 100644 --- a/AppInspector.Tests/TestHelpers.cs +++ b/AppInspector.Tests/TestHelpers.cs @@ -5,135 +5,140 @@ using System.Diagnostics.CodeAnalysis; using System.IO; using Microsoft.ApplicationInspector.Logging; using Microsoft.Extensions.Logging; +using Serilog.Events; -namespace AppInspector.Tests +namespace AppInspector.Tests; + +[ExcludeFromCodeCoverage] +public static class TestHelpers { - [ExcludeFromCodeCoverage] - public static class TestHelpers + public enum AppPath { - public static ILoggerFactory GenerateLoggerFactory(string logName = "testLog.txt", Serilog.Events.LogEventLevel fileLevel = Serilog.Events.LogEventLevel.Verbose, Serilog.Events.LogEventLevel consoleLevel = Serilog.Events.LogEventLevel.Debug) => - new LogOptions() - { - LogFileLevel = fileLevel, - LogFilePath = Path.Combine(GetPath(AppPath.testLogOutput), logName), - ConsoleVerbosityLevel = consoleLevel - }.GetLoggerFactory(); - public enum AppPath { basePath, testSource, testRules, testOutput, defaultRules, appInspectorCLI, testLogOutput }; + basePath, + testSource, + testRules, + testOutput, + defaultRules, + appInspectorCLI, + testLogOutput + } - private static string _basePath = string.Empty; + private static string _basePath = string.Empty; - private static string GetBaseAppPath() + public static ILoggerFactory GenerateLoggerFactory(string logName = "testLog.txt", + LogEventLevel fileLevel = LogEventLevel.Verbose, LogEventLevel consoleLevel = LogEventLevel.Debug) + { + return new LogOptions { - if (!string.IsNullOrEmpty(_basePath)) - { - return _basePath; - } + LogFileLevel = fileLevel, + LogFilePath = Path.Combine(GetPath(AppPath.testLogOutput), logName), + ConsoleVerbosityLevel = consoleLevel + }.GetLoggerFactory(); + } - _basePath = Path.GetFullPath(AppContext.BaseDirectory); - return _basePath; - } + private static string GetBaseAppPath() + { + if (!string.IsNullOrEmpty(_basePath)) return _basePath; - public static string GetPath(AppPath pathType) + _basePath = Path.GetFullPath(AppContext.BaseDirectory); + return _basePath; + } + + public static string GetPath(AppPath pathType) + { + var result = ""; + switch (pathType) { - string result = ""; - switch (pathType) - { - case AppPath.basePath: - result = GetBaseAppPath(); - break; + case AppPath.basePath: + result = GetBaseAppPath(); + break; - case AppPath.testSource: - result = Path.Combine(GetBaseAppPath(), "..", "..", "..", "..", "AppInspector.Tests", "source"); - break; + case AppPath.testSource: + result = Path.Combine(GetBaseAppPath(), "..", "..", "..", "..", "AppInspector.Tests", "source"); + break; - case AppPath.testRules://Packrules default output use - result = Path.Combine(GetBaseAppPath(), "..", "..", "..", "..", "AppInspector.Tests", "customrules"); - break; + case AppPath.testRules: //Packrules default output use + result = Path.Combine(GetBaseAppPath(), "..", "..", "..", "..", "AppInspector.Tests", "customrules"); + break; - case AppPath.testOutput://Packrules default output use - result = Path.Combine(GetBaseAppPath(), "..", "..", "..", "..", "AppInspector.Tests", "output"); - break; + case AppPath.testOutput: //Packrules default output use + result = Path.Combine(GetBaseAppPath(), "..", "..", "..", "..", "AppInspector.Tests", "output"); + break; - case AppPath.defaultRules: - result = Path.Combine(GetBaseAppPath(), "..", "..", "..", "..", "AppInspector", "rules"); - break; + case AppPath.defaultRules: + result = Path.Combine(GetBaseAppPath(), "..", "..", "..", "..", "AppInspector", "rules"); + break; - case AppPath.appInspectorCLI: + case AppPath.appInspectorCLI: #if DEBUG - result = Path.Combine(GetBaseAppPath(), "..", "..", "..", "..", "AppInspector.CLI", "bin", "debug", "net6.0", "applicationinspector.cli.exe"); + result = + Path.Combine(GetBaseAppPath(), "..", "..", "..", "..", "AppInspector.CLI", "bin", "debug", "net6.0", "applicationinspector.cli.exe"); #else - result = Path.Combine(GetBaseAppPath(), "..", "..", "..", "..", "AppInspector.CLI", "bin", "release", "net6.0", "applicationinspector.cli.exe"); + result = Path.Combine(GetBaseAppPath(), "..", "..", "..", "..", "AppInspector.CLI", "bin", "release", + "net6.0", "applicationinspector.cli.exe"); #endif - break; - case AppPath.testLogOutput: - result = Path.Combine(GetBaseAppPath(), "..", "..", "..", "..", "AppInspector.Tests", "logs"); - break; - } - - result = Path.GetFullPath(result); - return result; + break; + case AppPath.testLogOutput: + result = Path.Combine(GetBaseAppPath(), "..", "..", "..", "..", "AppInspector.Tests", "logs"); + break; } - public static List GetTagsFromFile(string[] contentLines) + result = Path.GetFullPath(result); + return result; + } + + public static List GetTagsFromFile(string[] contentLines) + { + List results = new(); + + int i; + for (i = 0; i < contentLines.Length; i++) + if (contentLines[i].Contains("[UniqueTags]")) + break; + + i++; //get past marker + while (!contentLines[i].Contains("Select Counters")) { - List results = new(); - - int i; - for (i = 0; i < contentLines.Length; i++) - { - if (contentLines[i].Contains("[UniqueTags]")) - { - break; - } - } - - i++;//get past marker - while (!contentLines[i].Contains("Select Counters")) - { - results.Add(contentLines[i++]); - if (i > contentLines.Length) - { - break; - } - } - - return results; + results.Add(contentLines[i++]); + if (i > contentLines.Length) break; } - public static int RunProcess(string appFilePath, string arguments) + return results; + } + + public static int RunProcess(string appFilePath, string arguments) + { + var result = 2; + using (Process process = new()) { - int result = 2; - using (Process process = new()) - { - process.StartInfo.FileName = appFilePath; - process.StartInfo.Arguments = arguments; - process.Start(); - process.WaitForExit(); - result = process.ExitCode; - } - - return result; + process.StartInfo.FileName = appFilePath; + process.StartInfo.Arguments = arguments; + process.Start(); + process.WaitForExit(); + result = process.ExitCode; } - public static int RunProcess(string appFilePath, string arguments, out string consoleContent) + return result; + } + + public static int RunProcess(string appFilePath, string arguments, out string consoleContent) + { + int result; + using (Process process = new()) { - int result; - using (Process process = new()) - { - process.StartInfo.FileName = appFilePath; - process.StartInfo.Arguments = arguments; - process.StartInfo.RedirectStandardOutput = true; - process.Start(); - consoleContent = process.StandardOutput.ReadToEnd(); - process.WaitForExit(); - result = process.ExitCode; + process.StartInfo.FileName = appFilePath; + process.StartInfo.Arguments = arguments; + process.StartInfo.RedirectStandardOutput = true; + process.Start(); + consoleContent = process.StandardOutput.ReadToEnd(); + process.WaitForExit(); + result = process.ExitCode; - using StreamWriter standardOutput = new(Console.OpenStandardOutput()); - standardOutput.AutoFlush = true; - Console.SetOut(standardOutput); - } - - return result; + using StreamWriter standardOutput = new(Console.OpenStandardOutput()); + standardOutput.AutoFlush = true; + Console.SetOut(standardOutput); } + + return result; } } \ No newline at end of file diff --git a/AppInspector/AppInspector.Commands.csproj b/AppInspector/AppInspector.Commands.csproj index 083ca5c..faeeeeb 100644 --- a/AppInspector/AppInspector.Commands.csproj +++ b/AppInspector/AppInspector.Commands.csproj @@ -1,94 +1,94 @@ - - Library - Microsoft.CST.ApplicationInspector.Commands - 0.0.0-placeholder - https://github.com/microsoft/ApplicationInspector - https://github.com/microsoft/ApplicationInspector - Security Static Analyzer - Microsoft Application Inspector is a software source code analysis tool that helps identify and surface well-known features and other interesting characteristics of source code to aid in determining what the software is or what it does. - © Microsoft Corporation. All rights reserved. - true - Application Inspector - Microsoft - 0.0.0-placeholder - ApplicationInspector.Commands - Microsoft.ApplicationInspector.Commands - Microsoft - true - 0.0.0 - 0.0.0 - false - LICENSE.txt - icon-128.png - true - snupkg - enable - 10.0 - net6.0;netstandard2.1 - + + Library + Microsoft.CST.ApplicationInspector.Commands + 0.0.0-placeholder + https://github.com/microsoft/ApplicationInspector + https://github.com/microsoft/ApplicationInspector + Security Static Analyzer + Microsoft Application Inspector is a software source code analysis tool that helps identify and surface well-known features and other interesting characteristics of source code to aid in determining what the software is or what it does. + © Microsoft Corporation. All rights reserved. + true + Application Inspector + Microsoft + 0.0.0-placeholder + ApplicationInspector.Commands + Microsoft.ApplicationInspector.Commands + Microsoft + true + 0.0.0 + 0.0.0 + false + LICENSE.txt + icon-128.png + true + snupkg + enable + 10.0 + net6.0;netstandard2.1 + + + + + + + + + + TRACE + AnyCPU + false + 1701;1702;2225 + + + + + - - + - - - - TRACE - AnyCPU - false - 1701;1702;2225 - - - - + + + + + + + + + + - - - + - - - - - - - - - - - - + + + True + True + Resources.resx + + - - - True - True - Resources.resx - - + + + ResXFileCodeGenerator + Resources.Designer.cs + + - - - ResXFileCodeGenerator - Resources.Designer.cs - - + + + true + ApplicationInspector.Common.dll + + + - - - true - ApplicationInspector.Common.dll - - - - - - - - - + + + + + diff --git a/AppInspector/Commands/AnalyzeCommand.cs b/AppInspector/Commands/AnalyzeCommand.cs index 5e9c12e..c3b2be6 100644 --- a/AppInspector/Commands/AnalyzeCommand.cs +++ b/AppInspector/Commands/AnalyzeCommand.cs @@ -1,493 +1,334 @@ // Copyright (C) Microsoft. All rights reserved. // Licensed under the MIT License. See LICENSE.txt in the project root for license information. +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; using System.Globalization; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using GlobExpressions; +using Microsoft.ApplicationInspector.Common; +using Microsoft.ApplicationInspector.RulesEngine; +using Microsoft.CST.RecursiveExtractor; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Newtonsoft.Json; +using ShellProgressBar; -namespace Microsoft.ApplicationInspector.Commands +namespace Microsoft.ApplicationInspector.Commands; + +/// +/// Options specific to analyze operation not to be confused with CLIAnalyzeCmdOptions which include CLI only args +/// +public class AnalyzeOptions { - using GlobExpressions; - using Microsoft.ApplicationInspector.RulesEngine; - using Microsoft.CST.RecursiveExtractor; - using Newtonsoft.Json; - using ShellProgressBar; - using System; - using System.Collections.Concurrent; - using System.Collections.Generic; - using System.Diagnostics; - using System.IO; - using System.Linq; - using System.Threading; - using System.Threading.Tasks; - using Microsoft.ApplicationInspector.Common; - using Microsoft.Extensions.Logging; - using Microsoft.Extensions.Logging.Abstractions; + public IEnumerable SourcePath { get; set; } = Array.Empty(); + public string? CustomRulesPath { get; set; } + public bool IgnoreDefaultRules { get; set; } + public IEnumerable ConfidenceFilters { get; set; } = new[] { Confidence.High, Confidence.Medium }; + + public IEnumerable SeverityFilters { get; set; } = new[] + { Severity.Critical | Severity.Important | Severity.Moderate | Severity.BestPractice | Severity.ManualReview }; + + public IEnumerable FilePathExclusions { get; set; } = Array.Empty(); + public bool SingleThread { get; set; } /// - /// Options specific to analyze operation not to be confused with CLIAnalyzeCmdOptions which include CLI only args + /// Treat files as if they were + /// when determining if tags should apply. /// - public class AnalyzeOptions + public bool AllowAllTagsInBuildFiles { get; set; } + + /// + /// If enabled, will not show the progress bar interface. + /// + public bool NoShowProgress { get; set; } = true; + + public bool TagsOnly { get; set; } + + /// + /// Amount of time in ms to allow to process each file. Not supported in async operations. + /// + public int FileTimeOut { get; set; } = 0; + + /// + /// Overall amount of time in ms to allow for processing. Not supported in async operations. + /// + public int ProcessingTimeOut { get; set; } = 0; + + public int ContextLines { get; set; } = 3; + public bool ScanUnknownTypes { get; set; } + public bool NoFileMetadata { get; set; } + + /// + /// If non-zero, and is not set, will ignore matches if all of the matches tags have already + /// been found the set value number of times. + /// + public int MaxNumMatchesPerTag { get; set; } = 0; + + public string? CustomCommentsPath { get; set; } + public string? CustomLanguagesPath { get; set; } + + /// + /// If set, will not crawl archives to scan the contents of the contained files. + /// + public bool DisableCrawlArchives { get; set; } + + /// + /// If is not set, will restrict the amount of time allowed to extract each + /// archive. Not supported in async operations. + /// + public int EnumeratingTimeout { get; set; } + + /// + /// By default, custom rules are verified before running. + /// + public bool DisableCustomRuleVerification { get; set; } + + /// + /// By default, rules must have unique IDs. + /// + public bool DisableRequireUniqueIds { get; set; } + + /// + /// Return a success error code when no matches were found but operation was apparently successful. Useful for CI + /// scenarios + /// + public bool SuccessErrorCodeOnNoMatches { get; set; } + + public bool RequireMustMatch { get; set; } + public bool RequireMustNotMatch { get; set; } +} + +/// +/// Result of Analyze command GetResult() operation +/// +public class AnalyzeResult : Result +{ + public enum ExitCode { - public IEnumerable SourcePath { get; set; } = Array.Empty(); - public string? CustomRulesPath { get; set; } - public bool IgnoreDefaultRules { get; set; } - public IEnumerable ConfidenceFilters { get; set; } = new[] { Confidence.High, Confidence.Medium }; - public IEnumerable SeverityFilters { get; set; } = new[] { Severity.Critical | Severity.Important | Severity.Moderate | Severity.BestPractice | Severity.ManualReview }; - public IEnumerable FilePathExclusions { get; set; } = Array.Empty(); - public bool SingleThread { get; set; } - /// - /// Treat files as if they were when determining if tags should apply. - /// - public bool AllowAllTagsInBuildFiles { get; set; } - /// - /// If enabled, will not show the progress bar interface. - /// - public bool NoShowProgress { get; set; } = true; - public bool TagsOnly { get; set; } - /// - /// Amount of time in ms to allow to process each file. Not supported in async operations. - /// - public int FileTimeOut { get; set; } = 0; - /// - /// Overall amount of time in ms to allow for processing. Not supported in async operations. - /// - public int ProcessingTimeOut { get; set; } = 0; - public int ContextLines { get; set; } = 3; - public bool ScanUnknownTypes { get; set; } - public bool NoFileMetadata { get; set; } - /// - /// If non-zero, and is not set, will ignore matches if all of the matches tags have already been found the set value number of times. - /// - public int MaxNumMatchesPerTag { get; set; } = 0; - public string? CustomCommentsPath { get; set; } - public string? CustomLanguagesPath { get; set; } - /// - /// If set, will not crawl archives to scan the contents of the contained files. - /// - public bool DisableCrawlArchives { get; set; } - - /// - /// If is not set, will restrict the amount of time allowed to extract each archive. Not supported in async operations. - /// - public int EnumeratingTimeout { get; set; } - - /// - /// By default, custom rules are verified before running. - /// - public bool DisableCustomRuleVerification { get; set; } - /// - /// By default, rules must have unique IDs. - /// - public bool DisableRequireUniqueIds { get; set; } - - /// - /// Return a success error code when no matches were found but operation was apparently successful. Useful for CI scenarios - /// - public bool SuccessErrorCodeOnNoMatches { get; set; } - - public bool RequireMustMatch { get; set; } - public bool RequireMustNotMatch { get; set; } + Success = 0, + NoMatches = 1, + CriticalError = Utils.ExitCode.CriticalError, //ensure common value for final exit log mention + Canceled = 3, + TimedOut = 4 } - /// - /// Result of Analyze command GetResult() operation - /// - public class AnalyzeResult : Result + public AnalyzeResult() { - public enum ExitCode - { - Success = 0, - NoMatches = 1, - CriticalError = Common.Utils.ExitCode.CriticalError, //ensure common value for final exit log mention - Canceled = 3, - TimedOut = 4 - } - - [JsonProperty(Order = 2, PropertyName = "resultCode")] - public ExitCode ResultCode { get; set; } - - /// - /// Analyze command result object containing scan properties - /// - [JsonProperty(Order = 3, PropertyName = "metaData")] - public MetaData Metadata { get; set; } - - public AnalyzeResult() - { - Metadata = new MetaData("", "");//needed for serialization for other commands; replaced later - } + Metadata = new MetaData("", ""); //needed for serialization for other commands; replaced later } + [JsonProperty(Order = 2, PropertyName = "resultCode")] + public ExitCode ResultCode { get; set; } + /// - /// Analyze operation for setup and processing of results from Rules Engine + /// Analyze command result object containing scan properties /// - public class AnalyzeCommand + [JsonProperty(Order = 3, PropertyName = "metaData")] + public MetaData Metadata { get; set; } +} + +/// +/// Analyze operation for setup and processing of results from Rules Engine +/// +public class AnalyzeCommand +{ + private const int ProgressBarUpdateDelay = 100; + private readonly Confidence _confidence = Confidence.Unspecified; + + private readonly List _fileExclusionList = new(); + private readonly ILogger _logger; + private readonly ILoggerFactory? _loggerFactory; + private readonly AnalyzeOptions _options; //copy of incoming caller options + private readonly Severity _severity = Severity.Unspecified; + private readonly List _srcfileList = new(); + private readonly Languages _languages = new(); + private readonly MetaDataHelper _metaDataHelper; //wrapper containing MetaData object to be assigned to result + private readonly RuleProcessor _rulesProcessor; + + /// + /// Constructor for AnalyzeCommand. + /// + /// The to use for this analysis. + /// The use for log messages. + public AnalyzeCommand(AnalyzeOptions opt, ILoggerFactory? loggerFactory = null) { - private readonly ILoggerFactory? _loggerFactory; - private readonly ILogger _logger; - private readonly List _srcfileList = new(); - private MetaDataHelper _metaDataHelper; //wrapper containing MetaData object to be assigned to result - private Languages _languages = new(); - private RuleProcessor _rulesProcessor; - private DateTime DateScanned { get; } - - private const int ProgressBarUpdateDelay = 100; + _loggerFactory = loggerFactory ?? NullLoggerFactory.Instance; + _logger = _loggerFactory?.CreateLogger() ?? NullLogger.Instance; + _options = opt; - private readonly List _fileExclusionList = new(); - private readonly Confidence _confidence = Confidence.Unspecified; - private readonly AnalyzeOptions _options; //copy of incoming caller options - private readonly Severity _severity = Severity.Unspecified; + _fileExclusionList = opt.FilePathExclusions.Any(x => !x.Equals("none")) + ? opt.FilePathExclusions.Select(x => new Glob(x)).ToList() + : new List(); - /// - /// Constructor for AnalyzeCommand. - /// - /// The to use for this analysis. - /// The use for log messages. - public AnalyzeCommand(AnalyzeOptions opt, ILoggerFactory? loggerFactory = null) + //create metadata helper to wrap and help populate metadata from scan + _metaDataHelper = new MetaDataHelper(string.Join(',', _options.SourcePath)); + DateScanned = DateTime.Now; + foreach (var confidence in _options.ConfidenceFilters) _confidence |= confidence; + foreach (var severity in _options.SeverityFilters) _severity |= severity; + + _logger.LogTrace("AnalyzeCommand::ConfigSourcetoScan"); + + if (!_options.SourcePath.Any()) + throw new OpException(MsgHelp.FormatString(MsgHelp.ID.CMD_REQUIRED_ARG_MISSING, "SourcePath")); + + foreach (var entry in _options.SourcePath) + if (Directory.Exists(entry)) + _srcfileList.AddRange(Directory.EnumerateFiles(entry, "*.*", SearchOption.AllDirectories)); + else if (File.Exists(entry)) + _srcfileList.Add(entry); + else + throw new OpException(MsgHelp.FormatString(MsgHelp.ID.CMD_INVALID_FILE_OR_DIR, entry)); + if (_srcfileList.Count == 0) + throw new OpException(MsgHelp.FormatString(MsgHelp.ID.CMD_NO_FILES_IN_SOURCE, + string.Join(',', _options.SourcePath))); + + _logger.LogTrace("AnalyzeCommand::ConfigRules"); + + if (!string.IsNullOrEmpty(_options.CustomCommentsPath) || !string.IsNullOrEmpty(_options.CustomLanguagesPath)) + _languages = Languages.FromConfigurationFiles(_loggerFactory, _options.CustomCommentsPath, + _options.CustomLanguagesPath); + + RuleSet? rulesSet = null; + + if (!_options.IgnoreDefaultRules) rulesSet = RuleSetUtils.GetDefaultRuleSet(_loggerFactory); + + if (!string.IsNullOrEmpty(_options.CustomRulesPath)) { - _loggerFactory = loggerFactory ?? NullLoggerFactory.Instance; - _logger = _loggerFactory?.CreateLogger() ?? NullLogger.Instance; - _options = opt; - - _fileExclusionList = opt.FilePathExclusions.Any(x => !x.Equals("none")) ? opt.FilePathExclusions.Select(x => new Glob(x)).ToList() : new List(); - - //create metadata helper to wrap and help populate metadata from scan - _metaDataHelper = new MetaDataHelper(string.Join(',', _options.SourcePath)); - DateScanned = DateTime.Now; - foreach(Confidence confidence in _options.ConfidenceFilters) - { - _confidence |= confidence; - } - foreach(Severity severity in _options.SeverityFilters) - { - _severity |= severity; - } - - _logger.LogTrace("AnalyzeCommand::ConfigSourcetoScan"); - - if (!_options.SourcePath.Any()) - { - throw new OpException(MsgHelp.FormatString(MsgHelp.ID.CMD_REQUIRED_ARG_MISSING, "SourcePath")); - } - - foreach(var entry in _options.SourcePath) - { - if (Directory.Exists(entry)) - { - _srcfileList.AddRange(Directory.EnumerateFiles(entry, "*.*", SearchOption.AllDirectories)); - } - else if (File.Exists(entry)) - { - _srcfileList.Add(entry); - } - else - { - throw new OpException(MsgHelp.FormatString(MsgHelp.ID.CMD_INVALID_FILE_OR_DIR, entry)); - } - } - if (_srcfileList.Count == 0) - { - throw new OpException(MsgHelp.FormatString(MsgHelp.ID.CMD_NO_FILES_IN_SOURCE, string.Join(',', _options.SourcePath))); - } - - _logger.LogTrace("AnalyzeCommand::ConfigRules"); - - if (!string.IsNullOrEmpty(_options.CustomCommentsPath) || !string.IsNullOrEmpty(_options.CustomLanguagesPath)) - { - _languages = Languages.FromConfigurationFiles(_loggerFactory, _options.CustomCommentsPath, _options.CustomLanguagesPath); - } - - RuleSet? rulesSet = null; - - if (!_options.IgnoreDefaultRules) - { - rulesSet = RuleSetUtils.GetDefaultRuleSet(_loggerFactory); - } - - if (!string.IsNullOrEmpty(_options.CustomRulesPath)) - { - rulesSet ??= new RuleSet(_loggerFactory); - RulesVerifierOptions rulesVerifierOptions = new() - { - LanguageSpecs = _languages, - LoggerFactory = _loggerFactory, - DisableRequireUniqueIds = _options.DisableRequireUniqueIds, - RequireMustMatch = _options.RequireMustMatch, - RequireMustNotMatch = _options.RequireMustNotMatch - }; - RulesVerifier verifier = new(rulesVerifierOptions); - bool anyFails = false; - if (Directory.Exists(_options.CustomRulesPath)) - { - foreach (string filename in Directory.EnumerateFileSystemEntries(_options.CustomRulesPath, "*.json", - SearchOption.AllDirectories)) - { - VerifyFile(filename); - } - } - else if (File.Exists(_options.CustomRulesPath)) - { - VerifyFile(_options.CustomRulesPath); - } - else - { - throw new OpException(MsgHelp.FormatString(MsgHelp.ID.CMD_INVALID_RULE_PATH, _options.CustomRulesPath)); - } - - if (anyFails) - { - throw new OpException(MsgHelp.FormatString(MsgHelp.ID.VERIFY_RULE_LOADFILE_FAILED, _options.CustomRulesPath)); - } - - void VerifyFile(string filename) - { - if (!_options.DisableCustomRuleVerification) - { - RulesVerifierResult verification = verifier.Verify(_options.CustomRulesPath); - if (!verification.Verified) - { - anyFails = true; - } - else - { - rulesSet.AddFile(filename); - } - } - else - { - rulesSet.AddFile(filename); - } - } - } - - //error check based on ruleset not path enumeration - if (rulesSet == null || !rulesSet.Any()) - { - throw new OpException(MsgHelp.GetString(MsgHelp.ID.CMD_NORULES_SPECIFIED)); - } - - //instantiate a RuleProcessor with the added rules and exception for dependency - RuleProcessorOptions rpo = new() + rulesSet ??= new RuleSet(_loggerFactory); + RulesVerifierOptions rulesVerifierOptions = new() { + LanguageSpecs = _languages, LoggerFactory = _loggerFactory, - AllowAllTagsInBuildFiles = _options.AllowAllTagsInBuildFiles, - ConfidenceFilter = _confidence, - SeverityFilter = _severity, - Parallel = !_options.SingleThread, - Languages = _languages + DisableRequireUniqueIds = _options.DisableRequireUniqueIds, + RequireMustMatch = _options.RequireMustMatch, + RequireMustNotMatch = _options.RequireMustNotMatch + }; + RulesVerifier verifier = new(rulesVerifierOptions); + var anyFails = false; + if (Directory.Exists(_options.CustomRulesPath)) + foreach (var filename in Directory.EnumerateFileSystemEntries(_options.CustomRulesPath, "*.json", + SearchOption.AllDirectories)) + VerifyFile(filename); + else if (File.Exists(_options.CustomRulesPath)) + VerifyFile(_options.CustomRulesPath); + else + throw new OpException(MsgHelp.FormatString(MsgHelp.ID.CMD_INVALID_RULE_PATH, _options.CustomRulesPath)); + + if (anyFails) + throw new OpException(MsgHelp.FormatString(MsgHelp.ID.VERIFY_RULE_LOADFILE_FAILED, + _options.CustomRulesPath)); + + void VerifyFile(string filename) + { + if (!_options.DisableCustomRuleVerification) + { + var verification = verifier.Verify(_options.CustomRulesPath); + if (!verification.Verified) + anyFails = true; + else + rulesSet.AddFile(filename); + } + else + { + rulesSet.AddFile(filename); + } + } + } + + //error check based on ruleset not path enumeration + if (rulesSet == null || !rulesSet.Any()) + throw new OpException(MsgHelp.GetString(MsgHelp.ID.CMD_NORULES_SPECIFIED)); + + //instantiate a RuleProcessor with the added rules and exception for dependency + RuleProcessorOptions rpo = new() + { + LoggerFactory = _loggerFactory, + AllowAllTagsInBuildFiles = _options.AllowAllTagsInBuildFiles, + ConfidenceFilter = _confidence, + SeverityFilter = _severity, + Parallel = !_options.SingleThread, + Languages = _languages + }; + + _rulesProcessor = new RuleProcessor(rulesSet, rpo); + } + + private DateTime DateScanned { get; } + + /// + /// Populate the MetaDataHelper with the data from the FileEntries + /// + /// + /// + /// + private AnalyzeResult.ExitCode PopulateRecords(CancellationToken cancellationToken, + IEnumerable? populatedEntries) + { + _logger.LogTrace("AnalyzeCommand::PopulateRecords"); + populatedEntries ??= EnumerateFileEntries(); + if (_metaDataHelper is null) + { + _logger.LogError("MetadataHelper is null"); + throw new NullReferenceException("_metaDataHelper"); + } + + if (_options.SingleThread) + foreach (var entry in populatedEntries) + { + if (cancellationToken.IsCancellationRequested) return AnalyzeResult.ExitCode.Canceled; + ProcessAndAddToMetadata(entry); + } + else + try + { + Parallel.ForEach(populatedEntries, new ParallelOptions { CancellationToken = cancellationToken }, + ProcessAndAddToMetadata); + } + catch (OperationCanceledException) + { + return AnalyzeResult.ExitCode.Canceled; + } + + return AnalyzeResult.ExitCode.Success; + + void ProcessAndAddToMetadata(FileEntry file) + { + FileRecord fileRecord = new() + { + FileName = file.FullPath, ModifyTime = file.ModifyTime, CreateTime = file.CreateTime, + AccessTime = file.AccessTime }; - _rulesProcessor = new RuleProcessor(rulesSet, rpo); - } + Stopwatch sw = new(); + sw.Start(); - /// - /// Populate the MetaDataHelper with the data from the FileEntries - /// - /// - /// - /// - private AnalyzeResult.ExitCode PopulateRecords(CancellationToken cancellationToken, IEnumerable? populatedEntries) - { - _logger.LogTrace("AnalyzeCommand::PopulateRecords"); - populatedEntries ??= EnumerateFileEntries(); - if (_metaDataHelper is null) + if (_fileExclusionList.Any(x => x.IsMatch(file.FullPath))) { - _logger.LogError("MetadataHelper is null"); - throw new NullReferenceException("_metaDataHelper"); - } - - if (_options.SingleThread) - { - foreach (FileEntry entry in populatedEntries) - { - if (cancellationToken.IsCancellationRequested) { return AnalyzeResult.ExitCode.Canceled; } - ProcessAndAddToMetadata(entry); - } + _logger.LogDebug(MsgHelp.GetString(MsgHelp.ID.ANALYZE_EXCLUDED_TYPE_SKIPPED), fileRecord.FileName); + fileRecord.Status = ScanState.Skipped; } else { - try + if (IsBinary(file.Content)) { - Parallel.ForEach(populatedEntries, new ParallelOptions() { CancellationToken = cancellationToken }, ProcessAndAddToMetadata); - } - catch (OperationCanceledException) - { - return AnalyzeResult.ExitCode.Canceled; - } - } - - return AnalyzeResult.ExitCode.Success; - - void ProcessAndAddToMetadata(FileEntry file) - { - FileRecord fileRecord = new() { FileName = file.FullPath, ModifyTime = file.ModifyTime, CreateTime = file.CreateTime, AccessTime = file.AccessTime }; - - Stopwatch sw = new(); - sw.Start(); - - if (_fileExclusionList.Any(x => x.IsMatch(file.FullPath))) - { - _logger.LogDebug(MsgHelp.GetString(MsgHelp.ID.ANALYZE_EXCLUDED_TYPE_SKIPPED), fileRecord.FileName); + _logger.LogDebug(MsgHelp.GetString(MsgHelp.ID.ANALYZE_EXCLUDED_BINARY), fileRecord.FileName); fileRecord.Status = ScanState.Skipped; } else { - if (IsBinary(file.Content)) + List results = new(); + + // Reusable parsing logic that is used in an anonymous task or called directly depending on timeout preference. + void ProcessLambda() { - _logger.LogDebug(MsgHelp.GetString(MsgHelp.ID.ANALYZE_EXCLUDED_BINARY), fileRecord.FileName); - fileRecord.Status = ScanState.Skipped; - } - else - { - List results = new(); - - // Reusable parsing logic that is used in an anonymous task or called directly depending on timeout preference. - void ProcessLambda() - { - _ = _metaDataHelper.FileExtensions.TryAdd(Path.GetExtension(file.FullPath).Replace('.', ' ').TrimStart(), 0); - - LanguageInfo languageInfo = new(); - - if (_languages.FromFileName(file.FullPath, ref languageInfo)) - { - _metaDataHelper.AddLanguage(languageInfo.Name); - } - else - { - _metaDataHelper.AddLanguage("Unknown"); - languageInfo = new LanguageInfo() { Extensions = new[] { Path.GetExtension(file.FullPath) }, Name = "Unknown" }; - if (!_options.ScanUnknownTypes) - { - fileRecord.Status = ScanState.Skipped; - } - } - - if (fileRecord.Status != ScanState.Skipped) - { - if (_options.TagsOnly) - { - results = _rulesProcessor.AnalyzeFile(file, languageInfo, _metaDataHelper.UniqueTags.Keys, -1); - } - else if (_options.MaxNumMatchesPerTag > 0) - { - results = _rulesProcessor.AnalyzeFile(file, languageInfo, _metaDataHelper.UniqueTags.Where(x => x.Value >= _options.MaxNumMatchesPerTag).Select(x => x.Key), _options.ContextLines); - } - else - { - results = _rulesProcessor.AnalyzeFile(file, languageInfo, null, _options.ContextLines); - } - } - } - - if (_options.FileTimeOut > 0) - { - using CancellationTokenSource cts = new(); - Task t = Task.Run(ProcessLambda, cts.Token); - try - { - if (!t.Wait(new TimeSpan(0, 0, 0, 0, _options.FileTimeOut))) - { - _logger.LogError("{Path} timed out", file.FullPath); - fileRecord.Status = ScanState.TimedOut; - cts.Cancel(); - } - else - { - fileRecord.Status = ScanState.Analyzed; - } - } - catch (Exception e) - { - _logger.LogDebug("Failed to analyze file {Path}. {Type}:{Message}. ({StackTrace}), fileRecord.FileName", file.FullPath, e.GetType(), e.Message, e.StackTrace); - fileRecord.Status = ScanState.Error; - } - } - else - { - ProcessLambda(); - fileRecord.Status = ScanState.Analyzed; - } - - if (results.Any()) - { - fileRecord.Status = ScanState.Affected; - fileRecord.NumFindings = results.Count; - } - foreach (MatchRecord matchRecord in results) - { - if (_options.TagsOnly) - { - _metaDataHelper.AddTagsFromMatchRecord(matchRecord); - } - else if (_options.MaxNumMatchesPerTag > 0) - { - if (matchRecord.Tags?.Any(x => _metaDataHelper.UniqueTags.TryGetValue(x, out int value) is bool foundValue && (!foundValue || foundValue && value < _options.MaxNumMatchesPerTag)) ?? false) - { - _metaDataHelper.AddMatchRecord(matchRecord); - } - } - else - { - _metaDataHelper.AddMatchRecord(matchRecord); - } - } - } - } - - sw.Stop(); - - fileRecord.ScanTime = sw.Elapsed; - - if (!_options.NoFileMetadata) - { - _metaDataHelper.Files.Add(fileRecord); - } - } - } - - /// - /// Populate the records in the metadata asynchronously. - /// - /// - /// Result code. - private async Task PopulateRecordsAsync(CancellationToken cancellationToken) - { - _logger.LogTrace("AnalyzeCommand::PopulateRecordsAsync"); - if (_metaDataHelper is null) - { - _logger.LogError("MetadataHelper is null"); - throw new NullReferenceException("_metaDataHelper"); - } - - await foreach (FileEntry entry in GetFileEntriesAsync()) - { - if (cancellationToken.IsCancellationRequested) { return AnalyzeResult.ExitCode.Canceled; } - await ProcessAndAddToMetadata(entry, cancellationToken); - } - - return AnalyzeResult.ExitCode.Success; - - async Task ProcessAndAddToMetadata(FileEntry file, CancellationToken cancellationToken) - { - FileRecord fileRecord = new() { FileName = file.FullPath, ModifyTime = file.ModifyTime, CreateTime = file.CreateTime, AccessTime = file.AccessTime }; - - Stopwatch sw = new(); - sw.Start(); - - if (_fileExclusionList.Any(x => x.IsMatch(file.FullPath))) - { - _logger.LogDebug(MsgHelp.GetString(MsgHelp.ID.ANALYZE_EXCLUDED_TYPE_SKIPPED), fileRecord.FileName); - fileRecord.Status = ScanState.Skipped; - } - else - { - if (IsBinary(file.Content)) - { - _logger.LogDebug(MsgHelp.GetString(MsgHelp.ID.ANALYZE_EXCLUDED_BINARY), fileRecord.FileName); - fileRecord.Status = ScanState.Skipped; - } - else - { - _ = _metaDataHelper.FileExtensions.TryAdd(Path.GetExtension(file.FullPath).Replace('.', ' ').TrimStart(), 0); + _ = _metaDataHelper.FileExtensions.TryAdd( + Path.GetExtension(file.FullPath).Replace('.', ' ').TrimStart(), 0); LanguageInfo languageInfo = new(); @@ -498,298 +339,394 @@ namespace Microsoft.ApplicationInspector.Commands else { _metaDataHelper.AddLanguage("Unknown"); - languageInfo = new LanguageInfo() { Extensions = new[] { Path.GetExtension(file.FullPath) }, Name = "Unknown" }; - if (!_options.ScanUnknownTypes) - { - fileRecord.Status = ScanState.Skipped; - } + languageInfo = new LanguageInfo + { Extensions = new[] { Path.GetExtension(file.FullPath) }, Name = "Unknown" }; + if (!_options.ScanUnknownTypes) fileRecord.Status = ScanState.Skipped; } if (fileRecord.Status != ScanState.Skipped) { - var contextLines = _options.TagsOnly ? -1 : _options.ContextLines; - var ignoredTags = _options.TagsOnly ? _metaDataHelper.UniqueTags.Keys : _options.MaxNumMatchesPerTag > 0 ? _metaDataHelper.UniqueTags.Where(x => x.Value < _options.MaxNumMatchesPerTag).Select(x => x.Key) : null; - var results = await _rulesProcessor.AnalyzeFileAsync(file, languageInfo, cancellationToken, ignoredTags, contextLines); - fileRecord.Status = ScanState.Analyzed; - - if (results.Any()) - { - fileRecord.Status = ScanState.Affected; - fileRecord.NumFindings = results.Count; - } - foreach (MatchRecord matchRecord in results) - { - if (_options.TagsOnly) - { - _metaDataHelper.AddTagsFromMatchRecord(matchRecord); - } - else if (_options.MaxNumMatchesPerTag > 0) - { - if (matchRecord.Tags?.Any(x => _metaDataHelper.UniqueTags.TryGetValue(x, out int value) is bool foundValue && (!foundValue || foundValue && value < _options.MaxNumMatchesPerTag)) ?? true) - { - _metaDataHelper.AddMatchRecord(matchRecord); - } - } - else - { - _metaDataHelper.AddMatchRecord(matchRecord); - } - } + if (_options.TagsOnly) + results = _rulesProcessor.AnalyzeFile(file, languageInfo, + _metaDataHelper.UniqueTags.Keys, -1); + else if (_options.MaxNumMatchesPerTag > 0) + results = _rulesProcessor.AnalyzeFile(file, languageInfo, + _metaDataHelper.UniqueTags.Where(x => x.Value >= _options.MaxNumMatchesPerTag) + .Select(x => x.Key), _options.ContextLines); + else + results = _rulesProcessor.AnalyzeFile(file, languageInfo, null, _options.ContextLines); } } - } - sw.Stop(); - - fileRecord.ScanTime = sw.Elapsed; - - _metaDataHelper.Files.Add(fileRecord); - } - } - - /// - /// Gets the FileEntries synchronously. - /// - /// An Enumerable of FileEntries. - private IEnumerable EnumerateFileEntries() - { - _logger.LogTrace("AnalyzeCommand::EnumerateFileEntries"); - - Extractor extractor = new(); - // For every file, if the file isn't excluded return it, and if it is track the exclusion in the metadata - foreach (var srcFile in _srcfileList) - { - if (_fileExclusionList.Any(x => x.IsMatch(srcFile))) - { - _metaDataHelper?.Metadata.Files.Add(new FileRecord() { FileName = srcFile, Status = ScanState.Skipped }); - } - else - { - Stream? contents = null; - try + if (_options.FileTimeOut > 0) { - contents = File.OpenRead(srcFile); - } - catch(Exception ex) - { - _logger.LogError("Failed to open source file '{Filename}' for reading. {Type}:{Message}", srcFile, ex.GetType().Name, ex.Message); - _metaDataHelper?.Metadata.Files.Add(new FileRecord() { FileName = srcFile, Status = ScanState.Error }); - } - if (contents != null) - { - if (_options.DisableCrawlArchives) + using CancellationTokenSource cts = new(); + var t = Task.Run(ProcessLambda, cts.Token); + try { - yield return new FileEntry(srcFile, contents); + if (!t.Wait(new TimeSpan(0, 0, 0, 0, _options.FileTimeOut))) + { + _logger.LogError("{Path} timed out", file.FullPath); + fileRecord.Status = ScanState.TimedOut; + cts.Cancel(); + } + else + { + fileRecord.Status = ScanState.Analyzed; + } + } + catch (Exception e) + { + _logger.LogDebug( + "Failed to analyze file {Path}. {Type}:{Message}. ({StackTrace}), fileRecord.FileName", + file.FullPath, e.GetType(), e.Message, e.StackTrace); + fileRecord.Status = ScanState.Error; + } + } + else + { + ProcessLambda(); + fileRecord.Status = ScanState.Analyzed; + } + + if (results.Any()) + { + fileRecord.Status = ScanState.Affected; + fileRecord.NumFindings = results.Count; + } + + foreach (var matchRecord in results) + if (_options.TagsOnly) + { + _metaDataHelper.AddTagsFromMatchRecord(matchRecord); + } + else if (_options.MaxNumMatchesPerTag > 0) + { + if (matchRecord.Tags?.Any(x => + _metaDataHelper.UniqueTags.TryGetValue(x, out var value) is bool foundValue && + (!foundValue || (foundValue && value < _options.MaxNumMatchesPerTag))) ?? + false) _metaDataHelper.AddMatchRecord(matchRecord); } else { - // Use MemoryStreamCutoff = 1 to force using FileStream with DeleteOnClose for backing, and avoid memory exhaustion. - ExtractorOptions opts = new() - { - Parallel = false, DenyFilters = _options.FilePathExclusions, MemoryStreamCutoff = 1 - }; - // This works if the contents contain any kind of file. - // If the file is an archive this gets all the entries it contains. - // If the file is not an archive, the stream is wrapped in a FileEntry container and yielded - foreach (FileEntry entry in extractor.Extract(srcFile, contents, opts)) - { - yield return entry; - } + _metaDataHelper.AddMatchRecord(matchRecord); } - } - - // Be sure to close the stream after we are done processing it. - contents?.Dispose(); } } + + sw.Stop(); + + fileRecord.ScanTime = sw.Elapsed; + + if (!_options.NoFileMetadata) _metaDataHelper.Files.Add(fileRecord); + } + } + + /// + /// Populate the records in the metadata asynchronously. + /// + /// + /// Result code. + private async Task PopulateRecordsAsync(CancellationToken cancellationToken) + { + _logger.LogTrace("AnalyzeCommand::PopulateRecordsAsync"); + if (_metaDataHelper is null) + { + _logger.LogError("MetadataHelper is null"); + throw new NullReferenceException("_metaDataHelper"); } - /// - /// Gets the FileEntries asynchronously. - /// - /// An enumeration of FileEntries - private async IAsyncEnumerable GetFileEntriesAsync() + await foreach (var entry in GetFileEntriesAsync()) { - _logger.LogTrace("AnalyzeCommand::GetFileEntriesAsync"); + if (cancellationToken.IsCancellationRequested) return AnalyzeResult.ExitCode.Canceled; + await ProcessAndAddToMetadata(entry, cancellationToken); + } - Extractor extractor = new(); - foreach (string srcFile in _srcfileList) + return AnalyzeResult.ExitCode.Success; + + async Task ProcessAndAddToMetadata(FileEntry file, CancellationToken cancellationToken) + { + FileRecord fileRecord = new() { - if (_fileExclusionList.Any(x => x.IsMatch(srcFile))) + FileName = file.FullPath, ModifyTime = file.ModifyTime, CreateTime = file.CreateTime, + AccessTime = file.AccessTime + }; + + Stopwatch sw = new(); + sw.Start(); + + if (_fileExclusionList.Any(x => x.IsMatch(file.FullPath))) + { + _logger.LogDebug(MsgHelp.GetString(MsgHelp.ID.ANALYZE_EXCLUDED_TYPE_SKIPPED), fileRecord.FileName); + fileRecord.Status = ScanState.Skipped; + } + else + { + if (IsBinary(file.Content)) { - _metaDataHelper?.Metadata.Files.Add(new FileRecord() { FileName = srcFile, Status = ScanState.Skipped }); + _logger.LogDebug(MsgHelp.GetString(MsgHelp.ID.ANALYZE_EXCLUDED_BINARY), fileRecord.FileName); + fileRecord.Status = ScanState.Skipped; } else { - await foreach (FileEntry entry in extractor.ExtractAsync(srcFile, new ExtractorOptions() { Parallel = false, DenyFilters = _options.FilePathExclusions, MemoryStreamCutoff = 1 })) + _ = _metaDataHelper.FileExtensions.TryAdd( + Path.GetExtension(file.FullPath).Replace('.', ' ').TrimStart(), 0); + + LanguageInfo languageInfo = new(); + + if (_languages.FromFileName(file.FullPath, ref languageInfo)) { - yield return entry; + _metaDataHelper.AddLanguage(languageInfo.Name); + } + else + { + _metaDataHelper.AddLanguage("Unknown"); + languageInfo = new LanguageInfo + { Extensions = new[] { Path.GetExtension(file.FullPath) }, Name = "Unknown" }; + if (!_options.ScanUnknownTypes) fileRecord.Status = ScanState.Skipped; + } + + if (fileRecord.Status != ScanState.Skipped) + { + var contextLines = _options.TagsOnly ? -1 : _options.ContextLines; + var ignoredTags = _options.TagsOnly ? _metaDataHelper.UniqueTags.Keys : + _options.MaxNumMatchesPerTag > 0 ? _metaDataHelper.UniqueTags + .Where(x => x.Value < _options.MaxNumMatchesPerTag).Select(x => x.Key) : null; + var results = await _rulesProcessor.AnalyzeFileAsync(file, languageInfo, cancellationToken, + ignoredTags, contextLines); + fileRecord.Status = ScanState.Analyzed; + + if (results.Any()) + { + fileRecord.Status = ScanState.Affected; + fileRecord.NumFindings = results.Count; + } + + foreach (var matchRecord in results) + if (_options.TagsOnly) + { + _metaDataHelper.AddTagsFromMatchRecord(matchRecord); + } + else if (_options.MaxNumMatchesPerTag > 0) + { + if (matchRecord.Tags?.Any(x => + _metaDataHelper.UniqueTags.TryGetValue(x, out var value) is bool foundValue && + (!foundValue || (foundValue && value < _options.MaxNumMatchesPerTag))) ?? + true) _metaDataHelper.AddMatchRecord(matchRecord); + } + else + { + _metaDataHelper.AddMatchRecord(matchRecord); + } } } } + + sw.Stop(); + + fileRecord.ScanTime = sw.Elapsed; + + _metaDataHelper.Files.Add(fileRecord); } + } + /// + /// Gets the FileEntries synchronously. + /// + /// An Enumerable of FileEntries. + private IEnumerable EnumerateFileEntries() + { + _logger.LogTrace("AnalyzeCommand::EnumerateFileEntries"); - // Follows Perl's model, if there are NULs or too many non printable characters, this is probably a binary file - private static bool IsBinary(Stream fileContents) - { - var numRead = 1; - var span = new Span(new byte[8192]); - var controlsEncountered = 0; - var maxControlsEncountered = (int)(0.3 * fileContents.Length); - while (numRead > 0) + Extractor extractor = new(); + // For every file, if the file isn't excluded return it, and if it is track the exclusion in the metadata + foreach (var srcFile in _srcfileList) + if (_fileExclusionList.Any(x => x.IsMatch(srcFile))) { - numRead = fileContents.Read(span); - for (var i = 0; i < numRead; i++) + _metaDataHelper?.Metadata.Files.Add(new FileRecord { FileName = srcFile, Status = ScanState.Skipped }); + } + else + { + Stream? contents = null; + try { - var ch = (char)span[i]; - if (ch == '\0') + contents = File.OpenRead(srcFile); + } + catch (Exception ex) + { + _logger.LogError("Failed to open source file '{Filename}' for reading. {Type}:{Message}", srcFile, + ex.GetType().Name, ex.Message); + _metaDataHelper?.Metadata.Files.Add(new FileRecord + { FileName = srcFile, Status = ScanState.Error }); + } + + if (contents != null) + { + if (_options.DisableCrawlArchives) + { + yield return new FileEntry(srcFile, contents); + } + else + { + // Use MemoryStreamCutoff = 1 to force using FileStream with DeleteOnClose for backing, and avoid memory exhaustion. + ExtractorOptions opts = new() + { + Parallel = false, DenyFilters = _options.FilePathExclusions, MemoryStreamCutoff = 1 + }; + // This works if the contents contain any kind of file. + // If the file is an archive this gets all the entries it contains. + // If the file is not an archive, the stream is wrapped in a FileEntry container and yielded + foreach (var entry in extractor.Extract(srcFile, contents, opts)) yield return entry; + } + } + + // Be sure to close the stream after we are done processing it. + contents?.Dispose(); + } + } + + /// + /// Gets the FileEntries asynchronously. + /// + /// An enumeration of FileEntries + private async IAsyncEnumerable GetFileEntriesAsync() + { + _logger.LogTrace("AnalyzeCommand::GetFileEntriesAsync"); + + Extractor extractor = new(); + foreach (var srcFile in _srcfileList) + if (_fileExclusionList.Any(x => x.IsMatch(srcFile))) + _metaDataHelper?.Metadata.Files.Add(new FileRecord { FileName = srcFile, Status = ScanState.Skipped }); + else + await foreach (var entry in extractor.ExtractAsync(srcFile, + new ExtractorOptions + { + Parallel = false, DenyFilters = _options.FilePathExclusions, + MemoryStreamCutoff = 1 + })) + yield return entry; + } + + + // Follows Perl's model, if there are NULs or too many non printable characters, this is probably a binary file + private static bool IsBinary(Stream fileContents) + { + var numRead = 1; + var span = new Span(new byte[8192]); + var controlsEncountered = 0; + var maxControlsEncountered = (int)(0.3 * fileContents.Length); + while (numRead > 0) + { + numRead = fileContents.Read(span); + for (var i = 0; i < numRead; i++) + { + var ch = (char)span[i]; + if (ch == '\0') + { + fileContents.Position = 0; + return true; + } + + if (char.IsControl(ch) && !char.IsWhiteSpace(ch)) + { + if (++controlsEncountered > maxControlsEncountered) { fileContents.Position = 0; return true; } - else if (char.IsControl(ch) && !char.IsWhiteSpace(ch)) - { - if (++controlsEncountered > maxControlsEncountered) - { - fileContents.Position = 0; - return true; - } - } } - } - fileContents.Position = 0; - return false; } - /// - /// Perform Analysis and get the result Asynchronously - /// - /// Cancellation token to stop analysis and return results found so far. - /// - public async Task GetResultAsync(CancellationToken? cancellationToken = null) + fileContents.Position = 0; + return false; + } + + /// + /// Perform Analysis and get the result Asynchronously + /// + /// Cancellation token to stop analysis and return results found so far. + /// + public async Task GetResultAsync(CancellationToken? cancellationToken = null) + { + _logger.LogTrace("AnalyzeCommand::GetResultAsync"); + _logger.LogInformation(MsgHelp.GetString(MsgHelp.ID.CMD_RUNNING), "Analyze"); + cancellationToken ??= new CancellationToken(); + if (_metaDataHelper is null) { - _logger.LogTrace("AnalyzeCommand::GetResultAsync"); - _logger.LogInformation(MsgHelp.GetString(MsgHelp.ID.CMD_RUNNING), "Analyze"); - cancellationToken ??= new CancellationToken(); - if (_metaDataHelper is null) - { - _logger.LogError("MetadataHelper is null"); - throw new NullReferenceException("_metaDataHelper"); - } - - AnalyzeResult analyzeResult = new() - { - AppVersion = Common.Utils.GetVersionString() - }; - - _ = await PopulateRecordsAsync(cancellationToken.Value); - - if (!_options.SuccessErrorCodeOnNoMatches) - { - if (!_options.NoFileMetadata && _metaDataHelper.Files.All(x => x.Status == ScanState.Skipped)) - { - _logger.LogError(MsgHelp.GetString(MsgHelp.ID.ANALYZE_NOSUPPORTED_FILETYPES)); - analyzeResult.ResultCode = AnalyzeResult.ExitCode.NoMatches; - } - else if (!_metaDataHelper.HasFindings) - { - _logger.LogError(MsgHelp.GetString(MsgHelp.ID.ANALYZE_NOPATTERNS)); - analyzeResult.ResultCode = AnalyzeResult.ExitCode.NoMatches; - } - } - - if (_metaDataHelper is {Metadata: { }}) - { - _metaDataHelper.Metadata.DateScanned = DateScanned.ToString(CultureInfo.InvariantCulture); - _metaDataHelper.PrepareReport(); - analyzeResult.Metadata = _metaDataHelper.Metadata; //replace instance with metadatahelper processed one - analyzeResult.ResultCode = AnalyzeResult.ExitCode.Success; - } - - if (cancellationToken.Value.IsCancellationRequested) - { - analyzeResult.ResultCode = AnalyzeResult.ExitCode.Canceled; - } - - return analyzeResult; + _logger.LogError("MetadataHelper is null"); + throw new NullReferenceException("_metaDataHelper"); } - /// - /// Main entry point to start analysis from CLI; handles setting up rules, directory enumeration - /// file type detection and handoff - /// Pre: All Configure Methods have been called already and we are ready to SCAN - /// - /// - public AnalyzeResult GetResult() + AnalyzeResult analyzeResult = new() { - _logger.LogTrace("AnalyzeCommand::GetResultAsync"); - _logger.LogInformation(MsgHelp.GetString(MsgHelp.ID.CMD_RUNNING), "Analyze"); - if (_metaDataHelper is null) - { - _logger.LogError("MetadataHelper is null"); - throw new NullReferenceException("_metaDataHelper"); - } - AnalyzeResult analyzeResult = new() - { - AppVersion = Utils.GetVersionString() - }; + AppVersion = Utils.GetVersionString() + }; - var timedOut = false; - - // If progress display is disabled then we can pass the enumerable directly - if (_options.NoShowProgress) - { - IEnumerable enumeratedEntries = Array.Empty(); - if (_options.EnumeratingTimeout > 0) - { - using CancellationTokenSource cts = new(); - var t = Task.Run(() => - { - try - { - enumeratedEntries = EnumerateFileEntries().ToList(); - } - catch (OverflowException e) - { - _logger.LogError( - "Overflowed while extracting file entries. Check the input for quines or zip bombs. {Message}", - e.Message); - } - catch (Exception e) - { - _logger.LogError( - "Unexpected error while enumerating files. {Type}:{Message}", - e.GetType().Name, e.Message); - } - }, cts.Token); - if (!t.Wait(new TimeSpan(0, 0, 0, 0, _options.EnumeratingTimeout))) - { - cts.Cancel(); - } - } - else - { - enumeratedEntries = EnumerateFileEntries(); - } - DoProcessing(enumeratedEntries); - } - else - { - bool doneEnumerating = false; - bool enumeratingTimedOut = false; - ConcurrentBag fileQueue = new(); + _ = await PopulateRecordsAsync(cancellationToken.Value); + if (!_options.SuccessErrorCodeOnNoMatches) + { + if (!_options.NoFileMetadata && _metaDataHelper.Files.All(x => x.Status == ScanState.Skipped)) + { + _logger.LogError(MsgHelp.GetString(MsgHelp.ID.ANALYZE_NOSUPPORTED_FILETYPES)); + analyzeResult.ResultCode = AnalyzeResult.ExitCode.NoMatches; + } + else if (!_metaDataHelper.HasFindings) + { + _logger.LogError(MsgHelp.GetString(MsgHelp.ID.ANALYZE_NOPATTERNS)); + analyzeResult.ResultCode = AnalyzeResult.ExitCode.NoMatches; + } + } + + if (_metaDataHelper is { Metadata: { } }) + { + _metaDataHelper.Metadata.DateScanned = DateScanned.ToString(CultureInfo.InvariantCulture); + _metaDataHelper.PrepareReport(); + analyzeResult.Metadata = _metaDataHelper.Metadata; //replace instance with metadatahelper processed one + analyzeResult.ResultCode = AnalyzeResult.ExitCode.Success; + } + + if (cancellationToken.Value.IsCancellationRequested) analyzeResult.ResultCode = AnalyzeResult.ExitCode.Canceled; + + return analyzeResult; + } + + /// + /// Main entry point to start analysis from CLI; handles setting up rules, directory enumeration + /// file type detection and handoff + /// Pre: All Configure Methods have been called already and we are ready to SCAN + /// + /// + public AnalyzeResult GetResult() + { + _logger.LogTrace("AnalyzeCommand::GetResultAsync"); + _logger.LogInformation(MsgHelp.GetString(MsgHelp.ID.CMD_RUNNING), "Analyze"); + if (_metaDataHelper is null) + { + _logger.LogError("MetadataHelper is null"); + throw new NullReferenceException("_metaDataHelper"); + } + + AnalyzeResult analyzeResult = new() + { + AppVersion = Utils.GetVersionString() + }; + + var timedOut = false; + + // If progress display is disabled then we can pass the enumerable directly + if (_options.NoShowProgress) + { + IEnumerable enumeratedEntries = Array.Empty(); + if (_options.EnumeratingTimeout > 0) + { using CancellationTokenSource cts = new(); - var t = Task.Run(() => + var t = Task.Run(() => { try { - foreach (FileEntry entry in EnumerateFileEntries()) - { - fileQueue.Add(entry); - } + enumeratedEntries = EnumerateFileEntries().ToList(); } catch (OverflowException e) { @@ -803,156 +740,193 @@ namespace Microsoft.ApplicationInspector.Commands "Unexpected error while enumerating files. {Type}:{Message}", e.GetType().Name, e.Message); } - finally - { - doneEnumerating = true; - } }, cts.Token); - if (_options.EnumeratingTimeout > 0) - { - if (!t.Wait(new TimeSpan(0, 0, 0, 0, _options.EnumeratingTimeout))) - { - enumeratingTimedOut = true; - doneEnumerating = true; - cts.Cancel(); - } - } - - ProgressBarOptions options = new() - { - ForegroundColor = ConsoleColor.Yellow, - ForegroundColorDone = ConsoleColor.DarkGreen, - BackgroundColor = ConsoleColor.DarkGray, - ForegroundColorError = ConsoleColor.Red, - BackgroundCharacter = '\u2593', - DisableBottomPercentage = true - }; - - using (IndeterminateProgressBar pbar = new("Enumerating Files.", options)) - { - while (!doneEnumerating) - { - Thread.Sleep(ProgressBarUpdateDelay); - pbar.Message = $"Enumerating Files. {fileQueue.Count} Discovered so far."; - } - - pbar.Message = enumeratingTimedOut - ? $"Enumerating Files Timed Out. {fileQueue.Count} Discovered." - : $"Enumerating Files Completed. {fileQueue.Count} Discovered."; - - pbar.ObservedError = enumeratingTimedOut; - pbar.Finished(); - } - Console.WriteLine(); - var doneProcessing = false; - - ProgressBarOptions options2 = new() - { - ForegroundColor = ConsoleColor.Yellow, - ForegroundColorDone = ConsoleColor.DarkGreen, - BackgroundColor = ConsoleColor.DarkGray, - ForegroundColorError = ConsoleColor.Red, - BackgroundCharacter = '\u2593', - DisableBottomPercentage = false, - ShowEstimatedDuration = true - }; - using (ProgressBar progressBar = new(fileQueue.Count, $"Analyzing Files.", options2)) - { - Stopwatch sw = new(); - sw.Start(); - _ = Task.Factory.StartNew(() => - { - try - { - DoProcessing(fileQueue); - } - catch (Exception e) - { - _logger.LogError("Unexpected exception while processing. {Type}:{Message}", - e.GetType().Name, e.Message); - } - finally - { - doneProcessing = true; - } - }); - - while (!doneProcessing) - { - Thread.Sleep(ProgressBarUpdateDelay); - var current = _metaDataHelper.Files.Count; - var timePerRecord = sw.Elapsed.TotalMilliseconds / current; - var millisExpected = (int)(timePerRecord * (fileQueue.Count - current)); - TimeSpan timeExpected = new(0, 0, 0, 0, millisExpected); - progressBar.Tick(_metaDataHelper.Files.Count, timeExpected, $"Analyzing Files. {_metaDataHelper.Matches.Count} Matches. {_metaDataHelper.Files.Count(x => x.Status == ScanState.Skipped)} Files Skipped. {_metaDataHelper.Files.Count(x => x.Status == ScanState.TimedOut)} Timed Out. {_metaDataHelper.Files.Count(x => x.Status == ScanState.Affected)} Affected. {_metaDataHelper.Files.Count(x => x.Status == ScanState.Analyzed)} Not Affected."); - } - - // If processing timed out it will have set the status to timeout skipped. - timedOut = _metaDataHelper.Files.Any(x => x.Status == ScanState.TimeOutSkipped); - - progressBar.Message = timedOut ? - $"Overall processing timeout hit. {_metaDataHelper.Matches.Count} Matches. {_metaDataHelper.Files.Count(x => x.Status == ScanState.Skipped)} Files Skipped. {_metaDataHelper.Files.Count(x => x.Status == ScanState.TimedOut)} Timed Out. {_metaDataHelper.Files.Count(x => x.Status == ScanState.Affected)} Affected. {_metaDataHelper.Files.Count(x => x.Status == ScanState.Analyzed)} Not Affected." : - $"{_metaDataHelper.Matches.Count} Matches. {_metaDataHelper.Files.Count(x => x.Status == ScanState.Skipped)} Files Skipped. {_metaDataHelper.Files.Count(x => x.Status == ScanState.TimedOut)} Timed Out. {_metaDataHelper.Files.Count(x => x.Status == ScanState.Affected)} Affected. {_metaDataHelper.Files.Count(x => x.Status == ScanState.Analyzed)} Not Affected."; - progressBar.Tick(progressBar.MaxTicks); - } - Console.WriteLine(); - } - - //wrapup result status - if (!_options.SuccessErrorCodeOnNoMatches) - { - if (!_options.NoFileMetadata && _metaDataHelper.Files.All(x => x.Status == ScanState.Skipped)) - { - _logger.LogError(MsgHelp.GetString(MsgHelp.ID.ANALYZE_NOSUPPORTED_FILETYPES)); - analyzeResult.ResultCode = AnalyzeResult.ExitCode.NoMatches; - } - else if (!_metaDataHelper.HasFindings) - { - _logger.LogError(MsgHelp.GetString(MsgHelp.ID.ANALYZE_NOPATTERNS)); - analyzeResult.ResultCode = AnalyzeResult.ExitCode.NoMatches; - } - } - - _metaDataHelper.Metadata.DateScanned = DateScanned.ToString(CultureInfo.InvariantCulture); - _metaDataHelper.PrepareReport(); - analyzeResult.Metadata = _metaDataHelper.Metadata; //replace instance with metadatahelper processed one - - if (timedOut) - { - _logger.LogError(MsgHelp.GetString(MsgHelp.ID.ANALYZE_PROCESSING_TIMED_OUT)); - analyzeResult.Metadata.TimedOut = true; - analyzeResult.ResultCode = AnalyzeResult.ExitCode.TimedOut; - } - - return analyzeResult; - } - - /// - /// Do processing of the given file entries - return if the timeout was hit - /// - /// - /// - /// True when the timeout was hit - private void DoProcessing(IEnumerable fileEntries, CancellationTokenSource? cts = null) - { - cts ??= new CancellationTokenSource(); - if (_options.ProcessingTimeOut > 0) - { - var t = Task.Run(() => PopulateRecords(cts.Token, fileEntries), cts.Token); - if (!t.Wait(new TimeSpan(0, 0, 0, 0, _options.ProcessingTimeOut))) - { - cts.Cancel(); - // Populate skips for all the entries we didn't process - foreach (FileEntry entry in fileEntries.Where(x => _metaDataHelper.Files.All(y => x.FullPath != y.FileName))) - { - _metaDataHelper.Files.Add(new FileRecord() { AccessTime = entry.AccessTime, CreateTime = entry.CreateTime, ModifyTime = entry.ModifyTime, FileName = entry.FullPath, Status = ScanState.TimeOutSkipped }); - } - } + if (!t.Wait(new TimeSpan(0, 0, 0, 0, _options.EnumeratingTimeout))) cts.Cancel(); } else { - PopulateRecords(cts.Token, fileEntries); + enumeratedEntries = EnumerateFileEntries(); } + + DoProcessing(enumeratedEntries); + } + else + { + var doneEnumerating = false; + var enumeratingTimedOut = false; + ConcurrentBag fileQueue = new(); + + using CancellationTokenSource cts = new(); + var t = Task.Run(() => + { + try + { + foreach (var entry in EnumerateFileEntries()) fileQueue.Add(entry); + } + catch (OverflowException e) + { + _logger.LogError( + "Overflowed while extracting file entries. Check the input for quines or zip bombs. {Message}", + e.Message); + } + catch (Exception e) + { + _logger.LogError( + "Unexpected error while enumerating files. {Type}:{Message}", + e.GetType().Name, e.Message); + } + finally + { + doneEnumerating = true; + } + }, cts.Token); + if (_options.EnumeratingTimeout > 0) + if (!t.Wait(new TimeSpan(0, 0, 0, 0, _options.EnumeratingTimeout))) + { + enumeratingTimedOut = true; + doneEnumerating = true; + cts.Cancel(); + } + + ProgressBarOptions options = new() + { + ForegroundColor = ConsoleColor.Yellow, + ForegroundColorDone = ConsoleColor.DarkGreen, + BackgroundColor = ConsoleColor.DarkGray, + ForegroundColorError = ConsoleColor.Red, + BackgroundCharacter = '\u2593', + DisableBottomPercentage = true + }; + + using (IndeterminateProgressBar pbar = new("Enumerating Files.", options)) + { + while (!doneEnumerating) + { + Thread.Sleep(ProgressBarUpdateDelay); + pbar.Message = $"Enumerating Files. {fileQueue.Count} Discovered so far."; + } + + pbar.Message = enumeratingTimedOut + ? $"Enumerating Files Timed Out. {fileQueue.Count} Discovered." + : $"Enumerating Files Completed. {fileQueue.Count} Discovered."; + + pbar.ObservedError = enumeratingTimedOut; + pbar.Finished(); + } + + Console.WriteLine(); + var doneProcessing = false; + + ProgressBarOptions options2 = new() + { + ForegroundColor = ConsoleColor.Yellow, + ForegroundColorDone = ConsoleColor.DarkGreen, + BackgroundColor = ConsoleColor.DarkGray, + ForegroundColorError = ConsoleColor.Red, + BackgroundCharacter = '\u2593', + DisableBottomPercentage = false, + ShowEstimatedDuration = true + }; + using (ProgressBar progressBar = new(fileQueue.Count, "Analyzing Files.", options2)) + { + Stopwatch sw = new(); + sw.Start(); + _ = Task.Factory.StartNew(() => + { + try + { + DoProcessing(fileQueue); + } + catch (Exception e) + { + _logger.LogError("Unexpected exception while processing. {Type}:{Message}", + e.GetType().Name, e.Message); + } + finally + { + doneProcessing = true; + } + }); + + while (!doneProcessing) + { + Thread.Sleep(ProgressBarUpdateDelay); + var current = _metaDataHelper.Files.Count; + var timePerRecord = sw.Elapsed.TotalMilliseconds / current; + var millisExpected = (int)(timePerRecord * (fileQueue.Count - current)); + TimeSpan timeExpected = new(0, 0, 0, 0, millisExpected); + progressBar.Tick(_metaDataHelper.Files.Count, timeExpected, + $"Analyzing Files. {_metaDataHelper.Matches.Count} Matches. {_metaDataHelper.Files.Count(x => x.Status == ScanState.Skipped)} Files Skipped. {_metaDataHelper.Files.Count(x => x.Status == ScanState.TimedOut)} Timed Out. {_metaDataHelper.Files.Count(x => x.Status == ScanState.Affected)} Affected. {_metaDataHelper.Files.Count(x => x.Status == ScanState.Analyzed)} Not Affected."); + } + + // If processing timed out it will have set the status to timeout skipped. + timedOut = _metaDataHelper.Files.Any(x => x.Status == ScanState.TimeOutSkipped); + + progressBar.Message = timedOut + ? $"Overall processing timeout hit. {_metaDataHelper.Matches.Count} Matches. {_metaDataHelper.Files.Count(x => x.Status == ScanState.Skipped)} Files Skipped. {_metaDataHelper.Files.Count(x => x.Status == ScanState.TimedOut)} Timed Out. {_metaDataHelper.Files.Count(x => x.Status == ScanState.Affected)} Affected. {_metaDataHelper.Files.Count(x => x.Status == ScanState.Analyzed)} Not Affected." + : $"{_metaDataHelper.Matches.Count} Matches. {_metaDataHelper.Files.Count(x => x.Status == ScanState.Skipped)} Files Skipped. {_metaDataHelper.Files.Count(x => x.Status == ScanState.TimedOut)} Timed Out. {_metaDataHelper.Files.Count(x => x.Status == ScanState.Affected)} Affected. {_metaDataHelper.Files.Count(x => x.Status == ScanState.Analyzed)} Not Affected."; + progressBar.Tick(progressBar.MaxTicks); + } + + Console.WriteLine(); + } + + //wrapup result status + if (!_options.SuccessErrorCodeOnNoMatches) + { + if (!_options.NoFileMetadata && _metaDataHelper.Files.All(x => x.Status == ScanState.Skipped)) + { + _logger.LogError(MsgHelp.GetString(MsgHelp.ID.ANALYZE_NOSUPPORTED_FILETYPES)); + analyzeResult.ResultCode = AnalyzeResult.ExitCode.NoMatches; + } + else if (!_metaDataHelper.HasFindings) + { + _logger.LogError(MsgHelp.GetString(MsgHelp.ID.ANALYZE_NOPATTERNS)); + analyzeResult.ResultCode = AnalyzeResult.ExitCode.NoMatches; + } + } + + _metaDataHelper.Metadata.DateScanned = DateScanned.ToString(CultureInfo.InvariantCulture); + _metaDataHelper.PrepareReport(); + analyzeResult.Metadata = _metaDataHelper.Metadata; //replace instance with metadatahelper processed one + + if (timedOut) + { + _logger.LogError(MsgHelp.GetString(MsgHelp.ID.ANALYZE_PROCESSING_TIMED_OUT)); + analyzeResult.Metadata.TimedOut = true; + analyzeResult.ResultCode = AnalyzeResult.ExitCode.TimedOut; + } + + return analyzeResult; + } + + /// + /// Do processing of the given file entries - return if the timeout was hit + /// + /// + /// + /// True when the timeout was hit + private void DoProcessing(IEnumerable fileEntries, CancellationTokenSource? cts = null) + { + cts ??= new CancellationTokenSource(); + if (_options.ProcessingTimeOut > 0) + { + var t = Task.Run(() => PopulateRecords(cts.Token, fileEntries), cts.Token); + if (!t.Wait(new TimeSpan(0, 0, 0, 0, _options.ProcessingTimeOut))) + { + cts.Cancel(); + // Populate skips for all the entries we didn't process + foreach (var entry in fileEntries.Where(x => _metaDataHelper.Files.All(y => x.FullPath != y.FileName))) + _metaDataHelper.Files.Add(new FileRecord + { + AccessTime = entry.AccessTime, CreateTime = entry.CreateTime, ModifyTime = entry.ModifyTime, + FileName = entry.FullPath, Status = ScanState.TimeOutSkipped + }); + } + } + else + { + PopulateRecords(cts.Token, fileEntries); } } } \ No newline at end of file diff --git a/AppInspector/Commands/ExportTagsCommand.cs b/AppInspector/Commands/ExportTagsCommand.cs index 5503df2..8cd9440 100644 --- a/AppInspector/Commands/ExportTagsCommand.cs +++ b/AppInspector/Commands/ExportTagsCommand.cs @@ -1,132 +1,115 @@ // Copyright (C) Microsoft. All rights reserved. // Licensed under the MIT License. See LICENSE.txt in the project root for license information. -namespace Microsoft.ApplicationInspector.Commands +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Microsoft.ApplicationInspector.Common; +using Microsoft.ApplicationInspector.RulesEngine; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Newtonsoft.Json; + +namespace Microsoft.ApplicationInspector.Commands; + +/// +/// Options for the Export Tags command. +/// +public class ExportTagsOptions { - using Microsoft.ApplicationInspector.RulesEngine; - using Newtonsoft.Json; - using System; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using Microsoft.ApplicationInspector.Common; - using Microsoft.Extensions.Logging; - using Microsoft.Extensions.Logging.Abstractions; + public string? CustomRulesPath { get; set; } + public bool IgnoreDefaultRules { get; set; } +} - /// - /// Options for the Export Tags command. - /// - public class ExportTagsOptions +/// +/// Final result of GetResult call +/// +public class ExportTagsResult : Result +{ + public enum ExitCode { - public string? CustomRulesPath { get; set; } - public bool IgnoreDefaultRules { get; set; } + Success = 0, + Error = 1, + CriticalError = Utils.ExitCode.CriticalError //ensure common value for final exit log mention } - /// - /// Final result of GetResult call - /// - public class ExportTagsResult : Result + public ExportTagsResult() { - public enum ExitCode - { - Success = 0, - Error = 1, - CriticalError = Common.Utils.ExitCode.CriticalError //ensure common value for final exit log mention - } - - [JsonProperty(Order = 2, PropertyName = "resultCode")] - public ExitCode ResultCode { get; set; } - - /// - /// List of tags exported from specified ruleset - /// - [JsonProperty(Order = 3, PropertyName = "tagsList")] - public List TagsList { get; set; } - - public ExportTagsResult() - { - TagsList = new List(); - } + TagsList = new List(); } + [JsonProperty(Order = 2, PropertyName = "resultCode")] + public ExitCode ResultCode { get; set; } + /// - /// Export command operation manages setp and delivery of ExportResult objects + /// List of tags exported from specified ruleset /// - public class ExportTagsCommand + [JsonProperty(Order = 3, PropertyName = "tagsList")] + public List TagsList { get; set; } +} + +/// +/// Export command operation manages setp and delivery of ExportResult objects +/// +public class ExportTagsCommand +{ + private readonly ILogger _logger; + private readonly ILoggerFactory? _loggerFactory; + private readonly ExportTagsOptions _options; + private RuleSet _rules; + + public ExportTagsCommand(ExportTagsOptions opt, ILoggerFactory? loggerFactory = null) { - private readonly ExportTagsOptions _options; - private readonly ILogger _logger; - private readonly ILoggerFactory? _loggerFactory; - private RuleSet _rules; + _options = opt; + _logger = loggerFactory?.CreateLogger() ?? NullLogger.Instance; + _loggerFactory = loggerFactory; + _rules = new RuleSet(); + ConfigRules(); + } - public ExportTagsCommand(ExportTagsOptions opt, ILoggerFactory? loggerFactory = null) + + private void ConfigRules() + { + _logger.LogTrace("ExportTagsCommand::ConfigRules"); + _rules = new RuleSet(_loggerFactory); + if (!_options.IgnoreDefaultRules) _rules = RuleSetUtils.GetDefaultRuleSet(_loggerFactory); + + if (!string.IsNullOrEmpty(_options?.CustomRulesPath)) { - _options = opt; - _logger = loggerFactory?.CreateLogger() ?? NullLogger.Instance; - _loggerFactory = loggerFactory; - _rules = new RuleSet(); - ConfigRules(); + if (Directory.Exists(_options.CustomRulesPath)) + _rules.AddDirectory(_options.CustomRulesPath); + else if (File.Exists(_options.CustomRulesPath)) + _rules.AddFile(_options.CustomRulesPath); + else + throw new OpException(MsgHelp.FormatString(MsgHelp.ID.CMD_INVALID_RULE_PATH, _options.CustomRulesPath)); } - - private void ConfigRules() + //error check based on ruleset not path enumeration + if (_rules == null || !_rules.Any()) throw new OpException(MsgHelp.GetString(MsgHelp.ID.CMD_NORULES_SPECIFIED)); + } + + + public ExportTagsResult GetResult() + { + _logger.LogTrace("ExportTagsCommand::Run"); + _logger.LogInformation(MsgHelp.GetString(MsgHelp.ID.CMD_RUNNING), "Export Tags"); + + ExportTagsResult exportTagsResult = new() { - _logger.LogTrace("ExportTagsCommand::ConfigRules"); - _rules = new RuleSet(_loggerFactory); - if (!_options.IgnoreDefaultRules) - { - _rules = RuleSetUtils.GetDefaultRuleSet(_loggerFactory); - } + AppVersion = Utils.GetVersionString() + }; - if (!string.IsNullOrEmpty(_options?.CustomRulesPath)) - { - if (Directory.Exists(_options.CustomRulesPath)) - { - _rules.AddDirectory(_options.CustomRulesPath); - } - else if (File.Exists(_options.CustomRulesPath)) - { - _rules.AddFile(_options.CustomRulesPath); - } - else - { - throw new OpException(MsgHelp.FormatString(MsgHelp.ID.CMD_INVALID_RULE_PATH, _options.CustomRulesPath)); - } - } + HashSet tags = new(); + foreach (var rule in _rules.GetAppInspectorRules()) + foreach (var tag in rule.Tags ?? Array.Empty()) + tags.Add(tag); - //error check based on ruleset not path enumeration - if (_rules == null || !_rules.Any()) - { - throw new OpException(MsgHelp.GetString(MsgHelp.ID.CMD_NORULES_SPECIFIED)); - } - } + exportTagsResult.TagsList = tags.ToList(); + exportTagsResult.TagsList.Sort(); - - public ExportTagsResult GetResult() - { - _logger.LogTrace("ExportTagsCommand::Run"); - _logger.LogInformation(MsgHelp.GetString(MsgHelp.ID.CMD_RUNNING), "Export Tags"); + exportTagsResult.ResultCode = ExportTagsResult.ExitCode.Success; - ExportTagsResult exportTagsResult = new() - { - AppVersion = Common.Utils.GetVersionString() - }; - - HashSet tags = new(); - foreach (var rule in _rules.GetAppInspectorRules()) - { - foreach (var tag in rule.Tags ?? Array.Empty()) - { - tags.Add(tag); - } - } - - exportTagsResult.TagsList = tags.ToList(); - exportTagsResult.TagsList.Sort(); - - exportTagsResult.ResultCode = ExportTagsResult.ExitCode.Success; - - return exportTagsResult; - } + return exportTagsResult; } } \ No newline at end of file diff --git a/AppInspector/Commands/PackRulesCommand.cs b/AppInspector/Commands/PackRulesCommand.cs index 4883fba..5fbd9d9 100644 --- a/AppInspector/Commands/PackRulesCommand.cs +++ b/AppInspector/Commands/PackRulesCommand.cs @@ -1,117 +1,112 @@ // Copyright (C) Microsoft. All rights reserved. // Licensed under the MIT License. See LICENSE.txt in the project root for license information. -namespace Microsoft.ApplicationInspector.Commands +using System.Collections.Generic; +using System.Linq; +using Microsoft.ApplicationInspector.Common; +using Microsoft.ApplicationInspector.RulesEngine; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Newtonsoft.Json; + +namespace Microsoft.ApplicationInspector.Commands; + +public class PackRulesOptions { - using Microsoft.ApplicationInspector.RulesEngine; - using Newtonsoft.Json; - using System.Collections.Generic; - using System.Linq; - using Microsoft.ApplicationInspector.Common; - using Microsoft.Extensions.Logging; - using Microsoft.Extensions.Logging.Abstractions; + public string? CustomRulesPath { get; set; } + public bool PackEmbeddedRules { get; set; } + public string? CustomCommentsPath { get; set; } + public string? CustomLanguagesPath { get; set; } + public bool DisableRequireUniqueIds { get; set; } + public bool RequireMustMatch { get; set; } + public bool RequireMustNotMatch { get; set; } +} - public class PackRulesOptions +public class PackRulesResult : Result +{ + public enum ExitCode { - public string? CustomRulesPath { get; set; } - public bool NotIndented { get; set; } - public bool PackEmbeddedRules { get; set; } - public string? CustomCommentsPath { get; set; } - public string? CustomLanguagesPath { get; set; } + Success = 0, + Error = 1, + CriticalError = Utils.ExitCode.CriticalError //ensure common value for final exit log mention } - public class PackRulesResult : Result - { - public enum ExitCode - { - Success = 0, - Error = 1, - CriticalError = Common.Utils.ExitCode.CriticalError //ensure common value for final exit log mention - } - - [JsonProperty(Order = 2)] - public ExitCode ResultCode { get; set; } - - /// - /// List of Rules to pack as specified in pack command - /// - [JsonProperty(Order = 3)] - public List? Rules { get; set; } - } + [JsonProperty(Order = 2)] public ExitCode ResultCode { get; set; } /// - /// Used to combine validated rules into one json for ease in distribution of this - /// application + /// List of Rules to pack as specified in pack command /// - public class PackRulesCommand + [JsonProperty(Order = 3)] + public List? Rules { get; set; } +} + +/// +/// Used to combine validated rules into one json +/// +public class PackRulesCommand +{ + private readonly ILogger _logger; + private readonly ILoggerFactory? _loggerFactory; + private readonly PackRulesOptions _options; + + public PackRulesCommand(PackRulesOptions opt, ILoggerFactory? loggerFactory = null) { - private readonly PackRulesOptions _options; - private readonly ILogger _logger; - private readonly ILoggerFactory? _loggerFactory; + _options = opt; + _logger = loggerFactory?.CreateLogger() ?? NullLogger.Instance; + _loggerFactory = loggerFactory; + ConfigRules(); + } - public PackRulesCommand(PackRulesOptions opt, ILoggerFactory? loggerFactory = null) + + private void ConfigRules() + { + _logger.LogTrace("PackRulesCommand::ConfigRules"); + + if (string.IsNullOrEmpty(_options.CustomRulesPath) && !_options.PackEmbeddedRules) + throw new OpException(MsgHelp.GetString(MsgHelp.ID.CMD_NORULES_SPECIFIED)); + } + + + /// + /// Intentional as no identified value in calling from DLL at this time + /// + /// + public PackRulesResult GetResult() + { + _logger.LogTrace("PackRulesCommand::ConfigRules"); + _logger.LogInformation(MsgHelp.GetString(MsgHelp.ID.CMD_RUNNING), "Pack Rules"); + + PackRulesResult packRulesResult = new() { - _options = opt; - _logger = loggerFactory?.CreateLogger() ?? NullLogger.Instance; - _loggerFactory = loggerFactory; - ConfigRules(); - } + AppVersion = Utils.GetVersionString() + }; - - private void ConfigRules() + try { - _logger.LogTrace("PackRulesCommand::ConfigRules"); - - if (string.IsNullOrEmpty(_options.CustomRulesPath) && !_options.PackEmbeddedRules) + RulesVerifierOptions options = new() { - throw new OpException(MsgHelp.GetString(MsgHelp.ID.CMD_NORULES_SPECIFIED)); - } - } - - - /// - /// Intentional as no identified value in calling from DLL at this time - /// - /// - public PackRulesResult GetResult() - { - _logger.LogTrace("PackRulesCommand::ConfigRules"); - _logger.LogInformation(MsgHelp.GetString(MsgHelp.ID.CMD_RUNNING), "Pack Rules"); - - PackRulesResult packRulesResult = new() - { - AppVersion = Common.Utils.GetVersionString() + LoggerFactory = _loggerFactory, + LanguageSpecs = Languages.FromConfigurationFiles(_loggerFactory, _options.CustomCommentsPath, + _options.CustomLanguagesPath), + DisableRequireUniqueIds = _options.DisableRequireUniqueIds, + RequireMustMatch = _options.RequireMustMatch, + RequireMustNotMatch = _options.RequireMustNotMatch }; - - try - { - RulesVerifierOptions options = new() - { - LoggerFactory = _loggerFactory, - LanguageSpecs = Languages.FromConfigurationFiles(_loggerFactory, _options.CustomCommentsPath, _options.CustomLanguagesPath) - }; - RulesVerifier verifier = new(options); - RuleSet? ruleSet = _options.PackEmbeddedRules ? RuleSetUtils.GetDefaultRuleSet() : new RuleSet(); - if (!string.IsNullOrEmpty(_options.CustomRulesPath)) - { - ruleSet.AddDirectory(_options.CustomRulesPath); - } - RulesVerifierResult result = verifier.Verify(ruleSet); - if (!result.Verified) - { - throw new OpException(MsgHelp.GetString(MsgHelp.ID.VERIFY_RULES_RESULTS_FAIL)); - } - packRulesResult.Rules = result.CompiledRuleSet.GetAppInspectorRules().ToList(); - packRulesResult.ResultCode = PackRulesResult.ExitCode.Success; - } - catch (OpException e) - { - _logger.LogError(e.Message); - //caught for CLI callers with final exit msg about checking log or throws for DLL callers - throw; - } - - return packRulesResult; + RulesVerifier verifier = new(options); + var ruleSet = _options.PackEmbeddedRules ? RuleSetUtils.GetDefaultRuleSet() : new RuleSet(); + if (!string.IsNullOrEmpty(_options.CustomRulesPath)) ruleSet.AddPath(_options.CustomRulesPath); + var result = verifier.Verify(ruleSet); + if (!result.Verified) throw new OpException(MsgHelp.GetString(MsgHelp.ID.VERIFY_RULES_RESULTS_FAIL)); + packRulesResult.Rules = result.CompiledRuleSet.GetAppInspectorRules().ToList(); + packRulesResult.ResultCode = PackRulesResult.ExitCode.Success; } + catch (OpException e) + { + _logger.LogError(e.Message); + //caught for CLI callers with final exit msg about checking log or throws for DLL callers + throw; + } + + return packRulesResult; } } \ No newline at end of file diff --git a/AppInspector/Commands/TagDiffCommand.cs b/AppInspector/Commands/TagDiffCommand.cs index 21f1c6b..931fc58 100644 --- a/AppInspector/Commands/TagDiffCommand.cs +++ b/AppInspector/Commands/TagDiffCommand.cs @@ -1,251 +1,250 @@ // Copyright (C) Microsoft. All rights reserved. // Licensed under the MIT License. See LICENSE.txt in the project root for license information. -namespace Microsoft.ApplicationInspector.Commands +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.ApplicationInspector.Common; +using Microsoft.ApplicationInspector.RulesEngine; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; +using Newtonsoft.Json; + +namespace Microsoft.ApplicationInspector.Commands; + +public class TagDiffOptions { - using Microsoft.ApplicationInspector.Common; - using Microsoft.ApplicationInspector.RulesEngine; - using Microsoft.Extensions.Logging; - using Microsoft.Extensions.Logging.Abstractions; - using Newtonsoft.Json; - using System; - using System.Collections.Generic; - using System.Linq; + public IEnumerable SourcePath1 { get; set; } = Array.Empty(); + public IEnumerable SourcePath2 { get; set; } = Array.Empty(); + public TagTestType TestType { get; set; } = TagTestType.Equality; + public IEnumerable FilePathExclusions { get; set; } = Array.Empty(); + public string? CustomRulesPath { get; set; } + public bool IgnoreDefaultRules { get; set; } + public int FileTimeOut { get; set; } + public int ProcessingTimeOut { get; set; } + public bool ScanUnknownTypes { get; set; } + public bool SingleThread { get; set; } + public IEnumerable ConfidenceFilters { get; set; } = new[] { Confidence.High, Confidence.Medium }; - public class TagDiffOptions + public IEnumerable SeverityFilters { get; set; } = new[] + { Severity.Critical | Severity.Important | Severity.Moderate | Severity.BestPractice | Severity.ManualReview }; + + public string? CustomCommentsPath { get; set; } + public string? CustomLanguagesPath { get; set; } + public bool DisableCustomRuleValidation { get; set; } + public bool DisableRequireUniqueIds { get; set; } + + /// + /// Return a success error code when no matches were found but operation was apparently successful. Useful for CI + /// scenarios + /// + public bool SuccessErrorCodeOnNoMatches { get; set; } + + public bool RequireMustMatch { get; set; } + public bool RequireMustNotMatch { get; set; } +} + +/// +/// Contains a tag that was detected missing in source1 or source2 +/// +public class TagDiff +{ + public enum DiffSource { - public IEnumerable SourcePath1 { get; set; } = Array.Empty(); - public IEnumerable SourcePath2 { get; set; } = Array.Empty(); - public TagTestType TestType { get; set; } = TagTestType.Equality; - public IEnumerable FilePathExclusions { get; set; } = Array.Empty(); - public string? CustomRulesPath { get; set; } - public bool IgnoreDefaultRules { get; set; } - public int FileTimeOut { get; set; } - public int ProcessingTimeOut { get; set; } - public bool ScanUnknownTypes { get; set; } - public bool SingleThread { get; set; } - public IEnumerable ConfidenceFilters { get; set; } = new Confidence[] { Confidence.High, Confidence.Medium }; - public IEnumerable SeverityFilters { get; set; } = new Severity[] { Severity.Critical | Severity.Important | Severity.Moderate | Severity.BestPractice | Severity.ManualReview }; - public string? CustomCommentsPath { get; set; } - public string? CustomLanguagesPath { get; set; } - public bool DisableCustomRuleValidation { get; set; } - public bool DisableRequireUniqueIds { get; set; } - /// - /// Return a success error code when no matches were found but operation was apparently successful. Useful for CI scenarios - /// - public bool SuccessErrorCodeOnNoMatches { get; set; } - - public bool RequireMustMatch { get; set; } - public bool RequireMustNotMatch { get; set; } + Source1 = 1, + Source2 = 2 } /// - /// Contains a tag that was detected missing in source1 or source2 + /// Tag value from rule used in comparison /// - public class TagDiff + [JsonProperty(PropertyName = "tag")] + public string? Tag { get; set; } + + /// + /// Source file (src1/src2) from the command option arguments + /// + [JsonProperty(PropertyName = "source")] + public DiffSource Source { get; set; } +} + +/// +/// Result wrapping list of tags not found in one of the sources scanned +/// +public class TagDiffResult : Result +{ + public enum ExitCode { - public enum DiffSource - { - Source1 = 1, - Source2 = 2 - } - - /// - /// Tag value from rule used in comparison - /// - [JsonProperty(PropertyName = "tag")] - public string? Tag { get; set; } - - /// - /// Source file (src1/src2) from the command option arguments - /// - [JsonProperty(PropertyName = "source")] - public DiffSource Source { get; set; } + TestPassed = 0, + TestFailed = 1, + CriticalError = Utils.ExitCode.CriticalError //ensure common value for final exit log mention } /// - /// Result wrapping list of tags not found in one of the sources scanned + /// List of tags which differ between src1 and src2 /// - public class TagDiffResult : Result + [JsonProperty(Order = 3, PropertyName = "tagDiffList")] + public List TagDiffList; + + public TagDiffResult() { - public enum ExitCode + TagDiffList = new List(); + } + + [JsonProperty(Order = 2, PropertyName = "resultCode")] + public ExitCode ResultCode { get; set; } +} + +public enum TagTestType +{ + Equality, + Inequality +} + +/// +/// Used to compare two source paths and report tag differences +/// +public class TagDiffCommand +{ + private readonly ILoggerFactory? _factory; + private readonly ILogger _logger; + + private readonly TagDiffOptions? _options; + + public TagDiffCommand(TagDiffOptions opt, ILoggerFactory? loggerFactory = null) + { + _options = opt; + _factory = loggerFactory; + _logger = loggerFactory?.CreateLogger() ?? NullLogger.Instance; + try { - TestPassed = 0, - TestFailed = 1, - CriticalError = Utils.ExitCode.CriticalError //ensure common value for final exit log mention + ConfigSourceToScan(); } - - [JsonProperty(Order = 2, PropertyName = "resultCode")] - public ExitCode ResultCode { get; set; } - - /// - /// List of tags which differ between src1 and src2 - /// - [JsonProperty(Order = 3, PropertyName = "tagDiffList")] - public List TagDiffList; - - public TagDiffResult() + catch (OpException e) //group error handling { - TagDiffList = new List(); + _logger.LogError(e.Message); + throw; } } - public enum TagTestType { Equality, Inequality } + + private void ConfigSourceToScan() + { + _logger.LogTrace("TagDiff::ConfigRules"); + + if ((!_options?.SourcePath1.Any() ?? true) || (!_options?.SourcePath2.Any() ?? true)) + throw new OpException(MsgHelp.GetString(MsgHelp.ID.CMD_INVALID_ARG_VALUE)); + } + /// - /// Used to compare two source paths and report tag differences + /// Main entry from CLI /// - public class TagDiffCommand + /// + public TagDiffResult GetResult() { + _logger.LogTrace("TagDiffCommand::Run"); + _logger.LogInformation(MsgHelp.GetString(MsgHelp.ID.CMD_RUNNING), "Tag Diff"); - private readonly TagDiffOptions? _options; - private readonly ILoggerFactory? _factory; - private readonly ILogger _logger; + TagDiffResult tagDiffResult = new() { AppVersion = Utils.GetVersionString() }; - public TagDiffCommand(TagDiffOptions opt, ILoggerFactory? loggerFactory = null) + try { - _options = opt; - _factory = loggerFactory; - _logger = loggerFactory?.CreateLogger() ?? NullLogger.Instance; - try + if (_options is null) throw new ArgumentNullException(nameof(_options)); + AnalyzeCommand cmd1 = new(new AnalyzeOptions { - ConfigSourceToScan(); - } - catch (OpException e) //group error handling + SourcePath = _options.SourcePath1, + CustomRulesPath = _options.CustomRulesPath, + IgnoreDefaultRules = _options.IgnoreDefaultRules, + FilePathExclusions = _options.FilePathExclusions, + TagsOnly = true, + ConfidenceFilters = _options.ConfidenceFilters, + SeverityFilters = _options.SeverityFilters, + FileTimeOut = _options.FileTimeOut, + ProcessingTimeOut = _options.ProcessingTimeOut, + NoFileMetadata = true, + NoShowProgress = true, + ScanUnknownTypes = _options.ScanUnknownTypes, + SingleThread = _options.SingleThread, + CustomCommentsPath = _options.CustomCommentsPath, + CustomLanguagesPath = _options.CustomLanguagesPath, + DisableCustomRuleVerification = _options.DisableCustomRuleValidation, + DisableRequireUniqueIds = _options.DisableRequireUniqueIds, + SuccessErrorCodeOnNoMatches = _options.SuccessErrorCodeOnNoMatches, + RequireMustMatch = _options.RequireMustMatch, + RequireMustNotMatch = _options.RequireMustNotMatch + }, _factory); + AnalyzeCommand cmd2 = new(new AnalyzeOptions { - _logger.LogError(e.Message); - throw; + SourcePath = _options.SourcePath2, + CustomRulesPath = _options.CustomRulesPath, + IgnoreDefaultRules = _options.IgnoreDefaultRules, + FilePathExclusions = _options.FilePathExclusions, + TagsOnly = true, + ConfidenceFilters = _options.ConfidenceFilters, + SeverityFilters = _options.SeverityFilters, + FileTimeOut = _options.FileTimeOut, + ProcessingTimeOut = _options.ProcessingTimeOut, + NoFileMetadata = true, + NoShowProgress = true, + ScanUnknownTypes = _options.ScanUnknownTypes, + SingleThread = _options.SingleThread, + CustomCommentsPath = _options.CustomCommentsPath, + CustomLanguagesPath = _options.CustomLanguagesPath, + DisableCustomRuleVerification = true, // Rules are already validated by the first command + SuccessErrorCodeOnNoMatches = _options.SuccessErrorCodeOnNoMatches + }, _factory); + + var analyze1 = cmd1.GetResult(); + var analyze2 = cmd2.GetResult(); + + //process results for each analyze call before comparing results + if (analyze1.ResultCode == AnalyzeResult.ExitCode.CriticalError) + throw new OpException(MsgHelp.FormatString(MsgHelp.ID.CMD_CRITICAL_FILE_ERR, + string.Join(',', _options.SourcePath1))); + + if (analyze2.ResultCode == AnalyzeResult.ExitCode.CriticalError) + { + throw new OpException(MsgHelp.FormatString(MsgHelp.ID.CMD_CRITICAL_FILE_ERR, + string.Join(',', _options.SourcePath2))); } + + //compare tag results; assumed (result1&2 == AnalyzeCommand.ExitCode.Success) + var list1 = analyze1.Metadata.UniqueTags ?? new List(); + var list2 = analyze2.Metadata.UniqueTags ?? new List(); + + var removed = list1.Except(list2); + var added = list2.Except(list1); + + foreach (var add in added) + tagDiffResult.TagDiffList.Add(new TagDiff + { + Source = TagDiff.DiffSource.Source2, + Tag = add + }); + foreach (var remove in removed) + tagDiffResult.TagDiffList.Add(new TagDiff + { + Source = TagDiff.DiffSource.Source1, + Tag = remove + }); + + + if (tagDiffResult.TagDiffList.Count > 0) + tagDiffResult.ResultCode = _options.TestType == TagTestType.Inequality + ? TagDiffResult.ExitCode.TestPassed + : TagDiffResult.ExitCode.TestFailed; + else + tagDiffResult.ResultCode = _options.TestType == TagTestType.Inequality + ? TagDiffResult.ExitCode.TestFailed + : TagDiffResult.ExitCode.TestPassed; + + return tagDiffResult; } - - private void ConfigSourceToScan() + catch (OpException e) { - _logger.LogTrace("TagDiff::ConfigRules"); - - if ((!_options?.SourcePath1.Any() ?? true) || (!_options?.SourcePath2.Any() ?? true)) - { - throw new OpException(MsgHelp.GetString(MsgHelp.ID.CMD_INVALID_ARG_VALUE)); - } - } - - - /// - /// Main entry from CLI - /// - /// - public TagDiffResult GetResult() - { - _logger.LogTrace("TagDiffCommand::Run"); - _logger.LogInformation(MsgHelp.GetString(MsgHelp.ID.CMD_RUNNING), "Tag Diff"); - - TagDiffResult tagDiffResult = new() { AppVersion = Common.Utils.GetVersionString() }; - - try - { - if (_options is null) - { - throw new ArgumentNullException(nameof(_options)); - } - AnalyzeCommand cmd1 = new(new AnalyzeOptions() - { - SourcePath = _options.SourcePath1, - CustomRulesPath = _options.CustomRulesPath, - IgnoreDefaultRules = _options.IgnoreDefaultRules, - FilePathExclusions = _options.FilePathExclusions, - TagsOnly = true, - ConfidenceFilters = _options.ConfidenceFilters, - SeverityFilters = _options.SeverityFilters, - FileTimeOut = _options.FileTimeOut, - ProcessingTimeOut = _options.ProcessingTimeOut, - NoFileMetadata = true, - NoShowProgress = true, - ScanUnknownTypes = _options.ScanUnknownTypes, - SingleThread = _options.SingleThread, - CustomCommentsPath = _options.CustomCommentsPath, - CustomLanguagesPath = _options.CustomLanguagesPath, - DisableCustomRuleVerification = _options.DisableCustomRuleValidation, - DisableRequireUniqueIds = _options.DisableRequireUniqueIds, - SuccessErrorCodeOnNoMatches = _options.SuccessErrorCodeOnNoMatches, - RequireMustMatch = _options.RequireMustMatch, - RequireMustNotMatch = _options.RequireMustNotMatch - }, _factory); - AnalyzeCommand cmd2 = new(new AnalyzeOptions() - { - SourcePath = _options.SourcePath2, - CustomRulesPath = _options.CustomRulesPath, - IgnoreDefaultRules = _options.IgnoreDefaultRules, - FilePathExclusions = _options.FilePathExclusions, - TagsOnly = true, - ConfidenceFilters = _options.ConfidenceFilters, - SeverityFilters = _options.SeverityFilters, - FileTimeOut = _options.FileTimeOut, - ProcessingTimeOut = _options.ProcessingTimeOut, - NoFileMetadata = true, - NoShowProgress = true, - ScanUnknownTypes = _options.ScanUnknownTypes, - SingleThread = _options.SingleThread, - CustomCommentsPath = _options.CustomCommentsPath, - CustomLanguagesPath = _options.CustomLanguagesPath, - DisableCustomRuleVerification = true, // Rules are already validated by the first command - SuccessErrorCodeOnNoMatches = _options.SuccessErrorCodeOnNoMatches - }, _factory); - - AnalyzeResult analyze1 = cmd1.GetResult(); - AnalyzeResult analyze2 = cmd2.GetResult(); - - //process results for each analyze call before comparing results - if (analyze1.ResultCode == AnalyzeResult.ExitCode.CriticalError) - { - throw new OpException(MsgHelp.FormatString(MsgHelp.ID.CMD_CRITICAL_FILE_ERR, string.Join(',', _options.SourcePath1))); - } - else if (analyze2.ResultCode == AnalyzeResult.ExitCode.CriticalError) - { - throw new OpException(MsgHelp.FormatString(MsgHelp.ID.CMD_CRITICAL_FILE_ERR, string.Join(',', _options.SourcePath2))); - } - else //compare tag results; assumed (result1&2 == AnalyzeCommand.ExitCode.Success) - { - - var list1 = analyze1.Metadata.UniqueTags ?? new List(); - var list2 = analyze2.Metadata.UniqueTags ?? new List(); - - var removed = list1.Except(list2); - var added = list2.Except(list1); - - foreach (var add in added) - { - tagDiffResult.TagDiffList.Add(new TagDiff() - { - Source = TagDiff.DiffSource.Source2, - Tag = add - }); - } - foreach (var remove in removed) - { - tagDiffResult.TagDiffList.Add(new TagDiff() - { - Source = TagDiff.DiffSource.Source1, - Tag = remove - }); - } - - - if (tagDiffResult.TagDiffList.Count > 0) - { - tagDiffResult.ResultCode = _options.TestType == TagTestType.Inequality ? TagDiffResult.ExitCode.TestPassed : TagDiffResult.ExitCode.TestFailed; - } - else - { - tagDiffResult.ResultCode = _options.TestType == TagTestType.Inequality ? TagDiffResult.ExitCode.TestFailed : TagDiffResult.ExitCode.TestPassed; - } - - return tagDiffResult; - } - } - catch (OpException e) - { - _logger.LogError(e.Message); - //caught for CLI callers with final exit msg about checking log or throws for DLL callers - throw; - } + _logger.LogError(e.Message); + //caught for CLI callers with final exit msg about checking log or throws for DLL callers + throw; } } } \ No newline at end of file diff --git a/AppInspector/Commands/VerifyRulesCommand.cs b/AppInspector/Commands/VerifyRulesCommand.cs index 9c33871..bc76421 100644 --- a/AppInspector/Commands/VerifyRulesCommand.cs +++ b/AppInspector/Commands/VerifyRulesCommand.cs @@ -1,149 +1,142 @@ // Copyright (C) Microsoft. All rights reserved. // Licensed under the MIT License. See LICENSE.txt in the project root for license information. +using System.Collections.Generic; +using System.IO; using System.Linq; +using Microsoft.ApplicationInspector.Common; +using Microsoft.ApplicationInspector.RulesEngine; using Microsoft.ApplicationInspector.RulesEngine.OatExtensions; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Newtonsoft.Json; using Newtonsoft.Json.Converters; -namespace Microsoft.ApplicationInspector.Commands +namespace Microsoft.ApplicationInspector.Commands; + +public class VerifyRulesOptions { - using Microsoft.ApplicationInspector.Common; - using Microsoft.ApplicationInspector.RulesEngine; - using Microsoft.Extensions.Logging; - using Microsoft.Extensions.Logging.Abstractions; - - using System.Collections.Generic; - using System.IO; + public bool VerifyDefaultRules { get; set; } + public string? CustomRulesPath { get; set; } + public string? CustomCommentsPath { get; set; } + public string? CustomLanguagesPath { get; set; } + public bool DisableRequireUniqueIds { get; set; } + public bool RequireMustMatch { get; set; } + public bool RequireMustNotMatch { get; set; } +} - public class VerifyRulesOptions +public class VerifyRulesResult : Result +{ + [JsonConverter(typeof(StringEnumConverter))] + public enum ExitCode { - public bool VerifyDefaultRules { get; set; } - public string? CustomRulesPath { get; set; } - public string? CustomCommentsPath { get; set; } - public string? CustomLanguagesPath { get; set; } - public bool DisableRequireUniqueIds { get; set; } - public bool RequireMustMatch { get; set; } - public bool RequireMustNotMatch { get; set; } + Verified = 0, + NotVerified = 1, + CriticalError = Utils.ExitCode.CriticalError } - public class VerifyRulesResult : Result + public VerifyRulesResult() { - [Newtonsoft.Json.JsonConverter(typeof(StringEnumConverter))] - public enum ExitCode - { - Verified = 0, - NotVerified = 1, - CriticalError = Utils.ExitCode.CriticalError - } - - [JsonProperty(PropertyName ="resultCode")] - public ExitCode ResultCode { get; set; } - - [JsonProperty(PropertyName ="ruleStatusList")] - public List RuleStatusList { get; set; } - - [JsonIgnore] public IEnumerable Unverified => RuleStatusList.Where(x => !x.Verified); - - public VerifyRulesResult() - { - RuleStatusList = new List(); - } + RuleStatusList = new List(); } + [JsonProperty(PropertyName = "resultCode")] + public ExitCode ResultCode { get; set; } + + [JsonProperty(PropertyName = "ruleStatusList")] + public List RuleStatusList { get; set; } + + [JsonIgnore] public IEnumerable Unverified => RuleStatusList.Where(x => !x.Verified); +} + +/// +/// Used to verify user custom ruleset. Default ruleset has no need for support outside of PackRulesCommand for +/// verification +/// since each build performs a verification already and the output is added to the binary manifest +/// +public class VerifyRulesCommand +{ + private readonly ILogger _logger; + private readonly ILoggerFactory? _loggerFactory; + private readonly VerifyRulesOptions _options; + + public VerifyRulesCommand(VerifyRulesOptions opt, ILoggerFactory? loggerFactory = null) + { + _options = opt; + _logger = loggerFactory?.CreateLogger() ?? NullLogger.Instance; + _loggerFactory = loggerFactory; + ConfigRules(); + } + + + private void ConfigRules() + { + _logger.LogTrace("VerifyRulesCommand::ConfigRules"); + + if (!_options.VerifyDefaultRules && string.IsNullOrEmpty(_options.CustomRulesPath)) + throw new OpException(MsgHelp.GetString(MsgHelp.ID.CMD_NORULES_SPECIFIED)); + } + + /// - /// Used to verify user custom ruleset. Default ruleset has no need for support outside of PackRulesCommand for verification - /// since each build performs a verification already and the output is added to the binary manifest + /// Option for DLL use as alternate to Run which only outputs a file to return results as string + /// CommandOption defaults will not have been set when used as DLL via CLI processing so some checks added /// - public class VerifyRulesCommand + /// output results + public VerifyRulesResult GetResult() { - private readonly VerifyRulesOptions _options; - private readonly ILogger _logger; - private readonly ILoggerFactory? _loggerFactory; + _logger.LogTrace("VerifyRulesCommand::Run"); + _logger.LogInformation(MsgHelp.GetString(MsgHelp.ID.CMD_RUNNING), "Verify Rules"); - public VerifyRulesCommand(VerifyRulesOptions opt, ILoggerFactory? loggerFactory = null) + VerifyRulesResult verifyRulesResult = new() { AppVersion = Utils.GetVersionString() }; + + try { - _options = opt; - _logger = loggerFactory?.CreateLogger() ?? NullLogger.Instance; - _loggerFactory = loggerFactory; - ConfigRules(); - } - - - private void ConfigRules() - { - _logger.LogTrace("VerifyRulesCommand::ConfigRules"); - - if (!_options.VerifyDefaultRules && string.IsNullOrEmpty(_options.CustomRulesPath)) + var analyzer = new ApplicationInspectorAnalyzer(); + RulesVerifierOptions options = new() { - throw new OpException(MsgHelp.GetString(MsgHelp.ID.CMD_NORULES_SPECIFIED)); - } - } - - - /// - /// Option for DLL use as alternate to Run which only outputs a file to return results as string - /// CommandOption defaults will not have been set when used as DLL via CLI processing so some checks added - /// - /// output results - public VerifyRulesResult GetResult() - { - _logger.LogTrace("VerifyRulesCommand::Run"); - _logger.LogInformation(MsgHelp.GetString(MsgHelp.ID.CMD_RUNNING), "Verify Rules"); - - VerifyRulesResult verifyRulesResult = new() { AppVersion = Utils.GetVersionString() }; + Analyzer = analyzer, + DisableRequireUniqueIds = _options.DisableRequireUniqueIds, + RequireMustMatch = _options.RequireMustMatch, + RequireMustNotMatch = _options.RequireMustNotMatch, + LoggerFactory = _loggerFactory, + LanguageSpecs = Languages.FromConfigurationFiles(_loggerFactory, _options.CustomCommentsPath, + _options.CustomLanguagesPath) + }; + RulesVerifier verifier = new(options); + verifyRulesResult.ResultCode = VerifyRulesResult.ExitCode.Verified; + RuleSet? ruleSet = new(_loggerFactory); + if (_options.VerifyDefaultRules) ruleSet = RuleSetUtils.GetDefaultRuleSet(_loggerFactory); try { - var analyzer = new ApplicationInspectorAnalyzer(); - RulesVerifierOptions options = new() + if (_options.CustomRulesPath != null) { - Analyzer = analyzer, - DisableRequireUniqueIds = _options.DisableRequireUniqueIds, - RequireMustMatch = _options.RequireMustMatch, - RequireMustNotMatch = _options.RequireMustNotMatch, - LoggerFactory = _loggerFactory, - LanguageSpecs = Languages.FromConfigurationFiles(_loggerFactory, _options.CustomCommentsPath, _options.CustomLanguagesPath) - }; - RulesVerifier verifier = new(options); - verifyRulesResult.ResultCode = VerifyRulesResult.ExitCode.Verified; - - RuleSet? ruleSet = new(_loggerFactory); - if (_options.VerifyDefaultRules) - { - ruleSet = RuleSetUtils.GetDefaultRuleSet(_loggerFactory); + if (Directory.Exists(_options.CustomRulesPath)) + ruleSet.AddDirectory(_options.CustomRulesPath); + else if (File.Exists(_options.CustomRulesPath)) ruleSet.AddFile(_options.CustomRulesPath); } - try - { - if (_options.CustomRulesPath != null) - { - if (Directory.Exists(_options.CustomRulesPath)) - { - ruleSet.AddDirectory(_options.CustomRulesPath); - } - else if (File.Exists(_options.CustomRulesPath)) - { - ruleSet.AddFile(_options.CustomRulesPath); - } - } - } - catch(JsonException e) - { - _logger.LogError(e.Message); - verifyRulesResult.ResultCode = VerifyRulesResult.ExitCode.CriticalError; - return verifyRulesResult; - } - var verifyResult = verifier.Verify(ruleSet); - verifyRulesResult.RuleStatusList = verifyResult.RuleStatuses; - verifyRulesResult.ResultCode = verifyResult.Verified ? VerifyRulesResult.ExitCode.Verified : VerifyRulesResult.ExitCode.NotVerified; } - catch (OpException e) + catch (JsonException e) { - _logger.LogTrace(e.Message); - //caught for CLI callers with final exit msg about checking log or throws for DLL callers - throw; + _logger.LogError(e.Message); + verifyRulesResult.ResultCode = VerifyRulesResult.ExitCode.CriticalError; + return verifyRulesResult; } - return verifyRulesResult; + + var verifyResult = verifier.Verify(ruleSet); + verifyRulesResult.RuleStatusList = verifyResult.RuleStatuses; + verifyRulesResult.ResultCode = verifyResult.Verified + ? VerifyRulesResult.ExitCode.Verified + : VerifyRulesResult.ExitCode.NotVerified; } + catch (OpException e) + { + _logger.LogTrace(e.Message); + //caught for CLI callers with final exit msg about checking log or throws for DLL callers + throw; + } + + return verifyRulesResult; } } \ No newline at end of file diff --git a/AppInspector/FileRecord.cs b/AppInspector/FileRecord.cs index f5e6475..a17ee90 100644 --- a/AppInspector/FileRecord.cs +++ b/AppInspector/FileRecord.cs @@ -1,29 +1,28 @@ -namespace Microsoft.ApplicationInspector.Commands +using System; +using Newtonsoft.Json; +using Newtonsoft.Json.Converters; + +namespace Microsoft.ApplicationInspector.Commands; + +public class FileRecord { - using Newtonsoft.Json; - using Newtonsoft.Json.Converters; - using System; - - public class FileRecord - { - public string FileName { get; set; } = string.Empty; - public TimeSpan ScanTime { get; set; } = new TimeSpan(); - public ScanState Status { get; set; } = ScanState.None; - public int NumFindings { get; set; } = 0; - public DateTime ModifyTime { get; set; } = DateTime.MinValue; - public DateTime CreateTime { get; set; } = DateTime.MinValue; - public DateTime AccessTime { get; set; } = DateTime.MinValue; - } - - [JsonConverter(typeof(StringEnumConverter))] - public enum ScanState - { - None, - Skipped, - TimedOut, - Analyzed, - Affected, - TimeOutSkipped, - Error - } + public string FileName { get; set; } = string.Empty; + public TimeSpan ScanTime { get; set; } = new(); + public ScanState Status { get; set; } = ScanState.None; + public int NumFindings { get; set; } = 0; + public DateTime ModifyTime { get; set; } = DateTime.MinValue; + public DateTime CreateTime { get; set; } = DateTime.MinValue; + public DateTime AccessTime { get; set; } = DateTime.MinValue; } + +[JsonConverter(typeof(StringEnumConverter))] +public enum ScanState +{ + None, + Skipped, + TimedOut, + Analyzed, + Affected, + TimeOutSkipped, + Error +} \ No newline at end of file diff --git a/AppInspector/MetaData.cs b/AppInspector/MetaData.cs index 94ccebb..e951d10 100644 --- a/AppInspector/MetaData.cs +++ b/AppInspector/MetaData.cs @@ -1,229 +1,237 @@ // Copyright (C) Microsoft. All rights reserved. // Licensed under the MIT License. See LICENSE.txt in the project root for license information. -namespace Microsoft.ApplicationInspector.Commands +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.ApplicationInspector.RulesEngine; +using Newtonsoft.Json; + +namespace Microsoft.ApplicationInspector.Commands; + +/// +/// Contains the analyze scanned meta data elements including rollup data for reporting purposes +/// +public class MetaData { - using Microsoft.ApplicationInspector.RulesEngine; - using Newtonsoft.Json; - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Linq; + public MetaData(string applicationName, string sourcePath) + { + ApplicationName = applicationName; + SourcePath = sourcePath; + } + + //simple properties + /// + /// Detected or derived project name + /// + [JsonProperty(PropertyName = "applicationName")] + public string? ApplicationName { get; set; } /// - /// Contains the analyze scanned meta data elements including rollup data for reporting purposes + /// Source path provided argument /// - public class MetaData + [JsonProperty(PropertyName = "sourcePath")] + public string? SourcePath { get; set; } + + /// + /// Detected project source version + /// + [JsonProperty(PropertyName = "sourceVersion")] + public string? SourceVersion { get; set; } + + /// + /// Detected source authors + /// + [JsonProperty(PropertyName = "authors")] + public string? Authors { get; set; } + + /// + /// Detected source description + /// + [JsonProperty(PropertyName = "description")] + public string? Description { get; set; } + + /// + /// Last modified date for source code scanned + /// + [JsonProperty(PropertyName = "lastUpdated")] + public string LastUpdated { - //simple properties - /// - /// Detected or derived project name - /// - [JsonProperty(PropertyName = "applicationName")] - public string? ApplicationName { get; set; } - - /// - /// Source path provided argument - /// - [JsonProperty(PropertyName = "sourcePath")] - public string? SourcePath { get; set; } - - /// - /// Detected project source version - /// - [JsonProperty(PropertyName = "sourceVersion")] - public string? SourceVersion { get; set; } - - /// - /// Detected source authors - /// - [JsonProperty(PropertyName = "authors")] - public string? Authors { get; set; } - - /// - /// Detected source description - /// - [JsonProperty(PropertyName = "description")] - public string? Description { get; set; } - - /// - /// Last modified date for source code scanned - /// - [JsonProperty(PropertyName = "lastUpdated")] - public string LastUpdated - { - get - { - if (Files.Any()) - { - return Files.Select(x => x.ModifyTime).Max().ToString(); - } - else - { - return DateTime.MinValue.ToString(); - } - } - } - - /// - /// Date of analyze scan - /// - [JsonProperty(PropertyName = "dateScanned")] - public string? DateScanned { get; set; } - - - /// - /// True if the overall analysis timed out - /// - [JsonProperty(PropertyName = "timedOut")] - public bool TimedOut { get; set; } - - //stats - /// - /// Total number of files in source path - /// - [JsonProperty(PropertyName = "totalFiles")] - public int TotalFiles { get { return Files.Count; } } - - /// - /// Total number of files Timed out on an individual timeout - /// - [JsonProperty(PropertyName = "filesTimedOut")] - public int FilesTimedOut { get { return Files.Count(x => x.Status == ScanState.TimedOut); } } - - /// - /// Total number of files scanned - /// - [JsonProperty(PropertyName = "filesAnalyzed")] - public int FilesAnalyzed { get { return Files.Count(x => x.Status is ScanState.Analyzed or ScanState.Affected); } } - - /// - /// Total number of skipped files based on supported formats - /// - [JsonProperty(PropertyName = "filesSkipped")] - public int FilesSkipped { get { return Files.Count(x => x.Status == ScanState.Skipped); } } - - /// - /// Total number of skipped files based on overall timeout - /// - [JsonProperty(PropertyName = "filesTimeOutSkipped")] - public int FilesTimeOutSkipped { get { return Files.Count(x => x.Status == ScanState.TimeOutSkipped); } } - - /// - /// Total files with at least one result - /// - [JsonProperty(PropertyName = "filesAffected")] - public int FilesAffected { get { return Files.Count(x => x.Status == ScanState.Affected); } } - - /// - /// Number of files which encountered an error when processing other than timing out. - /// - [JsonProperty(PropertyName = "filesErrored")] - public int FileErrored { get { return Files.Count(x => x.Status == ScanState.Error); } } - - /// - /// Total matches with supplied argument settings - /// - [JsonProperty(PropertyName = "totalMatchesCount")] - public int TotalMatchesCount { get { return Matches?.Count ?? 0; } } - - /// - /// Total unique matches by Rule Id - /// - [JsonProperty(PropertyName = "uniqueMatchesCount")] - public int UniqueMatchesCount + get { - get - { - return Matches?.Select(x => x.RuleId).Distinct().Count() ?? 0; - } - } - - /// - /// List of detected package types - /// - [JsonProperty(PropertyName = "packageTypes")] - public List? PackageTypes { get; set; } = new List(); - - /// - /// List of detected application types - /// - [JsonProperty(PropertyName = "appTypes")] - public List? AppTypes { get; set; } = new List(); - - /// - /// List of detected unique tags - /// - [JsonProperty(PropertyName = "uniqueTags")] - public List UniqueTags { get; set; } = new List(); - - /// - /// List of detected unique code dependency includes - /// - [JsonProperty(PropertyName = "uniqueDependencies")] - public List? UniqueDependencies { get; set; } = new List(); - - /// - /// List of detected output types - /// - [JsonProperty(PropertyName = "outputs")] - public List? Outputs { get; set; } = new List(); - - /// - /// List of detected target types - /// - [JsonProperty(PropertyName = "targets")] - public List Targets { get; set; } = new List(); - - /// - /// List of detected OS targets - /// - [JsonProperty(PropertyName = "OSTargets")] - public List? OSTargets { get; set; } = new List(); - - /// - /// LIst of detected file types (extension based) - /// - [JsonProperty(PropertyName = "fileExtensions")] - public List? FileExtensions { get; set; } = new List(); - - /// - /// List of detected cloud host targets - /// - [JsonProperty(PropertyName = "cloudTargets")] - public List? CloudTargets { get; set; } = new List(); - - /// - /// List of detected cpu targets - /// - [JsonProperty(PropertyName = "CPUTargets")] - public List? CPUTargets { get; set; } = new List(); - - /// - /// List of detected programming languages used and count of files - /// - [JsonProperty(PropertyName = "languages")] - public IDictionary? Languages { get; set; } //unable to init here for constr arg - - /// - /// List of detected tag counters i.e. metrics - /// - [JsonProperty(PropertyName = "tagCounters")] - public List? TagCounters { get; set; } = new List(); - - /// - /// List of detailed MatchRecords from scan - /// - [JsonProperty(PropertyName = "detailedMatchList")] - public List Matches { get; set; } = new List(); - - [JsonProperty(PropertyName = "filesInformation")] - public List Files { get; set; } = new List(); - - public MetaData(string applicationName, string sourcePath) - { - ApplicationName = applicationName; - SourcePath = sourcePath; + if (Files.Any()) + return Files.Select(x => x.ModifyTime).Max().ToString(); + return DateTime.MinValue.ToString(); } } + + /// + /// Date of analyze scan + /// + [JsonProperty(PropertyName = "dateScanned")] + public string? DateScanned { get; set; } + + + /// + /// True if the overall analysis timed out + /// + [JsonProperty(PropertyName = "timedOut")] + public bool TimedOut { get; set; } + + //stats + /// + /// Total number of files in source path + /// + [JsonProperty(PropertyName = "totalFiles")] + public int TotalFiles => Files.Count; + + /// + /// Total number of files Timed out on an individual timeout + /// + [JsonProperty(PropertyName = "filesTimedOut")] + public int FilesTimedOut + { + get { return Files.Count(x => x.Status == ScanState.TimedOut); } + } + + /// + /// Total number of files scanned + /// + [JsonProperty(PropertyName = "filesAnalyzed")] + public int FilesAnalyzed + { + get { return Files.Count(x => x.Status is ScanState.Analyzed or ScanState.Affected); } + } + + /// + /// Total number of skipped files based on supported formats + /// + [JsonProperty(PropertyName = "filesSkipped")] + public int FilesSkipped + { + get { return Files.Count(x => x.Status == ScanState.Skipped); } + } + + /// + /// Total number of skipped files based on overall timeout + /// + [JsonProperty(PropertyName = "filesTimeOutSkipped")] + public int FilesTimeOutSkipped + { + get { return Files.Count(x => x.Status == ScanState.TimeOutSkipped); } + } + + /// + /// Total files with at least one result + /// + [JsonProperty(PropertyName = "filesAffected")] + public int FilesAffected + { + get { return Files.Count(x => x.Status == ScanState.Affected); } + } + + /// + /// Number of files which encountered an error when processing other than timing out. + /// + [JsonProperty(PropertyName = "filesErrored")] + public int FileErrored + { + get { return Files.Count(x => x.Status == ScanState.Error); } + } + + /// + /// Total matches with supplied argument settings + /// + [JsonProperty(PropertyName = "totalMatchesCount")] + public int TotalMatchesCount => Matches?.Count ?? 0; + + /// + /// Total unique matches by Rule Id + /// + [JsonProperty(PropertyName = "uniqueMatchesCount")] + public int UniqueMatchesCount + { + get { return Matches?.Select(x => x.RuleId).Distinct().Count() ?? 0; } + } + + /// + /// List of detected package types + /// + [JsonProperty(PropertyName = "packageTypes")] + public List? PackageTypes { get; set; } = new(); + + /// + /// List of detected application types + /// + [JsonProperty(PropertyName = "appTypes")] + public List? AppTypes { get; set; } = new(); + + /// + /// List of detected unique tags + /// + [JsonProperty(PropertyName = "uniqueTags")] + public List UniqueTags { get; set; } = new(); + + /// + /// List of detected unique code dependency includes + /// + [JsonProperty(PropertyName = "uniqueDependencies")] + public List? UniqueDependencies { get; set; } = new(); + + /// + /// List of detected output types + /// + [JsonProperty(PropertyName = "outputs")] + public List? Outputs { get; set; } = new(); + + /// + /// List of detected target types + /// + [JsonProperty(PropertyName = "targets")] + public List Targets { get; set; } = new(); + + /// + /// List of detected OS targets + /// + [JsonProperty(PropertyName = "OSTargets")] + public List? OSTargets { get; set; } = new(); + + /// + /// LIst of detected file types (extension based) + /// + [JsonProperty(PropertyName = "fileExtensions")] + public List? FileExtensions { get; set; } = new(); + + /// + /// List of detected cloud host targets + /// + [JsonProperty(PropertyName = "cloudTargets")] + public List? CloudTargets { get; set; } = new(); + + /// + /// List of detected cpu targets + /// + [JsonProperty(PropertyName = "CPUTargets")] + public List? CPUTargets { get; set; } = new(); + + /// + /// List of detected programming languages used and count of files + /// + [JsonProperty(PropertyName = "languages")] + public IDictionary? Languages { get; set; } //unable to init here for constr arg + + /// + /// List of detected tag counters i.e. metrics + /// + [JsonProperty(PropertyName = "tagCounters")] + public List? TagCounters { get; set; } = new(); + + /// + /// List of detailed MatchRecords from scan + /// + [JsonProperty(PropertyName = "detailedMatchList")] + public List Matches { get; set; } = new(); + + [JsonProperty(PropertyName = "filesInformation")] + public List Files { get; set; } = new(); } \ No newline at end of file diff --git a/AppInspector/MetaDataHelper.cs b/AppInspector/MetaDataHelper.cs index bb8d2d9..6cd6922 100644 --- a/AppInspector/MetaDataHelper.cs +++ b/AppInspector/MetaDataHelper.cs @@ -1,361 +1,310 @@ // Copyright (C) Microsoft. All rights reserved. // Licensed under the MIT License. See LICENSE.txt in the project root for license information. -namespace Microsoft.ApplicationInspector.Commands +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Microsoft.ApplicationInspector.RulesEngine; + +namespace Microsoft.ApplicationInspector.Commands; + +/// +/// Provides utilty help specific to aggregating metadata from analyze cmd matches while isolating scanned data from +/// that process +/// Hides complexity i.e. threaded scanning so that caller gets simple list +/// that is presorted and consistent each scan +/// +public class MetaDataHelper { - using Microsoft.ApplicationInspector.RulesEngine; - using System; - using System.Collections.Concurrent; - using System.Collections.Generic; - using System.IO; - using System.Linq; + public MetaDataHelper(string sourcePath) + { + if (!sourcePath.Contains(',')) sourcePath = Path.GetFullPath(sourcePath); //normalize for .\ and similar + Metadata = new MetaData(sourcePath, sourcePath); + } + + //visible to callers i.e. AnalyzeCommand + internal ConcurrentDictionary PackageTypes { get; set; } = new(); + internal ConcurrentDictionary FileExtensions { get; set; } = new(); + internal ConcurrentDictionary UniqueDependencies { get; set; } = new(); + + private ConcurrentDictionary AppTypes { get; } = new(); + internal ConcurrentDictionary UniqueTags { get; set; } = new(); + private ConcurrentDictionary Outputs { get; } = new(); + private ConcurrentDictionary Targets { get; } = new(); + private ConcurrentDictionary CPUTargets { get; } = new(); + private ConcurrentDictionary CloudTargets { get; } = new(); + private ConcurrentDictionary OSTargets { get; } = new(); + private ConcurrentDictionary TagCounters { get; } = new(); + private ConcurrentDictionary Languages { get; } = new(); + + internal ConcurrentBag Matches { get; set; } = new(); + internal ConcurrentBag Files { get; set; } = new(); + + public int UniqueTagsCount => UniqueTags.Keys.Count; + + internal MetaData Metadata { get; set; } + + internal bool HasFindings => Matches.Any() || TagCounters.Any() || UniqueTags.Any(); /// - /// Provides utilty help specific to aggregating metadata from analyze cmd matches while isolating scanned data from that process - /// Hides complexity i.e. threaded scanning so that caller gets simple list that is presorted and consistent each scan + /// Assist in aggregating reporting properties of matches as they are added + /// Keeps helpers isolated from MetaData class which is used as a result object to keep pure /// - public class MetaDataHelper + /// + public void AddTagsFromMatchRecord(MatchRecord matchRecord) { - //visible to callers i.e. AnalyzeCommand - internal ConcurrentDictionary PackageTypes { get; set; } = new ConcurrentDictionary(); - internal ConcurrentDictionary FileExtensions { get; set; } = new ConcurrentDictionary(); - internal ConcurrentDictionary UniqueDependencies { get; set; } = new ConcurrentDictionary(); - - private ConcurrentDictionary AppTypes { get; set; } = new ConcurrentDictionary(); - internal ConcurrentDictionary UniqueTags { get; set; } = new ConcurrentDictionary(); - private ConcurrentDictionary Outputs { get; set; } = new ConcurrentDictionary(); - private ConcurrentDictionary Targets { get; set; } = new ConcurrentDictionary(); - private ConcurrentDictionary CPUTargets { get; set; } = new ConcurrentDictionary(); - private ConcurrentDictionary CloudTargets { get; set; } = new ConcurrentDictionary(); - private ConcurrentDictionary OSTargets { get; set; } = new ConcurrentDictionary(); - private ConcurrentDictionary TagCounters { get; set; } = new ConcurrentDictionary(); - private ConcurrentDictionary Languages { get; set; } = new ConcurrentDictionary(); - - internal ConcurrentBag Matches { get; set; } = new ConcurrentBag(); - internal ConcurrentBag Files { get; set; } = new ConcurrentBag(); - - public int UniqueTagsCount { get { return UniqueTags.Keys.Count; } } - - internal MetaData Metadata { get; set; } - - internal bool HasFindings - { - get + //special handling for standard characteristics in report + foreach (var tag in matchRecord.Tags ?? Array.Empty()) + switch (tag) { - return Matches.Any() || TagCounters.Any() || UniqueTags.Any(); + case "Metadata.Application.Author": + case "Metadata.Application.Publisher": + Metadata.Authors = ExtractValue(matchRecord.Sample); + break; + case "Metadata.Application.Description": + Metadata.Description = ExtractValue(matchRecord.Sample); + break; + case "Metadata.Application.Name": + Metadata.ApplicationName = ExtractValue(matchRecord.Sample); + break; + case "Metadata.Application.Version": + Metadata.SourceVersion = ExtractValue(matchRecord.Sample); + break; + case "Metadata.Application.Target.Processor": + CPUTargets.TryAdd(ExtractValue(matchRecord.Sample).ToLower(), 0); + break; + case "Metadata.Application.Output.Type": + Outputs.TryAdd(ExtractValue(matchRecord.Sample).ToLower(), 0); + break; + case "Dependency.SourceInclude": + return; //design to keep noise out of detailed match list + default: + if (tag.Split('.').Contains("Metric")) + _ = TagCounters.TryAdd(tag, new MetricTagCounter + { + Tag = tag + }); + else if (tag.Contains(".Platform.OS")) + OSTargets.TryAdd(tag[(tag.LastIndexOf('.', tag.Length - 1) + 1)..], 0); + else if (tag.Contains("CloudServices.Hosting")) + CloudTargets.TryAdd(tag[(tag.LastIndexOf('.', tag.Length - 1) + 1)..], 0); + break; } + + //Special handling; attempt to detect app types...review for multiple pattern rule limitation + var solutionType = DetectSolutionType(matchRecord); + if (!string.IsNullOrEmpty(solutionType)) AppTypes.TryAdd(solutionType, 0); + + var CounterOnlyTagSet = false; + var selected = matchRecord.Tags is not null + ? TagCounters.Where(x => matchRecord.Tags.Any(y => y.Contains(x.Value.Tag ?? ""))) + : new Dictionary(); + foreach (var select in selected) + { + CounterOnlyTagSet = true; + select.Value.IncrementCount(); } - public MetaDataHelper(string sourcePath) - { - if (!sourcePath.Contains(',')) - { - sourcePath = Path.GetFullPath(sourcePath);//normalize for .\ and similar - } - Metadata = new MetaData(sourcePath, sourcePath); - } - - /// - /// Assist in aggregating reporting properties of matches as they are added - /// Keeps helpers isolated from MetaData class which is used as a result object to keep pure - /// - /// - public void AddTagsFromMatchRecord(MatchRecord matchRecord) - { - //special handling for standard characteristics in report + //omit adding if ther a counter metric tag + if (!CounterOnlyTagSet) + //update list of unique tags as we go foreach (var tag in matchRecord.Tags ?? Array.Empty()) + if (!UniqueTags.TryAdd(tag, 1)) + UniqueTags[tag]++; + } + + /// + /// Assist in aggregating reporting properties of matches as they are added + /// Keeps helpers isolated from MetaData class which is used as a result object to keep pure + /// + /// + public void AddMatchRecord(MatchRecord matchRecord) + { + AddTagsFromMatchRecord(matchRecord); + + var nonCounters = matchRecord.Tags?.Where(x => !TagCounters.Any(y => y.Key == x)) ?? Array.Empty(); + + //omit adding if it if all the tags were counters + if (nonCounters.Any()) Matches.Add(matchRecord); + } + + + /// + /// Transfer concurrent data from scan to analyze result with sorted, simplier types for callers + /// + public void PrepareReport() + { + Metadata.CPUTargets = CPUTargets.Keys.ToList(); + Metadata.AppTypes = AppTypes.Keys.ToList(); + Metadata.OSTargets = OSTargets.Keys.ToList(); + Metadata.UniqueDependencies = UniqueDependencies.Keys.ToList(); + Metadata.UniqueTags = UniqueTags.Keys.ToList(); + Metadata.CloudTargets = CloudTargets.Keys.ToList(); + Metadata.PackageTypes = PackageTypes.Keys.ToList(); + Metadata.FileExtensions = FileExtensions.Keys.ToList(); + Metadata.Outputs = Outputs.Keys.ToList(); + Metadata.Targets = Targets.Keys.ToList(); + + Metadata.Files = Files.ToList(); + Metadata.Matches = Matches.ToList(); + + Metadata.CPUTargets.Sort(); + Metadata.AppTypes.Sort(); + Metadata.OSTargets.Sort(); + Metadata.UniqueDependencies.Sort(); + Metadata.UniqueTags.Sort(); + Metadata.CloudTargets.Sort(); + Metadata.PackageTypes.Sort(); + Metadata.FileExtensions.Sort(); + Metadata.Outputs.Sort(); + Metadata.Targets.Sort(); + + Metadata.Languages = new SortedDictionary(Languages); + + foreach (var metricTagCounter in TagCounters.Values) Metadata.TagCounters?.Add(metricTagCounter); + } + + /// + /// Defined here to isolate MetaData from data processing methods and keep as pure data + /// + /// + public void AddLanguage(string language) + { + Languages.AddOrUpdate(language, 1, (language, count) => count + 1); + } + + /// + /// Initial best guess to deduce project name; if scanned metadata from project solution value is replaced later + /// + /// + /// + private string GetDefaultProjectName(string sourcePath) + { + var applicationName = string.Empty; + + if (Directory.Exists(sourcePath)) + { + if (sourcePath != string.Empty) { - switch (tag) + if (sourcePath[^1] == Path.DirectorySeparatorChar) //in case path ends with dir separator; remove + applicationName = sourcePath.Trim(Path.DirectorySeparatorChar); + if (applicationName.LastIndexOf(Path.DirectorySeparatorChar) is int idx && idx != -1) + applicationName = applicationName[idx..].Trim(); + } + } + else + { + applicationName = Path.GetFileNameWithoutExtension(sourcePath); + } + + return applicationName; + } + + /// + /// Attempt to map application type tags or file type or language to identify + /// WebApplications, Windows Services, Client Apps, WebServices, Azure Functions etc. + /// + /// + public string DetectSolutionType(MatchRecord match) + { + var result = ""; + if (match.Tags is not null && match.Tags.Any(s => s.Contains("Application.Type"))) + foreach (var tag in match.Tags ?? Array.Empty()) + { + var index = tag.IndexOf("Application.Type"); + if (-1 != index) { - case "Metadata.Application.Author": - case "Metadata.Application.Publisher": - Metadata.Authors = ExtractValue(matchRecord.Sample); - break; - case "Metadata.Application.Description": - Metadata.Description = ExtractValue(matchRecord.Sample); - break; - case "Metadata.Application.Name": - Metadata.ApplicationName = ExtractValue(matchRecord.Sample); - break; - case "Metadata.Application.Version": - Metadata.SourceVersion = ExtractValue(matchRecord.Sample); - break; - case "Metadata.Application.Target.Processor": - CPUTargets.TryAdd(ExtractValue(matchRecord.Sample).ToLower(), 0); - break; - case "Metadata.Application.Output.Type": - Outputs.TryAdd(ExtractValue(matchRecord.Sample).ToLower(), 0); - break; - case "Dependency.SourceInclude": - return; //design to keep noise out of detailed match list - default: - if (tag.Split('.').Contains("Metric")) - { - _ = TagCounters.TryAdd(tag, new MetricTagCounter() + result = tag[(index + 17)..]; + break; + } + } + else + switch (match.FileName) + { + case "web.config": + result = "Web.Application"; + break; + + case "app.config": + result = ".NETclient"; + break; + + default: + switch (Path.GetExtension(match.FileName)) + { + case ".cshtml": + result = "Web.Application"; + break; + + case ".htm": + case ".html": + case ".js": + case ".ts": + result = "Web.Application"; + break; + + case "powershell": + case "shellscript": + case "wincmdscript": + result = "script"; + break; + + default: + switch (match.Language) { - Tag = tag - }); - } - else if (tag.Contains(".Platform.OS")) - { - OSTargets.TryAdd(tag[(tag.LastIndexOf('.', tag.Length - 1) + 1)..], 0); - } - else if (tag.Contains("CloudServices.Hosting")) - { - CloudTargets.TryAdd(tag[(tag.LastIndexOf('.', tag.Length - 1) + 1)..], 0); - } - break; - } - } + case "ruby": + case "perl": + case "php": + result = "Web.Application"; + break; + } - //Special handling; attempt to detect app types...review for multiple pattern rule limitation - string solutionType = DetectSolutionType(matchRecord); - if (!string.IsNullOrEmpty(solutionType)) - { - AppTypes.TryAdd(solutionType, 0); - } - - bool CounterOnlyTagSet = false; - var selected = matchRecord.Tags is not null ? TagCounters.Where(x => matchRecord.Tags.Any(y => y.Contains(x.Value.Tag ?? ""))) : new Dictionary(); - foreach (var select in selected) - { - CounterOnlyTagSet = true; - select.Value.IncrementCount(); - } - - //omit adding if ther a counter metric tag - if (!CounterOnlyTagSet) - { - //update list of unique tags as we go - foreach (string tag in matchRecord.Tags ?? Array.Empty()) - { - if (!UniqueTags.TryAdd(tag, 1)) - { - UniqueTags[tag]++; + break; } - } - } - } - /// - /// Assist in aggregating reporting properties of matches as they are added - /// Keeps helpers isolated from MetaData class which is used as a result object to keep pure - /// - /// - public void AddMatchRecord(MatchRecord matchRecord) + break; + } + + return result.ToLower(); + } + + private string ExtractValue(string s) + { + if (s.ToLower().Contains(""); + if (firstTag > -1 && firstTag < s.Length - 1) { - AddTagsFromMatchRecord(matchRecord); - - var nonCounters = matchRecord.Tags?.Where(x => !TagCounters.Any(y => y.Key == x)) ?? Array.Empty(); - - //omit adding if it if all the tags were counters - if (nonCounters.Any()) - { - Matches.Add(matchRecord); - } + var endTag = s.IndexOf(" -1) return s[(firstTag + 1)..endTag]; } + return s; + } - /// - /// Transfer concurrent data from scan to analyze result with sorted, simplier types for callers - /// - public void PrepareReport() - { - Metadata.CPUTargets = CPUTargets.Keys.ToList(); - Metadata.AppTypes = AppTypes.Keys.ToList(); - Metadata.OSTargets = OSTargets.Keys.ToList(); - Metadata.UniqueDependencies = UniqueDependencies.Keys.ToList(); - Metadata.UniqueTags = UniqueTags.Keys.ToList(); - Metadata.CloudTargets = CloudTargets.Keys.ToList(); - Metadata.PackageTypes = PackageTypes.Keys.ToList(); - Metadata.FileExtensions = FileExtensions.Keys.ToList(); - Metadata.Outputs = Outputs.Keys.ToList(); - Metadata.Targets = Targets.Keys.ToList(); - - Metadata.Files = Files.ToList(); - Metadata.Matches = Matches.ToList(); - - Metadata.CPUTargets.Sort(); - Metadata.AppTypes.Sort(); - Metadata.OSTargets.Sort(); - Metadata.UniqueDependencies.Sort(); - Metadata.UniqueTags.Sort(); - Metadata.CloudTargets.Sort(); - Metadata.PackageTypes.Sort(); - Metadata.FileExtensions.Sort(); - Metadata.Outputs.Sort(); - Metadata.Targets.Sort(); - - Metadata.Languages = new SortedDictionary(Languages); - - foreach (MetricTagCounter metricTagCounter in TagCounters.Values) - { - Metadata.TagCounters?.Add(metricTagCounter); - } - } - - /// - /// Defined here to isolate MetaData from data processing methods and keep as pure data - /// - /// - public void AddLanguage(string language) - { - Languages.AddOrUpdate(language, 1, (language, count) => count + 1); - } - - /// - /// Initial best guess to deduce project name; if scanned metadata from project solution value is replaced later - /// - /// - /// - private string GetDefaultProjectName(string sourcePath) - { - string applicationName = string.Empty; - - if (Directory.Exists(sourcePath)) - { - if (sourcePath != string.Empty) - { - if (sourcePath[^1] == Path.DirectorySeparatorChar) //in case path ends with dir separator; remove - { - applicationName = sourcePath.Trim(Path.DirectorySeparatorChar); - } - if (applicationName.LastIndexOf(Path.DirectorySeparatorChar) is int idx && idx != -1) - { - applicationName = applicationName[idx..].Trim(); - } - } - } - else - { - applicationName = Path.GetFileNameWithoutExtension(sourcePath); - } - - return applicationName; - } - - /// - /// Attempt to map application type tags or file type or language to identify - /// WebApplications, Windows Services, Client Apps, WebServices, Azure Functions etc. - /// - /// - public string DetectSolutionType(MatchRecord match) - { - string result = ""; - if (match.Tags is not null && match.Tags.Any(s => s.Contains("Application.Type"))) - { - foreach (string tag in match.Tags ?? Array.Empty()) - { - int index = tag.IndexOf("Application.Type"); - if (-1 != index) - { - result = tag[(index + 17)..]; - break; - } - } - } - else - { - switch (match.FileName) - { - case "web.config": - result = "Web.Application"; - break; - - case "app.config": - result = ".NETclient"; - break; - - default: - switch (Path.GetExtension(match.FileName)) - { - case ".cshtml": - result = "Web.Application"; - break; - - case ".htm": - case ".html": - case ".js": - case ".ts": - result = "Web.Application"; - break; - - case "powershell": - case "shellscript": - case "wincmdscript": - result = "script"; - break; - - default: - switch (match.Language) - { - case "ruby": - case "perl": - case "php": - result = "Web.Application"; - break; - } - break; - } - break; - } - } - - return result.ToLower(); - } - - private string ExtractValue(string s) - { - if (s.ToLower().Contains(""); - if (firstTag > -1 && firstTag < s.Length - 1) - { - int endTag = s.IndexOf(" -1) - { - return s[(firstTag + 1)..endTag]; - } - } - - return s; - } - - private string ExtractXMLValueMultiLine(string s) - { - int firstTag = s.IndexOf(">"); - if (firstTag > -1 && firstTag < s.Length - 1) - { - return s[(firstTag + 1)..]; - } - return s; - } + private string ExtractXMLValueMultiLine(string s) + { + var firstTag = s.IndexOf(">"); + if (firstTag > -1 && firstTag < s.Length - 1) return s[(firstTag + 1)..]; + return s; } } \ No newline at end of file diff --git a/AppInspector/MetricTagCounter.cs b/AppInspector/MetricTagCounter.cs index 3ea4753..0454ca8 100644 --- a/AppInspector/MetricTagCounter.cs +++ b/AppInspector/MetricTagCounter.cs @@ -1,27 +1,25 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -namespace Microsoft.ApplicationInspector.Commands +using System.Threading; +using Newtonsoft.Json; + +namespace Microsoft.ApplicationInspector.Commands; + +/// +/// Used for metric results not included in match details reported other than count of instances for given tag i.e. +/// Metrics.[value].[value] +/// +public class MetricTagCounter { - using Newtonsoft.Json; - using System.Threading; + private int _count; - /// - /// Used for metric results not included in match details reported other than count of instances for given tag i.e. Metrics.[value].[value] - /// - public class MetricTagCounter + [JsonProperty(PropertyName = "tag")] public string? Tag { get; set; } + + [JsonProperty(PropertyName = "count")] public int Count => _count; + + internal void IncrementCount(int amount = 1) { - [JsonProperty(PropertyName = "tag")] - public string? Tag { get; set; } - - [JsonProperty(PropertyName = "count")] - public int Count { get { return _count; } } - - private int _count = 0; - - internal void IncrementCount(int amount = 1) - { - Interlocked.Add(ref _count, amount); - } + Interlocked.Add(ref _count, amount); } } \ No newline at end of file diff --git a/AppInspector/Properties/PublishProfiles/FolderProfile.pubxml b/AppInspector/Properties/PublishProfiles/FolderProfile.pubxml index 3978c9a..3c20dae 100644 --- a/AppInspector/Properties/PublishProfiles/FolderProfile.pubxml +++ b/AppInspector/Properties/PublishProfiles/FolderProfile.pubxml @@ -3,12 +3,12 @@ https://go.microsoft.com/fwlink/?LinkID=208121. --> - - FileSystem - Release - Any CPU - netcoreapp3.0 - bin\Release\netcoreapp3.0\publish\ - false - + + FileSystem + Release + Any CPU + netcoreapp3.0 + bin\Release\netcoreapp3.0\publish\ + false + \ No newline at end of file diff --git a/AppInspector/Properties/Resources.Designer.cs b/AppInspector/Properties/Resources.Designer.cs index dbd1d16..621e27e 100644 --- a/AppInspector/Properties/Resources.Designer.cs +++ b/AppInspector/Properties/Resources.Designer.cs @@ -1,7 +1,6 @@ //------------------------------------------------------------------------------ // // This code was generated by a tool. -// Runtime Version:4.0.30319.42000 // // Changes to this file may cause incorrect behavior and will be lost if // the code is regenerated. diff --git a/AppInspector/Properties/Resources.resx b/AppInspector/Properties/Resources.resx index b6c1d2a..432451d 100644 --- a/AppInspector/Properties/Resources.resx +++ b/AppInspector/Properties/Resources.resx @@ -1,316 +1,326 @@  - - - - - - - + + + + - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, + PublicKeyToken=b77a5c561934e089 + + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, + PublicKeyToken=b77a5c561934e089 + + + Problem while decompressing {0}: Unzip and try running on uncompressed files - + Decompressing may take longer for larger files - + compressed - + Decompressing files... - + File skipped: Name or path in exclude list. {0} - + File skipped: First 100 bytes appear to indicate this is a binary file. {0} - + File skipped: File size is too large. {0} - + {0}% source files processed - + Unable to determine file type. File open error for {0} - + The output file does not have an html or htm extension and may not open properly in your browser - + File skipped: Language not found for file {0} - + Allow duplicate matches argument not supported for html output format. Select a different output format (text/json) or remove the argument. - + No pattern matches were detected for files in source path Overall processing timeout hit. - + No file types found in source path that are supported - + The output.html file size is large and may render slowly. Consider running using alternate output format options as needed. - + Simple tags only argument not supported for html output format. Select a different output format or remove the argument - + uncompressed - + unsupported - + Unable to launch output.html automatically. Set the BROWSER environment variable to your desired browser and try again or launch your browser and navigate to the file to view the report file manually. - + Unable to launch output.html in default browser. Launch your browser manually to view output.html report file - + Opening default browser to output.html report - + {0} command completed - + Critical error processing file {0} - + Invalid {0} argument value - + Invalid file or directory {0} - + The requested log file {0} cannot be written. - + Invalid rule path {0} - + No rules specified. At least one valid rules path required - + No output specified. Raise the console versosity or provide an output file argument. - + Preparing report - + Additional details may be found in log file at {0} - + {0} report completed - + Required {0} argument missing - + {0} command running - + See output file at {0} - - - ..\..\AppInspector.RulesEngine\Resources\comments.json;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - ..\..\AppInspector.RulesEngine\Resources\languages.json;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - + + + ..\..\AppInspector.RulesEngine\Resources\comments.json;System.Byte[], mscorlib, Version=4.0.0.0, + Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + ..\..\AppInspector.RulesEngine\Resources\languages.json;System.Byte[], mscorlib, Version=4.0.0.0, + Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Output file not specified for custom rules pack command - + Pack default rules request from outside CLI call not supported. Use custom rules option. - + Unable to locate local default rules folder. This command is intended to be run as part of the source code build operation only. Use the custom rules option instead. - + Additional details may be found in log file at {0} - + No additional information or log available - + Additional details may be found in log file at {0} - + No tags found in one or both source paths - + Files contain tag differences: - + failed - + Tags in {0} not detected in {1}: - + succeeded - + Test for all [{0}] in source - + Same file passed in for both sources. Test terminated - + failed - + none - + succeeded - + Found {0} in source - + Missing {0} in source - + Tagtest for [{0}]: - + Rule {0} failed from dupicate rule id specified - + Rule {0} failed from unrecognized language specified - + Verify default rules request from outside CLI call not supported. Use custom rules option. - + Rule {0} failed from missing rule id - + Rule {0} failed from invalid regex '{1}' with {2} - + Verify rules failed. See log file for details - + Verify rules succeeded - + Rule parsing failed for file {0} \ No newline at end of file diff --git a/AppInspector/Result.cs b/AppInspector/Result.cs index 85047eb..9fce18c 100644 --- a/AppInspector/Result.cs +++ b/AppInspector/Result.cs @@ -1,16 +1,15 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -namespace Microsoft.ApplicationInspector.Commands -{ - using Newtonsoft.Json; +using Newtonsoft.Json; - /// - /// base for all command operation results - /// - public class Result - { - [JsonProperty(Order = 1, PropertyName = "appVersion")] - public string? AppVersion { get; set; } - } +namespace Microsoft.ApplicationInspector.Commands; + +/// +/// base for all command operation results +/// +public class Result +{ + [JsonProperty(Order = 1, PropertyName = "appVersion")] + public string? AppVersion { get; set; } } \ No newline at end of file diff --git a/AppInspector/RuleSetUtils.cs b/AppInspector/RuleSetUtils.cs index 24b5d03..5ad3d5d 100644 --- a/AppInspector/RuleSetUtils.cs +++ b/AppInspector/RuleSetUtils.cs @@ -1,35 +1,35 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. -namespace Microsoft.ApplicationInspector.Commands +using System.IO; +using System.Linq; +using System.Reflection; +using Microsoft.ApplicationInspector.RulesEngine; +using Microsoft.Extensions.Logging; + +namespace Microsoft.ApplicationInspector.Commands; + +//Miscellenous common methods needed from several places throughout +public static class RuleSetUtils { - using Microsoft.ApplicationInspector.RulesEngine; - using Microsoft.Extensions.Logging; - using System.IO; - using System.Linq; - using System.Reflection; - - //Miscellenous common methods needed from several places throughout - public static class RuleSetUtils + /// + /// Common method of retrieving rules from AppInspector.Commands manifest + /// + /// If you want log message, provide a loggerfactory configured to your preferences. + /// The default RuleSet embedded in the App Inspector binary. + public static RuleSet GetDefaultRuleSet(ILoggerFactory? loggerFactory = null) { - /// - /// Common method of retrieving rules from AppInspector.Commands manifest - /// - /// If you want log message, provide a loggerfactory configured to your preferences. - /// The default RuleSet embedded in the App Inspector binary. - public static RuleSet GetDefaultRuleSet(ILoggerFactory? loggerFactory = null) + RuleSet ruleSet = new(loggerFactory); + var assembly = Assembly.GetExecutingAssembly(); + var resNames = Assembly.GetExecutingAssembly().GetManifestResourceNames(); + foreach (var resName in resNames.Where(x => + x.StartsWith("Microsoft.ApplicationInspector.Commands.rules.default"))) { - RuleSet ruleSet = new(loggerFactory); - Assembly assembly = Assembly.GetExecutingAssembly(); - string[] resNames = Assembly.GetExecutingAssembly().GetManifestResourceNames(); - foreach (string resName in resNames.Where(x => x.StartsWith("Microsoft.ApplicationInspector.Commands.rules.default"))) - { - Stream? resource = assembly.GetManifestResourceStream(resName); - using StreamReader file = new(resource ?? new MemoryStream()); - ruleSet.AddString(file.ReadToEnd(), resName, null); - } - - return ruleSet; + var resource = assembly.GetManifestResourceStream(resName); + using StreamReader file = new(resource ?? new MemoryStream()); + ruleSet.AddString(file.ReadToEnd(), resName); } + + return ruleSet; } } \ No newline at end of file diff --git a/AppInspector/rules/README.md b/AppInspector/rules/README.md index 70d51bb..5cc4785 100644 --- a/AppInspector/rules/README.md +++ b/AppInspector/rules/README.md @@ -1,10 +1,11 @@ # Application Inspector Rules -This repository contains the default rule set for [Application Inspector](https://github.com/Microsoft/ApplicationInspector/). +This repository contains the default rule set +for [Application Inspector](https://github.com/Microsoft/ApplicationInspector/). ## Contributing -This project welcomes contributions and suggestions. Most contributions require you to agree to a +This project welcomes contributions and suggestions. Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com. @@ -18,8 +19,10 @@ contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additio ## Trademarks -This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft -trademarks or logos is subject to and must follow -[Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general). -Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. +This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft +trademarks or logos is subject to and must follow +[Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general) +. +Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft +sponsorship. Any use of third-party trademarks or logos are subject to those third-party's policies. diff --git a/AppInspector/rules/SECURITY.md b/AppInspector/rules/SECURITY.md index f7b8998..f41f8d0 100644 --- a/AppInspector/rules/SECURITY.md +++ b/AppInspector/rules/SECURITY.md @@ -2,33 +2,45 @@ ## Security -Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). +Microsoft takes the security of our software products and services seriously, which includes all source code +repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft) +, [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet) +, [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). -If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)), please report it to us as described below. +If you believe you have found a security vulnerability in any Microsoft-owned repository that +meets [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)) +, please report it to us as described below. ## Reporting Security Issues **Please do not report security vulnerabilities through public GitHub issues.** -Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report). +Instead, please report them to the Microsoft Security Response Center (MSRC) +at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report). -If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc). +If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If +possible, encrypt your message with our PGP key; please download it from +the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc). -You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). +You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we +received your original message. Additional information can be found +at [microsoft.com/msrc](https://www.microsoft.com/msrc). -Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: +Please include the requested information listed below (as much as you can provide) to help us better understand the +nature and scope of the possible issue: - * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) - * Full paths of source file(s) related to the manifestation of the issue - * The location of the affected source code (tag/branch/commit or direct URL) - * Any special configuration required to reproduce the issue - * Step-by-step instructions to reproduce the issue - * Proof-of-concept or exploit code (if possible) - * Impact of the issue, including how an attacker might exploit the issue +* Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) +* Full paths of source file(s) related to the manifestation of the issue +* The location of the affected source code (tag/branch/commit or direct URL) +* Any special configuration required to reproduce the issue +* Step-by-step instructions to reproduce the issue +* Proof-of-concept or exploit code (if possible) +* Impact of the issue, including how an attacker might exploit the issue This information will help us triage your report more quickly. -If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs. +If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit +our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs. ## Preferred Languages diff --git a/AppInspector/rules/SUPPORT.md b/AppInspector/rules/SUPPORT.md index 291d4d4..947099e 100644 --- a/AppInspector/rules/SUPPORT.md +++ b/AppInspector/rules/SUPPORT.md @@ -3,23 +3,24 @@ **REPO OWNER**: Do you want Customer Service & Support (CSS) support for this product/project? - **No CSS support:** Fill out this template with information about how to file issues and get help. -- **Yes CSS support:** Fill out an intake form at [aka.ms/onboardsupport](https://aka.ms/onboardsupport). CSS will work with/help you to determine next steps. +- **Yes CSS support:** Fill out an intake form at [aka.ms/onboardsupport](https://aka.ms/onboardsupport). CSS will work + with/help you to determine next steps. - **Not sure?** Fill out an intake as though the answer were "Yes". CSS will help you decide. *Then remove this first heading from this SUPPORT.MD file before publishing your repo.* # Support -## How to file issues and get help +## How to file issues and get help -This project uses GitHub Issues to track bugs and feature requests. Please search the existing -issues before filing new issues to avoid duplicates. For new issues, file your bug or +This project uses GitHub Issues to track bugs and feature requests. Please search the existing +issues before filing new issues to avoid duplicates. For new issues, file your bug or feature request as a new Issue. -For help and questions about using this project, please **REPO MAINTAINER: INSERT INSTRUCTIONS HERE +For help and questions about using this project, please **REPO MAINTAINER: INSERT INSTRUCTIONS HERE FOR HOW TO ENGAGE REPO OWNERS OR COMMUNITY FOR HELP. COULD BE A STACK OVERFLOW TAG OR OTHER CHANNEL. WHERE WILL YOU HELP PEOPLE?**. -## Microsoft Support Policy +## Microsoft Support Policy Support for this **PROJECT or PRODUCT** is limited to the resources listed above. diff --git a/AppInspector/rules/default/cloud_services/ad_networks.json b/AppInspector/rules/default/cloud_services/ad_networks.json index 9394f82..9a41e63 100644 --- a/AppInspector/rules/default/cloud_services/ad_networks.json +++ b/AppInspector/rules/default/cloud_services/ad_networks.json @@ -3,15 +3,21 @@ "name": "Miscellaneous: Advertising Network (Google Adsense)", "id": "AI000100", "description": "Miscellaneous: Advertising Network (Google Adsense)", - "tags":[ "CloudServices.AdvertisingNetwork.Google.Adsense" ], + "tags": [ + "CloudServices.AdvertisingNetwork.Google.Adsense" + ], "severity": "moderate", "patterns": [ { "confidence": "high", "pattern": "adsense|googleadservices\\.com|googlesyndication\\.com", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ] + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ] } ] }, @@ -19,15 +25,21 @@ "name": "Miscellaneous: Advertising Network (Outbrain)", "id": "AI000200", "description": "Miscellaneous: Advertising Network (Outbrain)", - "tags":[ "CloudServices.AdvertisingNetwork.Outbrain" ], + "tags": [ + "CloudServices.AdvertisingNetwork.Outbrain" + ], "severity": "moderate", "patterns": [ { "confidence": "high", "pattern": "outbrain.com", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ] + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ] } ] }, @@ -35,15 +47,21 @@ "name": "Miscellaneous: Advertising Network (Bing Ads)", "id": "AI000300", "description": "Miscellaneous: Advertising Network (Bing Ads)", - "tags":[ "CloudServices.AdvertisingNetwork.Microsoft.BingAds" ], + "tags": [ + "CloudServices.AdvertisingNetwork.Microsoft.BingAds" + ], "severity": "moderate", "patterns": [ { "confidence": "high", "pattern": "bingads.com", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ] + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ] } ] } diff --git a/AppInspector/rules/default/cloud_services/bigdata.json b/AppInspector/rules/default/cloud_services/bigdata.json index d27c054..077e4e2 100644 --- a/AppInspector/rules/default/cloud_services/bigdata.json +++ b/AppInspector/rules/default/cloud_services/bigdata.json @@ -12,8 +12,12 @@ { "pattern": ".cloud.databricks.com", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] diff --git a/AppInspector/rules/default/cloud_services/cloud_hosting.json b/AppInspector/rules/default/cloud_services/cloud_hosting.json index 16c2274..54ba731 100644 --- a/AppInspector/rules/default/cloud_services/cloud_hosting.json +++ b/AppInspector/rules/default/cloud_services/cloud_hosting.json @@ -3,14 +3,20 @@ "name": "Cloud Service: Hosting (Amazon Web Services)", "id": "AI000500", "description": "Cloud Service: Hosting (Amazon Web Services)", - "tags":[ "CloudServices.Hosting.Amazon.AWS" ], + "tags": [ + "CloudServices.Hosting.Amazon.AWS" + ], "severity": "moderate", "patterns": [ { "pattern": "\\.amazonaws\\.com|aws\\.amazon\\.com", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -19,20 +25,28 @@ "name": "Cloud Service: Hosting (Apple iCloud)", "id": "AI000600", "description": "Cloud Service: Hosting (Apple iCloud)", - "tags":[ "CloudServices.Hosting.Apple.iCloud" ], + "tags": [ + "CloudServices.Hosting.Apple.iCloud" + ], "severity": "moderate", "patterns": [ { "pattern": "icloud", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" }, { "pattern": "NSUbiquitousKeyValueStore|URLForUbiquityContainerIdentifier", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -41,14 +55,20 @@ "name": "Cloud Service: Hosting (Microsoft Azure)", "id": "AI000700", "description": "Cloud Service: Hosting (Microsoft Azure)", - "tags":[ "CloudServices.Hosting.Microsoft.Azure" ], + "tags": [ + "CloudServices.Hosting.Microsoft.Azure" + ], "severity": "moderate", "patterns": [ { "pattern": "azure", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -57,19 +77,25 @@ "name": "Cloud Service: Application (Microsoft Office 365)", "id": "AI000800", "description": "Cloud Service: Application (Microsoft Office 365)", - "tags":[ "CloudServices.Application.Microsoft.O365" ], + "tags": [ + "CloudServices.Application.Microsoft.O365" + ], "severity": "moderate", "patterns": [ { "pattern": "o365|m365|office365|office\\.com", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "graph\\.microsoft\\.com|MicrosoftGraph", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -78,14 +104,20 @@ "name": "Cloud Service: Application (Telemetry)", "id": "AI000810", "description": "Cloud Service: Application (Telemetry)", - "applies_to": [ "csharp" ], - "tags":[ "CloudServices.Application.Microsoft.Telemetry" ], + "applies_to": [ + "csharp" + ], + "tags": [ + "CloudServices.Application.Microsoft.Telemetry" + ], "severity": "moderate", "patterns": [ { "pattern": "TelemetryClient|Microsoft\\.ApplicationInsights|MetricTelemetry", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -94,14 +126,21 @@ "name": "Cloud Service: Application (Telemetry)", "id": "AI000820", "description": "Cloud Service: Application (Telemetry)", - "applies_to": [ "javascript", "typescript" ], - "tags":[ "CloudServices.Application.Microsoft.Telemetry" ], + "applies_to": [ + "javascript", + "typescript" + ], + "tags": [ + "CloudServices.Application.Microsoft.Telemetry" + ], "severity": "moderate", "patterns": [ { "pattern": "appInsights.trackMetric", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -110,15 +149,21 @@ "name": "Cloud Service: Application (Telemetry)", "id": "AI000830", "description": "Cloud Service: Application (Telemetry)", - "tags":[ "CloudServices.Application.Microsoft.Telemetry" ], + "tags": [ + "CloudServices.Application.Microsoft.Telemetry" + ], "severity": "moderate", "patterns": [ { "pattern": "applicationinsights", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", - "modifiers": [ "i" ] + "modifiers": [ + "i" + ] } ] }, @@ -126,14 +171,20 @@ "name": "Cloud Service: Hosting (Google Cloud)", "id": "AI000900", "description": "Cloud Service: Hosting (Google Cloud)", - "tags":[ "CloudServices.Hosting.Google" ], + "tags": [ + "CloudServices.Hosting.Google" + ], "severity": "moderate", "patterns": [ { "pattern": "cloud.google.com", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -142,15 +193,21 @@ "name": "Cloud Service: Hosting (DigitalOcean Cloud)", "id": "AI001000", "description": "Cloud Service: Hosting (DigitalOcean Cloud)", - "tags":[ "CloudServices.Hosting.DigitalOcean" ], + "tags": [ + "CloudServices.Hosting.DigitalOcean" + ], "severity": "moderate", "rule_info": "", "patterns": [ { "pattern": "digitalocean.com", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -159,14 +216,20 @@ "name": "Cloud Service: Application (SendGrid)", "id": "AI001100", "description": "Cloud Service: Application (SendGrid)", - "tags":[ "CloudServices.Application.SendGrid.Mail" ], + "tags": [ + "CloudServices.Application.SendGrid.Mail" + ], "severity": "moderate", "patterns": [ { "pattern": "sendgrid.net", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -175,14 +238,21 @@ "name": "Cloud Service: Code Repository (GitHub)", "id": "AI001200", "description": "Cloud Service: Code Repository (GitHub)", - "tags":[ "CloudServices.Code.Repo.GitHub" ], + "tags": [ + "CloudServices.Code.Repo.GitHub" + ], "severity": "moderate", "patterns": [ { "pattern": "github", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -191,14 +261,21 @@ "name": "Cloud Service: Code Repository (GitLab)", "id": "AI001300", "description": "Cloud Service: Code Repository (GitLab)", - "tags":[ "CloudServices.Code.Repo.GitLab" ], + "tags": [ + "CloudServices.Code.Repo.GitLab" + ], "severity": "moderate", "patterns": [ { "pattern": "gitlab", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -207,14 +284,21 @@ "name": "Cloud Service: Code Repository (BitBucket)", "id": "AI001400", "description": "Cloud Service: Code Repository (BitBucket)", - "tags":[ "CloudServices.Code.Repo.BitBucket" ], + "tags": [ + "CloudServices.Code.Repo.BitBucket" + ], "severity": "moderate", "patterns": [ { "pattern": "bitbucket", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -223,14 +307,21 @@ "name": "Cloud Service: Code Repository (SourceForge)", "id": "AI001500", "description": "Cloud Service: Code Repository (SourceForge)", - "tags":[ "CloudServices.Code.Repo.SourceForge" ], + "tags": [ + "CloudServices.Code.Repo.SourceForge" + ], "severity": "moderate", "patterns": [ { "pattern": "sourceforge", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -239,14 +330,21 @@ "name": "Cloud Service: Code Repository (Savannah)", "id": "AI001600", "description": "Cloud Service: Code Repository (Savannah)", - "tags":[ "CloudServices.Code.Repo.Savannah" ], + "tags": [ + "CloudServices.Code.Repo.Savannah" + ], "severity": "moderate", "patterns": [ { "pattern": "savannah\\.(non)?gnu\\.org", "type": "regex", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -255,14 +353,21 @@ "name": "Cloud Service: Code Package Repository (NuGet)", "id": "AI001700", "description": "Cloud Service: Code Package Repository (NuGet)", - "tags":[ "CloudServices.Code.Repo.Microsoft.NuGet" ], + "tags": [ + "CloudServices.Code.Repo.Microsoft.NuGet" + ], "severity": "moderate", "patterns": [ { "pattern": "nuget", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -271,14 +376,21 @@ "name": "Cloud Service: Code Package Repository (NPM)", "id": "AI001800", "description": "Cloud Service: Code Package Repository (NPM)", - "tags":[ "CloudServices.Code.Repo.NPM" ], + "tags": [ + "CloudServices.Code.Repo.NPM" + ], "severity": "moderate", "patterns": [ { "pattern": "npm", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ] + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ] } ] }, @@ -286,14 +398,21 @@ "name": "Cloud Service: Code Package Repository (PyPI)", "id": "AI001900", "description": "Cloud Service: Code Package Repository (PyPI)", - "tags":[ "CloudServices.Code.Repo.PyPI" ], + "tags": [ + "CloudServices.Code.Repo.PyPI" + ], "severity": "moderate", "patterns": [ { "pattern": "pypi", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -302,14 +421,21 @@ "name": "Cloud Service: Code Package Repository (LaunchPad)", "id": "AI002000", "description": "Cloud Service: Code Package Repository (LaunchPad)", - "tags":[ "CloudServices.Code.Repo.LaunchPad" ], + "tags": [ + "CloudServices.Code.Repo.LaunchPad" + ], "severity": "moderate", "patterns": [ { "pattern": "launchpad.net", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -318,14 +444,21 @@ "name": "Cloud Service: Code Repository (Visual Studio)", "id": "AI001410", "description": "Cloud Service: Code Repository (Visual Studio)", - "tags":[ "CloudServices.Code.Repo.VisualStudio" ], + "tags": [ + "CloudServices.Code.Repo.VisualStudio" + ], "severity": "moderate", "patterns": [ { "pattern": "visualstudio.com", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -334,15 +467,23 @@ "name": "Cloud Service: CI (TravisCI)", "id": "AI002100", "description": "Cloud Service: CI (TravisCI)", - "applies_to":[ "yaml" ], - "tags":[ "CloudServices.Code.CI.TravisCI" ], + "applies_to": [ + "yaml" + ], + "tags": [ + "CloudServices.Code.CI.TravisCI" + ], "severity": "moderate", "patterns": [ { "pattern": "(language|dist|matrix):", "type": "regex", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -351,14 +492,20 @@ "name": "Cloud Service: CI (Circle CI)", "id": "AI002200", "description": "Cloud Service: CI (Circle CI)", - "applies_to":[ "yaml" ], - "tags":[ "CloudServices.Code.CI.CircleCI" ], + "applies_to": [ + "yaml" + ], + "tags": [ + "CloudServices.Code.CI.CircleCI" + ], "severity": "moderate", "patterns": [ { "pattern": "circleci", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -367,14 +514,20 @@ "name": "Cloud Service: CI (Azure Pipelines)", "id": "AI002300", "description": "Cloud Service: CI (Azure Pipelines)", - "applies_to":[ "yaml" ], - "tags":[ "CloudServices.Code.CI.Microsoft.Azure" ], + "applies_to": [ + "yaml" + ], + "tags": [ + "CloudServices.Code.CI.Microsoft.Azure" + ], "severity": "moderate", "patterns": [ { "pattern": "steps:", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -383,14 +536,21 @@ "name": "Cloud Service: Hosting App Store (Windows)", "id": "AI002400", "description": "Cloud Service: Hosting App Store (Windows)", - "tags":[ "CloudServices.Hosting.Microsoft.AppStore" ], + "tags": [ + "CloudServices.Hosting.Microsoft.AppStore" + ], "severity": "moderate", "patterns": [ { "pattern": "Windows Store|Windows App Store|WinAppStore", "type": "regex", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] diff --git a/AppInspector/rules/default/cloud_services/data_storage.json b/AppInspector/rules/default/cloud_services/data_storage.json index 9d1e9c7..cf13dad 100644 --- a/AppInspector/rules/default/cloud_services/data_storage.json +++ b/AppInspector/rules/default/cloud_services/data_storage.json @@ -3,7 +3,9 @@ "name": "CloudServices: DataStorage (Azure)", "id": "AI002500", "description": "CloudServices: DataStorage (Azure Table,File,Blob)", - "applies_to":[ "csharp" ], + "applies_to": [ + "csharp" + ], "tags": [ "CloudServices.DataStorage.Microsoft.Azure", "CloudServices.Hosting.Microsoft.Azure" @@ -13,15 +15,21 @@ { "pattern": "CloudStorageAccount|Microsoft\\.Azure\\.Storage|Microsoft\\.Azure\\.Cosmos\\.Table|Microsoft\\.Azure\\.Documents|Microsoft\\.WindowsAzure\\.Storage", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "cosmosdb", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", - "modifiers": [ "i" ] + "modifiers": [ + "i" + ] } ] }, @@ -35,21 +43,29 @@ "yaml", "pom.xml" ], - "tags":[ "CloudServices.DataStorage.Microsoft.Azure" ], + "tags": [ + "CloudServices.DataStorage.Microsoft.Azure" + ], "severity": "moderate", "patterns": [ { "pattern": "(BlockBlobService|azure)\\.storage|cosmosdb\\.azure\\.com|BlobStorageAccount", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "cosmosdb", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", - "modifiers": [ "i" ] + "modifiers": [ + "i" + ] } ] }, @@ -57,20 +73,28 @@ "name": "CloudServices: DataStorage (Azure)", "id": "AI002700", "description": "CloudServices: DataStorage (Azure Table,File,Blob)", - "applies_to":[ "javascript" ], - "tags":[ "CloudServices.DataStorage.Microsoft.Azure" ], + "applies_to": [ + "javascript" + ], + "tags": [ + "CloudServices.DataStorage.Microsoft.Azure" + ], "severity": "moderate", "patterns": [ { "pattern": "\\.createBlobService", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "medium" }, { "pattern": "azure/storage-blob", "type": "substring", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "medium" } ] @@ -79,7 +103,9 @@ "name": "CloudServices: DataStorage (AzureKeyVault)", "id": "AI002710", "description": "CloudServices: DataStorage (AzureKeyVault)", - "applies_to":[ "csharp" ], + "applies_to": [ + "csharp" + ], "tags": [ "CloudServices.DataStorage.Microsoft.AzureKeyVault", "Data.Sensitive.Secret" @@ -89,22 +115,33 @@ { "pattern": "new ClientSecret|KeyVaultClient|Microsoft\\.Azure\\.KeyVault|Microsoft\\.Azure\\.Management\\.KeyVault", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "keyvault", "type": "regexword", - "scopes": [ "code", "comment" ], - "modifiers": ["i"], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "medium" }, { "pattern": "azure\\.keyvault|azure\\.mgmt\\.keyvault", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", - "modifiers": ["i"] + "modifiers": [ + "i" + ] } ] }, @@ -112,7 +149,9 @@ "name": "CloudServices: DataStorage (HashiCorp Vault)", "id": "AI002730", "description": "CloudServices: DataStorage (HashiCorp Vault)", - "applies_to":[ "csharp" ], + "applies_to": [ + "csharp" + ], "tags": [ "CloudServices.DataStorage.HashiCorp.Vault", "Data.Sensitive.Secret" @@ -122,14 +161,21 @@ { "pattern": "VaultClientSettings|IVaultClient", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "hashicorp|VaultSharp", "type": "regex", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -147,8 +193,13 @@ { "pattern": "vaultSharp|hashicorp|spring vault|import org.springframework.vault", "type": "regex", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high", "_comment": "includes vaultsharp here for possible use in PS" } @@ -162,13 +213,17 @@ "c", "cpp" ], - "tags":[ "CloudServices.DataStorage.Microsoft.Azure" ], + "tags": [ + "CloudServices.DataStorage.Microsoft.Azure" + ], "severity": "moderate", "patterns": [ { "pattern": "azure::storage|#include ", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -177,15 +232,21 @@ "name": "CloudServices: DataStorage (Microsoft OneDrive)", "id": "AI002900", "description": "CloudServices: DataStorage (Microsoft OneDrive)", - "tags":[ "CloudServices.DataStorage.Microsoft.OneDrive" ], + "tags": [ + "CloudServices.DataStorage.Microsoft.OneDrive" + ], "severity": "moderate", "patterns": [ { "pattern": "OneDrive", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", - "modifiers": [ "i" ] + "modifiers": [ + "i" + ] } ] }, @@ -197,13 +258,17 @@ "c", "cpp" ], - "tags":[ "CloudServices.DataStorage.Amazon.S3" ], + "tags": [ + "CloudServices.DataStorage.Amazon.S3" + ], "severity": "moderate", "patterns": [ { "pattern": "aws/s3/|Aws::S3", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -212,14 +277,20 @@ "name": "CloudServices: DataStorage (Amazon S3)", "id": "AI003100", "description": "CloudServices: DataStorage (Amazon S3)", - "applies_to":[ "csharp" ], - "tags":[ "CloudServices.DataStorage.Amazon.S3" ], + "applies_to": [ + "csharp" + ], + "tags": [ + "CloudServices.DataStorage.Amazon.S3" + ], "severity": "moderate", "patterns": [ { "pattern": "Amazon.S3", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -228,14 +299,20 @@ "name": "CloudServices: DataStorage (Amazon S3)", "id": "AI003200", "description": "CloudServices: DataStorage (Amazon S3)", - "applies_to":[ "python" ], - "tags":[ "CloudServices.DataStorage.Amazon.S3" ], + "applies_to": [ + "python" + ], + "tags": [ + "CloudServices.DataStorage.Amazon.S3" + ], "severity": "moderate", "patterns": [ { "pattern": "boto3", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -244,15 +321,21 @@ "name": "CloudServices: DataStorage (Google Drive)", "id": "AI003300", "description": "CloudServices: DataStorage (Google Drive)", - "tags":[ "CloudServices.DataStorage.Google.Drive" ], + "tags": [ + "CloudServices.DataStorage.Google.Drive" + ], "severity": "moderate", "patterns": [ { "pattern": "googleapis.com/upload/drive", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", - "modifiers": ["i"] + "modifiers": [ + "i" + ] } ] }, @@ -260,15 +343,21 @@ "name": "CloudServices: DataStorage (DropBox)", "id": "AI003400", "description": "CloudServices: DataStorage (DropBox)", - "tags":[ "CloudServices.DataStorage.DropBox" ], + "tags": [ + "CloudServices.DataStorage.DropBox" + ], "severity": "moderate", "patterns": [ { "pattern": "api.dropboxapi.com", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", - "modifiers": ["i"] + "modifiers": [ + "i" + ] } ] }, @@ -276,15 +365,21 @@ "name": "CloudServices: DataStorage (MediaFire)", "id": "AI003500", "description": "CloudServices: DataStorage (MediaFire)", - "tags":[ "CloudServices.DataStorage.MediaFire" ], + "tags": [ + "CloudServices.DataStorage.MediaFire" + ], "severity": "moderate", "patterns": [ { "pattern": "MediaFire", "type": "substring", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", - "modifiers": ["i"] + "modifiers": [ + "i" + ] } ] } diff --git a/AppInspector/rules/default/cloud_services/ecommerce.json b/AppInspector/rules/default/cloud_services/ecommerce.json index 53603d8..3ec2680 100644 --- a/AppInspector/rules/default/cloud_services/ecommerce.json +++ b/AppInspector/rules/default/cloud_services/ecommerce.json @@ -3,41 +3,68 @@ "name": "CloudServices: Financial (eCommerce)", "id": "AI003600", "description": "Data: Financial (eCommerce)", - "tags":[ "CloudServices.Finance.eCommerce" ], + "tags": [ + "CloudServices.Finance.eCommerce" + ], "severity": "critical", "patterns": [ { "pattern": "wallet|fips-140", "type": "regexword", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "medium" }, { "pattern": "paypal|google pay|ebay|google\\.com/pay/api", "type": "regex", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" }, { "pattern": "price|shopping chart|payment", "type": "regexword", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ] + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ] }, { "pattern": "bigcommerce|shopify|bigcartel|woocommerce|wc_api_client|weebly|3dcart|squarespace|connect\\.squareup\\.com", "type": "regex", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" }, { "pattern": "demandware|yocart|opencart|magento|magentohost\/api|volusion|x-vtex-api-appkey|alibaba|apple.*pay", "type": "regexword", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] diff --git a/AppInspector/rules/default/cloud_services/socialmedia.json b/AppInspector/rules/default/cloud_services/socialmedia.json index 8cd65c7..1a994bd 100644 --- a/AppInspector/rules/default/cloud_services/socialmedia.json +++ b/AppInspector/rules/default/cloud_services/socialmedia.json @@ -3,14 +3,21 @@ "name": "Social Media: Facebook", "id": "AI003700", "description": "Social Media Facebook", - "tags":[ "CloudServices.SocialMedia.Facebook" ], + "tags": [ + "CloudServices.SocialMedia.Facebook" + ], "severity": "moderate", "patterns": [ { "pattern": "facebook", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -19,14 +26,21 @@ "name": "Social Media: Twitter", "id": "AI003800", "description": "Social Media (Twitter)", - "tags":[ "CloudServices.SocialMedia.Twitter" ], + "tags": [ + "CloudServices.SocialMedia.Twitter" + ], "severity": "moderate", "patterns": [ { "pattern": "twitter", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -35,14 +49,21 @@ "name": "Social Media: YouTube", "id": "AI003900", "description": "Social Media (YouTube)", - "tags":[ "CloudServices.SocialMedia.YouTube" ], + "tags": [ + "CloudServices.SocialMedia.YouTube" + ], "severity": "moderate", "patterns": [ { "pattern": "youtube", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -51,14 +72,21 @@ "name": "Social Media: Instagram", "id": "AI004000", "description": "Social Media: Instagram", - "tags":[ "CloudServices.SocialMedia.Instagram" ], + "tags": [ + "CloudServices.SocialMedia.Instagram" + ], "severity": "moderate", "patterns": [ { "pattern": "instagram", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -67,14 +95,21 @@ "name": "Social Media: Misc", "id": "AI004100", "description": "Social Media: Misc", - "tags":[ "CloudServices.SocialMedia.Misc" ], + "tags": [ + "CloudServices.SocialMedia.Misc" + ], "severity": "moderate", "patterns": [ { "pattern": "reddit|snapchat|whatsapp|tumblr|qzone|weibo|pinterest|ask\\.fm|flickr|linkedin|odnoklassniki|meetup|discord|diaspora|sociall\\.io|mastodon", "type": "regexword", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] diff --git a/AppInspector/rules/default/cloud_services/web_analytics.json b/AppInspector/rules/default/cloud_services/web_analytics.json index ad2a814..b49c1c3 100644 --- a/AppInspector/rules/default/cloud_services/web_analytics.json +++ b/AppInspector/rules/default/cloud_services/web_analytics.json @@ -3,14 +3,21 @@ "name": "Miscellaneous: Analytics Service", "id": "AI004200", "description": "Miscellaneous: Analytics Service", - "tags":[ "CloudServices.Web.Analytics" ], + "tags": [ + "CloudServices.Web.Analytics" + ], "severity": "moderate", "patterns": [ { "pattern": "analytics|tracker|tracking|tracking cookie|pixel url|tracking script", "type": "regexword", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "low" } ] @@ -19,14 +26,20 @@ "name": "Miscellaneous: Analytics Service (Facebook)", "id": "AI004300", "description": "Miscellaneous: Analytics Service (Facebook)", - "tags":[ "CloudServices.Web.Analytics.Facebook" ], + "tags": [ + "CloudServices.Web.Analytics.Facebook" + ], "severity": "moderate", "patterns": [ { "pattern": "connect.facebook.net", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ] + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ] } ] }, @@ -34,14 +47,20 @@ "name": "Miscellaneous: Analytics Service (Google GTag)", "id": "AI004400", "description": "Miscellaneous: Analytics Service (Google GTag)", - "tags":[ "CloudServices.Web.Analytics.Google.GTag" ], + "tags": [ + "CloudServices.Web.Analytics.Google.GTag" + ], "severity": "moderate", "patterns": [ { "pattern": "googletagmanager.com", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ] + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ] } ] }, @@ -49,14 +68,20 @@ "name": "Miscellaneous: Analytics Service (Bing)", "id": "AI004500", "description": "Miscellaneous: Analytics Service (Bing)", - "tags":[ "CloudServices.Web.Analytics.Microsoft.Bing" ], + "tags": [ + "CloudServices.Web.Analytics.Microsoft.Bing" + ], "severity": "moderate", "patterns": [ { "pattern": "bat.bing.com", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ] + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ] } ] }, @@ -64,14 +89,20 @@ "name": "Miscellaneous: Analytics Service (Twitter)", "id": "AI004600", "description": "Miscellaneous: Analytics Service (Twitter)", - "tags":[ "CloudServices.Web.Analytics.Twitter" ], + "tags": [ + "CloudServices.Web.Analytics.Twitter" + ], "severity": "moderate", "patterns": [ { "pattern": "static.ads-twitter.com", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ] + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ] } ] }, @@ -79,14 +110,20 @@ "name": "Miscellaneous: Analytics Service (Outbrain)", "id": "AI004700", "description": "Miscellaneous: Analytics Service (Outbrain)", - "tags":[ "CloudServices.Web.Analytics.Outbrain" ], + "tags": [ + "CloudServices.Web.Analytics.Outbrain" + ], "severity": "moderate", "patterns": [ { "pattern": "amplify.outbrain.com", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ] + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ] } ] }, @@ -94,14 +131,20 @@ "name": "Miscellaneous: Analytics Service (Pinterest)", "id": "AI004800", "description": "Miscellaneous: Analytics Service (Pinterest)", - "tags":[ "CloudServices.Web.Analytics.Pinterest" ], + "tags": [ + "CloudServices.Web.Analytics.Pinterest" + ], "severity": "moderate", "patterns": [ { "pattern": "s.pinimg.com", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ] + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ] } ] } diff --git a/AppInspector/rules/default/components/active_content.json b/AppInspector/rules/default/components/active_content.json index 96dae66..7364b2d 100644 --- a/AppInspector/rules/default/components/active_content.json +++ b/AppInspector/rules/default/components/active_content.json @@ -5,29 +5,46 @@ "description": "Component: Adobe Flash", "applies_to": [ ], - "tags":[ "Component.Executable.Adobe.Flash" ], + "tags": [ + "Component.Executable.Adobe.Flash" + ], "severity": "moderate", "patterns": [ { "pattern": "\\.(swf|flv)", "type": "regex", - "scopes": [ "code", "comment" ], + "scopes": [ + "code", + "comment" + ], "confidence": "high", - "modifiers": [ "i" ] + "modifiers": [ + "i" + ] }, { "pattern": "adobe flash", "type": "string", - "scopes": [ "code", "comment" ], + "scopes": [ + "code", + "comment" + ], "confidence": "medium", - "modifiers": [ "i" ] + "modifiers": [ + "i" + ] }, { "pattern": "flash", "type": "regexword", - "scopes": [ "code", "comment" ], + "scopes": [ + "code", + "comment" + ], "confidence": "low", - "modifiers": [ "i" ] + "modifiers": [ + "i" + ] } ] }, @@ -41,26 +58,36 @@ "csharp", "vb" ], - "tags":[ "Component.Executable.Microsoft.ActiveX" ], + "tags": [ + "Component.Executable.Microsoft.ActiveX" + ], "severity": "moderate", "patterns": [ { "pattern": "CoInitialize|CoCreateInstance|comClassInterface|CLSCTX_INPROC_SERVER|ProgId|COleControlModule|ComDefaultInterface|IOleInPlaceObject|IOleControl|IOleObjective", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "Type\\.GetTypeFromProgID", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "medium" }, { "pattern": "active-?x", "type": "regex", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "medium" } ] @@ -69,14 +96,20 @@ "name": "Component: Active-X", "id": "AI005001", "description": "Component: Active-X", - "applies_to":[ "vb" ], - "tags":[ "Component.Executable.Microsoft.ActiveX" ], + "applies_to": [ + "vb" + ], + "tags": [ + "Component.Executable.Microsoft.ActiveX" + ], "severity": "moderate", "patterns": [ { "pattern": "= CreateObject(", "type": "substring", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "low" } ] @@ -85,14 +118,21 @@ "name": "Component: Active-X", "id": "AI005010", "description": "Component: Active-X", - "applies_to":[ "javascript", "typescript" ], - "tags":[ "Component.Executable.Microsoft.ActiveX" ], + "applies_to": [ + "javascript", + "typescript" + ], + "tags": [ + "Component.Executable.Microsoft.ActiveX" + ], "severity": "moderate", "patterns": [ { "pattern": "new ActiveXObject", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -106,13 +146,17 @@ "cpp", "csharp" ], - "tags":[ "Component.Executable.Microsoft.COM" ], + "tags": [ + "Component.Executable.Microsoft.COM" + ], "severity": "moderate", "patterns": [ { "pattern": "CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER|CLSCTX_LOCAL_SERVER|CoCreateInstance", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -126,15 +170,21 @@ "cpp", "csharp" ], - "tags":[ "Component.Executable.Adobe.PDF" ], + "tags": [ + "Component.Executable.Adobe.PDF" + ], "severity": "moderate", "patterns": [ { "pattern": ".pdf", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", - "modifiers": [ "i" ] + "modifiers": [ + "i" + ] } ] }, @@ -142,14 +192,20 @@ "name": "Component: Microsoft Silverlight", "id": "AI005300", "description": "Component: Microsoft Silverlight", - "tags":[ "Component.Executable.Microsoft.Silverlight" ], + "tags": [ + "Component.Executable.Microsoft.Silverlight" + ], "severity": "moderate", "patterns": [ { "pattern": "silverlight|\\.xap", "type": "regex", - "modifiers": [ "i" ], - "scopes": [ "code" ], + "modifiers": [ + "i" + ], + "scopes": [ + "code" + ], "confidence": "high" } ] diff --git a/AppInspector/rules/default/components/load_dll.json b/AppInspector/rules/default/components/load_dll.json index ec8d794..24c6b95 100644 --- a/AppInspector/rules/default/components/load_dll.json +++ b/AppInspector/rules/default/components/load_dll.json @@ -3,14 +3,20 @@ "name": "Component: Windows DLL", "id": "AI005400", "description": "Component: Windows DLL", - "applies_to": [ "csharp" ], - "tags":[ "Component.Executable.SharedLibraryLoading" ], + "applies_to": [ + "csharp" + ], + "tags": [ + "Component.Executable.SharedLibraryLoading" + ], "severity": "moderate", "patterns": [ { "pattern": "DllImport|Assembly\\.LoadFile|Assembly\\.LoadFrom", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -19,14 +25,21 @@ "name": "Component: Windows DLL", "id": "AI005500", "description": "Component: Windows DLL", - "applies_to": [ "c", "cpp" ], - "tags":[ "Component.Executable.SharedLibraryLoading" ], + "applies_to": [ + "c", + "cpp" + ], + "tags": [ + "Component.Executable.SharedLibraryLoading" + ], "severity": "moderate", "patterns": [ { "pattern": "LoadLibrary", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -35,14 +48,20 @@ "name": "Component: Windows DLL", "id": "AI005510", "description": "Component: Windows DLL", - "applies_to": [ "rust" ], - "tags":[ "Component.Executable.SharedLibraryLoading" ], + "applies_to": [ + "rust" + ], + "tags": [ + "Component.Executable.SharedLibraryLoading" + ], "severity": "moderate", "patterns": [ { "pattern": "LoadLibrary|libloading|std::unstable::dynamic_lib::DynamicLibrary", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -51,14 +70,21 @@ "name": "Component: Windows DLL", "id": "AI005600", "description": "Component: Windows DLL", - "applies_to": [ "python", "java" ], - "tags":[ "Component.Executable.SharedLibraryLoading" ], + "applies_to": [ + "python", + "java" + ], + "tags": [ + "Component.Executable.SharedLibraryLoading" + ], "severity": "moderate", "patterns": [ { "pattern": "System\\.Load|ctypes\\.WinDLL", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] diff --git a/AppInspector/rules/default/cryptography/algorithm_implementation.json b/AppInspector/rules/default/cryptography/algorithm_implementation.json index d3bc34b..e938417 100644 --- a/AppInspector/rules/default/cryptography/algorithm_implementation.json +++ b/AppInspector/rules/default/cryptography/algorithm_implementation.json @@ -3,13 +3,17 @@ "name": "Cryptography: Algorithm Implementation (SHA1)", "id": "AI005800", "description": "Cryptography: Algorithm Implementation (SHA1)", - "tags":[ "Cryptography.Implementation.SHA1" ], + "tags": [ + "Cryptography.Implementation.SHA1" + ], "severity": "important", "patterns": [ { "pattern": "5a827999", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -18,13 +22,17 @@ "name": "Cryptography: Algorithm Implementation (MD5)", "id": "AI005900", "description": "Cryptography: Algorithm Implementation (MD5)", - "tags":[ "Cryptography.Implementation.MD5" ], + "tags": [ + "Cryptography.Implementation.MD5" + ], "severity": "important", "patterns": [ { "pattern": "242070db", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -33,13 +41,17 @@ "name": "Cryptography: Algorithm Implementation (SHA-256)", "id": "AI006000", "description": "Cryptography: Algorithm Implementation (SHA-256)", - "tags":[ "Cryptography.Implementation.SHA256" ], + "tags": [ + "Cryptography.Implementation.SHA256" + ], "severity": "important", "patterns": [ { "pattern": "d807aa98", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -48,13 +60,17 @@ "name": "Cryptography: Algorithm Implementation (Keccak)", "id": "AI006100", "description": "Cryptography: Algorithm Implementation (Keccak)", - "tags":[ "Cryptography.Implementation.SHA256" ], + "tags": [ + "Cryptography.Implementation.SHA256" + ], "severity": "important", "patterns": [ { "pattern": "800000000000808A", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] diff --git a/AppInspector/rules/default/cryptography/certificate.json b/AppInspector/rules/default/cryptography/certificate.json index dd4476f..37a8b8a 100644 --- a/AppInspector/rules/default/cryptography/certificate.json +++ b/AppInspector/rules/default/cryptography/certificate.json @@ -3,23 +3,33 @@ "name": "Cryptography: x.509 Certificate Use", "id": "AI006200", "description": "Cryptography: x.509 Certificate Use", - "tags":[ "Cryptography.X509Certificates.Use" ], + "tags": [ + "Cryptography.X509Certificates.Use" + ], "severity": "moderate", "patterns": [ { "pattern": "x509|\\.(crt|cer|spc|p7b|p7s|p7r|ca-bundle)", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high", "_comment": "do not include pfx or pem see rule below for x509 client auth" }, { "pattern": "PKCS|PaddingMode|SubjectIdentifier|Certificate", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", - "modifiers": [ "i" ] + "modifiers": [ + "i" + ] } ] }, @@ -27,14 +37,22 @@ "name": "Cryptography: x.509 Certificate Use", "id": "AI006300", "description": "Cryptography: x.509 Certificate Use", - "applies_to": [ "c", "cpp", "csharp" ], - "tags":[ "Cryptography.X509Certificates.Use" ], + "applies_to": [ + "c", + "cpp", + "csharp" + ], + "tags": [ + "Cryptography.X509Certificates.Use" + ], "severity": "moderate", "patterns": [ { "pattern": "_CERT|CERT_", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -52,8 +70,12 @@ { "pattern": "\\.(pem|pfx)", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high", "_comment": "use for client auth is not guaranteed" } @@ -63,13 +85,17 @@ "name": "Cryptography: x.509 Certificate ClientAuth", "id": "AI006410", "description": "Cryptography: x.509 Certificate ClientAuth", - "tags":[ "Cryptography.X509Certificates.ClientAuth" ], + "tags": [ + "Cryptography.X509Certificates.ClientAuth" + ], "severity": "moderate", "patterns": [ { "pattern": "\\.WithClientClaims\\(.*cert.*\\)|\\.WithCertificate\\(", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ], @@ -78,7 +104,9 @@ "pattern": { "pattern": "AcquireTokenForClient|ConfidentialClientApplicationBuilder", "type": "regexword", - "scopes": [ "code" ] + "scopes": [ + "code" + ] }, "search_in": "finding-region(-10,10)", "negate_finding": false @@ -89,26 +117,34 @@ "name": "Cryptography: x.509 Certificate Create", "id": "AI006500", "description": "Cryptography: x.509 Create Certificate", - "tags":[ "Cryptography.X509Certificates.Create" ], + "tags": [ + "Cryptography.X509Certificates.Create" + ], "severity": "moderate", "patterns": [ { "pattern": "genrsa|-keyout|-x509toreq|csr", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", "_comment": "Common OpenSSL commands" }, { "pattern": "---BEGIN CERTIFICATE---", "type": "substring", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "---BEGIN PKCS", "type": "substring", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", "_comment": "allow for PKCS7, other" }, @@ -127,20 +163,28 @@ "name": "Cryptography: Digital Signature or DSA", "id": "AI006600", "description": "Cryptography: Digital Signature or DSA", - "applies_to":[ "csharp" ], - "tags":[ "Cryptography.Signing.Data" ], + "applies_to": [ + "csharp" + ], + "tags": [ + "Cryptography.Signing.Data" + ], "severity": "moderate", "patterns": [ { "pattern": "DSACng|SignData|CreateSignature|DSACryptoServiceProvider|DSASignatureDeformatter|DSASignatureFormatter|DSAKeyValue", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "DSAParameters", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -149,21 +193,32 @@ "name": "Cryptography: Digital Signature", "id": "AI006700", "description": "Cryptography: Digital Signature", - "tags":[ "Cryptography.Signing.Data" ], + "tags": [ + "Cryptography.Signing.Data" + ], "severity": "moderate", "patterns": [ { "pattern": "sign(ing)*.?key|signature.?key|valid.?signature|data.?sign|sign.?data", "type": "regexword", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" }, { "pattern": "DSA", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "low" } ] @@ -172,14 +227,20 @@ "name": "Cryptography: CodeSign", "id": "AI006800", "description": "Cryptography: CodeSign", - "tags":[ "Cryptography.Signing.Code" ], + "tags": [ + "Cryptography.Signing.Code" + ], "severity": "moderate", "patterns": [ { "pattern": "code.?sign|authenticode|sign.?code", "type": "regex", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ], @@ -188,8 +249,12 @@ "pattern": { "pattern": "shared access|SAS", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ] + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ] }, "search_in": "finding-region(-30,30)", "negate_finding": true diff --git a/AppInspector/rules/default/cryptography/ciphers.json b/AppInspector/rules/default/cryptography/ciphers.json index ef0ef20..a3973eb 100644 --- a/AppInspector/rules/default/cryptography/ciphers.json +++ b/AppInspector/rules/default/cryptography/ciphers.json @@ -3,19 +3,25 @@ "name": "Cryptography: Encryption (AES)", "id": "AI006900", "description": "Cryptography: Encryption (AES)", - "tags":[ "Cryptography.Cipher.AES" ], + "tags": [ + "Cryptography.Cipher.AES" + ], "severity": "moderate", "patterns": [ { "pattern": "AES", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "AES-?(128|192|256)|Rijndael", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -24,13 +30,17 @@ "name": "Cryptography: Encryption (3DES)", "id": "AI007000", "description": "Cryptography: Encryption (3DES)", - "tags":[ "Cryptography.Cipher.3DES" ], + "tags": [ + "Cryptography.Cipher.3DES" + ], "severity": "moderate", "patterns": [ { "pattern": "3DES|TripleDES", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -39,21 +49,29 @@ "name": "Cryptography: Encryption (RC)", "id": "AI007100", "description": "Cryptography: Encryption (RC)", - "tags":[ "Cryptography.Cipher.RC" ], + "tags": [ + "Cryptography.Cipher.RC" + ], "severity": "moderate", "patterns": [ { "pattern": "RC([2456])", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "arc4random", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", - "modifiers": [ "i" ] + "modifiers": [ + "i" + ] } ] }, @@ -61,15 +79,21 @@ "name": "Cryptography: Encryption (Salsa20)", "id": "AI007101", "description": "Cryptography: Encryption (Salsa20)", - "tags":[ "Cryptography.Cipher.Salsa20" ], + "tags": [ + "Cryptography.Cipher.Salsa20" + ], "severity": "moderate", "patterns": [ { "pattern": "Salsa20", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", - "modifiers": [ "i" ] + "modifiers": [ + "i" + ] } ] }, @@ -77,15 +101,21 @@ "name": "Cryptography: Encryption (ChaCha)", "id": "AI007102", "description": "Cryptography: Encryption (ChaCha)", - "tags":[ "Cryptography.Cipher.ChaCha" ], + "tags": [ + "Cryptography.Cipher.ChaCha" + ], "severity": "moderate", "patterns": [ { "pattern": "ChaCha", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", - "modifiers": [ "i" ] + "modifiers": [ + "i" + ] } ] }, @@ -93,13 +123,17 @@ "name": "Encryption: Cipher Mode", "id": "AI007200", "description": "Encryption: Cipher Mode", - "tags":[ "Cryptography.Cipher.CipherMode" ], + "tags": [ + "Cryptography.Cipher.CipherMode" + ], "severity": "critical", "patterns": [ { "pattern": "CBC|CTR|ECB|OFB|CFB|CTS|PCBC|GMAC|XCBC|IACBC|IAPM|EAX|OCB|CWC|AEAD|LRW|XEX|XTS|CMC|EME|CBCMAC|OMAC|PMAC", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "low" } ] @@ -108,26 +142,36 @@ "name": "Cryptography: Encryption (RSA)", "id": "AI007300", "description": "Cryptography: Encryption", - "applies_to":[ "csharp" ], - "tags":[ "Cryptography.Cipher.RSA" ], + "applies_to": [ + "csharp" + ], + "tags": [ + "Cryptography.Cipher.RSA" + ], "severity": "moderate", "patterns": [ { "pattern": "RSACng|RSACryptoServiceProvider|RSAEncryptionPadding|RSAOAEPKeyExchangeDeformatter|X509AsymmetricSecurityKey", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "RSAOAEPKeyExchangeFormatter|RSAPKCS1KeyExchangeDeformatter|RSAPKCS1", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "RSASignaturePadding|RsaProtectedConfigurationProvider|RSACertificateExtensions", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -136,22 +180,32 @@ "name": "Cryptography: Encryption (RSA)", "id": "AI007400", "description": "Cryptography: Encryption", - "tags":[ "Cryptography.Cipher.RSA" ], + "tags": [ + "Cryptography.Cipher.RSA" + ], "severity": "moderate", "patterns": [ { "pattern": "RSA.*encrypt|RSA.*decrypt|public.?key|private.?key|privkey|pubkey", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", - "modifiers": [ "i" ] + "modifiers": [ + "i" + ] }, { "pattern": "rsa", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "low", - "modifiers": [ "i" ] + "modifiers": [ + "i" + ] } ] }, @@ -159,15 +213,22 @@ "name": "Cryptography: Encryption (General)", "id": "AI007600", "description": "Cryptography: Encryption", - "tags":[ "Cryptography.Encryption.General" ], + "tags": [ + "Cryptography.Encryption.General" + ], "severity": "moderate", "patterns": [ { "pattern": "encrypt|decrypt|cipher|crypt|symmmetric|asymmetric", "type": "regex", - "scopes": [ "code", "comment" ], + "scopes": [ + "code", + "comment" + ], "confidence": "medium", - "modifiers": [ "i" ] + "modifiers": [ + "i" + ] } ] } diff --git a/AppInspector/rules/default/cryptography/crypto_currency.json b/AppInspector/rules/default/cryptography/crypto_currency.json index 47611a5..bfef9c5 100644 --- a/AppInspector/rules/default/cryptography/crypto_currency.json +++ b/AppInspector/rules/default/cryptography/crypto_currency.json @@ -3,28 +3,42 @@ "name": "Cryptography: CryptoCurrency", "id": "AI007700", "description": "Cryptography: CryptoCurrency", - "tags":[ "Cryptography.CryptoCurrency" ], + "tags": [ + "Cryptography.CryptoCurrency" + ], "severity": "moderate", "patterns": [ { "pattern": "cardano|ethereum|cryptocurrency", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" }, { "pattern": "p2pool|miner|ripple|nxt|zcash|tether|ether|btc|monero|merkleroot|xmr|eth", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "medium" }, { "pattern": "(bit|lite|name|peer|doge|grid|prim|vert|pot)coin", "type": "regex", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] diff --git a/AppInspector/rules/default/cryptography/encoding.json b/AppInspector/rules/default/cryptography/encoding.json index fefdee1..6a93173 100644 --- a/AppInspector/rules/default/cryptography/encoding.json +++ b/AppInspector/rules/default/cryptography/encoding.json @@ -5,15 +5,21 @@ "description": "Cryptography: Encoding (Base-64)", "applies_to": [ ], - "tags":[ "Cryptography.Encoding.Base64" ], + "tags": [ + "Cryptography.Encoding.Base64" + ], "severity": "moderate", "patterns": [ { "pattern": "base64", "type": "substring", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", - "modifiers": [ "i" ] + "modifiers": [ + "i" + ] } ] } diff --git a/AppInspector/rules/default/cryptography/extended.json b/AppInspector/rules/default/cryptography/extended.json index 3ac769f..18d5fc7 100644 --- a/AppInspector/rules/default/cryptography/extended.json +++ b/AppInspector/rules/default/cryptography/extended.json @@ -8,211 +8,281 @@ "csharp", "vb" ], - "tags":[ "Cryptography.Encryption.General" ], + "tags": [ + "Cryptography.Encryption.General" + ], "severity": "critical", "patterns": [ { "pattern": "AsnEncodedData", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "Cng(Algorithm|Key|Property|Provider|UIPolicy)", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "CspKeyContainerInfo", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "CspParameters", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "DataProtector", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "DeriveBytes", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", "_comment": "e.g. Rfc2898DeriveBytes" }, { "pattern": "KeySizes", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "MaskGenerationMethod", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "Oid", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "OidCollection", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "OidEnumerator", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "ProtectedMemory", "type": "regex", - "scopes": [ "code" ] + "scopes": [ + "code" + ] }, { "pattern": "RIPEMD160", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "CngProperty|CngExportPolicies|CngKey|CngUI|CndProvider", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "DataProtectionScope", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "ECKeyXmlFormat", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "MemoryProtectionScope", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "OidGroup", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "KeyAgreeKeyChoice", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "AlgorithmIdentifier", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "CmsRecipient", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "CmsSigner", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "EnvelopedCms", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "KeyAgreeRecipientInfo|KeyTransRecipientInfo", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "X500DistinguishedName", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "OpenFlags", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "KeyInfo", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "TransformChain", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "XmlDsig", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "XmlLicenseTransform", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "IRelDecryptor", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "ProtectionLevel.None", "type": "substring", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "ProtectionLevel.Sign", "type": "substring", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "TsCertCompareCertContext", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] diff --git a/AppInspector/rules/default/cryptography/external_libraries.json b/AppInspector/rules/default/cryptography/external_libraries.json index 13f11d1..7c983fc 100644 --- a/AppInspector/rules/default/cryptography/external_libraries.json +++ b/AppInspector/rules/default/cryptography/external_libraries.json @@ -9,15 +9,21 @@ "kotlin", "scala" ], - "tags":[ "Cryptography.Library.TLS.BouncyCastle" ], + "tags": [ + "Cryptography.Library.TLS.BouncyCastle" + ], "severity": "moderate", "patterns": [ { "pattern": "bouncycastle|spongycastle", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", - "modifiers": [ "i" ] + "modifiers": [ + "i" + ] } ] }, @@ -27,15 +33,21 @@ "description": "Cryptographic Library: mbed TLS", "applies_to": [ ], - "tags":[ "Cryptography.Library.TLS.mbedTLS" ], + "tags": [ + "Cryptography.Library.TLS.mbedTLS" + ], "severity": "moderate", "patterns": [ { "pattern": "mbedtls", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", - "modifiers": [ "i" ] + "modifiers": [ + "i" + ] } ] }, @@ -45,15 +57,21 @@ "description": "Cryptographic Library: OpenSSL", "applies_to": [ ], - "tags":[ "Cryptography.Library.TLS.OpenSSL" ], + "tags": [ + "Cryptography.Library.TLS.OpenSSL" + ], "severity": "moderate", "patterns": [ { "pattern": "openssl|SSL_CTX_new", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", - "modifiers": [ "i" ] + "modifiers": [ + "i" + ] } ] }, @@ -63,15 +81,21 @@ "description": "Cryptographic Library: BoringSSL", "applies_to": [ ], - "tags":[ "Cryptography.Library.TLS.BoringSSL" ], + "tags": [ + "Cryptography.Library.TLS.BoringSSL" + ], "severity": "moderate", "patterns": [ { "pattern": "boringssl", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", - "modifiers": [ "i" ] + "modifiers": [ + "i" + ] } ] }, @@ -81,15 +105,21 @@ "description": "Cryptographic Library: LibreSSL", "applies_to": [ ], - "tags":[ "Cryptography.Library.TLS.LibreSSL" ], + "tags": [ + "Cryptography.Library.TLS.LibreSSL" + ], "severity": "moderate", "patterns": [ { "pattern": "libressl", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", - "modifiers": [ "i" ] + "modifiers": [ + "i" + ] } ] }, @@ -99,15 +129,21 @@ "description": "Cryptographic Library: Win32", "applies_to": [ ], - "tags":[ "Cryptography.Library.Win32" ], + "tags": [ + "Cryptography.Library.Win32" + ], "severity": "moderate", "patterns": [ { "pattern": "\\b(wincrypt|cryptxml|wintrust|dpapi|bcrypt|cryptdlg|ncrypt|cryptxml|ncryptprotect)\\.h", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", - "modifiers": [ "i" ] + "modifiers": [ + "i" + ] } ] }, @@ -120,20 +156,28 @@ "cpp", "csharp" ], - "tags":[ "Cryptography.Library.Microsoft.NET" ], + "tags": [ + "Cryptography.Library.Microsoft.NET" + ], "severity": "moderate", "patterns": [ { "pattern": "ProtectedData", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "_comment": "crypto class" }, { "pattern": "DPAPI", "type": "string", - "scopes": [ "code" ], - "modified": [ "i" ], + "scopes": [ + "code" + ], + "modified": [ + "i" + ], "confidence": "high" } ] diff --git a/AppInspector/rules/default/cryptography/hash_algorithm.json b/AppInspector/rules/default/cryptography/hash_algorithm.json index c202a39..f751d06 100644 --- a/AppInspector/rules/default/cryptography/hash_algorithm.json +++ b/AppInspector/rules/default/cryptography/hash_algorithm.json @@ -3,14 +3,20 @@ "name": "Cryptography: Hash Algorithm (SHA-256)", "id": "AI008700", "description": "Cryptography: Hash Algorithm Usage (SHA-256)", - "tags":[ "Cryptography.HashAlgorithm.SHA2" ], + "tags": [ + "Cryptography.HashAlgorithm.SHA2" + ], "severity": "moderate", "patterns": [ { "pattern": "SHA-?(2|224|256|384|512)", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -19,14 +25,21 @@ "name": "Cryptography: Hash Algorithm (Legacy)", "id": "AI008800", "description": "Cryptography: Hash Algorithm (Legacy)", - "tags":[ "Cryptography.HashAlgorithm.Legacy" ], + "tags": [ + "Cryptography.HashAlgorithm.Legacy" + ], "severity": "moderate", "patterns": [ { "pattern": "MD2|MD4|MD5|SHA-?(0|1)", "type": "regexword", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -35,14 +48,20 @@ "name": "Cryptography: Hash Algorithm (SHA-3)", "id": "AI008900", "description": "Cryptography: Hash Algorithm Usage (SHA-3)", - "tags":[ "Cryptography.HashAlgorithm.SHA3" ], + "tags": [ + "Cryptography.HashAlgorithm.SHA3" + ], "severity": "moderate", "patterns": [ { "pattern": "SHA-?3|Keccak", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -51,14 +70,21 @@ "name": "Cryptography: Hash Algorithm (Misc)", "id": "AI009000", "description": "Cryptography: Hash Algorithm (Misc)", - "tags":[ "Cryptography.HashAlgorithm.Misc" ], + "tags": [ + "Cryptography.HashAlgorithm.Misc" + ], "severity": "moderate", "patterns": [ { "pattern": "RIPEMD|Blowfish|Twofish|Threefish|Serpent|HMAC|KeyedHashAlgorithm|Blake2|Blake3", "type": "regexword", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -67,14 +93,20 @@ "name": "Cryptography: Hash Algorithm", "id": "AI009100", "description": "Cryptography: Hash Algorithm Usage", - "tags":[ "Cryptography.HashAlgorithm.Other" ], + "tags": [ + "Cryptography.HashAlgorithm.Other" + ], "severity": "moderate", "patterns": [ { "pattern": "HashAlgorithm|MessageDigest|DigestUtils", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -83,14 +115,21 @@ "name": "Cryptography: Hash Algorithm (General)", "id": "AI009200", "description": "Cryptography: Hash Algorithm (General)", - "tags":[ "Cryptography.HashAlgorithm.General" ], + "tags": [ + "Cryptography.HashAlgorithm.General" + ], "severity": "moderate", "patterns": [ { "pattern": "hash", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "low" } ] diff --git a/AppInspector/rules/default/cryptography/key_derivation.json b/AppInspector/rules/default/cryptography/key_derivation.json index b365398..bebaba5 100644 --- a/AppInspector/rules/default/cryptography/key_derivation.json +++ b/AppInspector/rules/default/cryptography/key_derivation.json @@ -3,14 +3,20 @@ "name": "Cryptography: Key Derivation", "id": "AI009300", "description": "Cryptography: Key Derivation", - "applies_to": ["csharp"], - "tags":[ "Cryptography.KeyDerivation.General" ], + "applies_to": [ + "csharp" + ], + "tags": [ + "Cryptography.KeyDerivation.General" + ], "severity": "moderate", "patterns": [ { "pattern": "KeyDerivationAlgorithmNames|SampleDeriveKeyMaterialPbkdf|RNGCryptoServiceProvider|Rfc2898DeriveBytes|KeyDerivation|DeriveKey|PasswordDeriveBytes", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -28,8 +34,12 @@ { "pattern": "pbkdf", "type": "substring", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ], @@ -38,8 +48,12 @@ "pattern": { "pattern": "pbkdf2", "type": "substring", - "scopes": [ "code" ], - "modifiers": [ "i" ] + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ] }, "search_in": "same-line", "negate_finding": true @@ -50,14 +64,20 @@ "name": "Cryptography: Key Derivation (PBKDF2)", "id": "AI009500", "description": "Cryptography: Key Derivation (PBKDF2)", - "tags": [ "Cryptography.KeyDerivation.PBKDF2" ], + "tags": [ + "Cryptography.KeyDerivation.PBKDF2" + ], "severity": "moderate", "patterns": [ { "pattern": "pbkdf2", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] diff --git a/AppInspector/rules/default/cryptography/protocol.json b/AppInspector/rules/default/cryptography/protocol.json index 0d8388f..6f77818 100644 --- a/AppInspector/rules/default/cryptography/protocol.json +++ b/AppInspector/rules/default/cryptography/protocol.json @@ -3,20 +3,28 @@ "name": "Cryptography: SSL/TLS Protocol", "id": "AI009600", "description": "Cryptography: SSL/TLS Protocol", - "applies_to":[ "csharp" ], - "tags":[ "Cryptography.Protocol.TLS" ], + "applies_to": [ + "csharp" + ], + "tags": [ + "Cryptography.Protocol.TLS" + ], "severity": "moderate", "patterns": [ { "pattern": "(SSL|TLS)(v)?([123]{1,2})", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "SslStream|SecurityProtocolType\\.(Ssl|Tls)|SslPolicyErrors|onReceivedSslError|SslContext", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -29,13 +37,17 @@ "c", "cpp" ], - "tags":[ "Cryptography.Protocol.TLS" ], + "tags": [ + "Cryptography.Protocol.TLS" + ], "severity": "moderate", "patterns": [ { "pattern": "SECURITY_FLAG_", "type": "substring", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", "_comment": "get all non-ignore (see weakssl.json) indicators of ssl" } @@ -45,7 +57,9 @@ "pattern": { "pattern": "SECURITY_FLAG_IGNORE", "type": "substring", - "scopes": [ "code" ] + "scopes": [ + "code" + ] }, "search_in": "finding-region(-1,1)", "negate_finding": true @@ -61,63 +75,85 @@ "objective-c", "swift" ], - "tags":[ "Cryptography.Protocol.TLS" ], + "tags": [ + "Cryptography.Protocol.TLS" + ], "severity": "moderate", "patterns": [ { "pattern": "(kCF|NS)StreamSocketSecurityLevel(None|SSLv2|SSLv3|TLSv1|NegotiatedSSL)", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "kSSLProtocol(2|3Only|All|Unknown)", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "k(D)?(SSL|TLS)Protocol(1|1Only|2|3Only|All)", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "ssl", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", - "modifiers": [ "i" ], + "modifiers": [ + "i" + ], "_comment": "e.g. ssl_crl, ssl_ecdh_curve, others" }, { "pattern": "SSLCADNRequest|SSLCARevocation|SSLCompression|SSLEngine|SSLFIPS|SSLInsecureRenegotiation", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "SSLOCSP(DefaultResponder|Enable|OverrideResponder|ProxyURL|ResponderTimeout|ResponseMaxAge|ResponseTimeSkew|UseRequestNonce)", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "SSLOpenSSLConfCmd|SSLOptions|SSLPassPhraseDialog|SSLProtocol|SSLProxy", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "SSLRandomSeed|SSLRenegBufferSize|SSLRequire|SSLSessionCache|SSLSRPUnknownUserSeed|SSLSRPVerifierFile|SSLStapling", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "SSLStrictSNIVHostCheck|SSLUserName|SSLUseStapling|SSLVerify", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -126,13 +162,17 @@ "name": "Cryptography: SSH Protocol", "id": "AI009900", "description": "Cryptography: SSH Protocol", - "tags":[ "Cryptography.Protocol.SSH" ], + "tags": [ + "Cryptography.Protocol.SSH" + ], "severity": "moderate", "patterns": [ { "pattern": "ssh", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -141,15 +181,22 @@ "name": "Cryptography: SSH Protocol", "id": "AI009910", "description": "Cryptography: SSH Protocol", - "tags":[ "Cryptography.Protocol.KeyExchange" ], + "tags": [ + "Cryptography.Protocol.KeyExchange" + ], "severity": "moderate", "patterns": [ { "pattern": "DiffieHellman|ECDsa", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", - "modifiers": [ "i", "comment" ] + "modifiers": [ + "i", + "comment" + ] } ] } diff --git a/AppInspector/rules/default/cryptography/random.json b/AppInspector/rules/default/cryptography/random.json index 2e5698b..fc67020 100644 --- a/AppInspector/rules/default/cryptography/random.json +++ b/AppInspector/rules/default/cryptography/random.json @@ -3,7 +3,9 @@ "name": "Cryptography: PRNG", "id": "AI010000", "description": "Cryptography: PRNG", - "tags":[ "Cryptography.Randomness.PRNG" ], + "tags": [ + "Cryptography.Randomness.PRNG" + ], "applies_to": [ "c", "cpp" @@ -13,19 +15,25 @@ { "pattern": "DUAL_EC_DRBG", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "pseudoRandomBytes", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "rand|srand", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "low" } ] @@ -34,15 +42,24 @@ "name": "Cryptography: PRNG", "id": "AI010100", "description": "Cryptography: PRNG", - "tags":[ "Cryptography.Randomness.PRNG" ], - "applies_to":[ "javascript","typescript" ], + "tags": [ + "Cryptography.Randomness.PRNG" + ], + "applies_to": [ + "javascript", + "typescript" + ], "severity": "critical", "patterns": [ { "pattern": "(pseudo)?randombytes", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -51,14 +68,20 @@ "name": "Cryptography: PRNG", "id": "AI010200", "description": "Cryptography: PRNG", - "tags":[ "Cryptography.Randomness.PRNG" ], - "applies_to":[ "java" ], + "tags": [ + "Cryptography.Randomness.PRNG" + ], + "applies_to": [ + "java" + ], "severity": "critical", "patterns": [ { "pattern": "SecureRandom", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -67,14 +90,20 @@ "name": "Cryptography: PRNG", "id": "AI010300", "description": "Cryptography: PRNG", - "tags":[ "Cryptography.Randomness.PRNG" ], - "applies_to":[ "csharp" ], + "tags": [ + "Cryptography.Randomness.PRNG" + ], + "applies_to": [ + "csharp" + ], "severity": "critical", "patterns": [ { "pattern": "RandomNumberGenerator", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -83,13 +112,17 @@ "name": "Cryptography: PRNG", "id": "AI010400", "description": "Cryptography: PRNG", - "tags":[ "Cryptography.Randomness.PRNG" ], + "tags": [ + "Cryptography.Randomness.PRNG" + ], "severity": "critical", "patterns": [ { "pattern": "random", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "low" } ] diff --git a/AppInspector/rules/default/cryptography/weakssl.json b/AppInspector/rules/default/cryptography/weakssl.json index eee607d..3612ff1 100644 --- a/AppInspector/rules/default/cryptography/weakssl.json +++ b/AppInspector/rules/default/cryptography/weakssl.json @@ -9,197 +9,263 @@ "cpp", "csharp" ], - "tags":[ "Cryptography.Protocol.TLS.WeakSSL" ], + "tags": [ + "Cryptography.Protocol.TLS.WeakSSL" + ], "severity": "critical", "rule_info": "", "patterns": [ { "pattern": "^iv.*=| +iv.*=", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "SECURITY_FLAG_IGNORE_", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", "_comment": "handles multiple ignore flag options for WinHttp" }, { "pattern": "SP_PROT_PCT1_", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "kSSLProtocol", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "CryptUnprotectData", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "mwc1616", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "32969|18273", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "StoreName.Root", "type": "substring", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "OAUTHLIB_INSECURE_TRANSPORT", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": ".trustAllCerts()", "type": "substring", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": ".trustAllHosts()", "type": "substring", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "NXOAuth2TrustModeAnyCertificate", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "--no-check-certificate", "type": "substring", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "--insecure", "type": "substring", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "encrypt=false", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "encryption=false", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "OpenSSL::SSL::VERIFY_NONE", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "RelaxSSLCertificateValidation", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "setAllowsAnyHTTPSCertificate", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "continueWithoutCredentialForAuthenticationChallenge", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "disable_ssl_certificate_validation", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "CERT_NONE", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": ".ServerCertificateValidationCallback", "type": "substring", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "IgnorableServerCertificateErrors", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "certificateValidationMode", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "CERT_CHAIN_POLICY_IGNORE_", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "WINHTTP_ENABLE_SSL_REVOCATION", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "CERT_CHAIN_REVOCATION_ACCUMULATIVE_TIMEOUT", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "ISC_REQ_MANUAL_CRED_VALIDATION", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "SECPKG_ATTR_REMOTE_CERT_CONTEXT", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "disable_ssl_certificate_validation", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", - "modifiers": [ "i" ] + "modifiers": [ + "i" + ] } ] } diff --git a/AppInspector/rules/default/data_handling/compressed_files.json b/AppInspector/rules/default/data_handling/compressed_files.json index 6d301a8..8b340dd 100644 --- a/AppInspector/rules/default/data_handling/compressed_files.json +++ b/AppInspector/rules/default/data_handling/compressed_files.json @@ -3,14 +3,20 @@ "name": "Data: Compressed File (Zip,Tgz)", "id": "AI010600", "description": "Data: Compressed File Handling", - "tags":[ "Data.Zipfile" ], + "tags": [ + "Data.Zipfile" + ], "severity": "moderate", "patterns": [ { "pattern": "\\.(zip|gz|gzip|gem|tar|tgz|tar\\.gz|xz|7z)", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] diff --git a/AppInspector/rules/default/data_handling/database.json b/AppInspector/rules/default/data_handling/database.json index ed9e7bd..d3be7bd 100644 --- a/AppInspector/rules/default/data_handling/database.json +++ b/AppInspector/rules/default/data_handling/database.json @@ -3,15 +3,24 @@ "name": "Data: ORM (SQL Alchemy)", "id": "AI010700", "description": "Data: ORM (SQL Alchemy)", - "applies_to": [ "python" ], - "tags":[ "Data.DBMS.ORM.SQLAlchemy" ], + "applies_to": [ + "python" + ], + "tags": [ + "Data.DBMS.ORM.SQLAlchemy" + ], "severity": "moderate", "patterns": [ { "pattern": "sqlalchemy", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -20,15 +29,24 @@ "name": "Data: ORM (Django)", "id": "AI010800", "description": "Data: ORM (Django)", - "applies_to": [ "python" ], - "tags":[ "Data.DBMS.ORM.Django" ], + "applies_to": [ + "python" + ], + "tags": [ + "Data.DBMS.ORM.Django" + ], "severity": "moderate", "patterns": [ { "pattern": "django", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ] + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ] } ] }, @@ -36,23 +54,35 @@ "name": "Data: DBMS (SQLite)", "id": "AI010900", "description": "Data: DBMS (SQLite)", - "applies_to": [ "python" ], - "tags":[ "Data.DBMS.SQLite" ], + "applies_to": [ + "python" + ], + "tags": [ + "Data.DBMS.SQLite" + ], "severity": "moderate", "patterns": [ { "pattern": "sqlite|python3-apsw", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", - "modifiers": [ "i" ] + "modifiers": [ + "i" + ] }, { "pattern": "apsw", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "medium", - "modifiers": [ "i" ] + "modifiers": [ + "i" + ] } ] }, @@ -60,14 +90,20 @@ "name": "Data: DBMS (SQLite)", "id": "AI010910", "description": "Data: DBMS (SQLite)", - "applies_to": [ "csharp" ], - "tags":[ "Data.DBMS.SQLite" ], + "applies_to": [ + "csharp" + ], + "tags": [ + "Data.DBMS.SQLite" + ], "severity": "moderate", "patterns": [ { "pattern": "SqliteCommand|SqliteConnection|System\\.Data\\.SQLite|Microsoft\\.Data\\.SQLite", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -83,15 +119,21 @@ "c", "cpp" ], - "tags":[ "Data.DBMS.SQLite" ], + "tags": [ + "Data.DBMS.SQLite" + ], "severity": "moderate", "patterns": [ { "pattern": "sqlite", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", - "modifiers": [ "i" ] + "modifiers": [ + "i" + ] } ] }, @@ -99,21 +141,32 @@ "name": "Data: DBMS (PostgreSQL)", "id": "AI011000", "description": "Data: DBMS (PostgreSQL)", - "applies_to": [ "javascript", "typescript" ], - "tags":[ "Data.DBMS.PostgreSQL" ], + "applies_to": [ + "javascript", + "typescript" + ], + "tags": [ + "Data.DBMS.PostgreSQL" + ], "severity": "moderate", "patterns": [ { "pattern": "(pgsql|PG)\\.connect|PG::Connection", "type": "regex", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" }, { "pattern": "require(['\"]pg['\"]);", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -122,15 +175,23 @@ "name": "Data: DBMS (PostgreSQL)", "id": "AI011010", "description": "Data: DBMS (PostgreSQL)", - "applies_to": [ "ruby" ], - "tags":[ "Data.DBMS.PostgreSQL" ], + "applies_to": [ + "ruby" + ], + "tags": [ + "Data.DBMS.PostgreSQL" + ], "severity": "moderate", "patterns": [ { "pattern": "require 'pg'|(pgsql|PG)\\.connect|PG::Connection", "type": "regex", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -139,15 +200,23 @@ "name": "Data: DBMS (PostgreSQL)", "id": "AI011100", "description": "Data: DBMS (PostgreSQL)", - "applies_to": [ "python" ], - "tags":[ "Data.DBMS.PostgreSQL" ], + "applies_to": [ + "python" + ], + "tags": [ + "Data.DBMS.PostgreSQL" + ], "severity": "moderate", "patterns": [ { "pattern": "psycopg2", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -156,15 +225,23 @@ "name": "Data: DBMS (PostgreSQL)", "id": "AI011200", "description": "Data: DBMS (PostgreSQL)", - "applies_to": [ "csharp" ], - "tags":[ "Data.DBMS.PostgreSQL" ], + "applies_to": [ + "csharp" + ], + "tags": [ + "Data.DBMS.PostgreSQL" + ], "severity": "moderate", "patterns": [ { "pattern": "npgsql", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -173,14 +250,20 @@ "name": "Data: DBMS (NoSQL)", "id": "AI011300", "description": "Data: DBMS (NoSQL)", - "tags":[ "Data.DBMS.NoSQL" ], + "tags": [ + "Data.DBMS.NoSQL" + ], "severity": "moderate", "patterns": [ { "pattern": "mongodb|mongoose|mongoclient|pymongo|redis|hbase|neo4j|cassandra|couchbase|memcached|couchdb|litedb|LiteDatabase|tinydb", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -189,14 +272,20 @@ "name": "Data: DBMS (SQL)", "id": "AI011400", "description": "Data: DBMS SQL", - "applies_to": [ "java" ], - "tags":[ "Data.DBMS.SQL" ], + "applies_to": [ + "java" + ], + "tags": [ + "Data.DBMS.SQL" + ], "severity": "moderate", "patterns": [ { "pattern": "import java.sql", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -205,14 +294,20 @@ "name": "Data: DBMS (SQL)", "id": "AI011500", "description": "Data: DBMS (SQL)", - "applies_to": [ "csharp" ], - "tags":[ "Data.DBMS.SQL" ], + "applies_to": [ + "csharp" + ], + "tags": [ + "Data.DBMS.SQL" + ], "severity": "moderate", "patterns": [ { "pattern": "using system.data.sqlclient", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -222,14 +317,20 @@ "id": "AI011600", "description": "Data: DBMS (SQL)", "applies_to": [], - "tags":[ "Data.DBMS.SQL" ], + "tags": [ + "Data.DBMS.SQL" + ], "severity": "moderate", "patterns": [ { "pattern": "['\\\"](select|insert|delete|update)\\s.*", "type": "regex", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ], @@ -238,8 +339,12 @@ "pattern": { "pattern": "from|where", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ] + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ] }, "search_in": "finding-region(-5,5)", "negate_finding": false @@ -251,14 +356,20 @@ "id": "AI011610", "description": "Data: DBMS (SQL)", "applies_to": [], - "tags":[ "Data.DBMS.SQL" ], + "tags": [ + "Data.DBMS.SQL" + ], "severity": "moderate", "patterns": [ { "pattern": "mysql", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -267,21 +378,32 @@ "name": "Data: DBMS (Oracle)", "id": "AI011700", "description": "Data: DBMS (Oracle)", - "applies_to": [ "java", "pom.xml" ], - "tags":[ "Data.DBMS.SQL.Oracle" ], + "applies_to": [ + "java", + "pom.xml" + ], + "tags": [ + "Data.DBMS.SQL.Oracle" + ], "severity": "moderate", "patterns": [ { "pattern": "oracle\\.jdbc|oracledriver|com.oracle", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "oracle", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "medium" } ] @@ -290,22 +412,34 @@ "name": "Data: DBMS (Oracle)", "id": "AI011800", "description": "Data: DBMS (Oracle)", - "applies_to": [ "python" ], - "tags":[ "Data.DBMS.SQL.Oracle" ], + "applies_to": [ + "python" + ], + "tags": [ + "Data.DBMS.SQL.Oracle" + ], "severity": "moderate", "patterns": [ { "pattern": "import cx_Oracle", "type": "regex", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" }, { "pattern": "oracle", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "low" } ] @@ -314,14 +448,26 @@ "name": "Data: DBMS (General)", "id": "AI011900", "description": "Data: DBMS (General)", - "applies_to": [ "csharp", "cpp", "java", "python", "objective-c", "go" ], - "tags":[ "Data.DBMS.General" ], + "applies_to": [ + "csharp", + "cpp", + "java", + "python", + "objective-c", + "go" + ], + "tags": [ + "Data.DBMS.General" + ], "severity": "moderate", "patterns": [ { "pattern": "database", "type": "string", - "scopes": [ "code", "comment" ], + "scopes": [ + "code", + "comment" + ], "confidence": "high" } ] @@ -330,14 +476,20 @@ "name": "Data: ODBC", "id": "AI012000", "description": "Data: ODBC", - "applies_to": [ "csharp" ], - "tags":[ "Data.DBMS.Connection.ODBC" ], + "applies_to": [ + "csharp" + ], + "tags": [ + "Data.DBMS.Connection.ODBC" + ], "severity": "moderate", "patterns": [ { "pattern": "OdbcConnection|OdbcCommand|OdbcDataReader", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -346,14 +498,25 @@ "name": "Data: ODBC", "id": "AI012010", "description": "Data: ODBC", - "applies_to": [ "csharp", "cpp", "java", "python", "objective-c", "go" ], - "tags":[ "Data.DBMS.Connection.ODBC" ], + "applies_to": [ + "csharp", + "cpp", + "java", + "python", + "objective-c", + "go" + ], + "tags": [ + "Data.DBMS.Connection.ODBC" + ], "severity": "moderate", "patterns": [ { "pattern": "Odbc", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] diff --git a/AppInspector/rules/default/data_handling/deserialization.json b/AppInspector/rules/default/data_handling/deserialization.json index 656b73e..ba1f182 100644 --- a/AppInspector/rules/default/data_handling/deserialization.json +++ b/AppInspector/rules/default/data_handling/deserialization.json @@ -7,14 +7,20 @@ "python", "rust" ], - "tags":[ "Data.Serialization" ], + "tags": [ + "Data.Serialization" + ], "severity": "critical", "patterns": [ { "pattern": "Serialize", "type": "regex", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "medium" } ] @@ -23,14 +29,20 @@ "name": "Data: Serialization", "id": "AI012200", "description": "Data: Serialization", - "applies_to":[ "python" ], - "tags":[ "Data.Serialization" ], + "applies_to": [ + "python" + ], + "tags": [ + "Data.Serialization" + ], "severity": "critical", "patterns": [ { "pattern": "import .*c?pickle", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -39,14 +51,20 @@ "name": "Data: Serialization", "id": "AI012300", "description": "Data: Serialization", - "applies_to":[ "python" ], - "tags":[ "Data.Serialization" ], + "applies_to": [ + "python" + ], + "tags": [ + "Data.Serialization" + ], "severity": "critical", "patterns": [ { "pattern": "c?pickle\\.dump", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -55,14 +73,20 @@ "name": "Data: Deserialization", "id": "AI012400", "description": "Data: Deserialization", - "applies_to":[ "python" ], - "tags":[ "Data.Deserialization" ], + "applies_to": [ + "python" + ], + "tags": [ + "Data.Deserialization" + ], "severity": "critical", "patterns": [ { "pattern": "c?pickle\\.load", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -71,14 +95,20 @@ "name": "Data: Deserialization", "id": "AI012500", "description": "Data: Deserialization", - "applies_to":[ "java" ], - "tags":[ "Data.Deserialization" ], + "applies_to": [ + "java" + ], + "tags": [ + "Data.Deserialization" + ], "severity": "critical", "patterns": [ { "pattern": ".readObject", "type": "substring", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -87,14 +117,20 @@ "name": "Data: Deserialization", "id": "AI012600", "description": "Data: Deserialization", - "applies_to":[ "php" ], - "tags":[ "Data.Deserialization" ], + "applies_to": [ + "php" + ], + "tags": [ + "Data.Deserialization" + ], "severity": "critical", "patterns": [ { "pattern": "unserialize\\(", "type": "substring", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -103,14 +139,20 @@ "name": "Data: Deserialization", "id": "AI012700", "description": "Data: Deserialization", - "applies_to":[ "ruby" ], - "tags":[ "Data.Deserialization" ], + "applies_to": [ + "ruby" + ], + "tags": [ + "Data.Deserialization" + ], "severity": "critical", "patterns": [ { "pattern": "(YAML|Syck|Marshal)\\.load", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -119,14 +161,20 @@ "name": "Data: Serialization", "id": "AI012800", "description": "Data: Serialization", - "applies_to":[ "csharp" ], - "tags":[ "Data.Serialization" ], + "applies_to": [ + "csharp" + ], + "tags": [ + "Data.Serialization" + ], "severity": "critical", "patterns": [ { "pattern": "SerializeObject|JavaScriptSerializer|Newtonsoft|json\\.net|Utf8Json|MessagePack", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -135,32 +183,44 @@ "name": "Data: Deserialization", "id": "AI012900", "description": "Data: Deserialization", - "applies_to":[ "csharp" ], - "tags":[ "Data.Deserialization" ], + "applies_to": [ + "csharp" + ], + "tags": [ + "Data.Deserialization" + ], "severity": "critical", "patterns": [ { "pattern": "DeserializeObject", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "PopulateObject", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "TypeNameHandling", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "(De)?[Ss]erialize", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -169,26 +229,36 @@ "name": "Data: Deserialization", "id": "AI013000", "description": "Data: Deserialization", - "applies_to":[ "csharp" ], - "tags":[ "Data.Deserialization" ], + "applies_to": [ + "csharp" + ], + "tags": [ + "Data.Deserialization" + ], "severity": "critical", "patterns": [ { "pattern": "System.Runtime.Serialization", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "BinaryFormatter", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "TypeNameHandling", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -201,14 +271,20 @@ "python", "rust" ], - "tags":[ "Data.Deserialization" ], + "tags": [ + "Data.Deserialization" + ], "severity": "critical", "patterns": [ { "pattern": "Deserialize", "type": "regex", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "medium" } ] @@ -217,14 +293,20 @@ "name": "Data: Bluetooth Serialization", "id": "AI013100", "description": "Data: Bluetooth Serialization", - "tags":[ "Data.Deserialization.Signal.Bluetooth" ], + "tags": [ + "Data.Deserialization.Signal.Bluetooth" + ], "severity": "critical", "patterns": [ { "pattern": "Bluetooth", "type": "string", - "modifiers": [ "i" ], - "scopes": [ "code" ], + "modifiers": [ + "i" + ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -233,14 +315,20 @@ "name": "Data: Cellular Serialization", "id": "AI013200", "description": "Data: Cellular Serialization", - "tags":[ "Data.Deserialization.Signal.Cellular" ], + "tags": [ + "Data.Deserialization.Signal.Cellular" + ], "severity": "critical", "patterns": [ { "pattern": "Cellular", "type": "string", - "modifiers": [ "i" ], - "scopes": [ "code" ], + "modifiers": [ + "i" + ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -249,14 +337,20 @@ "name": "Data: USB", "id": "AI013300", "description": "Data: USB Serialization", - "tags":[ "Data.Deserialization.Signal.USB" ], + "tags": [ + "Data.Deserialization.Signal.USB" + ], "severity": "critical", "patterns": [ { "pattern": "USB", "type": "string", - "modifiers": [ "i" ], - "scopes": [ "code" ], + "modifiers": [ + "i" + ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -265,14 +359,20 @@ "name": "Data: Ethernet", "id": "AI013400", "description": "Data: Ethernet Serialization", - "tags":[ "Data.Deserialization.Signal.Ethernet" ], + "tags": [ + "Data.Deserialization.Signal.Ethernet" + ], "severity": "critical", "patterns": [ { "pattern": "Ethernet", "type": "string", - "modifiers": [ "i" ], - "scopes": [ "code" ], + "modifiers": [ + "i" + ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -281,14 +381,20 @@ "name": "Data: Modem", "id": "AI013500", "description": "Data: Modem Serialization", - "tags":[ "Data.Deserialization.Signal.Modem" ], + "tags": [ + "Data.Deserialization.Signal.Modem" + ], "severity": "critical", "patterns": [ { "pattern": "Modem", "type": "string", - "modifiers": [ "i" ], - "scopes": [ "code" ], + "modifiers": [ + "i" + ], + "scopes": [ + "code" + ], "confidence": "high" } ] diff --git a/AppInspector/rules/default/data_handling/json_parsing.json b/AppInspector/rules/default/data_handling/json_parsing.json index 8bfc2db..19ba77d 100644 --- a/AppInspector/rules/default/data_handling/json_parsing.json +++ b/AppInspector/rules/default/data_handling/json_parsing.json @@ -3,15 +3,22 @@ "name": "Data: Parses JSON", "id": "AI013600", "description": "Data: Parses JSON", - "applies_to": [ "javascript", "typescript" ], - "tags":[ "Data.Parsing.JSON" ], + "applies_to": [ + "javascript", + "typescript" + ], + "tags": [ + "Data.Parsing.JSON" + ], "severity": "moderate", "patterns": [ { "pattern": "JSON.Parse", "confidence": "high", "type": "string", - "scopes": [ "code" ] + "scopes": [ + "code" + ] } ] }, @@ -19,15 +26,21 @@ "name": "Data: Parses JSON", "id": "AI013700", "description": "Data: Parses JSON", - "applies_to": [ "csharp" ], - "tags":[ "Data.Parsing.JSON" ], + "applies_to": [ + "csharp" + ], + "tags": [ + "Data.Parsing.JSON" + ], "severity": "moderate", "patterns": [ { "pattern": "JSON\\.createParser|JObject\\.Parse|JsonConvert|JsonSerializer", "confidence": "high", "type": "regexword", - "scopes": [ "code" ] + "scopes": [ + "code" + ] } ] }, @@ -35,14 +48,18 @@ "name": "Data: Parses JSON", "id": "AI013800", "description": "Data: Parses JSON", - "tags":[ "Data.Parsing.JSON" ], + "tags": [ + "Data.Parsing.JSON" + ], "severity": "moderate", "patterns": [ { "pattern": "json.{0,5}parser", "confidence": "high", "type": "regexword", - "scopes": [ "code" ] + "scopes": [ + "code" + ] } ] } diff --git a/AppInspector/rules/default/data_handling/media_parsing.json b/AppInspector/rules/default/data_handling/media_parsing.json index e2e9434..4990265 100644 --- a/AppInspector/rules/default/data_handling/media_parsing.json +++ b/AppInspector/rules/default/data_handling/media_parsing.json @@ -3,31 +3,47 @@ "name": "Audio Video Media Codec", "id": "AI013900", "description": "Audio Video Media Codec", - "tags":[ "Data.Parsing.Media" ], + "tags": [ + "Data.Parsing.Media" + ], "severity": "critical", "patterns": [ { "pattern": "ffmpeg|libraw|webp|DivX|XviDH.263|H.264|HEVC|MPEG4|Theora|3GP|Windows Media|JPEG 2000", "type": "regexword", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" }, { "pattern": "Quicktime|MPEG-4|VP8|VP6|MPEG1|MPEG2|MPEG-TS|MPEG-4|DNXHD|XDCAM|DVCPRO|IMX|XDCAM", "type": "regexword", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" }, { "pattern": "mux", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } - ] } ] \ No newline at end of file diff --git a/AppInspector/rules/default/data_handling/pastebin.json b/AppInspector/rules/default/data_handling/pastebin.json index 5c63d0b..82c6a75 100644 --- a/AppInspector/rules/default/data_handling/pastebin.json +++ b/AppInspector/rules/default/data_handling/pastebin.json @@ -3,14 +3,21 @@ "name": "CloudServices: DataStorage (PasteBin or Similar)", "id": "AI014000", "description": "CloudServices: DataStorage (PasteBin or Similar)", - "tags":[ "CloudServices.DataStorage.PasteBin" ], + "tags": [ + "CloudServices.DataStorage.PasteBin" + ], "severity": "moderate", "patterns": [ { "pattern": "pastebin|zerobin|ghostbin|hastebin", "type": "regex", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] diff --git a/AppInspector/rules/default/data_handling/xml_parsing.json b/AppInspector/rules/default/data_handling/xml_parsing.json index ce09bcd..939aff0 100644 --- a/AppInspector/rules/default/data_handling/xml_parsing.json +++ b/AppInspector/rules/default/data_handling/xml_parsing.json @@ -3,14 +3,18 @@ "name": "Data: Parses XML", "id": "AI014100", "description": "Data: Parses XML", - "tags":[ "Data.Parsing.XML" ], + "tags": [ + "Data.Parsing.XML" + ], "severity": "critical", "patterns": [ { "pattern": "sax|xpath|xmldom|xelement|xmldocument", "type": "regexword", "confidence": "high", - "scopes": [ "code" ] + "scopes": [ + "code" + ] } ] }, @@ -18,15 +22,21 @@ "name": "Data: Parses XML", "id": "AI014200", "description": "Data: Parses XML", - "applies_to": [ "python" ], - "tags":[ "Data.Parsing.XML" ], + "applies_to": [ + "python" + ], + "tags": [ + "Data.Parsing.XML" + ], "severity": "critical", "patterns": [ { "pattern": "lxml|etree|ElementTree|minidom|pulldom|xmlrpc", "type": "regexword", "confidence": "high", - "scopes": [ "code" ] + "scopes": [ + "code" + ] } ] }, @@ -34,15 +44,22 @@ "name": "Data: Parses XML", "id": "AI014300", "description": "Data: Parses XML", - "applies_to": [ "javascript", "typescript" ], - "tags":[ "Data.Parsing.XML" ], + "applies_to": [ + "javascript", + "typescript" + ], + "tags": [ + "Data.Parsing.XML" + ], "severity": "critical", "patterns": [ { "pattern": "parseFromString\\(.*,\"text\/xml\"\\)|xhttp\\.responseXML", "type": "regexword", "confidence": "high", - "scopes": [ "code" ] + "scopes": [ + "code" + ] } ] }, @@ -50,15 +67,21 @@ "name": "Data: Parses XML", "id": "AI014400", "description": "Data: Parses XML", - "applies_to": [ "java" ], - "tags":[ "Data.Parsing.XML" ], + "applies_to": [ + "java" + ], + "tags": [ + "Data.Parsing.XML" + ], "severity": "critical", "patterns": [ { "pattern": "jdom2|SAXBuilder", "type": "regexword", "confidence": "high", - "scopes": [ "code" ] + "scopes": [ + "code" + ] } ] }, @@ -66,15 +89,21 @@ "name": "Data: XSLT Transformations", "id": "AI014500", "description": "Data: XSLT Transformations", - "applies_to": [ "csharp" ], - "tags":[ "Data.Parsing.XSLT" ], + "applies_to": [ + "csharp" + ], + "tags": [ + "Data.Parsing.XSLT" + ], "severity": "critical", "patterns": [ { "pattern": "XslTransform|System\\.Xml\\.Xsl|XslCompiledTransform|transformNode", "type": "regexword", "confidence": "high", - "scopes": [ "code" ] + "scopes": [ + "code" + ] } ] }, @@ -82,15 +111,21 @@ "name": "Data: XSLT Transformations", "id": "AI014600", "description": "Data: XSLT Transformations", - "applies_to": [ "java" ], - "tags":[ "Data.Parsing.XSLT" ], + "applies_to": [ + "java" + ], + "tags": [ + "Data.Parsing.XSLT" + ], "severity": "critical", "patterns": [ { "pattern": "TransformerFactory|XSLDocument|javax\\.xml\\.transform", "type": "regexword", "confidence": "high", - "scopes": [ "code" ] + "scopes": [ + "code" + ] } ] }, @@ -98,15 +133,22 @@ "name": "Data: XSLT Transformations", "id": "AI014700", "description": "Data: XSLT Transformations", - "applies_to": [ "javascript", "typescript" ], - "tags":[ "Data.Parsing.XSLT" ], + "applies_to": [ + "javascript", + "typescript" + ], + "tags": [ + "Data.Parsing.XSLT" + ], "severity": "critical", "patterns": [ { "pattern": "XSLTProcessor", "type": "regexword", "confidence": "high", - "scopes": [ "code" ] + "scopes": [ + "code" + ] } ] }, @@ -114,14 +156,18 @@ "name": "Data: XSLT Transformations", "id": "AI014800", "description": "Data: XSLT Transformations", - "tags":[ "Data.Parsing.XSLT" ], + "tags": [ + "Data.Parsing.XSLT" + ], "severity": "critical", "patterns": [ { "pattern": ".xslt", "type": "string", "confidence": "high", - "scopes": [ "code" ] + "scopes": [ + "code" + ] } ], "conditions": [ @@ -129,8 +175,12 @@ "pattern": { "pattern": "exclude", "type": "regex", - "scopes": [ "code" ], - "modifiers": [ "i" ] + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ] }, "search_in": "same-line", "negate_finding": true @@ -141,15 +191,21 @@ "name": "Data: XSLT Transformations", "id": "AI014900", "description": "Data: XSLT Transformations", - "tags":[ "Data.Parsing.XSLT" ], + "tags": [ + "Data.Parsing.XSLT" + ], "severity": "critical", "patterns": [ { "pattern": "xslt|xsl:stylesheet", "type": "regexword", - "modifiers": [ "i" ], + "modifiers": [ + "i" + ], "confidence": "high", - "scopes": [ "code" ] + "scopes": [ + "code" + ] } ], "conditions": [ @@ -157,8 +213,12 @@ "pattern": { "pattern": "exclude", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ] + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ] }, "search_in": "same-line", "negate_finding": true diff --git a/AppInspector/rules/default/data_types/financial.json b/AppInspector/rules/default/data_types/financial.json index 7b9ddf6..c558d66 100644 --- a/AppInspector/rules/default/data_types/financial.json +++ b/AppInspector/rules/default/data_types/financial.json @@ -3,21 +3,32 @@ "name": "Data: Financial (Account)", "id": "AI015000", "description": "Data: Financial (Account)", - "tags":[ "Data.Sensitive.Financial.BankAccount" ], + "tags": [ + "Data.Sensitive.Financial.BankAccount" + ], "severity": "critical", "patterns": [ { "pattern": "bank\\s*(acct|account)", "type": "regexword", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" }, { "pattern": "(checking|savings|chk|401k|roth)\\s*(account|acct)", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -26,14 +37,20 @@ "name": "Data: Financial (Income)", "id": "AI015100", "description": "Data: Financial (Income)", - "tags":[ "Data.Sensitive.Financial.General" ], + "tags": [ + "Data.Sensitive.Financial.General" + ], "severity": "critical", "patterns": [ { "pattern": "finances*|financial", "type": "regex", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "low" } ] @@ -42,13 +59,19 @@ "name": "Data: Financial (Currency)", "id": "AI015200", "description": "Data: Financial (Currency)", - "tags":[ "Data.Sensitive.Financial.Currency" ], + "tags": [ + "Data.Sensitive.Financial.Currency" + ], "patterns": [ { "pattern": "currency|usd|money|dollar|coins|euro|peso|deutsche-mark|dinar|franc|krone|pound|rupee|shekel|yen", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "medium", "_comment": "Currencies" } @@ -58,14 +81,20 @@ "name": "Data: Financial (Credit Card)", "id": "AI015300", "description": "Data: Financial (Credit Card)", - "tags":[ "Data.Sensitive.Financial.CreditCard" ], + "tags": [ + "Data.Sensitive.Financial.CreditCard" + ], "severity": "critical", "patterns": [ { "pattern": "visa|americanexpress|amex|(master|discover|credit|debit)\\s*card", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -74,14 +103,20 @@ "name": "Data: Financial (Payroll)", "id": "AI015400", "description": "Data: Financial (Payroll)", - "tags":[ "Data.Sensitive.Financial.Payroll" ], + "tags": [ + "Data.Sensitive.Financial.Payroll" + ], "severity": "important", "patterns": [ { "pattern": "pay-*roll|salary|salaries|stock", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -90,14 +125,20 @@ "name": "Data: Financial (Salesdata)", "id": "AI015500", "description": "Data: Financial (Salesdata)", - "tags":[ "Data.Sensitive.Financial.Salesdata" ], + "tags": [ + "Data.Sensitive.Financial.Salesdata" + ], "severity": "critical", "patterns": [ { "pattern": "sales", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "low", "_comment": "sales data or forecast" } diff --git a/AppInspector/rules/default/data_types/media.json b/AppInspector/rules/default/data_types/media.json index 84130f9..624aee7 100644 --- a/AppInspector/rules/default/data_types/media.json +++ b/AppInspector/rules/default/data_types/media.json @@ -3,21 +3,33 @@ "name": "Data: Audio File", "id": "AI015600", "description": "Data: Audio File", - "tags":[ "Data.Media.Audio" ], + "tags": [ + "Data.Media.Audio" + ], "severity": "moderate", "patterns": [ { "pattern": "\\.(wma|wmv|asf|vp9|cr2|wav|mpeg|gsm|ogg|au|aiff|vox|aac|msv|dvf|flac|aifc|amz|atrac|m4a|m4p)\b", "type": "regex", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" }, { "pattern": "\\.(mp2|mp3|mp4|mpa|ra|rax|raw|smf|snd|sng|swa|hma|aac|ac3|eac3|Vorbis|pcm)\b", "type": "regex", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -26,21 +38,33 @@ "name": "Data: Video File", "id": "AI015700", "description": "Data: Video File", - "tags":[ "Data.Media.Video" ], + "tags": [ + "Data.Media.Video" + ], "severity": "moderate", "patterns": [ { "pattern": "\\.(avi|flv|mov|wmv|mp4|vob|hdv|ogg|oga|ogv|ogx)\b", "type": "regex", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" }, { "pattern": "\\.(mp4|m4v|f4v|f4a|m4b|m4r|f4b|mxf|op1a|op-atom)\b", "type": "regex", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] diff --git a/AppInspector/rules/default/data_types/secrets.json b/AppInspector/rules/default/data_types/secrets.json index 3aead56..96c5c95 100644 --- a/AppInspector/rules/default/data_types/secrets.json +++ b/AppInspector/rules/default/data_types/secrets.json @@ -3,28 +3,42 @@ "name": "Data: Access Credentials", "id": "AI015800", "description": "Data: Access Credentials", - "tags":[ "Data.Sensitive.Credentials" ], + "tags": [ + "Data.Sensitive.Credentials" + ], "severity": "critical", "patterns": [ { "pattern": "username|userid|passphrase|secret|credential|credentials", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", - "modifiers": [ "i" ] + "modifiers": [ + "i" + ] }, { "pattern": "\\.htpasswd|secret_key|private_key|authorized_keys|npmrc|\\.ssh", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", - "modifiers": [ "i" ] + "modifiers": [ + "i" + ] }, { "pattern": "auth_token|access_token|password|passwrd|client_credentials|client_*id|client_*secret", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -33,21 +47,31 @@ "name": "Data: Embedded Secret", "id": "AI015900", "description": "Data: Embedded Secret", - "tags":[ "Data.Sensitive.Secret" ], + "tags": [ + "Data.Sensitive.Secret" + ], "severity": "critical", "patterns": [ { "pattern": "(secret|pass).*[a-f0-9]{30,}", "type": "regex", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "low" }, { "pattern": "[\"'][a-f0-9]{30,}[\"']", "type": "regex", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "low" } ] @@ -56,20 +80,32 @@ "name": "Data: Secret", "id": "AI016000", "description": "Data: Secret", - "tags":[ "Data.Sensitive.Secret" ], + "tags": [ + "Data.Sensitive.Secret" + ], "severity": "important", "patterns": [ { "pattern": "(strictly|highly) confidential", "type": "regex", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ] + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ] }, { "pattern": "secret", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ] + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ] } ] }, @@ -77,14 +113,21 @@ "name": "Data: Product Key", "id": "AI016100", "description": "Data: Product Key", - "tags":[ "Data.Sensitive.ProductKey" ], + "tags": [ + "Data.Sensitive.ProductKey" + ], "severity": "critical", "patterns": [ { "pattern": "product[ ]*key", "type": "regex", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ] + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ] } ] } diff --git a/AppInspector/rules/default/data_types/sensitive.json b/AppInspector/rules/default/data_types/sensitive.json index e485ecc..bfdb814 100644 --- a/AppInspector/rules/default/data_types/sensitive.json +++ b/AppInspector/rules/default/data_types/sensitive.json @@ -3,35 +3,57 @@ "name": "Data: Sensitive (Identification)", "id": "AI016200", "description": "Data: Sensitive (Identification)", - "tags":[ "Data.Sensitive.Identification" ], + "tags": [ + "Data.Sensitive.Identification" + ], "severity": "critical", "patterns": [ { "pattern": "(first|last|sur)name", "type": "regex", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" }, { "pattern": "birthdate|ethnicity|gender|citizenship|nationality|martial|marriage|married|spouse|mother|father", "type": "regex", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" }, { "pattern": "socialsecurity|nationalid|passport|drvlic|studentID|govtID|driverlic|driverslicense", "type": "regex", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" }, { "pattern": "mobile_*(phone|number)|cell_*(phone|number)|telephone", "type": "regexword", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -40,21 +62,33 @@ "name": "Data: Sensitive (Identification)", "id": "AI016300", "description": "Data: Sensitive (Identification)", - "tags":[ "Data.Sensitive.Identification" ], + "tags": [ + "Data.Sensitive.Identification" + ], "severity": "important", "patterns": [ { "pattern": "mobile|cell|phone|ipaddress|ipaddr|geolocation|geotracking|identification|ssnumber|ssn|children|kids*", "type": "regexword", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "medium" }, { "pattern": "age", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "medium" } ], @@ -63,8 +97,12 @@ "pattern": { "pattern": "address|(first|last|sur)name|gender|contact|email|user|mother|father", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ] + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ] }, "search_in": "finding-region(-40,40)", "negate_finding": false @@ -75,14 +113,21 @@ "name": "Data: Sensitive (Identification)", "id": "AI016400", "description": "Data: Sensitive (Identification)", - "tags":[ "Data.Sensitive.Identification" ], + "tags": [ + "Data.Sensitive.Identification" + ], "severity": "important", "patterns": [ { "pattern": "email", "type": "regex", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "medium" } ], @@ -91,8 +136,12 @@ "pattern": { "pattern": "author|package.json", "type": "regex", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "_comment": "special case for package.json for SF" }, "search_in": "finding-region(-10,10)", @@ -100,19 +149,25 @@ } ] }, - { "name": "Data: Sensitive (Identification)", "id": "AI016500", "description": "Data: Sensitive (Identification)", - "tags":[ "Data.Sensitive.Identification" ], + "tags": [ + "Data.Sensitive.Identification" + ], "severity": "important", "patterns": [ { "pattern": "avatar|photo", "type": "regex", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "medium" } ], @@ -121,8 +176,12 @@ "pattern": { "pattern": "user", "type": "substring", - "scopes": [ "code" ], - "modifiers": [ "i" ] + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ] }, "search_in": "finding-region(-30,30)", "negate_finding": false @@ -133,14 +192,20 @@ "name": "Data: Sensitive (Medical)", "id": "AI016600", "description": "Data: Sensitive (Medical)", - "tags":[ "Data.Sensitive.Medical" ], + "tags": [ + "Data.Sensitive.Medical" + ], "severity": "important", "patterns": [ { "pattern": "medical|insurance|doctor|patient|physician|medicare|medicaid|physician|blood_*type", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "low" } ] @@ -149,14 +214,20 @@ "name": "Data: Sensitive (Account)", "id": "AI016700", "description": "Data: Sensitive (Account)", - "tags":[ "Data.Sensitive.UserAccount" ], + "tags": [ + "Data.Sensitive.UserAccount" + ], "severity": "critical", "patterns": [ { "pattern": "registration|membership|acctnum|accountnum|accountnumber", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ], @@ -165,8 +236,12 @@ "pattern": { "pattern": "user", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ] + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ] }, "search_in": "finding-region(-40,40)", "negate_finding": false diff --git a/AppInspector/rules/default/device_permissions/IOSPermissions.json b/AppInspector/rules/default/device_permissions/IOSPermissions.json index 04f2e98..409d97c 100644 --- a/AppInspector/rules/default/device_permissions/IOSPermissions.json +++ b/AppInspector/rules/default/device_permissions/IOSPermissions.json @@ -3,15 +3,23 @@ "name": "Permissions Request: Contacts (iOS)", "id": "AI016731", "description": "Permissions Request: Contacts (iOS)", - "applies_to":[ "swift" ], - "tags":[ "Device.Permissions.Contacts.iOS" ], + "applies_to": [ + "swift" + ], + "tags": [ + "Device.Permissions.Contacts.iOS" + ], "severity": "moderate", "patterns": [ { "pattern": "permission.contacts", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -20,15 +28,23 @@ "name": "Permissions Request: LocationAlways (iOS)", "id": "AI016732", "description": "Permissions Request: LocationAlways (iOS)", - "applies_to":[ "swift" ], - "tags":[ "Device.Permissions.LocationAlways.iOS" ], + "applies_to": [ + "swift" + ], + "tags": [ + "Device.Permissions.LocationAlways.iOS" + ], "severity": "moderate", "patterns": [ { "pattern": "permission.locationAlways", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -37,15 +53,23 @@ "name": "Permissions Request: LocationWhenInUse (iOS)", "id": "AI016733", "description": "Permissions Request: LocationWhenInUse (iOS)", - "applies_to":[ "swift" ], - "tags":[ "Device.Permissions.LocationWhenInUse.iOS" ], + "applies_to": [ + "swift" + ], + "tags": [ + "Device.Permissions.LocationWhenInUse.iOS" + ], "severity": "moderate", "patterns": [ { "pattern": "permission.locationWhenInUse", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -54,15 +78,23 @@ "name": "Permissions Request: Notifications (iOS)", "id": "AI016734", "description": "Permissions Request: Notifications (iOS)", - "applies_to":[ "swift" ], - "tags":[ "Device.Permissions.Notifications.iOS" ], + "applies_to": [ + "swift" + ], + "tags": [ + "Device.Permissions.Notifications.iOS" + ], "severity": "moderate", "patterns": [ { "pattern": "permission.notifications", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -71,15 +103,23 @@ "name": "Permissions Request: Microphone (iOS)", "id": "AI016735", "description": "Permissions Request: Microphone (iOS)", - "applies_to":[ "swift" ], - "tags":[ "Device.Permissions.Microphone.iOS" ], + "applies_to": [ + "swift" + ], + "tags": [ + "Device.Permissions.Microphone.iOS" + ], "severity": "moderate", "patterns": [ { "pattern": "permission.microphone", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -88,15 +128,23 @@ "name": "Permissions Request: Camera (iOS)", "id": "AI016736", "description": "Permissions Request: Camera (iOS)", - "applies_to":[ "swift" ], - "tags":[ "Device.Permissions.Camera.iOS" ], + "applies_to": [ + "swift" + ], + "tags": [ + "Device.Permissions.Camera.iOS" + ], "severity": "moderate", "patterns": [ { "pattern": "permission.camera", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -105,15 +153,23 @@ "name": "Permissions Request: Media Library (iOS)", "id": "AI016737", "description": "Permissions Request: Media Library (iOS)", - "applies_to":[ "swift" ], - "tags":[ "Device.Permissions.MediaLibrary.iOS" ], + "applies_to": [ + "swift" + ], + "tags": [ + "Device.Permissions.MediaLibrary.iOS" + ], "severity": "moderate", "patterns": [ { "pattern": "permission\\.photos|permission\\.mediaLibrary", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -122,15 +178,23 @@ "name": "Permissions Request: Reminders (iOS)", "id": "AI016738", "description": "Permissions Request: Reminders (iOS)", - "applies_to":[ "swift" ], - "tags":[ "Device.Permissions.Reminders.iOS" ], + "applies_to": [ + "swift" + ], + "tags": [ + "Device.Permissions.Reminders.iOS" + ], "severity": "moderate", "patterns": [ { "pattern": "permission.reminders", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -139,15 +203,23 @@ "name": "Permissions Request: Events (iOS)", "id": "AI016739", "description": "Permissions Request: Events (iOS)", - "applies_to":[ "swift" ], - "tags":[ "Device.Permissions.CalendarEvents.iOS" ], + "applies_to": [ + "swift" + ], + "tags": [ + "Device.Permissions.CalendarEvents.iOS" + ], "severity": "moderate", "patterns": [ { "pattern": "permission.events", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -156,15 +228,23 @@ "name": "Permissions Request: Bluetooth (iOS)", "id": "AI016740", "description": "Permissions Request: Bluetooth (iOS)", - "applies_to":[ "swift" ], - "tags":[ "Device.Permissions.Bluetooth.iOS" ], + "applies_to": [ + "swift" + ], + "tags": [ + "Device.Permissions.Bluetooth.iOS" + ], "severity": "moderate", "patterns": [ { "pattern": "permission.bluetooth", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -173,15 +253,23 @@ "name": "Permissions Request: Motion (iOS)", "id": "AI016741", "description": "Permissions Request: Motion (iOS)", - "applies_to":[ "swift" ], - "tags":[ "Device.Permissions.Motion.iOS" ], + "applies_to": [ + "swift" + ], + "tags": [ + "Device.Permissions.Motion.iOS" + ], "severity": "moderate", "patterns": [ { "pattern": "permission.motion", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -190,15 +278,23 @@ "name": "Permissions Request: SpeechRecognizer (iOS)", "id": "AI016742", "description": "Permissions Request: SpeechRecognizer (iOS)", - "applies_to":[ "swift" ], - "tags":[ "Device.Permissions.SpeechRecognizer.iOS" ], + "applies_to": [ + "swift" + ], + "tags": [ + "Device.Permissions.SpeechRecognizer.iOS" + ], "severity": "moderate", "patterns": [ { "pattern": "permission.speechRecognizer", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -207,15 +303,23 @@ "name": "Permissions Request: Siri (iOS)", "id": "AI016743", "description": "Permissions Request: Siri (iOS)", - "applies_to":[ "swift" ], - "tags":[ "Device.Permissions.VoiceCommand.iOS" ], + "applies_to": [ + "swift" + ], + "tags": [ + "Device.Permissions.VoiceCommand.iOS" + ], "severity": "moderate", "patterns": [ { "pattern": "permission.siri", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -224,15 +328,23 @@ "name": "Permissions Request: Never (iOS)", "id": "AI016744", "description": "Permissions Request: Never (iOS)", - "applies_to":[ "swift" ], - "tags":[ "Device.Permissions.Never.iOS" ], + "applies_to": [ + "swift" + ], + "tags": [ + "Device.Permissions.Never.iOS" + ], "severity": "moderate", "patterns": [ { "pattern": "permission.never", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] diff --git a/AppInspector/rules/default/device_permissions/UWP.json b/AppInspector/rules/default/device_permissions/UWP.json index 5fe0181..2494962 100644 --- a/AppInspector/rules/default/device_permissions/UWP.json +++ b/AppInspector/rules/default/device_permissions/UWP.json @@ -3,15 +3,23 @@ "name": "Declared Capabilities: InternetClient (Microsoft UWP)", "id": "AI016745", "description": "Declared Capabilities: InternetClient (Microsoft UWP)", - "applies_to":[ "Package.appxmanifest" ], - "tags":[ "Device.Permissions.InternetClient.Microsoft.UWP" ], + "applies_to": [ + "Package.appxmanifest" + ], + "tags": [ + "Device.Permissions.InternetClient.Microsoft.UWP" + ], "severity": "moderate", "patterns": [ { "pattern": "Name=\"internetClient\"", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -20,29 +28,45 @@ "name": "Declared Capabilities: Media Library (Microsoft UWP)", "id": "AI016746", "description": "Declared Capabilities: Media Library (Microsoft UWP)", - "applies_to":[ "Package.appxmanifest" ], - "tags":[ "Device.Permissions.MediaLibrary.Microsoft.UWP" ], + "applies_to": [ + "Package.appxmanifest" + ], + "tags": [ + "Device.Permissions.MediaLibrary.Microsoft.UWP" + ], "severity": "moderate", "patterns": [ { "pattern": "Name=\"musicLibrary\"", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" }, { "pattern": "Name=\"videosLibrary\"", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" }, { "pattern": "Name=\"picturesLibrary\"", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -51,15 +75,23 @@ "name": "Declared Capabilities: RemoveableStorage (Microsoft UWP)", "id": "AI016747", "description": "Declared Capabilities: RemoveableStorage (Microsoft UWP)", - "applies_to":[ "Package.appxmanifest" ], - "tags":[ "Device.Permissions.RemoveableStorage.Microsoft.UWP" ], + "applies_to": [ + "Package.appxmanifest" + ], + "tags": [ + "Device.Permissions.RemoveableStorage.Microsoft.UWP" + ], "severity": "moderate", "patterns": [ { "pattern": "Name=\"removableStorage\"", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -68,15 +100,23 @@ "name": "Declared Capabilities: Appointments (Microsoft UWP)", "id": "AI016748", "description": "Declared Capabilities: Appointments (Microsoft UWP)", - "applies_to":[ "Package.appxmanifest" ], - "tags":[ "Device.Permissions.CalendarEvents.Microsoft.UWP" ], + "applies_to": [ + "Package.appxmanifest" + ], + "tags": [ + "Device.Permissions.CalendarEvents.Microsoft.UWP" + ], "severity": "moderate", "patterns": [ { "pattern": "Name=\"appointments\"", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -85,15 +125,23 @@ "name": "Declared Capabilities: PhoneCall (Microsoft UWP)", "id": "AI016749", "description": "Declared Capabilities: PhoneCall (Microsoft UWP)", - "applies_to":[ "Package.appxmanifest" ], - "tags":[ "Device.Permissions.PhoneCall.Microsoft.UWP" ], + "applies_to": [ + "Package.appxmanifest" + ], + "tags": [ + "Device.Permissions.PhoneCall.Microsoft.UWP" + ], "severity": "moderate", "patterns": [ { "pattern": "Name=\"phoneCall\"", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -102,15 +150,23 @@ "name": "Declared Capabilities: RecordedCalls (Microsoft UWP)", "id": "AI016750", "description": "Declared Capabilities: RecordedCalls (Microsoft UWP)", - "applies_to":[ "Package.appxmanifest" ], - "tags":[ "Device.Permissions.RecordedCalls.Microsoft.UWP" ], + "applies_to": [ + "Package.appxmanifest" + ], + "tags": [ + "Device.Permissions.RecordedCalls.Microsoft.UWP" + ], "severity": "moderate", "patterns": [ { "pattern": "Name=\"recordedCallsFolder\"", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -119,15 +175,23 @@ "name": "Declared Capabilities: UserAcctRead (Microsoft UWP)", "id": "AI016751", "description": "Declared Capabilities: UserAcctRead (Microsoft UWP)", - "applies_to":[ "Package.appxmanifest" ], - "tags":[ "Device.Permissions.UserAcctRead.Microsoft.UWP" ], + "applies_to": [ + "Package.appxmanifest" + ], + "tags": [ + "Device.Permissions.UserAcctRead.Microsoft.UWP" + ], "severity": "moderate", "patterns": [ { "pattern": "Name=\"userAccountInformation\"", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -137,15 +201,23 @@ "id": "AI016752", "VoipCall": null, "description": "Declared Capabilities: VoipCall (Microsoft UWP)", - "applies_to":[ "Package.appxmanifest" ], - "tags":[ "Device.Permissions.VoipCall.Microsoft.UWP" ], + "applies_to": [ + "Package.appxmanifest" + ], + "tags": [ + "Device.Permissions.VoipCall.Microsoft.UWP" + ], "severity": "moderate", "patterns": [ { "pattern": "Name=\"voipCall\"", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -154,15 +226,23 @@ "name": "Declared Capabilities: System Management (Microsoft UWP)", "id": "AI016753", "description": "Declared Capabilities: System Management (Microsoft UWP)", - "applies_to":[ "Package.appxmanifest" ], - "tags":[ "Device.Permissions.SystemSettings.Microsoft.UWP" ], + "applies_to": [ + "Package.appxmanifest" + ], + "tags": [ + "Device.Permissions.SystemSettings.Microsoft.UWP" + ], "severity": "moderate", "patterns": [ { "pattern": "Name=\"systemManagement\"", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -171,15 +251,23 @@ "name": "Declared Capabilities: Microphone (Microsoft UWP)", "id": "AI016754", "description": "Declared Capabilities: Microphone (Microsoft UWP)", - "applies_to":[ "Package.appxmanifest" ], - "tags":[ "Device.Permissions.Microphone.Microsoft.UWP" ], + "applies_to": [ + "Package.appxmanifest" + ], + "tags": [ + "Device.Permissions.Microphone.Microsoft.UWP" + ], "severity": "moderate", "patterns": [ { "pattern": "Name=\"microphone\"", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -188,15 +276,23 @@ "name": "Declared Capabilities: WebCam (Microsoft UWP)", "id": "AI016755", "description": "Declared Capabilities: WebCam (Microsoft UWP)", - "applies_to":[ "Package.appxmanifest" ], - "tags":[ "Device.Permissions.Camera.Microsoft.UWP" ], + "applies_to": [ + "Package.appxmanifest" + ], + "tags": [ + "Device.Permissions.Camera.Microsoft.UWP" + ], "severity": "moderate", "patterns": [ { "pattern": "Name=\"webcam\"", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -205,15 +301,23 @@ "name": "Declared Capabilities: Bluetooth (Microsoft UWP)", "id": "AI016756", "description": "Declared Capabilities: Bluetooth (Microsoft UWP)", - "applies_to":[ "Package.appxmanifest" ], - "tags":[ "Device.Permissions.Bluetooth.Microsoft.UWP" ], + "applies_to": [ + "Package.appxmanifest" + ], + "tags": [ + "Device.Permissions.Bluetooth.Microsoft.UWP" + ], "severity": "moderate", "patterns": [ { "pattern": "Name=\"bluetooth\",", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] diff --git a/AppInspector/rules/default/device_permissions/android_intents.json b/AppInspector/rules/default/device_permissions/android_intents.json index cf8b609..dd4c1eb 100644 --- a/AppInspector/rules/default/device_permissions/android_intents.json +++ b/AppInspector/rules/default/device_permissions/android_intents.json @@ -7,13 +7,17 @@ "java", "kotlin" ], - "tags":[ "Device.Permissions.InternetClient.Android" ], + "tags": [ + "Device.Permissions.InternetClient.Android" + ], "severity": "moderate", "patterns": [ { "pattern": "Intent\\.ACTION_VIEW", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ], @@ -22,8 +26,12 @@ "pattern": { "pattern": "Uri\\.parse\\(\"https?:\\/\\/www\\.\\);", "type": "regex", - "scopes": [ "code" ], - "modifiers": [ "i" ] + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ] }, "search_in": "finding-region(-5,5)", "negate_finding": false @@ -38,13 +46,17 @@ "java", "kotlin" ], - "tags":[ "Device.Permissions.ViewMap.Android" ], + "tags": [ + "Device.Permissions.ViewMap.Android" + ], "severity": "moderate", "patterns": [ { "pattern": "Intent\\.ACTION_VIEW", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ], @@ -53,8 +65,12 @@ "pattern": { "pattern": "Uri\\.parse\\(\"geo:", "type": "regex", - "scopes": [ "code" ], - "modifiers": [ "i" ] + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ] }, "search_in": "finding-region(-5,5)", "negate_finding": false @@ -63,8 +79,12 @@ "pattern": { "pattern": "Uri\\.parse(\"http:\/\/maps\\.google\\.com\/maps\");", "type": "regex", - "scopes": [ "code" ], - "modifiers": [ "i" ] + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ] }, "search_in": "finding-region(-5,5)", "negate_finding": false @@ -79,13 +99,17 @@ "java", "kotlin" ], - "tags":[ "Device.Permissions.PhoneCalls.Android" ], + "tags": [ + "Device.Permissions.PhoneCalls.Android" + ], "severity": "moderate", "patterns": [ { "pattern": "Intent\\.ACTION_DIAL|Intent\\.ACTION_CALL", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -98,13 +122,17 @@ "java", "kotlin" ], - "tags":[ "Device.Permissions.SMS.Android" ], + "tags": [ + "Device.Permissions.SMS.Android" + ], "severity": "moderate", "patterns": [ { "pattern": "Intent\\.ACTION_(VIEW|SEND)", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ], @@ -113,8 +141,12 @@ "pattern": { "pattern": "sms|mms|smsto", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ] + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ] }, "search_in": "finding-region(-5,5)", "negate_finding": false @@ -129,13 +161,17 @@ "java", "kotlin" ], - "tags":[ "Device.Permissions.Email.Android" ], + "tags": [ + "Device.Permissions.Email.Android" + ], "severity": "moderate", "patterns": [ { "pattern": "Intent\\.ACTION_SENDTO|Intent\\.ACTION_SEND", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ], @@ -144,8 +180,12 @@ "pattern": { "pattern": "Intent\\.EXTRA_EMAIL|Intent\\.EXTRA_SUBJECT", "type": "regex", - "scopes": [ "code" ], - "modifiers": [ "i" ] + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ] }, "search_in": "finding-region(-5,5)", "negate_finding": false @@ -154,8 +194,12 @@ "pattern": { "pattern": "mailto:", "type": "regex", - "scopes": [ "code" ], - "modifiers": [ "i" ] + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ] }, "search_in": "finding-region(-5,5)", "negate_finding": false @@ -170,13 +214,17 @@ "java", "kotlin" ], - "tags":[ "Device.Permissions.PlayMedia.Android" ], + "tags": [ + "Device.Permissions.PlayMedia.Android" + ], "severity": "moderate", "patterns": [ { "pattern": "Intent\\.ACTION_VIEW", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ], @@ -185,8 +233,12 @@ "pattern": { "pattern": "Media\\.INTERNAL_CONTENT_URI|mp3|mp4", "type": "regex", - "scopes": [ "code" ], - "modifiers": [ "i" ] + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ] }, "search_in": "finding-region(-5,5)", "negate_finding": false @@ -201,13 +253,17 @@ "java", "kotlin" ], - "tags":[ "Device.Permissions.WebSearch.Android" ], + "tags": [ + "Device.Permissions.WebSearch.Android" + ], "severity": "moderate", "patterns": [ { "pattern": "Intent\\.ACTION_WEB_SEARCH", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -220,13 +276,17 @@ "java", "kotlin" ], - "tags":[ "Device.Permissions.SetAlarm.Android" ], + "tags": [ + "Device.Permissions.SetAlarm.Android" + ], "severity": "moderate", "patterns": [ { "pattern": "ACTION_SET_ALARM", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -239,13 +299,17 @@ "java", "kotlin" ], - "tags":[ "Device.Permissions.SetTimer.Android" ], + "tags": [ + "Device.Permissions.SetTimer.Android" + ], "severity": "moderate", "patterns": [ { "pattern": "ACTION_SET_TIMER", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -258,13 +322,17 @@ "java", "kotlin" ], - "tags":[ "Device.Permissions.CalendarEvents.Android" ], + "tags": [ + "Device.Permissions.CalendarEvents.Android" + ], "severity": "moderate", "patterns": [ { "pattern": "Intent\\.ACTION_INSERT", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ], @@ -273,8 +341,12 @@ "pattern": { "pattern": "Events\\.TITLE", "type": "regex", - "scopes": [ "code" ], - "modifiers": [ "i" ] + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ] }, "search_in": "finding-region(-5,5)", "negate_finding": false @@ -289,13 +361,17 @@ "java", "kotlin" ], - "tags":[ "Device.Permissions.Camera.Android" ], + "tags": [ + "Device.Permissions.Camera.Android" + ], "severity": "moderate", "patterns": [ { "pattern": "ACTION_IMAGE_CAPTURE|ACTION_VIDEO_CAPTURE", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -308,13 +384,17 @@ "java", "kotlin" ], - "tags":[ "Device.Permissions.SelectContact.Android" ], + "tags": [ + "Device.Permissions.SelectContact.Android" + ], "severity": "moderate", "patterns": [ { "pattern": "Intent\\.ACTION_PICK", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ], @@ -323,7 +403,9 @@ "pattern": { "pattern": "Contacts\\.CONTENT_TYPE|ContactsContract\\.CommonDataKinds", "type": "regex", - "scopes": [ "code" ] + "scopes": [ + "code" + ] }, "search_in": "finding-region(-5,5)", "negate_finding": false @@ -338,13 +420,17 @@ "java", "kotlin" ], - "tags":[ "Device.Permissions.Contacts.Android" ], + "tags": [ + "Device.Permissions.Contacts.Android" + ], "severity": "moderate", "patterns": [ { "pattern": "Intent\\.READ_CONTACTS", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -357,13 +443,17 @@ "java", "kotlin" ], - "tags":[ "Device.Permissions.GetContent.Android" ], + "tags": [ + "Device.Permissions.GetContent.Android" + ], "severity": "moderate", "patterns": [ { "pattern": "Intent\\.ACTION_GET_CONTENT", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -376,13 +466,17 @@ "java", "kotlin" ], - "tags":[ "Device.Permissions.OpenDocument.Android" ], + "tags": [ + "Device.Permissions.OpenDocument.Android" + ], "severity": "moderate", "patterns": [ { "pattern": "Intent\\.ACTION_OPEN_DOCUMENT", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -395,13 +489,17 @@ "java", "kotlin" ], - "tags":[ "Device.Permissions.CreateDocument.Android" ], + "tags": [ + "Device.Permissions.CreateDocument.Android" + ], "severity": "moderate", "patterns": [ { "pattern": "Intent\\.ACTION_CREATE_DOCUMENT", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -414,13 +512,17 @@ "java", "kotlin" ], - "tags":[ "Device.Permissions.SystemSettings.Android" ], + "tags": [ + "Device.Permissions.SystemSettings.Android" + ], "severity": "moderate", "patterns": [ { "pattern": "Intent\\.ACTION_*_SETTINGS", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -433,13 +535,17 @@ "java", "kotlin" ], - "tags":[ "Device.Permissions.AddorEditContent.Android" ], + "tags": [ + "Device.Permissions.AddorEditContent.Android" + ], "severity": "moderate", "patterns": [ { "pattern": "Intent\\.ACTION_EDIT|Intent\\.ACTION_INSERT_OR_EDIT", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -452,13 +558,17 @@ "java", "kotlin" ], - "tags":[ "Device.Permissions.InstallPackage.Android" ], + "tags": [ + "Device.Permissions.InstallPackage.Android" + ], "severity": "moderate", "patterns": [ { "pattern": "Intent\\.ACTION_INSTALL_PACKAGE|Intent\\.ACTION_PACKAGE_ADDED", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -471,13 +581,17 @@ "java", "kotlin" ], - "tags":[ "Device.Permissions.ProfileRemoved.Android" ], + "tags": [ + "Device.Permissions.ProfileRemoved.Android" + ], "severity": "moderate", "patterns": [ { "pattern": "Intent\\.ACTION_MANAGED_PROFILE_REMOVED|Intent\\.ACTION_UID_REMOVED", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -490,13 +604,17 @@ "java", "kotlin" ], - "tags":[ "Device.Permissions.ProfileUnlocked.Android" ], + "tags": [ + "Device.Permissions.ProfileUnlocked.Android" + ], "severity": "moderate", "patterns": [ { "pattern": "Intent\\.ACTION_MANAGED_PROFILE_UNLOCKED", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -509,13 +627,17 @@ "java", "kotlin" ], - "tags":[ "Device.Permissions.PackageChanged.Android" ], + "tags": [ + "Device.Permissions.PackageChanged.Android" + ], "severity": "moderate", "patterns": [ { "pattern": "Intent\\.ACTION_MY_PACKAGE_REPLACED|Intent\\.ACTION_PACKAGE_CHANGED", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -528,13 +650,17 @@ "java", "kotlin" ], - "tags":[ "Device.Permissions.Shutdown.Android" ], + "tags": [ + "Device.Permissions.Shutdown.Android" + ], "severity": "moderate", "patterns": [ { "pattern": "Intent\\.ACTION_REBOOT|Intent\\.ACTION_SHUTDOWN", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -547,13 +673,17 @@ "java", "kotlin" ], - "tags":[ "Device.Permissions.Search.Android" ], + "tags": [ + "Device.Permissions.Search.Android" + ], "severity": "moderate", "patterns": [ { "pattern": "Intent\\.ACTION_SEARCH", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -566,13 +696,17 @@ "java", "kotlin" ], - "tags":[ "Device.Permissions.VoiceCommand.Android" ], + "tags": [ + "Device.Permissions.VoiceCommand.Android" + ], "severity": "moderate", "patterns": [ { "pattern": "Intent\\.ACTION_VOICE_COMMAND", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -585,13 +719,17 @@ "java", "kotlin" ], - "tags":[ "Device.Permissions.AllowUnsafeUri.Android" ], + "tags": [ + "Device.Permissions.AllowUnsafeUri.Android" + ], "severity": "moderate", "patterns": [ { "pattern": "Intent\\.URI_ALLOW_UNSAFE", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] diff --git a/AppInspector/rules/default/frameworks/PHP.json b/AppInspector/rules/default/frameworks/PHP.json index 4ec6915..6ac8562 100644 --- a/AppInspector/rules/default/frameworks/PHP.json +++ b/AppInspector/rules/default/frameworks/PHP.json @@ -3,15 +3,25 @@ "name": "Development Framework: Laravel", "id": "AI023701", "description": "Development Framework: Laravel", - "applies_to": [ "python" ], - "tags": [ "Framework.Development.PHP", "Framework.Development.Laravel" ], + "applies_to": [ + "python" + ], + "tags": [ + "Framework.Development.PHP", + "Framework.Development.Laravel" + ], "severity": "moderate", "patterns": [ { "pattern": "Laravel", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -20,15 +30,25 @@ "name": "Development Framework: Symfony", "id": "AI025302", "description": "Development Framework: Symfony", - "applies_to": [ "python" ], - "tags": [ "Framework.Development.PHP", "Framework.Development.Symfony" ], + "applies_to": [ + "python" + ], + "tags": [ + "Framework.Development.PHP", + "Framework.Development.Symfony" + ], "severity": "moderate", "patterns": [ { "pattern": "Symfony", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -37,15 +57,25 @@ "name": "Development Framework: CodeIgniter", "id": "AI025303", "description": "Development Framework: CodeIgniter", - "applies_to": [ "python" ], - "tags": [ "Framework.Development.PHP", "Framework.Development.CodeIgniter" ], + "applies_to": [ + "python" + ], + "tags": [ + "Framework.Development.PHP", + "Framework.Development.CodeIgniter" + ], "severity": "moderate", "patterns": [ { "pattern": "CodeIgniter", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -54,15 +84,25 @@ "name": "Development Framework: Zend", "id": "AI025304", "description": "Development Framework: Zend", - "applies_to": [ "python" ], - "tags": [ "Framework.Development.PHP", "Framework.Development.Zend" ], + "applies_to": [ + "python" + ], + "tags": [ + "Framework.Development.PHP", + "Framework.Development.Zend" + ], "severity": "moderate", "patterns": [ { "pattern": "Zend", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -71,15 +111,25 @@ "name": "Development Framework: Fuelphp", "id": "AI025305", "description": "Development Framework: Fuelphp", - "applies_to": [ "python" ], - "tags": [ "Framework.Development.PHP", "Framework.Development.Fuelphp" ], + "applies_to": [ + "python" + ], + "tags": [ + "Framework.Development.PHP", + "Framework.Development.Fuelphp" + ], "severity": "moderate", "patterns": [ { "pattern": "Fuelphp", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -88,15 +138,25 @@ "name": "Development Framework: Slim", "id": "AI025306", "description": "Development Framework: Slim", - "applies_to": [ "python" ], - "tags": [ "Framework.Development.PHP", "Framework.Development.Slim" ], + "applies_to": [ + "python" + ], + "tags": [ + "Framework.Development.PHP", + "Framework.Development.Slim" + ], "severity": "moderate", "patterns": [ { "pattern": "Slim", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -105,15 +165,25 @@ "name": "Development Framework: Phalcon", "id": "AI025307", "description": "Development Framework: Phalcon", - "applies_to": [ "python" ], - "tags": [ "Framework.Development.PHP", "Framework.Development.Phalcon" ], + "applies_to": [ + "python" + ], + "tags": [ + "Framework.Development.PHP", + "Framework.Development.Phalcon" + ], "severity": "moderate", "patterns": [ { "pattern": "Phalcon", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -122,15 +192,25 @@ "name": "Development Framework: Aura", "id": "AI025308", "description": "Development Framework: Aura", - "applies_to": [ "python" ], - "tags": [ "Framework.Development.PHP", "Framework.Development.Aura" ], + "applies_to": [ + "python" + ], + "tags": [ + "Framework.Development.PHP", + "Framework.Development.Aura" + ], "severity": "moderate", "patterns": [ { "pattern": "Aura", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -139,15 +219,25 @@ "name": "Development Framework: Yii", "id": "AI025309", "description": "Development Framework: Yii", - "applies_to": [ "python" ], - "tags": [ "Framework.Development.PHP", "Framework.Development.Yii" ], + "applies_to": [ + "python" + ], + "tags": [ + "Framework.Development.PHP", + "Framework.Development.Yii" + ], "severity": "moderate", "patterns": [ { "pattern": "Yii", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -156,15 +246,25 @@ "name": "Development Framework: Cakephp", "id": "AI025310", "description": "Development Framework: Cakephp", - "applies_to": [ "python" ], - "tags": [ "Framework.Development.PHP", "Framework.Development.Cakephp" ], + "applies_to": [ + "python" + ], + "tags": [ + "Framework.Development.PHP", + "Framework.Development.Cakephp" + ], "severity": "moderate", "patterns": [ { "pattern": "Cakephp", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] diff --git a/AppInspector/rules/default/frameworks/build.json b/AppInspector/rules/default/frameworks/build.json index 2f62238..81172a4 100644 --- a/AppInspector/rules/default/frameworks/build.json +++ b/AppInspector/rules/default/frameworks/build.json @@ -3,15 +3,24 @@ "name": "Development: Build Tool (Maven)", "id": "AI016800", "description": "Development: Build Tool (Maven)", - "applies_to": [ "pom.xml" ], - "tags": [ "Development.Build.Maven" ], + "applies_to": [ + "pom.xml" + ], + "tags": [ + "Development.Build.Maven" + ], "severity": "moderate", "patterns": [ { "pattern": "Maven", "type": "regex", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -20,15 +29,24 @@ "name": "Development: Build Tool (Ant)", "id": "AI016900", "description": "Development: Build Tool (Ant)", - "applies_to": [ "build.xml" ], - "tags": [ "Development.Build.Ant" ], + "applies_to": [ + "build.xml" + ], + "tags": [ + "Development.Build.Ant" + ], "severity": "moderate", "patterns": [ { "pattern": "project", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -37,15 +55,24 @@ "name": "Development: Build Tool (Gradle)", "id": "AI017000", "description": "Development: Build Tool (Gradle)", - "applies_to": [ "build.gradle" ], - "tags": [ "Development.Build.Gradle" ], + "applies_to": [ + "build.gradle" + ], + "tags": [ + "Development.Build.Gradle" + ], "severity": "moderate", "patterns": [ { "pattern": "sourceSets", "type": "regex", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -54,15 +81,24 @@ "name": "Development: Build Tool (Jenkins)", "id": "AI017100", "description": "Development: Build Tool (Jenkins)", - "applies_to": [ "jenkins" ], - "tags": [ "Development.Build.Jenkins" ], + "applies_to": [ + "jenkins" + ], + "tags": [ + "Development.Build.Jenkins" + ], "severity": "moderate", "patterns": [ { "pattern": "pipeline", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -71,15 +107,24 @@ "name": "Development: Build Tool (SBT)", "id": "AI017200", "description": "Development: Build Tool (SBT)", - "applies_to": [ "sbt" ], - "tags": [ "Development.Build.SBT" ], + "applies_to": [ + "sbt" + ], + "tags": [ + "Development.Build.SBT" + ], "severity": "moderate", "patterns": [ { "pattern": "build", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -88,15 +133,24 @@ "name": "Development: Build Tool (Bamboo)", "id": "AI017300", "description": "Development: Build Tool (Bamboo)", - "applies_to": [ "yaml" ], - "tags": [ "Development.Build.Bamboo" ], + "applies_to": [ + "yaml" + ], + "tags": [ + "Development.Build.Bamboo" + ], "severity": "moderate", "patterns": [ { "pattern": "bamboo", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -106,14 +160,21 @@ "id": "AI017400", "description": "Development: Build Tool (TeamCity)", "recommendation": "", - "tags": [ "Development.Build.TeamCity" ], + "tags": [ + "Development.Build.TeamCity" + ], "severity": "moderate", "patterns": [ { "pattern": "teamcity", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -122,15 +183,24 @@ "name": "Development: Build Tool (Grape)", "id": "AI017500", "description": "Development: Build Tool (Grape)", - "applies_to": [ "java" ], - "tags": [ "Development.Build.Grape" ], + "applies_to": [ + "java" + ], + "tags": [ + "Development.Build.Grape" + ], "severity": "moderate", "patterns": [ { "pattern": "grape", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -139,15 +209,24 @@ "name": "Development: Build Tool (Ivy)", "id": "AI017600", "description": "Development: Build Tool (Ivy)", - "applies_to": [ "build.xml" ], - "tags": [ "Development.Build.Ivy" ], + "applies_to": [ + "build.xml" + ], + "tags": [ + "Development.Build.Ivy" + ], "severity": "moderate", "patterns": [ { "pattern": "ivy", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -156,15 +235,24 @@ "name": "Development: Build Tool (Leiningen)", "id": "AI017700", "description": "Development: Build Tool (Leiningen)", - "applies_to": [ "project.clj" ], - "tags": [ "Development.Build.Leiningen" ], + "applies_to": [ + "project.clj" + ], + "tags": [ + "Development.Build.Leiningen" + ], "severity": "moderate", "patterns": [ { "pattern": "plug-ins|repositories|build|java", "type": "regexword", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -173,15 +261,25 @@ "name": "Development: Build Tool (Visual Studio)", "id": "AI017800", "description": "Development: Build Tool (Studio)", - "applies_to": [ "VSSolution", "VSProject" ], - "tags": [ "Development.Build.VisualStudio" ], + "applies_to": [ + "VSSolution", + "VSProject" + ], + "tags": [ + "Development.Build.VisualStudio" + ], "severity": "moderate", "patterns": [ { "pattern": "VisualStudio|Visual Studio", "type": "regex", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] diff --git a/AppInspector/rules/default/frameworks/c.json b/AppInspector/rules/default/frameworks/c.json index 6e80a9e..cdc20c5 100644 --- a/AppInspector/rules/default/frameworks/c.json +++ b/AppInspector/rules/default/frameworks/c.json @@ -4,15 +4,23 @@ "id": "AI017900", "description": "Development Framework: Standard C Library", "recommendation": "", - "applies_to": [ "c" ], - "tags": [ "Framework.Development.Library.StandardC" ], + "applies_to": [ + "c" + ], + "tags": [ + "Framework.Development.Library.StandardC" + ], "severity": "moderate", "patterns": [ { "pattern": "std::", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -22,22 +30,34 @@ "id": "AI018000", "description": "Development Framework: Boost", "recommendation": "", - "applies_to": [ "c" ], - "tags": [ "Framework.Development.Library.Boost" ], + "applies_to": [ + "c" + ], + "tags": [ + "Framework.Development.Library.Boost" + ], "severity": "moderate", "patterns": [ { "pattern": "boost::", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" }, { "pattern": "#include ", "type": "regex", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] diff --git a/AppInspector/rules/default/frameworks/csharp-nonMS.json b/AppInspector/rules/default/frameworks/csharp-nonMS.json index db5d140..2b94e5c 100644 --- a/AppInspector/rules/default/frameworks/csharp-nonMS.json +++ b/AppInspector/rules/default/frameworks/csharp-nonMS.json @@ -3,15 +3,23 @@ "name": "Development Framework: Google API's", "id": "AI018100", "description": "Development Framework: Google API's", - "applies_to": [ "csharp" ], - "tags": [ "Framework.Development.Google.API" ], + "applies_to": [ + "csharp" + ], + "tags": [ + "Framework.Development.Google.API" + ], "severity": "moderate", "patterns": [ { "pattern": "using Google.Apis", "type": "regex", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] diff --git a/AppInspector/rules/default/frameworks/java.json b/AppInspector/rules/default/frameworks/java.json index bb8f5c4..041696a 100644 --- a/AppInspector/rules/default/frameworks/java.json +++ b/AppInspector/rules/default/frameworks/java.json @@ -3,15 +3,23 @@ "name": "Development Framework: Spring", "id": "AI018200", "description": "Development Framework: Spring", - "applies_to": [ "java" ], - "tags":[ "Framework.Development.Spring" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Development.Spring" + ], "severity": "moderate", "patterns": [ { "pattern": "springframework", "type": "string", - "scopes": [ "code" ], - "modifiers": ["i"], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -20,16 +28,24 @@ "name": "Development Framework: Hibernate", "id": "AI018300", "description": "Development Framework: Hibernate", - "applies_to": [ "java" ], - "tags":[ "Framework.Development.Hibernate" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Development.Hibernate" + ], "severity": "moderate", "patterns": [ { "pattern": "hibernate", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", - "modifiers": ["i"] + "modifiers": [ + "i" + ] } ] }, @@ -37,16 +53,24 @@ "name": "Development Framework: Blade", "id": "AI018400", "description": "Development Framework: Blade", - "applies_to": [ "java" ], - "tags":[ "Framework.Development.Blade" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Development.Blade" + ], "severity": "moderate", "patterns": [ { "pattern": "blade", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", - "modifiers": ["i"] + "modifiers": [ + "i" + ] } ] }, @@ -54,15 +78,23 @@ "name": "Development Framework: DropWizard", "id": "AI018500", "description": "Development Framework: DropWizard", - "applies_to": [ "java" ], - "tags":[ "Framework.Development.DropWizard" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Development.DropWizard" + ], "severity": "moderate", "patterns": [ { "pattern": "dropwizard", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -71,16 +103,24 @@ "name": "Development Framework: GWT", "id": "AI018600", "description": "Development Framework: GWT", - "applies_to": [ "java" ], - "tags":[ "Framework.Development.GWT" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Development.GWT" + ], "severity": "moderate", "patterns": [ { "pattern": "google.gwt", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", - "modifiers": ["i"] + "modifiers": [ + "i" + ] } ] }, @@ -88,16 +128,24 @@ "name": "Development Framework: JSF", "id": "AI018700", "description": "Development Framework: JSF", - "applies_to": [ "java" ], - "tags":[ "Framework.Development.JSF" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Development.JSF" + ], "severity": "moderate", "patterns": [ { "pattern": "http://xmlns.jcp.org/jsf/html", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", - "modifiers": ["i"] + "modifiers": [ + "i" + ] } ] }, @@ -105,15 +153,21 @@ "name": "Development Framework: JHipster", "id": "AI018800", "description": "Development Framework: JHipster", - "tags":[ "Framework.Development.JHipster" ], + "tags": [ + "Framework.Development.JHipster" + ], "severity": "moderate", "patterns": [ { "pattern": "jhipster", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", - "modifiers": ["i"] + "modifiers": [ + "i" + ] } ] }, @@ -121,16 +175,24 @@ "name": "Development Framework: MyBatis", "id": "AI018900", "description": "Development Framework: MyBatis", - "applies_to": [ "java" ], - "tags":[ "Framework.Development.MyBatis" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Development.MyBatis" + ], "severity": "moderate", "patterns": [ { "pattern": "mybatis|iobatis", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", - "modifiers": ["i"] + "modifiers": [ + "i" + ] } ] }, @@ -138,16 +200,24 @@ "name": "Development Framework: Play Framework", "id": "AI019000", "description": "Development Framework: Play Framework", - "applies_to": [ "java" ], - "tags":[ "Framework.Development.PlayFramework" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Development.PlayFramework" + ], "severity": "moderate", "patterns": [ { "pattern": "import play.", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", - "modifiers": ["i"] + "modifiers": [ + "i" + ] } ] }, @@ -155,16 +225,24 @@ "name": "Development Framework: PrimeFaces", "id": "AI019100", "description": "Development Framework: PrimeFaces", - "applies_to": [ "java" ], - "tags":[ "Framework.Development.PrimeFaces" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Development.PrimeFaces" + ], "severity": "moderate", "patterns": [ { "pattern": "primefaces", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", - "modifiers": ["i"] + "modifiers": [ + "i" + ] } ] }, @@ -172,16 +250,24 @@ "name": "Development Framework: Spark", "id": "AI019200", "description": "Development Framework: Spark", - "applies_to": [ "java" ], - "tags":[ "Framework.Development.Spark" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Development.Spark" + ], "severity": "moderate", "patterns": [ { "pattern": "import spark.", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", - "modifiers": ["i"] + "modifiers": [ + "i" + ] } ] }, @@ -189,16 +275,24 @@ "name": "Development Framework: Apache Struts", "id": "AI019300", "description": "Development Framework: Apache Struts", - "applies_to": [ "java" ], - "tags":[ "Framework.Development.ApacheStruts" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Development.ApacheStruts" + ], "severity": "moderate", "patterns": [ { "pattern": "struts", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", - "modifiers": ["i"] + "modifiers": [ + "i" + ] } ] }, @@ -206,16 +300,24 @@ "name": "Development Framework: Apache Tapestry", "id": "AI019400", "description": "Development Framework: Apache Tapestry", - "applies_to": [ "java" ], - "tags":[ "Framework.Development.ApacheTapestry" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Development.ApacheTapestry" + ], "severity": "moderate", "patterns": [ { "pattern": "tapestry", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", - "modifiers": ["i"] + "modifiers": [ + "i" + ] } ] }, @@ -223,16 +325,24 @@ "name": "Development Framework: Vaadin", "id": "AI019500", "description": "Development Framework: Vaadin", - "applies_to": [ "java" ], - "tags":[ "Framework.Development.Vaadin" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Development.Vaadin" + ], "severity": "moderate", "patterns": [ { "pattern": "vaadin", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", - "modifiers": ["i"] + "modifiers": [ + "i" + ] } ] }, @@ -240,17 +350,27 @@ "name": "Development Framework: Vertx", "id": "AI019600", "description": "Development Framework: Vertx", - "applies_to": [ "java" ], - "tags":[ "Framework.Development.Vertx" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Development.Vertx" + ], "severity": "moderate", "patterns": [ { "pattern": "import io.vertx", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high", - "modifiers": ["i"] + "modifiers": [ + "i" + ] } ] }, @@ -258,16 +378,24 @@ "name": "Development Framework: Wicket", "id": "AI019700", "description": "Development Framework: Wicket", - "applies_to": [ "java" ], - "tags":[ "Framework.Development.Wicket" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Development.Wicket" + ], "severity": "moderate", "patterns": [ { "pattern": "wicket", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", - "modifiers": ["i"] + "modifiers": [ + "i" + ] } ] }, @@ -275,16 +403,24 @@ "name": "Development Framework: Google API's", "id": "AI019800", "description": "Development Framework: Google API's", - "applies_to": [ "java" ], - "tags": [ "Framework.Development.Google.API" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Development.Google.API" + ], "severity": "moderate", "patterns": [ { "pattern": "googleapis", "type": "substring", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", - "modifiers": ["i"] + "modifiers": [ + "i" + ] } ] } diff --git a/AppInspector/rules/default/frameworks/javascript.json b/AppInspector/rules/default/frameworks/javascript.json index 6e4cc96..6ef46d3 100644 --- a/AppInspector/rules/default/frameworks/javascript.json +++ b/AppInspector/rules/default/frameworks/javascript.json @@ -3,15 +3,24 @@ "name": "Development Framework: Angular.JS", "id": "AI019900", "description": "Development Framework: Angular.JS", - "applies_to": [ "javascript", "typescript" ], - "tags": [ "Framework.Development.AngularJS" ], + "applies_to": [ + "javascript", + "typescript" + ], + "tags": [ + "Framework.Development.AngularJS" + ], "severity": "moderate", "patterns": [ { "pattern": "Angular", "type": "substring", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -20,15 +29,24 @@ "name": "Development Framework: Vue", "id": "AI020000", "description": "Development Framework: Vue", - "applies_to": [ "javascript", "typescript" ], - "tags": [ "Framework.Development.Vue" ], + "applies_to": [ + "javascript", + "typescript" + ], + "tags": [ + "Framework.Development.Vue" + ], "severity": "moderate", "patterns": [ { "pattern": "Vue", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -37,14 +55,21 @@ "name": "Development Framework: EmberJS", "id": "AI020100", "description": "Development Framework: EmberJS", - "applies_to": [ "javascript", "typescript" ], - "tags": [ "Framework.Development.EmberJS" ], + "applies_to": [ + "javascript", + "typescript" + ], + "tags": [ + "Framework.Development.EmberJS" + ], "severity": "moderate", "patterns": [ { "pattern": "ember", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -53,14 +78,21 @@ "name": "Development Framework: Backbone", "id": "AI020200", "description": "Development Framework: Backbone", - "applies_to": [ "javascript", "typescript" ], - "tags": [ "Framework.Development.Backbone" ], + "applies_to": [ + "javascript", + "typescript" + ], + "tags": [ + "Framework.Development.Backbone" + ], "severity": "moderate", "patterns": [ { "pattern": "backbone", "type": "string", - "scopes": [ "code" ] + "scopes": [ + "code" + ] } ] }, @@ -68,15 +100,24 @@ "name": "Development Framework: jQuery", "id": "AI020300", "description": "Development Framework: jQuery", - "applies_to": [ "javascript", "typescript" ], - "tags": [ "Framework.Development.jQuery" ], + "applies_to": [ + "javascript", + "typescript" + ], + "tags": [ + "Framework.Development.jQuery" + ], "severity": "moderate", "patterns": [ { "pattern": "jquery", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -85,14 +126,21 @@ "name": "Development Framework: Redux", "id": "AI020400", "description": "Development Framework: Redux", - "applies_to": [ "javascript", "typescript" ], - "tags": [ "Framework.Development.Redux" ], + "applies_to": [ + "javascript", + "typescript" + ], + "tags": [ + "Framework.Development.Redux" + ], "severity": "moderate", "patterns": [ { "pattern": "redux", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -101,14 +149,21 @@ "name": "Development Framework: Redux-Saga", "id": "AI020500", "description": "Development Framework: Redux-Saga", - "applies_to": [ "javascript", "typescript" ], - "tags": [ "Framework.Development.Redux-Saga" ], + "applies_to": [ + "javascript", + "typescript" + ], + "tags": [ + "Framework.Development.Redux-Saga" + ], "severity": "moderate", "patterns": [ { "pattern": "redux-saga", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -117,15 +172,24 @@ "name": "Development Framework: React", "id": "AI020600", "description": "Development Framework: React", - "applies_to": [ "javascript", "typescript" ], - "tags": [ "Framework.Development.React" ], + "applies_to": [ + "javascript", + "typescript" + ], + "tags": [ + "Framework.Development.React" + ], "severity": "moderate", "patterns": [ { "pattern": "react", "type": "string", - "scopes": [ "all" ], - "modifiers": [ "i" ], + "scopes": [ + "all" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -134,14 +198,21 @@ "name": "Development Framework: RITEWay", "id": "AI020700", "description": "Development Framework: RITEWay", - "applies_to": [ "javascript", "typescript" ], - "tags": [ "Framework.Development.RITEWay" ], + "applies_to": [ + "javascript", + "typescript" + ], + "tags": [ + "Framework.Development.RITEWay" + ], "severity": "moderate", "patterns": [ { "pattern": "riteway", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -150,14 +221,21 @@ "name": "Development Framework: Meteor", "id": "AI020800", "description": "Development Framework: Meteor", - "applies_to": [ "javascript", "typescript" ], - "tags": [ "Framework.Development.Meteor" ], + "applies_to": [ + "javascript", + "typescript" + ], + "tags": [ + "Framework.Development.Meteor" + ], "severity": "moderate", "patterns": [ { "pattern": "meteor", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -166,14 +244,21 @@ "name": "Development Framework: Mithril", "id": "AI020900", "description": "Development Framework: Mithril", - "applies_to": [ "javascript", "typescript" ], - "tags": [ "Framework.Development.Mithril" ], + "applies_to": [ + "javascript", + "typescript" + ], + "tags": [ + "Framework.Development.Mithril" + ], "severity": "moderate", "patterns": [ { "pattern": "mithril", "type": "string", - "scopes": [ "code" ] + "scopes": [ + "code" + ] } ] }, @@ -181,14 +266,21 @@ "name": "Development Framework: Polymer", "id": "AI021000", "description": "Development Framework: Polymer", - "applies_to": [ "javascript", "html" ], - "tags": [ "Framework.Development.Polymer" ], + "applies_to": [ + "javascript", + "html" + ], + "tags": [ + "Framework.Development.Polymer" + ], "severity": "moderate", "patterns": [ { "pattern": "polymer", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -197,14 +289,21 @@ "name": "Development Framework: Aurelia", "id": "AI021100", "description": "Development Framework: Aurelia", - "applies_to": [ "javascript", "typescript" ], - "tags": [ "Framework.Development.Aurelia" ], + "applies_to": [ + "javascript", + "typescript" + ], + "tags": [ + "Framework.Development.Aurelia" + ], "severity": "moderate", "patterns": [ { "pattern": "aurelia", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -213,15 +312,25 @@ "name": "Content Management Framework: Wordpress", "id": "AI021200", "description": "Development Framework: Wordpress", - "applies_to": [ "javascript", "html" ], - "tags": [ "Framework.CMS.Wordpress" ], + "applies_to": [ + "javascript", + "html" + ], + "tags": [ + "Framework.CMS.Wordpress" + ], "severity": "moderate", "patterns": [ { "pattern": "wordpress", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -230,15 +339,25 @@ "name": "Content Management Framework: Drupal", "id": "AI021300", "description": "Development Framework: Drupal", - "applies_to": [ "javascript", "html" ], - "tags": [ "Framework.CMS.Drupal" ], + "applies_to": [ + "javascript", + "html" + ], + "tags": [ + "Framework.CMS.Drupal" + ], "severity": "moderate", "patterns": [ { "pattern": "drupal", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -247,15 +366,25 @@ "name": "Content Management Framework: Sharepoint", "id": "AI021400", "description": "Development Framework: Sharepoint", - "applies_to": [ "javascript", "html" ], - "tags": [ "Framework.CMS.Sharepoint" ], + "applies_to": [ + "javascript", + "html" + ], + "tags": [ + "Framework.CMS.Sharepoint" + ], "severity": "moderate", "patterns": [ { "pattern": "sharepoint", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -264,15 +393,25 @@ "name": "Content Management Framework: Joomla", "id": "AI021500", "description": "Development Framework: Joomla", - "applies_to": [ "javascript", "html" ], - "tags": [ "Framework.CMS.Joomla" ], + "applies_to": [ + "javascript", + "html" + ], + "tags": [ + "Framework.CMS.Joomla" + ], "severity": "moderate", "patterns": [ { "pattern": "joomla", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -281,14 +420,21 @@ "name": "Development Framework: Google API's", "id": "AI021600", "description": "Development Framework: Google API's", - "applies_to": [ "javascript", "typescript" ], - "tags": [ "Framework.Development.Google.API" ], + "applies_to": [ + "javascript", + "typescript" + ], + "tags": [ + "Framework.Development.Google.API" + ], "severity": "moderate", "patterns": [ { "pattern": "apis\\.google\\.com/js/api\\.js", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -297,20 +443,29 @@ "name": "Development Framework: Node", "id": "AI021610", "description": "Development Framework: Node", - "applies_to": [ "javascript", "typescript" ], - "tags":[ "Application.Type.Web.Service" ], + "applies_to": [ + "javascript", + "typescript" + ], + "tags": [ + "Application.Type.Web.Service" + ], "severity": "moderate", "patterns": [ { "pattern": "app\\.listen|server\\.listen", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "\\.listen\\(", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "medium" } ] diff --git a/AppInspector/rules/default/frameworks/logging.json b/AppInspector/rules/default/frameworks/logging.json index c467110..8a90a6d 100644 --- a/AppInspector/rules/default/frameworks/logging.json +++ b/AppInspector/rules/default/frameworks/logging.json @@ -3,16 +3,24 @@ "name": "Framework: Logging (Log4J)", "id": "AI021700", "description": "Framework: Logging (Log4J)", - "applies_to":[ "java" ], - "tags":[ "Framework.Development.Logging.Log4j" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Development.Logging.Log4j" + ], "severity": "moderate", "patterns": [ { "pattern": "log4j", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", - "modifiers": ["i"] + "modifiers": [ + "i" + ] } ] }, @@ -20,16 +28,24 @@ "name": "Framework: Logging (NLog)", "id": "AI021800", "description": "Framework: Logging (NLog)", - "applies_to":[ "csharp" ], - "tags":[ "Framework.Development.Logging.NLog" ], + "applies_to": [ + "csharp" + ], + "tags": [ + "Framework.Development.Logging.NLog" + ], "severity": "moderate", "patterns": [ { "pattern": "nlog", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", - "modifiers": ["i"] + "modifiers": [ + "i" + ] } ] }, @@ -37,14 +53,20 @@ "name": "Framework: Logging (Serilog)", "id": "AI021900", "description": "Framework: Logging (Serilog)", - "applies_to":[ "csharp" ], - "tags":[ "Framework.Development.Logging.Serilog" ], + "applies_to": [ + "csharp" + ], + "tags": [ + "Framework.Development.Logging.Serilog" + ], "severity": "moderate", "patterns": [ { "pattern": "Serilog", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -53,16 +75,24 @@ "name": "Framework: Logging (log4net)", "id": "AI022000", "description": "Framework: Logging (log4net)", - "applies_to":[ "csharp" ], - "tags":[ "Framework.Development.Logging.Log4Net" ], + "applies_to": [ + "csharp" + ], + "tags": [ + "Framework.Development.Logging.Log4Net" + ], "severity": "moderate", "patterns": [ { "pattern": "log4net", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", - "modifiers": ["i"] + "modifiers": [ + "i" + ] } ] }, @@ -70,16 +100,24 @@ "name": "Framework: Logging (ulog)", "id": "AI022100", "description": "Framework: Logging (ulog)", - "applies_to":[ "csharp" ], - "tags":[ "Framework.Development.Logging.ULog" ], + "applies_to": [ + "csharp" + ], + "tags": [ + "Framework.Development.Logging.ULog" + ], "severity": "moderate", "patterns": [ { "pattern": "ulog", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", - "modifiers": ["i"] + "modifiers": [ + "i" + ] } ] }, @@ -87,16 +125,24 @@ "name": "Framework: Logging (Winston)", "id": "AI022200", "description": "Framework: Logging (Winston)", - "applies_to":[ "csharp" ], - "tags":[ "Framework.Development.Logging.Winston" ], + "applies_to": [ + "csharp" + ], + "tags": [ + "Framework.Development.Logging.Winston" + ], "severity": "moderate", "patterns": [ { "pattern": "winston", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "medium", - "modifiers": ["i"] + "modifiers": [ + "i" + ] } ] } diff --git a/AppInspector/rules/default/frameworks/microsoft.json b/AppInspector/rules/default/frameworks/microsoft.json index d99085c..7766239 100644 --- a/AppInspector/rules/default/frameworks/microsoft.json +++ b/AppInspector/rules/default/frameworks/microsoft.json @@ -3,15 +3,23 @@ "name": "Development Framework: .NET Core", "id": "AI022300", "description": "Development Framework: .NET Core", - "applies_to": [ "csharp" ], - "tags": [ "Framework.Development.Microsoft.NETCore" ], + "applies_to": [ + "csharp" + ], + "tags": [ + "Framework.Development.Microsoft.NETCore" + ], "severity": "moderate", "patterns": [ { "pattern": "netcore", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -20,14 +28,20 @@ "name": "Development Framework: .NET Core SDK", "id": "AI022400", "description": "Development Framework: .NET Core SDK", - "applies_to": [ "csharp" ], - "tags": [ "Framework.Development.Microsoft.NETCore.SDK" ], + "applies_to": [ + "csharp" + ], + "tags": [ + "Framework.Development.Microsoft.NETCore.SDK" + ], "severity": "moderate", "patterns": [ { "pattern": "Microsoft.NET.Sdk", "type": "string", - "scopes": [ "code" ] + "scopes": [ + "code" + ] } ] }, @@ -35,14 +49,20 @@ "name": "Development Framework: Mono", "id": "AI022500", "description": "Development Framework: Mono", - "applies_to": [ "csharp" ], - "tags": [ "Framework.Development.Microsoft.Mono" ], + "applies_to": [ + "csharp" + ], + "tags": [ + "Framework.Development.Microsoft.Mono" + ], "severity": "moderate", "patterns": [ { "pattern": "using (gtk|Glade)", "type": "regex", - "scopes": [ "code" ] + "scopes": [ + "code" + ] } ] }, @@ -50,7 +70,9 @@ "name": "Development Framework: Microsoft Azure", "id": "AI022600", "description": "Development Framework: Microsoft Azure", - "applies_to": [ "csharp" ], + "applies_to": [ + "csharp" + ], "tags": [ "Framework.Development.Microsoft.Azure", "Application.Type.Web.Application" @@ -60,7 +82,9 @@ { "pattern": "Microsoft.Azure.WebJobs.Host", "type": "string", - "scopes": [ "code" ] + "scopes": [ + "code" + ] } ] }, @@ -68,7 +92,9 @@ "name": "Development Framework: Microsoft WCF", "id": "AI022700", "description": "Development Framework: Microsoft WCF", - "applies_to": [ "csharp" ], + "applies_to": [ + "csharp" + ], "tags": [ "Framework.Development.Microsoft.WCF", "Application.Type.Web.Service" @@ -78,15 +104,23 @@ { "pattern": "ServiceContract|using System\\.ServiceModel|WSHttpBinding", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" }, { "pattern": "Controller\\b|\\[HttpGet\\]|\\[HttpPut\\]", "type": "regex", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -95,7 +129,11 @@ "name": "Development Framework: Microsoft MFC", "id": "AI022800", "description": "Development Framework: Microsoft MFC", - "applies_to": [ "c", "cpp", "csharp" ], + "applies_to": [ + "c", + "cpp", + "csharp" + ], "tags": [ "Framework.Development.Microsoft.MFC", "Application.Type.Client.Windows" @@ -105,7 +143,9 @@ { "pattern": "CFrameWnd|CMDIFrameWnd|CView|CWnd|stdafx\\.h|winafx\\.h|afx\\.h", "type": "regexword", - "scopes": [ "code" ] + "scopes": [ + "code" + ] } ] }, @@ -113,15 +153,24 @@ "name": "Development Framework: Microsoft ADO", "id": "AI022900", "description": "Development Framework: Microsoft ADO", - "applies_to": [ "csharp" ], - "tags": [ "Framework.Development.Microsoft.ADO", "Data.DBMS.SQL.ADO" ], + "applies_to": [ + "csharp" + ], + "tags": [ + "Framework.Development.Microsoft.ADO", + "Data.DBMS.SQL.ADO" + ], "severity": "moderate", "patterns": [ { "pattern": "System\\.Data\\.(SqlClient|OleDb|OracleClient|Odbc|EntityClient)", "type": "regex", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -130,15 +179,23 @@ "name": "Development Framework: Microsoft Linq", "id": "AI023000", "description": "Development Framework: Microsoft Linq", - "applies_to": [ "csharp" ], - "tags": [ "Framework.Development.Microsoft.Linq" ], + "applies_to": [ + "csharp" + ], + "tags": [ + "Framework.Development.Microsoft.Linq" + ], "severity": "moderate", "patterns": [ { "pattern": "linq", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -147,7 +204,11 @@ "name": "Development Framework: Windows SDK", "id": "AI023100", "description": "Development Framework: Windows SDK", - "applies_to": [ "c", "cpp", "csharp" ], + "applies_to": [ + "c", + "cpp", + "csharp" + ], "tags": [ "Framework.Development.Microsoft.WindowsSDK", "Application.Type.Client.Windows" @@ -157,8 +218,12 @@ { "pattern": "winsdk", "type": "regex", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -167,14 +232,21 @@ "name": "Development Framework: Microsoft Dynamics", "id": "AI023200", "description": "Development Framework: Microsoft Dynamics", - "tags":[ "Framework.Development.Microsoft.Server.Dynamics" ], + "tags": [ + "Framework.Development.Microsoft.Server.Dynamics" + ], "severity": "moderate", "patterns": [ { "pattern": "Microsoft Dynamics|dynamics.com|dynamic365", "type": "regexword", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -183,14 +255,20 @@ "name": "Development Framework: Microsoft Exchange", "id": "AI023300", "description": "Development Framework: Microsoft Exchange", - "tags":[ "Framework.Development.Microsoft.Server.Exchange" ], + "tags": [ + "Framework.Development.Microsoft.Server.Exchange" + ], "severity": "moderate", "patterns": [ { "pattern": "Microsoft Exchange", "type": "string", - "scopes": [ "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -208,7 +286,9 @@ { "pattern": "IIS", "type": "string", - "scopes": [ "code" ] + "scopes": [ + "code" + ] } ] }, @@ -225,8 +305,12 @@ { "pattern": "ASP\\.NET|\\b\\.MVC\\b", "type": "regex", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -249,7 +333,9 @@ { "pattern": "CLSCTX_INPROC_SERVER|CLSCTX_INPROC_HANDLER|CLSCTX_LOCAL_SERVER|CoCreateInstance", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] diff --git a/AppInspector/rules/default/frameworks/python.json b/AppInspector/rules/default/frameworks/python.json index fcc38d9..cc34f61 100644 --- a/AppInspector/rules/default/frameworks/python.json +++ b/AppInspector/rules/default/frameworks/python.json @@ -3,15 +3,23 @@ "name": "Development Framework: Django", "id": "AI023800", "description": "Development Framework: Django", - "applies_to": [ "python" ], - "tags":[ "Framework.Development.Django" ], + "applies_to": [ + "python" + ], + "tags": [ + "Framework.Development.Django" + ], "severity": "moderate", "patterns": [ { "pattern": "django", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -20,15 +28,23 @@ "name": "Development Framework: Pyramid", "id": "AI023900", "description": "Development Framework: Pyramid", - "applies_to": [ "python" ], - "tags": [ "Framework.Development.Pyramid" ], + "applies_to": [ + "python" + ], + "tags": [ + "Framework.Development.Pyramid" + ], "severity": "moderate", "patterns": [ { "pattern": "pyramid", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -37,15 +53,23 @@ "name": "Development Framework: Web2Py", "id": "AI024000", "description": "Development Framework: Web2Py", - "applies_to": [ "python" ], - "tags": [ "Framework.Development.Web2Py" ], + "applies_to": [ + "python" + ], + "tags": [ + "Framework.Development.Web2Py" + ], "severity": "moderate", "patterns": [ { "pattern": "web2py", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -54,15 +78,23 @@ "name": "Development Framework: CubicWeb", "id": "AI024100", "description": "Development Framework: CubicWeb", - "applies_to": [ "python" ], - "tags": [ "Framework.Development.CubicWeb" ], + "applies_to": [ + "python" + ], + "tags": [ + "Framework.Development.CubicWeb" + ], "severity": "moderate", "patterns": [ { "pattern": "cubicweb", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -71,15 +103,23 @@ "name": "Development Framework: OWL", "id": "AI024200", "description": "Development Framework: OWL", - "applies_to": [ "python" ], - "tags": [ "Framework.Development.OWL" ], + "applies_to": [ + "python" + ], + "tags": [ + "Framework.Development.OWL" + ], "severity": "moderate", "patterns": [ { "pattern": "owlready", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -88,15 +128,23 @@ "name": "Development Framework: RDF", "id": "AI024300", "description": "Development Framework: RDF", - "applies_to": [ "python" ], - "tags": [ "Framework.Development.RDF" ], + "applies_to": [ + "python" + ], + "tags": [ + "Framework.Development.RDF" + ], "severity": "moderate", "patterns": [ { "pattern": "rdflib", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -105,15 +153,23 @@ "name": "Development Framework: RQL", "id": "AI024400", "description": "Development Framework: RQL", - "applies_to": [ "python" ], - "tags": [ "Framework.Development.RQL" ], + "applies_to": [ + "python" + ], + "tags": [ + "Framework.Development.RQL" + ], "severity": "moderate", "patterns": [ { "pattern": "rql", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -122,15 +178,23 @@ "name": "Development Framework: Flask", "id": "AI024500", "description": "Development Framework: Flask", - "applies_to": [ "python" ], - "tags": [ "Framework.Development.Flask" ], + "applies_to": [ + "python" + ], + "tags": [ + "Framework.Development.Flask" + ], "severity": "moderate", "patterns": [ { "pattern": "flask", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -139,15 +203,23 @@ "name": "Development Framework: Bottle", "id": "AI024600", "description": "Development Framework: Bottle", - "applies_to": [ "python" ], - "tags": [ "Framework.Development.Bottle" ], + "applies_to": [ + "python" + ], + "tags": [ + "Framework.Development.Bottle" + ], "severity": "moderate", "patterns": [ { "pattern": "bottle", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -156,15 +228,23 @@ "name": "Development Framework: CherryPy", "id": "AI024700", "description": "Development Framework: CherryPy", - "applies_to": [ "python" ], - "tags": [ "Framework.Development.CherryPy" ], + "applies_to": [ + "python" + ], + "tags": [ + "Framework.Development.CherryPy" + ], "severity": "moderate", "patterns": [ { "pattern": "cherrypy", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -173,15 +253,23 @@ "name": "Development Framework: Sanic", "id": "AI024800", "description": "Development Framework: Sanic", - "applies_to": [ "python" ], - "tags": [ "Framework.Development.Sanic" ], + "applies_to": [ + "python" + ], + "tags": [ + "Framework.Development.Sanic" + ], "severity": "moderate", "patterns": [ { "pattern": "sanic", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -190,15 +278,23 @@ "name": "Development Framework: Tornado", "id": "AI024900", "description": "Development Framework: Tornado", - "applies_to": [ "python" ], - "tags": [ "Framework.Development.Tornado" ], + "applies_to": [ + "python" + ], + "tags": [ + "Framework.Development.Tornado" + ], "severity": "moderate", "patterns": [ { "pattern": "tornado", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -207,15 +303,23 @@ "name": "Development Framework: TurboGears", "id": "AI025000", "description": "Development Framework: TurboGears", - "applies_to":[ "python" ], - "tags": [ "Framework.Development.TurboGears" ], + "applies_to": [ + "python" + ], + "tags": [ + "Framework.Development.TurboGears" + ], "severity": "moderate", "patterns": [ { "pattern": "TGController|TurboGears2", "type": "regex", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -224,15 +328,23 @@ "name": "Development Framework: Giotto", "id": "AI025100", "description": "Development Framework: Giotto", - "applies_to":[ "python" ], - "tags": [ "Framework.Development.Giotto" ], + "applies_to": [ + "python" + ], + "tags": [ + "Framework.Development.Giotto" + ], "severity": "moderate", "patterns": [ { "pattern": "giotto", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -241,15 +353,23 @@ "name": "Development Framework: Pylons", "id": "AI025200", "description": "Development Framework: Pylons", - "applies_to":[ "python" ], - "tags": [ "Framework.Development.Pylons" ], + "applies_to": [ + "python" + ], + "tags": [ + "Framework.Development.Pylons" + ], "severity": "moderate", "patterns": [ { "pattern": "pylons", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -258,15 +378,23 @@ "name": "Development Framework: Google API's", "id": "AI025300", "description": "Development Framework: Google API's", - "applies_to": [ "python" ], - "tags": [ "Framework.Development.Google.API" ], + "applies_to": [ + "python" + ], + "tags": [ + "Framework.Development.Google.API" + ], "severity": "moderate", "patterns": [ { "pattern": "googleapis|googleapiclient", "type": "regex", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] diff --git a/AppInspector/rules/default/frameworks/ruby.json b/AppInspector/rules/default/frameworks/ruby.json index 6744be1..e109528 100644 --- a/AppInspector/rules/default/frameworks/ruby.json +++ b/AppInspector/rules/default/frameworks/ruby.json @@ -3,15 +3,23 @@ "name": "Development Framework: Grails", "id": "AI025400", "description": "Development Framework: Grails", - "applies_to": [ "groovy" ], - "tags": [ "Framework.Development.Grails" ], + "applies_to": [ + "groovy" + ], + "tags": [ + "Framework.Development.Grails" + ], "severity": "moderate", "patterns": [ { "pattern": "grails", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -20,15 +28,23 @@ "name": "Development Framework: Rails", "id": "AI025500", "description": "Development Framework: Rails", - "applies_to": [ "ruby" ], - "tags": [ "Framework.Development.Rails" ], + "applies_to": [ + "ruby" + ], + "tags": [ + "Framework.Development.Rails" + ], "severity": "moderate", "patterns": [ { "pattern": "rails", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -37,15 +53,23 @@ "name": "Development Framework: Google API's", "id": "AI025600", "description": "Development Framework: Google API's", - "applies_to": [ "ruby" ], - "tags": [ "Framework.Development.Google.API" ], + "applies_to": [ + "ruby" + ], + "tags": [ + "Framework.Development.Google.API" + ], "severity": "moderate", "patterns": [ { "pattern": "google/apis", "type": "substring", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] diff --git a/AppInspector/rules/default/frameworks/rust.json b/AppInspector/rules/default/frameworks/rust.json index 0f3f2fd..7343b43 100644 --- a/AppInspector/rules/default/frameworks/rust.json +++ b/AppInspector/rules/default/frameworks/rust.json @@ -3,14 +3,20 @@ "name": "Development Framework: Rocket", "id": "AI025610", "description": "Development Framework: Rocket", - "applies_to": [ "rust" ], - "tags": [ "Framework.Development.Rocket" ], + "applies_to": [ + "rust" + ], + "tags": [ + "Framework.Development.Rocket" + ], "severity": "moderate", "patterns": [ { "pattern": "extern crate rocket|use rocket", "type": "regex", - "scopes": [ "code" ] + "scopes": [ + "code" + ] } ] }, @@ -18,14 +24,20 @@ "name": "Development Framework: ActixWeb", "id": "AI025620", "description": "Development Framework: ActixWeb", - "applies_to": [ "rust" ], - "tags": [ "Framework.Development.ActixWeb" ], + "applies_to": [ + "rust" + ], + "tags": [ + "Framework.Development.ActixWeb" + ], "severity": "moderate", "patterns": [ { "pattern": "use actix_web", "type": "string", - "scopes": [ "code" ] + "scopes": [ + "code" + ] } ] }, @@ -33,14 +45,20 @@ "name": "Development Framework: Nickel", "id": "AI025630", "description": "Development Framework: Nickel", - "applies_to": [ "rust" ], - "tags": [ "Framework.Development.Nickel" ], + "applies_to": [ + "rust" + ], + "tags": [ + "Framework.Development.Nickel" + ], "severity": "moderate", "patterns": [ { "pattern": "use Nickel", "type": "string", - "scopes": [ "code" ] + "scopes": [ + "code" + ] } ] }, @@ -48,14 +66,20 @@ "name": "Development Framework: Yew", "id": "AI025640", "description": "Development Framework: Yew", - "applies_to": [ "rust" ], - "tags": [ "Framework.Development.Yew" ], + "applies_to": [ + "rust" + ], + "tags": [ + "Framework.Development.Yew" + ], "severity": "moderate", "patterns": [ { "pattern": "use yew", "type": "string", - "scopes": [ "code" ] + "scopes": [ + "code" + ] } ] }, @@ -63,14 +87,20 @@ "name": "Development Framework: Azul", "id": "AI025650", "description": "Development Framework: Azul", - "applies_to": [ "rust" ], - "tags": [ "Framework.Development.Azul" ], + "applies_to": [ + "rust" + ], + "tags": [ + "Framework.Development.Azul" + ], "severity": "moderate", "patterns": [ { "pattern": "use azul", "type": "string", - "scopes": [ "code" ] + "scopes": [ + "code" + ] } ] } diff --git a/AppInspector/rules/default/general/OSS_license.json b/AppInspector/rules/default/general/OSS_license.json index 39d09a2..fc07554 100644 --- a/AppInspector/rules/default/general/OSS_license.json +++ b/AppInspector/rules/default/general/OSS_license.json @@ -3,14 +3,21 @@ "name": "Open Source License: MIT", "id": "AI027600", "description": "Open Source License: MIT", - "tags":[ "Miscellaneous.OpenSourceLicense.MIT" ], + "tags": [ + "Miscellaneous.OpenSourceLicense.MIT" + ], "severity": "moderate", "patterns": [ { "pattern": "MIT License", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -19,14 +26,21 @@ "name": "Open Source License: Apache", "id": "AI027700", "description": "Open Source License: Apache", - "tags":[ "Miscellaneous.OpenSourceLicense.Apache" ], + "tags": [ + "Miscellaneous.OpenSourceLicense.Apache" + ], "severity": "moderate", "patterns": [ { "pattern": "Apache License", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -35,14 +49,21 @@ "name": "Open Source License: BSD", "id": "AI027800", "description": "Open Source License: BSD", - "tags":[ "Miscellaneous.OpenSourceLicense.BSD" ], + "tags": [ + "Miscellaneous.OpenSourceLicense.BSD" + ], "severity": "moderate", "patterns": [ { "pattern": "BSD.+License", "type": "regex", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -51,14 +72,21 @@ "name": "Open Source License: GPL", "id": "AI027900", "description": "Open Source License: GPL", - "tags":[ "Miscellaneous.OpenSourceLicense.GPL" ], + "tags": [ + "Miscellaneous.OpenSourceLicense.GPL" + ], "severity": "moderate", "patterns": [ { "pattern": "GNU GENERAL PUBLIC LICENSE", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -67,14 +95,21 @@ "name": "Open Source License: LGPL", "id": "AI028000", "description": "Open Source License: LGPL", - "tags":[ "Miscellaneous.OpenSourceLicense.LGPL" ], + "tags": [ + "Miscellaneous.OpenSourceLicense.LGPL" + ], "severity": "moderate", "patterns": [ { "pattern": "GNU LESSER GENERAL PUBLIC LICENSE", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -83,14 +118,21 @@ "name": "Open Source License: Mozilla", "id": "AI028100", "description": "Open Source License: Mozilla", - "tags":[ "Miscellaneous.OpenSourceLicense.Mozilla" ], + "tags": [ + "Miscellaneous.OpenSourceLicense.Mozilla" + ], "severity": "moderate", "patterns": [ { "pattern": "Mozilla Public License", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -99,14 +141,21 @@ "name": "Open Source License: Eclipse", "id": "AI028200", "description": "Open Source License: Eclipse", - "tags":[ "Miscellaneous.OpenSourceLicense.Eclipse" ], + "tags": [ + "Miscellaneous.OpenSourceLicense.Eclipse" + ], "severity": "moderate", "patterns": [ { "pattern": "Eclipse Public License", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -115,14 +164,21 @@ "name": "Open Source License: Creative Commons", "id": "AI028300", "description": "Open Source License: Creative Commons", - "tags":[ "Miscellaneous.OpenSourceLicense.CreativeCommons" ], + "tags": [ + "Miscellaneous.OpenSourceLicense.CreativeCommons" + ], "severity": "moderate", "patterns": [ { "pattern": "Creative Commons License", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -131,14 +187,21 @@ "name": "Open Source License: Microsoft", "id": "AI028400", "description": "Open Source License: Microsoft", - "tags":[ "Miscellaneous.OpenSourceLicense.Microsoft" ], + "tags": [ + "Miscellaneous.OpenSourceLicense.Microsoft" + ], "severity": "moderate", "patterns": [ { "pattern": "Microsoft Public License", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] diff --git a/AppInspector/rules/default/general/code_metrics.json b/AppInspector/rules/default/general/code_metrics.json index 70a3670..7fa4a01 100644 --- a/AppInspector/rules/default/general/code_metrics.json +++ b/AppInspector/rules/default/general/code_metrics.json @@ -3,14 +3,27 @@ "name": "Metric: Class Definition", "id": "AI025700", "description": "Metric: Classes Defined", - "tags":[ "Metric.Code.Class.Defined" ], + "tags": [ + "Metric.Code.Class.Defined" + ], "severity": "moderate", - "applies_to": [ "csharp", "cpp", "javascript", "python", "vb", "ruby", "groovy", "php" ], + "applies_to": [ + "csharp", + "cpp", + "javascript", + "python", + "vb", + "ruby", + "groovy", + "php" + ], "patterns": [ { "pattern": "class", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", "_comment": "data structure which provides complexity/size insight" } @@ -20,14 +33,24 @@ "name": "Metric: Struct Definition", "id": "AI025710", "description": "Metric: Struct Defined", - "tags":[ "Metric.Code.Class.Defined" ], + "tags": [ + "Metric.Code.Class.Defined" + ], "severity": "moderate", - "applies_to": [ "c", "cpp", "csharp", "objective-c", "rust" ], + "applies_to": [ + "c", + "cpp", + "csharp", + "objective-c", + "rust" + ], "patterns": [ { "pattern": "struct", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -36,13 +59,17 @@ "name": "Metric: Function Definition", "id": "AI025800", "description": "Metric: Function Defined", - "tags":[ "Metric.Code.Function.Defined" ], + "tags": [ + "Metric.Code.Function.Defined" + ], "severity": "moderate", "patterns": [ { "pattern": "(def|function|fun) (.*)", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "medium", "_comment": "doesn't detect a number of other potential patterns at present" } @@ -52,13 +79,17 @@ "name": "Metric: HTML Form Definition", "id": "AI025900", "description": "Metric: HTML Form Defined", - "tags":[ "Metric.Code.HTMLForm.Defined" ], + "tags": [ + "Metric.Code.HTMLForm.Defined" + ], "severity": "moderate", "patterns": [ { "pattern": "]", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -20,14 +27,20 @@ "name": "Dependency: Included Source", "id": "AI026500", "description": "Dependency: Included Source", - "tags":[ "Dependency.SourceInclude" ], - "applies_to": [ "php" ], + "tags": [ + "Dependency.SourceInclude" + ], + "applies_to": [ + "php" + ], "severity": "moderate", "patterns": [ { "pattern": "(include|require) [^\\s;]{1,50};", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -36,14 +49,20 @@ "name": "Dependency: Included Source", "id": "AI026600", "description": "Dependency: Included Source", - "tags":[ "Dependency.SourceInclude" ], - "applies_to": [ "objective-c" ], + "tags": [ + "Dependency.SourceInclude" + ], + "applies_to": [ + "objective-c" + ], "severity": "moderate", "patterns": [ { "pattern": "#import [A-Za-z]", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -52,31 +71,43 @@ "name": "Dependency: Included Source", "id": "AI026700", "description": "Dependency: Included Source", - "tags":[ "Dependency.SourceInclude" ], - "applies_to": [ "groovy" ], + "tags": [ + "Dependency.SourceInclude" + ], + "applies_to": [ + "groovy" + ], "severity": "moderate", "patterns": [ { "pattern": "import [A-Za-z]", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "loadScriptByName", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "evaluate ?\\(", "type": "regex", - "scopes": [ "code" ] + "scopes": [ + "code" + ] }, { "pattern": "shell\\.parse ?\\(?", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -85,20 +116,31 @@ "name": "Dependency: Included Source", "id": "AI026800", "description": "Dependency: Included Source", - "tags":[ "Dependency.SourceInclude" ], - "applies_to": [ "python", "java", "swift", "go" ], + "tags": [ + "Dependency.SourceInclude" + ], + "applies_to": [ + "python", + "java", + "swift", + "go" + ], "severity": "moderate", "patterns": [ { "pattern": "import [A-Za-z]", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "package [A-Za-z]", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -107,20 +149,29 @@ "name": "Dependency: Included Source", "id": "AI026900", "description": "Dependency: Included Source", - "tags":[ "Dependency.SourceInclude" ], - "applies_to": [ "perl", "rust"], + "tags": [ + "Dependency.SourceInclude" + ], + "applies_to": [ + "perl", + "rust" + ], "severity": "moderate", "patterns": [ { "pattern": "use [A-Za-z];", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "package [A-Za-z];", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -129,20 +180,28 @@ "name": "Dependency: Included Source", "id": "AI027000", "description": "Dependency: Included Source", - "tags":[ "Dependency.SourceInclude" ], - "applies_to": [ "ruby" ], + "tags": [ + "Dependency.SourceInclude" + ], + "applies_to": [ + "ruby" + ], "severity": "moderate", "patterns": [ { "pattern": "load [A-Za-z]", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "require [A-Za-z]", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -151,14 +210,20 @@ "name": "Dependency: Included Source", "id": "AI027100", "description": "Dependency: Included Source", - "tags":[ "Dependency.SourceInclude" ], - "applies_to": [ "csharp" ], + "tags": [ + "Dependency.SourceInclude" + ], + "applies_to": [ + "csharp" + ], "severity": "moderate", "patterns": [ { "pattern": "using [A-Za-z];", "type": "regex", - "scopes": [ "code" ] + "scopes": [ + "code" + ] } ] }, @@ -166,32 +231,45 @@ "name": "Dependency: Included Source", "id": "AI027200", "description": "Dependency: Included Source", - "tags":[ "Dependency.SourceInclude" ], - "applies_to": [ "javascript", "typescript" ], + "tags": [ + "Dependency.SourceInclude" + ], + "applies_to": [ + "javascript", + "typescript" + ], "severity": "moderate", "patterns": [ { "pattern": "import [A-Za-z];", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "fetchInject ?\\([A-Za-z]", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "require ?\\(['\\\"]([^'\\\"]+);", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "\\$\\.getScript\\([A-Za-z]", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] diff --git a/AppInspector/rules/default/general/hygiene.json b/AppInspector/rules/default/general/hygiene.json index baa76b3..57aef23 100644 --- a/AppInspector/rules/default/general/hygiene.json +++ b/AppInspector/rules/default/general/hygiene.json @@ -3,14 +3,20 @@ "name": "Hygiene: Todo Comment", "id": "AI027300", "description": "Hygiene: Todo Comment", - "tags":[ "Miscellaneous.CodeHygiene.Comment.Todo" ], + "tags": [ + "Miscellaneous.CodeHygiene.Comment.Todo" + ], "severity": "moderate", "patterns": [ { "pattern": "todo", "type": "regex", - "scopes": [ "comment" ], - "modifiers": [ "i" ] + "scopes": [ + "comment" + ], + "modifiers": [ + "i" + ] } ] }, @@ -18,14 +24,20 @@ "name": "Hygiene: Fix Comment", "id": "AI027400", "description": "Hygiene: Fix Comment", - "tags":[ "Miscellaneous.CodeHygiene.Comment.Fix" ], + "tags": [ + "Miscellaneous.CodeHygiene.Comment.Fix" + ], "severity": "moderate", "patterns": [ { "pattern": "fixme|broke|broken", "type": "regex", - "scopes": [ "comment" ], - "modifiers": [ "i" ] + "scopes": [ + "comment" + ], + "modifiers": [ + "i" + ] } ] }, @@ -33,14 +45,20 @@ "name": "Hygiene: Suspicious Comment", "id": "AI027500", "description": "Hygiene: Suspicious Comment", - "tags":[ "Miscellaneous.CodeHygiene.Comment.Suspicious" ], + "tags": [ + "Miscellaneous.CodeHygiene.Comment.Suspicious" + ], "severity": "moderate", "patterns": [ { "pattern": "hack|insecure|black magic|high risk|risky|riskiest|obfuscation|obfuscate|obfuscated", "type": "regex", - "scopes": [ "comment" ], - "modifiers": [ "i" ] + "scopes": [ + "comment" + ], + "modifiers": [ + "i" + ] } ] } diff --git a/AppInspector/rules/default/general/platforms.json b/AppInspector/rules/default/general/platforms.json index 98fb0ce..079d56b 100644 --- a/AppInspector/rules/default/general/platforms.json +++ b/AppInspector/rules/default/general/platforms.json @@ -3,15 +3,24 @@ "name": "Platform: Microsoft .NET Core", "id": "AI028500", "description": "Platform: .NET Core", - "applies_to": [ "VSProject", "csharp" ], - "tags": [ "Platform.Microsoft.NETCore" ], + "applies_to": [ + "VSProject", + "csharp" + ], + "tags": [ + "Platform.Microsoft.NETCore" + ], "severity": "moderate", "patterns": [ { "pattern": "netcore|net[5-9]", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ] + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ] } ] }, @@ -19,15 +28,24 @@ "name": "Platform: Microsoft .NET Standard", "id": "AI028600", "description": "Platform: Microsoft .NET Standard", - "applies_to": [ "VSProject", "csharp" ], - "tags": [ "Platform.Microsoft.NETStandard" ], + "applies_to": [ + "VSProject", + "csharp" + ], + "tags": [ + "Platform.Microsoft.NETStandard" + ], "severity": "moderate", "patterns": [ { "pattern": "Microsoft.NET.Sdk", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ] + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ] } ] }, @@ -35,15 +53,24 @@ "name": "Platform: Microsoft Mono", "id": "AI028700", "description": "Platform: Microsoft Mono", - "applies_to": [ "VSProject", "csharp" ], - "tags": [ "Platform.Microsoft.Mono" ], + "applies_to": [ + "VSProject", + "csharp" + ], + "tags": [ + "Platform.Microsoft.Mono" + ], "severity": "moderate", "patterns": [ { "pattern": "using (gtk|Glade)", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ] + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ] } ] }, @@ -51,14 +78,21 @@ "name": "Platform: Google Android", "id": "AI028800", "description": "Platform: Google Android", - "tags":[ "Platform.OS.Google.Android" ], + "tags": [ + "Platform.OS.Google.Android" + ], "severity": "moderate", "patterns": [ { "pattern": "android", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -67,14 +101,21 @@ "name": "Platform: Apple iOS", "id": "AI028900", "description": "Platform: Apple iOS", - "tags":[ "Platform.OS.Apple.iOS" ], + "tags": [ + "Platform.OS.Apple.iOS" + ], "severity": "moderate", "patterns": [ { "pattern": "AppleWebKit|UIKit|ios", "type": "regexword", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -83,14 +124,21 @@ "name": "Platform: Apple MacOS", "id": "AI029000", "description": "Platform: Apple MacOS", - "tags":[ "Platform.OS.Apple.MacOS" ], + "tags": [ + "Platform.OS.Apple.MacOS" + ], "severity": "moderate", "patterns": [ { "pattern": "(os x)|osx|macos|macosx", "type": "regexword", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -99,14 +147,21 @@ "name": "Platform: Microsoft Windows", "id": "AI029100", "description": "Platform: Microsoft Windows Server", - "tags":[ "Platform.OS.Microsoft.WindowsServer" ], + "tags": [ + "Platform.OS.Microsoft.WindowsServer" + ], "severity": "moderate", "patterns": [ { "pattern": "windows server", "type": "regex", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -115,31 +170,44 @@ "name": "Platform: Microsoft Windows", "id": "AI029200", "description": "Platform: Microsoft Windows", - "tags":[ "Platform.OS.Microsoft.WindowsStandard" ], + "tags": [ + "Platform.OS.Microsoft.WindowsStandard" + ], "severity": "moderate", "patterns": [ { "pattern": "windows", "type": "regex", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] }, - { "name": "Platform: Microsoft Windows", "id": "AI029300", "description": "Platform: Microsoft Windows", - "tags":[ "Platform.OS.Microsoft.WindowsCE" ], + "tags": [ + "Platform.OS.Microsoft.WindowsCE" + ], "severity": "moderate", "patterns": [ { "pattern": "PlatformID.WinCE|Windows Mobile|Windows Embedded|Windows Phone", "type": "regex", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -148,14 +216,20 @@ "name": "Development Framework: Windows Universal Platform", "id": "AI029400", "description": "Development Framework: Windows Universal Platform", - "tags":[ "Platform.OS.Microsoft.WindowsUniversalPlatform" ], + "tags": [ + "Platform.OS.Microsoft.WindowsUniversalPlatform" + ], "severity": "moderate", "patterns": [ { "pattern": "Windows universal platform|UWP", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ] + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ] } ] }, @@ -163,14 +237,21 @@ "name": "Platform: Linux", "id": "AI029500", "description": "Platform: Linux", - "tags":[ "Platform.OS.Linux.Distro" ], + "tags": [ + "Platform.OS.Linux.Distro" + ], "severity": "moderate", "patterns": [ { "pattern": "linux|xubuntu|ubuntu|coreos|manjaro|archlinux|debian|mint|gentoo|opensuse|redhat|mandrake|mandriva|slackware", "type": "regexword", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -179,14 +260,21 @@ "name": "Platform: Microsoft XBox", "id": "AI029600", "description": "Platform: Microsoft XBox", - "tags":[ "Platform.OS.Microsoft.XBox" ], + "tags": [ + "Platform.OS.Microsoft.XBox" + ], "severity": "moderate", "patterns": [ { "pattern": "xbox", "type": "regexword", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ], @@ -195,8 +283,12 @@ "pattern": { "pattern": "areapath", "type": "regex", - "scopes": [ "code" ], - "modifiers": [ "i" ] + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ] }, "search_in": "finding-region(-5,5)", "negate_finding": true @@ -207,14 +299,21 @@ "name": "Platform: Mobile Device", "id": "AI029700", "description": "Platform: Mobile Device", - "tags":[ "Platform.Device.Mobile" ], + "tags": [ + "Platform.Device.Mobile" + ], "severity": "moderate", "patterns": [ { "pattern": "mobile|tablet|iphone", "type": "regex", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ], @@ -223,8 +322,12 @@ "pattern": { "pattern": "address|(first|last|sur)name|contact|gender|email", "type": "regex", - "scopes": [ "code" ], - "modifiers": [ "i" ] + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ] }, "search_in": "finding-region(-7,7)", "negate_finding": true @@ -235,14 +338,21 @@ "name": "Platform: IOT", "id": "AI029800", "description": "Platform: IOT", - "tags":[ "Platform.Device.IOT" ], + "tags": [ + "Platform.Device.IOT" + ], "severity": "moderate", "patterns": [ { "pattern": "IOT", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high", "_comment": "driver?" } @@ -252,14 +362,21 @@ "name": "Hardware: Microsoft Kinex", "id": "AI029900", "description": "Platform: Microsoft Kinex", - "tags":[ "Hardware.Accessory.Microsoft.Kinex" ], + "tags": [ + "Hardware.Accessory.Microsoft.Kinex" + ], "severity": "moderate", "patterns": [ { "pattern": "kinex", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -268,14 +385,21 @@ "name": "Hardware: Reference Design", "id": "AI030000", "description": "Hardware: Reference Design", - "tags":[ "Hardware.ReferenceDesign" ], + "tags": [ + "Hardware.ReferenceDesign" + ], "severity": "moderate", "patterns": [ { "pattern": "Reference Design", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] diff --git a/AppInspector/rules/default/general/solutioninfo.json b/AppInspector/rules/default/general/solutioninfo.json index 4d7107b..8c327c9 100644 --- a/AppInspector/rules/default/general/solutioninfo.json +++ b/AppInspector/rules/default/general/solutioninfo.json @@ -3,15 +3,25 @@ "name": "Metadata: Target Processor", "id": "AI030100", "description": "Metadata: Target Processor", - "tags":[ "Metadata.Application.Target.Processor" ], - "applies_to": [ "VSsolution", "VSProject", "cpp" ], + "tags": [ + "Metadata.Application.Target.Processor" + ], + "applies_to": [ + "VSsolution", + "VSProject", + "cpp" + ], "severity": "moderate", "patterns": [ { "pattern": "Win32|Any CPU|Win64|x64|x86|Arm|Arm64", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -20,14 +30,20 @@ "name": "Metadata: Application Name", "id": "AI030200", "description": "Metadata: Application Name", - "tags":[ "Metadata.Application.Name" ], - "applies_to": [ "VSProject" ], + "tags": [ + "Metadata.Application.Name" + ], + "applies_to": [ + "VSProject" + ], "severity": "moderate", "patterns": [ { "pattern": ".*<\/Product(Name)?>|.*", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -36,14 +52,20 @@ "name": "Metadata: Application Author", "id": "AI030210", "description": "Metadata: Application Author", - "tags":[ "Metadata.Application.Author" ], - "applies_to": [ "VSProject" ], + "tags": [ + "Metadata.Application.Author" + ], + "applies_to": [ + "VSProject" + ], "severity": "moderate", "patterns": [ { "pattern": ".*<\/Authors>", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -52,30 +74,42 @@ "name": "Metadata: Application Version", "id": "AI030220", "description": "Metadata: Application Version", - "tags":[ "Metadata.Application.Version" ], - "applies_to": [ "VSProject" ], + "tags": [ + "Metadata.Application.Version" + ], + "applies_to": [ + "VSProject" + ], "severity": "moderate", "patterns": [ { "pattern": ".*<\/Version>", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] }, - { + { "name": "Metadata: Application Description", "id": "AI030230", "description": "Metadata: Application Description", - "tags":[ "Metadata.Application.Description" ], - "applies_to": [ "VSProject" ], + "tags": [ + "Metadata.Application.Description" + ], + "applies_to": [ + "VSProject" + ], "severity": "moderate", "patterns": [ { "pattern": ".*<\/Description>", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -84,20 +118,28 @@ "name": "Metadata: Build Output Type", "id": "AI030300", "description": "Metadata: Build Output Type", - "tags":[ "Metadata.Application.Output.Type" ], - "applies_to": [ "VSProject" ], + "tags": [ + "Metadata.Application.Output.Type" + ], + "applies_to": [ + "VSProject" + ], "severity": "moderate", "patterns": [ { "pattern": ".*<\/OutputType>", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": ".*<\/ConfigurationType>", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -106,15 +148,23 @@ "name": "Metadata: Application Type (Windows Forms)", "id": "AI030400", "description": "Metadata: Application Type (Windows Forms)", - "tags":[ "Application.Type.Client.Winforms" ], - "applies_to": [ "csharp" ], + "tags": [ + "Application.Type.Client.Winforms" + ], + "applies_to": [ + "csharp" + ], "severity": "moderate", "patterns": [ { "pattern": "winforms", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -123,14 +173,20 @@ "name": "Metadata: Application Type (Service)", "id": "AI030500", "description": "Metadata: Application Type (Service)", - "tags":[ "Application.Type.Client.Service.Windows" ], - "applies_to": [ "csharp" ], + "tags": [ + "Application.Type.Client.Service.Windows" + ], + "applies_to": [ + "csharp" + ], "severity": "moderate", "patterns": [ { "pattern": "ServiceStatus|ServiceController|InstallService", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -139,14 +195,22 @@ "name": "Metadata: Application Type (Service)", "id": "AI030600", "description": "Metadata: Application Type (Service)", - "tags":[ "Application.Type.Client.Service.Windows" ], - "applies_to": [ "c", "cpp", "shellscript" ], + "tags": [ + "Application.Type.Client.Service.Windows" + ], + "applies_to": [ + "c", + "cpp", + "shellscript" + ], "severity": "moderate", "patterns": [ { "pattern": "(/etc/rc\\.d/service|/etc/systemd/system)", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -155,14 +219,20 @@ "name": "Metadata: Application Type (Service)", "id": "AI030700", "description": "Metadata: Application Type (Service)", - "tags":[ "Application.Type.Client.Service.Windows" ], - "applies_to": [ "cpp" ], + "tags": [ + "Application.Type.Client.Service.Windows" + ], + "applies_to": [ + "cpp" + ], "severity": "moderate", "patterns": [ { "pattern": "RegisterServiceCtrlHandler|serviceCtrlHandler|StartServiceCtrlDispatcher|SERVICE_STATUS|SERVICE_RUNNING|SERVICE_WIN32|SERVICE_CONTROL", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -171,14 +241,22 @@ "name": "Metadata: Application Name", "id": "AI030800", "description": "Metadata: Application Name", - "tags":[ "Metadata.Application.Name" ], - "applies_to": [ "pom.xml", "build.gradle", "build.make.xml" ], + "tags": [ + "Metadata.Application.Name" + ], + "applies_to": [ + "pom.xml", + "build.gradle", + "build.make.xml" + ], "severity": "moderate", "patterns": [ { "pattern": "(.*)<\/artifactId>", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -187,14 +265,20 @@ "name": "Metadata: Application Description", "id": "AI030900", "description": "Metadata: Application Description", - "tags":[ "Metadata.Application.Description" ], - "applies_to": [ "package.json" ], + "tags": [ + "Metadata.Application.Description" + ], + "applies_to": [ + "package.json" + ], "severity": "moderate", "patterns": [ { "pattern": "\\\"description\\\"\\s*:\\s*\\\"([^\\\"])+\\\"", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -203,14 +287,20 @@ "name": "Metadata: Application Version", "id": "AI031000", "description": "Metadata: Application Version", - "tags":[ "Metadata.Application.Version" ], - "applies_to": [ "package.json" ], + "tags": [ + "Metadata.Application.Version" + ], + "applies_to": [ + "package.json" + ], "severity": "moderate", "patterns": [ { "pattern": "\\\"version\\\"\\s*:\\s*\\\"([^\\\"])+\\\"", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -219,14 +309,20 @@ "name": "Metadata: Application Author", "id": "AI031200", "description": "Metadata: Application Author", - "tags":[ "Metadata.Application.Author" ], - "applies_to": [ "package.json" ], + "tags": [ + "Metadata.Application.Author" + ], + "applies_to": [ + "package.json" + ], "severity": "moderate", "patterns": [ { "pattern": "\\\"author\\\"\\s*:\\s*\\\"([^\\\"])+\\\"", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -235,15 +331,21 @@ "name": "Metadata: Application Author", "id": "AI031300", "description": "Metadata: Application Author", - "tags":[ "Metadata.Application.Author" ], + "tags": [ + "Metadata.Application.Author" + ], "severity": "moderate", "patterns": [ { "pattern": "author[=: ](.*)", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", - "modifiers": [ "i" ] + "modifiers": [ + "i" + ] } ] }, @@ -251,14 +353,20 @@ "name": "Metadata: Application Publisher", "id": "AI031400", "description": "Metadata: Application Publisher", - "applies_to": [ "VSProject" ], - "tags":[ "Metadata.Application.Publisher" ], + "applies_to": [ + "VSProject" + ], + "tags": [ + "Metadata.Application.Publisher" + ], "severity": "moderate", "patterns": [ { "pattern": ".*<\/PublisherName>", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", "_comment": "use as additional author value" } @@ -268,13 +376,17 @@ "name": "Metadata: Target Framework", "id": "AI031500", "description": "Metadata: Target Framework", - "tags":[ "Application.Target.Framework" ], + "tags": [ + "Application.Target.Framework" + ], "severity": "moderate", "patterns": [ { "pattern": ".*<\/TargetFramework(s)?>", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -283,14 +395,20 @@ "name": "Metadata: Application Container (Kubernetes)", "id": "AI031510", "description": "Metadata: Application Container (Kubernetes)", - "tags":[ "Application.Container.Kubernetes" ], + "tags": [ + "Application.Container.Kubernetes" + ], "severity": "moderate", "patterns": [ { "pattern": "Kubernetes", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -299,14 +417,20 @@ "name": "Metadata: Application Container (Docker)", "id": "AI031520", "description": "Metadata: Application Container (Docker)", - "tags":[ "Application.Container.Docker" ], + "tags": [ + "Application.Container.Docker" + ], "severity": "moderate", "patterns": [ { "pattern": "Docker|RancherOS", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -315,14 +439,20 @@ "name": "Metadata: Application Container (rktlet)", "id": "AI031530", "description": "Metadata: Application Container (rktlet)", - "tags":[ "Application.Container.rktlet" ], + "tags": [ + "Application.Container.rktlet" + ], "severity": "moderate", "patterns": [ { "pattern": "rktlet", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -331,14 +461,20 @@ "name": "Metadata: Application Container (CRI-O)", "id": "AI031540", "description": "Metadata: Application Container (CRI-O)", - "tags":[ "Application.Container.CRI-O" ], + "tags": [ + "Application.Container.CRI-O" + ], "severity": "moderate", "patterns": [ { "pattern": "CRI-O", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -347,14 +483,20 @@ "name": "Metadata: Application Container (Microsoft Hyper-V)", "id": "AI031550", "description": "Metadata: Application Container (Microsoft Hyper-V)", - "tags":[ "Application.Container.Microsoft.Hyper-V" ], + "tags": [ + "Application.Container.Microsoft.Hyper-V" + ], "severity": "moderate", "patterns": [ { "pattern": "hyper-v", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -363,14 +505,20 @@ "name": "Metadata: Application Container (Microsoft Server)", "id": "AI031560", "description": "Metadata: Application Container (Microsoft Server)", - "tags":[ "Application.Container.Microsoft.Core" ], + "tags": [ + "Application.Container.Microsoft.Core" + ], "severity": "moderate", "patterns": [ { "pattern": "Nano Server|Server Core", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] diff --git a/AppInspector/rules/default/infrastructure/hashicorp_packers_tmpl.json b/AppInspector/rules/default/infrastructure/hashicorp_packers_tmpl.json index 66290a0..b4d77e0 100644 --- a/AppInspector/rules/default/infrastructure/hashicorp_packers_tmpl.json +++ b/AppInspector/rules/default/infrastructure/hashicorp_packers_tmpl.json @@ -3,14 +3,20 @@ "name": "Infrastructure As Code: Packers (Hashicorp)", "id": "AI031565", "description": "Infrastructure As Code: Packers (Hashicorp)", - "applies_to":[ "json" ], - "tags":[ "Infrastructure.Packers.Hashicorp" ], + "applies_to": [ + "json" + ], + "tags": [ + "Infrastructure.Packers.Hashicorp" + ], "severity": "moderate", "patterns": [ { "pattern": "builders\": \\[", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "medium" } ] diff --git a/AppInspector/rules/default/infrastructure/hashicorp_terraform_tmpl.json b/AppInspector/rules/default/infrastructure/hashicorp_terraform_tmpl.json index 2608911..7976263 100644 --- a/AppInspector/rules/default/infrastructure/hashicorp_terraform_tmpl.json +++ b/AppInspector/rules/default/infrastructure/hashicorp_terraform_tmpl.json @@ -3,15 +3,23 @@ "name": "Infrastructure As Code: Terraform (Hashicorp)", "id": "AI031566", "description": "Infrastructure As Code: Terraform (Hashicorp)", - "applies_to":[ "terraform" ], - "tags":[ "Infrastructure.Terraform.Hashicorp" ], + "applies_to": [ + "terraform" + ], + "tags": [ + "Infrastructure.Terraform.Hashicorp" + ], "severity": "moderate", "patterns": [ { "pattern": "terraform", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] diff --git a/AppInspector/rules/default/infrastructure/microsoft_arm_tmpl.json b/AppInspector/rules/default/infrastructure/microsoft_arm_tmpl.json index 0fb5989..4b203fb 100644 --- a/AppInspector/rules/default/infrastructure/microsoft_arm_tmpl.json +++ b/AppInspector/rules/default/infrastructure/microsoft_arm_tmpl.json @@ -3,26 +3,36 @@ "name": "Infrastructure As Code: Deployment (Microsoft ARM)", "id": "AI031567", "description": "Infrastructure As Code: Deployment (Microsoft ARM)", - "applies_to":[ "csharp" ], - "tags":[ "Infrastructure.ARM.Deployment" ], + "applies_to": [ + "csharp" + ], + "tags": [ + "Infrastructure.ARM.Deployment" + ], "severity": "moderate", "patterns": [ { "pattern": "DeploymentOperationsExtensions|DeploymentScriptsClient|IDeploymentOperations|IDeploymentScriptsClient", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "https://schema\\.management\\.azure\\.com/schemas/.*deploymentTemplate\\.json", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "az deployment create", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -31,20 +41,28 @@ "name": "Infrastructure As Code: Tag AddUpdate (Microsoft ARM)", "id": "AI031568", "description": "Infrastructure As Code: Tag AddUpdate (Microsoft ARM)", - "applies_to":[ "powershell" ], - "tags":[ "Infrastructure.ARM.TagAddUpdate" ], + "applies_to": [ + "powershell" + ], + "tags": [ + "Infrastructure.ARM.TagAddUpdate" + ], "severity": "moderate", "patterns": [ { "pattern": "(New|Update|Remove)-AzTag", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "az tag create", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -53,20 +71,28 @@ "name": "Infrastructure As Code: ResourceGroupAddUpdate (Microsoft ARM)", "id": "AI031569", "description": "Infrastructure As Code: ResourceGroupAddUpdate (Microsoft ARM)", - "applies_to":[ "powershell" ], - "tags":[ "Infrastructure.ARM.ResourceGroupAddUpdate" ], + "applies_to": [ + "powershell" + ], + "tags": [ + "Infrastructure.ARM.ResourceGroupAddUpdate" + ], "severity": "moderate", "patterns": [ { "pattern": "(New|Update|Remove)-AzResourceGroupDeployment", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "az group create", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -75,20 +101,28 @@ "name": "Infrastructure As Code: SubscriptionAddUpdate (Microsoft ARM)", "id": "AI031570", "description": "Infrastructure As Code: SubscriptionAddUpdate (Microsoft ARM)", - "applies_to":[ "powershell" ], - "tags":[ "Infrastructure.ARM.SubscriptionAddUpdate" ], + "applies_to": [ + "powershell" + ], + "tags": [ + "Infrastructure.ARM.SubscriptionAddUpdate" + ], "severity": "moderate", "patterns": [ { "pattern": "(New|Update|Remove)-AzSubscriptionDeployment", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "az subscription create", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -97,20 +131,28 @@ "name": "Infrastructure As Code: TemplateSpecUpdate (Microsoft ARM)", "id": "AI031571", "description": "Infrastructure As Code: TemplateSpecUpdate (Microsoft ARM)", - "applies_to":[ "powershell" ], - "tags":[ "Infrastructure.ARM.TemplateSpecAddUpdate" ], + "applies_to": [ + "powershell" + ], + "tags": [ + "Infrastructure.ARM.TemplateSpecAddUpdate" + ], "severity": "moderate", "patterns": [ { "pattern": "(New|Update|Remove)-AzTemplateSpec", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "az ts create", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -121,25 +163,33 @@ "description": "Infrastructure As Code: API Based (Microsoft ARM)", "applies_to": [ ], - "tags":[ "Infrastructure.ARM.API.General" ], + "tags": [ + "Infrastructure.ARM.API.General" + ], "severity": "moderate", "patterns": [ { "pattern": "https:\/\/management\\.azure\\.com\/subscriptions\/.*\/resourcegroups\/.*\/providers\/Microsoft\\.Resources\/deployments", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "https:\/\/management\\.azure\\.com\/subscriptions/.*\/providers\/Microsoft\\.Resources\/deployments", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "https:\/\/management\\.azure\\.com\/providers\/Microsoft\\.Management\/managementGroups\/providers\/Microsoft\\.Resources\/deployments", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -148,14 +198,20 @@ "name": "Infrastructure As Code: Deployment (Microsoft ARM)", "id": "AI031573", "description": "Infrastructure As Code: Deployment (Microsoft ARM)", - "applies_to":[ "java" ], - "tags":[ "Infrastructure.ARM.Deployment" ], + "applies_to": [ + "java" + ], + "tags": [ + "Infrastructure.ARM.Deployment" + ], "severity": "moderate", "patterns": [ { "pattern": "Deployment\\.Execution", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "medium", "_comment": "add conditions to raise confidence level" } @@ -165,14 +221,20 @@ "name": "Infrastructure As Code: General (Microsoft ARM)", "id": "AI031574", "description": "Infrastructure As Code: General (Microsoft ARM)", - "applies_to":[ "java" ], - "tags":[ "Infrastructure.ARM.General" ], + "applies_to": [ + "java" + ], + "tags": [ + "Infrastructure.ARM.General" + ], "severity": "moderate", "patterns": [ { "pattern": "com\\.microsoft\\.azure\\.management\\.resources", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "medium", "_comment": "add conditions to raise confidence level" } @@ -182,14 +244,20 @@ "name": "Infrastructure As Code: Deployment General (Microsoft ARM)", "id": "AI031575", "description": "Infrastructure As Code: (Microsoft ARM)", - "applies_to":[ "python" ], - "tags":[ "Infrastructure.ARM.General" ], + "applies_to": [ + "python" + ], + "tags": [ + "Infrastructure.ARM.General" + ], "severity": "moderate", "patterns": [ { "pattern": "from azure\\.mgmt\\.resource import ResourceManagementClient", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "medium", "_comment": "add conditions to raise confidence level" } diff --git a/AppInspector/rules/default/networkcomms/outbound_network.json b/AppInspector/rules/default/networkcomms/outbound_network.json index 37c0ba0..4bb185f 100644 --- a/AppInspector/rules/default/networkcomms/outbound_network.json +++ b/AppInspector/rules/default/networkcomms/outbound_network.json @@ -5,14 +5,20 @@ "id": "AI031600", "applies_to": [ ], - "tags":[ "OS.Network.Connection.Miscellaneous" ], + "tags": [ + "OS.Network.Connection.Miscellaneous" + ], "severity": "moderate", "patterns": [ { "pattern": "tftp|ntp\\.org|ntpupdate|imap|snmp|ftps|sftp|ftp|nntp|smtp|telnet|ssh|pop3|gopher", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high", "_comment": "don't include auth api's patterns to avoid duplicate reporting" } @@ -22,26 +28,36 @@ "name": "Network Connection: TCP", "description": "Network Connection TCP", "id": "AI031700", - "applies_to":[ "csharp" ], - "tags":[ "OS.Network.Connection.Tcp" ], + "applies_to": [ + "csharp" + ], + "tags": [ + "OS.Network.Connection.Tcp" + ], "severity": "moderate", "patterns": [ { "pattern": ".Listen(", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "TcpConnection", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "(Anonymous|Named)Pipe(Client|Server)Stream", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -54,13 +70,17 @@ "c", "cpp" ], - "tags":[ "OS.Network.Connection.Tcp" ], + "tags": [ + "OS.Network.Connection.Tcp" + ], "severity": "moderate", "patterns": [ { "pattern": "QTcpServer", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", "_comment": "Qt TCP Server" } @@ -74,27 +94,35 @@ "c", "cpp" ], - "tags":[ "OS.Network.Connection.Tcp" ], + "tags": [ + "OS.Network.Connection.Tcp" + ], "severity": "moderate", "patterns": [ { "pattern": "QTcpServer", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", "_comment": "Qt TCP Server" }, { "pattern": "tcp::acceptor", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", "_comment": "Boost::AsynchIO" }, { "pattern": "listen(", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", "_comment": "Standard C Library" } @@ -104,31 +132,42 @@ "name": "Network Connection: TCP", "description": "Network Connection", "id": "AI031910", - "applies_to":[ "rust" ], - "tags":[ "OS.Network.Connection.Http" ], + "applies_to": [ + "rust" + ], + "tags": [ + "OS.Network.Connection.Http" + ], "severity": "moderate", "patterns": [ { "pattern": "TcpStream::connect|HttpConnection", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] }, - { "name": "Network Connection: Socket", "description": "Network Connection Socket", "id": "AI032000", - "applies_to":[ "python" ], - "tags":[ "OS.Network.Connection.Socket" ], + "applies_to": [ + "python" + ], + "tags": [ + "OS.Network.Connection.Socket" + ], "severity": "moderate", "patterns": [ { "pattern": ".bind(", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", "_comment": "Python socket" } @@ -138,14 +177,20 @@ "name": "Network Connection: Socket", "description": "Network Socket", "id": "AI032100", - "tags":[ "OS.Network.Connection.Socket" ], + "tags": [ + "OS.Network.Connection.Socket" + ], "severity": "moderate", "patterns": [ { "pattern": "socket", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high", "_comment": "general" } @@ -155,14 +200,20 @@ "name": "Network Connection: Socket", "description": "Network Socket", "id": "AI032110", - "tags":[ "OS.Network.Connection.Socket" ], + "tags": [ + "OS.Network.Connection.Socket" + ], "severity": "moderate", "patterns": [ { "pattern": "connect|port", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "medium", "_comment": "general" } @@ -172,14 +223,20 @@ "name": "Network Connection: HTTP", "description": "Network Connection", "id": "AI032200", - "applies_to":[ "csharp" ], - "tags":[ "OS.Network.Connection.Http" ], + "applies_to": [ + "csharp" + ], + "tags": [ + "OS.Network.Connection.Http" + ], "severity": "moderate", "patterns": [ { "pattern": "WebClient|HttpClient|HttpRequest|HttpWebRequest|HttpWebResponse|System\\.Net\\.Http|Microsoft\\.AspNetCore\\.Http", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -192,28 +249,38 @@ "javascript", "html" ], - "tags":[ "OS.Network.Connection.Http.Ajax" ], + "tags": [ + "OS.Network.Connection.Http.Ajax" + ], "severity": "moderate", "patterns": [ { "pattern": "(\\$|jQuery)\\.(post|get|getJSON|getScript)\\(", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", "_comment": "jQuery AJAX" }, { "pattern": "fetch(", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", "_comment": "Fetch API" }, { "pattern": "XMLHttpRequest", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -222,14 +289,20 @@ "name": "Network Connection: HTTP", "description": "Network Connection: HTTP", "id": "AI032400", - "applies_to":[ "javascript" ], - "tags":[ "OS.Network.Connection.Http" ], + "applies_to": [ + "javascript" + ], + "tags": [ + "OS.Network.Connection.Http" + ], "severity": "moderate", "patterns": [ { "pattern": "require\\(['\\\"](http|https|request|axios|superagent|got)", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", "_comment": "Common Node.js HTTP libraries" } @@ -239,14 +312,20 @@ "name": "Network Connection: HTTP", "description": "Network Connection: HTTP", "id": "AI032500", - "applies_to":[ "python" ], - "tags":[ "OS.Network.Connection.Http" ], + "applies_to": [ + "python" + ], + "tags": [ + "OS.Network.Connection.Http" + ], "severity": "moderate", "patterns": [ { "pattern": "import .*(http|requests)", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", "_comment": "Common Python HTTP libraries" } @@ -258,20 +337,26 @@ "id": "AI032600", "applies_to": [ ], - "tags":[ "OS.Network.Connection.Http" ], + "tags": [ + "OS.Network.Connection.Http" + ], "severity": "moderate", "patterns": [ { "pattern": "https?:\/", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "low", "_comment": "HTTP URL with or without TLS" }, { "pattern": "curl|wpget", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "medium" } ] @@ -280,14 +365,20 @@ "name": "Network Connection: HTTP", "description": "Network Connection: HTTP", "id": "AI032700", - "applies_to": [ "ruby" ], - "tags":[ "OS.Network.Connection.Http" ], + "applies_to": [ + "ruby" + ], + "tags": [ + "OS.Network.Connection.Http" + ], "severity": "moderate", "patterns": [ { "pattern": "Net::HTTP", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -296,14 +387,20 @@ "name": "Network Connection: RPC", "description": "Network Connection: RPC", "id": "AI032800", - "applies_to": [ "csharp" ], - "tags":[ "OS.Network.Connection.RPC" ], + "applies_to": [ + "csharp" + ], + "tags": [ + "OS.Network.Connection.RPC" + ], "severity": "moderate", "patterns": [ { "pattern": "RPC|RemotableTypeclass|System\\.Runtime\\.Remoting|RemotingConfiguration|TcpClient|TcpChannel", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -312,14 +409,20 @@ "name": "Network Connection: RPC", "description": "Network Connection: RPC", "id": "AI032900", - "applies_to": [ "python" ], - "tags":[ "OS.Network.Connection.RPC" ], + "applies_to": [ + "python" + ], + "tags": [ + "OS.Network.Connection.RPC" + ], "severity": "moderate", "patterns": [ { "pattern": "fastrpc|xmlrpc|SimpleXMLRPCServer|jsonrpc|rpc\\.server|client\\.rpc", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -328,14 +431,20 @@ "name": "Network Connection: RPC", "description": "Network Connection: RPC", "id": "AI033000", - "applies_to": [ "c" ], - "tags":[ "OS.Network.Connection.RPC" ], + "applies_to": [ + "c" + ], + "tags": [ + "OS.Network.Connection.RPC" + ], "severity": "moderate", "patterns": [ { "pattern": "capnp/ez-rpc\\.h|rpc/server\\.h", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -344,14 +453,20 @@ "name": "Network Connection: RPC", "description": "Network Connection: RPC", "id": "AI033100", - "applies_to": [ "java" ], - "tags":[ "OS.Network.Connection.RPC" ], + "applies_to": [ + "java" + ], + "tags": [ + "OS.Network.Connection.RPC" + ], "severity": "moderate", "patterns": [ { "pattern": "RmiServiceExporter|RmiProxyFactoryBean|remoting|jaxrpc|RemoteAccountService|io\\.grpc|RPCClient", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -360,14 +475,21 @@ "name": "Network Connection: RPC", "description": "Network Connection: RPC", "id": "AI033200", - "applies_to": [ "javascript", "typescript" ], - "tags":[ "OS.Network.Connection.RPC" ], + "applies_to": [ + "javascript", + "typescript" + ], + "tags": [ + "OS.Network.Connection.RPC" + ], "severity": "moderate", "patterns": [ { "pattern": "RpcProvider|jayson|json-rpc", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -376,14 +498,22 @@ "name": "Network Connection: Cellular", "description": "Network Connection: Cellular", "id": "AI033300", - "applies_to": [ "c", "cpp", "csharp" ], - "tags":[ "OS.Network.Connection.Cellular" ], + "applies_to": [ + "c", + "cpp", + "csharp" + ], + "tags": [ + "OS.Network.Connection.Cellular" + ], "severity": "moderate", "patterns": [ { "pattern": "NetworkInterfaceType\\.(Wman|Wwanpp|Wwanpp2)", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", "_comment": "https://docs.microsoft.com/en-us/dotnet/api/system.net.networkinformation.networkinterfacetype?view=net-5.0" } @@ -393,14 +523,23 @@ "name": "Network Connection: Modem", "description": "Network Connection: Modem", "id": "AI033400", - "applies_to": [ "c", "cpp", "csharp", "java" ], - "tags":[ "OS.Network.Connection.Modem" ], + "applies_to": [ + "c", + "cpp", + "csharp", + "java" + ], + "tags": [ + "OS.Network.Connection.Modem" + ], "severity": "moderate", "patterns": [ { "pattern": "modem", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -409,19 +548,25 @@ "name": "Network Connection: General", "description": "Network Connection: General", "id": "AI033410", - "tags":[ "OS.Network.Connection.General" ], + "tags": [ + "OS.Network.Connection.General" + ], "severity": "moderate", "patterns": [ { "pattern": "send.*message", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "low" }, { "pattern": "send\\(", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "medium" } ] diff --git a/AppInspector/rules/default/os/acl.json b/AppInspector/rules/default/os/acl.json index 29f10f3..e617c5e 100644 --- a/AppInspector/rules/default/os/acl.json +++ b/AppInspector/rules/default/os/acl.json @@ -3,14 +3,20 @@ "name": "OS Operation: ACL Write (FileSecurity)", "id": "AI033500", "description": "OS Operation: ACL", - "applies_to":[ "csharp" ], - "tags":[ "OS.ACL.Write.FileSecurity" ], + "applies_to": [ + "csharp" + ], + "tags": [ + "OS.ACL.Write.FileSecurity" + ], "severity": "moderate", "patterns": [ { "pattern": "FileSecurity|DirectorySecurity", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ], @@ -19,8 +25,12 @@ "pattern": { "pattern": "SetAccessControl|RemoveAccessRule", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ] + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ] }, "search_in": "finding-region(-5,5)", "negate_finding": false @@ -31,14 +41,20 @@ "name": "OS Operation: ACL Write (General)", "id": "AI033600", "description": "OS Operation: ACL (Write)", - "applies_to":[ "java" ], - "tags":[ "OS.ACL.Write" ], + "applies_to": [ + "java" + ], + "tags": [ + "OS.ACL.Write" + ], "severity": "moderate", "patterns": [ { "pattern": "acl.(remove|add)", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -52,19 +68,25 @@ "cpp", "" ], - "tags":[ "OS.ACL.Write.General" ], + "tags": [ + "OS.ACL.Write.General" + ], "severity": "moderate", "patterns": [ { "pattern": "SetNamedSecurityInfo|SetEntriesInAcl|AddAccessAllowed|AceAddAccessAllowedObjectAce|AddAce|SetFileSecurity|DeleteAce", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "AuthzModifyClaims|AuthzModifySids|BuildExplicitAccessWithName|SetUserObjectSecurity|DestroyPrivateObjectSecurity|SetKernelObjectSecurity|SetSecurityDescriptorSacl|SetSecurityDescriptorOwner|SetSecurityDescriptorGroup|SetPrivateObjectSecurity|SetNamedSecurityInfo|SetServiceObjectSecurity", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -77,19 +99,25 @@ "c", "cpp" ], - "tags":[ "OS.ACL.Write.Audit" ], + "tags": [ + "OS.ACL.Write.Audit" + ], "severity": "moderate", "patterns": [ { "pattern": "AuditSetPerUserPolicy|AuditSetSecurity", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "DestroyPrivateObjectSecurity|SetKernelObjectSecurity|SetSecurityDescriptorSacl|SetSecurityDescriptorOwner|SetSecurityDescriptorGroup|SetPrivateObjectSecurity|SetNamedSecurityInfo|SetServiceObjectSecurity", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -98,15 +126,21 @@ "name": "OS Operation: ACL (Impersonation)", "id": "AI033900", "description": "OS Operation: ACL (Impersonation)", - "tags":[ "OS.ACL.Impersonation" ], + "tags": [ + "OS.ACL.Impersonation" + ], "severity": "moderate", "patterns": [ { "pattern": "impersonate", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "medium", - "modifiers": [ "i" ] + "modifiers": [ + "i" + ] } ] }, @@ -118,13 +152,17 @@ "c", "cpp" ], - "tags":[ "OS.ACL.TokenImpersonation" ], + "tags": [ + "OS.ACL.TokenImpersonation" + ], "severity": "moderate", "patterns": [ { "pattern": "ImpersonateAnonymousToken|ImpersonateLoggedOnUser", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -133,14 +171,20 @@ "name": "OS Operation: ACL (Impersonation)", "id": "AI034100", "description": "OS Operation: ACL (Impersonation)", - "applies_to":[ "csharp" ], - "tags":[ "OS.ACL.TokenImpersonation" ], + "applies_to": [ + "csharp" + ], + "tags": [ + "OS.ACL.TokenImpersonation" + ], "severity": "moderate", "patterns": [ { "pattern": "WindowsImpersonationContext|WindowsIdentity\\.Impersonate|WindowsIdentity\\.RunImpersonated|ImpersonateIdentity", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -149,14 +193,20 @@ "name": "OS Operation: ACL (Impersonation)", "id": "AI034200", "description": "OS Operation: ACL (Impersonation)", - "applies_to":[ "csharp" ], - "tags":[ "OS.ACL.TokenImpersonation" ], + "applies_to": [ + "csharp" + ], + "tags": [ + "OS.ACL.TokenImpersonation" + ], "severity": "moderate", "patterns": [ { "pattern": "WindowsImpersonationContext|WindowsIdentity\\.Impersonate|WindowsIdentity\\.RunImpersonated", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -169,13 +219,17 @@ "c", "cpp" ], - "tags":[ "OS.ACL.Write.AppContainer" ], + "tags": [ + "OS.ACL.Write.AppContainer" + ], "severity": "moderate", "patterns": [ { "pattern": "AuthzSetAppContainerInformation", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -188,13 +242,17 @@ "c", "cpp" ], - "tags":[ "OS.ACL.Write.SystemToken." ], + "tags": [ + "OS.ACL.Write.SystemToken." + ], "severity": "moderate", "patterns": [ { "pattern": "AdjustTokenPrivileges|SetTokenInformation", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -203,14 +261,20 @@ "name": "OS Operation: ACL (Write)", "id": "AI034500", "description": "OS Operation: ACL (Write)", - "applies_to":[ "powershell" ], - "tags":[ "OS.ACL.Write" ], + "applies_to": [ + "powershell" + ], + "tags": [ + "OS.ACL.Write" + ], "severity": "moderate", "patterns": [ { "pattern": "Set-Acl", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -219,13 +283,17 @@ "name": "OS Operation: ACL (Write)", "id": "AI034600", "description": "OS Operation: ACL (Write)", - "tags":[ "OS.ACL.Write" ], + "tags": [ + "OS.ACL.Write" + ], "severity": "moderate", "patterns": [ { "pattern": "chmod|chgrp|chown|setfacl", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -234,16 +302,24 @@ "name": "OS Operation: Enable Elevated Permissions", "id": "AI034700", "description": "OS Operation: Enable Elevated Permissions", - "applies_to":[ "package.json" ], - "tags":[ "OS.ACL.Write.Unsafe" ], + "applies_to": [ + "package.json" + ], + "tags": [ + "OS.ACL.Write.Unsafe" + ], "severity": "moderate", "patterns": [ { "pattern": "\"unsafe-perm\"\\s*:\\s*true", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", - "modifiers": [ "i" ] + "modifiers": [ + "i" + ] } ] } diff --git a/AppInspector/rules/default/os/dynamic_execution.json b/AppInspector/rules/default/os/dynamic_execution.json index caae39b..508479d 100644 --- a/AppInspector/rules/default/os/dynamic_execution.json +++ b/AppInspector/rules/default/os/dynamic_execution.json @@ -7,24 +7,29 @@ "objective-c", "swift" ], - "tags":[ "OS.Process.DynamicExecution" ], + "tags": [ + "OS.Process.DynamicExecution" + ], "severity": "moderate", "patterns": [ { "pattern": "stringByEvaluatingJavaScriptFromString", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", "_comment": "https://developer.apple.com/documentation/uikit/uiwebview/1617963-stringbyevaluatingjavascriptfrom?language=objc" }, { "pattern": "evaluateJavaScript", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", "_comment": "https://developer.apple.com/documentation/webkit/wkwebview/1415017-evaluatejavascript?language=objc" } - ] }, { @@ -37,35 +42,53 @@ "javascriptreact", "rust" ], - "tags":[ "OS.Process.DynamicExecution" ], + "tags": [ + "OS.Process.DynamicExecution" + ], "severity": "moderate", "patterns": [ { "pattern": "\beval\\(|new Function\b", "type": "regex", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" }, { "pattern": "exec(Sync)?\\(", "type": "regex", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" }, { "pattern": "\\bspawn\\(", "type": "regex", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" }, { "pattern": "\\bsystem\\(", "type": "regex", - "scopes": [ "code" ], - "modifiers": [ "i" ] + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ] } ] }, @@ -74,15 +97,23 @@ "id": "AI035000", "description": "OS: Dynamic Execution", "recommendation": "", - "applies_to":[ "csharp" ], - "tags":[ "OS.Process.DynamicExecution" ], + "applies_to": [ + "csharp" + ], + "tags": [ + "OS.Process.DynamicExecution" + ], "severity": "moderate", "patterns": [ { "pattern": "process.start", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -92,15 +123,23 @@ "id": "AI035010", "description": "OS: Dynamic Execution", "recommendation": "", - "applies_to":[ "rust" ], - "tags":[ "OS.Process.DynamicExecution" ], + "applies_to": [ + "rust" + ], + "tags": [ + "OS.Process.DynamicExecution" + ], "severity": "moderate", "patterns": [ { "pattern": "use std::process::Command;|Command::new", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -109,22 +148,34 @@ "name": "OS: Dynamic Execution", "id": "AI035100", "description": "OS: Dynamic Execution", - "applies_to":[ "powershell" ], - "tags":[ "OS.Process.DynamicExecution" ], + "applies_to": [ + "powershell" + ], + "tags": [ + "OS.Process.DynamicExecution" + ], "severity": "moderate", "patterns": [ { "pattern": "(iex|invoke-expression).*[.]*webclient", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" }, { "pattern": "(iex|invoke-expression).*[.]*downloadstring", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -137,26 +188,36 @@ "c", "cpp" ], - "tags":[ "OS.Process.DynamicExecution" ], + "tags": [ + "OS.Process.DynamicExecution" + ], "severity": "moderate", "patterns": [ { "pattern": "ksh|bash|csh|sh", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "system(", "type": "substring", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "shellexecute|createprocess|execl|execlp|execlp|execv|execve|execvp|execvpe|execle|fork|QProcess", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -165,14 +226,20 @@ "name": "OS: Dynamic Execution", "id": "AI035300", "description": "OS: Dynamic Execution", - "applies_to":[ "java" ], - "tags":[ "OS.Process.DynamicExecution" ], + "applies_to": [ + "java" + ], + "tags": [ + "OS.Process.DynamicExecution" + ], "severity": "moderate", "patterns": [ { "pattern": "Runtime.getRuntime().exec", "type": "substring", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -181,21 +248,31 @@ "name": "OS: Dynamic Execution", "id": "AI035400", "description": "OS: Dynamic Execution", - "applies_to":[ "python" ], - "tags":[ "OS.Process.DynamicExecution" ], + "applies_to": [ + "python" + ], + "tags": [ + "OS.Process.DynamicExecution" + ], "severity": "moderate", "patterns": [ { "pattern": "subprocess.Popen", "type": "substring", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "\\bsystem\\(", "type": "regex", - "scopes": [ "code" ], - "modifiers": [ "i" ] + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ] } ] }, @@ -203,14 +280,20 @@ "name": "OS: Dynamic Execution", "id": "AI035500", "description": "OS: Dynamic Execution", - "tags":[ "OS.Process.DynamicExecution" ], + "tags": [ + "OS.Process.DynamicExecution" + ], "severity": "moderate", "patterns": [ { "pattern": "xp_cmdshell|xp_regread|xp_instance_regread|xp_regwrite|sp_makewebtask|xp_sendmail|sp_configure|sp_executesql|dbcc writepage", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -219,28 +302,42 @@ "name": "OS: Dynamic Execution", "id": "AI035510", "description": "OS: Dynamic Execution", - "tags":[ "OS.Process.DynamicExecution" ], + "tags": [ + "OS.Process.DynamicExecution" + ], "severity": "moderate", "patterns": [ { "pattern": "powershell|cmd|rundll32|regedit|wscript|javaw|csc|regsvr32|certutil|bitsadmin|schtasks|wmic|eqnedt32|msiexec|cmstp|mshta|curl|installutil|regsvcs|regasm|msbuild|cscript|msxsl|runonce", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" }, { "pattern": "sc (config|query|start|stop)", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" }, { "pattern": "reg (add|copy|delete|import|export|restore|save|unload|compare)", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -249,38 +346,52 @@ "name": "OS: Dynamic Execution", "id": "AI035520", "description": "OS: Dynamic Execution", - "applies_to":[ "php" ], - "tags":[ "OS.Process.DynamicExecution" ], + "applies_to": [ + "php" + ], + "tags": [ + "OS.Process.DynamicExecution" + ], "severity": "critical", "patterns": [ { "pattern": " shell_exec[ ]*\\(", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": " exec[ ]*\\(", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": " system[ ]*\\(", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "medium" }, { "pattern": " passthru[ ]*\\(", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": " proc_open[ ]*\\(", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -289,20 +400,28 @@ "name": "OS: Dynamic Execution through Android Intent", "id": "AI035530", "description": "OS: Indirect Dynamic Execution through Android Intent", - "tags":[ "OS.Process.DynamicExecution.Intent" ], - "applies_to": [ "java" ], + "tags": [ + "OS.Process.DynamicExecution.Intent" + ], + "applies_to": [ + "java" + ], "severity": "moderate", "patterns": [ { "pattern": "startActivity\\(\\)", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "startActivityForResult\\(\\)", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -311,14 +430,20 @@ "name": "OS: Dynamic Execution through Android Intent", "id": "AI035531", "description": "OS: Indirect Dynamic Execution through Android Intent", - "tags":[ "OS.Process.DynamicExecution.Intent" ], - "applies_to": [ "csharp" ], + "tags": [ + "OS.Process.DynamicExecution.Intent" + ], + "applies_to": [ + "csharp" + ], "severity": "moderate", "patterns": [ { "pattern": "new Intent\\(\\)", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ], @@ -327,7 +452,9 @@ "pattern": { "pattern": "import android", "type": "string", - "scopes": [ "code" ] + "scopes": [ + "code" + ] }, "search_in": "finding-region(-200,0)", "negate_finding": false @@ -338,20 +465,28 @@ "name": "OS: Dynamic Execution through Android Intent", "id": "AI035532", "description": "OS: Indirect Dynamic Execution through Android Intent", - "tags":[ "OS.Process.DynamicExecution.Intent" ], - "applies_to": [ "cpp" ], + "tags": [ + "OS.Process.DynamicExecution.Intent" + ], + "applies_to": [ + "cpp" + ], "severity": "moderate", "patterns": [ { "pattern": "startActivity", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "medium" }, { "pattern": "android/content/Intent", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] diff --git a/AppInspector/rules/default/os/file_io.json b/AppInspector/rules/default/os/file_io.json index 52130a5..3352011 100644 --- a/AppInspector/rules/default/os/file_io.json +++ b/AppInspector/rules/default/os/file_io.json @@ -7,13 +7,17 @@ "c", "cpp" ], - "tags":[ "OS.FileOperation.Read" ], + "tags": [ + "OS.FileOperation.Read" + ], "severity": "moderate", "patterns": [ { "pattern": "ifstream|fread|fgets|fgetc", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -22,26 +26,36 @@ "name": "File Operation (Read)", "id": "AI035700", "description": "File Operation (Read)", - "applies_to":[ "csharp" ], - "tags":[ "OS.FileOperation.Read" ], + "applies_to": [ + "csharp" + ], + "tags": [ + "OS.FileOperation.Read" + ], "severity": "moderate", "patterns": [ { "pattern": "File.Read", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "new FileStream", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "File.OpenText", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -50,26 +64,36 @@ "name": "File Operation (Read)", "id": "AI035800", "description": "File Operation (Read)", - "applies_to":[ "python" ], - "tags":[ "OS.FileOperation.Read" ], + "applies_to": [ + "python" + ], + "tags": [ + "OS.FileOperation.Read" + ], "severity": "moderate", "patterns": [ { "pattern": "open\\(.*,.*r.*\\);", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": ".readlines(", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": ".read(", "type": "string", - "scopes": [ "code" ] + "scopes": [ + "code" + ] } ] }, @@ -77,25 +101,35 @@ "name": "File Operation (Read)", "id": "AI035900", "description": "File Operation (Read)", - "applies_to":[ "javascript" ], - "tags":[ "OS.FileOperation.Read" ], + "applies_to": [ + "javascript" + ], + "tags": [ + "OS.FileOperation.Read" + ], "severity": "moderate", "patterns": [ { "pattern": "fs.read", "type": "string", - "scopes": [ "code" ] + "scopes": [ + "code" + ] }, { "pattern": "fs.ReadStream", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "fs.open", "type": "string", - "scopes": [ "code" ] + "scopes": [ + "code" + ] } ] }, @@ -103,20 +137,28 @@ "name": "File Operation (Read)", "id": "AI035910", "description": "File Operation", - "applies_to":[ "java" ], - "tags":[ "OS.FileOperation.Read" ], + "applies_to": [ + "java" + ], + "tags": [ + "OS.FileOperation.Read" + ], "severity": "moderate", "patterns": [ { "pattern": "new BufferedReader(new FileReader", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "FileReader|new Scanner\\(Files\\.readAllLines|Files\\.readAllBytes|FileInputStream", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -125,20 +167,28 @@ "name": "File Operation (Read)", "id": "AI035911", "description": "File Operation", - "applies_to":[ "rust" ], - "tags":[ "OS.FileOperation.Read" ], + "applies_to": [ + "rust" + ], + "tags": [ + "OS.FileOperation.Read" + ], "severity": "moderate", "patterns": [ { "pattern": "BufRead|File::open|read_to_string|read_to_end|\\.read", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "FileReader|new Scanner\\(Files\\.readAllLines|Files\\.readAllBytes|FileInputStream", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -147,20 +197,29 @@ "name": "File Operation (Delete)", "id": "AI036000", "description": "File Operation (Delete)", - "applies_to": [ "c", "cpp" ], - "tags":[ "OS.FileOperation.Delete" ], + "applies_to": [ + "c", + "cpp" + ], + "tags": [ + "OS.FileOperation.Delete" + ], "severity": "moderate", "patterns": [ { "pattern": "\\b(std::)?remove\\(.+\\)", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "remove", "type": "string", - "scopes": [ "code" ] + "scopes": [ + "code" + ] } ], "conditions": [ @@ -168,7 +227,9 @@ "pattern": { "pattern": "include <(fstream|stdio|iostream)", "type": "regex", - "scopes": [ "code" ] + "scopes": [ + "code" + ] }, "search_in": "finding-region(-200,1)", "negate_finding": false @@ -179,14 +240,20 @@ "name": "File Operation (Delete)", "id": "AI036010", "description": "File Operation (Delete)", - "applies_to":[ "rust" ], - "tags":[ "OS.FileOperation.Delete" ], + "applies_to": [ + "rust" + ], + "tags": [ + "OS.FileOperation.Delete" + ], "severity": "moderate", "patterns": [ { "pattern": "remove_file", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -195,20 +262,28 @@ "name": "File Operation (Delete)", "id": "AI036100", "description": "File Operation (Delete)", - "applies_to":[ "python" ], - "tags":[ "OS.FileOperation.Delete" ], + "applies_to": [ + "python" + ], + "tags": [ + "OS.FileOperation.Delete" + ], "severity": "moderate", "patterns": [ { "pattern": "os.remove(", "type": "substring", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "os.ulink(", "type": "substring", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -217,14 +292,20 @@ "name": "File Operation (Delete)", "id": "AI036200", "description": "File Operation (Delete)", - "applies_to":[ "csharp" ], - "tags":[ "OS.FileOperation.Delete" ], + "applies_to": [ + "csharp" + ], + "tags": [ + "OS.FileOperation.Delete" + ], "severity": "moderate", "patterns": [ { "pattern": "File.Delete", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -233,16 +314,24 @@ "name": "File Operation (Delete)", "id": "AI036300", "description": "File Operation (Delete)", - "applies_to":[ "java" ], - "tags":[ "OS.FileOperation.Delete" ], + "applies_to": [ + "java" + ], + "tags": [ + "OS.FileOperation.Delete" + ], "severity": "moderate", "patterns": [ { "pattern": "file.delete(", "type": "substring", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", - "modifiers": [ "i" ] + "modifiers": [ + "i" + ] } ] }, @@ -250,20 +339,28 @@ "name": "File Operation (Delete)", "id": "AI036400", "description": "File Operation (Delete)", - "applies_to":[ "javascript" ], - "tags":[ "OS.FileOperation.Delete" ], + "applies_to": [ + "javascript" + ], + "tags": [ + "OS.FileOperation.Delete" + ], "severity": "moderate", "patterns": [ { "pattern": ".unlink(Sync)?\\(", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "fs.remove(", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -272,20 +369,28 @@ "name": "File Operation (Write)", "id": "AI036500", "description": "File Operation (Write)", - "applies_to":[ "csharp" ], - "tags":[ "OS.FileOperation.Write" ], + "applies_to": [ + "csharp" + ], + "tags": [ + "OS.FileOperation.Write" + ], "severity": "moderate", "patterns": [ { "pattern": "File\\.WriteAllText|File\\.AppendText", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "StreamWriter", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -294,14 +399,20 @@ "name": "File Operation (Write)", "id": "AI036501", "description": "File Operation (Write)", - "applies_to":[ "csharp" ], - "tags":[ "OS.FileOperation.Write" ], + "applies_to": [ + "csharp" + ], + "tags": [ + "OS.FileOperation.Write" + ], "severity": "moderate", "patterns": [ { "pattern": "StreamWriter", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ], @@ -310,13 +421,15 @@ "pattern": { "pattern": "FileStream", "type": "string", - "scopes": [ "code" ] + "scopes": [ + "code" + ] }, "search_in": "finding-region(-10,0)", "negate_finding": false } ] - }, + }, { "name": "File Operation (Write)", "id": "AI036600", @@ -325,34 +438,46 @@ "c", "cpp" ], - "tags":[ "OS.FileOperation.Write" ], + "tags": [ + "OS.FileOperation.Write" + ], "severity": "moderate", "patterns": [ { "pattern": "fstream|fwrite|fprintf|sprintf|fput", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "fopen\\(.*,.*w.*\\);", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", "_comment": "write" }, { "pattern": "fopen\\(.*,.*a.*\\);", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", "_comment": "append" }, { "pattern": "writefile|copyfile|filesystem::copy|std::ios_base::app", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -361,27 +486,37 @@ "name": "File Operation (Write)", "id": "AI036610", "description": "File Operation (Write)", - "applies_to":[ "python" ], - "tags":[ "OS.FileOperation.Write" ], + "applies_to": [ + "python" + ], + "tags": [ + "OS.FileOperation.Write" + ], "severity": "moderate", "patterns": [ { "pattern": ".write(", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "open\\(.*,.*w.*\\);", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", "_comment": "write" }, { "pattern": "open\\(.*,.*a.*\\);", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", "_comment": "append" } @@ -391,26 +526,36 @@ "name": "File Operation (Write)", "id": "AI036620", "description": "File Operation (Write)", - "applies_to":[ "javascript" ], - "tags":[ "OS.FileOperation.Write" ], + "applies_to": [ + "javascript" + ], + "tags": [ + "OS.FileOperation.Write" + ], "severity": "moderate", "patterns": [ { "pattern": "fs.write", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "WriteStream|appendFile|copyFile|createWriteStream|ftruncate", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "FileWriter|BufferedWriter|FileUtils\\.copyFile|Files\\.copy", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -419,14 +564,20 @@ "name": "File Operation (Write)", "id": "AI036621", "description": "File Operation (Write)", - "applies_to":[ "rust" ], - "tags":[ "OS.FileOperation.Write" ], + "applies_to": [ + "rust" + ], + "tags": [ + "OS.FileOperation.Write" + ], "severity": "moderate", "patterns": [ { "pattern": "File::create|use std::io::Write|BufWriter::new", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -435,20 +586,28 @@ "name": "File Operation (Write)", "id": "AI036622", "description": "File Operation (Write)", - "applies_to":[ "rust" ], - "tags":[ "OS.FileOperation.Write" ], + "applies_to": [ + "rust" + ], + "tags": [ + "OS.FileOperation.Write" + ], "severity": "moderate", "patterns": [ { "pattern": "\\.write_all|\\.write_fmt", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "write", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "low" } ], @@ -457,8 +616,12 @@ "pattern": { "pattern": "use std::io::Write|use std::io::File|use std:fs:File", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ] + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ] }, "search_in": "finding-region(-240,240)", "negate_finding": false diff --git a/AppInspector/rules/default/os/process.json b/AppInspector/rules/default/os/process.json index 88f7c7b..f76ba41 100644 --- a/AppInspector/rules/default/os/process.json +++ b/AppInspector/rules/default/os/process.json @@ -3,19 +3,25 @@ "name": "OS: Multiprocessing / Multithreading", "id": "AI036700", "description": "OS: Multiprocessing / Multithreading", - "tags":[ "OS.Process.Multiprocessing" ], + "tags": [ + "OS.Process.Multiprocessing" + ], "severity": "moderate", "patterns": [ { "pattern": "mutex|semaphore|thread|lock|fork", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "async|await", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "medium" } ] @@ -28,13 +34,17 @@ "c", "cpp" ], - "tags":[ "OS.Process.Multiprocessing" ], + "tags": [ + "OS.Process.Multiprocessing" + ], "severity": "moderate", "patterns": [ { "pattern": "WaitForSingleObject|WaitForMultipleObjects", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -43,20 +53,28 @@ "name": "OS: Multiprocessing / Multithreading", "id": "AI036900", "description": "OS: Multiprocessing / Multithreading", - "applies_to":[ "csharp" ], - "tags":[ "OS.Process.Multiprocessing" ], + "applies_to": [ + "csharp" + ], + "tags": [ + "OS.Process.Multiprocessing" + ], "severity": "moderate", "patterns": [ { "pattern": "BackgroundWorker|Task\\.Run|Async|System.Threading", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "(new System\\.Diagnostics.Process\\(\\)| new Process\\(\\))", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -65,20 +83,28 @@ "name": "OS: Multiprocessing / Multithreading", "id": "AI036910", "description": "OS: Multiprocessing / Multithreading", - "applies_to":[ "rust" ], - "tags":[ "OS.Process.Multiprocessing" ], + "applies_to": [ + "rust" + ], + "tags": [ + "OS.Process.Multiprocessing" + ], "severity": "moderate", "patterns": [ { "pattern": "thread::spawn", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "(new System\\.Diagnostics.Process\\(\\)| new Process\\(\\))", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -87,14 +113,20 @@ "name": "OS: Multiprocessing / Multithreading", "id": "AI037000", "description": "OS: Multiprocessing / Multithreading", - "applies_to":[ "java" ], - "tags":[ "OS.Process.Multiprocessing" ], + "applies_to": [ + "java" + ], + "tags": [ + "OS.Process.Multiprocessing" + ], "severity": "moderate", "patterns": [ { "pattern": "Runnable", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -103,14 +135,20 @@ "name": "OS: Multiprocessing / Multithreading", "id": "AI037100", "description": "OS: Multiprocessing / Multithreading", - "applies_to":[ "go" ], - "tags":[ "OS.Process.Multiprocessing" ], + "applies_to": [ + "go" + ], + "tags": [ + "OS.Process.Multiprocessing" + ], "severity": "moderate", "patterns": [ { "pattern": "<-", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -125,23 +163,31 @@ "csharp", "python" ], - "tags":[ "OS.Process.ListRequest" ], + "tags": [ + "OS.Process.ListRequest" + ], "severity": "moderate", "patterns": [ { "pattern": "Process.GetProcesses()", "type": "string", - "scopes": [ "code" ] + "scopes": [ + "code" + ] }, { "pattern": "ReadProcessMemory|OpenProcess|VirtualQueryEx", "type": "regexword", - "scopes": [ "code" ] + "scopes": [ + "code" + ] }, { "pattern": "system32\\tasklist.exe", "type": "string", - "scopes": [ "code" ] + "scopes": [ + "code" + ] } ] }, @@ -149,14 +195,20 @@ "name": "OS: Process Access (Read)", "id": "AI037300", "description": "OS: Process Access (Read)", - "applies_to":[ "powershell" ], - "tags":[ "OS.Process.ListRequest" ], + "applies_to": [ + "powershell" + ], + "tags": [ + "OS.Process.ListRequest" + ], "severity": "moderate", "patterns": [ { "pattern": "Get-Process ", "type": "substring", - "scopes": [ "code" ] + "scopes": [ + "code" + ] } ] }, @@ -164,15 +216,23 @@ "name": "OS: Process Access (Read)", "id": "AI037400", "description": "OS: Process Access (Read)", - "applies_to":[ "java" ], - "tags":[ "OS.Process.ListRequest" ], + "applies_to": [ + "java" + ], + "tags": [ + "OS.Process.ListRequest" + ], "severity": "moderate", "patterns": [ { "pattern": "exec.*['\\\"]ps ", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ] + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ] } ] } diff --git a/AppInspector/rules/default/os/setenv.json b/AppInspector/rules/default/os/setenv.json index 92d35e2..4381445 100644 --- a/AppInspector/rules/default/os/setenv.json +++ b/AppInspector/rules/default/os/setenv.json @@ -5,13 +5,17 @@ "description": "OS: Environment Variable (Write)", "applies_to": [ ], - "tags":[ "OS.Environment.Write" ], + "tags": [ + "OS.Environment.Write" + ], "severity": "moderate", "patterns": [ { "pattern": "setenv|putenv", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", "_comment": "linux cmd from lang x" } @@ -21,14 +25,20 @@ "name": "OS: Environment Variable (Write)", "id": "AI037600", "description": "OS: Environment Variable (Write)", - "applies_to":[ "python" ], - "tags":[ "OS.Environment.Write" ], + "applies_to": [ + "python" + ], + "tags": [ + "OS.Environment.Write" + ], "severity": "moderate", "patterns": [ { "pattern": "\\bos\\.environ.+=", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "medium" } ] @@ -37,14 +47,20 @@ "name": "OS: Environment Variable (Write)", "id": "AI037700", "description": "OS: Environment Variable (Write)", - "applies_to":[ "rust" ], - "tags":[ "OS.Environment.Write" ], + "applies_to": [ + "rust" + ], + "tags": [ + "OS.Environment.Write" + ], "severity": "moderate", "patterns": [ { "pattern": "env::set_var", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -53,14 +69,20 @@ "name": "OS: Environment Variable (Write)", "id": "AI037800", "description": "OS: Environment Variable (Write)", - "applies_to":[ "csharp" ], - "tags":[ "OS.Environment.Write" ], + "applies_to": [ + "csharp" + ], + "tags": [ + "OS.Environment.Write" + ], "severity": "moderate", "patterns": [ { "pattern": "SetEnvironmentVariable", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -69,14 +91,20 @@ "name": "OS: Environment Variable (Write)", "id": "AI037900", "description": "OS: Environment Variable (Write)", - "applies_to":[ "powershell" ], - "tags":[ "OS.Environment.Write" ], + "applies_to": [ + "powershell" + ], + "tags": [ + "OS.Environment.Write" + ], "severity": "moderate", "patterns": [ { "pattern": "SetEnvironmentVariable|\\$env:.+=", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -85,20 +113,28 @@ "name": "OS: Environment Variable (Write)", "id": "AI037910", "description": "OS: Environment Variable (Write)", - "applies_to":[ "javascript" ], - "tags":[ "OS.Environment.Write" ], + "applies_to": [ + "javascript" + ], + "tags": [ + "OS.Environment.Write" + ], "severity": "moderate", "patterns": [ { "pattern": "process\\.env\\[\\\".*\\\"\\] *= *", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "app\\.set\\('environment'", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -109,14 +145,18 @@ "description": "OS: Environment Variable (Write)", "applies_to": [ ], - "tags":[ "OS.Environment.Write" ], + "tags": [ + "OS.Environment.Write" + ], "severity": "moderate", "_comment": "Windows Registry location for environment variables", "patterns": [ { "pattern": "System\\CurrentControlSet\\Control\\Session Manager\\Environment", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "medium" } ], @@ -125,8 +165,12 @@ "pattern": { "pattern": "SetValue", "type": "regex", - "scopes": [ "code" ], - "modifiers": [ "i" ] + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ] }, "search_in": "finding-region(-10,10)", "negate_finding": false @@ -137,14 +181,20 @@ "name": "OS: Environment Variable (Read)", "id": "AI037930", "description": "OS: Environment Variable (Read)", - "applies_to":[ "python" ], - "tags":[ "OS.Environment.Read" ], + "applies_to": [ + "python" + ], + "tags": [ + "OS.Environment.Read" + ], "severity": "moderate", "patterns": [ { "pattern": "os\\.environ\\.items|os\\.environ\\[\\\".*\\\"\\]|os\\.environ\\.get", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "medium" } ] @@ -153,14 +203,20 @@ "name": "OS: Environment Variable (Read)", "id": "AI037940", "description": "OS: Environment Variable (Read)", - "applies_to":[ "rust" ], - "tags":[ "OS.Environment.Read" ], + "applies_to": [ + "rust" + ], + "tags": [ + "OS.Environment.Read" + ], "severity": "moderate", "patterns": [ { "pattern": "env::get_var", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -169,14 +225,20 @@ "name": "OS: Environment Variable (Read)", "id": "AI037950", "description": "OS: Environment Variable (Read)", - "applies_to":[ "csharp" ], - "tags":[ "OS.Environment.Read" ], + "applies_to": [ + "csharp" + ], + "tags": [ + "OS.Environment.Read" + ], "severity": "moderate", "patterns": [ { "pattern": "GetEnvironmentVariable", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -185,14 +247,20 @@ "name": "OS: Environment Variable (Read)", "id": "AI037960", "description": "OS: Environment Variable (Read)", - "applies_to":[ "powershell" ], - "tags":[ "OS.Environment.Read" ], + "applies_to": [ + "powershell" + ], + "tags": [ + "OS.Environment.Read" + ], "severity": "moderate", "patterns": [ { "pattern": "GetEnvironmentVariable|\\$env:|Env:\\\\", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -201,20 +269,28 @@ "name": "OS: Environment Variable (Read)", "id": "AI037970", "description": "OS: Environment Variable (Read)", - "applies_to":[ "javascript" ], - "tags":[ "OS.Environment.Read" ], + "applies_to": [ + "javascript" + ], + "tags": [ + "OS.Environment.Read" + ], "severity": "moderate", "patterns": [ { "pattern": "= *process\\.env\\[\\\".*\\\"\\]", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "app\\.get\\('environment'", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -225,14 +301,18 @@ "description": "OS: Environment Variable (Read)", "applies_to": [ ], - "tags":[ "OS.Environment.Read" ], + "tags": [ + "OS.Environment.Read" + ], "severity": "moderate", "_comment": "Windows Registry location for environment variables", "patterns": [ { "pattern": "System\\CurrentControlSet\\Control\\Session Manager\\Environment", "type": "substring", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "medium" } ], @@ -241,8 +321,12 @@ "pattern": { "pattern": "GetValue", "type": "substring", - "scopes": [ "code" ], - "modifiers": [ "i" ] + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ] }, "search_in": "finding-region(-10,10)", "negate_finding": false @@ -255,13 +339,17 @@ "description": "OS: Environment Variable (Read)", "applies_to": [ ], - "tags":[ "OS.Environment.Read" ], + "tags": [ + "OS.Environment.Read" + ], "severity": "moderate", "patterns": [ { "pattern": "getenv", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", "_comment": "linux for lang x" } diff --git a/AppInspector/rules/default/os/system_registry.json b/AppInspector/rules/default/os/system_registry.json index d00ada4..7436c85 100644 --- a/AppInspector/rules/default/os/system_registry.json +++ b/AppInspector/rules/default/os/system_registry.json @@ -3,7 +3,9 @@ "name": "OS Operation: Windows Registry (Write)", "id": "AI038000", "description": "OS Operation: Windows Registry (Write)", - "tags":[ "OS.SystemRegistry.Write" ], + "tags": [ + "OS.SystemRegistry.Write" + ], "applies_to": [ "c", "cpp" @@ -13,7 +15,9 @@ { "pattern": "RegSetValueEx|RegOpenKeyEx|RegCreateKeyEx", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -22,14 +26,20 @@ "name": "OS Operation: Windows Registry (Write)", "id": "AI038100", "description": "OS Operation: Windows Registry (Write)", - "tags":[ "OS.SystemRegistry.Write" ], - "applies_to":[ "csharp" ], + "tags": [ + "OS.SystemRegistry.Write" + ], + "applies_to": [ + "csharp" + ], "severity": "moderate", "patterns": [ { "pattern": "Registry(Key)?(.*)?.(SetValue|CreateSubKey|Delete)", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -38,14 +48,20 @@ "name": "OS Operation: Windows Registry (Write)", "id": "AI038110", "description": "OS Operation: Windows Registry (Write)", - "tags":[ "OS.SystemRegistry.Write" ], - "applies_to":[ "csharp" ], + "tags": [ + "OS.SystemRegistry.Write" + ], + "applies_to": [ + "csharp" + ], "severity": "moderate", "patterns": [ { "pattern": "CreateSubKey|SetValue", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ], @@ -54,7 +70,9 @@ "pattern": { "pattern": "HKEY_|Registry", "type": "regexword", - "scopes": [ "code" ] + "scopes": [ + "code" + ] }, "search_in": "finding-region(-40,40)", "negate_finding": false @@ -65,14 +83,20 @@ "name": "OS Operation: Windows Registry (Write)", "id": "AI038200", "description": "OS Operation: Windows Registry (Write)", - "tags":[ "OS.SystemRegistry.Write" ], - "applies_to":[ "python" ], + "tags": [ + "OS.SystemRegistry.Write" + ], + "applies_to": [ + "python" + ], "severity": "moderate", "patterns": [ { "pattern": "_?winreg\\.(Set|Delete|Create)(Key|Value)(Ex)?", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", "_comment": "_winreg and winreg built-in modules" } @@ -82,21 +106,31 @@ "name": "OS Operation: Windows Registry (Write)", "id": "AI038210", "description": "OS Operation: Windows Registry (Write)", - "tags":[ "OS.SystemRegistry.Write" ], - "applies_to":[ "powershell" ], + "tags": [ + "OS.SystemRegistry.Write" + ], + "applies_to": [ + "powershell" + ], "severity": "moderate", "patterns": [ { "pattern": "Registry", "type": "regex", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" }, { "pattern": "(HKLM|HKCU):", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ], @@ -105,7 +139,9 @@ "pattern": { "pattern": "Set-ItemProperty", "type": "string", - "scopes": [ "code" ] + "scopes": [ + "code" + ] }, "search_in": "finding-region(-5,5)", "negate_finding": false @@ -116,14 +152,20 @@ "name": "OS Operation: Windows Registry (ACL)", "id": "AI038300", "description": "OS Operation: Windows Registry (ACL)", - "tags":[ "OS.SystemRegistry.ACL" ], - "applies_to": [ "csharp" ], + "tags": [ + "OS.SystemRegistry.ACL" + ], + "applies_to": [ + "csharp" + ], "severity": "moderate", "patterns": [ { "pattern": "RegistryAccessRule|RegistrySecurity", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -132,7 +174,9 @@ "name": "OS Operation: Windows Registry (Read)", "id": "AI038400", "description": "OS Operation: Windows Registry (Read)", - "tags":[ "OS.SystemRegistry.Read" ], + "tags": [ + "OS.SystemRegistry.Read" + ], "applies_to": [ "c", "cpp" @@ -142,7 +186,9 @@ { "pattern": "RegGetValue|RegGetDWord|RegGetString|CRegKey|RegOpenKey|RegQueryKeyEx", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -151,20 +197,28 @@ "name": "OS Operation: Windows Registry (Read)", "id": "AI038500", "description": "OS Operation: Windows Registry (Read)", - "tags":[ "OS.SystemRegistry.Read" ], - "applies_to":[ "powershell" ], + "tags": [ + "OS.SystemRegistry.Read" + ], + "applies_to": [ + "powershell" + ], "severity": "moderate", "patterns": [ { "pattern": "Registry", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "(HKLM|HKCU):", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ], @@ -173,7 +227,9 @@ "pattern": { "pattern": "Get-ItemProperty", "type": "string", - "scopes": [ "code" ] + "scopes": [ + "code" + ] }, "search_in": "finding-region(-5,5)", "negate_finding": false @@ -184,14 +240,20 @@ "name": "OS Operation: Windows Registry (Read)", "id": "AI038600", "description": "OS Operation: Windows Registry (Read)", - "tags":[ "OS.SystemRegistry.Read" ], - "applies_to":[ "csharp" ], + "tags": [ + "OS.SystemRegistry.Read" + ], + "applies_to": [ + "csharp" + ], "severity": "moderate", "patterns": [ { "pattern": "Registry(Key)?(.*)?.GetValue", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ], @@ -200,7 +262,9 @@ "pattern": { "pattern": "HKEY_", "type": "string", - "scopes": [ "code" ] + "scopes": [ + "code" + ] }, "search_in": "finding-region(-40,40)", "negate_finding": false @@ -211,14 +275,20 @@ "name": "OS Operation: Windows Registry (Read)", "id": "AI038601", "description": "OS Operation: Windows Registry (Read)", - "tags":[ "OS.SystemRegistry.Read" ], - "applies_to":[ "rust" ], + "tags": [ + "OS.SystemRegistry.Read" + ], + "applies_to": [ + "rust" + ], "severity": "moderate", "patterns": [ { "pattern": ".open_subkey(", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ], @@ -227,7 +297,9 @@ "pattern": { "pattern": "HKEY_", "type": "string", - "scopes": [ "code" ] + "scopes": [ + "code" + ] }, "search_in": "finding-region(-40,40)", "negate_finding": false diff --git a/AppInspector/rules/default/os/user_accts.json b/AppInspector/rules/default/os/user_accts.json index 51071ad..d68b804 100644 --- a/AppInspector/rules/default/os/user_accts.json +++ b/AppInspector/rules/default/os/user_accts.json @@ -3,14 +3,20 @@ "name": "OS: User Account Write (Add)", "id": "AI038700", "description": "OS: User Account Write (Add)", - "applies_to":[ "csharp" ], - "tags":[ "OS.UserAccount.Write" ], + "applies_to": [ + "csharp" + ], + "tags": [ + "OS.UserAccount.Write" + ], "severity": "moderate", "patterns": [ { "pattern": "NetUserAdd|NetGroupAddUser|NetLocalGroupAdd", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -19,14 +25,20 @@ "name": "OS: User Account Write (Modify)", "id": "AI038800", "description": "OS: User Account Write (Modify)", - "applies_to":[ "csharp" ], - "tags":[ "OS.UserAccount.Write" ], + "applies_to": [ + "csharp" + ], + "tags": [ + "OS.UserAccount.Write" + ], "severity": "moderate", "patterns": [ { "pattern": "NetGroupSetInfo|NetGroupSetUsers|NetLocalGroupDel|NetUserChangePassword", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] diff --git a/AppInspector/rules/default/security_feature/authentication.json b/AppInspector/rules/default/security_feature/authentication.json index 8189a6c..f20db08 100644 --- a/AppInspector/rules/default/security_feature/authentication.json +++ b/AppInspector/rules/default/security_feature/authentication.json @@ -3,35 +3,57 @@ "name": "Authentication: Microsoft (Identity)", "id": "AI038900", "description": "Authentication using Microsoft Identity Platform (ADAL/MSAL) or similar", - "tags":[ "Authentication.Microsoft.Online" ], + "tags": [ + "Authentication.Microsoft.Online" + ], "severity": "critical", "patterns": [ { "pattern": "login\\.microsoftonline(-p)*\\.com|SAS.*Token|login\\.live.com|Shared *Access *Signature", "type": "regexword", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" }, { "pattern": "System\\.IdentityModel|AzureADB2C|SingleAccountPublicClientApplication|MultipleAccountPublicClientApplication", "type": "regexword", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" }, { "pattern": "microsoft\\.aad|microsoft\\.adal|microsoft\\.msal|AADClient|Microsoft\\.IdentityModel|passport\\.net|ServicePrincipalCredentials", "type": "regexword", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" }, { "pattern": "AuthenticationResult|MsalServiceException", "type": "regexword", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -40,13 +62,18 @@ "name": "Authentication: Microsoft (Identity)", "id": "AI038910", "description": "Authentication using Microsoft Identity Platform (ADAL/MSAL) or similar", - "tags":[ "Authentication.Microsoft.Online" ], + "tags": [ + "Authentication.Microsoft.Online" + ], "severity": "critical", "patterns": [ { "pattern": "AcquireTokenForClient|ConfidentialClientApplicationBuilder", "type": "regexword", - "scopes": [ "code", "comment" ], + "scopes": [ + "code", + "comment" + ], "confidence": "high" } ], @@ -55,7 +82,9 @@ "pattern": { "pattern": "\\.WithClientClaims\\(.*cert.*\\)|\\.WithCertificate\\(", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "_comment": "negate cases for certificate rule identifying client TLS auth" }, "search_in": "finding-region(-10,10)", @@ -67,21 +96,31 @@ "name": "Authentication: Google", "id": "AI039000", "description": "Authentication: Google", - "tags":[ "Authentication.Google" ], + "tags": [ + "Authentication.Google" + ], "severity": "critical", "patterns": [ { "pattern": "using Google\\.Apis\\.Auth|GoogleCredential|AppIdentityCredential|oauth2/google|apis\\.google\\.com/js/api\\.js", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" }, { "pattern": "OIDAuthorizationRequest|accounts\\.google\\.com/o/oauth2|googleapis\\.com/oauth2|GTMAppAuth", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -90,35 +129,53 @@ "name": "Authentication: Oauth", "id": "AI039100", "description": "Authentication: Oauth", - "tags":[ "Authentication.Oauth" ], + "tags": [ + "Authentication.Oauth" + ], "severity": "critical", "patterns": [ { "pattern": "oauth", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" }, { "pattern": "auth.?token|access.?token|client.?credentials|client.?id|client.?secret|api.?key", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "low" }, { "pattern": "Authorization: Bearer", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" }, { "pattern": "bearer", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "low" } ] @@ -127,14 +184,21 @@ "name": "Authentication: OpenIDConnect", "id": "AI039200", "description": "Authentication: OpenIDConnect based Oauth", - "tags":[ "Authentication.OpenIDConnect" ], + "tags": [ + "Authentication.OpenIDConnect" + ], "severity": "critical", "patterns": [ { "pattern": "OpenID", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -143,32 +207,47 @@ "name": "Authentication: Microsoft (Active Directory)", "id": "AI039300", "description": "Authentication: Microsoft (Active Directory)", - "tags":[ "Authentication.Microsoft.Windows.ActiveDirectory" ], + "tags": [ + "Authentication.Microsoft.Windows.ActiveDirectory" + ], "severity": "critical", "patterns": [ { "pattern": "active.?directory|ADFS", "type": "regexword", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" }, { "pattern": "ADsOpenObject|ADObject|ADComputer|ADGroup|ADUser|ADContainer|ADDomain", "type": "regexword", - "scopes": [ "code", "comment" ], + "scopes": [ + "code", + "comment" + ], "confidence": "high" }, { "pattern": "Microsoft\\.IdentityModel\\.ActiveDirectory", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "System\\.DirectoryServices|IDirectoryObject|IDirectorySearch", "type": "regexword", - "scopes": [ "code", "comment" ], + "scopes": [ + "code", + "comment" + ], "confidence": "medium" } ] @@ -177,14 +256,21 @@ "name": "Authentication: LDAP", "id": "AI039400", "description": "Authentication: LDAP", - "tags":[ "Authentication.LDAP" ], + "tags": [ + "Authentication.LDAP" + ], "severity": "critical", "patterns": [ { "pattern": "LDAP|ldaps", "type": "regexword", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -193,26 +279,36 @@ "name": "Authentication: Microsoft Windows (NTML)", "id": "AI039500", "description": "AAuthentication: Microsoft Windows (NTML)", - "tags":[ "Authentication.Microsoft.Windows.NTLM" ], + "tags": [ + "Authentication.Microsoft.Windows.NTLM" + ], "severity": "critical", "patterns": [ { "pattern": "WWW-Authenticate: NTLM|windowsAuthentication|CredentialCache\\.DefaultCredentials|HTTPNtlmAuthHandler", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "LogonUserA|LogonUserEx|LogonUserW", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "NTLM", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "medium" } ] @@ -228,14 +324,20 @@ "java", "csharp" ], - "tags":[ "Authentication.Microsoft.Windows.Kerberos" ], + "tags": [ + "Authentication.Microsoft.Windows.Kerberos" + ], "severity": "critical", "patterns": [ { "pattern": "WindowsIdentity|WindowsPrincipal|Kerberos", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high", "_comment": "possible use for NTLM but default is Kerberos" } @@ -245,14 +347,21 @@ "name": "Authentication: JWT", "id": "AI039600", "description": "Authentication: JWT", - "tags":[ "Authentication.JWT" ], + "tags": [ + "Authentication.JWT" + ], "severity": "critical", "patterns": [ { "pattern": "jwt|RFC 7519|System\\.IdentityModel\\.Tokens\\.Jwt|JwtSecurityToken|JsonWebToken", "type": "regexword", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -261,14 +370,20 @@ "name": "Authentication: HTML Form Based", "id": "AI039700", "description": "Authentication: HTML Form Based", - "tags":[ "Authentication.HTMLForm" ], + "tags": [ + "Authentication.HTMLForm" + ], "severity": "critical", "patterns": [ { "pattern": " type=['\\\"]password['\\\"] ", "type": "regex", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -277,15 +392,23 @@ "name": "Authentication: SAML", "id": "AI039800", "description": "Authentication: SAML", - "applies_to":[ "csharp" ], - "tags":[ "Authentication.SAML" ], + "applies_to": [ + "csharp" + ], + "tags": [ + "Authentication.SAML" + ], "severity": "critical", "patterns": [ { "pattern": "AuthenticationHeaderValue\\(\"SAML\"", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -294,21 +417,31 @@ "name": "Authentication: SAML", "id": "AI039810", "description": "Authentication: SAML", - "tags":[ "Authentication.SAML" ], + "tags": [ + "Authentication.SAML" + ], "severity": "critical", "patterns": [ { "pattern": "SAML|saml2", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" }, { "pattern": "federation|sso", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "medium" } ] @@ -317,14 +450,20 @@ "name": "Authentication: Basic-Auth", "id": "AI039900", "description": "Authentication: Basic-Auth", - "tags":[ "Authentication.General" ], + "tags": [ + "Authentication.General" + ], "severity": "critical", "patterns": [ { "pattern": "\"Authorization: Basic|WWW-Authenticate|AuthenticationHeaderValue\\(\"Basic\"", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -333,14 +472,20 @@ "name": "Authentication: General", "id": "AI040000", "description": "Authentication: General", - "tags":[ "Authentication.General" ], + "tags": [ + "Authentication.General" + ], "severity": "critical", "patterns": [ { "pattern": "principal|identity|auth|authenticated|authentication|signin|sign-?in|signout|sign-?out|networkcredential", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -349,14 +494,20 @@ "name": "Authentication: General", "id": "AI040001", "description": "Authentication: General", - "tags":[ "Authentication.General" ], + "tags": [ + "Authentication.General" + ], "severity": "critical", "patterns": [ { "pattern": "log(in|on)|log(off|out)", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "low" } ] @@ -365,21 +516,31 @@ "name": "Authentication: General", "id": "AI040100", "description": "Authentication: General", - "tags":[ "Authentication.General" ], + "tags": [ + "Authentication.General" + ], "severity": "critical", "patterns": [ { "pattern": "(username|userid|password|passphrase|multi-factor|credential|acct)s?", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" }, { "pattern": "connection.?string|conn.?string|DefaultNetworkCredentials", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "medium" } ] diff --git a/AppInspector/rules/default/security_feature/authorization.json b/AppInspector/rules/default/security_feature/authorization.json index 5c1b136..9eed8f0 100644 --- a/AppInspector/rules/default/security_feature/authorization.json +++ b/AppInspector/rules/default/security_feature/authorization.json @@ -5,20 +5,28 @@ "description": "Authorization: Microsoft Azure SAS", "applies_to": [ ], - "tags":[ "Authorization.Microsoft.Azuze.SAS" ], + "tags": [ + "Authorization.Microsoft.Azuze.SAS" + ], "severity": "critical", "patterns": [ { "pattern": "sharedAccessPolicy|storageAccessKey|sharedAccessAccountPolicy|storageAccountOrConnectionString", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" }, { "pattern": "SAS *Token", "type": "regex", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -29,14 +37,20 @@ "description": "Authorization: Roles Based Access (RBAC)", "applies_to": [ ], - "tags":[ "Authorization.RBAC" ], + "tags": [ + "Authorization.RBAC" + ], "severity": "critical", "patterns": [ { "pattern": "rbac|userroles?|roles?", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -47,14 +61,20 @@ "description": "Authorization: Permissions", "applies_to": [ ], - "tags":[ "Authorization.Permissions" ], + "tags": [ + "Authorization.Permissions" + ], "severity": "critical", "patterns": [ { "pattern": "permissions*", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ], @@ -63,8 +83,12 @@ "pattern": { "pattern": "License|copyright|readme.md", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "_comment": "eliminate notices" }, "search_in": "finding-region(-10,10)", @@ -78,14 +102,20 @@ "description": "Authorization: Claims", "applies_to": [ ], - "tags":[ "Authorization.Claims" ], + "tags": [ + "Authorization.Claims" + ], "severity": "critical", "patterns": [ { "pattern": "claims*", "type": "regex", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ], @@ -94,8 +124,12 @@ "pattern": { "pattern": "License|copyright|Permission to use|readme.md", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "_comment": "eliminate notices" }, "search_in": "finding-region(-10,10)", @@ -109,14 +143,20 @@ "description": "Authorization: General", "applies_to": [ ], - "tags":[ "Authorization.General" ], + "tags": [ + "Authorization.General" + ], "severity": "critical", "patterns": [ { "pattern": "authorize|authorized|authorization|isauthorized", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ], @@ -125,8 +165,12 @@ "pattern": { "pattern": "SAS|SharedAccess", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "_comment": "since already covered by another rule if SAS related" }, "search_in": "finding-region(-10,10)", @@ -136,8 +180,12 @@ "pattern": { "pattern": "UnauthorizedAccessException", "type": "regex", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "_comment": "This may not indicate that actual authorization is going on." }, "search_in": "same-line", diff --git a/AppInspector/rules/default/test_frameworks/cpp_testing.json b/AppInspector/rules/default/test_frameworks/cpp_testing.json index 790b7db..1415444 100644 --- a/AppInspector/rules/default/test_frameworks/cpp_testing.json +++ b/AppInspector/rules/default/test_frameworks/cpp_testing.json @@ -7,13 +7,20 @@ "c", "cpp" ], - "tags":[ "Framework.Testing.AceUnit" ], + "tags": [ + "Framework.Testing.AceUnit" + ], "patterns": [ { "pattern": "aceunit", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -26,13 +33,19 @@ "c", "cpp" ], - "tags":[ "Framework.Testing.Eryn" ], + "tags": [ + "Framework.Testing.Eryn" + ], "patterns": [ { "pattern": "eryn", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -45,13 +58,19 @@ "c", "cpp" ], - "tags":[ "Framework.Testing.Autounit" ], + "tags": [ + "Framework.Testing.Autounit" + ], "patterns": [ { "pattern": "autounit", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -64,13 +83,19 @@ "c", "cpp" ], - "tags":[ "Framework.Testing.Andit" ], + "tags": [ + "Framework.Testing.Andit" + ], "patterns": [ { "pattern": "andit", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -83,13 +108,19 @@ "c", "cpp" ], - "tags":[ "Framework.Testing.Boost" ], + "tags": [ + "Framework.Testing.Boost" + ], "patterns": [ { "pattern": "boost/test", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -102,13 +133,19 @@ "c", "cpp" ], - "tags":[ "Framework.Testing.BugEye" ], + "tags": [ + "Framework.Testing.BugEye" + ], "patterns": [ { "pattern": "bugeye", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -121,18 +158,23 @@ "c", "cpp" ], - "tags":[ "Framework.Testing.Catch2" ], + "tags": [ + "Framework.Testing.Catch2" + ], "patterns": [ { "pattern": "catch.hpp", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] }, - { "name": "Testing Framework: Catsrunner", "id": "AI041400", @@ -141,13 +183,19 @@ "c", "cpp" ], - "tags":[ "Framework.Testing.Catsrunner" ], + "tags": [ + "Framework.Testing.Catsrunner" + ], "patterns": [ { "pattern": "catsrunner", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -160,14 +208,20 @@ "c", "cpp" ], - "tags":[ "Framework.Testing.Cfix" ], + "tags": [ + "Framework.Testing.Cfix" + ], "_comment": "https://github.com/jpassing/cfix", "patterns": [ { "pattern": "cfix", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -180,13 +234,19 @@ "c", "cpp" ], - "tags":[ "Framework.Testing.Cgreen" ], + "tags": [ + "Framework.Testing.Cgreen" + ], "patterns": [ { "pattern": "cgreen", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] diff --git a/AppInspector/rules/default/test_frameworks/go_testing.json b/AppInspector/rules/default/test_frameworks/go_testing.json index 941f6d3..1d7da37 100644 --- a/AppInspector/rules/default/test_frameworks/go_testing.json +++ b/AppInspector/rules/default/test_frameworks/go_testing.json @@ -3,14 +3,22 @@ "name": "Testing Framework: Gmock", "id": "AI041700", "description": "Testing Framework: Gmock", - "applies_to":[ "go" ], - "tags":[ "Framework.Testing.Gmock" ], + "applies_to": [ + "go" + ], + "tags": [ + "Framework.Testing.Gmock" + ], "patterns": [ { "pattern": "Gmock", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] diff --git a/AppInspector/rules/default/test_frameworks/java_testing.json b/AppInspector/rules/default/test_frameworks/java_testing.json index 5f41f8a..4094b83 100644 --- a/AppInspector/rules/default/test_frameworks/java_testing.json +++ b/AppInspector/rules/default/test_frameworks/java_testing.json @@ -3,14 +3,22 @@ "name": "Testing Framework: Artos", "id": "AI046300", "description": "Testing Framework: Artos", - "applies_to":[ "java" ], - "tags":[ "Framework.Testing.Artos" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Testing.Artos" + ], "patterns": [ { "pattern": "artos", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -19,13 +27,19 @@ "name": "Testing Framework: Arquillian", "id": "AI046400", "description": "Testing Framework: Arquillian", - "applies_to":[ "java" ], - "tags":[ "Framework.Testing.Arquillian" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Testing.Arquillian" + ], "patterns": [ { "pattern": "Arquillian", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -34,13 +48,19 @@ "name": "Testing Framework: AssertJ", "id": "AI046500", "description": "Testing Framework: AssertJ", - "applies_to":[ "java" ], - "tags":[ "Framework.Testing.AssertJ" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Testing.AssertJ" + ], "patterns": [ { "pattern": "AssertJ", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -49,14 +69,22 @@ "name": "Testing Framework: BeanSpec", "id": "AI046600", "description": "Testing Framework: BeanSpec", - "applies_to":[ "java" ], - "tags":[ "Framework.Testing.BeanSpec" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Testing.BeanSpec" + ], "patterns": [ { "pattern": "beanspec", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -65,14 +93,22 @@ "name": "Testing Framework: BeanTest", "id": "AI046700", "description": "Testing Framework: BeanTest", - "applies_to":[ "java" ], - "tags":[ "Framework.Testing.BeanTest" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Testing.BeanTest" + ], "patterns": [ { "pattern": "BeanTest", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -81,14 +117,22 @@ "name": "Testing Framework: Cactus", "id": "AI046800", "description": "Testing Framework: Cactus", - "applies_to":[ "java" ], - "tags":[ "Framework.Testing.Cactus" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Testing.Cactus" + ], "patterns": [ { "pattern": "Cactus", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -97,14 +141,22 @@ "name": "Testing Framework: Concordion", "id": "AI046900", "description": "Testing Framework: Concordion", - "applies_to":[ "java" ], - "tags":[ "Framework.Testing.Concordion" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Testing.Concordion" + ], "patterns": [ { "pattern": "Concordion", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -113,14 +165,22 @@ "name": "Testing Framework: Concutest", "id": "AI047000", "description": "Testing Framework: Concutest", - "applies_to":[ "java" ], - "tags":[ "Framework.Testing.Concutest" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Testing.Concutest" + ], "patterns": [ { "pattern": "Concutest", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -129,14 +189,22 @@ "name": "Testing Framework: Cucumber", "id": "AI047100", "description": "Testing Framework: Cucumber", - "applies_to":[ "java" ], - "tags":[ "Framework.Testing.Cucumber" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Testing.Cucumber" + ], "patterns": [ { "pattern": "io.cucumber", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -145,14 +213,22 @@ "name": "Testing Framework: Cuppa", "id": "AI047200", "description": "Testing Framework: Cuppa", - "applies_to":[ "java" ], - "tags":[ "Framework.Testing.Cuppa" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Testing.Cuppa" + ], "patterns": [ { "pattern": "Cuppa", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -161,14 +237,22 @@ "name": "Testing Framework: DbUnit", "id": "AI047300", "description": "Testing Framework: DbUnit", - "applies_to":[ "java" ], - "tags":[ "Framework.Testing.DbUnit" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Testing.DbUnit" + ], "patterns": [ { "pattern": "DbUnit", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -177,14 +261,22 @@ "name": "Testing Framework: EasyMock", "id": "AI047400", "description": "Testing Framework: EasyMock", - "applies_to":[ "java" ], - "tags":[ "Framework.Testing.EasyMock" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Testing.EasyMock" + ], "patterns": [ { "pattern": "EasyMock", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -193,14 +285,22 @@ "name": "Testing Framework: EtlUnit", "id": "AI047500", "description": "Testing Framework: EtlUnit", - "applies_to":[ "java" ], - "tags":[ "Framework.Testing.EtlUnit" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Testing.EtlUnit" + ], "patterns": [ { "pattern": "EtlUnit", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -209,14 +309,22 @@ "name": "Testing Framework: EvoSuite", "id": "AI047600", "description": "Testing Framework: EvoSuite", - "applies_to":[ "java" ], - "tags":[ "Framework.Testing.EvoSuite" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Testing.EvoSuite" + ], "patterns": [ { "pattern": "EvoSuite", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -225,14 +333,23 @@ "name": "Testing Framework: GrandTestAuto", "id": "AI047700", "description": "Testing Framework: GrandTestAuto", - "applies_to":[ "java" ], - "tags":[ "Framework.Testing.GrandTestAuto" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Testing.GrandTestAuto" + ], "patterns": [ { "pattern": "GrandTestAuto", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -241,14 +358,22 @@ "name": "Testing Framework: GroboUtils", "id": "AI047800", "description": "Testing Framework: GroboUtils", - "applies_to":[ "java" ], - "tags":[ "Framework.Testing.GroboUtils" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Testing.GroboUtils" + ], "patterns": [ { "pattern": "GroboUtils", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -257,14 +382,22 @@ "name": "Testing Framework: HavaRunner", "id": "AI047900", "description": "Testing Framework: HavaRunner", - "applies_to":[ "java" ], - "tags":[ "Framework.Testing.HavaRunner" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Testing.HavaRunner" + ], "patterns": [ { "pattern": "HavaRunner", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -273,14 +406,22 @@ "name": "Testing Framework: Instinct", "id": "AI048000", "description": "Testing Framework: Instinct", - "applies_to":[ "java" ], - "tags":[ "Framework.Testing.Instinct" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Testing.Instinct" + ], "patterns": [ { "pattern": "Instinct", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "low" } ] @@ -289,14 +430,23 @@ "name": "Testing Framework: JSST", "id": "AI048100", "description": "Testing Framework: JSST", - "applies_to":[ "java" ], - "tags":[ "Framework.Testing.JSST" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Testing.JSST" + ], "patterns": [ { "pattern": "JSST", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -305,14 +455,23 @@ "name": "Testing Framework: JBehave", "id": "AI048200", "description": "Testing Framework: JBehave", - "applies_to":[ "java" ], - "tags":[ "Framework.Testing.JBehave" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Testing.JBehave" + ], "patterns": [ { "pattern": "JBehave", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -321,14 +480,22 @@ "name": "Testing Framework: JDave", "id": "AI048300", "description": "Testing Framework: JDave", - "applies_to":[ "java" ], - "tags":[ "Framework.Testing.JDave" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Testing.JDave" + ], "patterns": [ { "pattern": "JDave", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -337,14 +504,22 @@ "name": "Testing Framework: JExample", "id": "AI048400", "description": "Testing Framework: JExample", - "applies_to":[ "java" ], - "tags":[ "Framework.Testing.JExample" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Testing.JExample" + ], "patterns": [ { "pattern": "jexample", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -353,14 +528,22 @@ "name": "Testing Framework: JGiven", "id": "AI048500", "description": "Testing Framework: JGiven", - "applies_to":[ "java" ], - "tags":[ "Framework.Testing.JGiven" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Testing.JGiven" + ], "patterns": [ { "pattern": "jgiven", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -369,14 +552,22 @@ "name": "Testing Framework: JMock", "id": "AI048600", "description": "Testing Framework: JMock", - "applies_to":[ "java" ], - "tags":[ "Framework.Testing.JMock" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Testing.JMock" + ], "patterns": [ { "pattern": "jmock", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -385,14 +576,22 @@ "name": "Testing Framework: JMockit", "id": "AI048700", "description": "Testing Framework: JMockit", - "applies_to":[ "java" ], - "tags":[ "Framework.Testing.JMockit" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Testing.JMockit" + ], "patterns": [ { "pattern": "jmockit", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -401,14 +600,22 @@ "name": "Testing Framework: Jnario", "id": "AI048800", "description": "Testing Framework: Jnario", - "applies_to":[ "java" ], - "tags":[ "Framework.Testing.Jnario" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Testing.Jnario" + ], "patterns": [ { "pattern": "Jnario", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -417,14 +624,22 @@ "name": "Testing Framework: Jtest", "id": "AI048900", "description": "Testing Framework: Jtest", - "applies_to":[ "java" ], - "tags":[ "Framework.Testing.Jtest" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Testing.Jtest" + ], "patterns": [ { "pattern": "jtest", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -433,14 +648,22 @@ "name": "Testing Framework: Jukito", "id": "AI049000", "description": "Testing Framework: Jukito", - "applies_to":[ "java" ], - "tags":[ "Framework.Testing.Jukito" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Testing.Jukito" + ], "patterns": [ { "pattern": "jukito", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -449,14 +672,23 @@ "name": "Testing Framework: JUnit", "id": "AI049100", "description": "Testing Framework: JUnit", - "applies_to":[ "java" ], - "tags":[ "Framework.Testing.JUnit" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Testing.JUnit" + ], "patterns": [ { "pattern": "junit", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -465,14 +697,22 @@ "name": "Testing Framework: JUnitEE", "id": "AI049200", "description": "Testing Framework: JUnitEE", - "applies_to":[ "java" ], - "tags":[ "Framework.Testing.JUnitEE" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Testing.JUnitEE" + ], "patterns": [ { "pattern": "junitee", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -481,14 +721,22 @@ "name": "Testing Framework: JWalk", "id": "AI049300", "description": "Testing Framework: JWalk", - "applies_to":[ "java" ], - "tags":[ "Framework.Testing.JWalk" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Testing.JWalk" + ], "patterns": [ { "pattern": "jwalk", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -497,14 +745,22 @@ "name": "Testing Framework: Mockito", "id": "AI049400", "description": "Testing Framework: Mockito", - "applies_to":[ "java" ], - "tags":[ "Framework.Testing.Mockito" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Testing.Mockito" + ], "patterns": [ { "pattern": "mockito", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -513,14 +769,22 @@ "name": "Testing Framework: Mockrunner", "id": "AI049500", "description": "Testing Framework: Mockrunner", - "applies_to":[ "java" ], - "tags":[ "Framework.Testing.Mockrunner" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Testing.Mockrunner" + ], "patterns": [ { "pattern": "mockrunner", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -529,14 +793,22 @@ "name": "Testing Framework: Needle", "id": "AI049600", "description": "Testing Framework: Needle", - "applies_to":[ "java" ], - "tags":[ "Framework.Testing.Needle" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Testing.Needle" + ], "patterns": [ { "pattern": "needle", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -545,14 +817,22 @@ "name": "Testing Framework: NUTester", "id": "AI049700", "description": "Testing Framework: NUTester", - "applies_to":[ "java" ], - "tags":[ "Framework.Testing.NUTester" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Testing.NUTester" + ], "patterns": [ { "pattern": "NUTester", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -561,14 +841,22 @@ "name": "Testing Framework: OpenPojo", "id": "AI049800", "description": "Testing Framework: OpenPojo", - "applies_to":[ "java" ], - "tags":[ "Framework.Testing.OpenPojo" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Testing.OpenPojo" + ], "patterns": [ { "pattern": "openpojo", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -577,14 +865,22 @@ "name": "Testing Framework: PowerMock", "id": "AI049900", "description": "Testing Framework: PowerMock", - "applies_to":[ "java" ], - "tags":[ "Framework.Testing.PowerMock" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Testing.PowerMock" + ], "patterns": [ { "pattern": "powermock", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -593,14 +889,22 @@ "name": "Testing Framework: Randoop", "id": "AI050000", "description": "Testing Framework: Randoop", - "applies_to":[ "java" ], - "tags":[ "Framework.Testing.Randoop" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Testing.Randoop" + ], "patterns": [ { "pattern": "randoop", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -609,14 +913,22 @@ "name": "Testing Framework: Spock", "id": "AI050100", "description": "Testing Framework: Spock", - "applies_to":[ "java" ], - "tags":[ "Framework.Testing.Spock" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Testing.Spock" + ], "patterns": [ { "pattern": "spock", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "low" } ] @@ -625,14 +937,22 @@ "name": "Testing Framework: SpryTest", "id": "AI050200", "description": "Testing Framework: SpryTest", - "applies_to":[ "java" ], - "tags":[ "Framework.Testing.SpryTest" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Testing.SpryTest" + ], "patterns": [ { "pattern": "sprytest", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -641,14 +961,22 @@ "name": "Testing Framework:SureAssert ", "id": "AI050300", "description": "Testing Framework: SureAssert", - "applies_to":[ "java" ], - "tags":[ "Framework.Testing.SureAssert" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Testing.SureAssert" + ], "patterns": [ { "pattern": "sureassert", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -657,14 +985,22 @@ "name": "Testing Framework: Tacinga", "id": "AI050400", "description": "Testing Framework: Tacinga", - "applies_to":[ "java" ], - "tags":[ "Framework.Testing.Tacinga" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Testing.Tacinga" + ], "patterns": [ { "pattern": "tacinga", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -673,14 +1009,22 @@ "name": "Testing Framework: TestNG", "id": "AI050500", "description": "Testing Framework: TestNG", - "applies_to":[ "java" ], - "tags":[ "Framework.Testing.TestNG" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Testing.TestNG" + ], "patterns": [ { "pattern": "testng", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -689,14 +1033,22 @@ "name": "Testing Framework: Unitils", "id": "AI050600", "description": "Testing Framework: Unitils", - "applies_to":[ "java" ], - "tags":[ "Framework.Testing.Unitils" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Testing.Unitils" + ], "patterns": [ { "pattern": "unitils", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -705,14 +1057,22 @@ "name": "Testing Framework: XMLUnit", "id": "AI050700", "description": "Testing Framework: XMLUnit", - "applies_to":[ "java" ], - "tags":[ "Framework.Testing.XMLUnit" ], + "applies_to": [ + "java" + ], + "tags": [ + "Framework.Testing.XMLUnit" + ], "patterns": [ { "pattern": "xmunit", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] diff --git a/AppInspector/rules/default/test_frameworks/javascript_testing.json b/AppInspector/rules/default/test_frameworks/javascript_testing.json index 11f84ed..78ad3d7 100644 --- a/AppInspector/rules/default/test_frameworks/javascript_testing.json +++ b/AppInspector/rules/default/test_frameworks/javascript_testing.json @@ -3,13 +3,19 @@ "name": "Testing Framework: Ava", "id": "AI041800", "description": "Testing Framework: Ava", - "applies_to":[ "package.json" ], - "tags":[ "Framework.Testing.Ava" ], + "applies_to": [ + "package.json" + ], + "tags": [ + "Framework.Testing.Ava" + ], "patterns": [ { "pattern": "(^|\")ava[@\"]", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "modifiers": [ ], "confidence": "high" @@ -20,14 +26,23 @@ "name": "Testing Framework: DOH", "id": "AI041900", "description": "Testing Framework: DOH", - "applies_to":[ "javascript" ], - "tags":[ "Framework.Testing.DOH" ], + "applies_to": [ + "javascript" + ], + "tags": [ + "Framework.Testing.DOH" + ], "patterns": [ { "pattern": "require\\(['\\\"]doh['\\\"]\\)", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -36,14 +51,23 @@ "name": "Testing Framework: JSUnit", "id": "AI042000", "description": "Testing Framework: JSUnit", - "applies_to":[ "javascript" ], - "tags":[ "Framework.Testing.JSUnit" ], + "applies_to": [ + "javascript" + ], + "tags": [ + "Framework.Testing.JSUnit" + ], "patterns": [ { "pattern": "jsunit", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -52,14 +76,23 @@ "name": "Testing Framework: EnhanceJS", "id": "AI042100", "description": "Testing Framework: EnhanceJS", - "applies_to":[ "javascript" ], - "tags":[ "Framework.Testing.EnhanceJS" ], + "applies_to": [ + "javascript" + ], + "tags": [ + "Framework.Testing.EnhanceJS" + ], "patterns": [ { "pattern": "enhancejs", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -68,14 +101,23 @@ "name": "Testing Framework: QUnit", "id": "AI042200", "description": "Testing Framework: QUnit", - "applies_to":[ "javascript" ], - "tags":[ "Framework.Testing.QUnit" ], + "applies_to": [ + "javascript" + ], + "tags": [ + "Framework.Testing.QUnit" + ], "patterns": [ { "pattern": "qunit", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -84,14 +126,23 @@ "name": "Testing Framework: UnitJS", "id": "AI042300", "description": "Testing Framework: UnitJS", - "applies_to":[ "javascript" ], - "tags":[ "Framework.Testing.UnitJS" ], + "applies_to": [ + "javascript" + ], + "tags": [ + "Framework.Testing.UnitJS" + ], "patterns": [ { "pattern": "unit.js", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -100,14 +151,23 @@ "name": "Testing Framework: RHUnit", "id": "AI042400", "description": "Testing Framework: RHUnit", - "applies_to":[ "javascript" ], - "tags":[ "Framework.Testing.RhUnit" ], + "applies_to": [ + "javascript" + ], + "tags": [ + "Framework.Testing.RhUnit" + ], "patterns": [ { "pattern": "rhunit", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -116,14 +176,23 @@ "name": "Testing Framework: Crosscheck", "id": "AI042500", "description": "Testing Framework: Crosscheck", - "applies_to":[ "javascript" ], - "tags":[ "Framework.Testing.Crosscheck" ], + "applies_to": [ + "javascript" + ], + "tags": [ + "Framework.Testing.Crosscheck" + ], "patterns": [ { "pattern": "crosscheck", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -132,14 +201,23 @@ "name": "Testing Framework: J3Unit", "id": "AI042600", "description": "Testing Framework: J3Unit", - "applies_to":[ "javascript" ], - "tags":[ "Framework.Testing.J3Unit" ], + "applies_to": [ + "javascript" + ], + "tags": [ + "Framework.Testing.J3Unit" + ], "patterns": [ { "pattern": "j3unit", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -148,14 +226,23 @@ "name": "Testing Framework: Mocha", "id": "AI042700", "description": "Testing Framework: Mocha", - "applies_to":[ "javascript" ], - "tags":[ "Framework.Testing.Mocha" ], + "applies_to": [ + "javascript" + ], + "tags": [ + "Framework.Testing.Mocha" + ], "patterns": [ { "pattern": "mocha", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -164,14 +251,23 @@ "name": "Testing Framework: Intern", "id": "AI042800", "description": "Testing Framework: Intern", - "applies_to":[ "javascript" ], - "tags":[ "Framework.Testing.Intern" ], + "applies_to": [ + "javascript" + ], + "tags": [ + "Framework.Testing.Intern" + ], "patterns": [ { "pattern": "intern", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -180,14 +276,23 @@ "name": "Testing Framework: JSNUnit", "id": "AI042900", "description": "Testing Framework: JSNUnit", - "applies_to":[ "javascript" ], - "tags":[ "Framework.Testing.JSNUnit" ], + "applies_to": [ + "javascript" + ], + "tags": [ + "Framework.Testing.JSNUnit" + ], "patterns": [ { "pattern": "jsnunit", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -196,14 +301,23 @@ "name": "Testing Framework: YUITest", "id": "AI043000", "description": "Testing Framework: YUITest", - "applies_to":[ "javascript" ], - "tags":[ "Framework.Testing.YUITest" ], + "applies_to": [ + "javascript" + ], + "tags": [ + "Framework.Testing.YUITest" + ], "patterns": [ { "pattern": "yuitest", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -212,14 +326,23 @@ "name": "Testing Framework: JSSpec", "id": "AI043100", "description": "Testing Framework: JSSpec", - "applies_to":[ "javascript" ], - "tags":[ "Framework.Testing.JSSpec" ], + "applies_to": [ + "javascript" + ], + "tags": [ + "Framework.Testing.JSSpec" + ], "patterns": [ { "pattern": "jsspec", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -228,14 +351,23 @@ "name": "Testing Framework: UnitTesting", "id": "AI043200", "description": "Testing Framework: UnitTesting", - "applies_to":[ "javascript" ], - "tags":[ "Framework.Testing.UnitTesting" ], + "applies_to": [ + "javascript" + ], + "tags": [ + "Framework.Testing.UnitTesting" + ], "patterns": [ { "pattern": "unittesting", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -244,14 +376,23 @@ "name": "Testing Framework: JSpec", "id": "AI043300", "description": "Testing Framework: JSpec", - "applies_to":[ "javascript" ], - "tags":[ "Framework.Testing.JSpec" ], + "applies_to": [ + "javascript" + ], + "tags": [ + "Framework.Testing.JSpec" + ], "patterns": [ { "pattern": "jspec", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -260,14 +401,23 @@ "name": "Testing Framework: Jasmine", "id": "AI043400", "description": "Testing Framework: Jasmine", - "applies_to":[ "javascript" ], - "tags":[ "Framework.Testing.Jasmine" ], + "applies_to": [ + "javascript" + ], + "tags": [ + "Framework.Testing.Jasmine" + ], "patterns": [ { "pattern": "jasmine", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -276,14 +426,23 @@ "name": "Testing Framework: Screw.Unit", "id": "AI043500", "description": "Testing Framework: Screw.Unit", - "applies_to":[ "javascript" ], - "tags":[ "Framework.Testing.screw-unit" ], + "applies_to": [ + "javascript" + ], + "tags": [ + "Framework.Testing.screw-unit" + ], "patterns": [ { "pattern": "screw-unit", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -292,14 +451,23 @@ "name": "Testing Framework: Tape", "id": "AI043600", "description": "Testing Framework: Tape", - "applies_to":[ "javascript" ], - "tags":[ "Framework.Testing.Tape" ], + "applies_to": [ + "javascript" + ], + "tags": [ + "Framework.Testing.Tape" + ], "patterns": [ { "pattern": "require\\s*\\(\\s*['\\\"]tape['\\\"]\\)", "type": "regex", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -308,14 +476,23 @@ "name": "Testing Framework: Teenytest", "id": "AI043700", "description": "Testing Framework: Teenytest", - "applies_to":[ "javascript" ], - "tags":[ "Framework.Testing.Teenytest" ], + "applies_to": [ + "javascript" + ], + "tags": [ + "Framework.Testing.Teenytest" + ], "patterns": [ { "pattern": "teenytest", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -324,14 +501,23 @@ "name": "Testing Framework: Simple", "id": "AI043800", "description": "Testing Framework: Simple", - "applies_to":[ "javascript" ], - "tags":[ "Framework.Testing.Test.Simple" ], + "applies_to": [ + "javascript" + ], + "tags": [ + "Framework.Testing.Test.Simple" + ], "patterns": [ { "pattern": "test.simple", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -340,14 +526,23 @@ "name": "Testing Framework: TestCase", "id": "AI043900", "description": "Testing Framework: TestCase", - "applies_to":[ "javascript" ], - "tags":[ "Framework.Testing.TestCase" ], + "applies_to": [ + "javascript" + ], + "tags": [ + "Framework.Testing.TestCase" + ], "patterns": [ { "pattern": "testcase", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -356,14 +551,23 @@ "name": "Testing Framework: TestIt", "id": "AI044000", "description": "Testing Framework: TestIt", - "applies_to":[ "javascript" ], - "tags":[ "Framework.Testing.TestIt" ], + "applies_to": [ + "javascript" + ], + "tags": [ + "Framework.Testing.TestIt" + ], "patterns": [ { "pattern": "testit", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -372,14 +576,23 @@ "name": "Testing Framework: testdouble.js", "id": "AI044100", "description": "Testing Framework: testdouble.js", - "applies_to":[ "javascript" ], - "tags":[ "Framework.Testing.testdouble.js" ], + "applies_to": [ + "javascript" + ], + "tags": [ + "Framework.Testing.testdouble.js" + ], "patterns": [ { "pattern": "testdouble.js", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -388,14 +601,23 @@ "name": "Testing Framework: jsUnitTest", "id": "AI044200", "description": "Testing Framework: jsUnitTest", - "applies_to":[ "javascript" ], - "tags":[ "Framework.Testing.jsUnitTest" ], + "applies_to": [ + "javascript" + ], + "tags": [ + "Framework.Testing.jsUnitTest" + ], "patterns": [ { "pattern": "jsunittest", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -404,14 +626,23 @@ "name": "Testing Framework: JSTest", "id": "AI044300", "description": "Testing Framework: JSTest", - "applies_to":[ "javascript" ], - "tags":[ "Framework.Testing.JSTest" ], + "applies_to": [ + "javascript" + ], + "tags": [ + "Framework.Testing.JSTest" + ], "patterns": [ { "pattern": "JSTest.net", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -420,14 +651,23 @@ "name": "Testing Framework: jsUnity", "id": "AI044400", "description": "Testing Framework: jsUnity", - "applies_to":[ "javascript" ], - "tags":[ "Framework.Testing.jsUnity" ], + "applies_to": [ + "javascript" + ], + "tags": [ + "Framework.Testing.jsUnity" + ], "patterns": [ { "pattern": "jsunity", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -436,14 +676,23 @@ "name": "Testing Framework: RhinoUnit", "id": "AI044500", "description": "Testing Framework: RhinoUnit", - "applies_to":[ "javascript" ], - "tags":[ "Framework.Testing.RhinoUnit" ], + "applies_to": [ + "javascript" + ], + "tags": [ + "Framework.Testing.RhinoUnit" + ], "patterns": [ { "pattern": "rhinounit", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -452,14 +701,23 @@ "name": "Testing Framework: JasUnit", "id": "AI044600", "description": "Testing Framework: JasUnit", - "applies_to":[ "javascript" ], - "tags":[ "Framework.Testing.JasUnit" ], + "applies_to": [ + "javascript" + ], + "tags": [ + "Framework.Testing.JasUnit" + ], "patterns": [ { "pattern": "jasunit", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -468,14 +726,22 @@ "name": "Testing Framework: FireUnit", "id": "AI044700", "description": "Testing Framework: FireUnit", - "applies_to":[ "javascript" ], - "tags":[ "Framework.Testing.FireUnit" ], + "applies_to": [ + "javascript" + ], + "tags": [ + "Framework.Testing.FireUnit" + ], "patterns": [ { "pattern": "fireunit", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -484,14 +750,23 @@ "name": "Testing Framework: JSTestDriver", "id": "AI044800", "description": "Testing Framework: JSTestDriver", - "applies_to":[ "javascript" ], - "tags":[ "Framework.Testing.JSTestDriver" ], + "applies_to": [ + "javascript" + ], + "tags": [ + "Framework.Testing.JSTestDriver" + ], "patterns": [ { "pattern": "js-test-driver", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -500,14 +775,23 @@ "name": "Testing Framework: JS Test Runner", "id": "AI044900", "description": "Testing Framework: JS Test Runner", - "applies_to":[ "javascript" ], - "tags":[ "Framework.Testing.Js-test-runner" ], + "applies_to": [ + "javascript" + ], + "tags": [ + "Framework.Testing.Js-test-runner" + ], "patterns": [ { "pattern": "js-test-runner", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -516,14 +800,23 @@ "name": "Testing Framework: SinonJS", "id": "AI045000", "description": "Testing Framework: SinonJS", - "applies_to":[ "javascript" ], - "tags":[ "Framework.Testing.SinonJS" ], + "applies_to": [ + "javascript" + ], + "tags": [ + "Framework.Testing.SinonJS" + ], "patterns": [ { "pattern": "sinon.js", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -532,14 +825,23 @@ "name": "Testing Framework: SOAtest", "id": "AI045100", "description": "Testing Framework: SOAtest", - "applies_to":[ "javascript" ], - "tags":[ "Framework.Testing.SOAtest" ], + "applies_to": [ + "javascript" + ], + "tags": [ + "Framework.Testing.SOAtest" + ], "patterns": [ { "pattern": "soatest", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -548,14 +850,23 @@ "name": "Testing Framework: Vows", "id": "AI045200", "description": "Testing Framework: Vows", - "applies_to":[ "javascript" ], - "tags":[ "Framework.Testing.Vows" ], + "applies_to": [ + "javascript" + ], + "tags": [ + "Framework.Testing.Vows" + ], "patterns": [ { "pattern": "vows", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -564,14 +875,23 @@ "name": "Testing Framework: Nodeunit", "id": "AI045300", "description": "Testing Framework: Nodeunit", - "applies_to":[ "javascript" ], - "tags":[ "Framework.Testing.Nodeunit" ], + "applies_to": [ + "javascript" + ], + "tags": [ + "Framework.Testing.Nodeunit" + ], "patterns": [ { "pattern": "nodeunit", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -580,14 +900,23 @@ "name": "Testing Framework: Tyrtle", "id": "AI045400", "description": "Testing Framework: Tyrtle", - "applies_to":[ "javascript" ], - "tags":[ "Framework.Testing.Tyrtle" ], + "applies_to": [ + "javascript" + ], + "tags": [ + "Framework.Testing.Tyrtle" + ], "patterns": [ { "pattern": "tyrtle", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -596,15 +925,24 @@ "name": "Testing Framework: WRU", "id": "AI045500", "description": "Testing Framework: WRU", - "applies_to":[ "javascript" ], - "tags":[ "Framework.Testing.wru" ], + "applies_to": [ + "javascript" + ], + "tags": [ + "Framework.Testing.wru" + ], "_comment": "https://github.com/WebReflection/wru", "patterns": [ { "pattern": "wru", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -613,14 +951,23 @@ "name": "Testing Framework: BusterJS", "id": "AI045600", "description": "Testing Framework: BusterJS", - "applies_to":[ "javascript" ], - "tags":[ "Framework.Testing.BusterJS" ], + "applies_to": [ + "javascript" + ], + "tags": [ + "Framework.Testing.BusterJS" + ], "patterns": [ { "pattern": "buster.js", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -629,14 +976,23 @@ "name": "Testing Framework: Lighttest", "id": "AI045700", "description": "Testing Framework: Lighttest", - "applies_to":[ "javascript" ], - "tags":[ "Framework.Testing.Lighttest" ], + "applies_to": [ + "javascript" + ], + "tags": [ + "Framework.Testing.Lighttest" + ], "patterns": [ { "pattern": "lighttest", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -645,14 +1001,23 @@ "name": "Testing Framework: Chai", "id": "AI045800", "description": "Testing Framework: Chai", - "applies_to":[ "javascript" ], - "tags":[ "Framework.Testing.Chai" ], + "applies_to": [ + "javascript" + ], + "tags": [ + "Framework.Testing.Chai" + ], "patterns": [ { "pattern": "chai", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -661,14 +1026,23 @@ "name": "Testing Framework: JSUS", "id": "AI045900", "description": "Testing Framework: JSUS", - "applies_to":[ "javascript" ], - "tags":[ "Framework.Testing.JSUS" ], + "applies_to": [ + "javascript" + ], + "tags": [ + "Framework.Testing.JSUS" + ], "patterns": [ { "pattern": "jsus", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -677,14 +1051,23 @@ "name": "Testing Framework: WallabyJS", "id": "AI046000", "description": "Testing Framework: WallabyJS", - "applies_to":[ "javascript" ], - "tags":[ "Framework.Testing.WallabyJS" ], + "applies_to": [ + "javascript" + ], + "tags": [ + "Framework.Testing.WallabyJS" + ], "patterns": [ { "pattern": "wallaby.js", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -693,14 +1076,23 @@ "name": "Testing Framework: Fast-Check", "id": "AI046100", "description": "Testing Framework: Fast-Check", - "applies_to":[ "javascript" ], - "tags":[ "Framework.Testing.fast-check" ], + "applies_to": [ + "javascript" + ], + "tags": [ + "Framework.Testing.fast-check" + ], "patterns": [ { "pattern": "fast-check", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -709,14 +1101,23 @@ "name": "Testing Framework: Jest", "id": "AI046200", "description": "Testing Framework: Jest", - "applies_to":[ "javascript" ], - "tags":[ "Framework.Testing.Jest" ], + "applies_to": [ + "javascript" + ], + "tags": [ + "Framework.Testing.Jest" + ], "patterns": [ { "pattern": "jest", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] diff --git a/AppInspector/rules/default/test_frameworks/objectiveC_testing.json b/AppInspector/rules/default/test_frameworks/objectiveC_testing.json index fb892c7..3ef39a4 100644 --- a/AppInspector/rules/default/test_frameworks/objectiveC_testing.json +++ b/AppInspector/rules/default/test_frameworks/objectiveC_testing.json @@ -3,14 +3,22 @@ "name": "Testing Framework: GHUnit", "id": "AI050800", "description": "Testing Framework: GHUnit", - "applies_to":[ "objective-c" ], - "tags":[ "Framework.Testing.GHUnit" ], + "applies_to": [ + "objective-c" + ], + "tags": [ + "Framework.Testing.GHUnit" + ], "patterns": [ { "pattern": "GHUnit", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -19,14 +27,22 @@ "name": "Testing Framework: Cedar", "id": "AI051000", "description": "Testing Framework: Cedar", - "applies_to":[ "objective-c" ], - "tags":[ "Framework.Testing.Cedar" ], + "applies_to": [ + "objective-c" + ], + "tags": [ + "Framework.Testing.Cedar" + ], "patterns": [ { "pattern": "Cedar", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -35,14 +51,22 @@ "name": "Testing Framework: Kiwi", "id": "AI051100", "description": "Testing Framework: Kiwi", - "applies_to":[ "objective-c" ], - "tags":[ "Framework.Testing.Kiwi" ], + "applies_to": [ + "objective-c" + ], + "tags": [ + "Framework.Testing.Kiwi" + ], "patterns": [ { "pattern": "kiwi", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -51,14 +75,23 @@ "name": "Testing Framework: Specta", "id": "AI051200", "description": "Testing Framework: Specta", - "applies_to":[ "objective-c" ], - "tags":[ "Framework.Testing.Specta" ], + "applies_to": [ + "objective-c" + ], + "tags": [ + "Framework.Testing.Specta" + ], "patterns": [ { "pattern": "specta", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -67,14 +100,22 @@ "name": "Testing Framework: Quick", "id": "AI051300", "description": "Testing Framework: Quick", - "applies_to":[ "objective-c" ], - "tags":[ "Framework.Testing.quick" ], + "applies_to": [ + "objective-c" + ], + "tags": [ + "Framework.Testing.quick" + ], "patterns": [ { "pattern": "Quick", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -83,14 +124,22 @@ "name": "Testing Framework: ObjcUnit", "id": "AI051400", "description": "Testing Framework: ObjcUnit", - "applies_to":[ "objective-c" ], - "tags":[ "Framework.Testing.ObjcUnit" ], + "applies_to": [ + "objective-c" + ], + "tags": [ + "Framework.Testing.ObjcUnit" + ], "patterns": [ { "pattern": "objcunit", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -99,14 +148,22 @@ "name": "Testing Framework: OCUnit", "id": "AI051500", "description": "Testing Framework: OCUnit", - "applies_to":[ "objective-c" ], - "tags":[ "Framework.Testing.OCUnit" ], + "applies_to": [ + "objective-c" + ], + "tags": [ + "Framework.Testing.OCUnit" + ], "patterns": [ { "pattern": "ocunit", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -115,14 +172,22 @@ "name": "Testing Framework: WOTest", "id": "AI051600", "description": "Testing Framework: WOTest", - "applies_to":[ "objective-c" ], - "tags":[ "Framework.Testing.WOTest" ], + "applies_to": [ + "objective-c" + ], + "tags": [ + "Framework.Testing.WOTest" + ], "patterns": [ { "pattern": "wotest", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -131,14 +196,23 @@ "name": "Testing Framework: XCTest", "id": "AI051700", "description": "Testing Framework: XCTest", - "applies_to":[ "objective-c" ], - "tags":[ "Framework.Testing.XCTest" ], + "applies_to": [ + "objective-c" + ], + "tags": [ + "Framework.Testing.XCTest" + ], "patterns": [ { "pattern": "xctest", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] diff --git a/AppInspector/rules/default/test_frameworks/powershell_testing.json b/AppInspector/rules/default/test_frameworks/powershell_testing.json index 2fd3ff8..1116541 100644 --- a/AppInspector/rules/default/test_frameworks/powershell_testing.json +++ b/AppInspector/rules/default/test_frameworks/powershell_testing.json @@ -3,14 +3,23 @@ "name": "Testing Framework: Pester", "id": "AI051800", "description": "Testing Framework: Pester", - "applies_to":[ "powershell" ], - "tags":[ "Framework.Testing.Pester" ], + "applies_to": [ + "powershell" + ], + "tags": [ + "Framework.Testing.Pester" + ], "patterns": [ { "pattern": "ester", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ] + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ] } ] } diff --git a/AppInspector/rules/default/test_frameworks/python_testing.json b/AppInspector/rules/default/test_frameworks/python_testing.json index 3923c47..2b48c83 100644 --- a/AppInspector/rules/default/test_frameworks/python_testing.json +++ b/AppInspector/rules/default/test_frameworks/python_testing.json @@ -3,14 +3,22 @@ "name": "Testing Framework: AutoTest", "id": "AI051900", "description": "Testing Framework: AutoTest", - "applies_to":[ "python" ], - "tags":[ "Framework.Testing.AutoTest" ], + "applies_to": [ + "python" + ], + "tags": [ + "Framework.Testing.AutoTest" + ], "patterns": [ { "pattern": "autotest", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -19,13 +27,19 @@ "name": "Testing Framework: UnitTest", "id": "AI052000", "description": "Testing Framework: UnitTest", - "applies_to":[ "python" ], - "tags":[ "Framework.Testing.unittest" ], + "applies_to": [ + "python" + ], + "tags": [ + "Framework.Testing.unittest" + ], "patterns": [ { "pattern": "unittest", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -34,13 +48,19 @@ "name": "Testing Framework: XPyUnit", "id": "AI052100", "description": "Testing Framework: XPyUnit", - "applies_to":[ "python" ], - "tags":[ "Framework.Testing.XPyUnit" ], + "applies_to": [ + "python" + ], + "tags": [ + "Framework.Testing.XPyUnit" + ], "patterns": [ { "pattern": "xpyunit", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -49,13 +69,19 @@ "name": "Testing Framework: DocTest", "id": "AI052200", "description": "Testing Framework: DocTest", - "applies_to":[ "python" ], - "tags":[ "Framework.Testing.Doctest" ], + "applies_to": [ + "python" + ], + "tags": [ + "Framework.Testing.Doctest" + ], "patterns": [ { "pattern": "doctest", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -64,13 +90,19 @@ "name": "Testing Framework: Nose", "id": "AI052300", "description": "Testing Framework: Nose", - "applies_to":[ "python" ], - "tags":[ "Framework.Testing.Nose" ], + "applies_to": [ + "python" + ], + "tags": [ + "Framework.Testing.Nose" + ], "patterns": [ { "pattern": "import .*nose|nosetests|nose\\.run", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -79,13 +111,19 @@ "name": "Testing Framework: PyTest", "id": "AI052400", "description": "Testing Framework: PyTest", - "applies_to":[ "python" ], - "tags":[ "Framework.Testing.PyTest" ], + "applies_to": [ + "python" + ], + "tags": [ + "Framework.Testing.PyTest" + ], "patterns": [ { "pattern": "pytest", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -94,13 +132,19 @@ "name": "Testing Framework: Should DSL", "id": "AI052500", "description": "Testing Framework: Should DSL", - "applies_to":[ "python" ], - "tags":[ "Framework.Testing.ShouldDSL" ], + "applies_to": [ + "python" + ], + "tags": [ + "Framework.Testing.ShouldDSL" + ], "patterns": [ { "pattern": "should_dsl", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] diff --git a/AppInspector/rules/default/test_frameworks/ruby_testing.json b/AppInspector/rules/default/test_frameworks/ruby_testing.json index 029e32b..249110c 100644 --- a/AppInspector/rules/default/test_frameworks/ruby_testing.json +++ b/AppInspector/rules/default/test_frameworks/ruby_testing.json @@ -3,14 +3,22 @@ "name": "Testing Framework: TestUnit", "id": "AI052600", "description": "Testing Framework: TestUnit", - "applies_to":[ "ruby" ], - "tags":[ "Framework.Testing.TestUnit" ], + "applies_to": [ + "ruby" + ], + "tags": [ + "Framework.Testing.TestUnit" + ], "patterns": [ { "pattern": "test::unit", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -19,14 +27,23 @@ "name": "Testing Framework: RSpec", "id": "AI052700", "description": "Testing Framework: RSpec", - "applies_to":[ "ruby" ], - "tags":[ "Framework.Testing.RSpec" ], + "applies_to": [ + "ruby" + ], + "tags": [ + "Framework.Testing.RSpec" + ], "patterns": [ { "pattern": "rspec", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -35,14 +52,23 @@ "name": "Testing Framework: Shoulda", "id": "AI052800", "description": "Testing Framework: Shoulda", - "applies_to":[ "ruby" ], - "tags":[ "Framework.Testing.Shoulda" ], + "applies_to": [ + "ruby" + ], + "tags": [ + "Framework.Testing.Shoulda" + ], "patterns": [ { "pattern": "shoulda", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -51,14 +77,23 @@ "name": "Testing Framework: MicroTest", "id": "AI052900", "description": "Testing Framework: MicroTest", - "applies_to":[ "ruby" ], - "tags":[ "Framework.Testing.Microtest" ], + "applies_to": [ + "ruby" + ], + "tags": [ + "Framework.Testing.Microtest" + ], "patterns": [ { "pattern": "microtest", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -67,14 +102,23 @@ "name": "Testing Framework: Bacon", "id": "AI053000", "description": "Testing Framework: Bacon", - "applies_to":[ "ruby" ], - "tags":[ "Framework.Testing.Bacon" ], + "applies_to": [ + "ruby" + ], + "tags": [ + "Framework.Testing.Bacon" + ], "patterns": [ { "pattern": "bacon", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -83,14 +127,23 @@ "name": "Testing Framework: MiniTest", "id": "AI053100", "description": "Testing Framework: MiniTest", - "applies_to":[ "ruby" ], - "tags":[ "Framework.Testing.Minitest" ], + "applies_to": [ + "ruby" + ], + "tags": [ + "Framework.Testing.Minitest" + ], "patterns": [ { "pattern": "minitest", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -99,14 +152,23 @@ "name": "Testing Framework: TMF", "id": "AI053200", "description": "Testing Framework: TMF", - "applies_to":[ "ruby" ], - "tags":[ "Framework.Testing.TMF" ], + "applies_to": [ + "ruby" + ], + "tags": [ + "Framework.Testing.TMF" + ], "patterns": [ { "pattern": "TMF", "type": "string", - "scopes": [ "code", "comment" ], - "modifiers": [ "i" ], + "scopes": [ + "code", + "comment" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] diff --git a/AppInspector/rules/default/tools/pipeline.json b/AppInspector/rules/default/tools/pipeline.json index dc29503..8a02a84 100644 --- a/AppInspector/rules/default/tools/pipeline.json +++ b/AppInspector/rules/default/tools/pipeline.json @@ -3,14 +3,20 @@ "name": "Pipeline Tool (AntiMalware)", "id": "AI053300", "description": "General AntiMalware", - "tags":[ "Pipeline.StaticAnalysis.AntiMalware" ], + "tags": [ + "Pipeline.StaticAnalysis.AntiMalware" + ], "severity": "critical", "patterns": [ { "pattern": "AntiMalware", "type": "string", - "scopes": [ "code"], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -19,14 +25,20 @@ "name": "Pipeline Tool (Bandit)", "id": "AI053400", "description": "Finds vulnerabilities in Python code.", - "tags":[ "Pipeline.StaticAnalysis.Bandit" ], + "tags": [ + "Pipeline.StaticAnalysis.Bandit" + ], "severity": "critical", "patterns": [ { "pattern": "Bandit", "type": "string", - "scopes": [ "code"], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -35,14 +47,20 @@ "name": "Pipeline Tool (BinSkim)", "id": "AI053500", "description": "Validates compiler and linker security settings.", - "tags":[ "Pipeline.CompilerSettings.BinSkim" ], + "tags": [ + "Pipeline.CompilerSettings.BinSkim" + ], "severity": "critical", "patterns": [ { "pattern": "BinSkim", "type": "string", - "scopes": [ "code"], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -51,14 +69,20 @@ "name": "Pipeline Tool (CodeSigning)", "id": "AI053600", "description": "General code signing.", - "tags":[ "Pipeline.Packaging.CodeSigning" ], + "tags": [ + "Pipeline.Packaging.CodeSigning" + ], "severity": "critical", "patterns": [ { "pattern": "CodeSign", "type": "string", - "scopes": [ "code"], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high", "_comment": "uses regex to support 'codesigning' or other variations" } @@ -68,14 +92,20 @@ "name": "Pipeline Tool (CodeQL)", "id": "AI053700", "description": "Identifies patterns in code.", - "tags":[ "Pipeline.StaticAnalysis.CodeQL" ], + "tags": [ + "Pipeline.StaticAnalysis.CodeQL" + ], "severity": "critical", "patterns": [ { "pattern": "CodeQL", "type": "string", - "scopes": [ "code"], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -84,14 +114,20 @@ "name": "Pipeline Tool (ESLint)", "id": "AI053800", "description": "Identifies problematic coding patterns in JavaScript.", - "tags":[ "Pipeline.Linter.ESLint" ], + "tags": [ + "Pipeline.Linter.ESLint" + ], "severity": "critical", "patterns": [ { "pattern": "ESLint", "type": "string", - "scopes": [ "code"], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -100,14 +136,20 @@ "name": "Pipeline Tool (FxCop)", "id": "AI053900", "description": "Analyzer that checks conformance to Microsoft .NET Design Guidelines.", - "tags":[ "Pipeline.StaticAnalysis.FxCop" ], + "tags": [ + "Pipeline.StaticAnalysis.FxCop" + ], "severity": "critical", "patterns": [ { "pattern": "FxCop", "type": "string", - "scopes": [ "code"], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -116,14 +158,20 @@ "name": "Pipeline Tool (ModernCop)", "id": "AI054000", "description": "Analyzer that checks for security vulnerabilities in .NET code.", - "tags":[ "Pipeline.StaticAnalysis.ModernCop" ], + "tags": [ + "Pipeline.StaticAnalysis.ModernCop" + ], "severity": "critical", "patterns": [ { "pattern": "ModernCop", "type": "string", - "scopes": [ "code"], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -132,14 +180,20 @@ "name": "Pipeline Tool (NuGetCommand)", "id": "AI054100", "description": "Identifies package manager commands for NuGet.", - "tags":[ "Pipeline.Packaging.NuGetCommand" ], + "tags": [ + "Pipeline.Packaging.NuGetCommand" + ], "severity": "critical", "patterns": [ { "pattern": "NuGetCommand", "type": "string", - "scopes": [ "code"], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -148,14 +202,20 @@ "name": "Pipeline Tool (RoslynAnalyzers)", "id": "AI054200", "description": "Identifies style, quality, design and other code characteristics.", - "tags":[ "Pipeline.StaticAnalysis.RoslynAnalyzers" ], + "tags": [ + "Pipeline.StaticAnalysis.RoslynAnalyzers" + ], "severity": "critical", "patterns": [ { "pattern": "RoslynAnalyzers", "type": "string", - "scopes": [ "code"], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -164,14 +224,20 @@ "name": "Pipeline Tool (SpotBugs)", "id": "AI054300", "description": "Vulnerability detection for Java code.", - "tags":[ "Pipeline.StaticAnalysis.SpotBugs" ], + "tags": [ + "Pipeline.StaticAnalysis.SpotBugs" + ], "severity": "critical", "patterns": [ { "pattern": "SpotBugs", "type": "string", - "scopes": [ "code"], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -180,14 +246,20 @@ "name": "Pipeline Tool (TSLint)", "id": "AI054400", "description": "Checks TypeScript for readability and functionality errors.", - "tags":[ "Pipeline.Linter.TSLint" ], + "tags": [ + "Pipeline.Linter.TSLint" + ], "severity": "critical", "patterns": [ { "pattern": "TSLint", "type": "string", - "scopes": [ "code"], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] diff --git a/AppInspector/rules/default/webapp/comms.json b/AppInspector/rules/default/webapp/comms.json index c34ba22..7f29c88 100644 --- a/AppInspector/rules/default/webapp/comms.json +++ b/AppInspector/rules/default/webapp/comms.json @@ -3,7 +3,9 @@ "name": "Web App Communications Post Message", "id": "AI054500", "description": "Web App Communications Post Message", - "tags":[ "WebApp.Communications.PostMessage" ], + "tags": [ + "WebApp.Communications.PostMessage" + ], "applies_to": [ "javascript", "csharp", @@ -22,8 +24,12 @@ { "pattern": "window\\.parent\\.postMessage|contentWindow\\.postMessage", "type": "regexword", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -32,7 +38,9 @@ "name": "Web App Communications Sockets", "id": "AI054600", "description": "Web App Communications Sockets", - "tags":[ "WebApp.Communications.Sockets" ], + "tags": [ + "WebApp.Communications.Sockets" + ], "applies_to": [ "javascript", "csharp", @@ -51,8 +59,12 @@ { "pattern": "WebSocket", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -61,7 +73,9 @@ "name": "Web App Communications Server Events", "id": "AI054700", "description": "Web App Communications Server Events", - "tags":[ "WebApp.Communications.ServerEvent" ], + "tags": [ + "WebApp.Communications.ServerEvent" + ], "applies_to": [ "javascript", "csharp", @@ -80,9 +94,13 @@ { "pattern": "EventSource", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high", - "modifiers": [ "i" ] + "modifiers": [ + "i" + ] } ] } diff --git a/AppInspector/rules/default/webapp/headers.json b/AppInspector/rules/default/webapp/headers.json index 122e61e..d4f5ece 100644 --- a/AppInspector/rules/default/webapp/headers.json +++ b/AppInspector/rules/default/webapp/headers.json @@ -3,14 +3,20 @@ "name": "Web App Headers HSTS", "id": "AI054800", "description": "Web App Headers Strict-Transport Security (HSTS)", - "tags":[ "WebApp.Headers.HSTS" ], + "tags": [ + "WebApp.Headers.HSTS" + ], "severity": "critical", "patterns": [ { "pattern": "Strict-Transport-Security", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -19,14 +25,20 @@ "name": "Web App Headers CSP", "id": "AI054900", "description": "Web App Headers Content Security Policy (CSP)", - "tags":[ "WebApp.Headers.CSP" ], + "tags": [ + "WebApp.Headers.CSP" + ], "severity": "critical", "patterns": [ { "pattern": "Content-Security-Policy", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -35,14 +47,20 @@ "name": "Web App Headers X-Frame", "id": "AI055000", "description": "Web App Headers X-Frame", - "tags":[ "WebApp.Headers.X-Frame" ], + "tags": [ + "WebApp.Headers.X-Frame" + ], "severity": "critical", "patterns": [ { "pattern": "X-Frame-Options", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -51,14 +69,20 @@ "name": "Deprecated Web App Headers X-XSS", "id": "AI055100", "description": "Web App Headers Cross Site Scripting Protection (X-XSS)", - "tags":[ "WebApp.Headers.XSS" ], + "tags": [ + "WebApp.Headers.XSS" + ], "severity": "critical", "patterns": [ { "pattern": "X-XSS-Protection", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -67,14 +91,20 @@ "name": "Deprecated Web App Headers HPKP", "id": "AI055200", "description": "Web App Headers Publick Key Pin (HPKP)", - "tags":[ "WebApp.Headers.HPKP" ], + "tags": [ + "WebApp.Headers.HPKP" + ], "severity": "critical", "patterns": [ { "pattern": "Public-Key-Pins", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -83,14 +113,20 @@ "name": "Web App Headers Expect-CT", "id": "AI055300", "description": "Web App Headers Certificate Transparency Expect-CT", - "tags":[ "WebApp.Headers.Expect-CT" ], + "tags": [ + "WebApp.Headers.Expect-CT" + ], "severity": "important", "patterns": [ { "pattern": "Expect-CT", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -99,14 +135,20 @@ "name": "Web App Headers X-Content-Type-Options", "id": "AI055400", "description": "Web App Headers X-Content-Type-Options", - "tags":[ "WebApp.Headers.X-Content-Type-Options" ], + "tags": [ + "WebApp.Headers.X-Content-Type-Options" + ], "severity": "important", "patterns": [ { "pattern": "X-Content-Type-Options", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -115,14 +157,20 @@ "name": "Web App Headers Referrer-Policy", "id": "AI055500", "description": "Web App Headers Referrer-Policy", - "tags":[ "WebApp.Headers.Referrer-Policy" ], + "tags": [ + "WebApp.Headers.Referrer-Policy" + ], "severity": "important", "patterns": [ { "pattern": "Referrer-Policy", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -131,14 +179,20 @@ "name": "Web App Headers Cache-Control", "id": "AI055600", "description": "Web App Headers Cache-Control", - "tags":[ "WebApp.Headers.Cache-Control" ], + "tags": [ + "WebApp.Headers.Cache-Control" + ], "severity": "important", "patterns": [ { "pattern": "Cache-Control", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -147,14 +201,20 @@ "name": "Web App Headers Clear-Site-Data", "id": "AI055700", "description": "Web App Headers Clear-Site-Data", - "tags":[ "WebApp.Headers.Clear-Site-Data" ], + "tags": [ + "WebApp.Headers.Clear-Site-Data" + ], "severity": "critical", "patterns": [ { "pattern": "Clear-Site-Data", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -163,14 +223,20 @@ "name": "Web App Headers Feature-Policy", "id": "AI055800", "description": "Web App Headers", - "tags":[ "WebApp.Headers.Feature-Policy" ], + "tags": [ + "WebApp.Headers.Feature-Policy" + ], "severity": "critical", "patterns": [ { "pattern": "Feature-Policy", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] diff --git a/AppInspector/rules/default/webapp/media.json b/AppInspector/rules/default/webapp/media.json index 9f6eb5a..d986d0c 100644 --- a/AppInspector/rules/default/webapp/media.json +++ b/AppInspector/rules/default/webapp/media.json @@ -3,7 +3,9 @@ "name": "Web App Media Audio", "id": "AI055900", "description": "Web App Media Audio", - "tags":[ "WebApp.Media.Audio" ], + "tags": [ + "WebApp.Media.Audio" + ], "applies_to": [ "javascript", "python", @@ -21,7 +23,9 @@ { "pattern": "new AudioContext", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -30,7 +34,9 @@ "name": "Web App Media GetUserMedia", "id": "AI056000", "description": "Web App Media GetUserMedia", - "tags":[ "WebApp.Media.GetUserMedia" ], + "tags": [ + "WebApp.Media.GetUserMedia" + ], "applies_to": [ "javascript", "python", @@ -48,7 +54,9 @@ { "pattern": "getUserMedia|webkitGetUserMedia|mozGetUserMedia|msGetUserMedia", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] diff --git a/AppInspector/rules/default/webapp/sessions.json b/AppInspector/rules/default/webapp/sessions.json index 1102083..d9812e9 100644 --- a/AppInspector/rules/default/webapp/sessions.json +++ b/AppInspector/rules/default/webapp/sessions.json @@ -3,14 +3,20 @@ "name": "Web App Cookie HttpOnly", "id": "AI056100", "description": "Web App Cookie Attributes HttpOnly", - "tags":[ "WebApp.Cookies.Attr.HttpOnly" ], + "tags": [ + "WebApp.Cookies.Attr.HttpOnly" + ], "severity": "critical", "patterns": [ { "pattern": "httpOnly", "type": "string", - "modifiers": [ "i" ], - "scopes": [ "code" ], + "modifiers": [ + "i" + ], + "scopes": [ + "code" + ], "confidence": "high" } ], @@ -19,8 +25,12 @@ "pattern": { "pattern": "cookie", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ] + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ] }, "search_in": "finding-region(-1,1)", "negate_finding": false @@ -31,14 +41,20 @@ "name": "Web App Cookie Secure", "id": "AI056200", "description": "Web App Cookie Attributes Secure", - "tags":[ "WebApp.Cookies.Attr.Secure" ], + "tags": [ + "WebApp.Cookies.Attr.Secure" + ], "severity": "critical", "patterns": [ { "pattern": "secure", "type": "string", - "modifiers": [ "i" ], - "scopes": [ "code" ], + "modifiers": [ + "i" + ], + "scopes": [ + "code" + ], "confidence": "high" } ], @@ -47,8 +63,12 @@ "pattern": { "pattern": "cookie", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ] + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ] }, "search_in": "finding-region(-1,1)", "negate_finding": false @@ -59,14 +79,20 @@ "name": "Web App Cookie Attributes Lifetime", "id": "AI056300", "description": "Web App Cookie Attributes Lifetime", - "tags":[ "WebApp.Cookies.Attr.Lifetime" ], + "tags": [ + "WebApp.Cookies.Attr.Lifetime" + ], "severity": "critical", "patterns": [ { "pattern": "lifetime", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ], @@ -75,8 +101,12 @@ "pattern": { "pattern": "cookie", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ] + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ] }, "search_in": "finding-region(-1,1)", "negate_finding": false @@ -87,14 +117,20 @@ "name": "Web App Cookie Attributes Strict", "id": "AI056400", "description": "Web App Cookie Attributes Strict", - "tags":[ "WebApp.Cookies.Attr.Strict" ], + "tags": [ + "WebApp.Cookies.Attr.Strict" + ], "severity": "critical", "patterns": [ { "pattern": "strict", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ], @@ -103,8 +139,12 @@ "pattern": { "pattern": "cookie", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ] + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ] }, "search_in": "finding-region(-1,1)", "negate_finding": false @@ -115,14 +155,20 @@ "name": "Web App Cookie Attributes SameSite", "id": "AI056500", "description": "Web App Cookie Attributes SameSite", - "tags":[ "WebApp.Cookies.Attr.SameSite" ], + "tags": [ + "WebApp.Cookies.Attr.SameSite" + ], "severity": "critical", "patterns": [ { "pattern": "samesite", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ], @@ -131,8 +177,12 @@ "pattern": { "pattern": "cookie", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ] + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ] }, "search_in": "finding-region(-1,1)", "negate_finding": false @@ -143,14 +193,20 @@ "name": "Web App Cookie Attributes Expires", "id": "AI056600", "description": "Web App Cookie Attributes Expires", - "tags":[ "WebApp.Cookies.Attr.Expires" ], + "tags": [ + "WebApp.Cookies.Attr.Expires" + ], "severity": "critical", "patterns": [ { "pattern": "expires", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ], @@ -159,8 +215,12 @@ "pattern": { "pattern": "cookie", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ] + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ] }, "search_in": "finding-region(-1,1)", "negate_finding": false @@ -171,14 +231,20 @@ "name": "Web App Cookie Attributes Domain", "id": "AI056700", "description": "Web App Cookie Attributes Domain", - "tags":[ "WebApp.Cookies.Attr.Domain" ], + "tags": [ + "WebApp.Cookies.Attr.Domain" + ], "severity": "critical", "patterns": [ { "pattern": "domain", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ], @@ -187,8 +253,12 @@ "pattern": { "pattern": "cookie", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ] + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ] }, "search_in": "finding-region(-1,1)", "negate_finding": false @@ -199,13 +269,17 @@ "name": "Web App Session History Update", "id": "AI056800", "description": "Web App Session History Update", - "tags":[ "WebApp.History.Update" ], + "tags": [ + "WebApp.History.Update" + ], "severity": "critical", "patterns": [ { "pattern": "history\\.(pushState|replaceState)", "type": "regex", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -214,13 +288,17 @@ "name": "Web App Session Worker", "id": "AI056900", "description": "Web App Session Worker Used", - "tags":[ "WebApp.Worker" ], + "tags": [ + "WebApp.Worker" + ], "severity": "critical", "patterns": [ { "pattern": "new Worker", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] diff --git a/AppInspector/rules/default/webapp/storage.json b/AppInspector/rules/default/webapp/storage.json index 85f6cd6..09c1405 100644 --- a/AppInspector/rules/default/webapp/storage.json +++ b/AppInspector/rules/default/webapp/storage.json @@ -3,7 +3,9 @@ "name": "Web App Local Storage Write", "id": "AI057000", "description": "Web App Local Storage Write", - "tags":[ "WebApp.Storage.Local.Write" ], + "tags": [ + "WebApp.Storage.Local.Write" + ], "applies_to": [ "javascript", "python", @@ -21,7 +23,9 @@ { "pattern": "localstorage.setItem", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -30,7 +34,9 @@ "name": "Web App Session Storage Write", "id": "AI057100", "description": "Web App Session Storage Write", - "tags":[ "WebApp.Storage.Session.Write" ], + "tags": [ + "WebApp.Storage.Session.Write" + ], "applies_to": [ "javascript", "python", @@ -48,7 +54,9 @@ { "pattern": "sessionstorage.setItem", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -57,7 +65,9 @@ "name": "Web App Storage Cookie Write", "id": "AI057200", "description": "Web App Storage Cookie Write", - "tags":[ "WebApp.Storage.Cookie.Write" ], + "tags": [ + "WebApp.Storage.Cookie.Write" + ], "applies_to": [ "javascript", "python", @@ -75,8 +85,12 @@ { "pattern": ".cookie =", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ], + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ], "confidence": "high" } ] @@ -85,7 +99,9 @@ "name": "Web App Storage Indexed DB Use", "id": "AI057300", "description": "Web App Storage Indexed DB", - "tags":[ "WebApp.Storage.IndexedDB" ], + "tags": [ + "WebApp.Storage.IndexedDB" + ], "applies_to": [ "javascript", "python", @@ -103,7 +119,9 @@ { "pattern": "indexedDB|mozIndexedDB|webkitIndexedDB|msIndexedDB", "type": "regexword", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -112,7 +130,9 @@ "name": "Web App Storage FileReader", "id": "AI057400", "description": "Web App Storage FileReader", - "tags":[ "WebApp.Storage.FileReader" ], + "tags": [ + "WebApp.Storage.FileReader" + ], "applies_to": [ "javascript", "python", @@ -130,7 +150,9 @@ { "pattern": "new FileReader", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ] @@ -139,7 +161,9 @@ "name": "Web App Storage ReadFileAsURL", "id": "AI057500", "description": "Web App Storage ReadFileAsURL", - "tags":[ "WebApp.Storage.ReadFileAsURL" ], + "tags": [ + "WebApp.Storage.ReadFileAsURL" + ], "applies_to": [ "javascript", "python", @@ -157,7 +181,9 @@ { "pattern": "readAsDataURL", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ], @@ -166,7 +192,9 @@ "pattern": { "pattern": "FileReader", "type": "regexword", - "scopes": [ "code" ] + "scopes": [ + "code" + ] }, "search_in": "finding-region(-5,5)", "negate_finding": false @@ -177,7 +205,9 @@ "name": "Web App Storage FileUpload", "id": "AI057600", "description": "Web App Storage FileUpload", - "tags":[ "WebApp.Storage.FileUpload" ], + "tags": [ + "WebApp.Storage.FileUpload" + ], "applies_to": [ "javascript", "python", @@ -195,7 +225,9 @@ { "pattern": "fetch", "type": "string", - "scopes": [ "code" ], + "scopes": [ + "code" + ], "confidence": "high" } ], @@ -204,8 +236,12 @@ "pattern": { "pattern": "PUT", "type": "string", - "scopes": [ "code" ], - "modifiers": [ "i" ] + "scopes": [ + "code" + ], + "modifiers": [ + "i" + ] }, "search_in": "finding-region(-5,5)", "negate_finding": false diff --git a/Benchmarks/AnalyzeBenchmark.cs b/Benchmarks/AnalyzeBenchmark.cs index ab7f083..39a1803 100644 --- a/Benchmarks/AnalyzeBenchmark.cs +++ b/Benchmarks/AnalyzeBenchmark.cs @@ -1,59 +1,55 @@ -namespace Benchmarks +using System; +using System.IO; +using System.Reflection; +using BenchmarkDotNet.Attributes; +using Microsoft.ApplicationInspector.Commands; + +namespace Benchmarks; + +//[ConcurrencyVisualizerProfiler] +public class AnalyzeBenchmark { - using BenchmarkDotNet.Attributes; - using Microsoft.ApplicationInspector.Commands; - using System; - using System.IO; - using System.Reflection; + // Manually put the file you want to benchmark. But don't put this in a path with "Test" in the name ;) + private const string path = "C:\\Users\\gstocco\\Documents\\GitHub\\ApplicationInspector\\RulesEngine\\"; - //[ConcurrencyVisualizerProfiler] - public class AnalyzeBenchmark + [Benchmark(Baseline = true)] + public static void AnalyzeSingleThreaded() { - // Manually put the file you want to benchmark. But don't put this in a path with "Test" in the name ;) - private const string path = "C:\\Users\\gstocco\\Documents\\GitHub\\ApplicationInspector\\RulesEngine\\"; - - public AnalyzeBenchmark() + AnalyzeCommand command = new(new AnalyzeOptions { - } + SourcePath = new string[1] { path }, + SingleThread = true, + IgnoreDefaultRules = false, + FilePathExclusions = new[] { "**/bin/**", "**/obj/**" }, + NoShowProgress = true + }); - [Benchmark(Baseline = true)] - public static void AnalyzeSingleThreaded() - { - AnalyzeCommand command = new(new AnalyzeOptions() - { - SourcePath = new string[1] { path }, - SingleThread = true, - IgnoreDefaultRules = false, - FilePathExclusions = new string[] { "**/bin/**","**/obj/**" }, - NoShowProgress = true - }); - - _ = command.GetResult(); - } - - [Benchmark] - public static void AnalyzeMultiThread() - { - AnalyzeCommand command = new(new AnalyzeOptions() - { - SourcePath = new string[1] { path }, - SingleThread = false, - IgnoreDefaultRules = false, - FilePathExclusions = new string[] { "**/bin/**", "**/obj/**" }, - NoShowProgress = true - }); - - _ = command.GetResult(); - } - - public static string GetExecutingDirectoryName() - { - if (Assembly.GetEntryAssembly()?.GetName().CodeBase is string codeBaseLoc) - { - var location = new Uri(codeBaseLoc); - return new FileInfo(location.AbsolutePath).Directory?.FullName ?? string.Empty; - } - return string.Empty; - } + _ = command.GetResult(); } -} + + [Benchmark] + public static void AnalyzeMultiThread() + { + AnalyzeCommand command = new(new AnalyzeOptions + { + SourcePath = new string[1] { path }, + SingleThread = false, + IgnoreDefaultRules = false, + FilePathExclusions = new[] { "**/bin/**", "**/obj/**" }, + NoShowProgress = true + }); + + _ = command.GetResult(); + } + + public static string GetExecutingDirectoryName() + { + if (Assembly.GetEntryAssembly()?.GetName().CodeBase is string codeBaseLoc) + { + var location = new Uri(codeBaseLoc); + return new FileInfo(location.AbsolutePath).Directory?.FullName ?? string.Empty; + } + + return string.Empty; + } +} \ No newline at end of file diff --git a/Benchmarks/Benchmarks.csproj b/Benchmarks/Benchmarks.csproj index fe7e655..442e01d 100644 --- a/Benchmarks/Benchmarks.csproj +++ b/Benchmarks/Benchmarks.csproj @@ -1,24 +1,24 @@  - - Exe - net6.0 - enable - 10.0 - + + Exe + net6.0 + enable + 10.0 + - - - - - - - - - - - - PreserveNewest - - + + + + + + + + + + + + PreserveNewest + + diff --git a/Benchmarks/DistinctBenchmarks.cs b/Benchmarks/DistinctBenchmarks.cs index 368fca9..8d88f61 100644 --- a/Benchmarks/DistinctBenchmarks.cs +++ b/Benchmarks/DistinctBenchmarks.cs @@ -3,52 +3,30 @@ using System.Collections.Generic; using System.Linq; using BenchmarkDotNet.Attributes; using Microsoft.ApplicationInspector.Commands; -using Microsoft.ApplicationInspector.Common; using Microsoft.ApplicationInspector.RulesEngine; namespace Benchmarks; -[MemoryDiagnoser()] +[MemoryDiagnoser] public class DistinctBenchmarks { - public DistinctBenchmarks() - { - } - IEnumerable ruleSet = RuleSetUtils.GetDefaultRuleSet().GetAppInspectorRules(); - + private readonly IEnumerable ruleSet = RuleSetUtils.GetDefaultRuleSet().GetAppInspectorRules(); + [Benchmark(Baseline = true)] public List OldCode() { SortedDictionary uniqueTags = new(); List outList = new(); - try - { - foreach (Rule? r in ruleSet) - { - //builds a list of unique tags - foreach (string t in r?.Tags ?? Array.Empty()) - { - if (uniqueTags.ContainsKey(t)) - { - continue; - } - else - { - uniqueTags.Add(t, t); - } - } - } + foreach (var r in ruleSet) + //builds a list of unique tags + foreach (var t in r?.Tags ?? Array.Empty()) + if (uniqueTags.ContainsKey(t)) + continue; + else + uniqueTags.Add(t, t); - //generate results list - foreach (string s in uniqueTags.Values) - { - outList.Add(s); - } - } - catch (OpException e) - { - throw; - } + //generate results list + foreach (var s in uniqueTags.Values) outList.Add(s); return outList; } @@ -57,27 +35,23 @@ public class DistinctBenchmarks public List HashSet() { HashSet hashSet = new(); - foreach (Rule? r in ruleSet) - { + foreach (var r in ruleSet) //builds a list of unique tags - foreach (string t in r?.Tags ?? Array.Empty()) - { - hashSet.Add(t); - } - } + foreach (var t in r?.Tags ?? Array.Empty()) + hashSet.Add(t); - List theList = hashSet.ToList(); + var theList = hashSet.ToList(); theList.Sort(); return theList; } - + [Benchmark] public List WithLinq() { return ruleSet.SelectMany(x => x.Tags ?? Array.Empty()).Distinct().OrderBy(x => x) .ToList(); } - + [Benchmark] public List WithLinqAndHashSet() { diff --git a/Benchmarks/Program.cs b/Benchmarks/Program.cs index d1f94e9..060f90f 100644 --- a/Benchmarks/Program.cs +++ b/Benchmarks/Program.cs @@ -1,15 +1,14 @@ -namespace ApplicationInspector.Benchmarks -{ - using BenchmarkDotNet.Configs; - using BenchmarkDotNet.Running; +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Running; - public class Program +namespace ApplicationInspector.Benchmarks; + +public class Program +{ + public static void Main(string[] args) { - public static void Main(string[] args) - { - // new DebugInProcessConfig() - BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args, new DebugInProcessConfig()); - //var summary = BenchmarkRunner.Run(); - } + // new DebugInProcessConfig() + BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args, new DebugInProcessConfig()); + //var summary = BenchmarkRunner.Run(); } -} +} \ No newline at end of file diff --git a/Directory.Build.props b/Directory.Build.props index b923946..f035ff3 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,9 +1,9 @@  - - - 3.5.109 - all - - + + + 3.5.109 + all + + \ No newline at end of file diff --git a/README.md b/README.md index 8dcba68..454e71d 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,54 @@ -# Introduction +# Introduction + ![CodeQL](https://github.com/microsoft/ApplicationInspector/workflows/CodeQL/badge.svg) ![Nuget](https://img.shields.io/nuget/v/Microsoft.CST.ApplicationInspector.Cli?link=https://www.nuget.org/packages/Microsoft.CST.ApplicationInspector.CLI/&link=https://www.nuget.org/packages/Microsoft.CST.ApplicationInspector.CLI/) ![Nuget](https://img.shields.io/nuget/dt/Microsoft.CST.ApplicationInspector.Cli?link=https://www.nuget.org/packages/Microsoft.CST.ApplicationInspector.CLI/&link=https://www.nuget.org/packages/Microsoft.CST.ApplicationInspector.CLI/) -Microsoft Application Inspector is a software source code characterization tool that helps **identify coding features of first or third party software components** based on well-known library/API calls and is helpful in security and non-security use cases. It uses hundreds of rules and regex patterns to surface interesting characteristics of source code to aid in determining **what the software is** or **what it does** from what file operations it uses, encryption, shell operations, cloud API's, frameworks and more and has received industry attention as a new and valuable contribution to OSS on [ZDNet](https://www.zdnet.com/article/microsoft-application-inspector-is-now-open-source-so-use-it-to-test-code-security/ -), [SecurityWeek](https://www.securityweek.com/microsoft-introduces-free-source-code-analyzer), [CSOOnline](https://www.csoonline.com/article/3514732/microsoft-s-offers-application-inspector-to-probe-untrusted-open-source-code.html), [Linux.com/news](https://www.linux.com/news/microsoft-application-inspector-is-now-open-source-so-use-it-to-test-code-security/), [HelpNetSecurity](https://www.helpnetsecurity.com/2020/01/17/microsoft-application-inspector/ -), Twitter and more and was first featured on [Microsoft.com](https://www.microsoft.com/security/blog/2020/01/16/introducing-microsoft-application-inspector/). +Microsoft Application Inspector is a software source code characterization tool that helps **identify coding features of +first or third party software components** based on well-known library/API calls and is helpful in security and +non-security use cases. It uses hundreds of rules and regex patterns to surface interesting characteristics of source +code to aid in determining **what the software is** or **what it does** from what file operations it uses, encryption, +shell operations, cloud API's, frameworks and more and has received industry attention as a new and valuable +contribution to OSS +on [ZDNet](https://www.zdnet.com/article/microsoft-application-inspector-is-now-open-source-so-use-it-to-test-code-security/ +), [SecurityWeek](https://www.securityweek.com/microsoft-introduces-free-source-code-analyzer) +, [CSOOnline](https://www.csoonline.com/article/3514732/microsoft-s-offers-application-inspector-to-probe-untrusted-open-source-code.html) +, [Linux.com/news](https://www.linux.com/news/microsoft-application-inspector-is-now-open-source-so-use-it-to-test-code-security/) +, [HelpNetSecurity](https://www.helpnetsecurity.com/2020/01/17/microsoft-application-inspector/ +), Twitter and more and was first featured +on [Microsoft.com](https://www.microsoft.com/security/blog/2020/01/16/introducing-microsoft-application-inspector/). -Application Inspector is different from traditional static analysis tools in that it doesn't attempt to identify "good" or "bad" patterns; it simply reports what it finds against a set of over 400 rule patterns for feature detection including features that impact security such as the use of cryptography and more. This can be extremely helpful in reducing the time needed to determine what Open Source or other components do by examining the source directly rather than trusting to limited documentation or recommendations. +Application Inspector is different from traditional static analysis tools in that it doesn't attempt to identify "good" +or "bad" patterns; it simply reports what it finds against a set of over 400 rule patterns for feature detection +including features that impact security such as the use of cryptography and more. This can be extremely helpful in +reducing the time needed to determine what Open Source or other components do by examining the source directly rather +than trusting to limited documentation or recommendations. -The tool supports scanning various programming languages including C, C++, C#, Java, JavaScript, HTML, Python, Objective-C, Go, Ruby, PowerShell and [more](https://github.com/microsoft/ApplicationInspector/wiki/3.4-Applies_to-(languages)) and can scan projects with mixed language files. It supports generating results in HTML, JSON and text output formats with the **default being an HTML report** similar to the one shown here. +The tool supports scanning various programming languages including C, C++, C#, Java, JavaScript, HTML, Python, +Objective-C, Go, Ruby, PowerShell +and [more](https://github.com/microsoft/ApplicationInspector/wiki/3.4-Applies_to-(languages)) and can scan projects with +mixed language files. It supports generating results in HTML, JSON and text output formats with the **default being an +HTML report** similar to the one shown here. ![appinspector-Features](https://user-images.githubusercontent.com/47648296/72893326-9c82c700-3ccd-11ea-8944-9831ea17f3e0.png) -Be sure to see our complete project wiki page https://Github.com/Microsoft/ApplicationInspector/wiki for additional information and help. +Be sure to see our complete project wiki page https://Github.com/Microsoft/ApplicationInspector/wiki for additional +information and help. # Quick Start + ## Obtain Application Inspector + ### .NET Tool (recommended) + - Download and install the .NET 6 [SDK](https://dotnet.microsoft.com/download/) -- Run `dotnet tool install --global Microsoft.CST.ApplicationInspector.CLI` +- Run `dotnet tool install --global Microsoft.CST.ApplicationInspector.CLI` See more in the [wiki](https://github.com/microsoft/ApplicationInspector/wiki/2.-NuGet-Support) ### Platform Dependent Binary -- Download Application Inspector by selecting the pre-built package for the operating system of choice shown under the Assets section -of the [Releases](https://github.com/microsoft/ApplicationInspector/releases). + +- Download Application Inspector by selecting the pre-built package for the operating system of choice shown under the + Assets section + of the [Releases](https://github.com/microsoft/ApplicationInspector/releases). ## Run Application Inspector @@ -34,29 +59,44 @@ of the [Releases](https://github.com/microsoft/ApplicationInspector/releases). Microsoft Application Inspector helps you in securing your applications from start to deployment. -**Design Choices** - Enables you to choose which components meet your needs with a smaller footprint of unnecessary or unknowns features for keeping your application attack surface smaller as well as help to verify expected ones i.e. industry standard crypto only. +**Design Choices** - Enables you to choose which components meet your needs with a smaller footprint of unnecessary or +unknowns features for keeping your application attack surface smaller as well as help to verify expected ones i.e. +industry standard crypto only. -**Identifying Feature Deltas** - Detects changes between component versions which can be critical for detecting injection of backdoors. +**Identifying Feature Deltas** - Detects changes between component versions which can be critical for detecting +injection of backdoors. -**Automating Security Compliance Checks** - Use to identify components with features that require additional security scrutiny, approval or SDL compliance as part of your build pipeline or create a repository of metadata regarding all of your enterprise application. +**Automating Security Compliance Checks** - Use to identify components with features that require additional security +scrutiny, approval or SDL compliance as part of your build pipeline or create a repository of metadata regarding all of +your enterprise application. # Contribute -We have a strong default starting base of Rules for feature detection. But there are many feature identification patterns yet to be defined and we invite you to **submit ideas** on what you want to see or take a crack at defining a few. This is a chance to literally impact the open source ecosystem helping provide a tool that everyone can use. See the [Rules](https://github.com/microsoft/ApplicationInspector/wiki/3.-Understanding-Rules) section of the wiki for more. +We have a strong default starting base of Rules for feature detection. But there are many feature identification +patterns yet to be defined and we invite you to **submit ideas** on what you want to see or take a crack at defining a +few. This is a chance to literally impact the open source ecosystem helping provide a tool that everyone can use. See +the [Rules](https://github.com/microsoft/ApplicationInspector/wiki/3.-Understanding-Rules) section of the wiki for more. # Official Releases -Application Inspector is in GENERAL AUDIENCE release status. Your feedback is important to us. If you're interested in contributing, please review the CONTRIBUTING.md. +Application Inspector is in GENERAL AUDIENCE release status. Your feedback is important to us. If you're interested in +contributing, please review the CONTRIBUTING.md. -Application Inspector is availble as a command line tool or NuGet package and is supported on Windows, Linux, or MacOS. +Application Inspector is availble as a command line tool or NuGet package and is supported on Windows, Linux, or MacOS. -Platform specific binaries of the ApplicationInspector CLI are available on our GitHub [releases page](https://github.com/microsoft/ApplicationInspector/releases). +Platform specific binaries of the ApplicationInspector CLI are available on our +GitHub [releases page](https://github.com/microsoft/ApplicationInspector/releases). -The C# library is available on NuGet as [Microsoft.CST.ApplicationInspector.Commands](https://www.nuget.org/packages/Microsoft.CST.ApplicationInspector.Commands/). +The C# library is available on NuGet +as [Microsoft.CST.ApplicationInspector.Commands](https://www.nuget.org/packages/Microsoft.CST.ApplicationInspector.Commands/) +. -The .NET Global Tool is available on NuGet as [Microsoft.CST.ApplicationInspector.CLI](https://www.nuget.org/packages/Microsoft.CST.ApplicationInspector.CLI/). +The .NET Global Tool is available on NuGet +as [Microsoft.CST.ApplicationInspector.CLI](https://www.nuget.org/packages/Microsoft.CST.ApplicationInspector.CLI/). -If you use the .NET Core version, you will need to have .NET Core 6.0 or later installed. See the [JustRunIt.md](https://github.com/microsoft/ApplicationInspector/blob/master/JustRunIt.md) or [Build.md](https://github.com/microsoft/ApplicationInspector/blob/master/BUILD.md) files for more. +If you use the .NET Core version, you will need to have .NET Core 6.0 or later installed. See +the [JustRunIt.md](https://github.com/microsoft/ApplicationInspector/blob/master/JustRunIt.md) +or [Build.md](https://github.com/microsoft/ApplicationInspector/blob/master/BUILD.md) files for more. # CLI Usage Information @@ -83,21 +123,32 @@ c Microsoft Corporation. All rights reserved. ## Examples: ### Command Help + To get help for a specific command run `appinspector --help`. ### Analyze Command + The Analyze Command is the workhorse of Application Inspector. + #### Simple Default Analyze + This will produce an output.html of the analysis in the current directory using default arguments and rules. + ``` appinspector analyze -s path/to/files ``` + #### Excluding Files using Globs -This will create a json output named data.json of the analysis in the current directory, excluding all files in `test` and `.git` folders using the provided glob patterns. + +This will create a json output named data.json of the analysis in the current directory, excluding all files in `test` +and `.git` folders using the provided glob patterns. + ``` appinspector analyze -s path/to/files -o data.json -f json -g **/tests/**,**/.git/** ``` + #### Additional Usage Information + ``` > appinspector analyze --help ApplicationInspector.CLI 1.5.7+1e422537ad @@ -183,9 +234,12 @@ c Microsoft Corporation. All rights reserved. --version Display version information. ``` -For additional help on use of the console interface see [CLI Usage](https://github.com/microsoft/ApplicationInspector/wiki/1.-CLI-Usage). -For help using the NuGet package see [NuGet Support](https://github.com/microsoft/ApplicationInspector/wiki/2.-NuGet-Support) +For additional help on use of the console interface +see [CLI Usage](https://github.com/microsoft/ApplicationInspector/wiki/1.-CLI-Usage). + +For help using the NuGet package +see [NuGet Support](https://github.com/microsoft/ApplicationInspector/wiki/2.-NuGet-Support) # Build Instructions diff --git a/version.json b/version.json index b0caae3..e2f55d4 100644 --- a/version.json +++ b/version.json @@ -1,6 +1,6 @@ { "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", - "version": "1.6-beta", + "version": "1.6", "publicReleaseRefSpec": [ "^refs/heads/main$", "^refs/heads/v\\d+(?:\\.\\d+)?$"