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

criticalsecurity

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.