From 42ee287c76c85a738949127ae22b9b4c9612e8a4 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 14 Mar 2026 22:08:13 +0000
Subject: [PATCH 1/6] Initial plan
From 18d7e3c8b78607f8d8c7fef6a30824270a2fbe22 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 14 Mar 2026 22:18:45 +0000
Subject: [PATCH 2/6] refactor: make Program.Main internal and use direct call
in tests
Co-authored-by: Malcolmnixon <1863707+Malcolmnixon@users.noreply.github.com>
---
src/DemaConsulting.SarifMark/Program.cs | 2 +-
.../ProgramTests.cs | 23 ++++---------------
2 files changed, 5 insertions(+), 20 deletions(-)
diff --git a/src/DemaConsulting.SarifMark/Program.cs b/src/DemaConsulting.SarifMark/Program.cs
index d12b73b..03c9acb 100644
--- a/src/DemaConsulting.SarifMark/Program.cs
+++ b/src/DemaConsulting.SarifMark/Program.cs
@@ -46,7 +46,7 @@ public static string Version
///
/// Command-line arguments.
/// Exit code: 0 for success, non-zero for failure.
- private static int Main(string[] args)
+ internal static int Main(string[] args)
{
try
{
diff --git a/test/DemaConsulting.SarifMark.Tests/ProgramTests.cs b/test/DemaConsulting.SarifMark.Tests/ProgramTests.cs
index 3b558fe..25dce71 100644
--- a/test/DemaConsulting.SarifMark.Tests/ProgramTests.cs
+++ b/test/DemaConsulting.SarifMark.Tests/ProgramTests.cs
@@ -18,8 +18,6 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
-using System.Reflection;
-
namespace DemaConsulting.SarifMark.Tests;
///
@@ -45,7 +43,7 @@ public void Program_Main_NoArguments_ReturnsError()
Console.SetError(errWriter);
// Act
- var result = InvokeMain([]);
+ var result = Program.Main([]);
// Assert
Assert.AreEqual(1, result);
@@ -73,7 +71,7 @@ public void Program_Main_VersionFlag_DisplaysVersionOnly()
Console.SetOut(outWriter);
// Act
- var result = InvokeMain(["--version"]);
+ var result = Program.Main(["--version"]);
// Assert
Assert.AreEqual(0, result);
@@ -101,7 +99,7 @@ public void Program_Main_HelpFlag_DisplaysHelp()
Console.SetOut(outWriter);
// Act
- var result = InvokeMain(["--help"]);
+ var result = Program.Main(["--help"]);
// Assert
Assert.AreEqual(0, result);
@@ -130,7 +128,7 @@ public void Program_Main_UnknownArgument_ReturnsError()
Console.SetError(errWriter);
// Act
- var result = InvokeMain(["--unknown"]);
+ var result = Program.Main(["--unknown"]);
// Assert
Assert.AreEqual(1, result);
@@ -141,17 +139,4 @@ public void Program_Main_UnknownArgument_ReturnsError()
Console.SetError(originalError);
}
}
-
- ///
- /// Invokes the Main method using reflection.
- ///
- /// Command-line arguments.
- /// The exit code returned by Main.
- private static int InvokeMain(string[] args)
- {
- var programType = typeof(Program).Assembly.GetType("DemaConsulting.SarifMark.Program");
- var mainMethod = programType?.GetMethod("Main", BindingFlags.Static | BindingFlags.NonPublic);
- var result = mainMethod?.Invoke(null, [args]);
- return result is int exitCode ? exitCode : -1;
- }
}
From c01fa26f6dcb2c3c4dcce850664a2358837eb1a6 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 14 Mar 2026 22:23:11 +0000
Subject: [PATCH 3/6] docs: add CLI integration note to sarif.md covering
SarifMark-Sarif-Required (REQDOC-01)
Co-authored-by: Malcolmnixon <1863707+Malcolmnixon@users.noreply.github.com>
---
docs/design/sarif.md | 9 +++++++++
1 file changed, 9 insertions(+)
diff --git a/docs/design/sarif.md b/docs/design/sarif.md
index 4b9224e..9fc91f7 100644
--- a/docs/design/sarif.md
+++ b/docs/design/sarif.md
@@ -95,3 +95,12 @@ satisfies `SarifMark-Rpt-Locations`.
Each result is formatted as a single line ending with two trailing spaces (` `), which
forces a hard line break in rendered markdown. This satisfies `SarifMark-Rpt-LineBreaks`.
+
+## CLI Integration
+
+The requirement `SarifMark-Sarif-Required` (the tool shall require the `--sarif` parameter
+for analysis) is enforced at the command-line layer rather than within this library. The
+`ProcessSarifAnalysis` method in `Program.cs` validates that `--sarif` is provided before
+invoking the SARIF reading layer. See [command-line.md] for full details.
+
+[command-line.md]: command-line.md
From 57dbbcebdc25982e13f8433d0ec124cb1eb89ea0 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 14 Mar 2026 22:31:24 +0000
Subject: [PATCH 4/6] test: add test for unsupported results file extension in
Validation
Co-authored-by: Malcolmnixon <1863707+Malcolmnixon@users.noreply.github.com>
---
.../ValidationTests.cs | 36 +++++++++++++++++++
1 file changed, 36 insertions(+)
diff --git a/test/DemaConsulting.SarifMark.Tests/ValidationTests.cs b/test/DemaConsulting.SarifMark.Tests/ValidationTests.cs
index 93d3581..8021cc7 100644
--- a/test/DemaConsulting.SarifMark.Tests/ValidationTests.cs
+++ b/test/DemaConsulting.SarifMark.Tests/ValidationTests.cs
@@ -165,6 +165,42 @@ public void Validation_Run_WithTrxResultsFile_WritesResultsFile()
}
}
+ ///
+ /// Tests that when a results file path with an unsupported extension is supplied, an error is reported.
+ ///
+ [TestMethod]
+ public void Validation_Run_WithUnsupportedResultsFileExtension_WritesError()
+ {
+ // Arrange - supply a .json results path (not a supported format)
+ var logFile = CreateTempFile(".log");
+ var jsonFile = CreateTempFile(".json");
+ try
+ {
+ int exitCode;
+ using (var context = Context.Create(
+ ["--silent", "--log", logFile, "--results", jsonFile]))
+ {
+ // Act
+ Validation.Run(context);
+ exitCode = context.ExitCode;
+ }
+
+ // Assert - error must be reported and no results file created
+ Assert.AreEqual(1, exitCode,
+ "Exit code should be 1 when an unsupported results file extension is used");
+ var logContent = File.ReadAllText(logFile);
+ Assert.Contains("Unsupported results file format", logContent,
+ "Log should contain the unsupported format error message");
+ Assert.IsFalse(File.Exists(jsonFile),
+ "Results file should not be created for an unsupported extension");
+ }
+ finally
+ {
+ SafeDeleteFile(logFile);
+ SafeDeleteFile(jsonFile);
+ }
+ }
+
///
/// Tests that when a .xml results file path is supplied the file is created and contains JUnit XML content.
///
From ff77a9506324cffb44f0ec4c5a5c866d06761b2b Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 14 Mar 2026 22:59:23 +0000
Subject: [PATCH 5/6] fix: address all review-set findings across all five
reviews
Co-authored-by: Malcolmnixon <1863707+Malcolmnixon@users.noreply.github.com>
---
docs/design/command-line.md | 2 +-
docs/design/sarif.md | 3 +++
docs/reqstream/validation.yaml | 7 ++++---
src/DemaConsulting.SarifMark/Context.cs | 12 +++++++++---
.../PathHelpersTests.cs | 19 +++++++++++++++++++
5 files changed, 36 insertions(+), 7 deletions(-)
diff --git a/docs/design/command-line.md b/docs/design/command-line.md
index 6fd2fb0..796f4ff 100644
--- a/docs/design/command-line.md
+++ b/docs/design/command-line.md
@@ -83,7 +83,7 @@ arguments throw `ArgumentException`, satisfying `SarifMark-Cli-InvalidArgs`.
`WriteLine` writes to `Console.Out` unless `Silent` is set, and also writes to the log
file if one was opened. `WriteError` additionally sets `_hasErrors = true` (making
-`ExitCode` return 1) and writes to `Console.Error` in red. This satisfies
+`ExitCode` return 1) and, unless `Silent` is set, writes to `Console.Error` in red. This satisfies
`SarifMark-Cli-Silent` and `SarifMark-Enf-ExitCode`.
### Log File
diff --git a/docs/design/sarif.md b/docs/design/sarif.md
index 9fc91f7..cd52bb1 100644
--- a/docs/design/sarif.md
+++ b/docs/design/sarif.md
@@ -60,6 +60,9 @@ The static `Read` method loads and parses a SARIF file:
6. Delegates to `ParseResults` to iterate and parse all non-suppressed results. This
satisfies `SarifMark-Sarif-Results` and `SarifMark-Sarif-Reading`.
+Together, steps 1–6 form the complete pipeline for processing a valid SARIF file, satisfying
+`SarifMark-Sarif-Processing`.
+
### Version Extraction
`ExtractToolVersion` checks three fields in priority order: `version`,
diff --git a/docs/reqstream/validation.yaml b/docs/reqstream/validation.yaml
index e8175ad..862dbfe 100644
--- a/docs/reqstream/validation.yaml
+++ b/docs/reqstream/validation.yaml
@@ -18,7 +18,8 @@ sections:
Writing test results to files enables integration with CI/CD systems and provides persistent
records of validation outcomes for tracking quality trends and compliance.
tests:
- - IntegrationTest_ValidateFlag_RunsSelfValidation
+ - Validation_Run_WithTrxResultsFile_WritesResultsFile
+ - Validation_Run_WithXmlResultsFile_WritesResultsFile
- id: SarifMark-Val-TrxFormat
title: The tool shall support TRX format for test results.
@@ -26,7 +27,7 @@ sections:
TRX format support enables integration with Microsoft testing ecosystems and Azure DevOps,
providing native compatibility with Visual Studio and .NET tooling.
tests:
- - IntegrationTest_ValidateFlag_RunsSelfValidation
+ - Validation_Run_WithTrxResultsFile_WritesResultsFile
- id: SarifMark-Val-JUnitFormat
title: The tool shall support JUnit format for test results.
@@ -34,7 +35,7 @@ sections:
JUnit format support enables broad compatibility with CI/CD platforms like Jenkins, GitHub Actions,
and GitLab CI, which commonly use this standard format for test reporting.
tests:
- - IntegrationTest_ValidateFlag_RunsSelfValidation
+ - Validation_Run_WithXmlResultsFile_WritesResultsFile
- title: Quality Enforcement
requirements:
diff --git a/src/DemaConsulting.SarifMark/Context.cs b/src/DemaConsulting.SarifMark/Context.cs
index dd36ce7..22eb848 100644
--- a/src/DemaConsulting.SarifMark/Context.cs
+++ b/src/DemaConsulting.SarifMark/Context.cs
@@ -365,9 +365,15 @@ public void WriteError(string message)
if (!Silent)
{
var previousColor = Console.ForegroundColor;
- Console.ForegroundColor = ConsoleColor.Red;
- Console.Error.WriteLine(message);
- Console.ForegroundColor = previousColor;
+ try
+ {
+ Console.ForegroundColor = ConsoleColor.Red;
+ Console.Error.WriteLine(message);
+ }
+ finally
+ {
+ Console.ForegroundColor = previousColor;
+ }
}
// Write to log file if logging is enabled
diff --git a/test/DemaConsulting.SarifMark.Tests/PathHelpersTests.cs b/test/DemaConsulting.SarifMark.Tests/PathHelpersTests.cs
index 4c56716..8a6d4a5 100644
--- a/test/DemaConsulting.SarifMark.Tests/PathHelpersTests.cs
+++ b/test/DemaConsulting.SarifMark.Tests/PathHelpersTests.cs
@@ -100,6 +100,25 @@ public void PathHelpers_SafePathCombine_PathWithDoubleDots_ThrowsArgumentExcepti
Assert.Contains("Invalid path component", exception.Message);
}
+ ///
+ /// Test that SafePathCombine throws ArgumentException for a filename that contains ".." as a substring.
+ /// The check uses a strict Contains("..") comparison, so names like "v1..0.sarif" are rejected
+ /// even though they do not represent path traversal. This is intentional: the design favours
+ /// rejecting a small set of unusual but valid names over risking traversal edge-cases.
+ ///
+ [TestMethod]
+ public void PathHelpers_SafePathCombine_FilenameWithEmbeddedDots_ThrowsArgumentException()
+ {
+ // Arrange - "v1..0.sarif" contains ".." but is not a path traversal
+ var basePath = "/home/user";
+ var relativePath = "v1..0.sarif";
+
+ // Act & Assert - the strict substring check rejects this name
+ var exception = Assert.Throws(() =>
+ PathHelpers.SafePathCombine(basePath, relativePath));
+ Assert.Contains("Invalid path component", exception.Message);
+ }
+
///
/// Test that SafePathCombine throws ArgumentException for absolute paths.
///
From 145082afa5367e8bdbd75922863cef8e78e55540 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sat, 14 Mar 2026 23:07:17 +0000
Subject: [PATCH 6/6] refactor: merge SarifMark-Integration-Review into
SarifMark-CLI-Review
Co-authored-by: Malcolmnixon <1863707+Malcolmnixon@users.noreply.github.com>
---
.reviewmark.yaml | 16 ++++++----------
1 file changed, 6 insertions(+), 10 deletions(-)
diff --git a/.reviewmark.yaml b/.reviewmark.yaml
index 9b71d17..d79f529 100644
--- a/.reviewmark.yaml
+++ b/.reviewmark.yaml
@@ -18,14 +18,19 @@ evidence-source:
# Review sets grouping files by logical unit of review.
reviews:
- id: SarifMark-CLI-Review
- title: Review of SarifMark Command Line
+ title: Review of SarifMark Command Line and Integration Tests
paths:
- "docs/reqstream/command-line.yaml"
+ - "docs/reqstream/platform.yaml"
+ - "docs/reqstream/ots-software.yaml"
- "docs/design/command-line.md"
- "src/**/Program.cs"
- "src/**/Context.cs"
- "test/**/ProgramTests.cs"
- "test/**/ContextTests.cs"
+ - "test/**/IntegrationTests.cs"
+ - "test/**/Runner.cs"
+ - "test/**/AssemblyInfo.cs"
- id: SarifMark-SARIF-Review
title: Review of SarifMark SARIF and Reporting
@@ -51,12 +56,3 @@ reviews:
- "docs/design/utilities.md"
- "src/**/PathHelpers.cs"
- "test/**/PathHelpersTests.cs"
-
- - id: SarifMark-Integration-Review
- title: Review of SarifMark Integration Tests
- paths:
- - "docs/reqstream/platform.yaml"
- - "docs/reqstream/ots-software.yaml"
- - "test/**/IntegrationTests.cs"
- - "test/**/Runner.cs"
- - "test/**/AssemblyInfo.cs"