mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-17 02:15:31 +02:00
Freeze boundary hook had four verified bugs: the grep-first JSON extractor truncated at escaped quotes and failed OPEN on unparseable payloads; the deny JSON was printf-interpolated so a quote- or newline-bearing path silently no-oped the block; the freeze path read stripped INTERNAL spaces (a boundary like ~/My Project could never match); and the path resolver skipped the final component, letting an in-boundary symlink write through to an out-of-boundary target. Fixes, structurally: one shared sourced helper (careful/bin/hook-extract.sh) now owns JSON extraction and JSON-encoded decision envelopes for BOTH hooks -- the two-copy drift is how freeze kept a broken extractor after careful's was fixed. Freeze is now deny-tier fail-closed (unparseable payload denies, parsed-but-no-file_path still allows), trims only leading/trailing whitespace, and resolves symlinks through the final path component. Careful gains a HIGH tier (hard deny, simple commands only): recursive delete of /, ~, or $HOME, and force-push to the repo's default branch. Compound commands always fall through to the MEDIUM ask; --force-with-lease is never HIGH. Documented as a best-effort advisory hard-stop, not a policy boundary. Plus additive-only project patterns (~/.gstack/careful-patterns.txt + per-project file): config can only ADD warn rules, never suppress a baseline family. test/hook-scripts.test.ts: 89 tests incl. malformed-payload deny, parseable deny JSON for hostile paths, space-bearing boundaries, symlink escape, HIGH tier splits, additive invariant, invalid-regex resilience. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
93 lines
3.7 KiB
Cheetah
93 lines
3.7 KiB
Cheetah
---
|
|
name: careful
|
|
version: 0.1.0
|
|
description: |
|
|
Safety guardrails for destructive commands. Warns before rm -rf, DROP TABLE,
|
|
force-push, git reset --hard, kubectl delete, and similar destructive operations.
|
|
User can override each warning. Use when touching prod, debugging live systems,
|
|
or working in a shared environment. Use when asked to "be careful", "safety mode",
|
|
"prod mode", or "careful mode". (gstack)
|
|
triggers:
|
|
- be careful
|
|
- warn before destructive
|
|
- safety mode
|
|
allowed-tools:
|
|
- Bash
|
|
- Read
|
|
hooks:
|
|
PreToolUse:
|
|
- matcher: "Bash"
|
|
hooks:
|
|
- type: command
|
|
command: "bash $HOME/.claude/skills/gstack/careful/bin/check-careful.sh"
|
|
statusMessage: "Checking for destructive commands..."
|
|
sensitive: true
|
|
---
|
|
|
|
# /careful — Destructive Command Guardrails
|
|
|
|
Safety mode is now **active**. Every bash command will be checked for destructive
|
|
patterns before running. If a destructive command is detected, you'll be warned
|
|
and can choose to proceed or cancel.
|
|
|
|
```bash
|
|
mkdir -p ~/.gstack/analytics
|
|
echo '{"skill":"careful","ts":"'$(date -u +%Y-%m-%dT%H:%M:%SZ)'","repo":"'$(basename "$(git rev-parse --show-toplevel 2>/dev/null)" 2>/dev/null || echo "unknown")'"}' >> ~/.gstack/analytics/skill-usage.jsonl 2>/dev/null || true
|
|
```
|
|
|
|
## What's protected
|
|
|
|
| Pattern | Example | Risk |
|
|
|---------|---------|------|
|
|
| `rm -rf` / `rm -r` / `rm --recursive` | `rm -rf /var/data` | Recursive delete |
|
|
| `DROP TABLE` / `DROP DATABASE` | `DROP TABLE users;` | Data loss |
|
|
| `TRUNCATE` | `TRUNCATE orders;` | Data loss |
|
|
| `git push --force` / `-f` | `git push -f origin main` | History rewrite |
|
|
| `git reset --hard` | `git reset --hard HEAD~3` | Uncommitted work loss |
|
|
| `git checkout .` / `git restore .` | `git checkout .` | Uncommitted work loss |
|
|
| `kubectl delete` | `kubectl delete pod` | Production impact |
|
|
| `docker rm -f` / `docker system prune` | `docker system prune -a` | Container/image loss |
|
|
|
|
## Safe exceptions
|
|
|
|
These patterns are allowed without warning:
|
|
- `rm -rf node_modules` / `.next` / `dist` / `__pycache__` / `.cache` / `build` / `.turbo` / `coverage`
|
|
|
|
## How it works
|
|
|
|
The hook reads the command from the tool input JSON, checks it against the
|
|
patterns above, and returns a `hookSpecificOutput` payload with
|
|
`permissionDecision: "ask"` and a warning reason if a match is found (the
|
|
decision must be nested under `hookSpecificOutput` — Claude Code ignores a
|
|
top-level `permissionDecision`). You can always override a MEDIUM warning and
|
|
proceed.
|
|
|
|
## HIGH tier (hard deny)
|
|
|
|
A tiny set of catastrophic commands is **denied outright** while /careful is
|
|
active, not just warned:
|
|
|
|
- `rm -r`/`-R` targeting exactly `/`, `~`, or `$HOME`
|
|
- `git push --force` / `-f` to the repo's **default branch**
|
|
|
|
HIGH only fires on SIMPLE commands (no `;`, `&&`, `||`, `|`, newline) — string
|
|
matching cannot resolve what a compound command does, so compound shapes fall
|
|
through to the ordinary MEDIUM ask. `--force-with-lease` is deliberately not
|
|
matched (it's the safe force variant). This is a best-effort advisory
|
|
hard-stop, not a policy boundary: /careful is opt-in and session-scoped, so
|
|
the escape hatch is ending the /careful session.
|
|
|
|
## Project patterns (additive only)
|
|
|
|
Add your own warn rules — one POSIX ERE per line, `#` comments allowed — in:
|
|
|
|
- `~/.gstack/careful-patterns.txt` (all projects)
|
|
- `~/.gstack/projects/<slug>/careful-patterns.txt` (this project)
|
|
|
|
Matching lines warn with `[careful] Project rule matched: <pattern>`. Config
|
|
can only ADD rules: the files are consulted after the built-in families, so no
|
|
file content can suppress or weaken a baseline warning. Invalid regex lines
|
|
are skipped.
|
|
|
|
To deactivate, end the conversation or start a new one. Hooks are session-scoped.
|