1
0
Fork 0
llm-ingestion-pipeline-secu.../src/llm_ingestion_guard
Kjell Tore Guttormsen cff043787d fix(output): 19 quadratic regex runs on the output path, worst ~5.7h at the cap
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.
2026-07-31 18:31:58 +02:00
..
__init__.py fix(calibration): grade active content on URL shape, not construct type 2026-07-25 15:36:02 +02:00
active_content.py fix(output): 19 quadratic regex runs on the output path, worst ~5.7h at the cap 2026-07-31 18:31:58 +02:00
calibration.py fix(output): 19 quadratic regex runs on the output path, worst ~5.7h at the cap 2026-07-31 18:31:58 +02:00
contract.py fix(security): harden 5 adversarial-review findings (M1/M2/M3 + m4/m6) via TDD 2026-07-05 10:45:05 +02:00
coverage.py fix(calibration): grade active content on URL shape, not construct type 2026-07-25 15:36:02 +02:00
disposition.py fix(calibration): grade active content on URL shape, not construct type 2026-07-25 15:36:02 +02:00
entropy.py refactor(calibration): consolidate tunable thresholds into calibration.py 2026-07-15 09:44:53 +02:00
fence.py feat(fence): randomized unspoofable delimiter + attacker marker-strip (TDD) [skip-docs] 2026-07-04 18:23:35 +02:00
grounding.py feat(grounding): SourceGroundingCheck protocol + pass-through default — the semantic-poisoning seam (TDD) [skip-docs] 2026-07-04 22:44:41 +02:00
injection_lexicon.json fix(output): 19 quadratic regex runs on the output path, worst ~5.7h at the cap 2026-07-31 18:31:58 +02:00
lexicon.py refactor(calibration): consolidate tunable thresholds into calibration.py 2026-07-15 09:44:53 +02:00
neutralize.py feat(guard): active-content detector wired into the output gate (review MAJOR #1) 2026-07-15 06:11:33 +02:00
okf.py feat(okf): scan reserved index.md/log.md in mode-b import, not path-reject (review MAJOR #2) 2026-07-15 06:43:50 +02:00
output.py fix(output): 19 quadratic regex runs on the output path, worst ~5.7h at the cap 2026-07-31 18:31:58 +02:00
report.py feat: scaffold package + report and sanitize modules (TDD) 2026-07-04 09:24:20 +02:00
sanitize.py feat: scaffold package + report and sanitize modules (TDD) 2026-07-04 09:24:20 +02:00