Skip to main content
interlace
Plugin: vercel-ai-securityRules

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

PropertyValue
Typeproblem
Severity🔴 CRITICAL
OWASP LLMLLM02: Sensitive Information Disclosure
CWECWE-200: Information Exposure
CVSS8.5
Config Defaulterror (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

OptionTypeDefaultDescription
sensitivePatternsstring[]["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

🔄 Changes in the 2026-10-10 FP/FN audit

  • Names are matched by whole word: businessName has no ssn word, and maxTokens / totalTokens are 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 as user.password.
  • Options: the documented defaultOptions now apply. Earlier versions read context.options and 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 of x's type decide;
    • without it, x must 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/postgres or better-sqlite3 client with no column projection (Prisma select/omit, select(cols), or SQL other than SELECT *).
  • A field read (${user.name}) and chat history spread into messages are 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
.oxlintrc.json
{  "jsPlugins": ["oxlint-plugin-vercel-ai-security"],  "rules": {    "vercel-ai-security/no-sensitive-in-prompt": "error"  }}
vite.config.ts
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.