ESLint InterlaceESLint Interlace
Plugin: browser-securityRules

no-sensitive-data-in-analytics

ESLint rule documentation for no-sensitive-data-in-analytics

📡 Live from GitHub — This documentation is fetched directly from no-sensitive-data-in-analytics.md and cached for 6 hours.

Prevents PII being sent to analytics services

This rule detects when sensitive user data (email, SSN, credit card, password, phone, address) is passed to analytics...

Severity: 🟠 HIGH
CWE: CWE-359: Exposure of Private Personal Information to an Unauthorized Actor
OWASP Mobile: M6: Inadequate Privacy Controls

Rule Details

This rule detects when sensitive user data (email, SSN, credit card, password, phone, address) is passed to analytics tracking calls like analytics.track(). Analytics platforms may not provide adequate security for sensitive data, and data breaches in analytics services can expose user PII.

Why This Matters

Analytics platforms are third-party services with their own security postures. Sending PII to analytics:

  • Violates GDPR Article 6 (lawful processing)
  • Creates regulatory compliance risks (GDPR fines up to €20M or 4% of revenue)
  • Exposes data to third-party breaches (analytics provider compromises)
  • May violate user privacy expectations and consent agreements

❌ Incorrect

// Sending email to analytics
analytics.track('User Signup', {
  email: user.email, // ❌ PII in analytics
  userId: user.id,
});

// Sending credit card details
analytics.track('Payment', {
  creditCard: cardNumber, // ❌ PCI-DSS violation
  amount: total,
});

// Sending password (never!)
analytics.track('Login Attempt', {
  username: user.username,
  password: user.password, // ❌ Critical security violation
});

// Sending SSN
analytics.track('Profile Update', {
  ssn: user.ssn, // ❌ HIPAA/PII violation
  name: user.name,
});

✅ Correct

// Send only non-sensitive identifiers
analytics.track('User Signup', {
  userId: user.id, // ✅ Non-PII identifier
  timestamp: new Date(),
});

// Hash or mask sensitive data before tracking
analytics.track('Profile Update', {
  userId: user.id,
  hasEmail: !!user.email, // ✅ Boolean flag, not actual email
  emailDomain: user.email.split('@')[1], // ✅ Domain only (e.g., "gmail.com")
});

// Use event names and counts, not PII
analytics.track('Payment Completed', {
  userId: user.id,
  amount: total, // ✅ Transaction data, not PII
  currency: 'USD',
});

⚙️ Configuration

This rule has no configuration options.

Known False Negatives

The following patterns are not detected due to static analysis limitations:

PII from Variables or Function Returns

Why: We only analyze object literal properties passed directly to analytics.track(). Values from variables or function calls are not traced.

// ❌ NOT DETECTED - PII from variable
const userData = { email: user.email, phone: user.phone };
analytics.track('Event', userData);

Mitigation: Always review analytics tracking code manually for PII. Use TypeScript branded types to mark PII data.

Custom Analytics Wrappers

Why: We only detect analytics.track() calls. Custom wrapper functions are not recognized.

// ❌ NOT DETECTED - Custom wrapper
function trackUserEvent(data: any) {
  analytics.track('Event', data);
}
trackUserEvent({ email: user.email }); // PII in custom wrapper

Mitigation: Apply this rule to wrapper function implementations. Document analytics wrappers in code review guidelines.

Dynamic Property Names

Why: Properties accessed via bracket notation or computed property names cannot be statically analyzed.

// ❌ NOT DETECTED - Dynamic property
const field = 'email';
analytics.track('Event', {
  [field]: user[field], // Dynamic, can't determine if PII
});

Mitigation: Avoid dynamic property names for analytics tracking. Use explicit, static property names.

📚 References

On this page

No Headings