no-pii-in-logs
Prevent personally identifiable information (PII) — emails, SSNs, credit cards, phone numbers — from reaching console / logger output.
Keywords: no-pii-in-logs, PII, GDPR, HIPAA, CCPA, console.log, logger, email, SSN, credit card, phone number, CWE-359, ESLint rule CWE: CWE-359: Exposure of Private Personal Information to an Unauthorized Actor
Prevent personally identifiable information (PII) — emails, SSNs, credit cards, phone numbers — from reaching console.* or logger output. Logs are routinely shipped to third-party platforms (Datadog, Sentry, CloudWatch) and indexed by people who never had access authorization for that data.
This rule is part of eslint-plugin-secure-coding.
Quick Summary
| Aspect | Details |
|---|---|
| Severity | High (Security / Compliance — GDPR Art. 32, HIPAA §164.312) |
| Auto-Fix | 💡 Suggestions (redact, hash, or replace with stable user id) |
| Category | Secure Coding |
| CWE | CWE-359 |
| Best For | Any service that handles user data and ships logs off-host |
Why this matters
Application logs are the highest-volume, lowest-trust egress channel in most production services. By default they are:
- Shipped to vendors (Datadog, Sentry, Splunk, Loki, ELK) — broadening the trust boundary.
- Retained for weeks to months — long enough that a breach years from now still discloses today's data.
- Read by anyone with engineering oncall — not the access-controlled subset that touches the user record itself.
GDPR Article 32 (security of processing) and HIPAA §164.312 (technical safeguards) both treat log-leaked PII as a reportable incident. Most breach disclosures in 2024–2025 traced the data exposure to a logger, not a database.
What the rule detects
The rule flags PII patterns whether they enter the log call as a literal, a templated string, or a member access on an user/person/account identifier:
| Pattern | Detected as |
|---|---|
| Hard-coded email regex match | email |
user.email, account.ssn | inferred via identifier shape |
| 16-digit number passing Luhn | credit-card |
| ITU-formatted phone number | phone |
US SSN (\d{3}-\d{2}-\d{4}) | ssn |
Examples
❌ Incorrect
console.log('user signed in', user.email, user.phone);
logger.info(`payment from ${customer.creditCard}`);
console.error('lookup failed for SSN', record.ssn);
// PII concealed in JSON.stringify still gets logged
logger.debug(JSON.stringify(user));✅ Correct
import { redactPii, hashStable } from '@/lib/pii';
console.log('user signed in', { userId: user.id });
logger.info('payment authorised', { customerId: hashStable(customer.id) });
console.error('lookup failed', { ssnHash: hashStable(record.ssn) });
// Or use a structured logger that redacts known PII fields by allowlist
logger.debug({ user: redactPii(user) });Error Message Format
🔒 SECURE-CODING CWE-359 | PII detected in log argument | HIGH
Fix: Replace the PII payload with a stable opaque id (UUID, hash) or pass through a redacting logger before emission.Known False Negatives
- Custom logger wrappers (
logEvent,audit,track) are detected via theloggersrule option allowlist; loggers not on the allowlist are skipped. - PII reached via aliasing (
const e = user.email; log(e);) is tracked one assignment hop; deeper chains require type-aware enabling. - PII embedded inside an HTTP request body that incidentally surfaces in an error message (e.g.
JSON.stringify(req.body)in acatchblock) is not always reached without type info.
Did this rule catch something? Star the repo to get new CWE coverage as we ship it — or follow the AI-code-security benchmarks behind these rules.