Squashed 'scanners/commons/' changes from 0ffee85..532d70d
532d70d release(0.2.0): the contract gained a MUST, so the tag has to move 946f51d fix(active-content,conformance): cite line numbers per commit — they do not resolve at the pin bdcb1f1 feat(conformance): ship the six active-content cases; the id space already existed 807c0d4 feat(spec): add not-applicable, so a single-runtime table stops reading as 7 defects a1578e6 fix(conformance): record the guard's internal-surface position on _LEX_PAYLOADS 4d351d2 fix(mapping): state that three of four OWASP maps have no production consumer f082a91 fix(lexicon,docs): retract the claim that the guard's port cites severity.mjs git-subtree-dir: scanners/commons git-subtree-split: 532d70d5ed2f9b23a8efad760ef490356cf52ada
This commit is contained in:
parent
a640f43d73
commit
9b95fc607e
20 changed files with 533 additions and 37 deletions
138
CHANGELOG.md
138
CHANGELOG.md
|
|
@ -9,6 +9,139 @@ Versioning note: the repository tag versions **the contract** (file set, key nam
|
|||
case ids, disposition semantics). Each JSON file additionally carries its own
|
||||
`"version"` field, bumped when that file changes.
|
||||
|
||||
## [Unreleased]
|
||||
|
||||
## [0.2.0] — 2026-08-11
|
||||
|
||||
The contract gained a normative MUST, which is why this is a release rather than a
|
||||
metadata commit: **a runtime that conformed to `v0.1.0` does not conform to this one until
|
||||
it declares the set of commons data files it implements.** In 0.x that is a minor bump by
|
||||
the rules, but it is breaking in substance, and a consumer reading only the version number
|
||||
should learn that here rather than from a failing suite.
|
||||
|
||||
Everything below this heading was previously listed as unreleased.
|
||||
|
||||
### Retracted
|
||||
|
||||
- **The claim that the Python guard's port cites `severity.mjs` for hybrid severity.** It is
|
||||
false. It was carried in three places — `lexicon/injection-lexicon.json`
|
||||
(`families[hybrid].severity_provenance.not_from`), `docs/lexicon-port-divergence.md`
|
||||
(§ *Severity: the 8 hybrid patterns*), and the `[0.1.0]` entry below — and it was never
|
||||
measured here. It restated an assertion received from `llm-security` (coord message
|
||||
`20260809T201048Z`) as a commons finding.
|
||||
|
||||
Measured against the guard's own tree, which `llm-ingestion-pipeline-security` asked for
|
||||
twice before this was checked: `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`,
|
||||
correctly attributing the *report* module to `output.mjs` + `severity.mjs`. The guard's
|
||||
only source statement for the lexicon is the `note` at `injection_lexicon.json:3`, and it
|
||||
names `injection-patterns.mjs`.
|
||||
|
||||
**No detection data moves.** `families[hybrid].severity` is still `high`, still sourced to
|
||||
`injection-patterns.mjs:274-281`, re-verified at `b0de0ca`; `severity.mjs` still contains
|
||||
zero occurrences of `CRITICAL_PATTERNS`, `HIGH_PATTERNS`, `MEDIUM_PATTERNS` and
|
||||
`HYBRID_PATTERNS`, re-measured the same day. Only the sentence about the *other* repository
|
||||
falls.
|
||||
|
||||
The retraction is marked in place rather than edited away, and it is worth naming why this
|
||||
one survived review: the claim arrived bundled with a correct measurement of the same
|
||||
question, from a repository that had done its half properly. The correct half carried the
|
||||
incorrect half past the check — which is precisely the defect
|
||||
`severity_provenance.not_from` was written to warn about, one level up.
|
||||
|
||||
### Added
|
||||
|
||||
- **`not-applicable`, a third conformance verdict** (`spec/conformance-corpus.md` §1.1). A
|
||||
runtime now declares the set of commons data files it implements; a case whose `scope`
|
||||
names a file outside that set is `not-applicable` rather than failed. §1 alone would have
|
||||
reported an architectural difference as a defect — one seeding runtime has no
|
||||
active-content table and never will, and 7 permanent failures say nothing a reader can use.
|
||||
The verdict is fenced so it cannot become an exit: it attaches to a **table**, never to a
|
||||
case (per-case opt-out is the silent skip §1 forbids), a declared set MUST NOT be narrowed
|
||||
to convert failures into `not-applicable`, and such cases MUST still be enumerated rather
|
||||
than dropped from the denominator. §8 now states the consequence: a pass count is
|
||||
unreadable without the declared set beside it.
|
||||
|
||||
- **Six active-content conformance cases** — `active__markdown-image`, `active__markdown-link`,
|
||||
`active__reference-link`, `active__autolink`, `active__raw-html`, `active__data-uri`. The
|
||||
corpus goes 83 → 89, and `scope_covered` gains `signatures/active-content.json`.
|
||||
|
||||
Generated from measurement, not written: payload strings were extracted from the seed
|
||||
runtime's `coverage.py` by AST — evaluating each `_scan_case` argument in that module's own
|
||||
namespace rather than retyping detection data — then run through its public output gate.
|
||||
The fixtures were then re-read from disk by a separate checker that re-computed every
|
||||
digest, re-scanned the bytes and applied `exact-within-scope` independently of the
|
||||
generator, because a generator agreeing with itself proves nothing: 6 cases, 0 failed
|
||||
checks.
|
||||
|
||||
**These six prove less than the 83, and the manifest says so.** Their payloads come from
|
||||
the only runtime implementing the table, so no second implementation's agreement could be
|
||||
measured. They pin one runtime's behaviour as a contract a future implementer can be held
|
||||
to — which is less than cross-runtime agreement and more than nothing.
|
||||
|
||||
- `signatures/active-content.json` **0.1.0 → 0.2.0** — a `pattern_id_space` block. Unlike the
|
||||
lexicon's, nothing was constructed: `label_format` and the `constructs` keys were already
|
||||
extracted verbatim, and their concatenation *is* what the seed runtime emits. The block
|
||||
states an id space the file already had implicitly, and records that it is ratified by
|
||||
**one** runtime rather than two.
|
||||
|
||||
### Changed
|
||||
|
||||
- `lexicon/injection-lexicon.json` **0.5.0 → 0.5.1** — provenance metadata only; no pattern,
|
||||
id, alias, family or severity value changes.
|
||||
|
||||
- `conformance/manifest.json` **0.1.1 → 0.2.0** — the six cases, `scope_covered`,
|
||||
`count_by_scope`, separate provenance and measurement blocks for the active-content half
|
||||
(a different source structure at a different commit; one pin must not stand for two
|
||||
measurements), and `scope_planned.blockers`.
|
||||
|
||||
- **`spec/conformance-corpus.md` §4 no longer claims scoping "asks a question both can
|
||||
answer."** That held only while every case was scoped to the one table both runtimes
|
||||
implement, and stopped being true the moment a case was scoped to a single-runtime table.
|
||||
Scope narrows *what* is compared; it does not make every runtime a valid addressee. The
|
||||
superseded sentence is named in place rather than edited away.
|
||||
|
||||
### Fixed
|
||||
|
||||
- **§4 now states that "belongs to a data file" means published there, never "shares its
|
||||
prefix."** The distinction has a live witness: the seed runtime emits `active:oversize-input`,
|
||||
a self-safety flag about its own scan cap, which carries the `active:` prefix but is no
|
||||
construct in `signatures/active-content.json`. A prefix-matching runtime would pull it into
|
||||
the comparison and fail a case over a finding the corpus makes no claim about. Recorded in
|
||||
that file under `pattern_id_space.not_every_active_label` as well.
|
||||
|
||||
- **§6 now states the derivation's cost.** `case_id` derives from `pattern_id` alone, so a
|
||||
single-finding scope holds at most one case per pattern id — there is nowhere in the name
|
||||
for a second. The seed runtime's matrix drives *two* payloads at `active:markdown-image`;
|
||||
measured, their in-scope finding sets are identical, and the second's only distinguishing
|
||||
signal (`entropy:base64-blob`) falls outside every table this repository publishes. It was
|
||||
dropped rather than given a discriminated id, which would have broken the reverse
|
||||
transform, and it is named in `conformance/manifest.json` under `omitted_payloads` so that
|
||||
6 built from 7 offered reads as a decision rather than a miscount.
|
||||
|
||||
### Measured, not shipped
|
||||
|
||||
- **The remaining four cases are blocked on two distinct unresolved questions**, now recorded
|
||||
under `scope_planned.blockers` instead of the earlier blanket "no runtime has agreed to an
|
||||
id space". That framing was wrong for both:
|
||||
|
||||
- **Carriers (3).** No adoptable id space, and a second problem underneath. The guard emits
|
||||
two *stage-coupled* labels for one carrier — `sanitize:zero-width` on input,
|
||||
`output:zero-width-present` on output, same split for bidi and unicode-tag — while
|
||||
llm-security emits prose titles. A commons id must be invented stage-neutral, which no
|
||||
other id space here required. And since `exact-within-scope` compares a finding *set*, an
|
||||
id aliasing both labels makes the verdict depend on which entry point the runtime was
|
||||
measured through — an entry-point dependence the lexicon cases do not have.
|
||||
- **Secret egress (1).** Not an id-naming question at all. The two runtimes carry
|
||||
**different tables**: 18 entries here against the guard's 25, cut at different
|
||||
granularities (this file's single `GitHub Token` is four ids there, `Private Key PEM
|
||||
Block` three, `Database connection string` four), with membership diverging both ways.
|
||||
`aws-access-key-id` is the one clean 1:1 — which is why exactly one egress case was ever
|
||||
offered. That number was a symptom, not modesty. A shared id space presupposes a table
|
||||
reconciliation nobody has done.
|
||||
|
||||
## [0.1.0] — 2026-08-10
|
||||
|
||||
Initial extraction. Runtime-neutral detection data, the finding contract, and a conformance
|
||||
|
|
@ -224,6 +357,11 @@ mutation, not a weak fixture, and it is recorded as such rather than rounded up.
|
|||
`severity_provenance.not_from` records the miss explicitly: a wrong citation to a right
|
||||
value is the harder defect to catch later.
|
||||
|
||||
> **Correction 2026-08-10 (see Unreleased):** the two sentences about *the guard's* citation
|
||||
> are false and were never measured here. The guard's port cites `injection-patterns.mjs`,
|
||||
> the right file. Everything above about `severity.mjs` containing no injection-family
|
||||
> severity, and about where the value actually lives, stands and has been re-measured.
|
||||
|
||||
`pattern_id_space.not_yet_confirmed` is replaced by `ratification`. Both seeding runtimes
|
||||
agreed on 2026-08-09 — `llm-security` ratified the 0.2.0 proposal as-is and treats an id
|
||||
change as breaking on the same terms, and the guard confirmed the space its own port
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue