Skip to content
35 changes: 27 additions & 8 deletions .reviewmark.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -182,14 +182,33 @@ reviews:
- "src/**/SelfTest/Validation.cs" # implementation
- "test/**/SelfTest/SelfTestTests.cs" # subsystem tests (no separate unit test file)

- id: VersionMark-SelfTest-PathHelpers
title: Review that VersionMark SelfTest PathHelpers Implementation is Correct
paths:
- "docs/reqstream/version-mark/self-test/path-helpers.yaml" # requirements
- "docs/design/version-mark/self-test/path-helpers.md" # design
- "docs/verification/version-mark/self-test/path-helpers.md" # verification
- "src/**/SelfTest/PathHelpers.cs" # implementation
- "test/**/SelfTest/PathHelpersTests.cs" # unit tests
# VersionMark - Utilities
- id: VersionMark-Utilities
title: Review that VersionMark Utilities Satisfies Subsystem Requirements
paths:
- "docs/reqstream/version-mark/utilities.yaml"
- "docs/design/version-mark/utilities.md"
- "docs/verification/version-mark/utilities.md"
- "test/**/Utilities/GlobMatcherTests.cs" # subsystem tests (no separate subsystem test file needed)
- "test/**/Utilities/PathHelpersTests.cs" # subsystem tests

- id: VersionMark-Utilities-GlobMatcher
title: Review that VersionMark Utilities GlobMatcher Implementation is Correct
paths:
- "docs/reqstream/version-mark/utilities/glob-matcher.yaml"
- "docs/design/version-mark/utilities/glob-matcher.md"
- "docs/verification/version-mark/utilities/glob-matcher.md"
- "src/**/Utilities/GlobMatcher.cs"
- "test/**/Utilities/GlobMatcherTests.cs"

- id: VersionMark-Utilities-PathHelpers
title: Review that VersionMark Utilities PathHelpers Implementation is Correct
paths:
- "docs/reqstream/version-mark/utilities/path-helpers.yaml" # requirements
- "docs/design/version-mark/utilities/path-helpers.md" # design
- "docs/verification/version-mark/utilities/path-helpers.md" # verification
- "src/**/Utilities/PathHelpers.cs" # implementation
- "test/**/Utilities/PathHelpersTests.cs" # unit tests

# OTS Items
- id: OTS-BuildMark
Expand Down
4 changes: 3 additions & 1 deletion docs/design/definition.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ input-files:
- docs/design/version-mark/publishing/markdown-formatter.md
- docs/design/version-mark/self-test.md
- docs/design/version-mark/self-test/validation.md
- docs/design/version-mark/self-test/path-helpers.md
- docs/design/version-mark/utilities.md
- docs/design/version-mark/utilities/glob-matcher.md
- docs/design/version-mark/utilities/path-helpers.md
template: template.html
table-of-contents: true
number-sections: true
20 changes: 13 additions & 7 deletions docs/design/introduction.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ The purpose of this document is to:

## Scope

This document covers the design of five subsystems within VersionMark:
This document covers the design of six subsystems within VersionMark:

- The **Cli Subsystem**: the `Program` entry point and `Context` class
that handle argument parsing, output routing, and program flow control
Expand All @@ -25,8 +25,10 @@ This document covers the design of five subsystems within VersionMark:
captured version data to and from JSON
- The **Publishing Subsystem**: the `MarkdownFormatter` class that generates the markdown
version report from captured data
- The **SelfTest Subsystem**: the `Validation` class and `PathHelpers` utility that
together provide built-in verification of the tool's core functionality
- The **SelfTest Subsystem**: the `Validation` class that provides built-in verification
of the tool's core functionality
- The **Utilities Subsystem**: the `GlobMatcher` class that provides glob-pattern file matching
and the `PathHelpers` class that provides safe path combination for use by other subsystems

This document does not cover installation, end-user usage patterns, or the CI/CD pipeline
configuration. Those topics are addressed in the *VersionMark User Guide* and the
Expand All @@ -50,8 +52,10 @@ VersionMark (System) Version capture/publish tool
│ └── VersionInfo (Unit) JSON version data record
├── Publishing (Subsystem) Markdown report publishing
│ └── MarkdownFormatter (Unit) Version report formatter
└── SelfTest (Subsystem) Built-in self-validation
├── Validation (Unit) Self-validation runner
├── SelfTest (Subsystem) Built-in self-validation
│ └── Validation (Unit) Self-validation runner
└── Utilities (Subsystem) General-purpose helper utilities
├── GlobMatcher (Unit) Glob-pattern file matching
└── PathHelpers (Unit) Safe path combination
```

Expand All @@ -74,8 +78,10 @@ src/DemaConsulting.VersionMark/
│ └── VersionInfo.cs — captured version data record
├── Publishing/
│ └── MarkdownFormatter.cs — markdown report generation
└── SelfTest/
├── Validation.cs — self-validation test runner
├── SelfTest/
│ └── Validation.cs — self-validation test runner
└── Utilities/
├── GlobMatcher.cs — glob-pattern file matching
└── PathHelpers.cs — safe path utilities
```

Expand Down
15 changes: 9 additions & 6 deletions docs/design/version-mark.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,18 @@ Cli Subsystem → Configuration Subsystem → (shell)
### Publish Mode

```text
Cli Subsystem → Capture Subsystem (VersionInfo.LoadFromFile) → Publishing Subsystem
↓
markdown report file
Cli Subsystem → Utilities Subsystem (GlobMatcher.FindMatchingFiles)
↓
Capture Subsystem (VersionInfo.LoadFromFile) → Publishing Subsystem
↓
markdown report file
```

1. The Cli Subsystem (Program) parses arguments and calls `RunPublish`.
2. `RunPublish` resolves glob patterns, then uses the Capture Subsystem to load each
JSON file via `VersionInfo.LoadFromFile`.
3. The Publishing Subsystem (`MarkdownFormatter.Format`) converts the loaded records into
2. `RunPublish` uses `GlobMatcher.FindMatchingFiles` (Utilities Subsystem) to resolve glob
patterns into a concrete list of JSON file paths.
3. The Capture Subsystem loads each JSON file via `VersionInfo.LoadFromFile`.
4. The Publishing Subsystem (`MarkdownFormatter.Format`) converts the loaded records into
a markdown string, which is written to the report file.

### Lint Mode
Expand Down
20 changes: 4 additions & 16 deletions docs/design/version-mark/self-test.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@

### Overview

The SelfTest subsystem provides built-in verification of the tool's core functionality
and safe path construction for use within that verification. It consists of two units:
`Validation` (the self-validation test runner) and `PathHelpers` (a safe path combination
utility used internally by `Validation`).
The SelfTest subsystem provides built-in verification of the tool's core functionality.
It consists of one unit: `Validation` (the self-validation test runner).

The validation subsystem is invoked when the `--validate` flag is passed and can write
results to a TRX or JUnit XML file when `--results` is also provided. This satisfies
Expand All @@ -22,21 +20,11 @@ writes a structured results file.

See *Validation Unit Design* for the full unit design.

#### PathHelpers

The `PathHelpers` class (`PathHelpers.cs`) provides a single static method,
`SafePathCombine`, used internally by `Validation` when constructing paths inside temporary
directories. It protects against path-traversal attacks by ensuring the resolved combined
path stays within the intended base directory.

See *PathHelpers Unit Design* for the full unit design.

### Subsystem Interactions

`Validation.Run` creates temporary directories via the private `TemporaryDirectory` helper
class and uses `PathHelpers.SafePathCombine` for all path construction within those
directories. `PathHelpers` has no dependency on `Validation` and may be considered a pure
utility within the subsystem.
class and uses `PathHelpers.SafePathCombine` from the Utilities subsystem for all path
construction within those directories.

The subsystem depends on:

Expand Down
40 changes: 40 additions & 0 deletions docs/design/version-mark/utilities.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
## Utilities Subsystem

### Overview

The Utilities subsystem provides general-purpose helper classes used by other subsystems
Comment thread
Malcolmnixon marked this conversation as resolved.
within VersionMark. It consists of two units: `GlobMatcher`, which implements glob-pattern
file matching for the Publish mode, and `PathHelpers`, which provides safe path combination
to protect against path-traversal attacks.

This subsystem satisfies requirements `VersionMark-Utilities-GlobMatch` and
`VersionMark-Utilities-SafePath`.

### Units

#### GlobMatcher

The `GlobMatcher` class (`GlobMatcher.cs`) provides glob-pattern file matching. It exposes
two methods: `FindMatchingFiles`, which accepts an array of glob patterns and returns a
sorted, deduplicated list of matching file paths; and `SplitAbsolutePattern`, which splits
an absolute glob pattern into its root directory and relative pattern components.

See *GlobMatcher Unit Design* for the full unit design.

#### PathHelpers

The `PathHelpers` class (`PathHelpers.cs`) provides a single static method,
`SafePathCombine`, which safely combines a base path and a relative path while
preventing path-traversal attacks. It is used by `SelfTest.Validation` when
constructing paths inside temporary directories.

See *PathHelpers Unit Design* for the full unit design.

### Subsystem Interactions

`GlobMatcher.FindMatchingFiles` is called by the Cli Subsystem (`Program.RunPublish`) to
resolve the glob patterns supplied on the command line into a concrete list of JSON capture
files. `PathHelpers.SafePathCombine` is called by the SelfTest subsystem (`Validation.Run`)
when constructing paths inside temporary directories. The Utilities subsystem has no
dependencies on other VersionMark subsystems; it depends only on
`Microsoft.Extensions.FileSystemGlobbing` for pattern evaluation.
65 changes: 65 additions & 0 deletions docs/design/version-mark/utilities/glob-matcher.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
### GlobMatcher Unit

#### Overview

`GlobMatcher` is a static utility class that provides glob-pattern file matching. It
supports both relative patterns (evaluated against the current directory) and absolute
patterns (evaluated from their own root directory), and returns a sorted, deduplicated
list of full file paths. It uses `Microsoft.Extensions.FileSystemGlobbing` for pattern
evaluation.

#### FindMatchingFiles Method

```csharp
internal static List<string> FindMatchingFiles(string[] globPatterns)
```

Finds all files matching the specified glob patterns and returns them as a sorted list of
full paths.

**Processing steps:**

1. Iterate over each pattern in `globPatterns`.
2. If a pattern is rooted (`Path.IsPathRooted`), call `SplitAbsolutePattern` to obtain the
root directory and relative pattern, then use a `Matcher` against that directory.
3. If the pattern is relative, collect it into a separate list.
4. After iterating, if any relative patterns were collected, run a single `Matcher` against
`Directory.GetCurrentDirectory()` covering all relative patterns.
5. Combine all matches into a `HashSet<string>` (case-insensitive) to deduplicate, then
return the sorted result.

#### SplitAbsolutePattern Helper

```csharp
internal static (string rootDir, string relativePattern) SplitAbsolutePattern(string absolutePattern)
```

Splits an absolute glob pattern into its root directory and the relative pattern to be
passed to the `Matcher`.

**Algorithm:**

1. Determine the path root via `Path.GetPathRoot`.
2. Find the index of the first wildcard character (`*`, `?`, or `[`).
3. If no wildcard is found, return `(Path.GetDirectoryName, Path.GetFileName)`.
4. Find the last directory separator before the wildcard using `LastIndexOfAny` searching
backwards from the wildcard position.
5. Split at that separator, handling the drive-root edge case where the separator is the
first character (e.g. `/`) or where the root segment lacks a trailing separator (e.g.
`C:` on Windows).

#### Design Decisions

- **Separate absolute and relative handling**: Absolute patterns are rooted at a specific
directory and must be evaluated there, while relative patterns are evaluated relative to
the current directory. Separating the two cases avoids incorrect matches.
- **Single Matcher for relative patterns**: Collecting all relative patterns into one
`Matcher` run reduces directory enumeration overhead compared to one run per pattern.
- **Case-insensitive deduplication**: Using a case-insensitive `HashSet` prevents
duplicates when patterns overlap or when the file system is case-insensitive.
- **Sorted output**: Returning a sorted list makes the output deterministic, simplifying
testing and producing a consistent report order.

`GlobMatcher` is used by `Program.RunPublish` to resolve command-line glob patterns into
a concrete file list. This satisfies requirements `VersionMark-GlobMatcher-FindFiles` and
`VersionMark-GlobMatcher-AbsolutePaths`.
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,6 @@ the base directory.
- **No logging or error accumulation**: `SafePathCombine` is a pure utility method that throws
on invalid input; it does not interact with the `Context` or any output mechanism.

`PathHelpers` is used by `Validation` when constructing paths inside temporary directories
for self-validation tests. This satisfies requirement `VersionMark-PathHelpers-SafeCombine`.
`PathHelpers` is used by `SelfTest.Validation` when constructing paths inside temporary
directories for self-validation tests. This satisfies requirement
`VersionMark-PathHelpers-SafeCombine`.
24 changes: 24 additions & 0 deletions docs/reqstream/version-mark/utilities.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
sections:
- title: VersionMark Requirements
sections:
- title: Utilities
requirements:
- id: VersionMark-Utilities-GlobMatch
title: The Utilities subsystem shall provide glob-pattern file matching.
justification: |
Centralizing glob-pattern file matching in a dedicated subsystem separates
the matching concern from the CLI dispatch logic, making both easier to test
and maintain independently.
children:
- VersionMark-GlobMatcher-FindFiles
- VersionMark-GlobMatcher-AbsolutePaths

- id: VersionMark-Utilities-SafePath
title: The Utilities subsystem shall provide safe path combination.
justification: |
Centralizing safe path combination in a dedicated subsystem makes the
path-traversal protection reusable across all subsystems that construct
file paths from partially-trusted input.
children:
- VersionMark-PathHelpers-SafeCombine
31 changes: 31 additions & 0 deletions docs/reqstream/version-mark/utilities/glob-matcher.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
---
sections:
- title: GlobMatcher Unit Requirements
requirements:
- id: VersionMark-GlobMatcher-FindFiles
title: The GlobMatcher class shall find files matching relative glob patterns relative to the current directory.
justification: |
Publish mode accepts glob patterns supplied on the command line, which are
typically relative to the working directory. GlobMatcher must evaluate these
relative patterns against the current directory so callers do not need to
resolve them manually.
tests:
- GlobMatcher_FindMatchingFiles_RelativePattern_ReturnsMatchingFiles
- GlobMatcher_FindMatchingFiles_EmptyPatterns_ReturnsEmptyList
- GlobMatcher_FindMatchingFiles_PatternMatchingNoFiles_ReturnsEmptyList
- GlobMatcher_FindMatchingFiles_MixedPatterns_ReturnsCombinedFiles

- id: VersionMark-GlobMatcher-AbsolutePaths
title: >-
The GlobMatcher class shall find files matching absolute glob patterns
regardless of the current working directory.
justification: |
CI/CD pipelines frequently pass fully-qualified artifact paths to VersionMark.
GlobMatcher must evaluate absolute patterns from their own root directory so
that the caller's current working directory does not affect the result.
tests:
- GlobMatcher_FindMatchingFiles_AbsolutePattern_ReturnsMatchingFiles
- GlobMatcher_FindMatchingFiles_SingleFileAbsolutePath_ReturnsSingleFile
- GlobMatcher_FindMatchingFiles_MixedPatterns_ReturnsCombinedFiles
- GlobMatcher_SplitAbsolutePattern_PatternWithWildcard_SplitsCorrectly
- GlobMatcher_SplitAbsolutePattern_PatternWithoutWildcard_SplitsAtLastSeparator
4 changes: 3 additions & 1 deletion docs/verification/definition.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,9 @@ input-files:
- docs/verification/version-mark/publishing/markdown-formatter.md
- docs/verification/version-mark/self-test.md
- docs/verification/version-mark/self-test/validation.md
- docs/verification/version-mark/self-test/path-helpers.md
- docs/verification/version-mark/utilities.md
- docs/verification/version-mark/utilities/glob-matcher.md
- docs/verification/version-mark/utilities/path-helpers.md
- docs/verification/ots.md
- docs/verification/ots/buildmark.md
- docs/verification/ots/fileassert.md
Expand Down
3 changes: 2 additions & 1 deletion docs/verification/version-mark.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,14 @@ This section documents the verification design for the VersionMark system. Versi
a .NET global tool that captures tool version information from CI/CD job environments and
publishes consolidated version reports as markdown.

The verification strategy is organized around five subsystems:
The verification strategy is organized around six subsystems:

- **Cli** - command-line argument parsing and program dispatch
- **Configuration** - YAML configuration loading and validation
- **Capture** - tool version capture and JSON serialization
- **Publishing** - markdown report generation
- **SelfTest** - built-in self-validation
- **Utilities** - glob-pattern file matching and safe path combination

## Verification Approach

Expand Down
Loading
Loading