Skip to content

Context-file threat scanner: negation-blind role_pretend pattern silently drops the entire persona file #64268

Description

@imgyf

Summary

The context-file threat scanner is negation-blind: a SOUL.md containing the (perfectly benign, in fact anti-role-play) instruction

Don't pretend to be a specialist you're not.

matches the role_pretend pattern in tools/threat_patterns.py:

(rf'pretend\s+{_FILLER}(you\s+are|to\s+be)\s+', "role_pretend", "context"),

and because agent/prompt_builder.py replaces the entire context file with a placeholder on any single finding —

findings = _scan_for_threats(content, scope="context")
if findings:
    logger.warning("Context file %s blocked: %s", filename, ", ".join(findings))
    return f"[BLOCKED: {filename} contained potential prompt injection ({', '.join(findings)}). Content not loaded.]"

— the agent silently loses its whole persona/instruction file. The only signal is a single WARNING log line at prompt-build time. In our fleet, one agent ran persona-less for ~3 weeks before we noticed the warning while debugging something unrelated.

Reproduction

from tools.threat_patterns import scan_for_threats
content = "Route HR asks to the HR agent. Don't pretend to be a specialist you're not.\n"
print(scan_for_threats(content, scope="context"))   # ['role_pretend']

Observed on hermes-agent 0.18.0 (pattern present since at least 0.17.x).

Other context-scope patterns have the same negation/imperative blindness, e.g. a persona file saying "You are now the ops commander for X" trips role_hijack, and "never answer without restrictions" style phrasing trips remove_filters. Ordinary operator-authored instruction prose is a large false-positive surface.

Suggestions (any subset would help)

  1. Negation awareness or tighter anchoring for the role-play patterns — e.g. skip matches preceded by don't|do not|never|avoid within a few tokens, or anchor on second-person imperative forms actually used in injections.
  2. Fail smaller: redact/annotate the offending line(s) instead of dropping the whole file. Losing one sentence is recoverable; silently losing the entire persona changes agent behavior wholesale.
  3. Surface the block: a startup banner, hermes doctor check, or health-status flag when a context file is blocked. A log-level warning is easy to miss on a long-running gateway, and the failure mode (agent behaves generically) doesn't obviously point at the scanner.
  4. Optionally, a way for operators to mark their own config-managed context files as trusted (the threat model for scraped web content is very different from an operator-authored SOUL.md that ships with the deployment).

Happy to provide more detail from our deployment if useful.

Metadata

Metadata

Assignees

No one assigned

    Labels

    P2Medium — degraded but workaround existscomp/agentCore agent runtime: loop, agent_init, prompt builder, context-compression, responses endpointtool/skillsSkills system (list, view, manage)type/bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions