llm-security-commons/CHANGELOG.md
Kjell Tore Guttormsen f68f1e19d5 docs(lexicon): split the alias evidence, correct a false absence claim
Advisor review found six gaps the session's own checks did not cover. The one
that mattered was outward-facing: the divergence report told the guard repo
that two sub-agent bounds were undocumented. That was wrong. lexicon.py's
module docstring documents them explicitly under "Bounded token gaps", and
git log -S dates {0,12} to the original port commit f397cd9 and [^><] to the
ReDoS fix cff0437. Every divergence on the guard's side is documented and
traceable. The claim rested on two sed slices of one file; an absence claim
needs a search over the whole repository. Corrected here and by coord.

lexicon/injection-lexicon.json 0.2.0 -> 0.3.0:

- pattern_id_space.alias_evidence records the two aliases separately instead
  of averaging them. llm_ingestion_guard is verified — coverage.py asserts on
  that exact string, so it is demonstrably what a guard finding carries.
  llm_security is not: it is the pattern table's name, the finding producer
  was never supplied, and the known Node finding shape uses title, not label.
- normalisations[].affects now keys on id, with the prose names kept beside it
  as affects_labels. An internal cross-reference on label was a second
  identity space inside the file the id exists to unify.

Detection data unmoved again: labels, patterns, flags and the ids and aliases
added in 7b70f5b are all byte-identical in sequence; 166/166 Node compiles.

Also: the README lexicon row described thematic families the file does not
have (they are severity families; the theme is the id prefix), and the
conformance convertibility table gained its missing second condition — a case
is buildable only if the label it asserts maps to data this repository
publishes. Eleven cases fail that test (entropy, decoded, the sanitize rows,
the OKF scans), so the buildable set is ~94, not 105.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FaYqid3mejFmd9ZHsiHgp3
2026-08-09 21:56:45 +02:00

100 lines
5.5 KiB
Markdown

# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this
project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
Versioning note: the repository tag versions **the contract** (file set, key names,
case ids, disposition semantics). Each JSON file additionally carries its own
`"version"` field, bumped when that file changes.
## [Unreleased]
Initial extraction, in progress. Runtime-neutral detection data and the finding contract,
extracted from the `llm-security` Node implementation and a Python guard without behaviour
change. **Not yet tagged** — see *Not included* below.
### Added
- `schema/finding.schema.json` — the finding contract plus the SARIF output profile.
Normative. The JSONL profile is deliberately left `unspecified`.
- `signatures/active-content.json` — the EchoLeak class (CVE-2025-32711): 17 patterns,
severities, opacity floors and pass order, from the Python guard.
- `lexicon/injection-lexicon.json` — 83 prompt-injection patterns in four families
(21 critical, 32 high, 22 medium, 8 hybrid).
- `codepoints/carriers.json` — six carrier tables: zero-width characters, the Unicode Tags
block, the Supplementary Private Use Areas, BIDI controls, the Cyrillic presence set and
the 28-entry fold-to-Latin homoglyph map.
- `signatures/secret-egress.json` — the 18 fixed credential and token shapes. Array order
is normative.
- `mapping/owasp-map.json` — four taxonomy maps (LLM, ASI, AST, MCP) over one shared
16-prefix key set.
- `calibration/calibration.json` — risk-score tier constants, verdict thresholds, risk-band
cutoffs, posture grade thresholds.
### Verification
Every file above except `calibration.json` was proven rather than transcribed: the data was
rebuilt **from the commons JSON alone** and diffed against the source implementation. Each
file records its own result and its own limits.
`calibration/calibration.json` carries `verified: false`. Its source arrived as a prose
summary rather than as code, so no differential check was possible, and the file names the
checks that were not run instead of attaching a caveat to a pass.
- `docs/lexicon-port-divergence.md` — informative. A differential comparison of the two
ports of `injection-patterns.mjs` (this repository's and the Python guard's): 83/83
patterns correspond, 64 are byte-identical, 6 differ only by escaping and are proven
equivalent, and **13 behave differently**, with a witness input for each and misses on
both sides. The cause is two different ReDoS mitigations of one table. **No data file was
changed** — behaviour preservation holds and the finding is reported to the owning
repositories.
### Changed
- `lexicon/injection-lexicon.json` **0.2.0 → 0.3.0** — the two aliases are no longer presented
as equally backed. `pattern_id_space.alias_evidence` now records each one separately:
`llm_ingestion_guard` is **verified** (the guard's coverage matrix asserts on that exact
string, so it is demonstrably what a guard finding carries), while `llm_security` is
**not** — it is the pattern table's own name, and the finding producer was never supplied,
with the known Node finding shape using `title` rather than `label`. Averaging the two into
one file-level claim would have repeated the defect this repository corrects per-table
elsewhere.
Also: `normalisations[].affects` now keys on `id` with the prose names kept beside it as
`affects_labels`. An internal cross-reference on label was a second identity space inside
the file the id was added to unify.
- `lexicon/injection-lexicon.json` **0.1.0 → 0.2.0** — every pattern gains a commons-owned
`id` and an `aliases` object naming what each seeding runtime calls it, plus a top-level
`pattern_id_space` block explaining the field. This exists because a `conformance/`
fixture has to name a finding and the two runtimes do not name the same pattern the same
way.
The id was **adopted verbatim from the guard's port**, which already carried both names,
rather than invented here. Matching was by `label``desc` with em-dash normalised to
hyphen: 83/83, one-to-one, ids unique.
**No detection data moved.** Labels, patterns and flags are byte-identical in sequence,
no `flags` key was invented (78 before, 78 after), and stripping the three new fields
reproduces the previous committed file byte for byte — 23 566 bytes, identical. All 83
patterns still compile in Node bare and under `u` (166/166) and in Python `re` (83/83).
Neither `llm-security` nor the guard has ratified this id space yet; both were asked by
coord on 2026-08-09, and the file says so rather than implying agreement.
### Not included
- `signatures/malware-signatures.json` — seed data not yet delivered.
- `spec/decode-pipeline.md` — needs the decode implementation. A normative spec inferred
from a data dump would be worse than an absent one.
- `conformance/` — still absent, with half the blocker cleared. 105 of the guard's 134
coverage cases are convertible to static `input.txt`/`expected.json`; the other 29 assert
a runtime's API surface, which this repository does not own. Findings can now be **named**
(see `pattern_id_space` above), but 13 patterns still have no agreed **expected
behaviour** — the two ports genuinely differ on them — so those fixtures cannot be
authored until the owning repositories answer.
These are named in the README as planned rather than linked, so nothing in the repository
points at a file that does not exist.