no-sensitive-in-prompt
This rule identifies code patterns where sensitive data like passwords, API keys, tokens, or personally identifiable ...
Prevents sensitive data (passwords, tokens, PII) from being sent to LLMs.
📊 Rule Details
| Property | Value |
|---|---|
| Type | problem |
| Severity | 🔴 CRITICAL |
| OWASP LLM | LLM02: Sensitive Information Disclosure |
| CWE | CWE-200: Information Exposure |
| CVSS | 8.5 |
| Config Default | error (recommended, strict) |
🔍 What This Rule Detects
This rule identifies code patterns where sensitive data like passwords, API keys, tokens, or personally identifiable information (PII) is passed to AI prompts. LLM providers may log, store, or use this data for training.
❌ Incorrect Code
// Password in prompt
await generateText({
prompt: `Reset password for user. Current password: ${userPassword}`,
});
// API key in prompt
await generateText({
prompt: `Configure service with key: ${apiKey}`,
});
// SSN in prompt
await streamText({
prompt: `Process application for SSN: ${socialSecurityNumber}`,
});
// Credit card in prompt
await generateText({
prompt: `Validate credit card: ${creditCardNumber}`,
});✅ Correct Code
// Redacted data
await generateText({
prompt: `Reset password for user. Current password: [REDACTED]`,
});
// No sensitive data
await generateText({
prompt: `Configure service with the API key stored in environment variables.`,
});
// Use redaction helper
await streamText({
prompt: `Process application for SSN: ${redact(socialSecurityNumber)}`,
});
// Reference instead of value
await generateText({
prompt: `Validate the credit card on file for user ${userId}`,
});⚙️ Options
| Option | Type | Default | Description |
|---|---|---|---|
sensitivePatterns | string[] | ["password","secret","apiKey","api_key","token","credential","ssn","socialSecurity","creditCard","cardNumber","cvv","privateKey","private_key","accessToken","access_token","refreshToken","refresh_token","authToken","bearer","connectionString","dbPassword","dbUser"] | Variable patterns that suggest sensitive data |
🛡️ Why This Matters
Sending sensitive data to LLMs can result in:
- Data breach - LLM providers may store prompts
- Training data poisoning - Your data may be used to train models
- Compliance violations - GDPR, HIPAA, PCI-DSS violations
- Third-party exposure - Data shared with third-party AI providers
🔗 Related Rules
no-hardcoded-api-keys- Prevent hardcoded credentialsrequire-output-filtering- Filter sensitive tool output
🔄 Changes in the 2026-10-10 FP/FN audit
- Names are matched by whole word:
businessNamehas nossnword, andmaxTokens/totalTokensare counts of tokens, not tokens. messages: [...]is searched: array elements, spread elements, and the values of message objects ({ role, content: `… ${user.password}` }).user['password']is the same property asuser.password.- Options: the documented
defaultOptionsnow apply. Earlier versions readcontext.optionsand silently ran a shorter hard-coded list instead.
🔄 Changes in the 2026-10-11 zero-deferral pass
- A whole record embedded in a prompt —
JSON.stringify(x),String(x)or${x}— is reported when its fields include a sensitive one:- with type information (
parserOptions.projectService), the declared property names ofx's type decide; - without it,
xmust resolve in the file to an object literal with a sensitive key, or to a full database row: a read from a Prisma, pg, postgres, mysql2, knex, kysely, drizzle,@vercel/postgresor better-sqlite3 client with no column projection (Prismaselect/omit,select(cols), or SQL other thanSELECT *).
- with type information (
- A field read (
${user.name}) and chat history spread intomessagesare not records and are not reported.
Known False Negatives
The following patterns are not detected due to static analysis limitations:
Variable Names Not Matching Patterns
Why: Only configured sensitive patterns are checked.
// ❌ NOT DETECTED - Custom field name
await generateText({
prompt: `Process data: ${mySecretField}`, // Not in sensitivePatterns
});Mitigation: Configure sensitivePatterns with custom field names.
Dynamic Prompt Construction
Why: Prompts built at runtime are not analyzed.
// ❌ NOT DETECTED - Dynamic prompt
const fields = [userPassword, apiKey];
const prompt = fields.join(', ');
await generateText({ prompt });Mitigation: Never concatenate sensitive data into prompts.
Nested Object Properties
Why: Deep property access may not be recognized.
// ❌ NOT DETECTED - Nested property
await generateText({
prompt: `Reset with: ${user.credentials.password}`,
});Mitigation: Configure patterns to match nested sensitive fields.
Encrypted/Transformed Data
Why: Transformed data appears safe but may be sensitive.
// ❌ NOT DETECTED - Encrypted but still sensitive
await generateText({
prompt: `Decrypt this: ${encryptedPassword}`,
});Mitigation: Never send any form of credentials to LLMs.
📚 References
Use this rule
npm install -D oxlint-plugin-vercel-ai-security{ "jsPlugins": ["oxlint-plugin-vercel-ai-security"], "rules": { "vercel-ai-security/no-sensitive-in-prompt": "error" }}import { defineConfig } from 'vite-plus';export default defineConfig({ lint: { jsPlugins: ['oxlint-plugin-vercel-ai-security'], rules: { 'vercel-ai-security/no-sensitive-in-prompt': 'error', }, },});oxlint-plugin-vercel-ai-security re-exports eslint-plugin-vercel-ai-security/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.