Logging a full Stripe error object or a payment form's event.target captures card BIN data, error responses that reference card numbers, and provider-internal identifiers — all of which end up in browser consoles, error tracking tools, and analytics pipelines. PCI-DSS 4.0 Req 3.3 prohibits retaining sensitive authentication data; CWE-312 (Cleartext Storage of Sensitive Information) and CWE-532 (Insertion of Sensitive Information into Log File) both apply. OWASP A09 (Security Logging and Monitoring Failures) captures the dual failure: bad logging that records sensitive data while simultaneously failing to record operationally useful signals. A Sentry breadcrumb that captures form data can expose card numbers in your error dashboard to any team member with access.
Medium because payment-related console logs and analytics events can capture card BIN data and provider error details, creating PCI-scope violations in browser consoles and third-party monitoring tools.
Log structured error codes only — never raw error objects, form values, or full Stripe response objects.
// WRONG — full error object may contain card details
console.log('Payment failed:', error)
analytics.track('payment_failed', { error })
// CORRECT — log only the code and type
console.error('Payment failed', { code: error.code, type: error.type })
analytics.track('payment_failed', {
error_code: error.code, // e.g., 'card_declined'
error_type: error.type, // e.g., 'card_error'
})
If you use Sentry, add a beforeSend filter in your sentry.client.config.ts to strip request.data from events originating on checkout routes. Never pass error, event.target, or any form-derived object directly to a logging or analytics call.
ID: ecommerce-payment-security.fraud-prevention.no-card-logging
Severity: medium
What to look for: Count every console.log, console.error, console.warn, and analytics event call (e.g., analytics.track(), posthog.capture(), mixpanel.track(), gtag()) within or near payment-related code (within at least 50 lines of payment logic). For each, classify the logged data as safe (error codes, status strings) or unsafe (form values, full error objects, Stripe response objects). Also check error tracking tools (Sentry, Datadog) for breadcrumb or context configuration that might capture form input values.
Pass criteria: No console.log or analytics calls in payment-related code include card numbers, expiry dates, CVC codes, or full Stripe error objects with card details. No more than 0 unsafe logging calls should exist. Error logging captures error types and codes, not card data.
Fail criteria: Any console.log(formData), console.log(error), or analytics event includes card field values or raw Stripe error responses that contain card information.
Skip (N/A) when: Never — this check applies to all projects with client-side payment handling.
Detail on fail: Describe the logging location and what is exposed. Example: "console.log('Payment error:', error) in components/checkout/PaymentForm.tsx logs the full Stripe error object including card BIN data on payment failures"
Remediation: Log error codes and types, never raw error objects or form values:
// WRONG — logs full error object which may contain card details
console.log('Payment failed:', error)
analytics.track('payment_failed', { error })
// CORRECT — log only the code and message
console.error('Payment failed', { code: error.code, type: error.type })
analytics.track('payment_failed', {
error_code: error.code, // e.g., 'card_declined'
error_type: error.type, // e.g., 'card_error'
// Never include: card number, expiry, CVC, or full error object
})
If using Sentry, ensure beforeSend filters sensitive data from payment-related breadcrumbs.