security-guidance plugin: eval_injection rule falsely flags ast.literal_eval()

Status Fixed / completed
Maintainer reply ✓ Yes — mhegazy
Activity 5 comments · opened May 1, 2026 · closed May 29, 2026
💡 Likely answer: A maintainer (mhegazy, contributor) responded on this thread — see the highlighted reply below.

Describe the bug

The eval_injection rule in the security-guidance plugin (plugins/security-guidance/hooks/security_reminder_hook.py) uses substring matching for "eval(" which catches ast.literal_eval() — a safe Python stdlib function that only parses literal values (strings, numbers, tuples, lists, dicts, booleans, None). It does NOT execute arbitrary code.

This causes legitimate uses of ast.literal_eval() to be blocked during Edit/Write/MultiEdit operations with:

⚠️ Security Warning: eval() executes arbitrary code and is a major security risk.
Consider using JSON.parse() for data parsing or alternative design patterns that
don't require code evaluation. Only use eval() if you truly need to evaluate
arbitrary code.

Steps to reproduce

  1. Install security-guidance@claude-plugins-official
  2. Attempt to edit a .py file containing ast.literal_eval()
  3. The hook blocks the edit

Expected behavior

ast.literal_eval() should not be flagged. It's a well-known safe alternative to eval() specifically designed for parsing Python literals without code execution.

Suggested fix

Add an exclude_substrings field to the pattern schema so specific safe patterns can be whitelisted. For the eval_injection rule:

{
    "ruleName": "eval_injection",
    "substrings": ["eval("],
    "exclude_substrings": ["ast.literal_eval"],
    "reminder": "..."
}

And update check_patterns to check exclusions before returning a match:

if substring in content:
    excluded = False
    for exclude_sub in pattern.get("exclude_substrings", []):
        if exclude_sub in content:
            excluded = True
            break
    if not excluded:
        return pattern["ruleName"], pattern["reminder"]

This is backward-compatible (patterns without exclude_substrings work as before) and extensible to other false-positive cases.

Related

  • Issue #46720 reports the same class of substring-matching false positive for exec( in docs/markdown files. An exclude_substrings mechanism would also help there (e.g. exclude markdown code blocks from triggering the hook).

View original on GitHub ↗

5 Comments

github-actions[bot] · 5 months ago

Found 3 possible duplicate issues:

  1. https://github.com/anthropics/claude-code/issues/46720
  2. https://github.com/anthropics/claude-code/issues/10114
  3. https://github.com/anthropics/claude-code/issues/44958

This issue will be automatically closed as a duplicate in 3 days.

  • If your issue is a duplicate, please close it and 👍 the existing issue instead
  • To prevent auto-closure, add a comment or 👎 this comment

🤖 Generated with Claude Code

yurukusa · 5 months ago

Three working workarounds for anyone blocked by this right now, in order from least to most invasive. The proposed exclude_substrings fix is the right long-term answer — these are stopgaps until it lands.

1. Just retry the Edit (zero work, but undocumented)

Reading the hook source (security_reminder_hook.py, end of main()):

if warning_key not in shown_warnings:
    shown_warnings.add(warning_key)
    save_state(session_id, shown_warnings)
    print(reminder, file=sys.stderr)
    sys.exit(2)  # Block tool execution
# else: falls through to sys.exit(0)

The (file_path, rule_name) warning key is added to session state before the block, and only the first occurrence blocks — the second attempt against the same file passes through with no warning and no block. So if you hit this in the middle of work: re-issue the same Edit and it goes through. Fragile (silent in subsequent sessions, also masks legitimate first-occurrence flags), but it gets you unstuck without touching files.

2. Local patch the rule (survives until next plugin update)

The patterns are inline in the same script. Two options:

a. Apply the exclude_substrings patch you outlined directly in the local copy at:

~/.claude/plugins/marketplaces/claude-plugins-official/plugins/security-guidance/hooks/security_reminder_hook.py

b. Or remove the rule entirely if you don't want any eval( warnings at all. Find the dict starting "ruleName": "eval_injection" and drop it from the patterns list.

Either gets reverted on the next claude plugin update security-guidance, so it's not durable, but it works today. Keep a one-line cron or git diff to detect when the upstream lands the fix and you can drop the local patch.

3. Use Bash + sed for the one-off edit (sidesteps the hook entirely)

The matcher is Edit|Write|MultiEdit, so Bash invocations are not checked. For a single mechanical change like adding a new ast.literal_eval(...) call, sed -i (or a here-doc + cat >) goes through unimpeded. Less ergonomic, but useful when you'd rather not patch plugin internals.

---

On the substring approach in general — agreed that exclude_substrings is the right primitive. While we're at it, the same matcher also flags any markdown/doc that quotes eval( for explanatory purposes (#46720), so the exclusion mechanism would want to also handle "inside a code-fence in a .md file" — possibly via a file_extensions_exclude: [".md", ".rst"] companion field on each rule. That's two false-positive classes covered by one schema addition.

Kraken2003 · 5 months ago

Thank you for this @yurukusa , was struggling with data science work for the past 2 weeks because of this.

mhegazy contributor · 4 months ago

Audit shows the eval_injection regex now has a negative lookbehind that excludes ast.literal_eval. Please retest on the latest plugin (anthropics/claude-plugins-official). Closing — please reopen if you still get the false positive.

github-actions[bot] · 2 months ago

This issue has been automatically locked since it was closed and has not had any activity for 7 days. If you're experiencing a similar issue, please file a new issue and reference this one if it's relevant.