fix(banner): Footer-Erreichbarkeit prüfen — kein HIGH/MEDIUM bei nicht-blockierendem Banner
User-Bug (BMW): Banner überlagert, aber Footer-Links (Impressum/DSE) bleiben klickbar → fehlender In-Banner-Link ist dann nur Best Practice, kein Verstoß. banner_text_checker misst per Playwright-trial-Klick, ob der Footer-Impressum/ DSE-Link trotz Banner erreichbar ist: erreichbar → LOW/Best-Practice (+ Borlabs- Consent-Historie-Hinweis), blockiert → HIGH/MEDIUM wie bisher. browser_cross_ finding: redundante (nicht footer-bewusste) "Link im Banner fehlt"-Befunde raus. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -279,6 +279,22 @@ def build_cname_cloaking_finding(found: list) -> Violation:
|
||||
)
|
||||
|
||||
|
||||
async def _footer_link_reachable(page, selector: str) -> bool:
|
||||
"""True, wenn ein passender Link (Impressum/DSE im Footer) trotz offenem
|
||||
Banner anklickbar ist — das Banner ueberlagert, blockiert die Seite aber
|
||||
nicht. Playwright-trial-Klick = Aktionsbarkeits-Pruefung OHNE echten Klick;
|
||||
schlaegt fehl, wenn ein blockierendes Overlay den Link abfaengt."""
|
||||
try:
|
||||
loc = page.locator(selector).first
|
||||
if await loc.count() == 0:
|
||||
return False
|
||||
await loc.scroll_into_view_if_needed(timeout=1500)
|
||||
await loc.click(trial=True, timeout=1500)
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
async def check_banner_text(page) -> dict:
|
||||
"""Check cookie banner text for legal issues.
|
||||
|
||||
@@ -378,13 +394,29 @@ async def check_banner_text(page) -> dict:
|
||||
pass
|
||||
|
||||
if not has_impressum:
|
||||
violations.append(Violation(
|
||||
service="Cookie-Banner",
|
||||
severity="HIGH",
|
||||
text="Impressum nicht aus dem Cookie-Banner erreichbar. "
|
||||
"Bei ueberlagerndem Banner muss ein Impressum-Link im Banner vorhanden sein (§5 TMG).",
|
||||
legal_ref="§5 TMG, LG Rostock Az. 3 O 22/19",
|
||||
))
|
||||
# Entscheidend: blockiert das Banner den Footer-Zugriff wirklich?
|
||||
# Wenn der Footer-Impressum-Link trotz offenem Banner anklickbar ist
|
||||
# (Banner ueberlagert, blockiert aber nicht), ist der fehlende
|
||||
# In-Banner-Link nur Best Practice (LOW), kein Verstoss.
|
||||
if await _footer_link_reachable(
|
||||
page, 'a[href*="impressum"], a[href*="imprint"]'):
|
||||
violations.append(Violation(
|
||||
service="Cookie-Banner", severity="LOW",
|
||||
text="Impressum nicht direkt im Cookie-Banner verlinkt — es ist "
|
||||
"jedoch ueber den Footer erreichbar (das Banner blockiert "
|
||||
"die Seite nicht). Best Practice: Impressum- und DSE-Link "
|
||||
"zusaetzlich direkt im Banner anbieten (Borlabs ermoeglicht "
|
||||
"zudem eine sichtbare Einwilligungs-Historie).",
|
||||
legal_ref="Best Practice (§5 TMG via Footer erfuellt)",
|
||||
))
|
||||
else:
|
||||
violations.append(Violation(
|
||||
service="Cookie-Banner", severity="HIGH",
|
||||
text="Impressum nicht erreichbar: kein Link im Banner UND das "
|
||||
"ueberlagernde Banner blockiert den Footer-Zugriff. Dann "
|
||||
"muss ein Impressum-Link im Banner vorhanden sein (§5 TMG).",
|
||||
legal_ref="§5 TMG, LG Rostock Az. 3 O 22/19",
|
||||
))
|
||||
|
||||
# Check 2: DSE link in banner
|
||||
has_dse = any(
|
||||
@@ -394,13 +426,24 @@ async def check_banner_text(page) -> dict:
|
||||
for l in banner_links
|
||||
)
|
||||
if not has_dse:
|
||||
violations.append(Violation(
|
||||
service="Cookie-Banner",
|
||||
severity="MEDIUM",
|
||||
text="Kein Link zur Datenschutzerklaerung im Cookie-Banner. "
|
||||
"Nutzer sollten vor der Einwilligung die DSE einsehen koennen.",
|
||||
legal_ref="Art. 13 DSGVO, ErwGr. 42 DSGVO (informierte Einwilligung)",
|
||||
))
|
||||
if await _footer_link_reachable(
|
||||
page, 'a[href*="datenschutz"], a[href*="privacy"], a[href*="dsgvo"]'):
|
||||
violations.append(Violation(
|
||||
service="Cookie-Banner", severity="LOW",
|
||||
text="Datenschutzerklaerung nicht direkt im Banner verlinkt — sie "
|
||||
"ist jedoch ueber den Footer erreichbar (Banner blockiert "
|
||||
"nicht). Best Practice: DSE-Link direkt im Banner anbieten, "
|
||||
"damit Nutzer vor der Einwilligung informiert entscheiden.",
|
||||
legal_ref="Best Practice (Art. 13 DSGVO via Footer erfuellt)",
|
||||
))
|
||||
else:
|
||||
violations.append(Violation(
|
||||
service="Cookie-Banner", severity="MEDIUM",
|
||||
text="Kein Link zur Datenschutzerklaerung im Cookie-Banner und das "
|
||||
"Banner blockiert den Footer. Nutzer koennen vor der "
|
||||
"Einwilligung die DSE nicht einsehen.",
|
||||
legal_ref="Art. 13 DSGVO, ErwGr. 42 DSGVO (informierte Einwilligung)",
|
||||
))
|
||||
|
||||
# Check 3: Wrong wording — "Zustimmung zur Datenschutzerklärung"
|
||||
wrong_dse_consent_patterns = [
|
||||
|
||||
Reference in New Issue
Block a user