@odingard/guardian-hook 0.3.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -4
- package/dist/agent-sdk.js +1055 -10
- package/dist/index.js +1055 -10
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -22,19 +22,30 @@ There is no model in the decision: Guardian decides ALLOW / ASK / DENY determini
|
|
|
22
22
|
|
|
23
23
|
Shell commands are routed by consequence. That routing is what lets you put deploys behind your phone while ordinary commands flow freely.
|
|
24
24
|
|
|
25
|
-
**
|
|
25
|
+
**How a command is routed.** The hook parses the command line and routes it by the riskiest command inside it. It removes quotes and escapes, splits on `;`, `&&`, `||`, `|`, newlines and subshells, and looks inside `$( )`, backticks, `<( )`, `sh -c` / `bash -c` strings, `eval`, `su -c`, `env -S` and `find -exec`. It looks past wrappers (`sudo`, `doas`, `env`, `xargs`, `nice`, `nohup`, `time`, `timeout`, `stdbuf`, `exec`, `command`, `watch`, `npx`), git global options (`-C`, `-c`, `--git-dir`, `--work-tree`) and git aliases set with `-c alias.*`. So `find . | xargs rm -rf`, `git -C repo push -f`, `"git" push --force` and `r\m -rf ~` are all `shell_destructive`. When the hook cannot follow a command, it sends it to the Ask tier: an unterminated quote, a command name known only at run time (`$CMD -rf x`), `eval` or `sh -c` of run-time text (`sh -c "$(echo … | base64 -d)"`, `eval "$(tool init)"`), or a `case` statement.
|
|
26
|
+
|
|
27
|
+
**It is still not a security boundary.** The hook cannot see inside scripts (`bash deploy.sh`, `make clean`), code run by other interpreters (`python -c`, `node -e`, PowerShell), shell functions and aliases, or git aliases and hooks already in the git config. Under prompt injection, treat the Ask tier as advisory. Where that matters, set the everyday connection to **Ask me first** as well, or deny `shell` outright.
|
|
26
28
|
|
|
27
29
|
| Guardian tool | Suggested mode | Covers |
|
|
28
30
|
| --------------------- | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
29
31
|
| `shell` | Allow | Ordinary commands (build, test, inspect) |
|
|
30
32
|
| `shell_deploy` | Ask me first | terraform/tofu apply and destroy, pulumi up, kubectl apply and delete, helm install and upgrade, cloud deploy CLIs, package publish, `gh pr merge` |
|
|
31
|
-
| `shell_destructive` | Ask me first |
|
|
33
|
+
| `shell_destructive` | Ask me first | See the list below |
|
|
32
34
|
| `git_push` | Ask me first | Pushing commits |
|
|
33
35
|
| `shell_session` | Ask me first | Starting an interactive shell, interpreter, database client or remote session (`bash`, `python3`, `ssh host`, `docker exec -it`, `psql app`) |
|
|
34
36
|
| `file_write` | Allow | Write and edit (the approver sees the path, size and a SHA-256 of the content; the content itself is not sent) |
|
|
35
37
|
| `web_fetch` | Allow | Fetching a URL |
|
|
36
38
|
| `mcp.<server>.<tool>` | your choice | Any MCP tool, e.g. `mcp.github.create_pull_request` |
|
|
37
39
|
|
|
40
|
+
`shell_destructive` covers:
|
|
41
|
+
|
|
42
|
+
- `rm -r` / `rm -rf` (also with no operands, and through `xargs`), and any `rm` run by `find -exec`, `find -ok` or `xargs`; `find -delete`; `shred`, `mkfs`, `dd if=`, `wipefs`.
|
|
43
|
+
- `git reset --hard` or `--merge`, `git clean -f`, `git branch -D` (or `-d -f`), `git checkout -f` or `checkout -- <path>`, `git switch --discard-changes`, `git restore --source`, `git stash drop` / `clear`, `git update-ref -d`, `git reflog expire`, `git filter-branch`.
|
|
44
|
+
- Force pushes and remote ref deletes: `git push --force`, `-f`, `--force-with-lease`, `--force-if-includes`, `+refspec`, `--mirror`, `--prune`, `--delete`, `-d`, `origin :branch`, with or without git global options (`git -C dir push -f`).
|
|
45
|
+
- `DROP` / `TRUNCATE` / unqualified `DELETE FROM`, `chmod -R` to a world-writable mode, and downloaded code run by a shell or interpreter (`curl … | sh`, `bash <(curl …)`, `sh -c "$(curl …)"`).
|
|
46
|
+
- Changes to the agent's own MCP servers, hooks or permissions: `claude mcp add`, `add-json`, `add-from-claude-desktop`, `remove` and the other non-read `mcp`, `plugin` and `config` subcommands of `claude`, `codex` and `gemini`; a nested `claude` or `codex` given `--mcp-config`, `--settings`, `--dangerously-skip-permissions` or a wider `--permission-mode`; and writes to `.mcp.json`, `mcp.json`, `~/.claude.json`, `settings.local.json`, `.claude/settings*.json`, `.claude/hooks`, `agents`, `commands`, `skills` or `plugins`, `.codex/`, `.cursor/`, `.gemini/`, `.dsh/` or `guardian-hooks.json`. That includes writes by redirection (`> .mcp.json`), by commands such as `cp`, `mv`, `tee`, `sed -i` and `ln`, and by the file tools (`Write`, `Edit`, `MultiEdit`, `write`, `edit`, `str_replace_editor`, `apply_patch`), which then carry `touches: "agent-config"`. Reading those files stays `shell`.
|
|
47
|
+
- Commands the hook cannot parse (see above).
|
|
48
|
+
|
|
38
49
|
Read-only tools (Read, Grep, Glob, LS, WebSearch) are not routed through Guardian.
|
|
39
50
|
|
|
40
51
|
## Setup
|
|
@@ -244,8 +255,8 @@ A deny is the Claude Code deny JSON, plus the reason on stderr and exit 2, both
|
|
|
244
255
|
## Limits (read these)
|
|
245
256
|
|
|
246
257
|
- **Decision-gated.** The agent performs the effect after Guardian authorizes and redeems the call. The receipt proves Guardian's authorization of that exact call, not the command's own outcome.
|
|
247
|
-
- **Routing is local and evadable.** The
|
|
248
|
-
- **The agent can edit the hook's own config
|
|
258
|
+
- **Routing is local and evadable.** The parser and patterns run on your machine. They do not see inside scripts, other interpreters' code, shell functions or configured git aliases (see [Tools the hook proposes](#tools-the-hook-proposes)).
|
|
259
|
+
- **The agent can still edit the hook's own config in ways the hook does not recognise.** Writes to the known config files (`.codex/hooks.json`, `guardian-hooks.json`, `cordis.patch.yml`, `.claude/settings.json` and the others listed above) go to the Ask tier, but a script, an interpreter (`python script.py`) or a path built at run time can still rewrite them and remove the hook. On one-shot dsh runs the harness restarts on every message, so the edit takes effect on the next one. Deliver the hook as a managed hook (Codex `requirements.toml` with `allow_managed_hooks_only`, Claude Code managed settings), or mount the config read-only (owned by another user) where that matters.
|
|
249
260
|
- **Starting the hook.** It answers only when run as `guardian-hook` (or `…/hook/dist/index.js`). Installed under another file name it exits 0 with no output, which every agent treats as "no objection". Use the documented commands.
|
|
250
261
|
- **Only calls through the hook are governed.** Commands you run yourself, or other credentials on the machine, are not.
|
|
251
262
|
- **Arguments are bounded.** Commands over 2,000 characters are truncated in what the approver sees; MCP inputs over that size are sent as a digest.
|