Wire cfg.Security.ProtectedBinaries into ward hook pre-tool-use #43

Closed
opened 2026-06-03 23:08:03 +00:00 by coilysiren · 0 comments
Owner

Scope

Final remaining sub-issue of #4. Wire the security: block's protected binaries into the PreToolUse hook so a semi-trusted agent (Claude, Codex) cannot run a protected tool directly — bare token (gcloud), absolute path (/opt/homebrew/bin/gcloud), or any other /-containing spelling whose basename matches.

cli-guard already provides hook.Protected with basename-aware matching (PR #56). ward's runPreToolUse calls hook.PreToolUse(...) today with no protected args. This sub-issue closes that gap.

What

  • Load cfg.Security inside the hook from the Claude-supplied cwd, walking up to find .ward/ward.yaml or .coily/coily.yaml. Best-effort: any error (no config reachable, parse failure) passes through silently — same posture the rest of the hook takes for malformed payloads.
  • Map each ProtectedBinary to a hook.Protected{Name, Wrappers, Hint}. Hint = Hooks.RouteHints[name] when set, else synthesized from AllowedWrappers by the engine.
  • Also map Hooks.DenyBareBinaries entries that are NOT in ProtectedBinaries as hint-only Protected entries (no wrappers, hint from RouteHints).
  • Pass the merged list to hook.PreToolUse(...) as the variadic protected tail.
  • Document the wiring in docs/hook.md: where the config is read, what the basename match covers, and the best-effort failure posture.

Acceptance

  • With a config declaring protected_binaries: [{name: gcloud, allowed_wrappers: [kap]}], the hook blocks gcloud auth login, /opt/homebrew/bin/gcloud auth login, and ./gcloud auth login from cwd inside that repo.
  • Outside any ward/coily-managed cwd (walk-up finds nothing), the hook behaves exactly as it does today — no regressions to ward exec / route hints / integrity rules.
  • A malformed security: block does not crash the hook; it passes through silently and writes nothing to stderr.
  • Tests cover: bare-token deny, absolute-path deny, basename match against a /-containing path that is not absolute, no-config-reachable pass-through, malformed-config pass-through, hint synthesis when RouteHints[name] is set, hint synthesis when only AllowedWrappers is set.
  • docs/hook.md documents the protected-binary deny path and references cli-guard's basename-aware match contract.

Out of scope

  • New schema fields. cli-guard owns the schema.
  • Doctor-side --strict-credentials / --skip changes. Those landed with #42.
  • Removing the ward lint alias. Tracked separately as #41.
## Scope Final remaining sub-issue of #4. Wire the `security:` block's protected binaries into the PreToolUse hook so a semi-trusted agent (Claude, Codex) cannot run a protected tool directly — bare token (`gcloud`), absolute path (`/opt/homebrew/bin/gcloud`), or any other `/`-containing spelling whose basename matches. cli-guard already provides `hook.Protected` with basename-aware matching (PR #56). ward's `runPreToolUse` calls `hook.PreToolUse(...)` today with no protected args. This sub-issue closes that gap. ## What - Load `cfg.Security` inside the hook from the Claude-supplied `cwd`, walking up to find `.ward/ward.yaml` or `.coily/coily.yaml`. Best-effort: any error (no config reachable, parse failure) passes through silently — same posture the rest of the hook takes for malformed payloads. - Map each `ProtectedBinary` to a `hook.Protected{Name, Wrappers, Hint}`. Hint = `Hooks.RouteHints[name]` when set, else synthesized from `AllowedWrappers` by the engine. - Also map `Hooks.DenyBareBinaries` entries that are NOT in `ProtectedBinaries` as hint-only Protected entries (no wrappers, hint from `RouteHints`). - Pass the merged list to `hook.PreToolUse(...)` as the variadic `protected` tail. - Document the wiring in `docs/hook.md`: where the config is read, what the basename match covers, and the best-effort failure posture. ## Acceptance - With a config declaring `protected_binaries: [{name: gcloud, allowed_wrappers: [kap]}]`, the hook blocks `gcloud auth login`, `/opt/homebrew/bin/gcloud auth login`, and `./gcloud auth login` from cwd inside that repo. - Outside any ward/coily-managed cwd (walk-up finds nothing), the hook behaves exactly as it does today — no regressions to `ward exec` / route hints / integrity rules. - A malformed `security:` block does not crash the hook; it passes through silently and writes nothing to stderr. - Tests cover: bare-token deny, absolute-path deny, basename match against a `/`-containing path that is not absolute, no-config-reachable pass-through, malformed-config pass-through, hint synthesis when `RouteHints[name]` is set, hint synthesis when only `AllowedWrappers` is set. - `docs/hook.md` documents the protected-binary deny path and references cli-guard's basename-aware match contract. ## Out of scope - New schema fields. cli-guard owns the schema. - Doctor-side `--strict-credentials` / `--skip` changes. Those landed with #42. - Removing the `ward lint` alias. Tracked separately as #41.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
coilyco-flight-deck/ward#43
No description provided.