agentsec · Tools · tmplfence

Offline · stdlib-only · MIT

tmplfence: detect prompt-template injection in system prompts in AI agents


Offline prompt-template injection scanner for LLM apps.

What it detects

Offline prompt-template injection scanner for LLM apps.

Many agent and chatbot codebases build prompts with Jinja2, Mustache/Handlebars, or Python f-strings / .format / %. When a user-controlled placeholder is interpolated into a system / developer (privileged) section, the end user (or a retrieved document) can inject instructions that look like policy. tmplfence reviews template files and prompt-construction code for that class of bug.

Standard library only. It never connects to a network and never renders templates.

Rules

RuleSeverityWhat it flags
TM01criticalUser-controlled variable interpolated into a system/developer prompt
TM02highUnescaped / raw rendering of a user-ish variable (`\
TM03highUser-ish variable adjacent to role delimiters (`<\
TM04highDangerous Jinja2 construct in a prompt template ({%, `\
TM05highPython f-string / .format / % / concat of user data into a system prompt
TM06mediumSystem instructions and user placeholders share one undifferentiated template
TM07mediumDynamic include / extends / Mustache partial with a variable path
TM08mediumTriple-mustache or `\
TM09lowBroad catch-all placeholder (input / query / message / text) in a privileged section
TM10infoInventory of templates and placeholders scanned (disable in CI with --disable TM10)

Usage

python3 tmplfence.py template.j2 [more.py dir/ -]
  --disable RULE ...  skip rule ids (e.g. TM10 TM09)
  --json              machine-readable output
  --min-severity S    hide findings below S (default info)
  --fail-on S         exit 1 if any finding >= S (default high; any, none)
  --list-rules

After pip install of the kit, the same options work as agentsec tmplfence …. Every tool also accepts --help and almost every tool accepts --json.

Example output

Flags TM01 on {{ user_input }} and {{ ticket }} inside a system-style template (exit 1).

Flags TM05/TM01 where an f-string and string concatenation put user_input into system_prompt / system_message.

examples/clean_system.j2 and examples/clean_chat.json are clean under --fail-on high (exit 0) — user content stays in the user role. See also system_raw.mustache (TM02), dangerous_jinja.j2 (TM04/TM07), role_adjacent.tpl (TM03), and chat_templates.json.

python3 tmplfence.py examples/system_prompt.j2 --disable TM10

SARIF and CI

agentsec tmplfence path/to/input --sarif > tmplfence.sarif

# GitHub Actions (composite action ships with the repo)
- uses: dunkleschitz/agentsec@v0
  with:
    tools: tmplfence
    paths: path/to/input
    fail-on: high

SARIF 2.1.0 can be uploaded to GitHub code scanning. The composite action writes per-tool reports under agentsec-sarif/ by default.

OWASP mapping

Heuristic mapping of what this tool helps a reviewer look for. Not an official OWASP endorsement, and not complete coverage of any item. Sources: OWASP Top 10 for LLM Applications 2025 and OWASP Top 10 for Agentic Applications 2026.

Known limits

flagged (TM09/TM01); an attacker-controlled field named crm_note may be missed unless it matches a user-ish pattern — review snippets.

Python, other languages) is invisible.

custom delimiters can evade TM04.

prompt injection once messages are assembled.

Full source, tests, and fixtures: tools/tmplfence/ in the repository. Install the kit with pip install git+https://github.com/dunkleschitz/agentsec.git.