Fix spec review iteration 2: grammar, config, response examples

- Remove deferred condition from grammar production
- Add [meta] version to config.toml example
- Add PostToolUse allow response (empty object)
- Mark post.rules as deferred in directory tree
- Complete lockfile list for all supported ecosystems
- Handle startup race condition (EADDRINUSE retry)
- Note log rotation as deferred

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Flo
2026-03-27 14:26:19 +01:00
parent 0dca8797be
commit c7748bc2cf

View File

@@ -58,7 +58,7 @@ security-hooks/
│ ├── bash.rules # bash command rules
│ ├── edit.rules # file edit rules
│ ├── mcp.rules # MCP tool rules
│ ├── post.rules # post-tool-use checks
│ ├── post.rules # post-tool-use checks (deferred, reserved)
│ └── validators/ # complex Elixir validators
│ ├── unknown_executable.ex
│ ├── dependency_mutation.ex
@@ -149,7 +149,14 @@ PreToolUse ask (tier: suspicious):
}
```
PostToolUse block (note: PostToolUse uses top-level `decision`/`reason` fields, unlike PreToolUse which nests under `hookSpecificOutput`):
PostToolUse responses (note: PostToolUse uses top-level `decision`/`reason` fields, unlike PreToolUse which nests under `hookSpecificOutput`):
PostToolUse allow (no issues found):
```json
{}
```
PostToolUse block:
```json
{
"decision": "block",
@@ -202,7 +209,7 @@ comment := '#' <text to end of line>
rule := tier SP name NL clauses
tier := "block" | "suspicious"
name := '"' <text> '"'
clauses := matcher nudge [condition]*
clauses := matcher nudge
matcher := match | match_any | match_not_in | validator
match := INDENT "match " <regex to end of line> NL
match_any := INDENT "match_any" NL (INDENT2 <regex to end of line> NL)+
@@ -279,7 +286,7 @@ Rules are evaluated in file order. First match wins. Place specific rules before
**Tier: suspicious**
- Edits to CI/CD config: `.github/workflows/`, `.gitlab-ci.yml`, `Jenkinsfile`
- Edits to `Dockerfile`, `docker-compose.yml`
- Edits to lockfiles: `package-lock.json`, `mix.lock`, `Cargo.lock`, `poetry.lock`
- Edits to lockfiles: `package-lock.json`, `pnpm-lock.yaml`, `yarn.lock`, `mix.lock`, `Cargo.lock`, `poetry.lock`, `Gemfile.lock`, `go.sum`, `composer.lock`
- Dependency field changes in manifest files (validator: `DependencyMutation`)
### mcp.rules
@@ -344,6 +351,9 @@ sensitive = [
"/etc/passwd",
]
[meta]
version = "1.0.0"
[daemon]
idle_timeout_minutes = 30
log_format = "jsonl"
@@ -363,7 +373,7 @@ Merges on top of `config.toml`:
**Starting:** The shell shim starts the daemon on first hook call. The daemon writes its PID to `$XDG_RUNTIME_DIR/security-hooks/pid` (Linux/WSL) or `$TMPDIR/security-hooks/pid` (macOS) and opens the Unix socket. First-ever startup with a Burrito binary may take 1-3 seconds (unpacking); subsequent starts are ~300ms (BEAM boot). The install script pre-warms the daemon to avoid first-call latency.
**Health check:** The shim checks socket connectivity. If the PID file exists but the socket is dead, the shim kills the stale process and restarts.
**Health check:** The shim checks socket connectivity. If the PID file exists but the socket is dead, the shim kills the stale process and restarts. If two sessions race to start the daemon simultaneously, the second will get `EADDRINUSE` — the shim handles this by retrying the socket connection (the first daemon won the race and is now available).
**Idle shutdown:** The daemon exits after 30 minutes of inactivity (configurable via `daemon.idle_timeout_minutes`). Handles `SIGTERM` gracefully.
@@ -376,6 +386,8 @@ Merges on top of `config.toml`:
{"ts":"2026-03-26T14:02:05Z","event":"PreToolUse","tool":"Bash","input":"mix test","rule":null,"decision":"allow"}
```
Log rotation and size limits are deferred — users can manage this with external tools (logrotate, etc.) since the log path is well-defined.
Future: streaming connectors for centralized logging (stdout, webhook, syslog).
## Installation