1
0
Fork 0

feat(active-content): raw HTML graded on carrier, and a tag naming no target is inert

Two changes that had to ship together, because they co-occur.

`active:raw-html-link` (MEDIUM) splits the click-required carriers out of
`active:raw-html`. The same URL was LOW as `[t](url)` and HIGH as
`<a href="url">` — an asymmetry produced by syntax, not by affordance, on a
carrier the markdown path has graded MEDIUM since 0.3.1. The event-handler test
runs first, so `<a onclick=...>` stays HIGH. The url-attribute branch stays HIGH
too: a name outside the active set has unknown rendering, and grading
`<Card src=...>` as a link would be reasoning rather than measurement.

The no-URL narrowing makes `</a>`, `<Frame>`, `<video />` and `<img alt=...>`
without `src` inert — `<base />`'s argument from 0.6.0 applied to the rest of the
name branch. It tests for the URL attribute's PRESENCE, not for a readable value,
so the fail-secure gap `_url_attr_is_external` leaves open is not reopened here.

WHY TOGETHER: the narrowing strips a document's `</a>`/`<Frame>` and what remains
is the `<a href=...>` the split grades down, so each alone leaves the document
blocked by the other's residue.

`active_tag_class` is now the classification point and `is_active_tag` wraps it.
The census patches the former: a boolean could only express a narrowing, never a
regrade, so every carrier candidate would have measured equal to PRODUCTION —
silently, and in the direction that reads as "no change helps".

TWO COSTS, BOTH RECORDED RATHER THAN GLOSSED:

- The split TIGHTENS the trusted tier. One finding becomes two, and >=2 findings
  at MEDIUM+ trip the compound overlay, so a document carrying both an `<img src>`
  and an `<a href>` goes WARN -> quarantine_review on PRESET_TRUSTED_SOURCE. On
  that preset it is the only direction the split can move anything. The census
  now reports a TIGHTENS column on both trust tiers against the previously
  shipped row — "frees N" without "tightens M" is a one-sided number.
- `count` drops on documents containing `</a>`, a published field moving under a
  meaning that did not change.

MEASURED: reference-corpus (389) 54 -> 53 fail_secure, tightens 0/0, and the
census `PRODUCTION` row equals its `C1 + D` candidate row for row. The census
also reproduces 133/3/13/108/25 exactly, so it is calibrated against every
published historical number. The two wiki corpora are NOT yet re-measured; the
tree says so explicitly in the docstring, LIMITATIONS and CHANGELOG rather than
carrying probe numbers as fact.

791 tests (was 759), coverage 129/129, 6/6 documented gaps holding. Version
bumped to 0.7.0 across every surface; no tag is set until the measurement lands.
This commit is contained in:
Kjell Tore Guttormsen 2026-08-12 00:42:44 +02:00
commit fcfaee4589
18 changed files with 544 additions and 99 deletions

View file

@ -264,11 +264,28 @@ items; this is the full list, each with the mechanism.
no test discriminated the two halves. They are now `is_active_tag` and
`is_defangable_tag`; the mutator kept the broader behaviour deliberately, pinned by
`tests/test_neutralize.py::test_mutator_still_defangs_what_the_scanner_now_lets_pass`.
- **Raw-HTML findings count end tags.** `</a>` is active by name on its own, so a
corpus census that counts only opening tags understates what this detector reports
by roughly the ratio of closing to opening active tags (measured at 1.6× on one
corpus). Severity and finding count are unaffected — the class collapses to one
finding — but the `count` field is not a document count.
- **The carrier split TIGHTENS the trusted tier when both carrier classes are present.**
0.7.0 is sold as a loosening of the upload door, and on that door it is one. But
splitting one class into two means a document carrying both an `<img src>` and an
`<a href>` now emits *two* findings at MEDIUM+ where it emitted one, which trips
the compound overlay (`>=2 findings at MEDIUM+ -> escalated one tier`). Such a
document was WARN through 0.6.1 and is `quarantine_review` from 0.7.0 under
`PRESET_TRUSTED_SOURCE`. On the trusted preset nothing was hard-failed to begin
with, so this is the *only* direction the split can move it. Pinned by
`tests/test_wiring.py::test_split_tightens_the_trusted_tier_when_both_carriers_are_present`,
and `docs/rawhtml-census.py` now reports a `TIGHTENS` column against the
previously-shipped row on both trust tiers — "frees N" without "tightens M" is a
one-sided number. **Measured so far: 0 documents tightened on reference-corpus
(389). The two wiki corpora are NOT yet re-measured; until they are, the size of
this cost is unknown, not zero.**
- **Raw-HTML findings no longer count end tags, and that moved a published field.**
Through 0.6.1 `</a>` was active by name on its own, so `count` ran roughly 1.6×
the opening-tag total (measured on one corpus) and a start/end pair counted 2.
0.7.0's no-URL narrowing makes an end tag inert — it names no target — so `count`
is now the opening-tag total. A consumer reading `count` will see it *drop* for
every document carrying `</a>`, on a field whose meaning did not change. The
finding count is unaffected: the class still collapses to one finding per class
per document, and `count` was never a document count.
- **Measured, document by document: a large minority of *benign* documents do not
persist unattended at the upload door.** The bullets above bound single rules on
single URLs. This one bounds the thing a consumer actually feels — how often an