Back to Blog
high SEVERITY8 min read

Hardcoded API Keys in Init Scripts: A Silent Security Disaster

A critical security vulnerability was discovered and patched in the nullclaw-init script, where API keys were hardcoded directly into the source code at multiple locations. This type of exposure means anyone with read access to the repository or installed files can silently extract and abuse credentials without any technical hacking required. The fix eliminates this risk by removing hardcoded secrets in favor of secure credential management practices.

O
By Orbis AppSec
Published May 16, 2026Reviewed June 3, 2026

Answer Summary

Hardcoded API keys (CWE-798) in shell scripts occur when developers embed credentials directly in source code instead of using secure secret management. In the nullclaw-init script, multiple API keys were hardcoded at various locations, exposing them to anyone with repository access. The fix removes all embedded credentials and replaces them with environment variable references or secure credential retrieval mechanisms.

Vulnerability at a Glance

cweCWE-798
fixRemove hardcoded credentials and use environment variables or secure secret management
riskCredential theft and unauthorized API access by anyone with code access
languageShell Script
root causeAPI keys embedded directly in nullclaw-init script source code
vulnerabilityHardcoded API Keys / Use of Hard-coded Credentials

Hardcoded API Keys in Init Scripts: A Silent Security Disaster

Vulnerability: Hardcoded API Keys (V-005)
Severity: Critical
Affected File: nullclaw-init
Fixed In: Automated security patch via OrbisAI Security


Introduction

Imagine leaving the keys to your house taped to the front door with a note that says "please don't use these." That's essentially what hardcoded API keys in source code represent — a silent, passive vulnerability that requires zero technical skill to exploit.

The nullclaw project recently patched a critical security issue where API keys were embedded directly into an initialization script. With 53 API key occurrences identified across the codebase and specific references at lines 106, 117, and 121 of the nullclaw-init file, this wasn't a minor oversight — it was a systemic credential management problem.

This post breaks down what happened, why it's dangerous, and most importantly, how to make sure it never happens in your projects.


The Vulnerability Explained

What Are Hardcoded Credentials?

Hardcoded credentials occur when sensitive values — API keys, passwords, tokens, database connection strings — are written directly into source code as string literals instead of being loaded from a secure external source at runtime.

Here's a simplified example of what this looks like:

// ❌ DANGEROUS: Hardcoded API key in source code
const API_KEY = "sk-live-a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6";

async function downloadPlugin(pluginName) {
  const response = await fileDownloader.download({
    url: `https://api.nullclaw.io/plugins/${pluginName}`,
    headers: {
      "Authorization": `Bearer ${API_KEY}`
    }
  });
}

In the nullclaw case, the nullclaw-init script — which is likely run during installation or setup — contained multiple such references. Because init scripts are often distributed publicly or installed directly onto user systems, the exposure surface is significant.

How Could It Be Exploited?

The insidious thing about hardcoded credentials is that exploitation requires no hacking at all. Here's how an attacker could abuse this:

Scenario 1: Public Repository Exposure

  1. Developer pushes nullclaw-init to a public GitHub repository
  2. GitHub's search indexing picks up the file within minutes
  3. Automated credential-scanning bots (which are constantly crawling GitHub) detect the API key pattern
  4. Within hours, the key is extracted and potentially sold or abused
  5. The developer has no idea this happened

Scenario 2: Installed Script Exposure

  1. A user installs the nullclaw package on their system
  2. The nullclaw-init script is written to disk as a readable file
  3. Any other process or user with read access to that directory can cat the file and extract the keys
  4. On shared hosting or CI/CD systems, this is a very real threat

Scenario 3: Git History Exposure

Even if the developer catches the mistake and removes the hardcoded key in a new commit, the key lives forever in git history unless the history is explicitly rewritten:

# An attacker can easily search git history
git log --all --full-history -- nullclaw-init
git show <commit-hash>:nullclaw-init | grep -i "api_key\|bearer\|token"

Real-World Impact

The consequences of exposed API keys depend entirely on what the key controls, but common impacts include:

  • Financial damage: Cloud provider API keys can rack up enormous bills (AWS, GCP, and Azure credential abuse is a multi-million dollar problem annually)
  • Data breaches: Keys to data services expose user records, PII, and business data
  • Service abuse: Rate limits exhausted, quotas burned, services disrupted for legitimate users
  • Supply chain attacks: If the key controls package distribution or update mechanisms, attackers could push malicious updates to all users
  • Reputational damage: Public disclosure of a credential leak erodes user trust

Given that nullclaw-init uses nodejs-file-downloader to fetch content (plugins or updates), a compromised API key in this context could potentially allow an attacker to manipulate what gets downloaded and executed on user systems — escalating from credential theft to remote code execution.


The Fix

What Changed

The patch removes hardcoded API key literals from the nullclaw-init script and replaces them with secure alternatives. The core principle is simple: secrets should never live in code.

Before (Vulnerable Pattern)

// ❌ BEFORE: Credentials hardcoded as string literals
const NULLCLAW_API_KEY = "sk-nc-prod-xxxxxxxxxxxxxxxxxxxxxxxx";
const NULLCLAW_SECRET  = "nc-secret-yyyyyyyyyyyyyyyyyyyyyyyy";

async function initializeNullclaw() {
  const downloader = new Downloader({
    url: "https://api.nullclaw.io/init",
    directory: "./",
    headers: {
      "X-API-Key": NULLCLAW_API_KEY,
      "X-Secret": NULLCLAW_SECRET
    }
  });
  await downloader.download();
}

After (Secure Pattern)

// ✅ AFTER: Credentials loaded from environment variables
const NULLCLAW_API_KEY = process.env.NULLCLAW_API_KEY;
const NULLCLAW_SECRET  = process.env.NULLCLAW_SECRET;

async function initializeNullclaw() {
  // Validate that credentials are present before proceeding
  if (!NULLCLAW_API_KEY || !NULLCLAW_SECRET) {
    throw new Error(
      "Missing required credentials. Please set NULLCLAW_API_KEY " +
      "and NULLCLAW_SECRET environment variables."
    );
  }

  const downloader = new Downloader({
    url: "https://api.nullclaw.io/init",
    directory: "./",
    headers: {
      "X-API-Key": NULLCLAW_API_KEY,
      "X-Secret": NULLCLAW_SECRET
    }
  });
  await downloader.download();
}

Why This Works

By loading credentials from environment variables:

  1. The source code contains no secrets — it's safe to commit, share, and open-source
  2. Credentials are scoped to the runtime environment — different environments (dev, staging, prod) use different keys
  3. Access is controlled at the OS level — only processes that need the secret have it set in their environment
  4. Rotation is trivial — change the environment variable, no code changes needed
  5. Audit trails are cleaner — credential management systems log access, code doesn't

Prevention & Best Practices

1. Never Commit Secrets to Version Control

This sounds obvious, but it's the most violated rule in software development. Enforce it with tooling:

# Install git-secrets to prevent committing credentials
brew install git-secrets  # macOS
git secrets --install
git secrets --register-aws  # Add AWS patterns

Or use pre-commit hooks with tools like detect-secrets:

pip install detect-secrets
detect-secrets scan > .secrets.baseline
# Add to .pre-commit-config.yaml

2. Use a Secrets Manager

For production systems, environment variables alone aren't always sufficient. Use a dedicated secrets manager:

Tool Best For
HashiCorp Vault Self-hosted, highly configurable
AWS Secrets Manager AWS-native workloads
Azure Key Vault Azure-native workloads
GCP Secret Manager GCP-native workloads
Doppler Multi-cloud, developer-friendly
1Password Secrets Automation Teams already using 1Password
// Example: Loading secrets from AWS Secrets Manager
const { SecretsManagerClient, GetSecretValueCommand } = require("@aws-sdk/client-secrets-manager");

async function getApiKey() {
  const client = new SecretsManagerClient({ region: "us-east-1" });
  const response = await client.send(
    new GetSecretValueCommand({ SecretId: "nullclaw/api-key" })
  );
  return JSON.parse(response.SecretString).apiKey;
}

3. Rotate Compromised Keys Immediately

If you suspect a key was exposed (even briefly), treat it as compromised:

  1. Revoke the key immediately — don't wait to investigate first
  2. Issue a new key — update all legitimate consumers
  3. Audit access logs — check for unauthorized usage during the exposure window
  4. Rewrite git history if the key was committed (using git filter-branch or BFG Repo Cleaner)
  5. Notify affected parties if user data may have been accessed

4. Scan for Secrets Continuously

Add secret scanning to your CI/CD pipeline so issues are caught before merge:

# .github/workflows/security.yml
name: Secret Scanning
on: [push, pull_request]

jobs:
  scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
        with:
          fetch-depth: 0  # Full history for thorough scanning

      - name: Run Gitleaks
        uses: gitleaks/gitleaks-action@v2
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

5. Add Checksum Validation for Downloaded Files

Since this vulnerability also touches on nodejs-file-downloader usage, it's worth noting that downloaded files should always be cryptographically verified:

const crypto = require('crypto');
const fs = require('fs');

async function verifyDownload(filePath, expectedChecksum) {
  const fileBuffer = fs.readFileSync(filePath);
  const hashSum = crypto.createHash('sha256');
  hashSum.update(fileBuffer);
  const actualChecksum = hashSum.digest('hex');

  if (actualChecksum !== expectedChecksum) {
    fs.unlinkSync(filePath); // Delete the suspect file
    throw new Error(
      `Checksum verification failed!\n` +
      `Expected: ${expectedChecksum}\n` +
      `Got:      ${actualChecksum}`
    );
  }

  console.log('✅ File integrity verified');
}

6. Follow the Principle of Least Privilege

Even when using API keys securely, limit what each key can do:

  • Create scoped keys with only the permissions needed for each use case
  • Use short-lived tokens where possible (OAuth, JWT with expiry)
  • Implement IP allowlisting for keys used in server-side contexts
  • Set usage quotas to limit blast radius if a key is compromised

Security Standards & References

This vulnerability maps to several well-known security standards:

  • OWASP Top 10: A02:2021 – Cryptographic Failures, A05:2021 – Security Misconfiguration
  • CWE-798: Use of Hard-coded Credentials
  • CWE-259: Use of Hard-coded Password
  • NIST SP 800-63B: Digital Identity Guidelines (credential management)
  • SANS CWE Top 25: Hardcoded credentials consistently appear in the top 25 most dangerous software weaknesses

Conclusion

Hardcoded API keys are one of those vulnerabilities that feel embarrassing in retrospect — "how could anyone do that?" — but they happen constantly, at every scale, from solo projects to Fortune 500 companies. The pressure to ship fast, the habit of copy-pasting working code, the assumption that "we'll clean this up later" — these are universal experiences that create universal risks.

The key takeaways from this vulnerability:

  1. Secrets in code are public secrets — treat any committed credential as already compromised
  2. Environment variables are the minimum bar — secrets managers are better for production
  3. Tooling beats willpower — automated scanning catches what code review misses
  4. Git history is permanent — removing a secret from the latest commit doesn't erase it
  5. Defense in depth matters — combine secret management with checksum verification, least privilege, and monitoring

The nullclaw patch is a good reminder that security isn't about being perfect — it's about building systems and habits that catch mistakes before they become incidents. Automated security scanning, as used here, is exactly the kind of safety net every codebase deserves.

Stay secure, and remember: if it's a secret, it doesn't belong in your source code.


This vulnerability was identified and patched by OrbisAI Security. Automated security scanning helps teams find and fix issues like this before they reach production.

Frequently Asked Questions

What are hardcoded API keys?

Hardcoded API keys are authentication credentials embedded directly in source code rather than being stored securely in environment variables, secret managers, or configuration files outside version control.

How do you prevent hardcoded credentials in shell scripts?

Use environment variables, secure secret managers (like HashiCorp Vault or AWS Secrets Manager), or encrypted configuration files. Never commit credentials to version control and use pre-commit hooks to detect secrets before they're pushed.

What CWE is hardcoded credentials?

CWE-798: Use of Hard-coded Credentials covers this vulnerability, which occurs when software contains hardcoded credentials such as passwords or cryptographic keys.

Is removing the credential from the current commit enough to prevent exposure?

No. Once a credential is committed to version control, it exists in the repository's history. You must rotate the exposed credential immediately and consider using tools like git-filter-repo to purge history if the repository is private.

Can static analysis detect hardcoded credentials?

Yes. Tools like Semgrep, GitLeaks, TruffleHog, and secret scanning features in GitHub, GitLab, and Orbis AppSec can detect hardcoded credentials using pattern matching and entropy analysis.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #12929

Related Articles

high

How missing Dependabot cooldown happens in GitHub Actions and how to fix it

A high-severity configuration vulnerability was discovered in a `.github/dependabot.yml` file that lacked a cooldown period for package updates. Without this safeguard, Dependabot could immediately propose updates to newly published package versions—including potentially malicious or unstable releases. The fix adds a simple `cooldown` block with a 7-day waiting period before any new package version is suggested.

high

How Server-Sent Events Injection via Unsanitized Newlines happens in Node.js h3 and how to fix it

A high-severity Server-Sent Events (SSE) injection vulnerability (CVE-2026-33128) was discovered in the h3 HTTP framework, where unsanitized newline characters in event stream fields could allow attackers to inject arbitrary SSE messages. The fix upgrades h3 from version 1.15.5 to 1.15.6 in the frontend's dependency tree, ensuring that newline characters are properly sanitized before being written to event streams.

high

How Memory Exhaustion via Large Comma-Separated Selector Lists happens in Python Soup Sieve and how to fix it

A high-severity memory exhaustion vulnerability (CVE-2026-49476) was discovered in Soup Sieve version 2.8.3, affecting Python applications that parse CSS selectors from user-controlled input. The vulnerability allows attackers to craft malicious selector lists that consume excessive memory, potentially causing denial of service. The fix involves upgrading to soupsieve 2.8.4, which implements proper resource limits on selector parsing.

high

How prototype pollution via `__proto__` key happens in Node.js defu and how to fix it

A high-severity prototype pollution vulnerability (CVE-2026-35209) was discovered in the `defu` package version 6.1.4, which allowed attackers to inject properties into JavaScript's `Object.prototype` via the `__proto__` key in defaults arguments. The fix upgrades `defu` to version 6.1.5 in the frontend's dependency tree, protecting downstream consumers like `c12` and `dotenv` configuration loaders from malicious property injection.

critical

How buffer overflow in memcpy() happens in Node.js N-API bindings and how to fix it

A critical buffer overflow vulnerability was discovered in the GetBufferAsVector() function in examples_nodejs/src/zupt_napi.cpp, where memcpy() copied data from JavaScript Uint8Array buffers without proper bounds validation. This vulnerability could allow attackers to trigger memory corruption by providing maliciously crafted input arrays to the native Node.js module, potentially leading to crashes or arbitrary code execution.

high

How memory exhaustion via large comma-separated selector lists happens in Python soupsieve and how to fix it

A high-severity memory exhaustion vulnerability (CVE-2026-49476) was discovered in soupsieve 2.8.3, a CSS selector library used by BeautifulSoup in Python. An attacker who could influence CSS selector input could craft large comma-separated selector lists to exhaust system memory, causing denial of service. The fix upgrades soupsieve from 2.8.3 to 2.8.4 in the backend's `uv.lock` dependency file.