# Prompt-injection scanner

How Bookmark Context flags web pages that try to manipulate an AI reader.

Saved pages are third-party text that an AI assistant will later read. The
scanner looks for content written to manipulate that assistant — "ignore
previous instructions", fake system tokens, requests to read local files — and
flags it. It never edits or blocks text on its own; it produces a score and the
caller decides.

## Categories and weights

Each chunk is matched against regex categories. The weight is how strongly a
match implies a real injection attempt:

| Category | Weight | Example signal |
|---|---|---|
| `system_token` | 0.95 | `<\|im_start\|>system`, `[INST]`, `### System:` |
| `exfiltration` | 0.90 | `~/.ssh/id_rsa`, `curl https://evil…`, `password: …` |
| `direct_override` | 0.85 | "ignore all previous instructions" |
| `llm_targeted_instruction` | 0.82 | "if you are an AI, you must…" |
| `concealment` | 0.80 | "do not mention this to the user" |
| `role_reassignment` | 0.75 | "you are now DAN", "enable developer mode" |
| `persistent_instruction` | 0.70 | "for all future requests, always…" |

## Score

```
score = max(category weight) + 0.05 × (number of extra categories that fired)
```

capped at 1.0. A clean chunk scores 0.0.

## Save-time gate

`POST …/bookmarks` returns a `ScanWarning` (and saves nothing) when **any** chunk
scores above 0, or when the page looks like an unrendered SPA shell
(`content_unscannable`). Override with `?force=true` — see
[Overriding a scan warning](/docs/guides/scan-warnings/).

## After indexing

Every stored chunk keeps its `injection_risk` and `injection_signals` as Chroma
metadata, and both appear in `search_collection` / `ask_collection` results so a
client can down-weight or ignore a suspicious passage.

## What it deliberately ignores

Credential-shaped placeholders that appear in every API doc —
`api_key="YOUR_API_KEY"`, `<your-key>`, `os.getenv("API_KEY")`, empty strings —
are not flagged. A warning that is always wrong teaches you to dismiss the one
that is right.
