A 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.
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.
Strip every offending log call and replace it with a redacted equivalent. For structured loggers (pino, winston, Sentry), configure a redaction list:
// lib/logger.ts
import pino from 'pino'
export const logger = pino({
redact: { paths: ['req.body.password', 'req.body.token', 'user.email', 'user.ssn', '*.authorization'], censor: '[REDACTED]' },
})
For plain console.log, either delete the statement or replace the payload with a non-PII shape (console.log('[auth] login attempt', { userId: user.id, ok: true }) — ID only, never the email, token, or body). Deeper coverage of logging hygiene (log-retention limits, log-aggregator encryption, redaction testing) lives in the data-protection and security-hardening Pro audits.
project-snapshot.legal.no-pii-in-server-logshighapp/api/**/*.{ts,js}, pages/api/**/*.{ts,js}, app/**/route.{ts,js}, app/**/actions.{ts,js}, any .tsx file without 'use client', lib/**/*.{ts,js}, server/**/*.{ts,js}, middleware.ts. Skip node_modules, .next, dist, build, **/*.test.*, **/*.spec.*, **/__mocks__/*. Find calls matching any of: console.(log|error|warn|info|debug), logger.(info|warn|error|debug|trace), pino().*, winston.*, Sentry.captureException, Sentry.captureMessage, Sentry.setContext, Sentry.setUser (when the user argument is a full object rather than a sanitized { id }). Then classify each call's argument list. Fail-triggering argument shapes: a bare identifier matching /^(user|users|account|session|req|request|body|headers|cookies|token|tokens|password|secret|apiKey|api_key|creds|credentials|ssn|phone|address)$/; a property-access chain ending in one of those (req.body, req.headers, req.cookies, session.user); an object literal whose keys include any of email|password|token|session|apiKey|ssn|phone|address|firstName|lastName|fullName|dateOfBirth|dob. Non-triggering shapes: a string literal only (console.log('[auth] login attempt')), an object with only IDs ({ userId: 'abc' }, { reqId }), a redacted payload ({ email: '[REDACTED]' }). Count every match with file:line.app/api/, no pages/api/, no server actions, no middleware.ts, no server components importing a database. Quote: "No server-side code detected — purely static build."JSON.stringify (console.log(JSON.stringify(req.body))) — stringifying the object does not strip the PII, it serializes it. Also do not pass on commented-out // console.log(user) only if the commented line shipped uncommented anywhere else."app/api/auth/login/route.ts:17: console.log('login', req.body)". If zero matches, state "0 server-side log statements dump user / body / headers / session / credentials across N server files scanned.""Scanned <N> server-side files; 0 PII-leaking log calls; <M> sanitized log calls observed (ID-only payloads).""app/api/auth/login/route.ts:17 logs full req.body via console.log('login attempt', req.body) — credential exposure to Vercel log pipeline." or "app/api/webhook/route.ts:42 calls Sentry.captureException(err, { extra: { user } }) — full user object including email forwarded to Sentry."lib/logger.ts example in remediation_prose above). For plain console.log, delete or replace the payload with a non-PII shape. Deeper coverage lives in the data-protection and security-hardening Pro audits.