Back to Blog
critical SEVERITY8 min read

How Plaintext Secret Storage Happens in Cloudflare Workers (wrangler.toml) and How to Fix It

A critical misconfiguration in `platforms/m365/wrangler.toml` left developers one copy-paste away from committing live API keys directly into git history. The fix adds an explicit warning comment blocking the `[vars]` anti-pattern and adds `.dev.vars` to `.gitignore`, ensuring secrets flow through Cloudflare's encrypted `wrangler secret` mechanism instead of plaintext config. This matters because git history is permanent — a key committed even once can be extracted long after it's "deleted."

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

Answer Summary

This vulnerability is a hardcoded-secrets risk (CWE-312: Cleartext Storage of Sensitive Information) in a Cloudflare Workers configuration file (`platforms/m365/wrangler.toml`). The `[vars]` section of `wrangler.toml` is committed to git in plaintext, meaning any API key placed there — such as `ANTHROPIC_API_KEY` or `API_TOKEN` — becomes permanently readable in version history. The fix adds a prominent `WARNING` comment to block the `[vars]` anti-pattern, redirects developers to `wrangler secret put`, and adds `.dev.vars` to `.gitignore` to prevent local dev secrets from being committed.

Vulnerability at a Glance

cweCWE-312
fixAdded explicit WARNING comment blocking [vars] usage for secrets and added .dev.vars to .gitignore
riskLive API keys committed to git history, readable by anyone with repo access
languageTOML / Cloudflare Workers (JavaScript/TypeScript)
root causewrangler.toml [vars] section is version-controlled plaintext with no enforcement against storing secrets there
vulnerabilityCleartext Storage of Sensitive Information (API Key in Config File)

The Problem With "Just Don't Put Keys Here"

Documentation comments are not security controls.

In the platforms/m365/wrangler.toml file for this Cloudflare Workers deployment, a comment instructed developers to set ANTHROPIC_API_KEY and API_TOKEN via wrangler secret put. That's correct advice. But the comment sat directly adjacent to the [vars] section — the exact place where a developer under deadline pressure might paste a key "just to test something" and accidentally commit it.

That's the vulnerability: not a missing encryption library, not a broken authentication flow, but a configuration file that was one distracted commit away from leaking production credentials into version control forever.


The Vulnerability Explained

What Makes [vars] Dangerous for Secrets

Cloudflare Workers has two ways to inject values into a Worker's environment:

  1. [vars] in wrangler.toml — plaintext key-value pairs, committed to git alongside your code
  2. wrangler secret put — encrypted secrets stored in Cloudflare's secret store, never written to disk or version control

The distinction is critical. Anything in [vars] is:
- Committed to git in plaintext
- Visible in every clone, fork, and CI/CD log that checks out the repo
- Permanent in git history, even if later deleted

Here's what the vulnerable section looked like before the fix:

# platforms/m365/wrangler.toml (before fix, line 7-11)

ALLOWED_ORIGINS = ""

# Set secrets via:
#   wrangler secret put ANTHROPIC_API_KEY
#   wrangler secret put API_TOKEN

The problem isn't what's written — it's what's missing. There's no barrier between the [vars] block above and the comment below. A developer who sees ALLOWED_ORIGINS = "" and needs to add ANTHROPIC_API_KEY for local testing might naturally write:

# DANGEROUS — do not do this
[vars]
ALLOWED_ORIGINS = ""
ANTHROPIC_API_KEY = "sk-ant-api03-..."   # ← now in git forever
API_TOKEN = "Bearer eyJ..."              # ← same

Once that commit is pushed — even to a private repository — the key is in the git object database. Deleting the line in a follow-up commit does not remove it from history.

The Attack Scenario

The PR's exploitation scenario is concrete: an attacker gains read access to the repository via leaked Git credentials, an exposed CI/CD log, or a compromised developer account. With that access, they run:

git log --all --full-history -- platforms/m365/wrangler.toml
git show <commit-hash>:platforms/m365/wrangler.toml

If an ANTHROPIC_API_KEY was ever committed to [vars], it appears in plain text. The attacker now has:
- Full access to the Anthropic API under the victim's billing account
- Potentially unbounded LLM API spend
- Access to any conversation history or fine-tuning data tied to that key

The API_TOKEN exposure is similarly severe — depending on what it authenticates, it could grant access to M365 data, admin endpoints, or downstream services.

Why the 503 Fallback Doesn't Help

The codebase includes a defensive control: when secrets are missing at runtime, the Worker returns a 503 error rather than proceeding without credentials. This is good practice — but it protects against absent secrets, not exposed ones. If a key is committed to git, it isn't missing. The Worker runs fine. The attacker also runs fine, using the same key from outside the Worker entirely.


The Fix

The fix is two targeted changes that together close the gap between documentation intent and developer behavior.

Change 1: Explicit Warning in wrangler.toml

# Before
# Set secrets via:
#   wrangler secret put ANTHROPIC_API_KEY
#   wrangler secret put API_TOKEN

# After
# WARNING: NEVER add ANTHROPIC_API_KEY or API_TOKEN to [vars] above.
# [vars] is committed to git and is NOT encrypted. Use wrangler secrets:
#   wrangler secret put ANTHROPIC_API_KEY
#   wrangler secret put API_TOKEN

The change is minimal in lines but significant in intent. The original comment read like a suggestion. The new comment reads like a guardrail. By naming the specific variables (ANTHROPIC_API_KEY, API_TOKEN) and explaining why [vars] is unsafe ("committed to git and is NOT encrypted"), the warning gives a developer the information they need at the exact moment they might make the mistake.

This is the principle of contextual security guidance — security instructions are most effective when they appear at the point of decision, not in a README three directories away.

Change 2: .dev.vars Added to .gitignore

# .gitignore

+# Cloudflare Workers local dev secrets — must never be committed
+.dev.vars
+
 # Internal lead documents — not for public consumption

Cloudflare Workers supports a .dev.vars file for local development — it's the local equivalent of wrangler secret put, letting developers set secret values for wrangler dev without touching wrangler.toml. But .dev.vars is only safe if it's gitignored. Without this entry, a developer creating .dev.vars for local testing could accidentally commit it.

The comment in the .gitignore entry matters too: "must never be committed" explains the why, making it less likely a future developer will remove the entry thinking it's unnecessary.

What This Fix Does NOT Do (And Why That's Okay)

This fix doesn't add runtime enforcement — it doesn't fail the build if [vars] contains a secret-shaped value. That would be a stronger control, but it's also a more invasive change. The current fix correctly scopes the change to the minimum necessary: clear guidance at the point of risk, and a gitignore entry that closes the .dev.vars vector. The PR notes that "the project's existing tests still pass, so intended behavior is unchanged."


Prevention & Best Practices

1. Use Wrangler Secrets for All Sensitive Values

# Correct approach for all secrets
wrangler secret put ANTHROPIC_API_KEY
wrangler secret put API_TOKEN

# For local dev, use .dev.vars (gitignored)
echo 'ANTHROPIC_API_KEY=sk-ant-...' >> .dev.vars

Never use [vars] for anything you wouldn't want in a public git repository.

2. Audit Git History for Committed Secrets

If you suspect a secret was ever committed, don't just delete it — rotate it immediately and scan history:

# Scan git history for secret patterns
trufflehog git file://. --since-commit HEAD~100
git-secrets --scan-history

Tools like truffleHog, gitleaks, and git-secrets can find secrets in historical commits.

3. Pre-commit Hooks

Add a pre-commit hook that blocks commits containing secret patterns:

# Install gitleaks as a pre-commit hook
gitleaks protect --staged

Or use the pre-commit framework with the detect-secrets hook.

4. CI/CD Secret Scanning

Enable GitHub's built-in secret scanning (Settings → Security → Secret scanning) and push protection. This blocks pushes containing known secret formats before they reach the remote.

5. Principle of Least Privilege for Config Files

Review every configuration file in your repository and ask: "If this file were public, what would be exposed?" Config files that need secrets should reference environment variables or secret store paths — never inline values.

OWASP and CWE Alignment

This vulnerability maps to:
- CWE-312: Cleartext Storage of Sensitive Information
- CWE-798: Use of Hard-coded Credentials
- OWASP A02:2021 — Cryptographic Failures (storing sensitive data without encryption)
- OWASP A05:2021 — Security Misconfiguration (insecure default configuration patterns)


Key Takeaways

  • wrangler.toml [vars] is git-committed plaintext — treat it like a public file and never store ANTHROPIC_API_KEY, API_TOKEN, or any credential there, regardless of how temporary the intent is.
  • A comment saying "don't do X" is not a control — the original comment correctly pointed to wrangler secret put, but without a warning explaining why, it provided no friction against the dangerous alternative.
  • .dev.vars needs to be gitignored explicitly — Cloudflare's local dev secrets file is only safe if it never reaches version control; the absence of this gitignore entry was a second vector for the same class of exposure.
  • Git history is permanent — rotating a key after committing it is mandatory, not optional. Deleting the line in a follow-up commit does not remove it from git log.
  • Contextual warnings outperform distant documentation — the fix's WARNING: NEVER add ANTHROPIC_API_KEY or API_TOKEN to [vars] comment is more effective than any README section because it appears at the exact point of risk.

How Orbis AppSec Detected This

  • Source: The [vars] section of platforms/m365/wrangler.toml at line 11, where environment variables are defined in plaintext and committed to version control
  • Sink: Any git clone, CI/CD checkout, or repository browser that reads wrangler.toml — the "dangerous call site" is the git commit itself, which permanently stores the value
  • Missing control: No explicit prohibition against placing ANTHROPIC_API_KEY or API_TOKEN in [vars]; no .dev.vars gitignore entry to block the local dev secret file from being committed
  • CWE: CWE-312 — Cleartext Storage of Sensitive Information
  • Fix: Added a WARNING comment naming the specific forbidden variables and explaining that [vars] is git-committed plaintext, plus added .dev.vars to .gitignore

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 platforms/m365/wrangler.toml is a reminder that security misconfiguration doesn't require a sophisticated exploit — sometimes it just requires a developer who doesn't know which config section is safe for secrets. The fix is small: two files, a warning comment, and a gitignore entry. But the impact is significant: it closes the gap between "we told developers to use wrangler secrets" and "we made it hard for developers to accidentally do the wrong thing."

Cloudflare Workers makes secret management easy with wrangler secret put. The failure mode here wasn't a missing feature — it was a missing guardrail. The lesson for any project using configuration files: document the safe path, warn against the unsafe path, and use tooling to enforce the difference.


References

Frequently Asked Questions

What is cleartext storage of sensitive information in wrangler.toml?

It occurs when API keys or tokens are placed in the [vars] section of wrangler.toml, which is committed to git as plaintext and readable by anyone with repository access — including in historical commits.

How do you prevent API key exposure in Cloudflare Workers?

Use `wrangler secret put SECRET_NAME` to store secrets encrypted in Cloudflare's secret store. Never place sensitive values in the [vars] section of wrangler.toml, which is version-controlled.

What CWE is hardcoded API key exposure?

CWE-312 (Cleartext Storage of Sensitive Information) and CWE-798 (Use of Hard-coded Credentials) both apply. The root issue is that secrets are stored in a form that can be read without decryption.

Is adding .gitignore enough to prevent secret exposure?

No. .gitignore prevents future commits of a file, but if a secret was already committed, it remains in git history and must be rotated immediately. Defense-in-depth requires both .gitignore and enforced use of wrangler secrets.

Can static analysis detect API keys committed in wrangler.toml?

Yes. Tools like Semgrep, truffleHog, and git-secrets can scan for secret patterns in config files. Orbis AppSec's multi-agent AI scanner flagged this exact pattern in wrangler.toml line 11.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #11

Related Articles

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.

critical

How Hardcoded Firebase API Keys Happen in JavaScript Service Workers and How to Fix Them

A production Firebase service worker file (`static/firebase-messaging-sw.js`) contained hardcoded API keys and project configuration directly in publicly accessible JavaScript — including a second, previously commented-out set of credentials from an older project. While Firebase web config values are technically public identifiers, pairing them with unrestricted Firebase projects or missing Security Rules turns them into an open door for push notification abuse, quota exhaustion, and unauthorize

critical

How hardcoded API key exposure happens in Node.js plugins and how to fix it

A critical hardcoded API key (`actor-studio-gpt-beta`) was discovered in the `src/plugins/llm/index.js` file of the Actor Studio application, granting anyone with source code access the ability to make unauthorized requests to the LLM service endpoints. The fix removes the default key from both the LLM class definition and the settings module, requiring the key to be explicitly configured through module settings instead.

high

How Insecure Credential Storage Happens in Node.js and How to Fix It

A critical vulnerability in the Google Vision translator module stored API keys in plaintext configuration files accessible to attackers with local filesystem access. The fix relocates the API key from the URL query parameter to a secure HTTP header, eliminating the exposure vector while maintaining full functionality.