Skip to content

test(p2): command/flag-injection containment for name/url/.gitmodules (#62 P2)#74

Merged
bashandbone merged 2 commits into
mainfrom
test/p2-security-injection-vectors
Jun 30, 2026
Merged

test(p2): command/flag-injection containment for name/url/.gitmodules (#62 P2)#74
bashandbone merged 2 commits into
mainfrom
test/p2-security-injection-vectors

Conversation

@bashandbone

Copy link
Copy Markdown
Owner

What

tests/security_tests.rs had only 2 tests, both the same vector (a path component starting with -). The #62 audit (P2) flags missing command/flag injection via URL or name (CVE-2018-17456 class) and malicious .gitmodules fed to generate-config. This adds two characterization tests that prove submod's no-shell design holds end-to-end.

Tests

  • test_flag_like_name_and_url_do_not_inject_commands — a --upload-pack=touch <sentinel> payload passed as a submodule name (via --name= so clap accepts the leading dashes), and an ext::sh -c "touch <sentinel>" payload passed as the URL. Both sentinels (relative-in-worktree and absolute) are asserted absent. submod drives git via gix/git2 (no shell) and ---guards url/path on the CLI fallback, so nothing executes. Non-vacuous: an unsafe shell-out would create the sentinel.
  • test_generate_config_from_malicious_gitmodules_does_not_execute — a hostile .gitmodules (ext:: url, --upload-pack= branch) fed to generate-config --from-setup. No sentinel is created, and the generated config provably contains the hostile values as inert data strings — proving the parse path actually ran (non-vacuous).

Both vectors were probed against the real binary first and are already safe; these tests lock that behavior in.

Verification

Full suite 557 pass, 0 fail; cargo fmt + clippy --all-features --tests clean.

Scope / follow-ups

Remaining P2 (separate PRs): symlink-escape containment; remaining assert!(!stderr.is_empty()) sites + wide || error-message disjunctions + exit-code assertions; root-no-op permission/lock tests; idempotency/partial-failure (add-twice, delete-nonexistent, failed-add-leaves-no-partial-state).

🤖 Generated with Claude Code

…modules (#62 P2)

security_tests.rs had only 2 tests (both the same `-`-prefixed-path vector). The
#62 audit (P2) calls out missing command/flag-injection and malicious-.gitmodules
coverage. Adds two characterization tests proving submod's no-shell design holds
end-to-end:

- test_flag_like_name_and_url_do_not_inject_commands: a CVE-2018-17456-class
  `--upload-pack=<cmd>` payload as a submodule NAME, and an `ext::sh -c` payload
  as the URL, must never execute. Sentinel files (relative in the worktree,
  absolute) are asserted absent. Non-vacuous: an unsafe shell-out would create them.
- test_generate_config_from_malicious_gitmodules_does_not_execute: a hostile
  .gitmodules fed to `generate-config --from-setup` is parsed as data only — no
  sentinel is created, and the generated config provably contains the hostile
  values as inert strings (so the parse path actually ran).

Behavior probed against the real binary first (both vectors are already safe;
these lock that in). Full suite 557 pass; fmt + clippy clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T8D5ZK1473YCiZkbueAY2X
@bashandbone bashandbone merged commit 50bd136 into main Jun 30, 2026
5 of 8 checks passed
@bashandbone bashandbone deleted the test/p2-security-injection-vectors branch June 30, 2026 02:59
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