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')) +})