From 9c11b5463cdfce473aed7dec7ce7a5f771d0ed78 Mon Sep 17 00:00:00 2001 From: Benjamin Admin Date: Thu, 21 May 2026 15:08:33 +0200 Subject: [PATCH] =?UTF-8?q?fix(audit):=20P98=20+=20P100=20=E2=80=94=20Cook?= =?UTF-8?q?ie-Tabellen-Whitespace=20+=20Anpassen-Button-Check?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit P98: HTML-Tabellen-Zellen wurden bei VW-Cookie-Richtlinie ohne Whitespace verkettet ('smartSignals2UiDsmartSignals2sUiDsmartSignals2CPs...'). Grund: el.textContent ignoriert Block-Element-Grenzen. Fix: innerText (whitespace- respecting) statt textContent. Cookie-Namen werden jetzt einzeln erkannt — VW-Lauf sollte ~100 Cookies statt 1 finden. P100: Banner-Check fuer 'Anpassen'/'Einstellungen'-Button im Initial-Banner. VW-Pattern: nur 2 Buttons (Nur technisch notwendige / Alle akzeptieren), keine granulare Wahl vor Akzeptanz/Ablehnung. Faktische Manipulation Richtung Pauschal-Akzeptanz. HIGH-Finding nach EDPB 5/2020 §82. Pattern: anpassen/einstellungen/cookie-einstellungen/manage cookies/ preferences/customize. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../services/banner_text_checker.py | 27 +++++++++++++++++++ consent-tester/services/dsi_discovery.py | 13 ++++++--- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/consent-tester/services/banner_text_checker.py b/consent-tester/services/banner_text_checker.py index c6977def..31602f61 100644 --- a/consent-tester/services/banner_text_checker.py +++ b/consent-tester/services/banner_text_checker.py @@ -196,6 +196,33 @@ async def check_banner_text(page) -> dict: legal_ref="EDPB 5/2020 (Consent) + DSK-OH 2024 (Telemedien)", )) + # P100: Granular-Wahl-Pruefung — "Anpassen"/"Einstellungen"-Button + # im Initial-Banner. Wenn er FEHLT (VW-Pattern), ist die granulare + # Cookie-Wahl erst nach Akzeptanz/Ablehnung moeglich — faktische + # Manipulation Richtung "Alle akzeptieren". EDPB 5/2020 §82. + granular_button_texts = [ + "anpassen", "einstellungen", "cookie-einstellungen", + "cookies verwalten", "manage cookies", "customize", + "weitere optionen", "more options", "settings", + "individuell", "detaillierte einstellungen", + "praeferenzen", "preferences", + ] + has_granular_button = any(t in banner_lower for t in granular_button_texts) + if not has_granular_button: + violations.append(Violation( + service="Cookie-Banner", + severity="HIGH", + text="Granulare Cookie-Auswahl im Initial-Banner nicht " + "moeglich (kein 'Anpassen'/'Einstellungen'-Button). " + "Nutzer koennen nur 'Alle akzeptieren' oder 'Nur " + "technisch notwendige' waehlen — Detailwahl pro " + "Kategorie erst nach Akzeptanz/Ablehnung. Das ist " + "faktische Manipulation Richtung Pauschal-Akzeptanz.", + legal_ref="EDPB Guidelines 5/2020 §82 (granular consent), " + "§25 Abs. 1 TDDDG, Art. 4(11) DSGVO (informierte " + "Einwilligung)", + )) + # Check 5: Pre-ticked checkboxes (EuGH Planet49) try: pre_checked = await page.evaluate(""" diff --git a/consent-tester/services/dsi_discovery.py b/consent-tester/services/dsi_discovery.py index 5fc37036..72111701 100644 --- a/consent-tester/services/dsi_discovery.py +++ b/consent-tester/services/dsi_discovery.py @@ -506,14 +506,21 @@ async def discover_dsi_documents( ]; for (const sel of selectors) { const el = document.querySelector(sel); - if (el && el.textContent.trim().length > 200) { - return el.textContent.trim(); + if (el) { + // P98: innerText statt textContent — innerText + // respektiert Whitespace zwischen Block-Elementen. + // textContent verkettet HTML-Tabellen-Zellen ohne + // Spaces (VW-Cookie-Tabelle: ~100 Cookie-Namen + // wurden zu einem Klumpen "smartSignals2UiDsmartSignals2sUiD..."). + const txt = (el.innerText || el.textContent || '').trim(); + if (txt.length > 200) return txt; } } // Fallback: full body minus nav/header/footer const body = document.body.cloneNode(true); body.querySelectorAll('nav, header, footer, script, style, [class*="nav"], [class*="sidebar"]').forEach(e => e.remove()); - return body.textContent?.trim() || ''; + // P98: innerText respektiert Whitespace (s.o.) + return (body.innerText || body.textContent || '').trim(); } """) if text and len(text) > 50: