fix(lexicon): two quadratic patterns, reachable through the output gate too
The input-path duty `8deca93` scoped. All 83 lexicon patterns measured arm by
arm; two are quadratic, same shape 0.3.2 fixed -- a run in front of a required
literal that may cross the pattern's own opening anchor. Exponent 1.98 over five
points, so quadratic, not exponential.
markdown:link-anchor-injection `[` 1.91s @8k ~8.3h at the cap
markdown:link-anchor-injection `[system](` 0.006s @8k ~89s at the cap
markdown:link-ref-comment `[//]: # (` 0.22s @8k ~1.0h at the cap
Not input-path-only: `scan_lexicon` runs on the output path, so `scan_output("["
* 100_000)` took 334.7s. 0.3.2's "last quadratic site on the output path" was
false when written -- its sweep drove `[` only through `scan_active_content`.
Fix is anchor exclusion, not bounding (bounding attacker-controlled content is a
one-line bypass). The excluded char is `(`, not the obvious `[`: excluding `[`
drops `[//]: # (see [x] then ignore this)`, which no other pattern catches. The
anchors contain `(` too, so it telescopes at zero measured recall cost.
N is per row deliberately. The URL arm ran 0.9s UNFIXED at N=100_000 -- under the
2.0s bound, so that row could not have failed. Measured at N=300_000 instead,
where crafted (8.10s) and legitimate (0.926s) separate 8.8x.
This commit is contained in:
parent
75ae48277b
commit
b8028ba870
4 changed files with 97 additions and 5 deletions
|
|
@ -422,3 +422,17 @@ def test_crafted_redos_payload_bounded_through_the_public_gate():
|
|||
start = time.monotonic()
|
||||
scan_output(payload)
|
||||
assert time.monotonic() - start < 2.0
|
||||
|
||||
|
||||
def test_gate_is_bounded_on_the_payload_the_first_sweep_missed():
|
||||
# The hole in 0.3.2, found by the input-path sweep that `8deca93` scoped.
|
||||
# `[` appears in the table above only against `scan_active_content`, and the
|
||||
# gate test above uses `<a:` -- so no row ever drove `[` through the LEXICON,
|
||||
# which `scan_output` also runs. It was quadratic there: 8.045s at 16_000
|
||||
# chars, ~8.3 HOURS extrapolated to the cap. The guilty pattern is named by
|
||||
# test_lexicon.py::test_crafted_redos_payload_stays_bounded_in_the_lexicon;
|
||||
# this row exists so the composed gate a caller actually invokes is covered.
|
||||
payload = "[" * _REDOS_N
|
||||
start = time.monotonic()
|
||||
scan_output(payload)
|
||||
assert time.monotonic() - start < 2.0
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue