GDPR Audit Findings

CRITICAL GAPS IDENTIFIED

This audit reveals 7 critical gaps that must be fixed before production. The Bisnode precedent (€220K fine) applies directly.

Summary

CategoryStatusNotes
Opt-Out Blocklist⚠️ PARTIALHashing exists but NOT actively used in pipeline
RoPA Logging✅ FUNCTIONALComprehensive logging across all major operations
Article 14 Notifications⚠️ PARTIALInfrastructure exists but has critical gaps
Reklamspärr (Advertising Block)✅ FUNCTIONALChecked at multiple gates
Data Retention / TTL❌ CRITICAL GAPNo automated retention enforcement
Subprocessor Handling❌ GAPNo subprocessor register or DPA tracking
Legitimate Interest Assessment⚠️ DOCUMENTATION ONLYExtensive KB articles, no formal LIA document
Privacy Portal / Self-Service❌ MISSINGNo opt-out web form or self-service portal
Data Deletion Mechanisms❌ CRITICAL GAPNo automated deletion or erasure workflow

Critical Gaps (Must Fix Before Production)

2026-04-28 — PARTIALLY FIXED

The original claim that “reklamspärr not in queue workers” was incorrect. Reklamspärr IS checked in multiple places. However, isOptedOut() (opt-out blocklist) is still not actively checked in API endpoints.

C1: isOptedOut() Not Checked on API Read Endpoints

  • Location: src/scraping/middleware.ts (definition only)
  • Impact: Opt-out blocklist exists but is not enforced on API reads. Opted-out individuals still visible in search, lists, detail views.
  • Evidence: isOptedOut() defined but never called in src/api/*.ts endpoints.
  • Fix: Add middleware-level opt-out gate on ALL data access endpoints.
  • Note: Reklamspärr IS properly enforced — see below.

C2: No Automated Data Retention/Deletion

  • Impact: Personal data accumulates indefinitely. Violates Art. 5(1)(e) storage limitation.
  • Evidence: No retention_expires_at columns on any PII table. KB recommends 12-24 months for contact data; code implements nothing.
  • Fix: Add retention fields + scheduled cleanup jobs.

C3: No Data Erasure Workflow (Art. 17)

  • Impact: Cannot comply with right to erasure. No mechanism to delete individual records on request.
  • Fix: Build cascade deletion workflow with RoPA logging.

C4: No Privacy Portal or Opt-Out Form

  • Impact: Cannot comply with Art. 12-14 transparency obligations. Art. 14 emails reference non-existent pages.
  • Evidence: PRIVACY_NOTICE_URL and OPT_OUT_URL env vars default to https://enrichnode.io/privacy and /opt-out but no actual pages exist.
  • Fix: Build minimal static pages for privacy notice and opt-out.

C5: SMTP Not Configured for Art. 14 Notifications

  • Location: src/lib/article14Notification.ts:263
  • Impact: Notifications are scheduled at collection time (correct) but never delivered. Bisnode-level violation risk.
  • Evidence:
    logger.warn(..., 'SMTP not configured — notification skipped. Set SMTP_HOST, SMTP_USER, SMTP_PASS to enable.');
    return { sent: false, error: 'EMAIL_API_URL_NOT_SET' };
  • Fix: Configure SMTP_HOST, SMTP_USER, SMTP_PASS or integrate SendGrid/Mailgun.

C6: No Subprocessor Register or DPAs

  • Impact: Cannot demonstrate Art. 28 compliance. No transparency on who processes data.
  • Identified subprocessors: Serper.dev, Anthropic (Claude), OpenAI, SendGrid/Mailgun/Postmark
  • Fix: Build subprocessor register with DPA/SCC documentation.

C7: No Formal LIA Document

  • Impact: Art. 6(1)(f) basis is documented in KB but not as standalone signed document.
  • Evidence: Excellent KB articles exist (KB/src/kb/b2b-enrichment/legitimate-interest.ts) but no standalone, signed LIA.
  • Fix: Create formal LIA document based on KB articles.
  • Note: Code uses HMAC-SHA256 (not plain SHA-256 as old docs claimed).

High Priority Gaps

#IssueLocation
H1No opt-out check on API read endpointssrc/api/companies.ts, src/api/leads.ts, src/api/kundkort.ts, src/api/search.ts
H2No RoPA logging on data access (SELECT)All GET endpoints
H3No reklamspärr check on search endpointssrc/api/search.ts
H4HASH_SALT is module-level constantsrc/compliance.ts:14
H5No retention fields on personal data tablescontact_discovery_log, mapped_contacts, etc.
H6Art. 14 notification timing not enforcedsrc/workers/art14Worker.ts
H7No deduplication across companies for Art. 14src/lib/article14Notification.ts:67-78
H8Kundkort CSV export doesn’t filter reklamspärrsrc/api/kundkort.ts:962-1085

Positive Compliance Features

Despite gaps, the codebase demonstrates strong compliance awareness:

  1. HMAC-SHA256 hashing with salt validation
  2. Reklamspärr multi-gate checking (enrichment, export, dispatch, scraping)
  3. Comprehensive RoPA logging for all write operations
  4. Art. 14 notification infrastructure (email templates, queue, deduplication)
  5. Rate limiting on API endpoints
  6. Authentication and RBAC on all sensitive endpoints
  7. CSV injection protection in exports
  8. Export limits (10,000 records max)
  9. Excellent KB documentation on GDPR
  10. Bisnode precedent awareness throughout codebase
  11. SCB advertising block import (Reklam = '2' correctly mapped)
  12. Archive tables for inactive company data
  13. Enrichment error tracking with classification
  1. Is the current Art. 6(1)(f) basis defensible without a signed LIA?
  2. Can the platform demonstrate Art. 14 compliance given notifications are not actually sent?
  3. What is the exposure under IMY enforcement given the Bisnode precedent?
  4. Should the platform consider appointing a DPO (Art. 37)?
  5. Are the current DPAs with Serper, Anthropic, and OpenAI sufficient for Art. 28?

See also