From f5d4e3bd95e5323fe7e55f73f9170ae580c080ad Mon Sep 17 00:00:00 2001 From: Benjamin Admin Date: Tue, 12 May 2026 16:55:24 +0200 Subject: [PATCH] feat(cmp): active script blocking + DSE Interessenabwaegung MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ScriptManager: two blocking mechanisms — injection of CONSENT_SCRIPTS after consent + activation of type="text/plain" data-consent scripts. Standard CMP blocking pattern ready for third-party analytics/marketing. DSE: add Interessenabwaegung (balancing test) for Art. 6(1)(f) DSGVO processing: Hosting and Server-Logfiles sections now document why legitimate interest outweighs data subject rights. Co-Authored-By: Claude Opus 4.6 (1M context) --- marketing-website/app/datenschutz/page.tsx | 28 ++++++-- .../components/layout/ScriptManager.tsx | 71 +++++++++++++------ 2 files changed, 75 insertions(+), 24 deletions(-) diff --git a/marketing-website/app/datenschutz/page.tsx b/marketing-website/app/datenschutz/page.tsx index 4630a1c..78ae644 100644 --- a/marketing-website/app/datenschutz/page.tsx +++ b/marketing-website/app/datenschutz/page.tsx @@ -31,7 +31,17 @@ export default function DatenschutzPage() {

Diese Website wird auf Servern der Hetzner Online GmbH in Deutschland gehostet. Es findet kein Drittlandtransfer fuer das Hosting statt. - Rechtsgrundlage: Art. 6 Abs. 1 lit. f DSGVO (berechtigtes Interesse an zuverlaessigem Betrieb). +

+

+ Rechtsgrundlage: Art. 6 Abs. 1 lit. f DSGVO (berechtigtes Interesse). +

+

+ Interessenabwaegung: Unser berechtigtes Interesse liegt im + zuverlaessigen und sicheren Betrieb der Website. Ohne Hosting-Infrastruktur koennen wir unser Angebot + nicht bereitstellen. Die Verarbeitung beschraenkt sich auf technisch notwendige Verbindungsdaten + (IP-Adresse, Zeitstempel). Entgegenstehende Interessen der Betroffenen ueberwiegen nicht, da die + Daten nur kurzzeitig (7 Tage) gespeichert, nicht mit anderen Datenquellen zusammengefuehrt und + ausschliesslich zur Sicherstellung des Betriebs und der IT-Sicherheit verwendet werden.

@@ -80,9 +90,19 @@ export default function DatenschutzPage() {

5. Server-Logfiles

- Der Hosting-Provider erhebt technisch notwendige Logfiles (IP-Adresse, Browsertyp, Zeitstempel). - Rechtsgrundlage: Art. 6 Abs. 1 lit. f DSGVO (berechtigtes Interesse an der Sicherheit). - Logfiles werden nach 7 Tagen automatisch geloescht. + Der Hosting-Provider erhebt technisch notwendige Logfiles (IP-Adresse, Browsertyp, Zeitstempel, + aufgerufene Seite, HTTP-Statuscode). +

+

+ Rechtsgrundlage: Art. 6 Abs. 1 lit. f DSGVO (berechtigtes Interesse). +

+

+ Interessenabwaegung: Die Erhebung von Server-Logfiles ist + fuer die Erkennung und Abwehr von Cyberangriffen, die Fehlerbehebung und die Gewaehrleistung der + Systemstabilitaet unerlasslich. Die Daten werden automatisiert nach 7 Tagen geloescht und nicht + zur Profilbildung oder Identifizierung einzelner Nutzer verwendet. Eine Zusammenfuehrung mit anderen + Datenquellen findet nicht statt. Das Interesse der Betroffenen am Schutz ihrer Daten wird durch die + kurze Speicherdauer und die rein technische Nutzung angemessen gewahrt.

diff --git a/marketing-website/components/layout/ScriptManager.tsx b/marketing-website/components/layout/ScriptManager.tsx index cc4b723..61e6f18 100644 --- a/marketing-website/components/layout/ScriptManager.tsx +++ b/marketing-website/components/layout/ScriptManager.tsx @@ -1,31 +1,43 @@ 'use client' -import { useEffect, useRef } from 'react' +import { useEffect, useRef, useCallback } from 'react' /** - * ScriptManager — consent-aware script injection. + * ScriptManager — active consent-aware script blocking + injection. * - * Listens to the `consent-change` CustomEvent dispatched by ConsentBanner - * and injects/skips third-party scripts based on accepted categories. + * Two mechanisms: + * 1. INJECTION: Scripts in CONSENT_SCRIPTS are only injected AFTER consent. + * 2. BLOCKING: Existing + * + * Usage for adding new third-party scripts: + * Add to CONSENT_SCRIPTS below. They'll be injected only after consent. */ interface ConsentScript { src: string async?: boolean + id?: string } const CONSENT_SCRIPTS: Record = { analytics: [ - // Example: { src: 'https://www.googletagmanager.com/gtag/js?id=G-XXXXXX', async: true }, + // { src: 'https://www.googletagmanager.com/gtag/js?id=G-XXXXXX', async: true, id: 'gtag' }, + // { src: 'https://plausible.io/js/script.js', async: true, id: 'plausible' }, ], marketing: [ - // Example: { src: 'https://connect.facebook.net/en_US/fbevents.js', async: true }, + // { src: 'https://connect.facebook.net/en_US/fbevents.js', async: true, id: 'fb-pixel' }, + // { src: 'https://snap.licdn.com/li.lms-analytics/insight.min.js', async: true, id: 'li-insight' }, ], functional: [ - // Example: { src: 'https://widget.example.com/chat.js', async: true }, + // { src: 'https://widget.example.com/chat.js', async: true, id: 'chat-widget' }, ], } @@ -46,39 +58,58 @@ function getStoredConsent(): ConsentState | null { export default function ScriptManager() { const injected = useRef(new Set()) - function injectScripts(consent: ConsentState) { - const categories: (keyof typeof CONSENT_SCRIPTS)[] = [] - if (consent.analytics) categories.push('analytics') - if (consent.functional) categories.push('functional') - // marketing would need its own toggle in ConsentState + const applyConsent = useCallback((consent: ConsentState) => { + const accepted = new Set() + accepted.add('essential') // always allowed + if (consent.functional) accepted.add('functional') + if (consent.analytics) accepted.add('analytics') - for (const cat of categories) { + // 1. INJECT: Add scripts from CONSENT_SCRIPTS for accepted categories + for (const cat of accepted) { for (const script of CONSENT_SCRIPTS[cat] ?? []) { if (injected.current.has(script.src)) continue const el = document.createElement('script') el.src = script.src if (script.async) el.async = true + if (script.id) el.id = script.id el.dataset.consent = cat document.head.appendChild(el) injected.current.add(script.src) } } - } + + // 2. ACTIVATE: Unblock