A zero-dependency heuristic scanner that flags prompt-injection indicators in untrusted text before it reaches an LLM.
Most prompt-injection defenses focus on the user's prompt. In real agent
systems, the more common exposure is the content an agent pulls in on its
own: a scraped web page, a tool's return value, a retrieved RAG chunk, a
user-uploaded file. sourceward is a small, auditable pre-filter for exactly
that boundary — no model download, no API key, no network call, no GPU.
Pure text in, a risk score and findings out.
from sourceward import scan
result = scan(fetched_web_page)
if result.verdict in {"likely_injection", "high_risk"}:
raise ValueError(f"Blocked untrusted content: {result.findings}")You might also want one — they catch phrasing this can't. But a regex/structural pass has properties an ML classifier doesn't:
- Zero supply-chain surface. No
transformers, notorch, no model weights to trust. Read every detection rule inpatterns.pyin five minutes. - Deterministic and fast. Microseconds per call, no cold start, safe to run on every tool result in a hot loop.
- Catches what classifiers often miss. Zero-width character stuffing, Unicode Tag-block steganography (an attack that renders as nothing but tokenizes fine), and base64-encoded instructions get structural checks, not just phrasing checks.
Use it as your first, cheap gate — and layer a stronger model-based classifier behind it if your threat model calls for one.
| Category | Example |
|---|---|
instruction_override |
"ignore all previous instructions...", including Arabic phrasing |
role_hijack |
"you are now...", fake system:/admin: markers, raw chat-template tokens (<|im_start|>, [INST]) |
exfiltration |
"send this conversation to...", markdown images pointing at external collection URLs |
prompt_leak |
"reveal your system prompt" |
hidden_content |
zero-width character runs, Unicode Tag-block steganography, instruction text hidden in HTML comments |
encoded_payload |
base64 blobs — recursively re-scanned after decoding, so an injection hidden inside the encoding is still caught |
Each finding carries a severity (low/medium/high/critical); the
overall risk_score (0-100) applies diminishing returns per category so ten
copies of one weak signal don't outweigh a single strong one.
pip install git+https://github.com/wedo911/sourceward.git(Local development: pip install -e ".[dev]" from a clone.)
sourceward scan document.txt
curl -s https://example.com | sourceward scan --stdin
sourceward scan document.txt --format json --fail-at suspicious--fail-at exits non-zero once the verdict reaches the given level
(suspicious / likely_injection / high_risk) — drop it straight into a
CI step or an agent's pipeline as a gate.
from sourceward import scan, scan_file
result = scan(text)
result.risk_score # 0-100
result.verdict # "clean" | "suspicious" | "likely_injection" | "high_risk"
result.findings # list[Finding]
result.by_category() # dict[str, list[Finding]]
scan_file("retrieved_doc.txt")See examples/rag_pipeline_example.py for
a worked example of gating a RAG pipeline.
- Not a jailbreak classifier for your own users' prompts — it's tuned for third-party content flowing into the model.
- Not a guarantee. Heuristics have false negatives against novel phrasing;
treat a
cleanverdict as "nothing obvious," not "safe." - Not a replacement for least-privilege tool design. Even a perfectly detected injection is safer to have never mattered because the agent had no destructive capability to hijack in the first place.
New detection patterns, additional languages, and false-positive reports are all welcome — open an issue or a PR. Keep the library dependency-free; that constraint is the point.
MIT — see LICENSE.