Back to Blogindustry2026-05-2413 min read
My own IBAN appeared in a Sentry stack trace in my GDPR SAR response — worker PII scrub (PR #626 GGG F4)
Aleksi (27) freelance dev + TikTok food creator Helsinki Kallio (@aleksieats, 39k followers), 14 months thMenu affiliate (~6-9 restaurants/month, ~€85 commission). Previous month read about separate SaaS data breach, started filing GDPR Article 15 SARs habitually. thMenu support 30 days later sent ZIP — affiliate_profiles dump, commission history, postback log, customer_activity events, + **sentry_events.json**. Aleksi opened ZIP, looked at sentry_events.json, 7 events. 3rd was Wise transfer failure exception, stack trace context.extra: **"iban": "FI21 1234 5600 0007 85"** — Aleksi s own IBAN plaintext in Sentry. Forensic: apps/web-superadmin/src/lib/wise.ts:initiateTransfer() catch branch Sentry.captureException(err, { extra: { affiliateId, transferId, iban: decryptedIban, ... }}) — fills full context including IBAN. Intent good (debugging Wise rejected IBAN). But Sentry default retention **90 days**, Synaltix internal SRE + contractor + dev access broad, IBAN sits plaintext. 3 months ago Aleksi s Wise transfer failed (KYC re-verification), catch branch sent Sentry event with IBAN in extra, accessible 90 days on dashboard. Aleksi scan: Event 3 IBAN, Event 5 Resend email send failure email + name, Event 7 Stripe checkout webhook breadcrumb customer_email + plan_id. 3 distinct PII events. Aleksi reported GDPR Article 5(1)(f) integrity + confidentiality + minimization violation. thMenu engineering 1 hour reproduce + severity HIGH. apps/web-admin/src/lib/observability.ts Sentry.init missing beforeSend hook. cloudflare/src/lib/sentry.ts worker side (PR #362 fire-and-forget envelope) same pattern. 90-day Sentry retention PII-keyword grep: **3,847 events contained PII**, 218 unique affiliates/customers. **PR #626 batch GGG F4** 4-layer fix: **Layer 1 beforeSend hook PII scrub** — PII_KEYS denylist (iban, tax_id, ssn, email, phone, address, card_number, cvv, session_id, token, magic_link, password, authorization, cookie) + PII_REGEX inline pattern (IBAN-like [A-Z]{2}\d{2}[A-Z0-9 ]{15,30}, email-like, phone-like). redactObject recursive applied to event.extra + event.contexts + event.breadcrumbs.data + event.request.url + event.user. Defense-in-depth key denylist + regex pattern. **Layer 2 worker-side sibling fix** — cloudflare/src/lib/sentry.ts pure-fetch envelope POST same redactObject; helper moved to shared packages/sentry-redact single source of truth. **Layer 3 Backfill retroactive Sentry redaction** — Sentry events.update API script redacted 3,847 affected events extra + contexts + breadcrumbs in ~6 hours. **Layer 4 CI test** — vitest fixture simulates exception payload with IBAN/email/phone, runs through beforeSend, asserts [REDACTED]; CI regression guard. 24 hours later Aleksi got response + 6-month free Pro tier + Hall of Fame. Platform-wide 218 affected received proactive emails + Synaltix DPA addendum updated with "Sentry now PII-scrubbed at SDK level" line + Schrems II audit docs refreshed. Pattern: **exception + log payloads sent to Sentry / Datadog / Honeycomb / Logz.io must be PII-scrubbed mandatorily. At SDK init use beforeSend / before_log hook with key-based denylist + regex-based inline pattern match — defense-in-depth.** Implementation checklist: (1) beforeSend mandatory, don t ship empty; (2) PII_KEYS denylist standard 15 fields; (3) PII_REGEX inline pattern match; (4) event.extra/contexts/breadcrumbs/request/user coverage; (5) shared packages/sentry-redact module; (6) CI vitest fixture regression test; (7) retroactive backfill events.update API; (8) quarterly audit random sample grep PII threshold alarm. Eren Trabzon Ortahisar version (@ereneats) with same flow TR IBAN.