no-tracking-without-consent
CWE-359
Quick Summary
| Aspect | Details |
|---|---|
| Severity | Medium (Privacy Violation) |
| Auto-Fix | ❌ No (requires manual review) |
| Category | Security |
| ESLint MCP | ✅ Optimized for ESLint MCP integration |
| Best For | Frontend applications handling user analytics |
| Suggestions | ✅ Advice on implementing consent wrappers |
Vulnerability and Risk
Vulnerability: Privacy violation occurs when an application tracks user behavior or collects personal data without obtaining explicit consent. This is a direct violation of international privacy laws.
Risk: Tracking users without permission can lead to massive legal fines (up to 4% of global turnover for GDPR), loss of user trust, and potential removal from app stores.
Error Message Format
The rule provides LLM-optimized error messages (Compact 2-line format) with actionable security guidance:
🔒 CWE-359 OWASP:M6 | Tracking Without Consent detected | MEDIUM [GDPR,CCPA,ePrivacy]
Fix: Wrap tracking calls in consent check: if (hasConsent) { analytics.track(...) } | https://cwe.mitre.org/data/definitions/359.htmlMessage Components
| Component | Purpose | Example |
|---|---|---|
| Risk Standards | Security benchmarks | CWE-359 OWASP:M6 |
| Issue Description | Specific vulnerability | Tracking Without Consent detected |
| Severity & Compliance | Impact assessment | MEDIUM [GDPR,CCPA,ePrivacy] |
| Fix Instruction | Actionable remediation | Wrap tracking calls in consent check |
| Technical Truth | Official reference | Privacy Violation |
Rule Details
Privacy regulations worldwide require that users provide informed consent before their personal data or behavior is tracked. This rule flags calls to common tracking and analytics libraries that are not explicitly wrapped in a conditional block, which is the standard pattern for checking user consent status.
Why This Matters
| Issue | Impact | Solution |
|---|---|---|
| 🔒 Compliance | Massive regulatory fines | Use opt-in consent mechanisms (GDPR/CCPA) |
| 🤝 Trust | Brand reputation damage | Be transparent about data collection |
| ⚖️ Legal | Class action lawsuits | Implement strictly enforced tracking filters |
Configuration
| Option | Type | Default | Description |
|---|---|---|---|
consentIdentifiers | string[] | ["consent","gdpr","optin","cookiesaccepted","trackingallowed"] | Words that mark an identifier as a consent flag; replaces the default vocabulary |
Consent has no API to bind to — it is a boolean the product decides on — so the guard is recognised by NAME. That name test can only ever silence a finding, never produce one, so getting the vocabulary wrong costs recall rather than trust. It is configurable for exactly that reason:
{
"rules": {
"browser-security/no-tracking-without-consent": [
"error",
{ "consentIdentifiers": ["privacyOk", "cmpAccepted"] }
]
}
}Examples
❌ Incorrect
// Direct tracking call without consent check
analytics.track('Item Purchased', { price: 9.99 });
// Direct identification call
analytics.identify('user_123', { email: 'user@example.com' });
// Global GA tracking call
gtag('event', 'login');
// A guard that is about something else entirely
if (isMobile) {
analytics.track('Item Purchased');
}
// The branch where consent was REFUSED
if (!hasConsent) {
analytics.track('Item Purchased');
}
if (hasConsent) {
renderBanner();
} else {
gtag('event', 'login');
}✅ Correct
// Tracking call wrapped in a consent check
if (userHasConsented) {
analytics.track('Item Purchased', { price: 9.99 });
}
// Using a ternary for conditional tracking
userConsentGiven
? gtag('event', 'conversion')
: console.log('Tracking skipped');
// Encapsulated tracking function (logic inside)
function trackEvent(name, data) {
if (getConsentStatus()) {
analytics.track(name, data);
}
}
// Short-circuit
hasConsent && analytics.track('Item Purchased');
// Early return — the idiomatic spelling
function report(name) {
if (!hasConsent) return;
analytics.track(name);
}Known False Negatives
The following patterns are not detected due to static analysis limitations:
Abstracted Consent Logic
Why: If the consent check is deep within a helper function or a custom library, this rule cannot see it — the guard has to be visible from the call site, either as an enclosing if / ternary / &&, or as an earlier if (!consent) return; in the same block.
// This will be flagged even if myTracker handles consent internally
myTracker.track('test'); // ❌ NOT DETECTED (as safe)Mitigation: Ensure that tracking calls are either wrapped in a local check or that the custom tracking functions are added to the list of "safe" patterns if the rule is extended.
Non-Standard Libraries
Why: This rule specifically looks for analytics and gtag. Custom tracking implementations or lesser-known libraries will not be detected.
Mitigation: Manually review all third-party scripts and internal tracking code to ensure they respect the user's consent choice.
References
- CWE-359: Privacy Violation
- GDPR Articles 6 & 7 (Consent)
- Segment.js - Managing User Consent
- Google Analytics - User Consent State
⚙️ Options
| Option | Type | Default | Description |
|---|---|---|---|
consentIdentifiers | string[] | ["consent","gdpr","optin","cookiesaccepted","trackingallowed"] | Words that mark an identifier as a consent flag; replaces the default vocabulary |
analyticsMethods | string[] | ["track","identify","page"] | Method names on an analytics client that count as a tracking call |
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.