fix: Banner checks no longer default to PASS when untested
20 checks were defaulting to PASS when no violation was found, even if the scanner couldn't actually test them. Now: - Phase-based checks (tracking/cookies): absence = PASS (correct) - UI checks: only PASS if banner_checks actually ran - If banner not detected: everything except banner_detected = FAIL This prevents false 100% scores when violations exist but the text→code mapping doesn't cover them. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -38,7 +38,10 @@ def map_scan_to_checks(scan_result: dict) -> dict:
|
||||
|
||||
# For checks whose check_key appears in violations → failed
|
||||
# For checks whose check_key appears only in passes → passed
|
||||
# For checks where neither → assume passed (not tested = no finding)
|
||||
# For checks where neither:
|
||||
# - Phase-based checks (tracking/cookies) → PASS (absence = good)
|
||||
# - Banner UI checks → PASS only if banner was detected and
|
||||
# the scanner actually ran the relevant check
|
||||
if is_violation_key:
|
||||
passed = False
|
||||
matched_text = violation_codes[key]
|
||||
@@ -46,12 +49,20 @@ def map_scan_to_checks(scan_result: dict) -> dict:
|
||||
passed = True
|
||||
matched_text = pass_codes.get(key, "")
|
||||
else:
|
||||
# Key not found in violations or explicit passes.
|
||||
# If the scan ran (banner detected) → assume passed.
|
||||
# If banner not detected → only banner_detected fails.
|
||||
passed = scan_result.get("banner_detected", False) or key == "banner_detected"
|
||||
banner_detected = scan_result.get("banner_detected", False)
|
||||
if key == "banner_detected":
|
||||
passed = scan_result.get("banner_detected", False)
|
||||
passed = banner_detected
|
||||
elif key in _ABSENCE_IS_PASS:
|
||||
# For these checks, no violation = passed (e.g. no tracking cookies)
|
||||
passed = True
|
||||
elif banner_detected:
|
||||
# Banner was detected but this specific check produced no result.
|
||||
# If the scanner ran banner_checks → assume checked and passed.
|
||||
# If banner_checks is empty → scanner couldn't test → not passed.
|
||||
has_banner_results = bool(scan_result.get("banner_checks", {}).get("violations") is not None)
|
||||
passed = has_banner_results
|
||||
else:
|
||||
passed = False
|
||||
matched_text = ""
|
||||
|
||||
# L2 checks are skipped if their parent L1 failed
|
||||
@@ -213,6 +224,17 @@ def _collect_pass_codes(scan: dict) -> dict[str, str]:
|
||||
return passes
|
||||
|
||||
|
||||
# Checks where absence of a violation means PASS (not "untested")
|
||||
# These are phase-based checks: if no tracking was detected, that's good.
|
||||
_ABSENCE_IS_PASS = {
|
||||
"tracking_before_consent",
|
||||
"cookies_before_consent",
|
||||
"tracking_after_reject",
|
||||
"google_consent_mode_defaults",
|
||||
"banner_language_mismatch",
|
||||
"cookie_wall",
|
||||
}
|
||||
|
||||
_TRACKING_COOKIE_PREFIXES = (
|
||||
"_ga", "_gid", "_fbp", "_fbc", "IDE", "_gcl", "fr", "_pin",
|
||||
"_tt_", "li_sugr", "_hj", "mp_", "ajs_", "_clck", "_clsk",
|
||||
|
||||
Reference in New Issue
Block a user