The output gate claimed LLM10 self-safety on the grounds that its patterns have
no nested quantifiers. True, and irrelevant: nesting is not what makes these
blow up. A run in front of a REQUIRED literal, reachable from a short anchor, is
enough -- crafted input repeats the anchor and never supplies the literal, so
every start position rescans the tail. Quadratic, not exponential, and the
max_scan_chars cap does not help: it bounds the input, and quadratic work on a
bounded input is still hours.
Measured, not argued. `<a:` x 100_000 took 23.4s in AUTOLINK_RE alone; the
composed gate on that payload took 458.7s, extrapolating to ~5.7 hours at the
1_000_000-char input the gate itself accepts. Size-matched ordinary prose runs
0.31s, so the separation is 18x-660x -- unlike the blob in the neighbouring
test, which is the *faster* side of prose and never exercised backtracking.
Two fixes, chosen per pattern rather than uniformly:
- active_content + lexicon JSON (15 runs): exclude the character that opens the
pattern's own anchor (`[` for markdown, `<` for tags), so a run cannot reach
past the next start position and the per-start costs telescope. Verified to
cost no recall: long URLs, long alt text, and `<` inside a quoted attribute
all still match. Bounding instead would have been linear too but wrong here --
the content is attacker-controlled, so padding past a bound would be a
one-line bypass of the EchoLeak class this table exists to catch.
- connstr egress (4 runs): bound the password at MAX_CONNSTR_VALUE. The
exclusion fix is unavailable -- the anchor character is `/` and passwords
containing `/` are the common case (measured: they match today). The residual
miss is a credential over 256 chars; a token that long is still caught by
egress:jwt-token.
hybrid-xss:script-tag had neither option: its run is the script BODY, which may
legitimately contain `<`. It now matches the opening tag and drops the
`</script>` requirement. That also closes a fail-open -- `<script>alert(1)`
unclosed was silently missed -- at the cost of flagging prose that merely
mentions `<script>`, now documented.
Found by the composed-gate test staying red after every individual scanner was
already linear: the lexicon's six html-obfuscation patterns were the remaining
813x. A per-scanner test alone would have shipped that.
662 passed (was 642), and faster than before the fix.
PLAN.md's test-plan line read as if both scan paths carried ReDoS coverage.
f74245f measured that the output path's "pathological" payload is the FASTER
side against size-matched ordinary prose (0.93x / 0.96x, order swapped), so it
does not exercise catastrophic backtracking at all.
Leaving the line unscoped would be the same defect c0899e9 just retracted in
LIMITATIONS.md: a committed claim resting on a premise now known to be false.
Names which path carries which duty and says plainly that a crafted payload for
the output regexes does not exist. No new limitation bullet -- 27 stays 27.
Reframe .pdf from a 'known gap'/TODO to a deliberate concession across the
honest-limits and OKF-upload docs. A top-level .pdf drop is already refused as
an unsupported format (inbox_frontend.py raises on the else branch), not
half-scanned; adding a PDF parser + reportlab (solely to author white-on-white
test fixtures) is disproportionate for a dev-scoped showcase, and the OCR /
font-render stego carriers a PDF would smuggle are out of scope regardless.
- README honest-limits: .pdf = concession, not TODO; only the numeric CSV FP
remains a known gap.
- docs/PLAN.md: upload table .pdf row marked 'conceded'; honest-scope paragraph
names .pdf; assertions tightened to 'every accepted format'.
Closes the last format gap before v1.0 freeze (Session G). No code touched;
357 tests green.
Replace the unverified/absolute novelty statement with the defensible
four-part-contract form, verified against a focused adversarial PyPI+GitHub
survey (2026-07-15):
- BRIEF §11: 'assumed, not verified' -> verified survey with sources. Names
aig-guardian (real, query-time; blurs only the minimal-dep-library
differentiator), GuardLLM (nearest neighbour, runtime hardening, no
scan-before-persist / capability isolation / fail-secure), and ipi-scanner
(orphaned placeholder repo, recorded for honesty not as prior art).
- README: differentiator moved from 'library vs hosted/model' to the full
four-part write-time contract.
- PLAN §27-31: drop the unverifiable 'the first' superlative.
Also promotes the v1.0 session plan (PLAN-v1.md) and the cross-model review
(review-2026-07.md) into docs/ on the open/ mirror, referenced by PLAN.md's
re-sequencing addendum.
Ground-truth pass over the named flagship consumer (portfolio-optimiser's
'OKF-upload-inbox') found it does not exist as a seam: both optimiser siblings
are frozen at release, carry their own OKF layer, receive no external bundles,
and take no dependency on this guard. Defer consumer integration (stream 2)
until the guard is mature; mature it here first, Node-port-friendly. Next
concrete build is the in-repo OKF inbox showcase (mode-b import_bundle as a
receive/quarantine gate), spec'd under 'The OKF inbox showcase'.
One realistic content sample carrying many vulnerabilities at once, run through
a mock ingestion pipeline (sanitize -> lexicon+entropy+decode-rescan -> output
gate -> disposition); assert every planted vuln is caught and disposition fails
secure. Doubles as the README worked example. Added to build-order step 11 and
the test strategy; tracked as task #11. Inspiration: llm-security/examples/*.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01K8GmKRCdsPjWYAKWsNgeQS