# PII excluded from application logs

- **Pattern:** `ab-001430` (`finserv-encryption.pci-alignment.pii-excluded-from-logs`)
- **Severity:** low
- **Lifecycle:** active
- **Last modified:** 2026-04-18
- **Canonical URL:** https://auditbuffet.com/patterns/ab-001430
- **License:** CC-BY-4.0 — attribute to AuditBuffet Pattern Catalog (https://auditbuffet.com/patterns/ab-001430)

## Why it matters

CWE-532 (insertion of sensitive information into log files) directly applies when account numbers, SSNs, or email addresses appear in application logs. Financial application logs are typically stored in cloud logging services (CloudWatch, Datadog, Splunk) with broader access than the production database — developers, support staff, and contractors may all have log read access without needing database access. PCI-DSS 4.0 Req-3.3 explicitly prohibits storing sensitive authentication data after authorization. OWASP 2021 A09 (Security Logging and Monitoring Failures) includes inadequate log sanitization. A single `console.log(transaction)` that dumps the full transaction object will log account numbers in plaintext to every log aggregator in the pipeline.

## Severity rationale

Low severity at this check level because PII in logs requires log access to exploit, but the compliance impact is equivalent to a data exposure event under PCI-DSS and most state breach notification laws.

## Remediation

Create masking utilities in `src/lib/logging.ts` and use them consistently in all transaction-path logging:

```typescript
// src/lib/logging.ts
export const mask = {
  accountNumber: (v: string) => `****${v.slice(-4)}`,
  ssn: (v: string) => `***-**-${v.slice(-4)}`,
  email: (v: string) => {
    const [local, domain] = v.split('@');
    return `${local[0]}***@${domain}`;
  },
  cardToken: (v: string) => `tok_****${v.slice(-4)}`,
};

// Usage:
logger.info('transfer_initiated', {
  account: mask.accountNumber(accountNumber),
  amount: transferAmount, // numeric, safe to log
  currency: 'USD',
});
```

Add a pre-commit lint rule or custom ESLint plugin that flags `console.log` or `logger.*` calls in `src/app/api/` routes that reference known PII field names (`ssn`, `accountNumber`, `cardToken`, `email`). Review all existing logs for historical PII exposure and rotate credentials if any appear.

## Detection

- **ID:** `pii-excluded-from-logs`
- **Severity:** `low`
- **What to look for:** Count all logging statements in financial transaction code paths. For each, enumerate the data fields logged and classify as PII or non-PII. Quote the actual field names logged. Count all PII masking/redaction functions. Any unmasked PII in logging must not pass — do not pass if any logging statement outputs full account numbers, SSNs, or email addresses.
- **Pass criteria:** Count all logging statements — 0 must log unmasked PII. At least 1 masking/redaction utility exists if PII fields are referenced in logs. Report the count even on pass (e.g., "12 logging statements checked, 0 unmasked PII, 2 masking functions: maskAccountNumber(), maskSSN()").
- **Fail criteria:** Any PII logged unmasked (even 1 instance of full account number, SSN, or email in logs).
- **Skip (N/A) when:** Never — logging PII is a compliance violation for any financial system.
- **Detail on fail:** `"3 of 12 logging statements log unmasked PII — full account_number in src/services/transfer.ts:45"` or `"Customer SSN logged unmasked in debug output at src/app/api/account/route.ts:23"`
- **Cross-reference:** Check `finserv-encryption.data-at-rest.aes256-encryption` for data encryption, and `finserv-encryption.pci-alignment.encryption-documentation` for compliance documentation.
- **Remediation:**
  - Create PII masking utilities:
    ```typescript
    const maskAccountNumber = (account: string) => {
      return account.slice(-4).padStart(account.length, '*');
    };
    const maskSSN = (ssn: string) => {
      return ssn.slice(-4).padStart(ssn.length, '*');
    };

    logger.info('Transaction', {
      account: maskAccountNumber(accountNumber),
      amount: 100,
    });
    ```
  - Review and redact existing logs:
    ```typescript
    // Log filter to remove PII
    const redactLogs = (logEntry: any) => {
      const redactedEntry = { ...logEntry };
      if (redactedEntry.ssn) delete redactedEntry.ssn;
      if (redactedEntry.accountNumber) redactedEntry.accountNumber = maskAccountNumber(redactedEntry.accountNumber);
      return redactedEntry;
    };
    ```

## External references

- cwe CWE-532
- owasp A09
- pci-dss Req-3.3
- nist SI-12

Taxons: cryptography-and-secrets, regulatory-conformance

HTML version: https://auditbuffet.com/patterns/ab-001430
