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
| Category | Status | Notes |
|---|---|---|
| Opt-Out Blocklist | ⚠️ PARTIAL | Hashing exists but NOT actively used in pipeline |
| RoPA Logging | ✅ FUNCTIONAL | Comprehensive logging across all major operations |
| Article 14 Notifications | ⚠️ PARTIAL | Infrastructure exists but has critical gaps |
| Reklamspärr (Advertising Block) | ✅ FUNCTIONAL | Checked at multiple gates |
| Data Retention / TTL | ❌ CRITICAL GAP | No automated retention enforcement |
| Subprocessor Handling | ❌ GAP | No subprocessor register or DPA tracking |
| Legitimate Interest Assessment | ⚠️ DOCUMENTATION ONLY | Extensive KB articles, no formal LIA document |
| Privacy Portal / Self-Service | ❌ MISSING | No opt-out web form or self-service portal |
| Data Deletion Mechanisms | ❌ CRITICAL GAP | No 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 insrc/api/*.tsendpoints. - 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_atcolumns 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_URLandOPT_OUT_URLenv vars default tohttps://enrichnode.io/privacyand/opt-outbut 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_PASSor 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
| # | Issue | Location |
|---|---|---|
| H1 | No opt-out check on API read endpoints | src/api/companies.ts, src/api/leads.ts, src/api/kundkort.ts, src/api/search.ts |
| H2 | No RoPA logging on data access (SELECT) | All GET endpoints |
| H3 | No reklamspärr check on search endpoints | src/api/search.ts |
| H4 | HASH_SALT is module-level constant | src/compliance.ts:14 |
| H5 | No retention fields on personal data tables | contact_discovery_log, mapped_contacts, etc. |
| H6 | Art. 14 notification timing not enforced | src/workers/art14Worker.ts |
| H7 | No deduplication across companies for Art. 14 | src/lib/article14Notification.ts:67-78 |
| H8 | Kundkort CSV export doesn’t filter reklamspärr | src/api/kundkort.ts:962-1085 |
Positive Compliance Features
Despite gaps, the codebase demonstrates strong compliance awareness:
- HMAC-SHA256 hashing with salt validation
- Reklamspärr multi-gate checking (enrichment, export, dispatch, scraping)
- Comprehensive RoPA logging for all write operations
- Art. 14 notification infrastructure (email templates, queue, deduplication)
- Rate limiting on API endpoints
- Authentication and RBAC on all sensitive endpoints
- CSV injection protection in exports
- Export limits (10,000 records max)
- Excellent KB documentation on GDPR
- Bisnode precedent awareness throughout codebase
- SCB advertising block import (
Reklam = '2'correctly mapped) - Archive tables for inactive company data
- Enrichment error tracking with classification
Legal Assessment Questions for Board
- Is the current Art. 6(1)(f) basis defensible without a signed LIA?
- Can the platform demonstrate Art. 14 compliance given notifications are not actually sent?
- What is the exposure under IMY enforcement given the Bisnode precedent?
- Should the platform consider appointing a DPO (Art. 37)?
- Are the current DPAs with Serper, Anthropic, and OpenAI sufficient for Art. 28?