From 6a25df22fb7910ea7181e159080f090ca804449c Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 22 Oct 2026 13:10:22 +0000 Subject: [PATCH] Tests: legal_datenschutz_anfragen (Abschnitt 11 Weg B, Stand 22.10.2026), Titelliste und Stand-Pin angepasst MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drei neue Tests: tragende Wendungen von Abschnitt 11, keine Stufe-2-Reste und keine offenen Markierungen, Abschnitt 2, 12 und 14 passend zu 11. Der alte Pin "Stand 17.10.2026" prüft nur noch "nicht vor der Reichweitenmessung". Die Titelliste kennt die neuen Überschriften von 11 und 14. pdf_pruefsumme bleibt rot, bis das PDF neu gedruckt ist. Co-Authored-By: Claude Sonnet 5.5 --- test/legalTexts.test.js | 70 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 67 insertions(+), 3 deletions(-) diff --git a/test/legalTexts.test.js b/test/legalTexts.test.js index 49a06df..fecac16 100644 --- a/test/legalTexts.test.js +++ b/test/legalTexts.test.js @@ -558,7 +558,8 @@ test('legal_datenschutz_kundenansicht: Abschnitt 6 nennt Kundenansicht, Sperren, const terms = /^(\d{4})-(\d{2})-(\d{2})-v\d+$/.exec(config.abo.termsVersion) assert.ok(terms) assert.ok(standTag >= `${terms[1]}-${terms[2]}-${terms[3]}`, `Stand ${stand[0]} liegt vor termsVersion ${config.abo.termsVersion}`) - assert.ok(html.includes('

Stand 17.10.2026

'), 'Stand = Tag der Änderung (Abschnitt 4a Reichweitenmessung auf webklar.com)') + // Stand = Tag der letzten Änderung der Erklärung; seit dem 17.10.2026 (Abschnitt 4a Reichweitenmessung auf webklar.com) nicht davor (der genaue Tag ist in legal_datenschutz_anfragen gepinnt) + assert.ok(html.includes(`

${stand[0]}

`) && standTag >= '2026-10-17', `Stand ${stand[0]} liegt vor der Reichweitenmessung (17.10.2026)`) }) // ---------------------------------------------------------------- Abschnitt 6c und Funktionsbestellung (TP2 F12a) @@ -603,8 +604,8 @@ test('legal_datenschutz_termine: Abschnitt 6c steht zwischen 6b und 7 und nennt const titel = [...html.matchAll(/

(\d+)\. ([^<]*)<\/h2>/g)].map((m) => `${m[1]}. ${m[2]}`) assert.deepEqual(titel, [ '1. Verantwortlicher', '2. Geltungsbereich', '3. Hosting, Server-Protokolle und Überwachung der Erreichbarkeit', '4. Cookies und Speicher im Browser', '5. Kontaktaufnahme', '6. Kundenportal', - '7. Abos und Zahlung über Stripe', '8. E-Mail', '9. Buchhaltung und Rechnungen', '10. Domains unserer Kunden', '11. Interessenten', '12. Empfänger im Überblick', '13. Ihre Rechte', - '14. Pflicht zur Bereitstellung, keine automatisierten Entscheidungen', '15. Änderungen', + '7. Abos und Zahlung über Stripe', '8. E-Mail', '9. Buchhaltung und Rechnungen', '10. Domains unserer Kunden', '11. Interessenten: Antworten auf öffentliche Anfragen', '12. Empfänger im Überblick', '13. Ihre Rechte', + '14. Pflicht zur Bereitstellung, keine automatisierten Einzelentscheidungen nach Art. 22 DSGVO', '15. Änderungen', ]) }) @@ -732,3 +733,66 @@ test('legal_datenschutz_4a: eigene Website-Analyse auf webklar.com mit Rolle, Re // Kontaktformular: seit 17.10.2026 auch per Mail an das eigene Postfach assert.ok(ds.includes('und schicken sie zugleich per E-Mail an unser Postfach (Abschnitt 8)')) }) + +// ---------------------------------------------------------------- Abschnitt 11 Interessenten, Weg B (nur der heute laufende Weg: öffentliche Anfragen) + +test('legal_datenschutz_anfragen: Abschnitt 11 beschreibt nur den Anfrage-Weg (KI bewertet, Mitarbeiter sendet von Hand), mit Anker, Quelle, Daten, Rechtsgrundlage, Fristen und Widerspruch', () => { + const html = read(DS) + // Stand-Pin: der Tag der Fassung (das endgültige Datum setzt der Push, dann auch hier) + assert.ok(html.includes('

Stand 22.10.2026

'), 'Stand 22.10.2026') + assert.match(html, /<\/a>\s*

11\. Interessenten: Antworten auf öffentliche Anfragen<\/h2>/) + assert.equal(html.split('id="interessenten"').length - 1, 1, 'der Anker steht genau einmal da') + const s = between(strip(html), '11. Interessenten: Antworten auf öffentliche Anfragen', '12. Empfänger im Überblick') + for (const t of ['Art. 14 DSGVO', 'KI-Assistent', 'Claude von Anthropic', 'von Hand', 'Automatische E-Mails verschicken wir nicht', 'Für E-Mails gilt Abschnitt 8', + // Quellen: Alarm-E-Mails und Websuche; Plattformen mit Bot-Verbot ausgenommen + 'Benachrichtigungs-E-Mails', 'Kleinanzeigen, freelancermap und freelance.de', 'Websuche', 'Frage-Foren', 'Die Suche und den Abruf der Seiten führt Anthropic aus', 'Facebook, Instagram, LinkedIn, Xing, Reddit', + 'nicht nach bestimmten Personen', + // Daten + 'Titel des Beitrags', 'Beitragstext (höchstens 4000 Zeichen)', 'den Link', 'Ort und Postleitzahl', 'die Plattform', 'Punktzahl mit kurzer Begründung', 'E-Mail-Adresse aus dem Beitrag', + 'Ein eigenes Feld für Ihren Namen gibt es nicht', 'Telefonnummern entfernen wir beim Speichern automatisch', 'Faustregel', + // Rechtsgrundlage, Empfänger + 'Art. 6 Abs. 1 lit. f DSGVO', 'Hetzner Online GmbH', 'Anthropic Ireland, Limited', 'als Auftragsverarbeiter', 'USA', 'Standardvertragsklauseln (Art. 46 Abs. 2 lit. c DSGVO)', + // keine automatisierte Einzelentscheidung + 'entscheidet ein Mitarbeiter (Abschnitt 14)', + // Fristen und Reste + 'spätestens 90 Tage nach dem Fund', 'innerhalb von 30 Tagen', 'sperren und kürzen wir sie sofort', 'pseudonymisierte Prüfwerte (Hash-Werte)', 'Ort, Plattform, Punktzahl, Status', + 'jedoch ohne Titel, Text, Link und Kontaktdaten', 'bereinigen die zugehörigen Einträge im Protokoll', 'Abschnitt 5 und, wenn ein Vertrag zustande kommt, die Abschnitte 7 und 9', + // Kopien + 'spätestens nach 90 Tagen', 'Datensicherungen unseres Servers', 'Geschäftsbrief', + // Widerspruch + 'Ihr Widerspruchsrecht gegen diese Verarbeitung (Art. 21 DSGVO)', 'Antworten Sie dazu auf unsere Nachricht oder schreiben Sie an support@webklar.com']) assert.ok(s.includes(t), t) + // der Widerspruch steht im Kasten (box widerspruch) im Abschnitt 11 + const kasten = html.slice(html.indexOf(''), html.indexOf('

12. Empfänger im Überblick

')) + assert.ok(kasten.includes('
')) +}) + +test('legal_datenschutz_anfragen: nichts aus dem Kaltmail-Weg (Stufe 2) und keine Zusage, die der Dienst heute nicht einhält', () => { + const html = read(DS) + const ds = strip(html) + const s = between(ds, '11. Interessenten: Antworten auf öffentliche Anfragen', '12. Empfänger im Überblick') + for (const t of ['Gratis-Vorschau', 'höchstens zwei E-Mails', 'höchstens drei Betriebe', 'schreibt und versendet', 'Antworten sortiert', 'Place-ID', 'Impressum', 'OpenStreetMap', 'Overpass', 'Resend', 'Cloudflare', + 'Abweichend von Abschnitt 8', 'Abmelde-Link', 'bei Heilberufen', 'Geschäftsdaten in geringem Umfang', 'sechs Monate nach der letzten Nachricht', 'zwölf Monaten', 'Branchenverzeichnisse', 'nur für diese Antwort', + 'Postfach bei der Hetzner', 'In allen Fällen bleiben nur']) assert.ok(!s.includes(t), `noch da (Stufe 2 oder zu viel versprochen): ${t}`) + // Resend, Cloudflare und Overpass stehen nirgends in der Erklärung + for (const t of ['Resend', 'Cloudflare', 'Overpass']) assert.ok(!ds.includes(t), `${t} gehört nicht in die Weg-B-Fassung`) + // keine sichtbaren Platzhalter für offene Punkte (die stehen in der Notiz text-offen.md, nicht im Text) + assert.ok(!/\[OFFEN|\[Prüfen|TODO|XXX|\{[a-z]+\}/i.test(strip(html.replace(//g, ''))), 'keine offenen Markierungen im sichtbaren Text') +}) + +test('legal_datenschutz_anfragen: Abschnitt 2, 12 und 14 passen zu Abschnitt 11 und es gibt keinen Widerspruch zu 8', () => { + const ds = strip(read(DS)) + const s2 = between(ds, '2. Geltungsbereich', '3. Hosting') + assert.ok(s2.includes('Menschen und Betriebe, die im Netz öffentlich nach einer Website suchen') && s2.includes('Privatpersonen') && s2.includes('Frage-Foren') && s2.includes('(Abschnitt 11)')) + const emp = between(ds, '12. Empfänger im Überblick', '13. Ihre Rechte') + assert.ok(emp.includes('Anthropic Ireland, Limited: Bewertung öffentlicher Anfragen, Entwurf von Antworten sowie Websuche und Seitenabruf durch ein Sprachmodell (Auftragsverarbeiter, Daten auch in den USA, Abschnitt 11)')) + assert.ok(emp.includes('Plattformen, auf denen wir Ihre Anfrage beantworten')) + assert.ok(!emp.includes('Betrieben') && !emp.includes('Website-Texte'), 'der Anthropic-Zweck nennt keine Website-Texte von Betrieben mehr') + const s14 = between(ds, '14. Pflicht zur Bereitstellung', '15. Änderungen') + assert.ok(s14.includes('keine automatisierten Einzelentscheidungen nach Art. 22 DSGVO')) + assert.ok(s14.includes('Die Bewertung öffentlicher Anfragen durch einen KI-Assistenten (Abschnitt 11) bereitet nur eine Antwort vor; ob und was wir antworten, entscheidet ein Mitarbeiter, der die Antwort von Hand abschickt.')) + assert.ok(!ds.includes('keine automatisierten Entscheidungen'), 'die zu weite alte Überschrift ist weg') + // Einleitung nennt die Antwort auf öffentliche Anfragen + assert.ok(ds.includes('oder wenn wir auf Ihre öffentliche Anfrage nach einer Website antworten')) + // Abschnitt 8 (E-Mail über Hetzner) bleibt unverändert gültig, Abschnitt 11 verweist darauf, statt davon abzuweichen + assert.ok(between(ds, '8. E-Mail', '9. Buchhaltung').includes('versenden und empfangen wir über den E-Mail-Dienst der Hetzner Online GmbH')) +})