Back to catalogue
SecurityMessageDisplay
Redact secrets from displayed output
Secrets never show on screen during a share
Intercepts assistant message text as it streams to the screen and replaces patterns matching API keys, GitHub tokens and Bearer credentials with [REDACTED] placeholders. The transcript and model context retain the original text; only the rendered display is sanitized.
What does the Redact secrets from displayed output hook do?
Redact secrets from displayed output is a Claude Code MessageDisplay hook. It fires automatically at that lifecycle event — outside the model, so it can't be skipped or forgotten. Secrets never show on screen during a share.
Because it is a deterministic Node.js script, it executes on every matching event without relying on the model to remember — the guarantee that makes agentic workflows safe to automate.
Use cases
- Prevent API keys from being visible on screen during pair programming or screen shares
- Sanitize assistant output that echoes back tool results containing credentials
- Compliance screen recording: ensure no secrets appear in recorded sessions
Tags
#security#redaction#secrets#display#compliance
settings.json fragment
{
"hooks": {
"MessageDisplay": [
{
"hooks": [
{
"command": "node $CLAUDE_PROJECT_DIR/.claude/hooks/message-display-redact-secrets.mjs",
"type": "command"
}
]
}
]
}
}Script · .claude/hooks/message-display-redact-secrets.mjs
#!/usr/bin/env node
// @hookstack message-display-redact-secrets
// Caviarde les secrets dans le contenu affiché (MessageDisplay)
import { readFileSync } from "node:fs";
import { fileURLToPath } from "node:url";
export function run(input) {
const delta = input.delta ?? "";
const redacted = delta
.replace(
/sk-(?:ant-api03-|proj-)[A-Za-z0-9_-]{20,}/g,
"[REDACTED-ANTHROPIC-KEY]",
)
.replace(/sk-[A-Za-z0-9]{20,}/g, "[REDACTED-API-KEY]")
.replace(/ghp_[A-Za-z0-9]{36}/g, "[REDACTED-GH-TOKEN]")
.replace(/ghs_[A-Za-z0-9]{36}/g, "[REDACTED-GH-TOKEN]")
.replace(/Bearer [A-Za-z0-9_\-.]{20,}/g, "Bearer [REDACTED]");
if (redacted === delta) return null;
return {
hookSpecificOutput: {
hookEventName: "MessageDisplay",
displayContent: redacted,
},
};
}
/* v8 ignore next 5 */
if (process.argv[1] === fileURLToPath(import.meta.url)) {
const input = JSON.parse(readFileSync(0, "utf8"));
const result = run(input);
if (result) process.stdout.write(JSON.stringify(result));
}
Learn more
Related hooks
- Secret detection before Bash executionCatch a leaked API key before it ever runs
- Destructive command blockingStops a disk-wiping shell command before it runs
- Sensitive file write protectionYour .env and keys stay untouched by the agent
- Lock file write protectionLock files stay intact - package manager only