Repository navigation
fix(git-safe): close checkout/switch force+detach smuggling and pull last-wins gap - #223
Conversation
…last-wins gap Combined short-option clusters admitted force and detach spellings that the standalone-flag checks were written to reject: - git checkout -fb / -tf discarded uncommitted work (same as blocked -f) - git switch -fd / -td discarded work and detached HEAD, bypassing the --detach rejection and the assigned-branch invariant - git switch --discard-changes is the long spelling of switch -f - pull --rebase --rebase=false was admitted as a rebase pull though git applies the last option and performs a merge Scan clusters up to the first value-taking letter (checkout -b/-B, switch -c/-C) so switch -cd keeps parsing d as -c's attached value. Evaluate pull's rebase flags last-wins like git does. Co-Authored-By: Raul Cardenas Montoya <montoyaraul34@gmail.com>
|
I'll fix CI failures and address comments from users with write access that start with 'Devin'.
Original prompt from Raul Cardenas Montoya
|
|
Skipping PR review because a bot author is detected. If you want to trigger CodeAnt AI, comment |
|
Important Review skippedBot user detected. To trigger a single review, invoke the ⚙️ Run configuration
You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Up to standards ✅🟢 Issues
|
| Metric | Results |
|---|---|
| Duplication | 10 |
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
CodeScene delta review flagged the added checks: SafeGitCommand::new grew 24->28 cyclomatic complexity and smuggled_cluster_flag carried a compound guard conditional. Move the detach + cluster-force + cluster-detach rejection into reject_checkout_switch_escape and split the new conditionals into single-branch guards. No behavior change; the six regression tests still pass. Co-Authored-By: Raul Cardenas Montoya <montoyaraul34@gmail.com>
|
Complex Method increase addressed: the three checks (detach, cluster Devin: writ agent: fixed in 5f91f15 |
|
Complex Conditional fixed: the compound Devin: writ agent: fixed in 5f91f15 |
Codecov flagged the -b/-B value-taking arm and the non-alphabetic early exit in smuggled_cluster_flag as the only uncovered lines. Co-Authored-By: Raul Cardenas Montoya <montoyaraul34@gmail.com>
Code Review SummaryStatus: No Issues Found | Recommendation: Merge Files Reviewed (2 files)
Previous Review Summaries (4 snapshots, latest commit 27e4d43)Current summary above is authoritative. Previous snapshots are kept for context only. Previous review (commit 27e4d43)Status: No Issues Found | Recommendation: Merge Files Reviewed (2 files)
Previous review (commit d451c0f)Status: 3 Suggestions | Recommendation: Merge (suggestions are optional improvements) Overview
Issue Details (click to expand)SUGGESTION
Files Reviewed (2 files)
Note: Two previous review findings (smuggled cluster Previous review (commit f74effc)Status: No Issues Found | Recommendation: Merge Files Reviewed (2 files)
Previous review (commit 17937bc)Status: 2 Issues Found | Recommendation: Low-risk suggestions, safe to merge Overview
Issue Details (click to expand)SUGGESTION
Files Reviewed (2 files)
The PR closes all four smuggling vectors described in the PR:
The refactoring in commit 2 cleanly extracts all checkout/switch escape checks into All entry points ( Verified against git 2.55.0: Reviewed by free · Input: 684.3K · Output: 49.2K · Cached: 1.2M Review guidance: REVIEW.md from base branch |
Kilo review nits: args after the end-of-options marker are pathspecs, so checkout -- -fd must not be read as a force cluster; and the boolean --discard-changes=<bool> takes explicit values where =false/0/no/off disable it rather than carry -f semantics. Co-Authored-By: Raul Cardenas Montoya <montoyaraul34@gmail.com>
|
Fixed: all three scans in Devin: writ agent: fixed in f74effc |
|
Fixed: Devin: writ agent: fixed in f74effc |
There was a problem hiding this comment.
✅ Devin resolved all 2 findings on 27e4d43
Fixed by Devin (2)
- Attached branch names bypass branch pinning
- Equals-prefixed branch escapes branch pin
…ters checkout_or_switch_target skipped `-cd`-style clusters as pure flags, so `switch -cd <point>` read the start point as the target branch while git actually creates and lands on the attached name. The supervisor's --expected-branch pin compared the wrong name and admitted the escape. cluster_create_value now resolves the value at the first value-taking letter (-b/-B on checkout, -c/-C on switch): attached remainder, optional '=' prefix, or the next argv token when the letter is last. The same letter table is shared with smuggled_cluster_flag. Found by Devin Review on PR #223. Co-Authored-By: Raul Cardenas Montoya <montoyaraul34@gmail.com>
Per Kilo review on #223: parametrize the create-cluster test over -b/-B (checkout) and -c/-C (switch) in both attached and =-prefixed spellings, and clarify the cluster_create_value doc comment. Co-Authored-By: Raul Cardenas Montoya <montoyaraul34@gmail.com>
|
Resolved — coverage added in 27e4d43. |
|
Resolved — doc clarified in 27e4d43. |
|
Resolved — force-create variants covered in 27e4d43. |
git does NOT strip '=' from short-option values: 'switch -c=name' creates branch '=name' (verified on git 2.x). Stripping it made the branch pin compare 'name' while HEAD landed on '=name' — a pin escape for any expected branch the attacker spells '-c=<expected>'. Found by Devin Review on PR #223. Co-Authored-By: Raul Cardenas Montoya <montoyaraul34@gmail.com>
There was a problem hiding this comment.
Code Health Improved
(1 files improve in Code Health)
Gates Passed
6 Quality Gates Passed
See analysis details in CodeScene
View Improvements
| File | Code Health Impact | Categories Improved |
|---|---|---|
| mod.rs | 4.50 → 4.50 | Low Cohesion, Complex Method, Large Method, Overall Code Complexity, String Heavy Function Arguments |
Quality Gate Profile: Pay Down Tech Debt
Install CodeScene MCP: safeguard and uplift AI-generated code. Catch issues early with our IDE extension and CLI tool.
|
Resolved — fixed in 2bc724f (verbatim attached values). |
|
Resolved — fixed in d451c0f (cluster-aware target parsing). |
|
Devin is currently unreachable - the session may have died. |
Summary
SafeGitCommandspelled its force/detach checks exactly, so combined short-option clusters smuggled the same flags straight through — reopening the WIP-destruction paths the checks exist to block:git checkout -fb/checkout -tf— discards uncommitted work, identical to the rejected bare-f/--force.git switch -fd/switch -td— discards worktree changes and detaches HEAD, bypassing both the force rejection and the--detachrejection in one token.switchadmits-das detach (is_detach_flag), so a cluster starting with-dwas caught, but-fd/-tdwere not.git switch --discard-changes— the long spelling ofswitch -f, unhandled.git pull --rebase --rebase=false—pull_uses_safe_history_strategyreturned on the first--rebaseit saw, but git applies the last rebase option, so this spelled a merge pull that slipped past the rebase/ff-only requirement.The push-scoped cluster scan (
is_combined_short_force_cluster) only ran forsubcommand == "push", so none of this was visible on the local-side commands where it matters most — including throughadmit_git_invocationand thewrit supervisorpath (reject_mismatched_checkoutseescheckout -fb <expected>as a matching target).Fix
smuggled_cluster_flag(subcommand, arg, flag)scans cluster letters only until the first value-taking option (checkout -b/-B,switch -c/-C) — soswitch -cd <name>still parsesdas-c's attached value, not--detach.is_bare_force_flagnow also rejects--discard-changes[=...].pull_uses_safe_history_strategyevaluates rebase options last-wins like git;--ff-onlystill qualifies anywhere.Six new tests cover the cluster spellings,
--discard-changes, the-cdvalue-parsing boundary, and both last-wins pull orderings.Validation
cargo test -p writ-core git_safe: 136 passed (includes 6 new)cargo test --workspace: 622 passedcargo fmt --check: cleancargo clippy --workspace --all-targets -- -D warningsfails on a pre-existingneedless_returnatpaths.rs:25on a clean tree (unrelated to this diff; my files are clippy-clean).Link to Devin session: https://app.devin.ai/sessions/ca3edfc32e1f44ada3bbab1345c3f358
Open in Devin Desktop: https://app.devin.ai/desktop/session/ca3edfc32e1f44ada3bbab1345c3f358?variant=devin
Requested by: @rmems
Summary by cubic
Fixes
SafeGitCommandsocheckout/switchcan no longer smuggle force/detach flags via combined short clusters (-fb,-tf,-fd,-td), andgit pullnow respects git's last-wins rebase option ordering. Option scanning stops at--, so a pathspec likecheckout -- -fdis never read as a flag cluster, and falsy--discard-changes=values disable discarding instead of carrying-fsemantics.switch -cd→ branchd,checkout -bfoo→foo) so the supervisor's expected-branch pin compares the branch HEAD actually lands on, covering-b/-Band-c/-Cin attached and=-prefixed forms. Attached values are kept verbatim: git creates=namefor-c=name, so the pin compares the literal name.--discard-changesas the long spelling ofswitch -f, except=false/0/no/off.--rebase/--no-rebase/--rebase=in order so--rebase --rebase=falseis correctly rejected as a merge pull.reject_checkout_switch_escape; no behavior change.--discard-changesbooleans, the--pathspec boundary, and both pull orderings.Written for commit 2bc724f. Summary will update on new commits.