# Lexicon port divergence — commons vs the Python guard **Status: informative.** Nothing here is normative and nothing here changes a data file. It records a measured disagreement between two ports of one source table, so the decision can be taken where each table is tested. Under this repository's behaviour-preservation invariant, a divergence found here is **reported, not fixed**. Produced 2026-08-09. Every number below came from a command; the scripts live in the session scratchpad rather than in this repository, because executable code here would breach the charter. They are reproducible from the method column. ## What was compared | Side | Artefact | Version | | --- | --- | --- | | commons | [`lexicon/injection-lexicon.json`](../lexicon/injection-lexicon.json) | file `version` as committed | | guard | `llm-ingestion-pipeline-security` `src/llm_ingestion_guard/injection_lexicon.json` | lexicon `version` 1.0, repo v0.3.4, commit `0bf0729` | Both are **ports of the same file**: `llm-security/scanners/lib/injection-patterns.mjs`. The guard's JSON says so in its own `note` field — *"Injection lexicon ported from llm-security injection-patterns.mjs. Single source of truth."* Commons extracted the same table from an operator dump of that module. That is what makes the comparison worth running. These are not two different detectors that happen to overlap; they are two transcriptions of one table, and where they disagree, they disagree about what the same source says. ## Result | Measure | Method | Result | | --- | --- | --- | | Pattern count, both sides | count entries | 83 and 83 | | Label correspondence | match commons `label` to guard `desc`, em-dash normalised to hyphen | **83/83** | | Regex source byte-identical | string compare | **64/83** | | Differing regex text | string compare | 19 | | — of those, provably equivalent | unescape commons' JS-isms (`\/` → `/`, `\uXXXX` → literal) and compare for string identity | **6/6 identical** | | — of those, behaviourally divergent | differential match-set comparison (offsets + matched text), targeted corpus per pattern family | **13**, each with a concrete witness input | | Total input comparisons | count | 401 | | Flags | compare declared flags | **0 differences** | | Severity / family | commons family vs guard `severity` | **8 differences** (all `hybrid`) | 64 identical + 6 escaping-only + 13 divergent = 83. The 6 escaping-only pairs are equivalent by **string identity after unescaping**, not merely by absence of a counter-example: `<\/system>` vs ``, `text\/html` vs `text/html`, `(?:https?:\/\/…)` vs `(?:https?://…)`, `(?:~\/)?` vs `(?:~/)?`, and two `\uXXXX`-vs-literal character classes. Commons writes code points as escapes by convention; the guard's `note` records the same choice for non-ASCII. ## The 13, and which side misses what Divergence is **not one-directional**. Each side has inputs the other's pattern does not match. | Pattern | Direction | Witness input | | --- | --- | --- | | `html-obfuscation: display:none element with content` | commons misses; span differs | attribute run padded past 256 chars | | `html-obfuscation: visibility:hidden element` | span only | `
` | | `html-obfuscation: off-screen positioned element` | span only | same shape, `position:absolute` | | `html-obfuscation: zero font-size element` | span only | same shape, `font-size:0` | | `html-obfuscation: zero opacity element` | span only | same shape, `opacity:0` | | `html-obfuscation: zero-size overflow-hidden element` | span only | same shape, `height:0;overflow:hidden` | | `html-obfuscation: injection in aria-label attribute` | **commons misses** | `x` | | `markdown: suspicious instruction in link-reference comment` | **guard misses** | `[//]: # (a (b) ignore)` | | `markdown: injection payload in link anchor text` | **guard misses** | `[click ignore this](http://e.com/a(b))` | | `sub-agent: spawn instruction with dangerous capability keywords` | **guard misses** | `create an agent that` + 20 filler words + `execute` | | `sub-agent: delegation with safety bypass instruction` | **guard misses** | `delegate to a new agent` + 200 chars + `bypass` | | `hybrid-xss: