All 31 checks with why-it-matters prose, severity, and cross-references to related audits.
A single unprotected API route or Server Action is a complete authorization bypass for any operation it performs. API routes in Next.js are public endpoints by default — authentication and authorization must be added explicitly. CWE-285 (Improper Authorization) and CWE-284 (Improper Access Control) are both in play when a route performs database writes without verifying who is asking. OWASP A01 (Broken Access Control) is the top web application risk category for this exact reason. NIST 800-53 AC-3 and AC-6 require access enforcement before any privileged operation executes. Server Actions receive the same treatment — they are callable from the browser and must not assume caller identity.
Why this severity: Critical because a single unprotected route exposes all of its data operations to unauthenticated callers, with no authentication barrier between the public internet and the database.
saas-authorization.resource-auth.every-api-checks-permsSee full patternHardcoded secrets in source code violate CWE-798 (Use of Hard-coded Credentials) and OWASP A07 (Identification & Authentication Failures), and they are permanent: unlike a leaked .env file, a credential baked into a function call survives every deployment, every refactor, and every repo clone. A Stripe live key or database password embedded in a TypeScript file is readable by every developer, CI runner, contractor, and open-source contributor who touches the repo. NIST IA-5 requires credential management controls — hardcoded values have no rotation path, no access control, and no audit trail.
Why this severity: Critical because hardcoded credentials are unrevokable without a code change and are visible to everyone with repository access, making targeted rotation impossible.
environment-security.secrets-management.no-hardcoded-secretsSee full patternSOC 2 CC6.1 (logical access security software, infrastructure, and architectures) expects credentials that gate production systems to live in a managed store with controlled access, not in flat files anyone with repo read access can copy. This check evidences CC6.1: an auditor's first secrets question is "where do production credentials live and who can read them", and the answer must be a platform (Vercel or Netlify encrypted env vars, AWS Secrets Manager, Google Secret Manager, Vault), not a tracked `.env`. AI-generated and vibe-coded apps typically fail this because coding assistants scaffold `dotenv` plus a working `.env`, the app runs, and nothing ever forces the move to a managed store or the writing of a `.env.example`, so the team cannot even enumerate which secrets exist. This is partly an evidence-pointer check: for dashboard-managed platform env vars it verifies committed evidence of the control (a `.env.example`, platform config, or ops doc naming the store), which is exactly the artifact a SOC 2 auditor will ask for. It deliberately does not grep for leaked literal values; the sibling no-hardcoded-secrets check covers that. This one checks management posture and documentation.
Why this severity: High because undocumented or flat-file secret management means production credential access is untracked and unrevocable per person, and a single repo-access grant silently becomes production access, which is the exact access-control architecture failure CC6.1 targets.
soc2-readiness.logical-access.production-secrets-platform-managedSee full patternClearing a session cookie on the client has no effect on a session token that was already captured — by an XSS payload, a network sniff, or a shared machine where the token was read from memory. CWE-613 (Insufficient Session Expiration) applies when logout does not invalidate the server-side session record. NIST 800-63B §7.1 (Session Bindings) requires that session termination be enforced server-side, not only client-side. OWASP A07 (Identification and Authentication Failures) lists insufficient logout among its named failure modes. The server must forget the session; the cookie clear is only a UX courtesy.
Why this severity: High because client-only logout leaves a captured session token valid until its natural expiry, giving attackers continued access even after the legitimate user has explicitly logged out.
saas-authentication.auth-flow.logout-invalidates-sessionSee full patternThis evidences SOC 2 CC6.2, which requires that access is removed when no longer authorized: an auditor reads "removes access" to include in-flight sessions, not just the next login attempt. AI-generated apps fail this in a very specific way: the scaffold ships stateless JWTs with long lifetimes (next-auth defaults to 30 days) and an admin "ban user" route that flips a `banned` or `is_active` column, and nothing ever consults that column again until the token expires. The result is a deprovisioning gap where a fired employee, a compromised account, or a demoted admin keeps full live access for days or weeks after the org believes it was cut off. That gap is exactly what a SOC 2 auditor probes during the logical-access walkthrough ("show me what happens to an active session when you disable this user"), and it is the difference between a clean CC6.2 test and an exception in the report.
Why this severity: High because a disabled or demoted user retains their full prior access for the entire remaining token or session lifetime, a live deprovisioning gap that defeats the point of the disable action; it is not critical because exploitation requires an account that was already legitimately provisioned.
soc2-readiness.logical-access.sessions-revoked-on-access-changeSee full patternSOC 2 CC6.3 requires that access to systems and data is modified or removed based on roles, and that access grants are periodically reviewed: an auditor's first request in the logical-access section is "show me your access-review procedure and its last output." This is an evidence-pointer check: it verifies committed evidence of the control (a doc naming who holds production and admin access, how often the grant list is reviewed, and who runs the review), which is exactly the artifact an auditor will ask for. It does not, and cannot, verify that reviews actually happen off-repo. AI-generated and vibe-coded apps almost never carry this artifact: coding assistants scaffold roles, RBAC middleware, and admin routes on request, but "write down our quarterly access-review ritual" is an organizational habit, not a code prompt, so the repo ships with admin surfaces and zero paper trail. Teams then discover the gap during audit readiness, when reconstructing months of undocumented review history is impossible and the Type II observation window has to restart. Committing the procedure now (even a half-page in `SECURITY.md`) is the cheapest control in the whole SOC 2 readiness set.
Why this severity: Info because the absence of a committed procedure doc creates audit-evidence friction, not a direct exploitable weakness: the underlying access controls are scored by separate checks, and this one only measures whether the review ritual is written down where an auditor can see it.
soc2-readiness.logical-access.access-review-procedure-documentedSee full patternHTTPS enforcement prevents plaintext connections, but HSTS goes further: it instructs the browser to refuse HTTP for your domain entirely for up to a year after the first visit, eliminating SSL-stripping attacks that can silently downgrade an HTTPS connection before the browser even sees it. Without a `max-age` of at least 31536000, an attacker who intercepts the first request can strip the redirect and operate in plaintext for every subsequent session. RFC 6797 defines the spec; OWASP A02 identifies the absence of HSTS as a cryptographic failure that enables credential theft.
Why this severity: High because a missing or short-lived HSTS header leaves the window open for SSL-stripping attacks that HTTPS redirects alone cannot prevent.
security-headers.transport.hsts-enabledSee full patternA database reachable from the open internet is the single most catastrophic misconfiguration class in cloud history: the Moltbook exposure Wiz Research documented in 2026 and the recurring MongoDB and Elasticsearch ransom sweeps (tens of thousands of open instances wiped and held for bitcoin) all started with exactly this, a data store bound to 0.0.0.0/0 instead of a private subnet. This check evidences SOC 2 CC6.6, protection against threats from outside system boundaries: an auditor will ask how you restrict network access to systems holding customer data, and your committed IaC is the artifact that answers. AI coding tools fail here reliably because generated Terraform and docker-compose examples default to `publicly_accessible = true` and `0.0.0.0:5432:5432` so the demo "just works", and nobody revisits the setting before production. The check reads only what is declared in the repo; it makes no claim about live cloud state.
Why this severity: Critical because a publicly routable database port turns a single leaked or default credential into full exfiltration of the entire data store, with no application-layer control in the path.
soc2-readiness.boundary-and-supply-chain.iac-database-not-publicly-accessibleSee full patternWithout a committed lock file, every `npm install` — on your machine, in CI, and at deploy time — is a fresh resolution against whatever versions are current on the registry at that moment. A transitive dependency can silently update between your local install and the production deploy, introducing a behavioral change or a newly-published vulnerability without any visible diff. SLSA L2 requires that the build process be reproducible from a pinned source; a missing lock file breaks that invariant. CWE-829 (Inclusion of Functionality from Untrusted Control Sphere) applies because the install is pulling code from a source you haven't reviewed at that exact version. Lock files are the minimum viable proof that what you shipped is what you tested.
Why this severity: High because a missing lock file means your production environment can silently install different code than what was tested, turning any npm publish into a potential silent update to your running application.
dependency-supply-chain.security-vulns.lockfile-committedSee full patternA `uses: someorg/some-action@v3` line in a GitHub Actions workflow trusts whoever controls that tag, forever: tags are mutable, so a compromised or malicious maintainer can retag `v3` to point at new code and your CI runs it on the next push, with full access to your repository checkout and every secret exposed to the job. That is exactly what happened in the tj-actions/changed-files compromise (March 2025, CVE-2025-30066): attackers retagged the action and exfiltrated CI secrets from thousands of repositories that referenced it by tag. Pinning third-party actions to a full 40-character commit SHA makes the reference immutable, so upstream retagging cannot change what your pipeline executes. This evidences SOC 2 CC6.8 (prevention and detection of unauthorized or malicious software) and supports CC8.1 (change management: only authorized, reviewed changes enter the build pipeline). AI-generated and vibe-coded apps fail this almost universally, because every tutorial, template, and AI-scaffolded workflow writes `@v4` style tags: the SHA-pinning convention only shows up when someone has been burned by supply chain attacks, and coding assistants reproduce the tutorial form.
Why this severity: High because a mutable tag reference lets an upstream retag execute arbitrary code inside CI with access to checkout contents and all job-scoped secrets, the exact mechanism of CVE-2025-30066.
soc2-readiness.boundary-and-supply-chain.third-party-actions-pinnedSee full patternRunning a major version that is 2+ releases behind the current means you are on a branch of the dependency that no longer receives backported security patches. React 16 does not receive fixes for vulnerabilities discovered in React's rendering pipeline. Express 3 has not received security patches since 2015. OWASP A06 (Vulnerable and Outdated Components) directly names major version lag as a risk vector. CWE-1104 (Use of Unmaintained Third Party Components) applies when the installed major version has reached end-of-life. The deeper the lag, the larger the eventual migration — skipping from React 16 to 19 requires addressing three sets of breaking changes simultaneously, while each single-major upgrade is manageable. Major version lag is technical debt with a compounding interest rate.
Why this severity: Low because being one or two majors behind does not introduce immediate exploits, but it accumulates migration debt and means security patches from the current major branch do not apply to your version.
dependency-supply-chain.maintenance.current-major-versionsSee full patternThis is an evidence-pointer check: it verifies committed evidence of the control, which is what an auditor will ask for. SOC 2 CC2.3 requires the entity to communicate with external parties about matters affecting internal control, and a vulnerability-disclosure policy is the cheapest CC2.3 evidence a repository can carry: a SECURITY.md naming a working reporting contact plus instructions for what to send. AI coding tools scaffold READMEs, licenses, and CI configs but essentially never generate a disclosure policy, and template repos that do include one often ship it with `security@example.com` placeholder text, so vibe-coded apps routinely reach production with no intake channel at all. The consequence is concrete: a researcher who finds a hole and cannot locate a contact either gives up or discloses publicly, and when the SOC 2 auditor asks how external parties report security issues, there is nothing to point at.
Why this severity: Low because a missing disclosure policy is an absent evidence artifact and intake channel, not itself an exploitable weakness: the mechanism of harm is slower researcher-to-vendor communication and a missing auditor deliverable, not direct compromise.
soc2-readiness.boundary-and-supply-chain.security-md-disclosure-policySee full patternDeploying untested code to production is a direct path to user-facing regressions, data corruption, and outages. Without CI/CD gates (NIST SP 800-218 PW.8, SSDF), a broken commit reaches users the moment it's merged. ISO 25010 reliability.maturity requires demonstrable automated quality gates. Teams without mandatory test runs discover failures in production — where the cost is measured in downtime, rollbacks, and lost trust — rather than in a pull request, where the cost is a 5-minute fix.
Why this severity: Critical because untested code in production is a direct cause of undetected regressions, outages, and data integrity failures that affect all users simultaneously.
deployment-readiness.ci-cd-pipeline.automated-testsSee full patternDeploying directly to production without a staging environment means every untested configuration change, database migration, or third-party integration issue hits live users first. NIST CM-4 requires evaluating the security impact of changes before production; that evaluation requires a representative environment. A staging environment that uses SQLite while production uses PostgreSQL is not representative — subtle query behavior differences and migration incompatibilities only surface at the worst time.
Why this severity: High because skipping a production-equivalent staging environment means integration failures, migration errors, and environment-specific bugs are discovered by users rather than by the team.
deployment-readiness.ci-cd-pipeline.staging-environmentSee full patternApplying untested database migrations directly to production risks table corruption, constraint violations, or data loss that cannot be easily undone. NIST CP-9 and SOC 2 A1.2 require verified recovery procedures for data; a migration that drops a column or alters a type without pre-testing can trigger application errors for every user and may require hours of manual data recovery. ISO 25010 reliability.recoverability demands that any data-modifying operation be validated before it reaches the primary store.
Why this severity: High because a failed migration in production can corrupt live data or take down the application for all users, with recovery time measured in hours if no pre-tested rollback path exists.
deployment-readiness.ci-cd-pipeline.migration-testingSee full patternSOC 2 CC8.1 requires that changes to production systems are authorized, tested, and approved before they ship, and the very first thing an auditor asks for is your change management evidence: pull requests, review approvals, and the CI checks that gated them. This is an evidence-pointer check: it verifies committed evidence of the control, which is what an auditor will ask for. Server-side branch protection settings live in the GitHub or GitLab dashboard and are invisible to a repository audit, so the repo needs artifacts that make the workflow real and auditable: CI that triggers on pull requests, a CODEOWNERS file, branch protection exported as code, or a PR template. AI-generated and vibe-coded apps typically fail here because the default workflow is a solo founder (or their coding agent) committing straight to `main` and letting Vercel deploy it: no PR, no reviewer, no gate. That habit is fine for a prototype, but for SOC 2 it means every change to production is a one-person unauthorized change, and an auditor will flag the entire change management criterion as not operating. Setting up a PR-based flow with branch protection takes about 10 minutes on GitHub, which makes this one of the cheapest readiness wins in the whole audit.
Why this severity: High because without a review gate a single actor (or a misbehaving coding agent) can push unauthorized changes directly to the production deploy branch, which auditors treat as a pervasive CC8.1 change management failure rather than an isolated finding.
soc2-readiness.change-management.pr-review-workflow-configuredSee full patternSOC 2 CC8.1 requires that changes to infrastructure and software reach production through an authorized, repeatable path, and the first thing an auditor traces in the change-management section is "show me how a merged change becomes a deploy." This is an evidence-pointer check: it verifies committed evidence of the deployment path (a CI deploy job, a platform config file like `vercel.json`, a Dockerfile with orchestration, or IaC), which is what an auditor will ask for. It does not claim to see dashboard state. AI-built and vibe-coded apps fail this by construction: the standard flow is clicking "import repo" in the Vercel or Netlify dashboard, after which the platform deploys every push while the repo itself carries zero record of how production comes to exist. The deploy works, but when an auditor, a new engineer, or an incident review asks "what config governs this deploy," the answer lives only in a dashboard nobody can diff, review, or roll back through git. Committing the config file the platform already supports turns an invisible deploy path into reviewable, versioned evidence.
Why this severity: Low because platform git integration (a Vercel or Netlify dashboard connected to the repo) is a legitimate, controlled deploy path that is invisible from the repo, so a fail here usually means missing committed evidence of the path rather than an actually ad hoc deployment.
soc2-readiness.change-management.deployment-config-in-repoSee full patternPCI-DSS Req 10.2 mandates logging of all access to payment-related system components, including who performed actions and when. SOC 2 CC7.2 requires detection and logging of security-relevant activities. CWE-778 covers the failure directly: sensitive operations performed without an audit trail leave you unable to answer the most common post-incident questions — "when did this role change happen?", "who approved this data export?", "what did this admin do?" Application logs are insufficient because they are ephemeral, mutable, and not designed for forensic analysis. OWASP A09 identifies missing audit trails as a primary monitoring failure. A breach investigation without an audit log typically takes 3–5x longer and often cannot establish the timeline of events at all.
Why this severity: Critical because sensitive operations without an audit trail cannot be forensically reconstructed after a security incident, and the absence violates PCI-DSS Req 10.2 and SOC 2 CC7.2 directly.
saas-logging.audit-trail.audit-log-sensitive-opsSee full patternSOC 2 CC7.2 requires that you monitor system components for anomalies, and the first question an auditor asks is "how do you know when production breaks?" An error-tracking service (Sentry, Bugsnag, Rollbar, Honeybadger) that aggregates exceptions and alerts a human is the standard code-level answer; `console.error` scrolling past in a log nobody tails is not. AI-generated apps fail this in a specific, repeatable way: the model adds `@sentry/nextjs` to package.json because the prompt mentioned "production ready", then never runs the wizard, so there is no `sentry.server.config.ts`, no `instrumentation.ts` register, and no DSN wiring. The dependency line looks like monitoring; unhandled server exceptions still vanish silently. Client-only inits are the second failure mode: browser errors get captured while the API routes that actually corrupt data report nothing. This check verifies the wiring in the repo (an init call with a DSN sourced from env, on the server side), which is exactly the code evidence a SOC 2 auditor will trace when testing CC7.2.
Why this severity: High because unwired error tracking means server-side failures in production go undetected until users report them, which is a direct CC7.2 monitoring gap an auditor will test on day one, though it does not by itself expose data.
soc2-readiness.vuln-management-and-monitoring.error-tracking-initializedSee full patternA one-time secrets sweep tells you what has already leaked; a scanning gate in the change path is what stops the next leak, and the next one is coming: GitGuardian measured 23.8 million new secrets pushed to public GitHub in 2023 alone, and the Toyota incident (an access key sitting in a public repo for five years) shows how long a single miss survives. This evidences SOC 2 CC7.1 (detection and monitoring of configuration changes and vulnerabilities): the auditor wants to see a recurring, automated detection mechanism, not a clean point-in-time grep. AI-generated and vibe-coded apps typically fail here because coding assistants scaffold the app and sometimes even a test workflow, but no tutorial-shaped prompt ever adds gitleaks or trufflehog to CI, so the change path ships with zero secret detection while `.env` values migrate into committed config during rapid iteration. One leg of this check is an evidence-pointer: GitHub's native secret scanning and push protection live in repo settings that are invisible from a clone, so a committed doc stating they are enabled counts as the evidence an auditor will ask for, with the detail noting it is doc-evidenced rather than directly observed.
Why this severity: Medium because this is a layered detective control: its absence does not itself expose a secret, it removes the automated gate that would catch the hardcoded credentials other checks score directly, so exposure accrues on the next mistake rather than immediately.
soc2-readiness.vuln-management-and-monitoring.secret-scanning-in-ciSee full patternKnown vulnerabilities in dependencies are one of the most reliable attack paths for automated exploitation. CWE-1357 (Reliance on Insufficiently Trustworthy Component) and OWASP A06 (Vulnerable and Outdated Components) document that unpatched CVEs are commonly exploited within days of public disclosure. NIST 800-53 SA-10 and SSDF PW.4 both require regular assessment of component security. Projects without automated scanning may not discover a critical CVE in a transitive dependency until a breach occurs. Dependabot or `npm audit` in CI catches these before they ship to production.
Why this severity: Low because vulnerability scanning prevents exploitation of known CVEs — the risk is low only when scanning runs continuously; without it, the effective severity of any undetected CVE is unbounded.
security-hardening.secrets-config.dependency-vuln-scanningSee full patternCWE-779 (Logging of Excessive Data) identifies verbose debug output in production as a logging misconfiguration with real costs: inflated log ingestion bills, slower log shipping, and inadvertent exposure of internal state that debug-level entries often contain. NIST AU-11 covers log retention and volume management — producing debug output in production that you don't need directly increases retention costs without increasing observability value. The secondary risk is that debug output frequently captures internal parameter values, function inputs, and intermediate state that are inappropriate for a production log store. A hardcoded `level: 'debug'` in your logger config means your production environment is behaving like a development instance.
Why this severity: Low because debug logging in production inflates cost and occasionally leaks internal state, but does not directly enable attack vectors or data loss.
saas-logging.app-logging.no-debug-productionSee full patternSOC 2 CC7.2 requires the entity to monitor system components for anomalies indicative of malicious acts and errors, and availability monitoring is the same evidence an auditor pulls for A1.1: "show me what watches production and who gets paged" is a standard request in the incident-management section. AI-generated and vibe-coded apps fail this reliably because monitoring is an operations habit, not a code prompt: coding assistants scaffold the app, the deploy config, even a `/api/health` endpoint, but nothing ever watches that endpoint, so the first signal of an outage or an anomaly is a customer complaint. Detection time drives incident impact and, for a Type II report, an unmonitored production service means there is no anomaly-detection evidence to sample at all. One passing leg of this check is an evidence-pointer: dashboard-configured monitors (UptimeRobot, Better Stack, Vercel alerts set in the web UI) are invisible to a repo audit, so a committed ops doc naming the monitoring provider and what it watches counts as the evidence, which is exactly the artifact an auditor will ask for. Note the relationship to this audit's health-check-endpoint check: that check verifies the probe surface exists; this check verifies something is actually watching it.
Why this severity: High because an unwatched production service turns every outage and anomaly into a customer-reported incident, stretching detection time from minutes to hours or days, and leaves zero CC7.2 anomaly-detection evidence for the audit window; it is not critical because the gap enables slow response rather than direct exploitation.
soc2-readiness.incident-and-recovery.monitoring-and-alerting-configuredSee full patternNIST AU-2 requires that systems generate audit records for events sufficient to support incident investigation, and a health check endpoint is the single most important hook for external monitoring and orchestration systems. Without a dedicated health endpoint, uptime monitors are forced to check your homepage — which may return 200 even when your database is unreachable or your API is broken. Load balancers and container orchestrators (ECS, Kubernetes, Fly.io) also depend on health endpoints to route traffic and restart failed instances. A missing health check means your monitoring tools and infrastructure cannot reliably determine whether your application is actually functional.
Why this severity: Medium because without a health endpoint, uptime monitors check the wrong signal and load balancers cannot automatically isolate degraded instances.
saas-logging.monitoring.health-check-endpointSee full patternWhen production goes down at 2am, the difference between a 15-minute resolution and a 3-hour outage is whether your team has a documented incident runbook. NIST IR-8 requires incident response plans; SOC 2 CC9.1 requires risk mitigation procedures; NIST CSF RS.RP-1 requires response plan execution. Without defined severity levels, teams cannot triage — every problem looks like a P1. Without escalation contacts and communication channels, the on-call engineer is making high-stakes decisions alone with no authority to act.
Why this severity: Medium because the absence of an incident runbook degrades response quality significantly during outages, but its impact is realized only when an incident occurs, not on every deployment.
deployment-readiness.rollback-recovery.incident-runbookSee full patternThis is an evidence-pointer check: it verifies committed evidence of the backup and restore control, which is exactly what an auditor will ask for. It evidences SOC 2 A1.2 (backup and recovery infrastructure) and supports A1.3 and CC7.5 (recovery testing and incident recovery). Auditors do not accept "Supabase probably handles it": they want to see where backups are configured and, crucially, how a restore would actually be executed and by whom. AI-generated and vibe-coded apps typically fail both halves. The scaffolded stack leans on platform defaults nobody has confirmed (Supabase free tier has no PITR; daily backups are not restore rehearsals), and no human ever writes the restore runbook because "how do I restore prod" is not a prompt anyone types until the day the table is already dropped. The GitLab 2017 database incident is the canonical case: five backup mechanisms existed on paper, none restorable when needed, roughly six hours of production data lost. A committed IaC flag or ops doc plus a restore procedure is cheap insurance and the minimum artifact a SOC 2 readiness assessment expects.
Why this severity: Medium because missing backup and restore evidence does not itself expose data, but it turns any destructive incident (bad migration, dropped table, ransomware) into unbounded data loss and stalls the SOC 2 availability criteria until the evidence exists.
soc2-readiness.incident-and-recovery.backup-and-restore-posture-documentedSee full patternGDPR Art. 17 ("right to be forgotten") and CCPA §1798.105 both require that a user who asks for their account and personal data to be deleted actually gets it deleted — not hidden behind a `deletedAt` timestamp while the email, name, and history sit indefinitely in the database. CNIL fined Clearview AI €20M in 2022 in part for failing to act on erasure requests, and ICO enforcement routinely issues multi-million-pound penalties on the same grounds. Beyond regulatory exposure, the Apple App Store and Google Play Store have both mandated in-app account deletion since 2022: apps that gate deletion behind a support-email workflow get rejected at review. AI coding tools reliably scaffold signup, login, and password reset, then skip the deletion route entirely — because "how do I build auth" is a well-represented training pattern and "how do I build GDPR-compliant erasure" is not. The quiet failure mode is a live app that passes every happy-path test while accumulating unlawful retention exposure with every new user.
Why this severity: Critical because missing or stub-only deletion is a direct ongoing Art. 17 violation accruing exposure per active user per day, blocks App Store and Play Store approval outright, and cannot be patched retroactively against users who already asked and were ignored.
project-snapshot.legal.account-deletion-codedSee full patternA `console.log(user)`, `console.log(req.body)`, or `logger.info({ email, password })` inside a route handler ends up durably stored in Vercel Functions logs, CloudWatch, Datadog, Better Stack, or Sentry for days to months — searchable by every engineer, support agent, and third-party log processor with access, and harvestable in any breach of the logging pipeline itself. Slack (2023) leaked employee credentials through Sentry when raw auth objects were captured on exception; the T-Mobile 2023 class action ($350M) explicitly cited logged PII as a breach-scope amplifier. Under GDPR Art. 32 ("security of processing"), logging passwords, tokens, session IDs, or unredacted PII into a shared log pipeline is a processing-security failure whether or not the logs leak — and once they do, Art. 33-34 breach-notification timelines start. AI coding tools default to `console.log(req.body)` and `console.log(user)` during debugging and routinely leave those statements in on ship, because `grep console.log` is not part of the "ready to deploy" checklist that training data reinforces.
Why this severity: High because logged credentials are an immediate live credential-theft vector against the logging pipeline itself, and widen any downstream breach scope dramatically — small code change, disproportionate exposure reduction.
project-snapshot.legal.no-pii-in-server-logsSee full patternSOC 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.
Why this severity: 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.
soc2-readiness.data-lifecycle.no-real-pii-in-committed-fixturesSee full patternSOC 2 C1.2 requires that confidential information be disposed of when it is no longer needed, and "show me your retention schedule and the mechanism that enforces it" is a standard confidentiality-section request; this check evidences C1.2 directly (ISO 27001 A.8.10 and NIST SI-12 map the same obligation). AI-generated and vibe-coded apps fail this almost universally: coding assistants scaffold tables that only ever grow (users, sessions, events, messages, request logs) because every training example inserts and none deletes, so the default posture is indefinite retention of everything, which inflates breach blast radius, storage cost, and GDPR storage-limitation exposure with every day the app runs. One passing leg of this check is an evidence-pointer: where retention is enforced by a managed platform or an operations habit rather than repo-visible code, a committed retention schedule document naming data classes and periods is the evidence, which is exactly the artifact a SOC 2 auditor will ask for. This check is deliberately distinct from account-deletion-coded in this same audit: that one is user-initiated erasure on request (GDPR Art. 17); this one is systematic retention bounds for data nobody asked to delete.
Why this severity: Medium because unbounded retention does not itself expose data, but it silently compounds breach blast radius and regulatory exposure over time and leaves the C1.2 disposal criterion with zero evidence until a purge mechanism or schedule exists.
soc2-readiness.data-lifecycle.data-retention-or-purge-definedSee full patternApplication logs quietly accumulate confidential data: IP addresses, emails, session identifiers, request payloads. SOC 2 C1.2 requires that confidential information is disposed of when no longer needed, and unbounded log retention is the most common quiet violation of it (bounded retention also evidences CC7.2, since a log pipeline someone actually configured is one someone actually monitors). Auditors ask "how long do you keep logs, and where is that enforced": the answer must point at a rotation config, an IaC retention setting, or a platform whose defaults bound it. AI-generated and vibe-coded apps typically fail this because tutorials show `new winston.transports.File(...)` and never the lifecycle: the transport ships, the retention bound never does, and the log directory grows until the disk fills or a subpoena arrives. GDPR's storage-limitation principle (Art. 5(1)(e)) applies the same pressure from the privacy side. The good news is that the dominant deploy targets (Vercel, Netlify, Heroku) manage log retention themselves, so most stdout-only apps pass with a note; the check exists to catch the self-managed file and ELK/Loki setups where nobody ever set a bound.
Why this severity: Low because unbounded log retention is a slow-accruing confidentiality and disk exposure rather than an immediate breach vector, and platform-managed deployments bound it by default; the mechanism is retained confidential data with no disposal path.
soc2-readiness.data-lifecycle.log-retention-configuredSee full patternRun this audit in your AI coding tool (Claude Code, Cursor, Bolt, etc.) and submit results here for scoring and benchmarks.
Open SOC 2 Technical Readiness Audit