hotfix: scan only the prompt field from the hook stdin envelope - #45
Merged
Conversation
Claude Code pipes a full UserPromptSubmit JSON envelope into the hook
(session_id, transcript_path, cwd, prompt_id, permission_mode,
hook_event_name, prompt), but scripts/prompt-guard.js read stdin as if it
were the bare prompt text. Everything downstream therefore operated on the
raw JSON string.
Changes:
- Add an extractPrompt(raw) helper to scripts/prompt-guard.js that parses
the stdin payload and returns payload.prompt. Falls back to the raw text
when stdin does not start with "{" or fails to parse, so the guard still
scans something when the script is invoked manually with a piped prompt
or if the hook protocol changes.
- Wire the helper into the stdin read path and expand the surrounding
comment to explain why only the "prompt" field may be scanned or echoed.
- Fixes redact mode pasting back the entire JSON envelope instead of the
cleaned prompt. The copy-pasteable block now contains just the redacted
prompt text, e.g. "my email is <PII_1>".
- Fixes a false-positive source affecting all three modes: transcript_path,
cwd and the various UUIDs in the envelope were previously scanned and
could produce findings, inflate the risk score, and block prompts that
contained no sensitive data themselves.
- Add tests/prompt-payload.test.ts with four end-to-end tests that spawn
the real hook script against a realistic envelope: redact mode returns
the cleaned prompt and leaks no envelope keys, a clean prompt with
metadata-heavy envelope is allowed through, sensitive data inside the
prompt still blocks, and non-JSON stdin falls back to a raw scan.
Full suite: 351 tests passing, lint clean.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Claude Code pipes a full
UserPromptSubmitJSON envelope into the hook, butscripts/prompt-guard.jsread stdin as if it were the bare prompt. Everything downstream operated on the raw JSON string.Most visibly, redact mode handed back the whole envelope as the "cleaned copy you can paste instead":
Fix
Added an
extractPrompt(raw)helper that parses the stdin payload and scans onlypayload.prompt. It falls back to the raw text when stdin doesn't look like JSON or fails to parse, so the guard still scans something when the script is piped a prompt manually or if the protocol changes.Redact mode now returns:
Secondary win:
transcript_path,cwdand the envelope UUIDs are no longer scanned. They were a false-positive source across all three modes — capable of producing findings, inflating the risk score, and blocking prompts that contained no sensitive data of their own.Tests
New
tests/prompt-payload.test.tsspawns the real hook script against a realistic envelope:promptfield still blocksFull suite: 351 passing, lint clean.
Notes
No
dist/change — the fix is inscripts/, which ships as-is, so the CIdist/freshness check is unaffected.