Skip to content

fix: close git-worktree bypass via global options and quote sourced colcon args - #25

Merged
HansRobo merged 1 commit into
mainfrom
fix/git-wrapper-global-opts-and-sourced-quoting
Jun 13, 2026
Merged

HansRobo merged 1 commit into
mainfrom
fix/git-wrapper-global-opts-and-sourced-quoting

Conversation

@HansRobo

@HansRobo HansRobo commented Jun 13, 2026 •

Copy link
Copy Markdown
Owner

Hardens the two trust boundaries where CWM consumes agent-supplied input. From the §1 triage of the review report (follows PR #24, which closed report items 1/2/5); this is the security pair, report items 6 and 7, deliberately bundled as the report recommended.

Item 6 — git worktree interception bypass

Both the shell-function wrapper (shell_init_cmd.py) and the PATH-shim wrapper (worktree_state.py) detected the worktree subcommand with a literal $1 comparison. An invocation that prefixes git global options — e.g. git -C <path> worktree add, which agents use heavily — therefore slipped past both interceptors and could mutate the overlay structure directly.

  • Add a __cwm_git_has_worktree scanner to both wrappers that skips leading global options, consuming value-taking options (-C, -c, --git-dir, --work-tree, ...) in pairs so their value is not mistaken for the subcommand. (A naive "skip all -*" would still bypass, because the path after -C would be read as the subcommand.)
  • When worktree is found behind global options, the original argv is forwarded unshifted to the hook.
  • Policy lives in one place: git_hook strips the globals via _strip_git_globals and refuses invocations carrying repository-retargeting options (-C/--git-dir/--work-tree) — those target a different repository and cannot be safely re-homed onto the CWM-managed overlay. Benign globals (-c, --no-pager, ...) are stripped and the command proceeds normally.

The bare git worktree ... fast path is unchanged.

Item 7 — Unquoted arguments on the sourced colcon path

run_colcon_sourced joined colcon arguments with a bare space before handing them to bash -c, so a value containing spaces or shell metacharacters (such as a --cmake-args value) was word-split or interpreted by the subshell. Each argument is now quoted with shlex.quote, matching the treatment the source paths already received.

Tests

  • Real-bash wrapper cases: -C <path> worktree add reaches the hook (no bypass); -C <path> status still goes to real git (value not mistaken for a subcommand).
  • Python cases for _strip_git_globals and __git_hook retarget refusal vs. benign-global pass-through.
  • A quoting case asserting a spaced --cmake-args value becomes a single token.
  • Full suite green (258 passed) via uv run python -m pytest.

…olcon args

Harden the two trust boundaries where CWM consumes agent-supplied input.

#6 git worktree interception bypass:
Both the shell-function wrapper and the PATH-shim wrapper detected the
'worktree' subcommand with a literal `$1` comparison, so an invocation that
prefixes git global options - e.g. `git -C <path> worktree add` (which agents
use heavily) - slipped past both interceptors and could mutate the overlay
structure directly. Add a `__cwm_git_has_worktree` scanner to both wrappers
that skips leading global options, correctly consuming value-taking options
(`-C`, `-c`, `--git-dir`, `--work-tree`, ...) in pairs so their value is not
mistaken for the subcommand. When 'worktree' is found behind global options the
original argv is forwarded unshifted to the hook. The policy lives in one place:
`git_hook` strips the globals via `_strip_git_globals` and refuses invocations
carrying repository-retargeting options (`-C`/`--git-dir`/`--work-tree`), since
those target a different repository and cannot be safely re-homed onto the
CWM-managed overlay.

#7 unquoted arguments on the sourced colcon path:
`run_colcon_sourced` joined colcon arguments with a bare space before handing
them to `bash -c`, so a value containing spaces or shell metacharacters (such as
a `--cmake-args` value) was word-split or interpreted by the subshell. Quote
each argument with `shlex.quote`, matching the treatment the source paths
already received.

Tests: real-bash wrapper cases proving `-C <path> worktree` reaches the hook and
`-C <path> status` still goes to real git; Python cases for the global-option
parsing and retarget refusal; a quoting case for a spaced `--cmake-args` value.
@HansRobo HansRobo added the release:patch Bump patch version on merge label Jun 13, 2026
@HansRobo
HansRobo merged commit 9fb6a06 into main Jun 13, 2026
12 checks passed
@HansRobo
HansRobo deleted the fix/git-wrapper-global-opts-and-sourced-quoting branch June 13, 2026 03:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

release:patch Bump patch version on merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant