Skip to content

sec (5/9): reject unsafe targets on the Windows host shell - #29

Merged
jesse-merhi merged 1 commit into
mainfrom
sec/05-windows-target-guard
Jul 26, 2026
Merged

sec (5/9): reject unsafe targets on the Windows host shell#29
jesse-merhi merged 1 commit into
mainfrom
sec/05-windows-target-guard

Conversation

@jesse-merhi

@jesse-merhi jesse-merhi commented Jul 23, 2026

Copy link
Copy Markdown
Member

What changes

On the Windows host shell (--sandbox off on Windows), a user-defined
scanner target containing %, ", or ! is now rejected instead of being
interpolated into the cmd.exe command line.

Why it matters

cmd.exe expands %VAR% even inside double quotes, expands !VAR! when delayed
expansion is on, and does not let backslash escape a ". So a target path
containing those characters could break out of the intended argument and inject
host commands. Since a target name/path can come from untrusted skill content,
that is a command-injection vector on Windows. Rejecting those characters closes
it. This affects only the Windows host shell path — the Docker sandbox (the
default) and POSIX hosts pass the target as a positional "$1" argument and are
unaffected.

Before / after

Target on Windows host Before After
skill%PATH%.md interpolated into cmd.exe line — expansion/injection risk scanner result failed, no command run
plain-skill.md runs runs (unchanged)

The guard is unsafeWindowsShellTarget(target); it fires only when the resolved
shell is the Windows host shell, so Docker/POSIX behavior is untouched.

Verify

go test ./internal/runner/ -run 'UnsafeWindowsShellTarget' -count=1

TestUnsafeWindowsShellTargetDetectsInjectionCharacters covers %, ", !
(rejected) and safe targets (allowed). Cross-compiles clean:
GOOS=windows go build ./....

Copilot AI review requested due to automatic review settings July 23, 2026 12:51

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@jesse-merhi
jesse-merhi force-pushed the sec/04-benchmark-preflight branch from 6f1cade to 33a4013 Compare July 26, 2026 02:34
@jesse-merhi
jesse-merhi force-pushed the sec/05-windows-target-guard branch from 82956cd to 5ecd550 Compare July 26, 2026 02:35
@jesse-merhi
jesse-merhi changed the base branch from sec/04-benchmark-preflight to main July 26, 2026 02:35
@clawsweeper clawsweeper Bot added rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. P1 Urgent regression or broken agent/channel workflow affecting real users now. merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. labels Jul 26, 2026
@clawsweeper

clawsweeper Bot commented Jul 26, 2026

Copy link
Copy Markdown

Codex review: needs maintainer review before merge. Reviewed July 25, 2026, 10:38 PM ET / July 26, 2026, 02:38 UTC.

ClawSweeper review

What this changes

Reject user-defined scanner targets containing %, !, or " when ClawScan runs a scanner through the Windows host cmd.exe shell, while leaving Docker and POSIX positional-argument execution unchanged.

Merge readiness

⚠️ Ready for maintainer review - 3 items remain

Keep this PR open for maintainer review. It is a narrowly scoped security hardening for the merged user-defined-scanner path, and its member author plus the intentional Windows host compatibility change make automatic cleanup inappropriate.

Priority: P1
Reviewed head: 5ecd550f1b445714115550f71e45b416240b19a6
Owner decision: Required. See Decision needed.

Review scores

Measure Result What it means
Overall readiness 🐚 platinum hermit (4/6) The patch is compact, aligned with the existing scanner runner, and covered by focused unit cases; only the intentional Windows compatibility boundary needs maintainer acceptance.
Proof confidence 🌊 off-meta tidepool Not applicable: This is a member-authored PR, so the external-contributor proof gate does not apply; the PR body supplies test commands but no separate live Windows-host transcript.
Patch quality 🐚 platinum hermit (4/6) No actionable review findings were identified.

Verification

Check Result Evidence
Real behavior Not applicable Not applicable: This is a member-authored PR, so the external-contributor proof gate does not apply; the PR body supplies test commands but no separate live Windows-host transcript.
Evidence reviewed 4 items Proposed Windows guard: The PR adds a guard before command rendering that returns a failed scanner result when the non-POSIX resolved shell receives a target containing %, !, or "; this is the stated Windows host-shell path.
Focused coverage: The branch adds unit coverage for the helper's rejected %, !, and quote cases and for ordinary safe URL and filesystem targets.
Feature provenance: The related merged PR at #23 introduced profile-defined user scanners and identifies this runner path as the command-rendering seam; this PR is a focused follow-up hardening change on that feature.
Findings None None.
Security None None.

How this fits together

User-defined scanners are profile-defined commands that receive a scan target and emit raw JSON evidence. The runner resolves either a Docker/POSIX shell or the host shell, renders the target into the scanner command, then records the scanner result for the CLI output.

flowchart LR
  A[Profile-defined scanner] --> B[Scanner runner]
  C[Scan target] --> B
  B --> D{Resolved execution shell}
  D -->|Docker or POSIX| E[Positional target argument]
  D -->|Windows host shell| F[Reject unsafe target characters]
  E --> G[Scanner command]
  F --> H[Failed scanner result]
  G --> I[Raw JSON evidence]
Loading

Decision needed

Question Recommendation
Should ClawScan intentionally reject these otherwise valid target paths on Windows host-shell runs to prevent cmd.exe interpolation, rather than attempting a separate Windows quoting strategy? Accept the fail-closed guard: Keep the targeted rejection and direct affected operators to the Docker sandbox or a compatible target path.

Why: The patch deliberately changes an existing host execution path from run to fail for certain filenames, so the security-versus-compatibility boundary needs maintainer acceptance.

Before merge

  • Resolve merge risk (P1) - Existing Windows --sandbox off workflows whose target paths contain %, !, or " will now fail instead of running; operators must use the Docker sandbox or choose a compatible target path.
  • Complete next step (P1) - A maintainer needs to approve the intentional fail-closed behavior for existing Windows host-shell target paths; there is no narrow mechanical repair to dispatch first.
Agent review details

Security

None.

Review metrics

Metric Value Why it matters
Patch surface 2 files affected; 38 additions, 0 deletions The change is constrained to the command-rendering guard and its focused unit coverage.

Merge-risk options

Maintainer options:

  1. Accept the fail-closed Windows behavior (recommended)
    Merge after a maintainer confirms that rejecting unsafe host-shell targets is the intended compatibility boundary and Docker remains the documented alternative.
  2. Pause for a safe Windows invocation design
    Do not merge until a tested non-interpolating Windows command path can retain support for targets containing the rejected characters.

Technical review

Best possible solution:

Keep the fail-closed Windows host-shell guard and have a maintainer explicitly accept the narrow compatibility trade-off, with the Docker sandbox remaining the supported escape path for affected targets.

Do we have a high-confidence way to reproduce the issue?

No high-confidence live reproduction was established in this review because an actual Windows host-shell run was not available; the source path and focused helper cases make the affected interpolation path source-reproducible.

Is this the best way to solve the issue?

Yes, conditionally: rejecting untrusted target characters before cmd.exe rendering is the narrowest safe repair shown here, provided maintainers accept the documented Windows host-shell compatibility boundary.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 5cfa90b129f4.

Labels

Label changes:

  • add P1: A Windows host-shell command-injection boundary affects real scanner executions when users explicitly run outside the default sandbox.
  • add merge-risk: 🚨 compatibility: The new fail-closed behavior intentionally stops existing Windows host-shell scans for target names containing %, !, or ".
  • add rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🌊 off-meta tidepool and patch quality is 🐚 platinum hermit.
  • add status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Not applicable: This is a member-authored PR, so the external-contributor proof gate does not apply; the PR body supplies test commands but no separate live Windows-host transcript.

Label justifications:

  • P1: A Windows host-shell command-injection boundary affects real scanner executions when users explicitly run outside the default sandbox.
  • merge-risk: 🚨 compatibility: The new fail-closed behavior intentionally stops existing Windows host-shell scans for target names containing %, !, or ".
  • rating: 🐚 platinum hermit: Overall readiness is 🐚 platinum hermit; proof is 🌊 off-meta tidepool and patch quality is 🐚 platinum hermit.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Not applicable: This is a member-authored PR, so the external-contributor proof gate does not apply; the PR body supplies test commands but no separate live Windows-host transcript.

Evidence

What I checked:

Likely related people:

  • jesse-merhi: The merged user-defined-scanner implementation and this focused follow-up both trace to the same contributor, including the command-rendering runner surface. (role: feature owner and recent area contributor; confidence: high; commits: 656e558b4ba5, 5ecd550f1b44; files: internal/runner/user_defined_scanner.go, internal/runner/runner_test.go)

Rank-up moves

Optional improvements that raise the rating; they are not merge blockers.

  • Confirm the fail-closed Windows host-shell compatibility policy before merge.

Rating scale

Score Internal tier Crab rank Meaning
6/6 S 🦀 challenger crab Exceptional readiness
5/6 A 🦞 diamond lobster Very strong readiness
4/6 B 🐚 platinum hermit Good normal PR; ordinary maintainer review
3/6 C 🦐 gold shrimp Useful, but confidence is limited
2/6 D 🦪 silver shellfish Proof or implementation needs work
1/6 F 🧂 unranked krab Not merge-ready
N/A NA 🌊 off-meta tidepool Rating does not apply

Overall follows the weaker of proof and patch quality.
Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

Workflow

  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.

Reject target strings that cmd.exe cannot safely interpolate when Docker is disabled, preventing expansion or command injection through %, !, and quote characters.
@jesse-merhi
jesse-merhi force-pushed the sec/05-windows-target-guard branch from 5ecd550 to dec077c Compare July 26, 2026 04:01
@clawsweeper

clawsweeper Bot commented Jul 26, 2026

Copy link
Copy Markdown

ClawSweeper status: review started.

I am starting a fresh review of this pull request: sec (5/9): reject unsafe targets on the Windows host shell This is item 1/1 in the current shard. Shard 0/1.

This placeholder means the worker is alive and reading the current context. I will edit this same comment with the actual review when the claws are done clicking.

Crustacean status: shell secured, claws on keyboard, evidence pebbles being sorted.

@jesse-merhi
jesse-merhi merged commit 316cd87 into main Jul 26, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge-risk: 🚨 compatibility 🚨 Merging this PR could break existing users, config, migrations, defaults, or upgrades. P1 Urgent regression or broken agent/channel workflow affecting real users now. rating: 🐚 platinum hermit Good normal PR readiness with ordinary maintainer review expected. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants