Add ctxguard: hook-enforced context sanitization
Keeps credentials, company names, personal names and PII out of the model's context. Enforcement lives in Claude Code hooks rather than in instructions to the agent: a skill alone cannot protect anything, because by the time the agent reads a rule the surrounding context has already been sent. Credentials are removed irreversibly and marked. Entities from a user-supplied dictionary become stable aliases, rewritten back to real values on their way to disk and to the shell, so code and commands referring to them still work. Published from a clean tree; development history is not included.
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
---
|
||||
description: Show what ctxguard has been blocking and substituting
|
||||
---
|
||||
|
||||
```
|
||||
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/ctxguard.py" audit -n 40
|
||||
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/ctxguard.py" status
|
||||
```
|
||||
|
||||
The audit log records rule ids, tool names and aliases — never plaintext, so it is
|
||||
safe to read inside a session.
|
||||
|
||||
Read it for false positives: a rule that keeps firing on harmless content is a rule
|
||||
that will get the whole plugin switched off. Fixes, in order of preference:
|
||||
add the value to `allowlist` / `allowlist_substrings` in `policy.json`, narrow the
|
||||
rule, or add it to `disabled_secret_rules`.
|
||||
@@ -0,0 +1,26 @@
|
||||
---
|
||||
description: Protect a company name, person, hostname or project codename behind a stable alias
|
||||
---
|
||||
|
||||
Register a value in the ctxguard dictionary so it is replaced by an alias everywhere
|
||||
before it reaches the model, and restored on the way back to disk.
|
||||
|
||||
Arguments: `$ARGUMENTS` — the value, optionally followed by a type.
|
||||
|
||||
Run:
|
||||
|
||||
```
|
||||
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/ctxguard.py" entity add "<value>" --type <company|person|host|project|email|phone|custom> [--match stem] [--hint "<safe description>"]
|
||||
```
|
||||
|
||||
Guidance:
|
||||
- Use `--match stem` for Russian names and any word that inflects, so that
|
||||
`Петров / Петрова / Петровым` all match.
|
||||
- `--hint` is injected into the model's context verbatim, so it must describe the
|
||||
entity without repeating it ("retail customer", not "Globex is a customer"). The
|
||||
command rejects a hint containing the value.
|
||||
- Add spelling variants with repeated `--variant` (transliterations, abbreviations).
|
||||
|
||||
Afterwards show the new alias and run `entity list` so the user can see the whole
|
||||
dictionary. Never print the value back to the user's terminal — they typed it, but the
|
||||
transcript is exactly what we are trying to keep clean.
|
||||
@@ -0,0 +1,19 @@
|
||||
---
|
||||
description: Turn a block of text (ticket, log, email) into an aliased version safe to paste
|
||||
---
|
||||
|
||||
The user has text containing sensitive data and needs an aliased version they can
|
||||
send. A hook cannot rewrite a prompt, only block it, so this is the supported route.
|
||||
|
||||
Ask the user to paste the text into a file (or use text they already have on disk),
|
||||
then run:
|
||||
|
||||
```
|
||||
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/ctxguard.py" sanitize < <file>
|
||||
```
|
||||
|
||||
The aliased text goes to stdout; a per-rule summary goes to stderr.
|
||||
|
||||
Note: only values already in the dictionary become aliases. Credentials and PII are
|
||||
detected by pattern regardless. If a company or person came through untouched, it is
|
||||
not registered yet — offer `/ctx-entity`.
|
||||
@@ -0,0 +1,25 @@
|
||||
---
|
||||
description: Prove ctxguard actually blocks what it claims, and measure real leakage
|
||||
---
|
||||
|
||||
Two different questions, two different commands. Run both.
|
||||
|
||||
1. Does the machinery work? Drives the canary corpus through the real hook entry
|
||||
points in an isolated state directory:
|
||||
|
||||
```
|
||||
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/ctxguard.py" verify
|
||||
```
|
||||
|
||||
2. Did anything actually leak? Scans the transcripts — the record of what was really
|
||||
sent to the model — for real values from the dictionary:
|
||||
|
||||
```
|
||||
python3 "${CLAUDE_PLUGIN_ROOT}/scripts/ctxguard.py" scan-transcript
|
||||
```
|
||||
|
||||
Report both numbers plainly. The second is the one that matters: the first measures
|
||||
intent, the second measures outcome. If `scan-transcript` is non-zero, say so
|
||||
directly — it means data reached the model despite the hooks.
|
||||
|
||||
For a red-team run, `verify --adversarial` prints a brief to hand to a subagent.
|
||||
Reference in New Issue
Block a user