feat(signatures): the staleness we disclosed is closed by reading the module, not the message
secret-egress.json 0.2.0 -> 0.3.0. `OpenAI Legacy API Key` enters at order 17, second to last; JWT stays last because ordering.last_entry_is_load_bearing says it must. 18 -> 19. The regex was in the coord message that reported it. That is the path evidence_limits explicitly ruled out, so it was read out of the module text at a pinned public commit instead: refs/heads/main = 47905da, and 088e458 (which carries the entry) confirmed an ancestor with `git merge-base --is-ancestor` rather than accepted from their log. The entry is the smaller half. All 19 positions were compared against the module - name, source, flags, order - with 0 divergences, so positions 0-16 are no longer resting on a 2026-08-09 transcription whose module fidelity stood recorded as llm-security's assertion. It is reproduced now, and both the fidelity bullet and the staleness bullet retire. manifest.json 0.3.2: the blocker prose promised its note would stand until this landed. Item (2) is marked closed and the count moves 18 -> 19. The blocker itself does NOT close - 19 against the guard's 25 at different cut points is a table reconciliation nobody has performed, and one closed hole is not that. Verified: JSON well-formed, orders contiguous 0..18, count matches array length, new pattern compiles in Node bare, Node `u` and Python `re`, and does not match sk-ant-/sk-proj- shapes. Charter clean. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JLEZ4XCSnSrQUFA8SzkQB4
This commit is contained in:
parent
4187715ab7
commit
d467324380
2 changed files with 20 additions and 14 deletions
|
|
@ -1,10 +1,10 @@
|
|||
{
|
||||
"version": "0.2.0",
|
||||
"version": "0.3.0",
|
||||
"id": "secret-egress",
|
||||
"description": "Credential and token shapes that must never leave a machine: the fixed pattern table a pre-write guard matches against content before it is persisted. Detection data only - what to DO when one matches (block, warn, redact) is the consumer's policy and is not described here.",
|
||||
"owasp": "LLM02",
|
||||
"match_semantics": "first match wins; patterns are evaluated in ascending `order`",
|
||||
"$comment": "Extracted without behaviour change from llm-security/hooks/scripts/pre-edit-secrets.mjs (`SECRET_PATTERNS`), delivered as operator dump 2/2 through the local coord mailbox on 2026-08-09. NOTE THE SOURCE FILE: the dump states explicitly that this is the engine-consumed hook table and NOT knowledge/secrets-patterns.md, which is a separate PCRE-flavoured agent-consumed variant that stays where it is. This repository's own extraction plan originally named the wrong one of the two; the file recorded here is the one that was actually delivered. Only the 18 fixed entries are data - the dump states that entries 19 and beyond are policy-injected custom patterns at runtime and are not part of the base table.",
|
||||
"$comment": "Extracted without behaviour change from llm-security/hooks/scripts/pre-edit-secrets.mjs (`SECRET_PATTERNS`). NOTE THE SOURCE FILE: this is the engine-consumed hook table and NOT knowledge/secrets-patterns.md, which is a separate PCRE-flavoured agent-consumed variant that stays where it is. This repository's own extraction plan originally named the wrong one of the two; the file recorded here is the one that was actually delivered. Only the 19 fixed entries are data - entries 20 and beyond are policy-injected custom patterns at runtime and are not part of the base table. Version 0.3.0 re-extracted the table from a pinned public commit rather than from a transcription: positions 0-16 were confirmed byte-identical to what the 0.1.0 dump had already produced, and order 17 (`OpenAI Legacy API Key`) was read out of the module at that commit. The one-entry staleness disclosed in 0.2.0 is therefore closed by measurement, not by transcribing the regex out of the coord message that reported it.",
|
||||
"provenance": {
|
||||
"source_repo": "llm-security",
|
||||
"source_files": [
|
||||
|
|
@ -13,14 +13,15 @@
|
|||
"source_exports": [
|
||||
"SECRET_PATTERNS"
|
||||
],
|
||||
"source_delivery": "operator dump 2/2, coord message from llm-security, 2026-08-09",
|
||||
"source_commit": "unknown - not supplied with the dump",
|
||||
"verified": "differentially, against the dump",
|
||||
"source_delivery": "0.1.0: operator dump 2/2, coord message from llm-security, 2026-08-09. 0.3.0: re-extracted from the module text at a pinned public commit, obtained with `git show <commit>:hooks/scripts/pre-edit-secrets.mjs` against the public remote.",
|
||||
"source_commit": "47905dacae8bd5613c8ed76c088cb3de93f40091",
|
||||
"source_commit_note": "`refs/heads/main` on the public remote when 0.3.0 was extracted. The commit that introduced order 17 is `088e458`, confirmed here to be an ancestor of the pinned head with `git merge-base --is-ancestor`, rather than accepted from the reporting message.",
|
||||
"verified": "positionally against the module at the pinned commit: name, regex source, flags and order compared for all 19 entries, 0 divergences",
|
||||
"evidence_limits": [
|
||||
"The dump is a transcription of the source module, not the module file itself. The checks recorded for this file prove that this JSON agrees with the DUMP; dump-to-module fidelity is llm-security's assertion, not a result reproduced here.",
|
||||
"No severity, and no per-entry disposition, was supplied. The source table carries a name and a pattern and nothing else, so neither is invented here.",
|
||||
"The runtime-injected custom patterns (entries 19+) are policy, not data, and are out of scope. A consumer that matches only this table matches LESS than the seed hook does when a policy is loaded.",
|
||||
"STALE AGAINST ITS SOURCE, disclosed 2026-08-11 in version 0.2.0. llm-security reports having taken the source `SECRET_PATTERNS` from 18 to 19 fixed entries by adding `OpenAI Legacy API Key`. That is their report, NOT reproduced here: the commit carrying it is not on their public remote, which stood at `b1ba1fb` when this was checked. Measured here against the 18 patterns below: no legacy `sk-…T3BlbkFJ…` key shape matches any of them. So a consumer vendoring this file today under-matches the seed hook by one entry, on a live credential shape, and that is a false negative rather than a difference of opinion. It will be closed by RE-EXTRACTION from a pinned public commit — never by authoring the entry here from a coord message, which is what the behaviour-preservation rule in CLAUDE.md forbids."
|
||||
"The runtime-injected custom patterns (entries 20+) are policy, not data, and are out of scope. A consumer that matches only this table matches LESS than the seed hook does when a policy is loaded.",
|
||||
"Fidelity is asserted against ONE commit. The pinned commit above is where this table was read; it says nothing about any later state of the source module. A consumer that needs to know whether it has drifted must re-measure against the remote, not re-read this field.",
|
||||
"The comparison covers what the table declares - name, pattern source, flags, order. It does not cover the surrounding module: how the guard reads content, what it does on a match, or which paths it exempts are engine behaviour and are deliberately absent from this file."
|
||||
]
|
||||
},
|
||||
"ordering": {
|
||||
|
|
@ -30,7 +31,7 @@
|
|||
},
|
||||
"dialect": {
|
||||
"name": "ecmascript",
|
||||
"$comment": "Patterns are ECMAScript regular-expression source text exactly as the source literals spell it. Flags are declared per pattern; an entry with no `flags` key carries no flags. All 18 compile in Node with their declared flags, in Node with `u` added, and in Python `re` with the equivalent re.I.",
|
||||
"$comment": "Patterns are ECMAScript regular-expression source text exactly as the source literals spell it. Flags are declared per pattern; an entry with no `flags` key carries no flags. All 19 compile in Node with their declared flags, in Node with `u` added, and in Python `re` with the equivalent re.I.",
|
||||
"flags": {
|
||||
"i": "case-insensitive"
|
||||
},
|
||||
|
|
@ -41,13 +42,13 @@
|
|||
"character classes"
|
||||
],
|
||||
"translation_notes": [
|
||||
"Python (`re`): compile with re.I where flags contain `i`. No rewriting needed; verified by compiling all 18.",
|
||||
"Python (`re`): compile with re.I where flags contain `i`. No rewriting needed; verified by compiling all 19.",
|
||||
"Two patterns contain `\\/` - the redundant escape a JavaScript regex LITERAL requires and that `RegExp.prototype.source` preserves ('Slack/Discord Webhook URL' and 'Database connection string'). Kept byte-identical because Node bare, Node under `u` and Python `re` all accept it. Engines that reject unknown escapes (Go `regexp`, RE2) MUST report these two as unsupported rather than skip them silently.",
|
||||
"The 'Generic credential assignment' and 'Authorization header with token' entries are shape matches, not proofs of a live credential. A consumer treating every match as a confirmed leak will produce false positives; that trade-off belongs to the consumer's policy, not to this table."
|
||||
]
|
||||
},
|
||||
"normalisations": [],
|
||||
"normalisations_note": "Empty by result, not by omission: all 18 patterns are byte-identical to the source, verified below. No escaping change was needed.",
|
||||
"normalisations_note": "Empty by result, not by omission: all 19 patterns are byte-identical to the source module at the pinned commit, compared positionally. No escaping change was needed.",
|
||||
"patterns": [
|
||||
{
|
||||
"order": 0,
|
||||
|
|
@ -142,9 +143,14 @@
|
|||
},
|
||||
{
|
||||
"order": 17,
|
||||
"name": "OpenAI Legacy API Key",
|
||||
"pattern": "\\bsk-[A-Za-z0-9]{20}T3BlbkFJ[A-Za-z0-9]{20}\\b"
|
||||
},
|
||||
{
|
||||
"order": 18,
|
||||
"name": "JWT (three-part token)",
|
||||
"pattern": "\\beyJ[A-Za-z0-9_-]{10,}\\.[A-Za-z0-9_-]{10,}\\.[A-Za-z0-9_-]{10,}\\b"
|
||||
}
|
||||
],
|
||||
"count": 18
|
||||
"count": 19
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue