docs(divergence): the span row had one witness; llm-security measured five more

Their coord message of 2026-08-11T11:01:59Z measured the three <script> forms as
totally ordered by what they match — [^>] strictly contains [^><] strictly contains
the pre-0.7.0 closing-tag form — and named two input classes the guard's narrower
class drops that commons had not recorded: `<script a="<" >x` and `<script<div>`.

Reproduced independently before writing it down, including the strict-superset
property in both directions. Their table came with a claim attached; the claim is
now a measurement here rather than a relayed assertion.

Their argument for [^>] — the narrower class buys an empty false-positive surface,
since anything reaching [^>]-and-not-[^><] already contains a literal <script tag —
is recorded as THEIRS and marked explicitly as not what decided commons' form.
Commons carries [^>] on provenance and would have carried [^><] had the source been
the guard's. Keeping that distinction visible matters more than the regex: if merit
ever becomes the reason a commons pattern moves, behaviour-preservation is gone.

Also records honestly that they asked to hear the guard's reason for [^><] before
commons shipped, and commons shipped first — with why that order is defensible
rather than an oversight. A reason that beats their argument is a reason for the
guard's own port; commons would still track its declared source.

Informative file only. v0.3.0 unaffected.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HuudQLGMyMenuyeZk8fKs5
This commit is contained in:
Kjell Tore Guttormsen 2026-08-11 13:44:28 +02:00
commit d96fbbf791
2 changed files with 47 additions and 12 deletions

View file

@ -11,7 +11,23 @@ case ids, disposition semantics). Each JSON file additionally carries its own
## [Unreleased]
Nothing yet.
### Changed
- `docs/lexicon-port-divergence.md` (informative) — the residual `[^>]` vs `[^><]` row gains a
fuller witness set. `llm-security` measured the three forms as **totally ordered** by what they
match, each a strict superset of the next, and named two input classes the guard's narrower
class drops (`<script a="<" >x`, `<script<div>`) beyond the one commons had recorded.
Reproduced here independently, including the strict-superset property in both directions,
before being written down.
Their argument — that the narrower class buys an empty false-positive surface, since anything
reaching `[^>]`-and-not-`[^><]` already contains a literal `<script` tag — is recorded as
theirs and explicitly **not** what decided commons' form. Commons carries `[^>]` on provenance,
and would have carried `[^><]` had the source been the guard's. Also records that they asked to
hear the guard's reason for `[^><]` before commons shipped and commons shipped first, with why
that order was deliberate.
No data file touched; `v0.3.0` is unaffected.
## [0.3.0] — 2026-08-11

View file

@ -167,17 +167,36 @@ one, so relative to `0.6.0` it can add matches and cannot add a false negative.
would not have been adoptable on the same reasoning.
**What is still open is the one-byte span difference**, and convergence did not touch it. The
guard excludes `<` from its negated class and the other two do not, so:
guard excludes `<` from its negated class and the other two do not. The three forms turn out to
be **totally ordered by what they match** — each a strict superset of the next:
| Witness | commons `0.7.0` / `llm-security` | guard |
| --- | --- | --- |
| `<script>steal()</script>` | match | match |
| `<script src=x.js>` | match | match |
| `<script>alert(1)` | match | match |
| `<script <x>` | **match** | **no match** |
| Witness | commons `0.7.0` / `llm-security` `[^>]` | guard `[^><]` | commons ≤ `0.6.0` |
| --- | --- | --- | --- |
| `<script>steal()</script>` | match | match | match |
| `<script src=x.js>` | match | match | no match |
| `<script>alert(1)` | match | match | no match |
| `<script\n src=x>` | match | match | no match |
| `<script a="<" >x` | **match** | **no match** | no match |
| `<script<div>` | **match** | **no match** | no match |
Measured by compiling all three forms, not reasoned from the character classes. The last row is
the whole residual divergence and neither side has claimed it.
Measured by compiling all three forms, not reasoned from the character classes. The last two rows
are the whole residual divergence.
The witness set and the ordering observation are `llm-security`'s, from their coord message of
2026-08-11T11:01:59Z; both were **reproduced here independently** before being recorded, including
the strict-superset property in both directions. Their reading of it: `[^><]` refuses to cross a
`<`, and since anything that reaches `[^>]`-and-not-`[^><]` already contains a literal `<script`
opening tag, the narrower class buys an empty false-positive surface in exchange for two dropped
input classes — a recall loss with no precision gain to trade for.
**That argument is recorded, not endorsed, and it did not decide commons' form.** Commons carries
`[^>]` because the lexicon's declared provenance is `llm-security`'s table (see above), not
because their regex won on merit — the same reasoning would have taken commons to `[^><]` had the
source been the guard's. `llm-security` asked to hear the guard's reason for `[^><]` — a ReDoS
bound, or a span-extraction consumer needing the run to stop at `<` — before commons shipped, and
commons shipped first. That order is deliberate rather than an oversight: a reason that beats this
argument is a reason for the *guard's own port*, and commons would still track its declared
source. Put to the guard as an open question 2026-08-11; unanswered at time of writing.
## Why they diverge: two different ReDoS mitigations of one table