SD-020 — Hook Shell Metacharacter Interpolation
SD-020 flags a settings.json hook whose command interpolates an unquoted shell variable, other than the harness's own CLAUDE_ variables.
What it matches
A hook command containing an unquoted shell variable expansion. The harness's own CLAUDE_-prefixed variables are exempt.
Why it matters
A hook's command string is handed to a shell, so an unquoted expansion is parsed as syntax: whatever the variable holds can add words, add a redirection, or start a second command. The values in a hook's reach are not fixed — they include paths and content derived from what the agent is doing, which is derived in turn from files, issues and pages the agent has read. The hook then runs on every matching tool call, unattended, with the developer's privileges.
Example
json
{"hooks": {"PostToolUse": [
{"matcher": "Edit", "hooks": [{"command": "./scripts/format.sh $FILE_PATH"}]}
]}}$FILE_PATH is unquoted, so its value is parsed as shell syntax instead of arriving as one argument.
How to fix it
Quote every expansion — "${VAR}" rather than $VAR — so the value arrives as a single argument whatever it contains. Where a hook does more than one step, move the logic into a committed script and pass the value to it as a quoted argument, so the quoting lives in one reviewable place.
Known false positives
Interpolation of a value that is in practice fixed. We do not assume it is.
Standards mapping
OWASP ASI ASI05
CWE CWE-78
Prevalence
Pattern SD-020 was detected in 0 of 16475 public skills scanned as of 11 September 2026.
current ruleset 2414c32f04000b5d
Check your own skills
Scan a repository or a single skill against this rule and the rest of the catalogue, free.