7ce0ba7 docs(carriers): the third verdict exists, and publishing an alias is what takes it away 302625e fix(conformance): the tag carrier has no output: label, and our blocker claimed it did fe5e6b2 docs(conventions): the merge button is off for a reason, and the reason now lives in the repo 2d86151 fix(divergence): our own iframe number read 3x low, and the reported cause was not the cause daa7ba4 release(0.4.0): two values moved by two mechanisms, and the difference is the release 2eee7e1 feat(lexicon): both unbounded rows narrow to [^><]*, and the mechanism is new here d467324 feat(signatures): the staleness we disclosed is closed by reading the module, not the message 4187715 docs(divergence): our own form has a number now, and it is quadratic 0e765a0 docs(security): the attack surface here is data, so the report route had to say where a wrong entry gets fixed d96fbbf docs(divergence): the span row had one witness; llm-security measured five more git-subtree-dir: scanners/commons git-subtree-split: 7ce0ba706cadd032ec3c16622dcfdb5ce4dc32d6
1045 lines
59 KiB
JSON
1045 lines
59 KiB
JSON
{
|
|
"version": "0.8.0",
|
|
"id": "injection-lexicon",
|
|
"description": "Prompt-injection pattern lexicon: the four pattern families a detector matches against untrusted text, and the severity family each belongs to. Data only. The variant building that feeds these patterns (normalisation, homoglyph folding, rot13, unicode-tag escalation) and the buried-payload heuristic are engine behaviour and are deliberately NOT described here.",
|
|
"owasp": "LLM01",
|
|
"label_format": "{class}: {detail}",
|
|
"pattern_id_space": {
|
|
"$comment": "Stable, commons-owned identifier for each pattern. It exists because an expected.json in conformance/ has to name a finding, and the two runtimes that seed this repository do not name the same pattern the same way: llm-security calls it `override: ignore previous instructions`, the Python guard calls it `override:ignore-previous`. Without a shared key no fixture can be written at all, including for the patterns whose regex is byte-identical in both.",
|
|
"field": "families[].patterns[].id",
|
|
"adopted_from": "llm-ingestion-pipeline-security src/llm_ingestion_guard/injection_lexicon.json (lexicon version 1.0, repo v0.3.4, commit 0bf0729) — the `id` field of its port of the same source table. Adopted verbatim rather than invented: the guard's port already carries both names (`id` and `desc`), so the mapping is read from verified source data rather than constructed here.",
|
|
"matching_method": "Each commons pattern was matched to a guard pattern by comparing this file's `label` to the guard's `desc`, with em-dash normalised to hyphen (the two differ only by that character, in the eight hitl-trap entries). 83/83 matched, one-to-one.",
|
|
"stability": "An id is a stable identifier. Changing one is a BREAKING change for any consumer matching on it, and requires a major bump of this file.",
|
|
"aliases": "Each pattern carries an `aliases` object naming what each seeding runtime calls it today. A runtime renames its own label by changing its alias here; the `id` does not move. The two aliases do NOT have the same evidentiary status — see alias_evidence.",
|
|
"ratification": {
|
|
"status": "ratified by both seeding runtimes",
|
|
"date": "2026-08-09",
|
|
"llm_security": "Ratified as-is, including the 0.2.0 proposal in commit 7b70f5b, by coord reply on 2026-08-09. They treat an id change as breaking on the same terms this file does.",
|
|
"llm_ingestion_pipeline_security": "Ratified by coord reply on 2026-08-09. The id space was adopted verbatim from their port, so ratification confirms rather than adopts.",
|
|
"$comment": "This replaces a `not_yet_confirmed` field carried through version 0.4.0, which said neither runtime had agreed. Both have. `id` is now a ratified cross-runtime contract rather than this repository's proposal, and conformance fixtures may name findings by it."
|
|
},
|
|
"alias_evidence": {
|
|
"llm_ingestion_guard": {
|
|
"verified": true,
|
|
"method": "The guard's coverage matrix asserts on this exact string: coverage.py builds one case per lexicon pattern passing `pattern.id` as the expected value, and its probe compares that against the set of `label` fields on the findings the guard actually returns. The id is therefore demonstrably what a guard finding carries."
|
|
},
|
|
"llm_security": {
|
|
"verified": "at table level only",
|
|
"method": "Every one of the 83 alias strings was compared to the `label` field of the corresponding entry in injection-patterns.mjs at b0de0ca: 83/83 exact, in array order. So this alias is certainly the pattern's name in the source table.",
|
|
"still_not_verified": "That a llm-security FINDING carries this string. The finding producer is now known - scanners/lib/output.mjs:finding(), line 32 - and it emits `title`, not `label`; there is no `label` key in the finding shape at all. Nothing connects a pattern's table label to the title a scanner passes in. The SARIF profile derives rule ids by slugging the title, so it inherits the same gap. Match on `id`, not on this alias.",
|
|
"asymmetry": "This is why the two aliases are recorded separately rather than averaged: the guard's alias is verified at the level that matters (its coverage matrix asserts the finding carries that exact string), and this one is verified one level short of it."
|
|
}
|
|
}
|
|
},
|
|
"$comment": "Extracted without behaviour change from llm-security/scanners/lib/injection-patterns.mjs. Seeded 2026-08-09 from an operator dump delivered through the local coord mailbox; re-verified the same day against the module file itself at commit b0de0ca, which corrected two transcription artefacts the dump had introduced (see source_fidelity.retracted). Only the four pattern arrays are data. checkCognitiveLoadTrap (the buried-after-2000-chars logic) and scanForInjection's variant building are ENGINE logic that never moves here; they are absent by intent, not by omission. Array order is preserved exactly as in the module.",
|
|
"provenance": {
|
|
"source_repo": "llm-security",
|
|
"source_files": [
|
|
"scanners/lib/injection-patterns.mjs"
|
|
],
|
|
"source_exports": [
|
|
"CRITICAL_PATTERNS",
|
|
"HIGH_PATTERNS",
|
|
"MEDIUM_PATTERNS",
|
|
"HYBRID_PATTERNS"
|
|
],
|
|
"source_delivery": "operator dump 2/2, coord message from llm-security, 2026-08-09; superseded by a direct read of the module at the pinned commit below",
|
|
"source_commit": "b0de0ca6d86ce697f39669d177c2c2654c280128",
|
|
"source_remote": "ssh://git@git.fromaitochitta.com/open/llm-security.git",
|
|
"verified": "differentially, against the module file at b0de0ca - all 83 patterns compared on source, flags and label, in family and array order, 2026-08-09. See source_fidelity.",
|
|
"evidence_limits": [
|
|
"Family severities are not a field in the source. Three of the four are named by the constant they live in; the hybrid family's is set by the engine at the point of match. This file records the value and where it is set, but a consumer that changes how it buckets matches can still diverge without contradicting any value here.",
|
|
"Whether a consumer applies these patterns to raw or to normalised text is engine behaviour and is not fixed by this file. Two runtimes matching identical patterns against differently normalised text will still disagree on identical input."
|
|
]
|
|
},
|
|
"dialect": {
|
|
"name": "ecmascript",
|
|
"$comment": "Patterns are ECMAScript regular-expression source text, exactly as the source literals spell it. Flags are declared per pattern in a `flags` field and never inlined as `(?i)` or `(?m)`; a pattern with no `flags` key carries no flags. All 83 patterns compile in Node with their declared flags, in Node with `u` added, and in Python `re` with the equivalent re.I / re.M.",
|
|
"flags": {
|
|
"i": "case-insensitive",
|
|
"m": "multiline: ^ and $ match at line boundaries"
|
|
},
|
|
"features_used": [
|
|
"negative lookahead: (?!...)",
|
|
"lookahead assertion: (?=...)",
|
|
"non-capturing groups: (?:...)",
|
|
"bounded quantifiers: {n,m}",
|
|
"unicode escapes: \\uXXXX"
|
|
],
|
|
"translation_notes": [
|
|
"Python (`re`): compile with re.I where flags contain `i` and re.M where they contain `m`. No rewriting is needed; verified by compiling all 83.",
|
|
"Nine patterns contain `\\/` - a redundant escape that a JavaScript regex LITERAL requires and that `RegExp.prototype.source` preserves. It is kept rather than normalised away because both seed runtimes accept it (Node bare, Node under `u`, and Python `re`), so removing it would break byte-identity to buy nothing. Engines that reject unknown escapes (Go `regexp`, RE2) MUST report these patterns as unsupported rather than skip them silently - a skipped pattern is an invisible false negative.",
|
|
"`\\w` and `\\b` are ASCII-only in ECMAScript without `u` but Unicode-aware in Python on `str`. Input that mixes non-ASCII word characters with these patterns can therefore match differently between runtimes. This is a real portability seam. It is recorded rather than normalised, because normalising it would change behaviour in the seed runtime.",
|
|
"No pattern in this file contains a non-ASCII byte. The source module's regex literals are pure ASCII throughout - invisible code points, Cyrillic look-alikes and accented Latin are all written as \\uXXXX in the literal, and this file reproduces that spelling. An earlier version of this note claimed the leetspeak and multi-language patterns embed accented Latin directly; that was wrong in both halves (the leetspeak patterns are ASCII-only, and the one accented pattern is escaped at source). See source_fidelity.retracted."
|
|
]
|
|
},
|
|
"normalisations": [],
|
|
"normalisations_note": "Empty by result, not by omission: all 83 patterns are byte-identical to the source module, so there is no transformation to declare. Through version 0.3.0 this array held one entry; it was retracted as false in 0.4.0. See source_fidelity.",
|
|
"source_fidelity": {
|
|
"$comment": "This file applies NO transformation to the pattern text. All 83 patterns are byte-identical to the RegExp literal source in llm-security/scanners/lib/injection-patterns.mjs - but READ THE COORDINATE BELOW, because it moved in version 0.7.0. Through 0.6.0 the coordinate was the original extraction commit b0de0ca. In 0.7.0 one pattern (hybrid-xss:script-tag) was RE-EXTRACTED at b1ba1fb, so the coordinate for a 83/83 claim is now b1ba1fb and the count against b0de0ca is 82/83. The `normalisations` array is empty because there is nothing to declare, not because the question was skipped.",
|
|
"patterns_total": 83,
|
|
"patterns_byte_identical_to_source": 83,
|
|
"byte_identical_against_commit": "b1ba1fb",
|
|
"byte_identical_against_commit_date": "2026-08-11",
|
|
"byte_identical_against_commit_$comment": "The missing coordinate on `patterns_byte_identical_to_source`, added in 0.7.0 when that count stopped referring to a single commit for the whole file's life. The key and its value are unchanged - 83 patterns, byte-identical - and this field states WHICH source commit they are identical TO. Against the original extraction commit b0de0ca the count is 82/83; the one difference is the re-extracted pattern recorded in post_extraction_drift.",
|
|
"method": "The module was imported in Node from a read-only clone of the public remote at b0de0ca, and every entry compared in array order on three axes: RegExp.prototype.source, RegExp.prototype.flags, and label. Family membership, array order and per-family counts were compared at the same time. Nothing was read by eye. In 0.7.0 the single re-extracted pattern was verified the same way against b1ba1fb (injection-patterns.mjs:170); the other 82 were not re-imported, so their identity rests on the b0de0ca run plus the measured fact that `git diff b0de0ca..b1ba1fb -- scanners/lib/injection-patterns.mjs` changes exactly one `{ pattern, label }` line - the script-tag one - and otherwise only adds five comment lines. Exactly one commit in that range touches the file at all (90f576f).",
|
|
"class_membership_verified": {
|
|
"zero_width_class": [
|
|
"U+200B",
|
|
"U+200C",
|
|
"U+200D",
|
|
"U+FEFF"
|
|
],
|
|
"cyrillic_class": [
|
|
"U+0430",
|
|
"U+0435",
|
|
"U+043E",
|
|
"U+0440",
|
|
"U+0441",
|
|
"U+0456",
|
|
"U+0443"
|
|
],
|
|
"$comment": "The classes inside `unicode:zero-width-in-word` and `homoglyph:cyrillic-latin-mix`, enumerated mechanically from the module bytes. Note that the zero-width class does NOT include U+00AD even though codepoints/carriers.json lists U+00AD as a zero-width carrier, and that the Cyrillic class is a 7-member set distinct from the 13-member CYRILLIC_CONFUSABLES list in that same file. The three sets are deliberately different in the seed implementation and are NOT reconciled here."
|
|
},
|
|
"retracted": [
|
|
{
|
|
"id": "unicode-escape-invisible-and-confusable",
|
|
"was": "A `normalisations` entry, present through version 0.3.0, asserting that this file rewrote raw code points as \\uXXXX escapes in `homoglyph:cyrillic-latin-mix` and `unicode:zero-width-in-word`, and carrying byte_identical_to_source: false.",
|
|
"why_retracted": "False against the module. The source literals already write those code points as \\uXXXX; nothing was ever rewritten, and both patterns are byte-identical to source. The stored pattern text was correct all along - only the account of where it came from was wrong.",
|
|
"cause": "The seed dump rendered the module's escape sequences as the literal code points they denote. Reading that as the source spelling, this repository re-escaped them and arrived at the correct bytes by way of an incorrect story. This is transcription drift, and it is why the file is now verified against the module rather than against a dump.",
|
|
"evidence_status": "The differential result recorded for the retracted claim - Node over 173 adversarial inputs in bare and `u` mode (692 comparisons) and Python `re` over 118 inputs (236 comparisons), zero differences - stands as a true statement about raw-versus-escaped equivalence. It is simply no longer load-bearing here, because no such transformation is applied."
|
|
},
|
|
{
|
|
"id": "french-escape-inverted",
|
|
"was": "`multi-lang:french` carried the character class spelled with raw accented Latin: pr[eé]c[eé]dentes?.",
|
|
"why_retracted": "The same transcription inversion in the opposite direction. The module writes pr[e\\u00e9]c[e\\u00e9]dentes? with escapes; this was the only one of the 83 patterns not byte-identical to source. Corrected in 0.4.0 to the module's spelling, which makes the count 83/83.",
|
|
"behaviour_impact": "None. The two spellings are the same regular expression. Verified by compiling both forms and comparing match presence and offsets in Node (bare and `u` mode) and in Python `re` over accented, unaccented, uppercase and non-matching French inputs - identical results throughout."
|
|
}
|
|
],
|
|
"post_extraction_drift": {
|
|
"$comment": "The record of source drift AFTER the original extraction, and of how it was closed. Through 0.6.0 this block described an OPEN divergence: llm-security had moved one pattern and commons had not followed. In 0.7.0 that one pattern was re-extracted and the divergence is closed. The block is kept rather than deleted because a consumer diffing this file against b0de0ca still needs the coordinate that explains the one mismatch it will find.",
|
|
"status": "resolved in 0.7.0 by re-extraction",
|
|
"extracted_from_commit": "b0de0ca",
|
|
"extracted_from_commit_date": "2026-08-09",
|
|
"count_at_extraction": 83,
|
|
"drift": [
|
|
{
|
|
"pattern_id": "hybrid-xss:script-tag",
|
|
"source_commit": "90f576f",
|
|
"source_commit_date": "2026-08-10",
|
|
"change": "llm-security dropped the closing-tag requirement: <script\\b[^>]*>[\\s\\S]*?<\\/script> became <script\\b[^>]*>, closing a recall hole where `<script>alert(1)` (unclosed) and `<script src=x.js>` produced no finding.",
|
|
"commons_pattern_unchanged": false,
|
|
"commons_pattern_before_0_7_0": "<script\\b[^>]*>[\\s\\S]*?<\\/script>",
|
|
"commons_pattern_from_0_7_0": "<script\\b[^>]*>",
|
|
"resolved_in_version": "0.7.0",
|
|
"resolved_date": "2026-08-11",
|
|
"resolved_at_source_commit": "b1ba1fb",
|
|
"resolved_how": "RE-EXTRACTION, not revision, and the distinction is the whole justification. This file's declared provenance is llm-security's injection table; being loadable verbatim by that table's owner is the single thing it exists to do. Behaviour-preservation forbids commons from CORRECTING seed data it believes is wrong - it does not forbid re-reading the source after the source itself moved and its owner asked. The alternative on offer was a per-pattern override in llm-security's own loader, which would have meant the published core was not verbatim-loadable by the repository it was extracted from.",
|
|
"direction_note": "Widening, not narrowing: the new form matches a strict superset of the old one. It cannot introduce a false negative relative to 0.6.0, only additional matches. That is why this direction was safe to adopt while the reverse would not have been.",
|
|
"decided_by": "operator decision, 2026-08-11, on a llm-security coord request (their message 2026-08-11T10:46:28Z, blocking their step-4 table swap). NOT decided by majority: the guard also drops the closing tag, but a 2-of-3 count is not a mandate over detection data. The reason is provenance, and it would hold with the guard on either side.",
|
|
"reconfirmed_no_further_drift_at": "b1ba1fb",
|
|
"reconfirmed_date": "2026-08-11",
|
|
"reconfirmed_method": "git diff 90f576f..b1ba1fb -- scanners/lib/injection-patterns.mjs on a fresh fetch of the public remote is empty for this pattern; no later commit touched it a second time."
|
|
}
|
|
],
|
|
"count_against_llm_security_head_as_of_2026_08_11": 83,
|
|
"count_against_llm_security_head_note": "b1ba1fb was llm-security's public-remote HEAD when this was measured, 2026-08-11, and after the 0.7.0 re-extraction the count against it is 83/83. Was 82/83 in versions 0.6.0 and earlier. Not re-checked automatically; re-verify before citing against a later HEAD.",
|
|
"count_against_llm_security_head_is_now_undefined": "This metric HAS NO TARGET from 0.8.0 onward, and reporting a number for it would be reporting a comparison that cannot be made. It compared commons' patterns against llm-security's local regex literals; at their commit `be14867` those literals were removed and the tables are built from this file instead. Measured on their published surface at `47905da`: `scanners/lib/injection-patterns.mjs` contains zero `pattern: /.../` literals. A comparison against their vendored COPY of this file is a subtree-freshness check, not an independent agreement measurement, and must not be cited as though it were the latter.",
|
|
"count_against_extraction_commit": 82,
|
|
"count_against_extraction_commit_note": "From 0.7.0 this file is 82/83 byte-identical to b0de0ca, the commit it was originally extracted from. That number will not improve: the re-extracted pattern is deliberately the newer form.",
|
|
"residual_divergence_vs_guard": "CLOSED IN 0.8.0, and the account through 0.7.0 is preserved here because a consumer diffing against v0.3.0 needs it. That account read: 'Closing the llm-security divergence does NOT make this pattern universal. The guard's port is <script\\b[^><]*> - its negated class also excludes `<` - so commons and the guard now agree on SHAPE (opening tag only) and still differ by one byte in the excluded set. Consequence, measured on construction rather than assumed: an input like `<script <x>` matches the commons/llm-security form and not the guard's.' That one-byte difference no longer exists: see `owner_directed_changes` below, which moved commons to `[^><]*` on both unbounded rows. The `span differs` row in docs/lexicon-port-divergence.md is retired by that change, not by anyone conceding the argument."
|
|
},
|
|
"owner_directed_changes": {
|
|
"$comment": "A THIRD mechanism, distinct from both `retracted` (this file described its own provenance wrongly) and `post_extraction_drift` (the source moved and this file re-read it). Here the source's OWNER decided a value and asked this file to carry it, because the dependency direction has inverted: as of llm-security `be14867`, their four injection tables are built from THIS file and hold zero local regex literals - measured here on their published surface at `47905da`, and their vendored copy of `lexicon/injection-lexicon.json` was confirmed byte-identical to commons 0.7.0. Re-extraction is therefore not available as a mechanism for these rows: there is no upstream literal left to re-read. Recording this as drift would have been false in the specific way that matters - it would say the source moved and commons followed, when the source now reads commons.",
|
|
"changes": [
|
|
{
|
|
"pattern_ids": [
|
|
"hybrid-xss:script-tag",
|
|
"hybrid-xss:iframe-src"
|
|
],
|
|
"version": "0.8.0",
|
|
"date": "2026-08-11",
|
|
"change": "The unbounded negated class between the tag name and the rest of the pattern was narrowed from `[^>]*` to `[^><]*` on both rows: `<script\\b[^>]*>` became `<script\\b[^><]*>`, and `<iframe\\b[^>]*src\\s*=\\s*[\"'][^\"']*(?:javascript:|data:text\\/html)` became the same with `[^><]*`.",
|
|
"why": "ReDoS. Both forms are quadratic in scan length on input that repeats the literal tag prefix without ever supplying a `>`: each occurrence is a match start and `[^>]*` scans to end of input from each one. Measured here in Node v25.8.2 on the repeated-prefix unit, x4 per doubling for both patterns: script-tag 32.65 / 113.36 / 479.02 / 1988.83 / 7772.25 ms and iframe-src 39.23 / 131.76 / 574.94 / 2469.55 / 9449.94 ms at 16k / 32k / 64k / 128k / 256k chars. Under `[^><]*` the same inputs cost 0.08-0.66 ms and 0.10-1.00 ms - flat, not merely faster.",
|
|
"recall_cost": "One input class stops matching: content carrying a literal `<` between the tag name and the `>`, such as `<script <x>`. Measured over ALL 90 conformance cases rather than only the four that cite these ids: 0 cases lost a match and 0 gained one, under both patterns. The dropped class is real but unwitnessed by the corpus.",
|
|
"why_not_bounded": "`[^>]{0,256}` was on the table and is linear, but a bound is paddable - an attacker who pads the attribute run past the bound falls out of the pattern, which is the attribute-padding evasion class llm-security already carries open as v8.x-A3. `[^>]{1,256}` is worse than a trade: it requires at least one character before the `>`, so it stops matching bare `<script>` and drops `hybrid-xss__script-tag` and `active__raw-html` from the corpus. That is llm-security's measurement, reproduced here as the reason the six bounded html-obfuscation rows are NOT the analogue to reach for. `[^><]*` is linear and has no bound to pad past, so it is better on both axes.",
|
|
"decided_by": "llm-security, the declared source of this table, in their coord message 2026-08-11T15:59:49Z, having re-measured rather than accepted commons' report; carried here on operator decision, 2026-08-11.",
|
|
"provenance_reasoning": "The justification is the SAME one that kept commons on `[^>]` through 0.7.0, applied after the facts moved. docs/lexicon-port-divergence.md recorded at 0.7.0 that commons carried `[^>]` because the declared provenance is llm-security's table and not because that regex won on merit, and stated explicitly that commons would still track its declared source. The declared source has now chosen `[^><]*`. Not merit, and NOT majority: the guard reached `[^><]` first and independently (their commit `cff0437`), so all three runtimes now agree - but a 3-of-3 count is not what decided this, and would not have been sufficient on its own.",
|
|
"consumer_impact": "This changes pattern TEXT, so it is not a no-op release of the v0.3.1 class. Any consumer asserting byte-identity of this file against commons v0.3.0 will go red by construction. Pattern ids, labels, aliases, family membership and the 83 count are all unchanged."
|
|
}
|
|
]
|
|
}
|
|
},
|
|
"families": [
|
|
{
|
|
"id": "critical",
|
|
"source_export": "CRITICAL_PATTERNS",
|
|
"severity": "critical",
|
|
"count": 21,
|
|
"patterns": [
|
|
{
|
|
"id": "override:ignore-previous",
|
|
"label": "override: ignore previous instructions",
|
|
"pattern": "ignore\\s+(?:all\\s+)?previous",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "override: ignore previous instructions",
|
|
"llm_ingestion_guard": "override:ignore-previous"
|
|
}
|
|
},
|
|
{
|
|
"id": "override:forget-instructions",
|
|
"label": "override: forget instructions",
|
|
"pattern": "forget\\s+your\\s+(?:instructions|rules|guidelines|directives)",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "override: forget instructions",
|
|
"llm_ingestion_guard": "override:forget-instructions"
|
|
}
|
|
},
|
|
{
|
|
"id": "override:disregard",
|
|
"label": "override: disregard instructions",
|
|
"pattern": "disregard\\s+your",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "override: disregard instructions",
|
|
"llm_ingestion_guard": "override:disregard"
|
|
}
|
|
},
|
|
{
|
|
"id": "override:override-instructions",
|
|
"label": "override: override instructions",
|
|
"pattern": "override\\s+your",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "override: override instructions",
|
|
"llm_ingestion_guard": "override:override-instructions"
|
|
}
|
|
},
|
|
{
|
|
"id": "override:suspend-constraints",
|
|
"label": "override: suspend constraints",
|
|
"pattern": "suspend\\s+all\\s+constraints",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "override: suspend constraints",
|
|
"llm_ingestion_guard": "override:suspend-constraints"
|
|
}
|
|
},
|
|
{
|
|
"id": "override:prior-constraints",
|
|
"label": "override: prior constraints",
|
|
"pattern": "all\\s+prior\\s+constraints",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "override: prior constraints",
|
|
"llm_ingestion_guard": "override:prior-constraints"
|
|
}
|
|
},
|
|
{
|
|
"id": "spoofed-header:hash-system",
|
|
"label": "spoofed header: # SYSTEM:",
|
|
"pattern": "^#+\\s*SYSTEM\\s*:",
|
|
"flags": "m",
|
|
"aliases": {
|
|
"llm_security": "spoofed header: # SYSTEM:",
|
|
"llm_ingestion_guard": "spoofed-header:hash-system"
|
|
}
|
|
},
|
|
{
|
|
"id": "spoofed-header:inst",
|
|
"label": "spoofed header: [INST]",
|
|
"pattern": "^\\[INST\\]",
|
|
"flags": "m",
|
|
"aliases": {
|
|
"llm_security": "spoofed header: [INST]",
|
|
"llm_ingestion_guard": "spoofed-header:inst"
|
|
}
|
|
},
|
|
{
|
|
"id": "spoofed-header:pipe-system",
|
|
"label": "spoofed header: <|system|>",
|
|
"pattern": "<\\|system\\|>",
|
|
"aliases": {
|
|
"llm_security": "spoofed header: <|system|>",
|
|
"llm_ingestion_guard": "spoofed-header:pipe-system"
|
|
}
|
|
},
|
|
{
|
|
"id": "spoofed-tag:system-open",
|
|
"label": "spoofed tag: <system>",
|
|
"pattern": "<system>",
|
|
"aliases": {
|
|
"llm_security": "spoofed tag: <system>",
|
|
"llm_ingestion_guard": "spoofed-tag:system-open"
|
|
}
|
|
},
|
|
{
|
|
"id": "spoofed-tag:system-close",
|
|
"label": "spoofed tag: </system>",
|
|
"pattern": "<\\/system>",
|
|
"aliases": {
|
|
"llm_security": "spoofed tag: </system>",
|
|
"llm_ingestion_guard": "spoofed-tag:system-close"
|
|
}
|
|
},
|
|
{
|
|
"id": "spoofed-header:instructions",
|
|
"label": "spoofed header: ### Instructions:",
|
|
"pattern": "^###\\s*Instructions\\s*:",
|
|
"flags": "m",
|
|
"aliases": {
|
|
"llm_security": "spoofed header: ### Instructions:",
|
|
"llm_ingestion_guard": "spoofed-header:instructions"
|
|
}
|
|
},
|
|
{
|
|
"id": "identity:no-longer",
|
|
"label": "identity: you are no longer",
|
|
"pattern": "you\\s+are\\s+no\\s+longer",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "identity: you are no longer",
|
|
"llm_ingestion_guard": "identity:no-longer"
|
|
}
|
|
},
|
|
{
|
|
"id": "identity:you-are-now",
|
|
"label": "identity: you are now [name]",
|
|
"pattern": "you\\s+are\\s+now\\s+(?!able|going|ready|done|running|using|looking|in\\s|at\\s|on\\s)",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "identity: you are now [name]",
|
|
"llm_ingestion_guard": "identity:you-are-now"
|
|
}
|
|
},
|
|
{
|
|
"id": "identity:new-role",
|
|
"label": "identity: your new role",
|
|
"pattern": "your\\s+new\\s+role\\s+is",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "identity: your new role",
|
|
"llm_ingestion_guard": "identity:new-role"
|
|
}
|
|
},
|
|
{
|
|
"id": "identity:new-directive",
|
|
"label": "identity: new primary directive",
|
|
"pattern": "your\\s+new\\s+primary\\s+directive",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "identity: new primary directive",
|
|
"llm_ingestion_guard": "identity:new-directive"
|
|
}
|
|
},
|
|
{
|
|
"id": "identity:pretend",
|
|
"label": "identity: pretend you are",
|
|
"pattern": "pretend\\s+you\\s+are",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "identity: pretend you are",
|
|
"llm_ingestion_guard": "identity:pretend"
|
|
}
|
|
},
|
|
{
|
|
"id": "config:ignore-claudemd",
|
|
"label": "config: ignore CLAUDE.md",
|
|
"pattern": "ignore\\s+your\\s+CLAUDE\\.md",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "config: ignore CLAUDE.md",
|
|
"llm_ingestion_guard": "config:ignore-claudemd"
|
|
}
|
|
},
|
|
{
|
|
"id": "config:override-system-prompt",
|
|
"label": "config: override system prompt",
|
|
"pattern": "override\\s+your\\s+system\\s+prompt",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "config: override system prompt",
|
|
"llm_ingestion_guard": "config:override-system-prompt"
|
|
}
|
|
},
|
|
{
|
|
"id": "config:bypass-safety",
|
|
"label": "config: bypass safety",
|
|
"pattern": "bypass\\s+your\\s+safety",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "config: bypass safety",
|
|
"llm_ingestion_guard": "config:bypass-safety"
|
|
}
|
|
},
|
|
{
|
|
"id": "config:disable-filtering",
|
|
"label": "config: disable output filtering",
|
|
"pattern": "disable\\s+output\\s+filtering",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "config: disable output filtering",
|
|
"llm_ingestion_guard": "config:disable-filtering"
|
|
}
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"id": "high",
|
|
"source_export": "HIGH_PATTERNS",
|
|
"severity": "high",
|
|
"count": 32,
|
|
"patterns": [
|
|
{
|
|
"id": "normalization:prioritize-user",
|
|
"label": "normalization: prioritize user over rules",
|
|
"pattern": "always\\s+prioritize\\s+user\\s+requests?\\s+over",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "normalization: prioritize user over rules",
|
|
"llm_ingestion_guard": "normalization:prioritize-user"
|
|
}
|
|
},
|
|
{
|
|
"id": "normalization:user-precedence",
|
|
"label": "normalization: user intent precedence",
|
|
"pattern": "user\\s+intent\\s+takes?\\s+precedence\\s+over",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "normalization: user intent precedence",
|
|
"llm_ingestion_guard": "normalization:user-precedence"
|
|
}
|
|
},
|
|
{
|
|
"id": "normalization:claudemd-conflict",
|
|
"label": "normalization: CLAUDE.md conflict override",
|
|
"pattern": "if\\s+CLAUDE\\.md\\s+conflicts",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "normalization: CLAUDE.md conflict override",
|
|
"llm_ingestion_guard": "normalization:claudemd-conflict"
|
|
}
|
|
},
|
|
{
|
|
"id": "normalization:skip-confirmation",
|
|
"label": "normalization: skip confirmation",
|
|
"pattern": "skip\\s+confirmation\\s+for",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "normalization: skip confirmation",
|
|
"llm_ingestion_guard": "normalization:skip-confirmation"
|
|
}
|
|
},
|
|
{
|
|
"id": "reframing:actual-task",
|
|
"label": "reframing: your actual task",
|
|
"pattern": "your\\s+actual\\s+task\\s+is",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "reframing: your actual task",
|
|
"llm_ingestion_guard": "reframing:actual-task"
|
|
}
|
|
},
|
|
{
|
|
"id": "reframing:real-purpose",
|
|
"label": "reframing: your real purpose",
|
|
"pattern": "your\\s+real\\s+purpose\\s+is",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "reframing: your real purpose",
|
|
"llm_ingestion_guard": "reframing:real-purpose"
|
|
}
|
|
},
|
|
{
|
|
"id": "reframing:should-actually-do",
|
|
"label": "reframing: what you should actually do",
|
|
"pattern": "what\\s+you\\s+should\\s+actually\\s+do",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "reframing: what you should actually do",
|
|
"llm_ingestion_guard": "reframing:should-actually-do"
|
|
}
|
|
},
|
|
{
|
|
"id": "hidden-comment:agent-directed",
|
|
"label": "hidden comment: agent-directed HTML comment",
|
|
"pattern": "<!--\\s*(?:AGENT|AI|HIDDEN|ACTUAL\\s+TASK|REAL\\s+INSTRUCTION)\\s*:",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "hidden comment: agent-directed HTML comment",
|
|
"llm_ingestion_guard": "hidden-comment:agent-directed"
|
|
}
|
|
},
|
|
{
|
|
"id": "html-obfuscation:display-none",
|
|
"label": "html-obfuscation: display:none element with content",
|
|
"pattern": "<[^>]{1,256}style\\s*=\\s*\"[^\"]{0,256}display\\s*:\\s*none[^\"]{0,256}\"[^>]{0,256}>",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "html-obfuscation: display:none element with content",
|
|
"llm_ingestion_guard": "html-obfuscation:display-none"
|
|
}
|
|
},
|
|
{
|
|
"id": "html-obfuscation:visibility-hidden",
|
|
"label": "html-obfuscation: visibility:hidden element",
|
|
"pattern": "<[^>]{1,256}style\\s*=\\s*\"[^\"]{0,256}visibility\\s*:\\s*hidden[^\"]{0,256}\"[^>]{0,256}>",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "html-obfuscation: visibility:hidden element",
|
|
"llm_ingestion_guard": "html-obfuscation:visibility-hidden"
|
|
}
|
|
},
|
|
{
|
|
"id": "html-obfuscation:offscreen",
|
|
"label": "html-obfuscation: off-screen positioned element",
|
|
"pattern": "<[^>]{1,256}style\\s*=\\s*\"[^\"]{0,256}position\\s*:\\s*absolute[^\"]{0,256}-\\d{3,}px[^\"]{0,256}\"[^>]{0,256}>",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "html-obfuscation: off-screen positioned element",
|
|
"llm_ingestion_guard": "html-obfuscation:offscreen"
|
|
}
|
|
},
|
|
{
|
|
"id": "html-obfuscation:zero-font",
|
|
"label": "html-obfuscation: zero font-size element",
|
|
"pattern": "<[^>]{1,256}style\\s*=\\s*\"[^\"]{0,256}font-size\\s*:\\s*0[^\"]{0,256}\"[^>]{0,256}>",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "html-obfuscation: zero font-size element",
|
|
"llm_ingestion_guard": "html-obfuscation:zero-font"
|
|
}
|
|
},
|
|
{
|
|
"id": "html-obfuscation:zero-opacity",
|
|
"label": "html-obfuscation: zero opacity element",
|
|
"pattern": "<[^>]{1,256}style\\s*=\\s*\"[^\"]{0,256}opacity\\s*:\\s*0[^\"]{0,256}\"[^>]{0,256}>",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "html-obfuscation: zero opacity element",
|
|
"llm_ingestion_guard": "html-obfuscation:zero-opacity"
|
|
}
|
|
},
|
|
{
|
|
"id": "html-obfuscation:zero-size-overflow",
|
|
"label": "html-obfuscation: zero-size overflow-hidden element",
|
|
"pattern": "<[^>]{1,256}style\\s*=\\s*\"[^\"]{0,256}(?:height|width)\\s*:\\s*0[^\"]{0,256}overflow\\s*:\\s*hidden[^\"]{0,256}\"[^>]{0,256}>",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "html-obfuscation: zero-size overflow-hidden element",
|
|
"llm_ingestion_guard": "html-obfuscation:zero-size-overflow"
|
|
}
|
|
},
|
|
{
|
|
"id": "html-obfuscation:aria-label",
|
|
"label": "html-obfuscation: injection in aria-label attribute",
|
|
"pattern": "aria-label\\s*=\\s*\"[^\"]{0,256}(?:ignore|override|system|instruction|execute|exfiltrate)[^\"]{0,256}\"",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "html-obfuscation: injection in aria-label attribute",
|
|
"llm_ingestion_guard": "html-obfuscation:aria-label"
|
|
}
|
|
},
|
|
{
|
|
"id": "evasion:educational",
|
|
"label": "evasion: educational purpose framing",
|
|
"pattern": "for\\s+educational\\s+purposes?\\s+only",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "evasion: educational purpose framing",
|
|
"llm_ingestion_guard": "evasion:educational"
|
|
}
|
|
},
|
|
{
|
|
"id": "evasion:hypothetical",
|
|
"label": "evasion: hypothetical scenario framing",
|
|
"pattern": "(?:this\\s+is\\s+)?a\\s+hypothetical\\s+scenario",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "evasion: hypothetical scenario framing",
|
|
"llm_ingestion_guard": "evasion:hypothetical"
|
|
}
|
|
},
|
|
{
|
|
"id": "evasion:fictional",
|
|
"label": "evasion: fictional setting framing",
|
|
"pattern": "in\\s+a\\s+fictional\\s+(?:setting|scenario|world)\\s+where",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "evasion: fictional setting framing",
|
|
"llm_ingestion_guard": "evasion:fictional"
|
|
}
|
|
},
|
|
{
|
|
"id": "evasion:red-team",
|
|
"label": "evasion: red-team exercise framing",
|
|
"pattern": "(?:this\\s+is\\s+)?a\\s+red[- ]?team(?:ing)?\\s+(?:exercise|simulation|test)",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "evasion: red-team exercise framing",
|
|
"llm_ingestion_guard": "evasion:red-team"
|
|
}
|
|
},
|
|
{
|
|
"id": "evasion:security-audit-sim",
|
|
"label": "evasion: security audit simulation framing",
|
|
"pattern": "security\\s+audit\\s+simulation",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "evasion: security audit simulation framing",
|
|
"llm_ingestion_guard": "evasion:security-audit-sim"
|
|
}
|
|
},
|
|
{
|
|
"id": "evasion:test-pretense",
|
|
"label": "evasion: test/lab/sandbox pretense",
|
|
"pattern": "pretend\\s+(?:this\\s+is|we\\s+are\\s+in)\\s+a\\s+(?:test|lab|sandbox|simulation)",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "evasion: test/lab/sandbox pretense",
|
|
"llm_ingestion_guard": "evasion:test-pretense"
|
|
}
|
|
},
|
|
{
|
|
"id": "evasion:thought-experiment",
|
|
"label": "evasion: thought experiment framing",
|
|
"pattern": "as\\s+a\\s+thought\\s+experiment",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "evasion: thought experiment framing",
|
|
"llm_ingestion_guard": "evasion:thought-experiment"
|
|
}
|
|
},
|
|
{
|
|
"id": "evasion:research-purpose",
|
|
"label": "evasion: research purpose framing",
|
|
"pattern": "for\\s+(?:research|academic|scientific)\\s+purposes",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "evasion: research purpose framing",
|
|
"llm_ingestion_guard": "evasion:research-purpose"
|
|
}
|
|
},
|
|
{
|
|
"id": "evasion:academic-context",
|
|
"label": "evasion: academic context framing",
|
|
"pattern": "in\\s+an?\\s+(?:academic|research|educational)\\s+context",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "evasion: academic context framing",
|
|
"llm_ingestion_guard": "evasion:academic-context"
|
|
}
|
|
},
|
|
{
|
|
"id": "hitl-trap:approve-immediately",
|
|
"label": "hitl-trap: approval urgency — approve immediately",
|
|
"pattern": "(?:approve|accept|confirm)\\s+(?:this\\s+)?immediately",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "hitl-trap: approval urgency — approve immediately",
|
|
"llm_ingestion_guard": "hitl-trap:approve-immediately"
|
|
}
|
|
},
|
|
{
|
|
"id": "hitl-trap:no-need-review",
|
|
"label": "hitl-trap: approval urgency — no need to review",
|
|
"pattern": "no\\s+need\\s+to\\s+review",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "hitl-trap: approval urgency — no need to review",
|
|
"llm_ingestion_guard": "hitl-trap:no-need-review"
|
|
}
|
|
},
|
|
{
|
|
"id": "hitl-trap:just-approve",
|
|
"label": "hitl-trap: approval urgency — just approve",
|
|
"pattern": "(?:just|simply)\\s+(?:approve|accept|confirm)\\s+(?:it|this|everything)",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "hitl-trap: approval urgency — just approve",
|
|
"llm_ingestion_guard": "hitl-trap:just-approve"
|
|
}
|
|
},
|
|
{
|
|
"id": "hitl-trap:suppress-review",
|
|
"label": "hitl-trap: approval urgency — suppress review",
|
|
"pattern": "(?:don'?t|do\\s+not)\\s+(?:bother\\s+)?(?:check|review|verify|inspect|read)(?:ing)?\\s+(?:this|the|it)",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "hitl-trap: approval urgency — suppress review",
|
|
"llm_ingestion_guard": "hitl-trap:suppress-review"
|
|
}
|
|
},
|
|
{
|
|
"id": "hitl-trap:brevity-skip",
|
|
"label": "hitl-trap: summary suppression — for brevity, skipping",
|
|
"pattern": "for\\s+brevity[\\s,]+(?:skipping|omitting|leaving\\s+out)",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "hitl-trap: summary suppression — for brevity, skipping",
|
|
"llm_ingestion_guard": "hitl-trap:brevity-skip"
|
|
}
|
|
},
|
|
{
|
|
"id": "hitl-trap:too-long",
|
|
"label": "hitl-trap: summary suppression — too long to show",
|
|
"pattern": "(?:too\\s+(?:long|verbose|detailed)\\s+to\\s+(?:show|display|list|include))",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "hitl-trap: summary suppression — too long to show",
|
|
"llm_ingestion_guard": "hitl-trap:too-long"
|
|
}
|
|
},
|
|
{
|
|
"id": "hitl-trap:details-omitted",
|
|
"label": "hitl-trap: summary suppression — details omitted",
|
|
"pattern": "(?:details?\\s+(?:omitted|redacted|hidden|suppressed))",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "hitl-trap: summary suppression — details omitted",
|
|
"llm_ingestion_guard": "hitl-trap:details-omitted"
|
|
}
|
|
},
|
|
{
|
|
"id": "hitl-trap:scope-minimization",
|
|
"label": "hitl-trap: scope minimization — minor change + destructive action",
|
|
"pattern": "(?:minor|trivial|small|cosmetic)\\s+(?:change|update|fix|adjustment)\\b[^.]{0,80}(?:delet|remov|drop|truncat|reset|destroy|\\bformat\\b|wipe)",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "hitl-trap: scope minimization — minor change + destructive action",
|
|
"llm_ingestion_guard": "hitl-trap:scope-minimization"
|
|
}
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"id": "medium",
|
|
"source_export": "MEDIUM_PATTERNS",
|
|
"severity": "medium",
|
|
"count": 22,
|
|
"patterns": [
|
|
{
|
|
"id": "leetspeak:ignore-previous",
|
|
"label": "leetspeak: ignore previous (obfuscated)",
|
|
"pattern": "[1!|l][gq9]n[o0]r[e3]\\s+(?:all\\s+)?pr[e3]v[i1!|l][o0]us",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "leetspeak: ignore previous (obfuscated)",
|
|
"llm_ingestion_guard": "leetspeak:ignore-previous"
|
|
}
|
|
},
|
|
{
|
|
"id": "leetspeak:forget-instructions",
|
|
"label": "leetspeak: forget instructions (obfuscated)",
|
|
"pattern": "f[o0]rg[e3]t\\s+y[o0]ur\\s+[i1!|l]nstruct[i1!|l][o0]ns",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "leetspeak: forget instructions (obfuscated)",
|
|
"llm_ingestion_guard": "leetspeak:forget-instructions"
|
|
}
|
|
},
|
|
{
|
|
"id": "leetspeak:disregard",
|
|
"label": "leetspeak: disregard your (obfuscated)",
|
|
"pattern": "d[i1!|l]sr[e3]g[a4@]rd\\s+y[o0]ur",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "leetspeak: disregard your (obfuscated)",
|
|
"llm_ingestion_guard": "leetspeak:disregard"
|
|
}
|
|
},
|
|
{
|
|
"id": "leetspeak:override",
|
|
"label": "leetspeak: override your (obfuscated)",
|
|
"pattern": "[o0]v[e3]rr[i1!|l]d[e3]\\s+y[o0]ur",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "leetspeak: override your (obfuscated)",
|
|
"llm_ingestion_guard": "leetspeak:override"
|
|
}
|
|
},
|
|
{
|
|
"id": "homoglyph:cyrillic-latin-mix",
|
|
"label": "homoglyph: Cyrillic-Latin mixing in adjacent characters",
|
|
"pattern": "[a-zA-Z][\\u0430\\u0435\\u043E\\u0440\\u0441\\u0456\\u0443]|[\\u0430\\u0435\\u043E\\u0440\\u0441\\u0456\\u0443][a-zA-Z]",
|
|
"aliases": {
|
|
"llm_security": "homoglyph: Cyrillic-Latin mixing in adjacent characters",
|
|
"llm_ingestion_guard": "homoglyph:cyrillic-latin-mix"
|
|
}
|
|
},
|
|
{
|
|
"id": "unicode:zero-width-in-word",
|
|
"label": "unicode: zero-width character inside word (keyword splitting)",
|
|
"pattern": "\\w[\\u200B\\u200C\\u200D\\uFEFF]\\w",
|
|
"aliases": {
|
|
"llm_security": "unicode: zero-width character inside word (keyword splitting)",
|
|
"llm_ingestion_guard": "unicode:zero-width-in-word"
|
|
}
|
|
},
|
|
{
|
|
"id": "indirect:instruction-to-ai",
|
|
"label": "indirect: instruction addressed to AI/assistant",
|
|
"pattern": "(?:note|message|instruction)\\s+(?:to|for)\\s+(?:the\\s+)?(?:AI|assistant|model|LLM|Claude)\\b",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "indirect: instruction addressed to AI/assistant",
|
|
"llm_ingestion_guard": "indirect:instruction-to-ai"
|
|
}
|
|
},
|
|
{
|
|
"id": "indirect:direct-address",
|
|
"label": "indirect: direct address to AI/assistant",
|
|
"pattern": "(?:dear|attention)\\s+(?:AI|assistant|model|LLM|Claude)\\b",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "indirect: direct address to AI/assistant",
|
|
"llm_ingestion_guard": "indirect:direct-address"
|
|
}
|
|
},
|
|
{
|
|
"id": "indirect:trigger-based",
|
|
"label": "indirect: trigger-based instruction for AI",
|
|
"pattern": "when\\s+(?:you|the\\s+AI|the\\s+assistant|Claude)\\s+(?:read|see|encounter|process)\\s+this",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "indirect: trigger-based instruction for AI",
|
|
"llm_ingestion_guard": "indirect:trigger-based"
|
|
}
|
|
},
|
|
{
|
|
"id": "multi-lang:french",
|
|
"label": "multi-lang: French \"ignore previous instructions\"",
|
|
"pattern": "ignor(?:ez?|er?)\\s+(?:les?\\s+)?instructions?\\s+pr[e\\u00e9]c[e\\u00e9]dentes?",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "multi-lang: French \"ignore previous instructions\"",
|
|
"llm_ingestion_guard": "multi-lang:french"
|
|
}
|
|
},
|
|
{
|
|
"id": "multi-lang:spanish",
|
|
"label": "multi-lang: Spanish \"ignore previous instructions\"",
|
|
"pattern": "ignor(?:ar?|e)\\s+(?:las?\\s+)?instrucciones?\\s+anteriores?",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "multi-lang: Spanish \"ignore previous instructions\"",
|
|
"llm_ingestion_guard": "multi-lang:spanish"
|
|
}
|
|
},
|
|
{
|
|
"id": "multi-lang:german",
|
|
"label": "multi-lang: German \"ignore previous instructions\"",
|
|
"pattern": "ignorier(?:e|en)?\\s+(?:alle\\s+)?vorherigen?\\s+(?:Anweisungen|Instruktionen)",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "multi-lang: German \"ignore previous instructions\"",
|
|
"llm_ingestion_guard": "multi-lang:german"
|
|
}
|
|
},
|
|
{
|
|
"id": "markdown:link-ref-comment",
|
|
"label": "markdown: suspicious instruction in link-reference comment",
|
|
"pattern": "\\[\\/\\/\\]:\\s*#\\s*\\(.*(?:ignore|override|system|instruction|execute)",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "markdown: suspicious instruction in link-reference comment",
|
|
"llm_ingestion_guard": "markdown:link-ref-comment"
|
|
}
|
|
},
|
|
{
|
|
"id": "data-uri:executable",
|
|
"label": "data-uri: executable content type",
|
|
"pattern": "data:(?:text\\/html|application\\/javascript|text\\/javascript)[;,]",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "data-uri: executable content type",
|
|
"llm_ingestion_guard": "data-uri:executable"
|
|
}
|
|
},
|
|
{
|
|
"id": "markdown:link-anchor-injection",
|
|
"label": "markdown: injection payload in link anchor text",
|
|
"pattern": "\\[[^\\]]*(?:system|ignore|override|exfiltrate|execute)[^\\]]*\\]\\([^)]+\\)",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "markdown: injection payload in link anchor text",
|
|
"llm_ingestion_guard": "markdown:link-anchor-injection"
|
|
}
|
|
},
|
|
{
|
|
"id": "sub-agent:spawn-dangerous",
|
|
"label": "sub-agent: spawn instruction with dangerous capability keywords",
|
|
"pattern": "(?:create|spawn|launch|start|run)\\s+(?:an?\\s+)?(?:new\\s+)?(?:sub-?agent|agent|task|worker)\\s+(?:that|to|which|with)\\s+(?:.*?\\s+)?(?:execute|run|delete|remove|send|post|exfiltrate|access|reads?\\s+(?:.*?\\s+)?(?:secret|credential|key|token|\\.env|\\.ssh))",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "sub-agent: spawn instruction with dangerous capability keywords",
|
|
"llm_ingestion_guard": "sub-agent:spawn-dangerous"
|
|
}
|
|
},
|
|
{
|
|
"id": "sub-agent:delegate-bypass",
|
|
"label": "sub-agent: delegation with safety bypass instruction",
|
|
"pattern": "(?:delegate|dispatch)\\s+(?:to\\s+)?(?:an?\\s+)?(?:new\\s+)?(?:agent|sub-?agent|task)\\s+.*?(?:bypass|override|ignore|without\\s+(?:review|confirmation|approval))",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "sub-agent: delegation with safety bypass instruction",
|
|
"llm_ingestion_guard": "sub-agent:delegate-bypass"
|
|
}
|
|
},
|
|
{
|
|
"id": "nl-indirection:fetch-execute",
|
|
"label": "nl-indirection: fetch URL and execute",
|
|
"pattern": "fetch\\s+(?:this|the|that)\\s+(?:URL|link|endpoint)\\s+and\\s+(?:execute|run|eval)",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "nl-indirection: fetch URL and execute",
|
|
"llm_ingestion_guard": "nl-indirection:fetch-execute"
|
|
}
|
|
},
|
|
{
|
|
"id": "nl-indirection:send-external",
|
|
"label": "nl-indirection: send data to external destination",
|
|
"pattern": "send\\s+(?:this|the|that|all)\\s+(?:data|content|output|result|information|file)\\s+to\\s+(?:https?:\\/\\/|ftp:\\/\\/|\\S+@\\S+)",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "nl-indirection: send data to external destination",
|
|
"llm_ingestion_guard": "nl-indirection:send-external"
|
|
}
|
|
},
|
|
{
|
|
"id": "nl-indirection:read-dotfile",
|
|
"label": "nl-indirection: read sensitive dotfile path",
|
|
"pattern": "read\\s+(?:the\\s+)?(?:contents?\\s+of\\s+)?(?:~\\/)?\\.(?:ssh|aws|gnupg|env)\\b",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "nl-indirection: read sensitive dotfile path",
|
|
"llm_ingestion_guard": "nl-indirection:read-dotfile"
|
|
}
|
|
},
|
|
{
|
|
"id": "nl-indirection:extract-exfiltrate",
|
|
"label": "nl-indirection: extract credentials and exfiltrate",
|
|
"pattern": "(?:extract|copy|dump|exfiltrate)\\s+(?:all\\s+)?(?:the\\s+)?(?:credentials?|secrets?|tokens?|keys?|passwords?)\\s+(?:and|then)\\s+(?:send|post|upload|transmit)",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "nl-indirection: extract credentials and exfiltrate",
|
|
"llm_ingestion_guard": "nl-indirection:extract-exfiltrate"
|
|
}
|
|
},
|
|
{
|
|
"id": "nl-indirection:download-execute",
|
|
"label": "nl-indirection: download and execute",
|
|
"pattern": "(?:download|fetch|retrieve)\\s+(?:from\\s+)?\\S+\\s+(?:and|then)\\s+(?:execute|run|eval|pipe\\s+to\\s+(?:bash|sh|node|python))",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "nl-indirection: download and execute",
|
|
"llm_ingestion_guard": "nl-indirection:download-execute"
|
|
}
|
|
}
|
|
]
|
|
},
|
|
{
|
|
"id": "hybrid",
|
|
"source_export": "HYBRID_PATTERNS",
|
|
"severity": "high",
|
|
"severity_note": "Resolved 2026-08-09. This family has no severity field of its own in the source: the engine assigns it by pushing HYBRID_PATTERNS matches straight into the `high` bucket, at scanners/lib/injection-patterns.mjs:274-281 under the comment 'Hybrid patterns are HIGH severity (v5.0 S6)'. Note that this family therefore shares a severity with the `high` family without sharing an array - a consumer that reconstructs the engine must keep the arrays separate even though both produce `high`.",
|
|
"severity_provenance": {
|
|
"source_file": "scanners/lib/injection-patterns.mjs",
|
|
"source_lines": "274-281",
|
|
"source_commit": "b0de0ca6d86ce697f39669d177c2c2654c280128",
|
|
"verified": "directly, by reading the module at the pinned commit",
|
|
"not_from": "scanners/lib/severity.mjs, which was the first file asked about and which contains no injection-family severity at all - grep for CRITICAL_PATTERNS, HIGH_PATTERNS, MEDIUM_PATTERNS or HYBRID_PATTERNS there returns nothing at the pinned commit. That part stands and was re-measured 2026-08-10.",
|
|
"retracted_2026-08-10": "This field also said: 'The Python guard's port carries the correct value but cites that file, so a consumer following its citation would find no answer.' RETRACTED - it is false, and it was never measured here. It restated an assertion received from llm-security (coord message 20260809T201048Z) as if it were a commons finding. Measured against the guard's own tree: severity.mjs has never appeared in src/llm_ingestion_guard/injection_lexicon.json at any point in that file's history (git log -S returns no commits), and at 0bf0729 - the commit conformance/manifest.json pins - the only tree-wide occurrence is docs/PLAN.md:114, which correctly attributes the report module to output.mjs + severity.mjs and says nothing about injection-family severity. The guard's only source statement for the lexicon is the note field at injection_lexicon.json:3, and it cites injection-patterns.mjs - the right file. Kept rather than deleted because this repository's stated reason for recording the original claim was that a wrong citation to a right value is the harder defect to notice, and that turned out to describe this record itself."
|
|
},
|
|
"count": 8,
|
|
"patterns": [
|
|
{
|
|
"id": "hybrid-p2sql:inject-sql",
|
|
"label": "hybrid-p2sql: injection + SQL keywords (prompt-to-SQL attack)",
|
|
"pattern": "(?:ignore|override|disregard|forget)[^.]{0,60}(?:SELECT\\s+\\*|DROP\\s+TABLE|UNION\\s+SELECT|DELETE\\s+FROM|INSERT\\s+INTO|UPDATE\\s+\\w+\\s+SET)(?:\\b|(?=\\s|$))",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "hybrid-p2sql: injection + SQL keywords (prompt-to-SQL attack)",
|
|
"llm_ingestion_guard": "hybrid-p2sql:inject-sql"
|
|
}
|
|
},
|
|
{
|
|
"id": "hybrid-p2sql:sql-inject",
|
|
"label": "hybrid-p2sql: SQL operation + injection override keywords",
|
|
"pattern": "(?:SELECT\\s+\\*|DROP\\s+TABLE|UNION\\s+SELECT|DELETE\\s+FROM)\\s[^;]{0,80}(?:ignore|override|disregard|bypass)",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "hybrid-p2sql: SQL operation + injection override keywords",
|
|
"llm_ingestion_guard": "hybrid-p2sql:sql-inject"
|
|
}
|
|
},
|
|
{
|
|
"id": "hybrid-recursive:inject-output",
|
|
"label": "hybrid-recursive: instruction to inject into model output",
|
|
"pattern": "(?:inject|insert|embed|include)\\s+(?:this|the\\s+following)\\s+(?:into|in)\\s+(?:your|the)\\s+(?:output|response|reply|message|prompt|context)",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "hybrid-recursive: instruction to inject into model output",
|
|
"llm_ingestion_guard": "hybrid-recursive:inject-output"
|
|
}
|
|
},
|
|
{
|
|
"id": "hybrid-recursive:conditional-response",
|
|
"label": "hybrid-recursive: conditional response injection (recursive payload)",
|
|
"pattern": "(?:when|if)\\s+(?:the\\s+)?(?:user|human|operator)\\s+(?:asks?|requests?|queries)[^.]{0,60}(?:respond\\s+with|output|reply\\s+with|include)\\s+(?:this|the\\s+following)",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "hybrid-recursive: conditional response injection (recursive payload)",
|
|
"llm_ingestion_guard": "hybrid-recursive:conditional-response"
|
|
}
|
|
},
|
|
{
|
|
"id": "hybrid-xss:script-tag",
|
|
"label": "hybrid-xss: <script> tag in content (agent context XSS)",
|
|
"pattern": "<script\\b[^><]*>",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "hybrid-xss: <script> tag in content (agent context XSS)",
|
|
"llm_ingestion_guard": "hybrid-xss:script-tag"
|
|
}
|
|
},
|
|
{
|
|
"id": "hybrid-xss:javascript-uri",
|
|
"label": "hybrid-xss: javascript: URI scheme (agent context XSS)",
|
|
"pattern": "javascript\\s*:",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "hybrid-xss: javascript: URI scheme (agent context XSS)",
|
|
"llm_ingestion_guard": "hybrid-xss:javascript-uri"
|
|
}
|
|
},
|
|
{
|
|
"id": "hybrid-xss:event-handler",
|
|
"label": "hybrid-xss: inline event handler attribute (agent context XSS)",
|
|
"pattern": "\\bon(?:error|load|click|mouseover|focus|blur)\\s*=",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "hybrid-xss: inline event handler attribute (agent context XSS)",
|
|
"llm_ingestion_guard": "hybrid-xss:event-handler"
|
|
}
|
|
},
|
|
{
|
|
"id": "hybrid-xss:iframe-src",
|
|
"label": "hybrid-xss: iframe with executable src (agent context XSS)",
|
|
"pattern": "<iframe\\b[^><]*src\\s*=\\s*[\"'][^\"']*(?:javascript:|data:text\\/html)",
|
|
"flags": "i",
|
|
"aliases": {
|
|
"llm_security": "hybrid-xss: iframe with executable src (agent context XSS)",
|
|
"llm_ingestion_guard": "hybrid-xss:iframe-src"
|
|
}
|
|
}
|
|
]
|
|
}
|
|
],
|
|
"totals": {
|
|
"families": 4,
|
|
"patterns": 83
|
|
}
|
|
}
|