Skip to content

fix(hosthooks): source delete operands from the raw command, not the redacted target - #692

Merged
fu351 merged 2 commits into
mainfrom
fix/hosthooks/delete-operands-from-raw-command
Sep 10, 2026
Merged

fix(hosthooks): source delete operands from the raw command, not the redacted target#692
fu351 merged 2 commits into
mainfrom
fix/hosthooks/delete-operands-from-raw-command

Conversation

@fu351

@fu351 fu351 commented Sep 10, 2026

Copy link
Copy Markdown
Collaborator

What

The host-hook delete guard (blast-radius preview + post-approval TOCTOU recheck, shipped in v0.18.7 for #649/#665) silently does not run when the delete's path is long enough to be redacted.

_delete_operands read action.target, which is the post-redaction string. normalize._redact_value replaces any value carrying a 40+ character unbroken [A-Za-z0-9+/_-] run with <redacted> — and / is in that class, so any ordinary nested absolute path qualifies. The operand source then became <redacted>, _delete_operands returned (None, False), and resolve_auth_result skipped both the preview and the recheck. Reproduced on v0.18.7:

rm -rf ./build                                                              -> target "rm -rf ./build" -> (['./build'], False)
rm -rf /home/user/projects/acme-platform/services/billing/node_modules      -> target "<redacted>"      -> (None, False)  # guard skipped

The proxy path never had this bug (executor.py builds operands from the raw tool arguments via command_line_from_arguments). The host-hook port regressed the source.

Fix

  • Thread the raw tool arguments through resolve_auth / resolve_auth_result / _delete_operands; build the command with the same command_line_from_arguments the proxy uses, so the operand source is never the redacted string.
  • Pass the raw tool_input at all three live call sites: claude_code.py, codex.py, cursor.py. openclaw.py does not call resolve_auth_result (it delegates to OpenClaw's own approve flow), so it is untouched — tracked separately.
  • Fail closed: a delete whose command line cannot be recovered (redacted target, no raw arguments) now returns unknown effects and still runs the recheck, instead of skipping the guard. "A guard that cannot verify must not report success."

raise-only: no decision that was denied before can now be allowed.

Verification

  • ruff check, ruff format --check, lint-imports: green. (command_line_from_arguments comes from the same engine.rules.commands module already imported here, so no new import contract.)
  • tests/unit/test_hosthook_delete_recheck.py + tests/unit/test_hosthook_claude_cursor_compat.py: 36 passed. New tests cover a 60-char redacted path with raw args (real operands recovered), the end-to-end challenge (preview computed, recheck runs), and the fail-closed no-args case (unknown effects, guard still runs).
  • Broad tests/unit sweep: one failure, test_benchmark_agentdojo_live — a local pyarrow/NumPy-2 ABI import error (_ARRAY_API not found), reproduces on the base commit and is unrelated to this change. GUI-prompter tests excluded locally (the separate nightly-red font-metric hang).

@fu351
fu351 merged commit 10ef2e6 into main Sep 10, 2026
12 checks passed
@fu351
fu351 deleted the fix/hosthooks/delete-operands-from-raw-command branch September 10, 2026 05:41
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