Skip to main content
interlace
Plugin: anthropic-securityRules

no-hardcoded-api-key

Forbid a literal API key in the Anthropic client options

Forbid a literal API key in the Anthropic client options.

Why

A key written into source is committed, pushed, mirrored into every clone and CI cache, and is billable by anyone who reads it. Because rotating it means a code change, leaked keys tend to stay live far longer than they should.

Covers every credential option the @anthropic-ai/* clients take: apiKey, authToken, webhookKey, the Bedrock / AWS awsSecretKey, awsSecretAccessKey and awsSessionToken, and the Vertex accessToken. The AWS access key id is left out — it identifies, the secret authenticates.

What counts as a hardcoded key

  • Only the SDK's own client. The callee must resolve, through its import or require(), to the SDK — a constructor, a factory the SDK exports, client.withOptions(...), a same-file subclass (super(...) included), or a same-file wrapper that passes its options parameter straight into the client. new Pinecone({ apiKey }) in the same file, a test matcher such as expect.objectContaining({ apiKey }), and the Vercel AI SDK's provider factories (owned by vercel-ai-security) are not this rule's.
  • Key-shaped values only. A credential is a generated token: at least 20 characters, no whitespace, letters and digits mixed. Stand-ins the SDKs' own tests and docs use — 'my-anthropic-api-key', 'test-key', '...', 'ollama', '<YOUR_API_KEY>', 'sk-xxxxxxxx…' — are not reported.
  • Followed within the file. A key written as an environment fallback (process.env.KEY ?? 'sk-…', || 'sk-…', or a ternary branch) is committed exactly like a bare one and is reported. A key held in a const, an options object held in a const, shorthand { apiKey }, and a same-file spread ({ ...base }) are read. A reassigned let, a parameter, or an import is not followed.

Incorrect

import Anthropic from '@anthropic-ai/sdk';
const client = new Anthropic({ apiKey: 'sk-ant-api03-...' });

Correct

import Anthropic from '@anthropic-ai/sdk';
const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });

Use this rule

npm install -D oxlint-plugin-anthropic-security
.oxlintrc.json
{  "jsPlugins": ["oxlint-plugin-anthropic-security"],  "rules": {    "anthropic-security/no-hardcoded-api-key": "error"  }}
vite.config.ts
import { defineConfig } from 'vite-plus';export default defineConfig({  lint: {    jsPlugins: ['oxlint-plugin-anthropic-security'],    rules: {      'anthropic-security/no-hardcoded-api-key': 'error',    },  },});

oxlint-plugin-anthropic-security re-exports eslint-plugin-anthropic-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.