require-data-minimization
Identifies excessive data collection patterns that violate privacy principles
Identifies excessive data collection patterns that violate privacy principles
Severity: 🟡 MEDIUM
CWE: CWE-213: Exposure of Sensitive Information Due to Incompatible Policies
OWASP Mobile: M6: Inadequate Privacy Controls
Rule Details
This rule detects when applications collect excessive user data. It reports an object literal that:
- writes more than
maxPropertiesproperties (default 10). A spread (...rest) copies a set the code does not show, so it does not count; - includes one of
piiFieldsas a key, in any spelling (email,'email',['email']); and - collects: at least one value is read at run time. An object of literals only is configuration and is not reported.
Which fields are personal is a fact about your schema, so the rule ships with no piiFields and, until you name some, reports nothing.
Why This Matters
Collecting excessive user data increases privacy risks and regulatory compliance burden. GDPR Article 5(1)(c) and CCPA Section 1798.100(c) require organizations to minimize data collection to only what is necessary for the stated purpose. Excessive data collection:
- Increases attack surface for data breaches
- Creates regulatory compliance risks (GDPR fines up to €20M or 4% of revenue)
- Violates user privacy expectations
- Requires additional security controls and retention policies
❌ Incorrect
// Collecting excessive user data (15 properties)
const userData = {
email: req.body.email,
name: req.body.name,
phone: req.body.phone,
address: req.body.address,
age: req.body.age,
gender: req.body.gender,
occupation: req.body.occupation,
income: req.body.income,
education: req.body.education,
maritalStatus: req.body.maritalStatus,
hobbies: req.body.hobbies,
favoriteColor: req.body.favoriteColor,
shoeSize: req.body.shoeSize,
bloodType: req.body.bloodType,
mothersMaidenName: req.body.mothersMaidenName,
};
// Collecting data not needed for the feature
async function createNewsletterSubscription(user: any) {
return db.subscribers.create({
email: user.email,
name: user.name,
phone: user.phone, // ❌ Not needed for newsletter
address: user.address, // ❌ Not needed
ssn: user.ssn, // ❌ Definitely not needed!
creditCard: user.creditCard, // ❌ Not needed
});
}✅ Correct
// Collect only necessary data for newsletter subscription
const newsletterData = {
email: req.body.email,
preferredName: req.body.name, // Optional, for personalization
};
// Separate data collection for different purposes
async function createNewsletterSubscription(email: string, name?: string) {
return db.subscribers.create({
email, // Required for sending newsletter
name, // Optional, for "Hi {name}" personalization
subscribedAt: new Date(),
});
}
// Collect data progressively based on actual need
async function updateUserProfile(
userId: string,
updates: Partial<UserProfile>,
) {
// Only collect additional data when user explicitly provides it
return db.users.update(userId, updates);
}Known False Negatives
The following patterns are not detected due to static analysis limitations:
Data Collection via Spread Operators
Why: Spread operators copy all properties, but we can't analyze the source object statically, so a spread does not count toward maxProperties.
// ❌ NOT DETECTED - Spread may copy excessive fields
const userData = {
email: req.body.email,
...req.body, // Copies all fields from request
};Mitigation: Explicitly list required fields instead of using spread operators for user data.
Data Collection in Class Constructors
Why: We only analyze object literals, not class instantiation patterns.
// ❌ NOT DETECTED - Constructor parameters
class User {
constructor(
public email: string,
public name: string,
public phone: string,
public address: string,
// ... 10 more properties
) {}
}Mitigation: Review class constructors manually for data minimization compliance.
Dynamic Property Assignment
Why: Properties added via bracket notation or Object.assign are not statically analyzable.
// ❌ NOT DETECTED - Dynamic assignment
const userData = {};
Object.assign(userData, req.body); // Copies all fieldsMitigation: Use explicit property access and validation schemas (e.g., Zod, Joi).
⚙️ Configuration
| Option | Type | Default | Description |
|---|---|---|---|
piiFields | string[] | [] | Property names this project treats as personal data. Empty: the rule stays silent |
maxProperties | number | 10 | How many written properties in one object literal count as excessive breadth |
{
rules: {
'operability/require-data-minimization': ['warn', { piiFields: ['email', 'phone', 'ssn'], maxProperties: 8 }]
}
}🔗 Related Rules
no-sensitive-data-in-analytics- Prevent sending PII to analyticsno-pii-in-logs- Prevent logging sensitive user data
📚 References
- GDPR Article 5(1)(c) - Data Minimization
- CCPA Section 1798.100 - Right to Know
- CWE-213: Exposure of Sensitive Information
- OWASP Mobile Top 10: M6 - Inadequate Privacy
Use this rule
npm install -D oxlint-plugin-operability{ "jsPlugins": ["oxlint-plugin-operability"], "rules": { "operability/require-data-minimization": "error" }}import { defineConfig } from 'vite-plus';export default defineConfig({ lint: { jsPlugins: ['oxlint-plugin-operability'], rules: { 'operability/require-data-minimization': 'error', }, },});oxlint-plugin-operability re-exports eslint-plugin-operability/oxlint: the same rules under the same IDs, and no ESLint install. Why oxlint.
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.