* test(#3504): add failing-first parity, fail-closed, and bypass suites * fix(#3504): harden hook injection patterns and force-add guard * test(#3504): stage the scanner lib dependency in shared-hooks fixture * chore(#3504): backfill changeset pr number * test(#3504): build parity samples from fragments for the ci scan --------- Co-authored-by: sim <sim@local>
This commit is contained in:
@@ -512,7 +512,7 @@ If `.planning/` is in `.gitignore`, `commit_docs` is automatically `false` regar
|
||||
| Setting | Type | Default | Description |
|
||||
|---------|------|---------|-------------|
|
||||
| `hooks.context_warnings` | boolean | `true` | Show context window usage warnings via context monitor hook |
|
||||
| `hooks.workflow_guard` | boolean | `false` | Warn when file edits happen outside GSD workflow context (advises using `/gsd-quick` or `/gsd-fast`) |
|
||||
| `hooks.workflow_guard` | boolean | `false` | Warn when file edits happen outside GSD workflow context (advises using `/gsd-quick` or `/gsd-fast`). When enabled, the hook's one hard block — `git add -f` on `agent-*`/`worktree-agent-*` branches — also fails closed on internal error (see `docs/explanation/security-model.md`, #3504) |
|
||||
| `statusline.show_last_command` | boolean | `false` | Append `last: /<cmd>` suffix to the statusline showing the most recently invoked slash command. Opt-in; reads the active session transcript to extract the latest `<command-name>` tag (closes #2538) |
|
||||
| `statusline.context_position` | string | `"end"` | Position of the context-window meter. `"end"` (default) renders at line tail; `"front"` renders immediately after the model name so the meter stays visible in narrow terminals. Closes #2937 |
|
||||
| `statusline.show_context_tokens` | boolean | `false` | Append the absolute token count (e.g. `(156k)`) after the context meter's percentage. Sums input, cache-creation, cache-read, and output tokens from the hook payload — a broader basis than the meter's percentage (which excludes output tokens), so the two figures can diverge slightly. Opt-in; the meter is unchanged when the flag is absent |
|
||||
|
||||
@@ -151,12 +151,14 @@ module is the central security utility. It provides:
|
||||
|
||||
**Runtime hook: `gsd-prompt-guard.js`.** This hook fires on every Write or
|
||||
Edit call that targets `.planning/` files. It scans the content being written
|
||||
for the same injection patterns as `security.cjs` (a subset inlined directly
|
||||
into the hook for independence — the hook does not `require()` the module, so
|
||||
it runs even if the module path changes). Detection is **advisory-only**: the
|
||||
hook logs the finding but does not block the write. The rationale is that a
|
||||
false-positive block on a legitimate planning write would be more disruptive
|
||||
than a missed injection in a secondary scan layer.
|
||||
for injection patterns shared with `gsd-read-injection-scanner.js` through
|
||||
`hooks/lib/injection-patterns.js` — one module both hooks `require()`, so the
|
||||
two surfaces cannot drift apart (#3504). The set is deliberately a subset of
|
||||
`security.cjs`'s patterns: the hooks stay loadable standalone, without the
|
||||
compiled lib tree. Detection is **advisory-only**: the hook logs the finding
|
||||
but does not block the write. The rationale is that a false-positive block on
|
||||
a legitimate planning write would be more disruptive than a missed injection
|
||||
in a secondary scan layer.
|
||||
|
||||
**Runtime hook: `gsd-read-injection-scanner.js`.** This hook fires on the
|
||||
output of every Read, WebFetch, and WebSearch tool call. It scans the *content
|
||||
@@ -207,6 +209,25 @@ agents provides an additional containment layer: even if an injected string
|
||||
reaches an agent, it is structurally separated from the instruction region.
|
||||
Together these controls bracket the ingest → store → re-read lifecycle.
|
||||
|
||||
**Runtime hook: `gsd-workflow-guard.js` — advisory vs. blocking posture.**
|
||||
This hook has two legs with two deliberately different failure postures. The
|
||||
edit leg is **advisory**: when `hooks.workflow_guard` is enabled it warns on
|
||||
edits made outside a GSD workflow, and on any internal error it fails open
|
||||
(exit 0) — a broken advisory must never wedge a session's tool calls. The
|
||||
Bash leg carries the hook's one **hard block**: `git add -f` / `git add
|
||||
--force` on an `agent-*` or `worktree-agent-*` branch is blocked outright
|
||||
(`WORKTREE_AGENT_FORCE_ADD_FORBIDDEN`, exit 2), enforcing the
|
||||
skipped-gitignored contract. When the guard is enabled, this block leg
|
||||
**fails closed** (#3504): if an internal error strikes before the block
|
||||
decision and the blocking context can be re-derived from the payload (a Bash
|
||||
tool call, the guard enabled, the branch determinably an agent branch), the
|
||||
hook exits 2 rather than silently allowing. What it cannot establish — an
|
||||
unparseable payload, a non-Bash tool, the guard disabled, or a branch it
|
||||
cannot determine — still fails open. The known trade-off: on an agent branch
|
||||
with the guard enabled, a Bash call that trips an internal error is blocked
|
||||
even when it was not a force-add; that is the conservative direction for the
|
||||
one hard block this hook owns.
|
||||
|
||||
---
|
||||
|
||||
## Layer 3 — Repository and dependency integrity
|
||||
|
||||
Reference in New Issue
Block a user