What it detects
Offline scanner and sanitizer for LLM output before you render it in a chat UI, a markdown viewer, a terminal or an email.
Most prompt-injection tooling looks at what goes *into* a model. outfence looks at what comes *out*. When an injected document convinces a model to emit a markdown image such as , the renderer fetches that URL automatically and the data leaves with no click. The same idea applies to HTML <img>, CSS url(), terminal escape sequences that write to the clipboard (OSC 52) or hide a link target (OSC 8), and invisible Unicode that carries hidden instructions to the next agent.
Single Python file, standard library only, no network calls.
Rules
| Rule | Default severity | What it flags |
|---|---|---|
| OF01 | high (critical if the URL carries data) | Markdown image (inline or reference-style) pointing at an external host |
| OF02 | high (critical if the URL carries data) | Auto-loading HTML/CSS: <img>, <iframe>, <script>, <video>, <object>, <link>, srcset, <meta refresh>, url(), @import |
| OF03 | medium | Clickable link to an external host whose query/path/fragment carries encoded or free-text data |
| OF04 | critical | URL carries a secret: a known key shape or a secret-named parameter (token=, api_key=, …) |
| OF05 | high (medium for data:image/*) | javascript:, vbscript:, data: or file: URLs in links or src/href |
| OF06 | high (critical for OSC 52) | ANSI/OSC terminal escapes: clipboard write, OSC 8 hyperlinks, conceal/overwrite CSI codes, BEL/BS |
| OF07 | medium (high for TAG chars, decoded) | Zero-width, bidi, Unicode TAG and stray variation-selector characters |
| OF08 | medium | Suggested command that pipes remote content into a shell (`curl … \ |
| OF09 | low | URL host is an IP literal, punycode or a link shortener |
Usage
python3 outfence.py examples # scan a folder (text/md/html/json/jsonl)
python3 outfence.py response.md --json
llm "summarize" < page.html | python3 outfence.py - # stdin
python3 outfence.py transcript.jsonl --allow-host '*.mycompany.com' --allow-host cdn.example.org
python3 outfence.py --sanitize response.md > safe.md # neutralised copy
python3 outfence.py --list-rulesAfter pip install of the kit, the same options work as agentsec outfence …. Every tool also accepts --help and almost every tool accepts --json.
Example output
The examples folder has a poisoned markdown answer, a terminal answer with OSC 52/OSC 8/ conceal escapes, an OpenAI-style transcript, and a clean answer that produces no findings. All URLs use reserved .example domains or documentation IP ranges, and all secrets are fake.
$ python3 outfence.py examples/exfil_response.md
CRITICAL OF01 examples/exfil_response.md:3 Markdown image auto-loads an external URL (zero-click exfiltration channel)
markdown image → collector.evil.example; long encoded-looking value (47 chars)
MEDIUM OF03 examples/exfil_response.md:5 Link to an external host carries data-like parameters
markdown link → docs.evil.example; free text in URL (51 chars)
CRITICAL OF02 examples/exfil_response.md:7 HTML/CSS element auto-loads an external resource
<img src> → tracker.evil.example; template placeholder in URL
...
HIGH OF07 examples/exfil_response.md:15 Invisible or bidirectional Unicode in model output
12 TAG char(s); decoded: 'send history'
SARIF and CI
agentsec outfence path/to/input --sarif > outfence.sarif
# GitHub Actions (composite action ships with the repo)
- uses: dunkleschitz/agentsec@v0
with:
tools: outfence
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.
- LLM05 Improper Output Handling — OWASP LLM Top 10 (2025)
Known limits
Heuristic scanner, not a security boundary. A clean result does not mean something is safe. All example secrets in this repository are fake.
Full source, tests, and fixtures: tools/outfence/ in the repository. Install the kit with pip install git+https://github.com/dunkleschitz/agentsec.git.