Committed seeds and fixtures contain no real-looking PII
Why it matters
SOC 2 C1.1 requires identifying and protecting confidential information, and a git repository is the leakiest place personal data can live: it is cloned to every laptop, mirrored to CI runners, shared with contractors, and immortal in history even after deletion. Real customer rows exported into a seed file "just to have realistic test data" are a confidentiality breach an auditor will flag on day one, and the pattern is common in AI-built projects because coding assistants happily paste a production query result into prisma/seed.ts when asked for sample data. Unlike a leaked key, leaked PII cannot be rotated: once an email address, SSN, or card number is in history, the exposure is permanent. This check evidences SOC 2 C1.1 by verifying that committed data files use synthetic, reserved-domain, or documented-test values only.
Severity rationale
Medium because committed PII is a real confidentiality exposure with permanent git-history persistence, but exploitation requires repo access and the blast radius is bounded by how much data was committed, unlike a live credential.
Remediation
Replace real values in seeds and fixtures with synthetic data: use @faker-js/faker configured to emit reserved documentation domains (faker.internet.email({ provider: 'example.com' })), the 555-01XX fictional phone range, and processor-published test card numbers (Stripe's 4242424242424242). Purge already-committed real values from git history with git filter-repo (history persistence is the actual exposure, not just the current tree), then add a secret/PII scanning step to CI so regressions are caught at commit time.
Detection
- ID:
no-real-pii-in-committed-fixtures - Severity:
medium - What to look for: Enumerate committed data files:
prisma/seed.ts,seeds/,fixtures/,__fixtures__/,test/andtests/data files (.json,.csv,.sql,.tsseed scripts),supabase/seed.sql,cypress/fixtures/,db/seeds/. Within them, apply ONLY these deterministic detectors, precision over recall: (a) email addresses whose domain is NOT a reserved documentation domain — reserved domains pass:example.com,example.org,example.net, any*.test,*.invalid,*.localhost,*.example(RFC 2606/6761); real deliverable domains fail, including the project's own domain (internal staff emails are real PII); (b) SSN-format strings (\d{3}-\d{2}-\d{4}) unless the area number is000,666, or900-999(invalid/never-issued ranges); (c) 13-19 digit numbers that pass the Luhn check and are NOT processor-published test PANs (allow Stripe and major-processor documented test numbers:4242424242424242,4000...series,4111111111111111,5555555555554444,378282246310005,6011111111111117); (d) phone numbers ONLY when in labeled fields (phone:,tel:,mobile:) and outside the555-01XXfictional range. Names alone NEVER fail: a"John Smith"in a seed is not evidence of real PII. Count all data files examined, and for each detector hit quote a REDACTED excerpt showing shape only — never the full value. - Pass criteria: Data files exist and every detector above comes back clean — values are faker-generated, reserved-domain, invalid-range, or documented test numbers — or data files contain no person-shaped fields at all. Report the count of data files scanned and their paths even on pass. Do NOT pass on a comment or README claiming the data is fake — only the detector rules above decide, and a deliverable-domain email is a fail regardless of intent annotations.
- Fail criteria: Any detector (a)-(d) fires on a committed data file. Volume matters for the detail string, not the verdict: one real email fails the same as five hundred rows, because git history makes any committed value permanent.
- Skip (N/A) when: The repository has no seed, fixture, or committed test-data files at all. Quote:
"No seed/fixture/test-data files found (checked prisma/seed.*, seeds/, fixtures/, cypress/fixtures/, supabase/seed.sql, test data files)". - Before evaluating, quote: The list of data files examined, and for each finding the file path plus a REDACTED excerpt showing shape only (e.g.
j***@gmail.com,4539-…-…03 (Luhn-valid, not in the documented test-PAN set)). Never reproduce a full email, SSN, or PAN in the report or telemetry. - Report even on pass:
"<N> data files scanned (paths); all person-shaped values synthetic: reserved domains / 555-01XX phones / documented test PANs". - Detail on fail:
"prisma/seed.ts: 12 email addresses at deliverable domains (gmail.com, outlook.com) and 3 labeled phone fields outside 555-01XX — values redacted; git history retains them even if replaced"or"cypress/fixtures/users.json: 2 Luhn-valid card numbers not in the documented test-PAN set". - Remediation: Regenerate seeds with
@faker-js/fakerpinned to reserved domains (faker.internet.email({ provider: 'example.com' })), fictional phone ranges, and Stripe test PANs; purge history withgit filter-reposince replacement alone leaves the exposure in every clone; then wire a CI scanning gate so it cannot recur. Thesecret-scanning-in-cicheck in this audit covers that gate.
External references
- soc2:2017 · C1.1 — Identification and protection of confidential information
- iso-27001:2022 · A.8.33 — Test information
Taxons
History
- 2026-07-03·v1.0.0·Initial soc2-readiness v1.0.0 authoring — deterministic real-PII detection in committed seeds/fixtures/test data, evidencing SOC 2 C1.1 and ISO 27001 A.8.33.·by soc2-readiness-v1-build