fix(coverage): count a joined/CamelCase class name as coverage

A row recorded as "DirectoryTraversal" or "XSLeaks" normalizes to one
token, which whole-word matching missed, so finish_scan/coverage.json
could report tested work as a gap. _entry_is_about now also matches a
phrasing's words joined into one token as a fallback - still a
whole-token check, never a substring. The branch is additive: it can
only turn a gap into a cover, never the reverse, so it introduces no
false gap.
This commit is contained in:
Abdelrahman Mostafa 2026-10-06 12:24:45 +03:00
parent b4018019fb
commit 2a76196f78
2 changed files with 22 additions and 2 deletions

View file

@ -333,10 +333,19 @@ def _entry_is_about(entry: dict[str, Any], phrasings: list[list[str]]) -> bool:
A phrasing matches when every one of its words appears as a **word** in the
row (``risk_area`` + ``surface``) — not as a substring, so "brute force"
never counts as ``rce`` coverage and "corridor" never as ``idor``.
never counts as ``rce`` coverage and "corridor" never as ``idor``. As a
fallback it also matches the phrasing's words joined into one token, so a
row that recorded a joined / CamelCase name the normalizer kept whole
("DirectoryTraversal" -> "directorytraversal", "XSLeaks" -> "xsleaks")
still counts — still a whole-token check, never a substring.
"""
words = set(_normalized(f"{entry.get('risk_area', '')} {entry.get('surface', '')}").split())
return any(all(_term_in_words(term, words) for term in terms) for terms in phrasings)
for terms in phrasings:
if all(_term_in_words(term, words) for term in terms):
return True
if len(terms) > 1 and _term_in_words("".join(terms), words):
return True
return False
def _entry_authored_by(entry: dict[str, Any], owner_ids: set[str]) -> bool:

View file

@ -104,6 +104,17 @@ def test_substring_match_does_not_hide_a_gap() -> None:
assert skill.replace("_", " ") in classes, (skill, surface, risk, classes)
def test_joined_camelcase_name_counts_as_coverage() -> None:
"""A row recorded as a joined/CamelCase class name ('DirectoryTraversal')
still counts, via the joined-token fallback — no substring matching."""
doc = _document(
entries=[_entry(risk_area="DirectoryTraversal reviewed", surface="/download")],
agent_graph=_graph(metadata={"agent-1": {"skills": ["path_traversal_lfi_rfi"]}}),
)
assert not [g for g in doc["gaps"] if g["kind"] == "unrecorded_risk_class"]
def test_simple_plural_still_counts_as_coverage() -> None:
"""Whole-word matching tolerates a trivial plural, so 'passwords' /
'credentials' still cover weak_password_detection."""