fix(hook): close branch-guard bypasses for bare filenames, new dirs, missing path fields - #291
Conversation
…and missing path fields CodeRabbit flagged this as critical on PR #283 but it was squash-merged without a fix. target_repo resolution called repo_toplevel() directly on p.parent(), which fails (and silently skips the branch guard) for: - a bare filename (parent is \, ENOENT) - a new file under a not-yet-created directory (parent doesn't exist) - a MUTATING_TOOLS call with no top-level file_path/path (e.g. MultiEdit) Now walks ancestors to the first directory that actually exists on disk before resolving its repo toplevel, and falls back to cwd when the path can't be extracted at all -- matching the original bug's intended fix. Agentflare-Agent: claude-code_2-1-216_agent Agentflare-Branch: fix/hook-redirect-branch-guard-bypass
|
Caution Review failedAn error occurred during the review process. Please try again later. 📝 WalkthroughWalkthrough
ChangesMutating-tool repository resolution
Estimated code review effort: 4 (Complex) | ~45 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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 |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/hook_redirect.rs (1)
163-175: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winRedundant
repo_toplevelsubprocess calls beyond the first existing ancestor.
git rev-parse --show-toplevelalready walks up from its start directory to find.git, so once it's called on the first existing ancestor and returnsNone, every further (higher) existing ancestor is guaranteed to also returnNone— they're strict super-ancestors already covered by that first call's internal walk. As written,find_mapwill keep re-invokingrepo_toplevel(a new git subprocess spawn) for every subsequent existing ancestor instead of stopping after the first. For a target path with several existing directory levels outside any repo, this spawns one subprocess per level instead of one, adding latency that competes withGATING_TIMEOUT— ironically risking a fail-open skip of the very guard this PR hardens.♻️ Only call repo_toplevel once, on the first existing ancestor
- let target_repo = target_path.and_then(|p| { - p.ancestors().skip(1).find_map(|ancestor| { - let check = if ancestor == Path::new("") { - Path::new(".") - } else { - ancestor - }; - check - .exists() - .then(|| flare_git_core::branch::repo_toplevel(check)) - .flatten() - }) - }); + let target_repo = target_path.and_then(|p| { + let first_existing = p.ancestors().skip(1).find(|ancestor| { + let check = if *ancestor == Path::new("") { + Path::new(".") + } else { + *ancestor + }; + check.exists() + })?; + let check = if first_existing == Path::new("") { + Path::new(".") + } else { + first_existing + }; + flare_git_core::branch::repo_toplevel(check) + });🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/hook_redirect.rs` around lines 163 - 175, Update the target_repo resolution around target_path so it selects only the first existing ancestor before calling flare_git_core::branch::repo_toplevel. Replace the repeated find_map subprocess calls with logic that stops at that ancestor and invokes repo_toplevel exactly once, while preserving the Path::new("") to "." normalization and the existing Option result behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@src/hook_redirect.rs`:
- Around line 163-175: Update the target_repo resolution around target_path so
it selects only the first existing ancestor before calling
flare_git_core::branch::repo_toplevel. Replace the repeated find_map subprocess
calls with logic that stops at that ancestor and invokes repo_toplevel exactly
once, while preserving the Path::new("") to "." normalization and the existing
Option result behavior.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 2eb81b52-84c6-45e0-bbde-aeecc158e4cf
📒 Files selected for processing (1)
src/hook_redirect.rs
Two issues found reviewing this PR's own CI run: - The ancestor-walk tests used std::env::set_current_dir, which is global process state -- it collided with an unrelated parallel test (hook::tests::session_start_message_shows_pending_items_from_backend_db, which resolves its project from cwd) and broke CI's ubuntu build. Rewritten to use absolute paths and cwd-independent ground-truth comparisons instead, so no test here touches the real cwd. - CodeRabbit nitpick on the ancestor walk: repo_toplevel() (a git subprocess) was being re-invoked for every existing ancestor instead of stopping at the first one, since git rev-parse --show-toplevel already walks upward internally. Now finds the first existing ancestor and calls repo_toplevel exactly once. Agentflare-Agent: claude-code_2-1-216_agent Agentflare-Branch: fix/hook-redirect-branch-guard-bypass
…b.com/getappz/agentflare into fix/hook-redirect-branch-guard-bypass Agentflare-Agent: claude-code_2-1-216_agent Agentflare-Branch: fix/hook-redirect-branch-guard-bypass
…ranch-guard-bypass # Conflicts: # src/hook_redirect.rs Agentflare-Agent: claude-code_2-1-216_agent Agentflare-Branch: fix/hook-redirect-branch-guard-bypass
Summary
redirect_decision's target-repo resolution calledrepo_toplevel()directly onfile_path.parent(), which fails (ENOENT) and silently skips the branch guard for a bare filename (parent""), a new file under a not-yet-created directory, or any mutating tool call with no top-levelfile_path/path(e.g.MultiEdit) — no cwd fallback existed for that last case despite fix(hook): resolve PreToolUse branch guard against target file's repo, not host cwd #283's stated intent.Path::ancestors()to the first directory that actually exists before resolving its repo toplevel, and fall back to cwd when the path can't be extracted at all. Genuinely out-of-repo targets still skip the guard as before.Test plan
cargo build— cleancargo fmt --check— cleancargo test hook_redirect— 21/21 pass, including 4 new regression tests (bare filename, new nested dir, missing path field, still-skips-outside-repo)cargo clippy --workspace --all-features -- -D warnings— pre-existing unrelated dead-code failure indaemon_autostart.rsconfirmed present onmasterbefore this change (same class as tracked items handoff: assign items + attach versioned assets instead of raw artifacts #169/chore: add CODEOWNERS #239)Summary by CodeRabbit