From f90684dc19f1baf14e020a31998ee4824bdf671c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 4 Feb 2026 21:36:33 +0000 Subject: [PATCH 1/5] Initial plan From 8211aa34865dbd10312a1de18c1345315b9b52f7 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 4 Feb 2026 21:39:18 +0000 Subject: [PATCH 2/5] Initial exploration complete Co-authored-by: gfs <98900+gfs@users.noreply.github.com> --- nuget.config | 11 +++++------ nuget.config.bak | 7 +++++++ 2 files changed, 12 insertions(+), 6 deletions(-) create mode 100644 nuget.config.bak diff --git a/nuget.config b/nuget.config index be6dddfa..a0b839c4 100644 --- a/nuget.config +++ b/nuget.config @@ -1,7 +1,6 @@ - + - - - - - \ No newline at end of file + + + + diff --git a/nuget.config.bak b/nuget.config.bak new file mode 100644 index 00000000..751d4b6c --- /dev/null +++ b/nuget.config.bak @@ -0,0 +1,7 @@ + + + + + + + From 73bc3be4018bdcd8d001174d1537445f6e50e57e Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 4 Feb 2026 21:44:58 +0000 Subject: [PATCH 3/5] Add XML suppression comment syntax and comprehensive tests - Added XML language support to comments.json with prefix "" - Added comprehensive unit tests for XML suppression generation - Added unit tests for XML suppression parsing - Added integration tests for CLI suppress command with XML files - Added integration tests for XML suppression with duration and reviewer - Added analysis test to verify suppressions are respected in XML files - Restored nuget.config to original private feed configuration Co-authored-by: gfs <98900+gfs@users.noreply.github.com> --- .../SuppressionsTest.cs | 234 ++++++++++++++++++ .../Microsoft.DevSkim/resources/comments.json | 7 + nuget.config | 11 +- nuget.config.bak | 7 - 4 files changed, 247 insertions(+), 12 deletions(-) delete mode 100644 nuget.config.bak diff --git a/DevSkim-DotNet/Microsoft.DevSkim.Tests/SuppressionsTest.cs b/DevSkim-DotNet/Microsoft.DevSkim.Tests/SuppressionsTest.cs index 9d7cc85d..6af4c94a 100644 --- a/DevSkim-DotNet/Microsoft.DevSkim.Tests/SuppressionsTest.cs +++ b/DevSkim-DotNet/Microsoft.DevSkim.Tests/SuppressionsTest.cs @@ -343,5 +343,239 @@ public void DontChangeFilesWithoutSelectedFindings(string lineBreakSequence, boo string result = File.ReadAllText(sourceFile); Assert.AreEqual(originalContent, result); } + + /// + /// Test that XML suppression comments are generated correctly + /// + [TestMethod] + public void GenerateXmlSuppression() + { + // Test basic XML suppression generation + string suppression = DevSkimRuleProcessor.GenerateSuppressionByLanguage("xml", "DS123456"); + Assert.IsTrue(suppression.StartsWith(""), "XML suppression should end with -->"); + Assert.IsTrue(suppression.Contains("DevSkim: ignore DS123456"), "XML suppression should contain DevSkim ignore directive"); + } + + /// + /// Test that XML suppression comments with duration are generated correctly + /// + [TestMethod] + public void GenerateXmlSuppressionWithDuration() + { + int duration = 30; + DateTime expectedDate = DateTime.Now.AddDays(duration); + string expectedDateStr = expectedDate.ToString("yyyy-MM-dd"); + + string suppression = DevSkimRuleProcessor.GenerateSuppressionByLanguage("xml", "DS123456", duration: duration); + Assert.IsTrue(suppression.StartsWith(""), "XML suppression should end with -->"); + Assert.IsTrue(suppression.Contains($"until {expectedDateStr}"), "XML suppression should contain expiration date"); + } + + /// + /// Test that XML suppression comments with reviewer are generated correctly + /// + [TestMethod] + public void GenerateXmlSuppressionWithReviewer() + { + string suppression = DevSkimRuleProcessor.GenerateSuppressionByLanguage("xml", "DS123456", reviewerName: "TestReviewer"); + Assert.IsTrue(suppression.StartsWith(""), "XML suppression should end with -->"); + Assert.IsTrue(suppression.Contains("by TestReviewer"), "XML suppression should contain reviewer name"); + } + + /// + /// Test that XML suppression comments are properly recognized by the Suppression parser + /// + [TestMethod] + public void ParseXmlSuppression() + { + string xmlLine = ""; + Suppression suppression = new Suppression(xmlLine); + + Assert.IsTrue(suppression.IsInEffect, "Suppression should be in effect"); + Assert.AreEqual("DS123456", suppression.GetSuppressedIds.First(), "Suppression should contain the correct rule ID"); + } + + /// + /// Test that XML suppression comments with expiration are properly recognized by the Suppression parser + /// + [TestMethod] + public void ParseXmlSuppressionWithExpiration() + { + string futureDate = DateTime.Now.AddDays(30).ToString("yyyy-MM-dd"); + string xmlLine = $""; + Suppression suppression = new Suppression(xmlLine); + + Assert.IsTrue(suppression.IsInEffect, "Suppression should be in effect"); + Assert.AreEqual("DS123456", suppression.GetSuppressedIds.First(), "Suppression should contain the correct rule ID"); + Assert.AreEqual(DateTime.ParseExact(futureDate, "yyyy-MM-dd", System.Globalization.CultureInfo.InvariantCulture), + suppression.ExpirationDate, "Suppression should have the correct expiration date"); + } + + /// + /// Test that expired XML suppression comments are not in effect + /// + [TestMethod] + public void ParseExpiredXmlSuppression() + { + string pastDate = DateTime.Now.AddDays(-30).ToString("yyyy-MM-dd"); + string xmlLine = $""; + Suppression suppression = new Suppression(xmlLine); + + Assert.IsFalse(suppression.IsInEffect, "Expired suppression should not be in effect"); + Assert.IsTrue(suppression.IsExpired, "Suppression should be marked as expired"); + } + + /// + /// Test that XML suppression comments with reviewer are properly recognized by the Suppression parser + /// + [TestMethod] + public void ParseXmlSuppressionWithReviewer() + { + string xmlLine = ""; + Suppression suppression = new Suppression(xmlLine); + + Assert.IsTrue(suppression.IsInEffect, "Suppression should be in effect"); + Assert.AreEqual("DS123456", suppression.GetSuppressedIds.First(), "Suppression should contain the correct rule ID"); + Assert.AreEqual("TestReviewer", suppression.Reviewer, "Suppression should have the correct reviewer"); + } + + /// + /// Integration test: Execute suppressions for XML files + /// + [TestMethod] + public void ExecuteSuppressionsForXml() + { + // XML content with an MD5 reference that triggers DevSkim rule + string xmlContent = @"MD5"; + + (string basePath, string sourceFile, string sarifPath) = runAnalysis(xmlContent, "xml"); + + SuppressionCommandOptions opts = new SuppressionCommandOptions + { + Path = basePath, + SarifInput = sarifPath, + ApplyAllSuppression = true + }; + + int resultCode = new SuppressionCommand(opts).Run(); + Assert.AreEqual(0, resultCode); + + string result = File.ReadAllText(sourceFile); + + // Verify that XML-style suppressions were added + Assert.IsTrue(result.Contains(""), "XML suppression comment should be properly closed"); + + // Verify suppression can be parsed + string[] lines = File.ReadAllLines(sourceFile); + bool foundSuppression = false; + foreach (string line in lines) + { + if (line.Contains(""), "XML suppression comment should be properly closed"); + + // Verify suppression contains expected parts + string[] lines = File.ReadAllLines(sourceFile); + foreach (string line in lines) + { + if (line.Contains(""; + + string tempFileName = $"{Path.GetTempFileName()}.xml"; + File.WriteAllText(tempFileName, xmlContent); + + DevSkimRuleSet devSkimRuleSet = DevSkimRuleSet.GetDefaultRuleSet(); + DevSkimRuleProcessor processor = new DevSkimRuleProcessor(devSkimRuleSet, new DevSkimRuleProcessorOptions() + { + EnableSuppressions = true + }); + + IEnumerable issues = processor.Analyze(xmlContent, tempFileName); + + // The DS126858 (MD5) issue should be suppressed + var unsuppressedIssues = issues.Where(i => !i.IsSuppressionInfo && i.Rule.Id == "DS126858"); + Assert.AreEqual(0, unsuppressedIssues.Count(), "MD5 issue should be suppressed"); + + // Cleanup + File.Delete(tempFileName); + } + + /// + /// Test that XML suppression is generated correctly via filename-based method + /// + [TestMethod] + public void GenerateXmlSuppressionByFilename() + { + string suppression = DevSkimRuleProcessor.GenerateSuppressionByFileName("test.xml", "DS123456"); + Assert.IsTrue(suppression.StartsWith(""), "XML suppression should end with -->"); + Assert.IsTrue(suppression.Contains("DevSkim: ignore DS123456"), "XML suppression should contain DevSkim ignore directive"); + } } } \ No newline at end of file diff --git a/DevSkim-DotNet/Microsoft.DevSkim/resources/comments.json b/DevSkim-DotNet/Microsoft.DevSkim/resources/comments.json index e892c1dd..540ab45f 100644 --- a/DevSkim-DotNet/Microsoft.DevSkim/resources/comments.json +++ b/DevSkim-DotNet/Microsoft.DevSkim/resources/comments.json @@ -74,5 +74,12 @@ "inline": "::", "prefix": "Rem", "suffix": "\n" + }, + { + "language": [ + "xml" + ], + "prefix": "" } ] \ No newline at end of file diff --git a/nuget.config b/nuget.config index a0b839c4..be6dddfa 100644 --- a/nuget.config +++ b/nuget.config @@ -1,6 +1,7 @@ - + - - - - + + + + + \ No newline at end of file diff --git a/nuget.config.bak b/nuget.config.bak deleted file mode 100644 index 751d4b6c..00000000 --- a/nuget.config.bak +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - From f9ee44ce70edab6911990fd7b25d0e78b3e6d392 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 4 Feb 2026 22:43:04 +0000 Subject: [PATCH 4/5] Refactor: use explicit .Where() filtering instead of implicit if-statements in foreach loops Co-authored-by: gfs <98900+gfs@users.noreply.github.com> --- .../SuppressionsTest.cs | 44 ++++++++----------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/DevSkim-DotNet/Microsoft.DevSkim.Tests/SuppressionsTest.cs b/DevSkim-DotNet/Microsoft.DevSkim.Tests/SuppressionsTest.cs index 6af4c94a..222b44fe 100644 --- a/DevSkim-DotNet/Microsoft.DevSkim.Tests/SuppressionsTest.cs +++ b/DevSkim-DotNet/Microsoft.DevSkim.Tests/SuppressionsTest.cs @@ -471,18 +471,14 @@ public void ExecuteSuppressionsForXml() // Verify suppression can be parsed string[] lines = File.ReadAllLines(sourceFile); - bool foundSuppression = false; - foreach (string line in lines) + var suppressionLines = lines.Where(line => line.Contains("