Back to Blog
critical SEVERITY9 min read

How Hardcoded API Keys happen in JavaScript plugins and how to fix them

A critical hardcoded API key was discovered in `plugins/ocr.js` at line 21, where the OCR integration used a plaintext fallback credential `'K81241004488957'` whenever the `OCR_API_KEY` environment variable was absent. This exposed a live API key to anyone with repository access, enabling unauthorized use of the OCR service. The fix removes the hardcoded fallback entirely and fails fast with a clear error message when the required environment variable is not configured.

O
By Orbis AppSec
Published August 26, 2026Reviewed August 26, 2026

Answer Summary

This is a hardcoded secret vulnerability (CWE-798) in a JavaScript OCR plugin (`plugins/ocr.js`), where the API key `'K81241004488957'` was embedded as a fallback value in a `form.append()` call. Any attacker with source code access — through a public repo, leaked archive, or reverse engineering — could extract and abuse this key. The fix removes the fallback literal and adds an early-exit guard that returns an error message when `process.env.OCR_API_KEY` is not set, ensuring credentials are never baked into the codebase.

Vulnerability at a Glance

cweCWE-798
fixRemove the hardcoded fallback and add an environment variable presence check before the API call
riskUnauthorized access to the OCR API service; credential theft via source code exposure
languageJavaScript (Node.js)
root cause`form.append('apikey', process.env.OCR_API_KEY || 'K81241004488957')` embeds a live API key as a fallback literal
vulnerabilityHardcoded API Key / Hardcoded Secret

How Hardcoded API Keys Happen in JavaScript Plugins and How to Fix Them


The Vulnerability at a Glance

Field Detail
Vulnerability Hardcoded API Key / Hardcoded Secret
CWE CWE-798: Use of Hard-coded Credentials
Language JavaScript (Node.js)
Risk Unauthorized OCR API access; credential theft via source code
Root Cause form.append('apikey', process.env.OCR_API_KEY \|\| 'K81241004488957')
Fix Remove fallback literal; fail fast when env var is absent

Direct Answer

This is a hardcoded secret vulnerability (CWE-798) in plugins/ocr.js. The API key 'K81241004488957' was embedded as a fallback value in a form.append() call. Any attacker with source code access could extract and abuse this key. The fix removes the fallback and adds an early-exit guard: if process.env.OCR_API_KEY is not set, the function replies with an error message and stops execution before the API call is made.


Introduction

The plugins/ocr.js file handles optical character recognition requests — a user sends an image, the plugin downloads it, submits it to an external OCR API, and returns the extracted text. It's a convenient feature, but a single line of convenience-oriented code turned it into a critical security liability:

form.append('apikey', process.env.OCR_API_KEY || 'K81241004488957')

The || operator here is doing what JavaScript developers use it for all the time: providing a default value when the left side is falsy. In most contexts, that's perfectly reasonable. In the context of API credentials, it means a live, functional API key — K81241004488957 — is sitting in plaintext inside the source file, ready to be discovered by anyone who can read the code.

This pattern is so common it has its own CWE entry: CWE-798, Use of Hard-coded Credentials. It's also one of the most consistently exploited categories in real-world breaches, precisely because it feels harmless when you write it.


The Vulnerability Explained

What Actually Happened

At line 21 of plugins/ocr.js, the OCR plugin constructs a multipart form submission to an external OCR API service. The apikey field is populated using this expression:

// VULNERABLE — line 21 of plugins/ocr.js
form.append('apikey', process.env.OCR_API_KEY || 'K81241004488957')

The intent was likely: "Use the environment variable in production, but have a fallback so the feature works during development or if someone forgets to set the variable." That's a reasonable developer concern. The execution, however, is dangerous.

The fallback 'K81241004488957' is not a placeholder. It is a real API key that was almost certainly used during development or testing. Once committed to the repository, it becomes permanently accessible in the git history, even if the line is later changed.

How This Gets Exploited

The attack chain here is short — rated as a 2-step chain in the vulnerability assessment:

  1. Step 1 — Source acquisition: An attacker gains access to the source code. This could happen through a public GitHub repository, a leaked backup, a compromised developer machine, or even by decompiling a distributed build of the application.

  2. Step 2 — Key extraction and abuse: The attacker searches for the string apikey or OCR_API_KEY in the codebase, finds 'K81241004488957' at plugins/ocr.js:21, and begins making OCR API requests authenticated with that key.

No network interception, no brute force, no sophisticated tooling required. The key is in the code.

Real-World Impact

Depending on the OCR API provider's pricing and rate limits, an attacker exploiting this key could:

  • Run up significant charges on the account associated with the key, resulting in unexpected billing for the application owner
  • Exhaust API quotas, causing the legitimate OCR feature to fail for real users
  • Access usage data or account information if the API key grants broader permissions than just OCR submissions
  • Use the key as a pivot — some API providers expose account management endpoints authenticated by the same key

Because this is described as a web service in the threat model, the source code exposure surface is elevated. The application is deployed and potentially accessible to a wide range of actors who might attempt to obtain its code.


The Fix

The fix makes two precise changes to plugins/ocr.js:

Before

const form = new FormData()
form.append('apikey', process.env.OCR_API_KEY || 'K81241004488957')
form.append('language', 'eng')

After

if (!process.env.OCR_API_KEY) return m.reply('OCR API key is not configured.')
const form = new FormData()
form.append('apikey', process.env.OCR_API_KEY)
form.append('language', 'eng')

Why This Fix Works

Change 1 — Early exit guard: The added line if (!process.env.OCR_API_KEY) return m.reply('OCR API key is not configured.') checks for the environment variable before any form construction or API call happens. If the variable is absent, the function returns immediately with a user-facing error message. This is a "fail fast" pattern — the application makes its configuration requirements explicit rather than silently falling back to a hardcoded value.

Change 2 — Removal of the fallback literal: The || 'K81241004488957' expression is completely removed. form.append('apikey', process.env.OCR_API_KEY) now passes the environment variable directly. Since the guard above ensures OCR_API_KEY is truthy before this line executes, there is no risk of submitting an undefined or empty apikey to the OCR service.

The fix is scoped to one file and one logical path — the OCR request handler. Valid inputs (a properly configured OCR_API_KEY environment variable) are completely unaffected. Only the insecure fallback behavior is removed.

What Happens to the Hardcoded Key?

The key K81241004488957 should be treated as permanently compromised. Even after this fix is merged, the key exists in the repository's git history. The correct remediation steps alongside this code fix are:

  1. Revoke the key immediately via the OCR API provider's dashboard
  2. Generate a new key and store it in the deployment environment's secret management system
  3. Audit API usage logs for the old key to determine if unauthorized access occurred
  4. Consider a git filter-repo or BFG Repo Cleaner run if the repository is public, to purge the key from history

Key Takeaways

  • The || 'fallback' pattern is dangerous for secrets: In plugins/ocr.js, process.env.OCR_API_KEY || 'K81241004488957' silently used a live API key whenever the environment variable was absent — a pattern that looks harmless but creates a critical exposure.
  • Hardcoded keys survive code changes via git history: Even after the fix is merged, K81241004488957 exists in the repository's commit history and must be revoked at the provider level.
  • Fail fast beats silent fallback for credentials: The replacement guard if (!process.env.OCR_API_KEY) return m.reply('OCR API key is not configured.') makes misconfiguration visible and actionable instead of silently dangerous.
  • Source code access is a realistic threat vector: This was assessed as a 2-step exploit chain — obtain source, extract key. For web services with any public-facing exposure, this attack surface is real.
  • Secret scanning in CI would have caught this before merge: Tools like TruffleHog or GitHub Secret Scanning can detect patterns like K81241004488957 automatically, preventing this class of bug from reaching production.

How Orbis AppSec Detected This

  • Source: The hardcoded string literal 'K81241004488957' embedded directly in plugins/ocr.js at line 21
  • Sink: form.append('apikey', process.env.OCR_API_KEY || 'K81241004488957') — the credential is passed to an external HTTP request to the OCR API service
  • Missing control: No environment variable presence validation; the || fallback operator bypassed any requirement to configure the secret externally
  • CWE: CWE-798 — Use of Hard-coded Credentials
  • Fix: Removed the || 'K81241004488957' fallback and added an explicit early-exit check that returns an error message when OCR_API_KEY is not set

Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.


Conclusion

The vulnerability in plugins/ocr.js is a textbook example of how developer convenience — a simple || fallback to keep the OCR feature working without configuration — can create a critical security exposure. The key K81241004488957 was never meant to be permanent, but once committed, it became a permanent part of the repository's history and a permanent risk.

The fix is minimal and precise: one line added, one literal removed. But the lesson is broader. Every time you reach for process.env.SECRET || 'some_value' in a Node.js application, ask whether that fallback value is something you'd be comfortable publishing publicly. If the answer is no, use a fail-fast guard instead.

Credentials belong in secrets managers and environment variables — not in source code, not in comments, and not in || fallback expressions.


Prevention and further reading

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #74

Related Articles

critical

How credential leakage through console logging happens in JavaScript browser extensions and how to fix it

A browser extension's `src/background/credentials.js` printed the full Strava authentication cookie string — including a signed JWT and CloudFront-Signature values — straight into the extension console via `console.debug`. Anyone who could open DevTools on the background page (or any tooling that scraped the console) could copy a live session and impersonate the user. The fix replaces the credential payload in both log statements with `Boolean(credentials)` and strips a realistic-looking JWT out

critical

How Hardcoded Secrets Compromise Authentication in JavaScript and How to Fix It

A critical vulnerability in `Tool/QuantumultX/Rewrite/RRSP.js` exposed hardcoded API authentication credentials—a TOKEN and UMID device identifier—directly in source code. Anyone with repository access could extract these credentials to impersonate the legitimate user and gain full account access to the RRTV API service. The fix replaced hardcoded secrets with empty placeholders, forcing users to manually configure credentials through secure channels.

critical

How API Key Exposure in Request Bodies happens in React and how to fix it

The Chatbot component in gitforme was transmitting Azure OpenAI API keys inside JSON request bodies, causing them to be logged by servers, proxies, and middleware. By moving the apiKey from requestBody.apiKey to an Authorization header, credentials are now protected from persistence in generic request logging infrastructure.

critical

How Hardcoded API Keys in WASM Modules Happen in KAP and How to Fix Them

A critical security vulnerability in `wasm/kap/standard-lib/fhelp-impl.kap` exposed hardcoded Gemini API keys directly in source code distributed to end users via WASM modules. The fix replaces the embedded credential with secure environment variable retrieval, preventing credential extraction through browser developer tools or binary inspection.

critical

How Hardcoded API Key Exposure Happens in Node.js and How to Fix It

A critical vulnerability in `archive/open_claude_code/src/api/client.mjs` exposed five different API providers' credentials through direct `process.env` access. The fix introduced a centralized `readApiKey()` function to enforce secure credential retrieval across Anthropic, OpenAI, Google, and other integrations.

high

How Insufficiently Protected Credentials happens in Node.js and how to fix it

A regex-based guard in `skills/xmemo/scripts/xmemo-skill.mjs` was supposed to block sensitive credentials from being passed as CLI flags, but it only matched a handful of keyword patterns—missing `password`, `client-secret`, `access-token`, `xmemo-key`, and more. The fix expands the blocklist regex to cover these additional credential patterns, closing a gap that could let secrets end up in shell history and process listings.