1
0
Fork 0

feat(active-content,okf): bound the last two detection surfaces

`scan_active_content` called directly and `okf.link_graph` were the two surfaces
still reading attacker-supplied text with no cap — the first reached by an
adapter that wants the active-content classes alone, the second running a
`findall` over every body in a bundle. Both are detection-shaped, so they
truncate and flag rather than raise the way the transform surfaces do: what a
detector shortens is its own coverage, not the caller's content.

Truncation is only honest if it is visible, so neither goes quiet: the scanner
emits `active:oversize-input` (LLM10), and `link_graph` records
`(from_id, body_length)` in `LinkGraphResult.truncated` — the field that lets a
caller tell "no links past here" from "no links read past here".

Reached through `scan_output`, the text is already under that surface's cap and
`max_scan_chars` is now passed down, so the flag is raised once, there.
This commit is contained in:
Kjell Tore Guttormsen 2026-08-10 14:48:54 +02:00
commit b90233481a
6 changed files with 111 additions and 22 deletions

View file

@ -331,3 +331,30 @@ def test_url_defanging_survives_the_redos_fix():
evidence = " ".join(f.evidence or "" for f in report.findings)
assert expected in evidence, raw
assert "http://" not in evidence and "https://" not in evidence, raw
# --- self-safety (OWASP LLM10) ----------------------------------------------
#
# Reached through `scan_output` this detector inherits that surface's cap. Called
# directly — the shape an adapter reaches for when it wants the active-content
# classes alone — it had none. It is detection-shaped, so it truncates and flags
# rather than raising the way the transform surfaces do: what a shortened scan
# costs is coverage of the tail, not the caller's content.
def test_oversize_input_is_capped_and_flagged():
big = "x" * 200 + "\n![alt](https://evil.example/beyond-the-cap)\n"
report = scan_active_content(big, max_scan_chars=50)
oversize = [f for f in report.findings if "oversize" in f.label]
assert len(oversize) == 1
assert oversize[0].owasp == "LLM10"
assert oversize[0].count == len(big)
# Prefix only: the construct past the cap is not reported. This is the cost
# the flag exists to announce, so assert it rather than assume it.
assert not [f for f in report.findings if f.label == "active:markdown-image"]
def test_input_exactly_at_the_cap_is_not_flagged():
# The cap is the largest scanned size, not the smallest truncated one.
report = scan_active_content("x" * 50, max_scan_chars=50)
assert not [f for f in report.findings if "oversize" in f.label]