SD-018 — settings.json Redundant Deny Rule

SD-018 flags a settings.json deny entry that a wildcard allow entry already covers, which shows the allowlist is wider than the deny beside it suggests it was meant to be.

What it matches

A deny entry for a shell subcommand that a wildcard allow entry already covers. Deny takes precedence, so the deny still blocks; what the pair shows is an allowlist wider than it implies.

Why it matters

The deny itself still holds — Claude Code applies deny over allow — so nothing is bypassed by this pair today. What it shows is that the restriction lives in the wrong place: the allow entry grants a whole family of commands and the deny carves exactly one back out, so every other command in that family is granted and nobody wrote down that they were. The intent survives on one rule's precedence rather than on the allowlist, and renaming or re-spelling the denied command leaves the broad grant standing with nothing carved out of it.

Example

json

{"permissions": {
  "allow": ["Bash(git *)"],
  "deny":  ["Bash(git push *)"]
}}

The allow grants every git subcommand; the deny names back only one of them.

How to fix it

Express the restriction in the allowlist: list the subcommands the agent may run instead of granting the whole command and denying one. Keep a deny only as defence in depth for something the allowlist already excludes — a deny that carves a hole out of a wildcard is hiding how wide that wildcard is.

Known false positives

A deny kept deliberately as defence in depth.

Standards mapping

OWASP ASI ASI03

CWE CWE-732

Agent Skills in the Wild PE1

Prevalence

highpermission_hygiene

Pattern SD-018 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.