feat(output): decode hex-wrapped credentials at the persist gate
Gap 1 closed. The output gate finds runs of 32+ hex digits (calibration.HEX_WRAP_MIN_LEN), decodes both byte alignments, keeps text that is at least 80% printable (the base64 bar, now one shared helper in entropy) and runs the credential patterns on it. A hex-wrapped AWS key surfaces as decoded:egress:aws-access-key-id with "in hex run @ N" as evidence; the value is never in it. Credentials only: a hex-wrapped injection is not decoded. Measured before and after on the same trees: all 3583 clean documents (three FP populations and the 78 pinned OKF concepts) are identical line for line under both presets (disposition, labels, severity, count, offset); OKF import stays 47/31. They and 569 READMEs hold 2403 hex runs of 32+ digits, none of which decodes to text. scan_output CPU 122.9 s before, 123.4 s after; one solid hex run at the 1M cap 4.2 s -> 5.8 s. 14 new tests (10 red for the right reason first), 6 of 6 mutations red. Matrix 146/146 + 5/5 gaps, suite 1274 passed, ReDoS sweep 169 patterns.
This commit is contained in:
parent
61d7d521e4
commit
0eab4345bf
14 changed files with 265 additions and 40 deletions
|
|
@ -222,8 +222,9 @@ Conceding these is itself a control. The full list is in the guard's `README.md`
|
|||
- **Text-only, extracted-text-only.** No file parsing in the core; what survives
|
||||
text extraction (macros, OLE objects, OCR-embedded instructions, render/font
|
||||
stego, encrypted files) is out of scope beyond the sanitizer's character layer.
|
||||
- **Secret egress: base64-wrapped is caught, hex-wrapped is not** (a documented
|
||||
boundary — decode the transport layer first if you need it scanned).
|
||||
- **Secret egress: base64- and hex-wrapped are caught (hex since 1.10.0); hex with
|
||||
separators, other encodings and nested wraps are not** (a documented boundary —
|
||||
decode the transport layer first if you need it scanned).
|
||||
|
||||
## 9. Where to read more (in the guard repo)
|
||||
|
||||
|
|
|
|||
|
|
@ -623,11 +623,14 @@ items; this is the full list, each with the mechanism.
|
|||
the most common shape in real documentation. The residual: a *clicked* link to an
|
||||
attacker-controlled page can have its `location.hash` read by that page's script,
|
||||
so a fragment payload on a link (not an image) is uncovered.
|
||||
- **Secret egress: base64-wrapped is caught, hex-wrapped is not.** The output gate
|
||||
decodes base64 blobs and re-scans the plaintext, so a base64-*wrapped* secret
|
||||
surfaces as `decoded:egress:*`. `entropy` exposes decoded plaintext for base64
|
||||
only, so hex (and other encodings, or nested wraps) is a deliberate boundary —
|
||||
decode the transport layer first if you need it scanned.
|
||||
- **Secret egress: base64- and hex-wrapped are caught, other wraps are not.** The
|
||||
output gate decodes base64 blobs and, since 1.10.0, hex runs of 32 characters or
|
||||
more (both byte alignments, so one stray digit cannot hide the credential), and
|
||||
re-scans the plaintext, so a wrapped secret surfaces as `decoded:egress:*`. Hex
|
||||
split by separators (`41:4b`, `41 4b`, `\x41`), other encodings and nested wraps
|
||||
are a deliberate boundary — decode the transport layer first if you need it
|
||||
scanned. The hex plaintext goes to the credential patterns only: an injection
|
||||
wrapped in hex is not decoded (one wrapped in base64 is).
|
||||
|
||||
- **Prose that merely mentions `<script>` fires `hybrid-xss:script-tag`.** The
|
||||
pattern matches the opening tag and no longer requires `</script>`, so a
|
||||
|
|
@ -686,7 +689,7 @@ items; this is the full list, each with the mechanism.
|
|||
missed; on one preset it is held for review instead of halted.
|
||||
|
||||
- **The ReDoS sweep has a measured sensitivity floor, not a clean bill of
|
||||
health.** All 168 compiled patterns across all eleven regex-bearing modules are
|
||||
health.** All 169 compiled patterns across all eleven regex-bearing modules are
|
||||
swept arm by arm — payloads synthesised per run from each pattern's own
|
||||
skeleton, so `[`, `[system]` and `[system](` are each probed separately rather
|
||||
than relying on generic units, and each pattern is timed in the call mode the
|
||||
|
|
@ -816,8 +819,11 @@ today (WARN) that the rule would stop. Because those 3,583 hold no external imag
|
|||
569 package READMEs (from installed npm and PyPI packages, deduplicated by sha256)
|
||||
were measured too.
|
||||
|
||||
1. **Hex-wrapped secret egress** — `entropy` decodes base64 only (above). Open.
|
||||
Measured to close without new alarms; next in line.
|
||||
1. **Hex-wrapped secret egress** — **closed in 1.10.0.** The output gate decodes
|
||||
hex runs of 32 characters or more and runs the credential patterns on the text
|
||||
(see "Secret egress" above). Measured as built: every one of the 3,583 documents
|
||||
keeps its disposition, labels and severities. They and the 569 READMEs hold
|
||||
2,403 hex runs this long (checksums and ids), and none decodes to text.
|
||||
2. **Semantic / factual poisoning** — invisible to token analysis (above).
|
||||
**Measured reason:** the cheapest rule, a factual claim of the form "is made of"
|
||||
or "was completed in", gave 0 new alarms on the 3,583 and fired on 3 of the 569
|
||||
|
|
|
|||
43
docs/PLAN.md
43
docs/PLAN.md
|
|
@ -283,6 +283,49 @@ skjøtene raden melder. Matrisesaken står nå som fanget (`lexicon:count`), og
|
|||
ligger fortsatt ubehandlet i innboksen deres. Rad 1 er ikke framme før jobben deres
|
||||
har kjørt lista vår.
|
||||
|
||||
## V.9 Økt 4, hull 1 lukket (01.11.2026, release 1.10.0)
|
||||
|
||||
**Hull 1 (heks-innpakket hemmelighet):** utgangsporten finner løp på minst 32
|
||||
heks-sifre (`calibration.HEX_WRAP_MIN_LEN`), dekoder dem og kjører
|
||||
hemmelighetsmønstrene på klarteksten. Funnet heter `decoded:egress:*`, som for
|
||||
base64, og beviset sier `in hex run @ <offset>`. Matrisesaken `hex-wrapped` står nå
|
||||
som fanget og sjekker at funnet kom fra heks-løpet, ikke fra en base64-dekoding.
|
||||
`tests/test_gap_register.py` har `CLOSED = {"dedup": "count", "hex-wrapped": "hex-wrapped"}`.
|
||||
|
||||
**To avvik fra regelen målt i § V.6**, begge for å stå imot en enkel omgåelse:
|
||||
- Begge byte-justeringene dekodes. Med bare én ville ett løst heks-siffer foran
|
||||
nøkkelen (eller et oddetall-løp) gjemt den.
|
||||
- Kravet er minst 80 % skrivbare tegn, samme krav som base64-klarteksten møter (én
|
||||
delt hjelper i `entropy`), ikke 100 %. Med 100 % ville én søppelbyte foran nøkkelen
|
||||
gjemt den.
|
||||
Begge målt som bygget (under), så § V.6-tallet er ikke gjenbrukt.
|
||||
|
||||
Bare hemmelighetsmønstrene kjøres på klarteksten. En heks-innpakket injeksjon dekodes
|
||||
ikke (en base64-innpakket gjør det). Det står i LIMITATIONS.
|
||||
|
||||
- **TDD:** 14 nye tester i `tests/test_output.py` erstatter den gamle hull-testen. De ti
|
||||
som krever et funn var røde av riktig grunn (ingen funn), de fire negative holdt
|
||||
allerede. Kalibreringstesten (`HEX_WRAP_MIN_LEN` frosset og lest fra
|
||||
`calibration`) ble skrevet før konstanten, men ikke kjørt rød da. Den er rød med
|
||||
33. Netto +14 tester. Mutasjonssjekk: bare justering 0, gulv 34, gulv 30, 100 % skrivbart,
|
||||
steget fjernet og bevis-suffikset fjernet gir hver minst én rød test (6 av 6).
|
||||
Gulv 30 overlevde først, og gulvtesten fikk en sak som dekker den.
|
||||
- **Vaktregelen:** målt før og etter på samme trær. Alle 3 583 dokumenter har samme
|
||||
disposisjon under begge forhåndsinnstillingene og samme etiketter,
|
||||
alvorlighetsgrader, `count` og offset (utdataene er identiske linje for linje).
|
||||
OKF-importen står på 47 fail-secure / 31 warn, uendret. 0 nye alarmer.
|
||||
- **Overflaten:** de 3 583 og de 569 README-ene har **2 403** heks-løp på minst 32
|
||||
tegn i **2 351** dokumenter (5 + 2 311 + 4 + 0 + 31; sjekksummer og id-er). **0**
|
||||
dekodes til tekst, så regelen får aldri noe å skanne. Positiv kontroll: matrisetekstens
|
||||
løp dekodes og fyrer, og et løp med vanlig tekst dekodes uten å fyre.
|
||||
- **Ytelse:** `scan_output` over de 3 583 tok 122,9 s CPU før og 123,4 s etter. Ved
|
||||
taket på 1 000 000 tegn koster ett sammenhengende heks-løp 5,8 s mot 4,2 s før,
|
||||
og 31-tegns løp (under gulvet) 4,5 s mot 4,2 s.
|
||||
- **ReDoS:** sveipet teller 169 mønstre (nytt: `_HEX_RUN_RE`). Fire kjøringer ga 1, 0,
|
||||
0 og 0 tidsflagg (den kjente støyen), og output-tabellen alene 0 av 14.
|
||||
- **Rad 2:** hull 1 og 4 er lukket, og 2, 3, 5 og 7 står med målt begrunnelse. Hull 6
|
||||
er åpent og neste: regelen må kreve mer av klarteksten og måles på nytt (§ V.6).
|
||||
|
||||
## Forrige retning (godkjent av operatøren 28.10.2026, nådd 31.10)
|
||||
|
||||
> «llm-ingestion-guard bygger på nyeste utgave av hver trusselkatalog den viser til,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue