diff --git a/AppInspector.Benchmarks/Program.cs b/AppInspector.Benchmarks/Program.cs
index bb77cb6..bd421ce 100644
--- a/AppInspector.Benchmarks/Program.cs
+++ b/AppInspector.Benchmarks/Program.cs
@@ -1,5 +1,4 @@
-using BenchmarkDotNet.Configs;
-using BenchmarkDotNet.Running;
+using BenchmarkDotNet.Running;
namespace ApplicationInspector.Benchmarks;
diff --git a/AppInspector.Benchmarks/WriterBench.cs b/AppInspector.Benchmarks/WriterBench.cs
index ac3d860..84e1022 100644
--- a/AppInspector.Benchmarks/WriterBench.cs
+++ b/AppInspector.Benchmarks/WriterBench.cs
@@ -3,7 +3,6 @@ using System.IO;
using System.Reflection;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
-using DotLiquid;
using Microsoft.ApplicationInspector.CLI;
using Microsoft.ApplicationInspector.Commands;
using Microsoft.ApplicationInspector.RulesEngine;
diff --git a/AppInspector.CLI/Writers/AnalyzeSarifWriter.cs b/AppInspector.CLI/Writers/AnalyzeSarifWriter.cs
index 425b316..83d5e4f 100644
--- a/AppInspector.CLI/Writers/AnalyzeSarifWriter.cs
+++ b/AppInspector.CLI/Writers/AnalyzeSarifWriter.cs
@@ -5,7 +5,6 @@ using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using System.Text.Json;
using Microsoft.ApplicationInspector.Commands;
using Microsoft.ApplicationInspector.RulesEngine;
using Microsoft.CodeAnalysis.Sarif;
diff --git a/AppInspector.CLI/Writers/JsonWriter.cs b/AppInspector.CLI/Writers/JsonWriter.cs
index 7cb8f19..7cf1582 100644
--- a/AppInspector.CLI/Writers/JsonWriter.cs
+++ b/AppInspector.CLI/Writers/JsonWriter.cs
@@ -1,6 +1,5 @@
using System;
using System.IO;
-using System.Net.Sockets;
using System.Text.Json;
using System.Text.Json.Serialization;
using Microsoft.ApplicationInspector.Commands;
diff --git a/AppInspector.CLI/html/index.html b/AppInspector.CLI/html/index.html
index ebffce2..7f4cda0 100644
--- a/AppInspector.CLI/html/index.html
+++ b/AppInspector.CLI/html/index.html
@@ -4,7 +4,7 @@
-
+
diff --git a/AppInspector.CLI/preferences/tagreportgroups.json b/AppInspector.CLI/preferences/tagreportgroups.json
index 27ec94c..bf1d19b 100644
--- a/AppInspector.CLI/preferences/tagreportgroups.json
+++ b/AppInspector.CLI/preferences/tagreportgroups.json
@@ -82,6 +82,21 @@
"searchPattern": "^CloudServices.AdvertisingNetwork.*",
"displayName": "Advertising network",
"detectedIcon": "fas fa-ad"
+ },
+ {
+ "searchPattern": "^CloudServices.SalesForce$",
+ "displayName": "Salesforce",
+ "detectedIcon": "fa-solid fa-cloud"
+ },
+ {
+ "searchPattern": "^CloudServices.ServiceNow$",
+ "displayName": "ServiceNow",
+ "detectedIcon": "fa-solid fa-bell-concierge"
+ },
+ {
+ "searchPattern": "^CloudServices.WorkDay$",
+ "displayName": "WorkDay",
+ "detectedIcon": "fa-solid fa-calendar-day"
}
]
},
@@ -315,12 +330,12 @@
"detectedIcon": "far fa-file-archive"
},
{
- "searchPattern": "^Metric.Code.HTMLForm.Defined$",
+ "searchPattern": ".Code.HTMLForm.Defined$",
"displayName": "HTML form",
"detectedIcon": "far fa-window-maximize"
},
{
- "searchPattern": "^Metric.Code.Exception.Caught$",
+ "searchPattern": ".Code.Exception.Caught$",
"displayName": "Exception caught",
"detectedIcon": "fas fa-meteor"
},
diff --git a/AppInspector.Common/Utils.cs b/AppInspector.Common/Utils.cs
index 92d85d4..dfdf0d6 100644
--- a/AppInspector.Common/Utils.cs
+++ b/AppInspector.Common/Utils.cs
@@ -2,7 +2,6 @@
using System;
using System.Collections.Generic;
using System.IO;
-using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
diff --git a/AppInspector.RulesEngine/TextContainer.cs b/AppInspector.RulesEngine/TextContainer.cs
index 17e55b3..15c4b1c 100644
--- a/AppInspector.RulesEngine/TextContainer.cs
+++ b/AppInspector.RulesEngine/TextContainer.cs
@@ -243,15 +243,50 @@ public class TextContainer
}
}
+ private int GetPrefixLocation(int startOfLineIndex, int currentIndex, string prefix, bool multiline)
+ {
+ // Find the first potential index of the prefix
+ var prefixLoc = FullContent.LastIndexOf(prefix, currentIndex, StringComparison.Ordinal);
+ if (prefixLoc != -1)
+ {
+ // TODO: Possibly support quoted multiline comment markers
+ if (multiline)
+ {
+ return prefixLoc;
+ }
+ if (prefixLoc < startOfLineIndex)
+ {
+ return -1;
+ }
+ // Check how many quote marks occur on the line before the prefix location
+ // TODO: This doesn't account for multi-line strings
+ var numDoubleQuotes = FullContent[startOfLineIndex..prefixLoc].Count(x => x == '"');
+ var numSingleQuotes = FullContent[startOfLineIndex..prefixLoc].Count(x => x == '\'');
+
+ // If the number of quotes is odd, this is in a string, so not actually a comment prefix
+ // It might be like var address = "http://contoso.com";
+ if (numDoubleQuotes % 2 == 1 || numSingleQuotes % 2 == 1)
+ {
+ return GetPrefixLocation(startOfLineIndex, prefixLoc, prefix, multiline);
+ }
+ }
+
+ return prefixLoc;
+ }
+
///
/// 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)
+ private void PopulateCommentedStatesInternal(int index, string prefix, string suffix, bool multiline)
{
- var prefixLoc = FullContent.LastIndexOf(prefix, index, StringComparison.Ordinal);
+ // Get the line boundary for the prefix location
+ var startOfLine = GetLineBoundary(index);
+ // Get the index of the prefix
+ var prefixLoc = GetPrefixLocation(startOfLine.Index, index, prefix, multiline);
+
if (prefixLoc != -1)
{
if (!CommentedStates.ContainsKey(prefixLoc))
@@ -287,13 +322,13 @@ public class TextContainer
// 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);
+ PopulateCommentedStatesInternal(index, prefix, suffix, true);
}
// 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");
+ PopulateCommentedStatesInternal(index, inline, "\n", false);
}
var i = index;
diff --git a/AppInspector.Tests/Commands/TestVerifyRulesCmd.cs b/AppInspector.Tests/Commands/TestVerifyRulesCmd.cs
index 3aab372..42e33c9 100644
--- a/AppInspector.Tests/Commands/TestVerifyRulesCmd.cs
+++ b/AppInspector.Tests/Commands/TestVerifyRulesCmd.cs
@@ -2,7 +2,6 @@
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
-using CommandLine;
using Microsoft.ApplicationInspector.Commands;
using Microsoft.ApplicationInspector.Common;
using Microsoft.ApplicationInspector.Logging;
diff --git a/AppInspector.Tests/RuleProcessor/QuotedStringsTests.cs b/AppInspector.Tests/RuleProcessor/QuotedStringsTests.cs
new file mode 100644
index 0000000..cda630b
--- /dev/null
+++ b/AppInspector.Tests/RuleProcessor/QuotedStringsTests.cs
@@ -0,0 +1,80 @@
+using System.IO;
+using System.Linq;
+using Microsoft.ApplicationInspector.Logging;
+using Microsoft.ApplicationInspector.RulesEngine;
+using Microsoft.CST.RecursiveExtractor;
+using Microsoft.Extensions.Logging;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Serilog.Events;
+
+namespace AppInspector.Tests.RuleProcessor;
+
+[TestClass]
+public class QuotedStringsTests
+{
+
+ private const string testDoubleQuotesAreCode = "var url = \"https://contoso.com\"; // contoso.com";
+ private const string testSingleQuotesAreCode = "var url = 'https://contoso.com'; // contoso.com";
+ private const string testSingleLineWithQuotesInComment = "// var url = 'https://contoso.com';";
+ private const string testSingleLineWithDoubleQuotesInComment = "// var url = 'https://contoso.com';";
+ private const string testMultiLine = @"/*
+https://contoso.com
+*/";
+ private const string testMultiLineWithoutProto = @"
+/*
+contoso.com
+*/";
+ private const string testMultiLineWithResultFollowingCommentEnd = @"
+/*
+contoso.com
+*/ var url = ""https://contoso.com""";
+
+ private static string detectContosoRule = @"
+ [
+ {
+ ""id"": ""RE000001"",
+ ""name"": ""Testing.Rules.Quotes"",
+ ""tags"": [
+ ""Testing.Rules.Quotes""
+ ],
+ ""severity"": ""Critical"",
+ ""description"": ""Find contoso.com"",
+ ""patterns"": [
+ {
+ ""pattern"": ""contoso.com"",
+ ""type"": ""regex"",
+ ""confidence"": ""High"",
+ ""scopes"": [
+ ""code""
+ ]
+ }
+ ],
+ ""_comment"": """"
+ }
+]
+";
+
+ private readonly ILoggerFactory _loggerFactory =
+ new LogOptions { ConsoleVerbosityLevel = LogEventLevel.Verbose }.GetLoggerFactory();
+
+ private readonly Microsoft.ApplicationInspector.RulesEngine.Languages _languages = new();
+
+ [DataRow(testDoubleQuotesAreCode,1)]
+ [DataRow(testSingleQuotesAreCode,1)]
+ [DataRow(testMultiLine,0)]
+ [DataRow(testMultiLineWithoutProto,0)]
+ [DataRow(testMultiLineWithResultFollowingCommentEnd,1)]
+ [DataRow(testSingleLineWithQuotesInComment,0)]
+ [DataRow(testSingleLineWithDoubleQuotesInComment,0)]
+ [DataTestMethod]
+ public void QuotedStrings(string content, int numIssues)
+ {
+ RuleSet rules = new(_loggerFactory);
+ rules.AddString(detectContosoRule, "contosorule");
+ Microsoft.ApplicationInspector.RulesEngine.RuleProcessor ruleProcessor =
+ new Microsoft.ApplicationInspector.RulesEngine.RuleProcessor(rules, new RuleProcessorOptions());
+ _languages.FromFileNameOut("testfile.cs", out LanguageInfo info);
+ Assert.AreEqual(numIssues,
+ ruleProcessor.AnalyzeFile(content, new FileEntry("testfile.cs", new MemoryStream()), info).Count());
+ }
+}
\ No newline at end of file
diff --git a/AppInspector.Tests/RuleProcessor/XmlAndJsonTests.cs b/AppInspector.Tests/RuleProcessor/XmlAndJsonTests.cs
index d98336c..2a4d11a 100644
--- a/AppInspector.Tests/RuleProcessor/XmlAndJsonTests.cs
+++ b/AppInspector.Tests/RuleProcessor/XmlAndJsonTests.cs
@@ -1,9 +1,6 @@
-using System;
-using System.IO;
-using System.Linq;
+using System.IO;
using Microsoft.ApplicationInspector.RulesEngine;
using Microsoft.CST.RecursiveExtractor;
-using Microsoft.VisualBasic.CompilerServices;
using Microsoft.VisualStudio.TestTools.UnitTesting;
namespace AppInspector.Tests.RuleProcessor;
diff --git a/AppInspector/rules/default/cloud_services/saas.json b/AppInspector/rules/default/cloud_services/saas.json
new file mode 100644
index 0000000..dbc72f2
--- /dev/null
+++ b/AppInspector/rules/default/cloud_services/saas.json
@@ -0,0 +1,69 @@
+[
+ {
+ "name": "SaaS: Salesforce",
+ "id": "AI060001",
+ "description": "SaaS: Salesforce Rest API",
+ "tags": [
+ "CloudServices.Salesforce"
+ ],
+ "severity": "moderate",
+ "patterns": [
+ {
+ "confidence": "high",
+ "pattern": "my\\.salesforce\\.com",
+ "type": "regex",
+ "scopes": [
+ "code"
+ ],
+ "modifiers": [
+ "i"
+ ]
+ }
+ ]
+ },
+ {
+ "name": "SaaS: ServiceNow",
+ "id": "AI060002",
+ "description": "SaaS: ServiceNow Rest API",
+ "tags": [
+ "CloudServices.ServiceNow"
+ ],
+ "severity": "moderate",
+ "patterns": [
+ {
+ "confidence": "high",
+ "pattern": "service-now\\.com/api",
+ "type": "regex",
+ "scopes": [
+ "code"
+ ],
+ "modifiers": [
+ "i"
+ ]
+ }
+ ]
+ }
+ ,
+ {
+ "name": "SaaS: WorkDay",
+ "id": "AI060000",
+ "description": "SaaS: WorkDay Rest API",
+ "tags": [
+ "CloudServices.WorkDay"
+ ],
+ "severity": "moderate",
+ "patterns": [
+ {
+ "confidence": "high",
+ "pattern": "workday.com",
+ "type": "regex",
+ "scopes": [
+ "code"
+ ],
+ "modifiers": [
+ "i"
+ ]
+ }
+ ]
+ }
+]
\ No newline at end of file