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

no-hardcoded-api-keys

This rule identifies hardcoded API keys, tokens, and secrets in your codebase that are used with AI SDK providers

Detects hardcoded API keys and secrets in AI SDK configuration.

πŸ“Š Rule Details

PropertyValue
Typeproblem
SeverityπŸ”΄ CRITICAL
OWASP AgenticASI03: Identity & Privilege Abuse
CWECWE-798: Use of Hard-coded Credentials
CVSS9.8
Config Defaulterror (recommended, strict)

πŸ” What This Rule Detects

This rule identifies hardcoded API keys, tokens, and secrets in your codebase that are used with AI SDK providers. Hardcoded credentials in source code can be exposed through version control, logs, or client bundles.

❌ Incorrect Code

// Hardcoded OpenAI key
const openai = createOpenAI({
  apiKey: 'sk-proj-abc123xyz789...',
});

// Hardcoded in provider function
const model = openai('gpt-4', {
  apiKey: 'sk-1234567890abcdefghij',
});

// Hardcoded Anthropic key
const anthropic = createAnthropic({
  apiKey: 'sk-ant-api03-abcdefghijklmnop',
});

// Hardcoded Google API key
const google = createGoogle({
  apiKey: 'AIzaSyA1234567890abcdefghij',
});

βœ… Correct Code

// Environment variable
const openai = createOpenAI({
  apiKey: process.env.OPENAI_API_KEY,
});

// Config object from environment
const anthropic = createAnthropic({
  apiKey: config.anthropicApiKey,
});

// Dynamic retrieval
const google = createGoogle({
  apiKey: getSecret('GOOGLE_API_KEY'),
});

// No explicit key (uses OPENAI_API_KEY env var by default)
const openai = createOpenAI();

βš™οΈ Options

OptionTypeDefaultDescription
apiKeyPatternsstring[]["apiKey","api_key","token","secret","credentials","authorization"]Property names that contain API keys

πŸ›‘οΈ Why This Matters

Hardcoded API keys can be:

  • Exposed in git history - Even if deleted, keys remain in commit history
  • Leaked in logs - Stack traces and error messages may expose keys
  • Bundled in client code - Build processes may include keys in client bundles
  • Shared accidentally - Code sharing, screenshots, or screen recordings may expose keys

πŸ”„ Changes in the 2026-10-10 FP/FN audit

  • Provider-shaped keys are reported wherever they appear β€” a module constant, a header value ('x-api-key', Authorization: 'Bearer sk-…'), an array element: sk-…/sk-proj-…/sk-ant-…, AIza…, gsk_…, hf_…, r8_…, xai-…, pplx-…, AKIA…. The body must contain a digit, so sk-loading-spinner is not a key.
  • Generic long strings are reported only under a key-ish name (whole word of apiKeyPatterns, now including authorization) and only if they are not a URL, a multi-segment resource path, or the NAME of an environment variable (OPENAI_API_KEY_PRODUCTION).
  • One literal is reported once (it used to be reported twice when passed as a provider's second argument).
  • Options: the documented defaultOptions now apply. Earlier versions read context.options and silently ran a shorter hard-coded list instead.

Known False Negatives

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

Encoded/Obfuscated Keys

Why: Base64 or other encoded keys are not decoded.

// ❌ NOT DETECTED - Encoded key
const key = Buffer.from('c2stcHJvai1hYmM...', 'base64').toString();
const openai = createOpenAI({ apiKey: key });

Mitigation: Never obfuscate keys. Use proper secrets management.

Keys from Config Files

Why: Keys imported from config files are not visible.

// ❌ NOT DETECTED - Key from import
import { apiKeys } from './config';
const openai = createOpenAI({ apiKey: apiKeys.openai });

Mitigation: Apply rule to config files. Use environment variables.

Template Literal Construction

Why: Keys built from parts may not be recognized.

// ❌ NOT DETECTED - Constructed key
const openai = createOpenAI({
  apiKey: `sk-${projectId}-${keyPart}`,
});

Mitigation: Never construct keys dynamically.

πŸ“š References

Error Message Format

The rule provides LLM-optimized error messages (Compact 2-line format) with actionable security guidance:

πŸ”’ CWE-798 OWASP:A04 CVSS:9.8 | Hardcoded Credentials detected | CRITICAL [SOC2,PCI-DSS,HIPAA,GDPR,ISO27001,NIST-CSF]
   Fix: Review and apply the recommended fix | https://owasp.org/Top10/A04_2021/

Message Components

ComponentPurposeExample
Risk StandardsSecurity benchmarksCWE-798 OWASP:A04 CVSS:9.8
Issue DescriptionSpecific vulnerabilityHardcoded Credentials detected
Severity & ComplianceImpact assessmentCRITICAL [SOC2,PCI-DSS,HIPAA,GDPR,ISO27001,NIST-CSF]
Fix InstructionActionable remediationFollow the remediation steps below
Technical TruthOfficial referenceOWASP Top 10

Use this rule

npm install -D oxlint-plugin-vercel-ai-security
.oxlintrc.json
{  "jsPlugins": ["oxlint-plugin-vercel-ai-security"],  "rules": {    "vercel-ai-security/no-hardcoded-api-keys": "error"  }}
vite.config.ts
import { defineConfig } from 'vite-plus';export default defineConfig({  lint: {    jsPlugins: ['oxlint-plugin-vercel-ai-security'],    rules: {      'vercel-ai-security/no-hardcoded-api-keys': '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.