Skip to content

Release 0.3.2: security hardening, --clean-env, project-level fallback, security model - #43

Merged
csa7mdm merged 8 commits into
mainfrom
release/v0.3.2
Sep 24, 2026
Merged

csa7mdm merged 8 commits into
mainfrom
release/v0.3.2

Conversation

@csa7mdm

@csa7mdm csa7mdm commented Sep 24, 2026

Copy link
Copy Markdown
Owner

Security release. Two external reviews of 0.3.1 were checked claim by claim against the code first: several claims were wrong or outdated, but they led to real findings, fixed here. Upgrade from 0.3.0/0.3.1.

Security

Finding Fix
Argument injection: tool values were concatenated into dotnet/git command lines. -p:Version=1.0 -p:CustomBeforeMicrosoftCommonTargets=evil.targets imported a targets file (code runs during build); gitBase=--output=... made git write a file; framework, branch and remote values could add flags All child processes use ProcessStartInfo.ArgumentList; framework/runtime/configuration/property names/git refs validated; property values escape ; and ,; MTP filter may only carry --filter*/--treenode-filter options
Path check: StartsWith on un-normalized paths let .. and look-alike sibling folders through PathBoundary.IsWithin (GetFullPath + GetRelativePath)
Child processes inherit tokens and cloud credentials New opt-in --clean-env (allow-listed environment; not a sandbox, and says so)

Also

  • dotnet_test_affected project-level fallback: out of budget or too large → only the test projects referencing the changed projects (ranScope, testProjectsRun), skipping helper libraries without tests. On Polly every tested change is in Polly.Core, which all test projects use, so it correctly still runs everything there; it pays off in solutions with independent modules.
  • VSTest name filter widens past the command-line limit (like the MTP path).
  • WorkflowEngine: no Task.Run around async steps.
  • Security model in SECURITY.md, README and the wiki (Security page is live; 0.3.2-only lines are marked "(0.3.2+)").

How it was built

Two Sonnet subagents in parallel worktrees (security; fallback + workflow). The security agent hit a usage limit before building or writing tests; I completed it, fixed three gaps found in review (, as MSBuild separator, proxy/cert variables for --clean-env, MSBuild switches smuggled through the MTP filter incl. /p: with C:/ paths) and wrote the tests. Merge conflict in RunAffected resolved; both build loops share one argument-list helper.

Verification

  • dotnet build -c Release 0 errors; dotnet test 118/118 (new: 21 security tests, 6 runner-argument tests, 5 fallback tests, 2 build-argument tests).
  • End to end on Polly with the packed server under --clean-env: framework, gitBase and MTP-filter injection strings all rejected (no file written); a real build + affected run succeeds with the scrubbed environment (5 tests, 27.5 s); fallback decisions checked on 3 real commits.
  • --help lists --clean-env; startup logs kept/dropped counts, names only at Debug, never values.

After merge: tag v0.3.2, mcp-publisher publish, then I refresh the wiki (tool reference regenerated from 0.3.2, "(0.3.2+)" markers removed).

🤖 Generated with Claude Code

csa7mdm and others added 8 commits September 24, 2026 05:21
…iming from the shipped-defaults session

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s redundant Task.Run

When the Roslyn reference walk in dotnet_test_affected runs out of budget or its
selection is too large, it used to run the whole solution. Try a cheaper
middle ground first: walk the project reference graph (AffectedTestFinder.
FindAffectedTestProjects) to find the test projects that can be affected by
the change, and run just those (no name filter) when that's a real subset of
all test projects. Falls back to the whole solution only when the reachable
set is empty or covers every test project. Response now reports RanScope
(selection/projects/solution) and TestProjectsRun alongside the existing
SelectionComplete/RanWholeSolution fields.

WorkflowEngine wrapped already-async parallel step execution in Task.Run for
no reason - ExecuteStepAsync already catches its own exceptions, so starting
its task directly is enough to run steps concurrently.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…referencing xUnit can't be run)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ath boundary, --clean-env, VSTest filter widening

- dotnet and git child processes get ProcessStartInfo.ArgumentList (one value = one argument) in BuildService,
  GitService and TestRunner; framework/runtime/configuration/MSBuild property names and git refs are validated;
  MSBuild property values escape ';' and ','; under Microsoft.Testing.Platform the filter may only carry
  --filter* / --treenode-filter options (no -p:/--property smuggling)
- PathBoundary.IsWithin (GetFullPath + GetRelativePath) replaces the StartsWith solution-directory check
- --clean-env (opt-in): child processes get an allow-listed environment; logs kept/dropped counts, names at Debug
- VSTest name filter widens method -> class -> none past the command-line limit, like the MTP path
- Tests for every injection string, path bypass, allow-list and widening case

Started by a subagent that hit a usage limit before building or writing tests; completed, reviewed and tested here.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…build loops use argument lists via BuildEachAsync)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@csa7mdm
csa7mdm merged commit a016015 into main Sep 24, 2026
6 of 7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant