Back to Blog
critical SEVERITY7 min read

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.

O
By Orbis AppSec
Published September 9, 2026Reviewed September 9, 2026

Answer Summary

Hardcoded Secrets (CWE-798) in JavaScript occurs when authentication credentials like API tokens and device identifiers are embedded directly in source code. In RRSP.js, the TOKEN "rrtv-483e4fb0a5e14f0ef6e632f36db9c59704857993" and UMID "C6CFE97D-A15C-4FE0-8666-FB0036C5E32A" were visible to anyone accessing the repository, allowing credential theft and account takeover. The fix removes hardcoded values and requires manual configuration, eliminating the exposed credentials from version control.

Vulnerability at a Glance

cweCWE-798 (Use of Hard-Coded Credentials)
fixReplace hardcoded credentials with empty string defaults and document manual configuration requirement
riskComplete account compromise; credential theft; API impersonation; unauthorized access to user data and services
languageJavaScript
root causeAuthentication credentials embedded directly in source code instead of being externalized
vulnerabilityHardcoded Secrets / Credential Exposure

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

Introduction

In the Tool/QuantumultX/Rewrite/RRSP.js file, a critical vulnerability exposed authentication credentials directly in source code. The USER_CONFIG object on lines 27-28 contained a hardcoded TOKEN (rrtv-483e4fb0a5e14f0ef6e632f36db9c59704857993) and UMID device identifier (C6CFE97D-A15C-4FE0-8666-FB0036C5E32A) used to authenticate with the RRTV API service. This vulnerability created a 2-step attack chain: an attacker could extract these credentials from the public repository and immediately use them to make authenticated API requests to api.rrmj.plus, gaining complete access to the user's account.

For developers working with API integrations and configuration management, this vulnerability illustrates a fundamental security principle: credentials must never live in code. Even in "private" scripts or seemingly internal tools, hardcoded secrets are a liability the moment code is shared, archived, or exposed.

The Vulnerability Explained

What Made This Code Vulnerable?

The vulnerable code in RRSP.js looked like this:

// 配置 
let USER_CONFIG = {
    TOKEN: "rrtv-483e4fb0a5e14f0ef6e632f36db9c59704857993", 
    UMID: "C6CFE97D-A15C-4FE0-8666-FB0036C5E32A",  
    PLAYER_Code: "SenPlayer", 
    CustomScheme: "" 
};

The specific problem:

  1. Lines 27-28 contain literal credential strings embedded in configuration that will be read into memory every time the script executes
  2. The TOKEN follows a predictable pattern (rrtv- prefix), making it easily identifiable as an authentication secret
  3. The UMID is a UUID format, another recognizable pattern that credential scanners detect
  4. These values are committed to the repository, making them accessible to anyone with clone/fork permissions—forever in Git history unless actively purged
  5. No environment variable or external configuration mechanism exists to override these defaults, forcing users to either modify source code or accept the exposed credentials

How This Could Be Exploited

An attacker following this 2-step chain:

  1. Discovery: Clone or fork the public repository containing RRSP.js, scan the file, and extract the TOKEN and UMID strings from lines 27-28
  2. Exploitation: Use these credentials in HTTP requests to api.rrmj.plus:
    javascript fetch('https://api.rrmj.plus/endpoint', { headers: { 'Authorization': `Bearer ${TOKEN}`, 'X-UMID': UMID } })
    The API would recognize these credentials as valid and authenticate the request as the legitimate user, granting full access to account data, content library, viewing history, and any other RRTV API functionality

Real-World Impact for RRSP.js

  • Account Takeover: The attacker gains full control over the RRTV account associated with these credentials
  • Data Breach: Access to all content watched, bookmarks, preferences, and personal viewing history
  • Service Abuse: Using the account to stream premium content without authorization, potentially triggering false fraud alerts or account suspension for the legitimate user
  • Persistence: Without rotating credentials, the compromise is persistent until the legitimate user manually changes their token
  • Supply Chain Risk: If this script is shared across teams or embedded in other tools, the compromise spreads to all downstream users

The Fix

What Changed?

The pull request replaced the hardcoded credentials with empty string defaults and added inline comments instructing users to manually fill in values:

// BEFORE (vulnerable)
let USER_CONFIG = {
    TOKEN: "rrtv-483e4fb0a5e14f0ef6e632f36db9c59704857993", 
    UMID: "C6CFE97D-A15C-4FE0-8666-FB0036C5E32A",  
    PLAYER_Code: "SenPlayer", 
    CustomScheme: "" 
};

// AFTER (fixed)
let USER_CONFIG = {
    TOKEN: "", //手动填写频道更新的token
    UMID: "",  //手动填写..
    PLAYER_Code: "SenPlayer", 
    CustomScheme: "" 
};

Why this specific change works:

  1. Empty defaults prevent automatic credential exposure: No credentials live in the repository or Git history
  2. Credentials remain outside version control: Users manually configure their own TOKEN and UMID at runtime or through external configuration
  3. Shifts responsibility to users: The script won't function without explicit user configuration, preventing accidental use of leaked credentials
  4. No hardcoded patterns to detect: Secret scanning tools won't flag empty strings, reducing false positives
  5. Eliminates 2-step attack chain: Even if an attacker accesses the repository, no valid credentials exist to extract

How the Fix Solves This Specific Problem

Before the fix:
- Credentials: Always present, always exploitable, always in Git history
- Exploit complexity: Trivial (copy-paste from source)
- Blast radius: Everyone who cloned the repo

After the fix:
- Credentials: Never stored in code or Git
- Exploit complexity: Impossible from repository alone; attacker must compromise the user's local configuration
- Blast radius: Limited to individual user's local machine

The fix follows the principle of least privilege in configuration: the script ships with no secrets, and users must consciously provide them during setup.

Prevention & Best Practices

To prevent this vulnerability in similar code:

1. Use Environment Variables

let USER_CONFIG = {
    TOKEN: process.env.RRTV_TOKEN || "", 
    UMID: process.env.RRTV_UMID || "",  
    PLAYER_Code: "SenPlayer", 
    CustomScheme: "" 
};

Users set export RRTV_TOKEN=<token> before running the script.

2. Use External Configuration Files (Outside .gitignore)

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

// Load from ~/.config/rrsp/config.json
const configPath = path.join(process.env.HOME, '.config/rrsp/config.json');
const userConfig = JSON.parse(fs.readFileSync(configPath, 'utf8'));

Add ~/.config/rrsp/config.json to .gitignore.

3. Implement Secret Scanning Pre-Commit Hooks

Use tools like TruffleHog or detect-secrets to block commits containing credential patterns:

npm install --save-dev @trufflesecurity/trufflehog
# Or
npm install --save-dev detect-secrets

4. Use Semgrep Rules to Catch Hardcoded Secrets

Deploy Semgrep rule javascript.lang.security.hardcoded-secrets to catch patterns like:

TOKEN: "[a-f0-9]{40,}"  # 40+ hex chars (common token format)

5. Follow OWASP Guidelines

Reference OWASP's Secrets Management Cheat Sheet for best practices on credential storage and rotation.

6. Rotate Exposed Credentials Immediately

If hardcoded credentials are ever committed, they must be assumed compromised. Rotate the TOKEN and UMID in the RRTV API settings immediately.

7. Audit Git History

Use git log -S "rrtv-" to find all commits containing the leaked token pattern and ensure they're removed from all branches.

Key Takeaways

  • Hardcoded tokens in JavaScript configuration objects are trivial to extract and exploit: The USER_CONFIG object in RRSP.js lines 27-28 exposed credentials directly to anyone with repository access—no decryption or reverse engineering needed.

  • Credential patterns are predictable and easily scannable: The rrtv- prefix and UUID format in the UMID made this vulnerability detectable by automated secret scanning tools, showing why secrets should be externalized before code review.

  • Empty string defaults with documentation shift security responsibility: Replacing hardcoded values with "" and comments forces users to consciously configure credentials, preventing accidental deployment of exposed secrets.

  • 2-step account takeover chains are high-risk but preventable: This vulnerability required only repository access + credential usage—a trivial 2-step exploit that environment variables or secret managers completely eliminate.

  • Version control history is permanent: Deleting hardcoded secrets from the current branch isn't enough; they persist in Git history unless actively purged with tools like git-filter-repo, so prevention at commit time is essential.

How Orbis AppSec Detected This

Orbis AppSec identified this vulnerability through automated secret pattern analysis:

  • Source: Configuration object USER_CONFIG on lines 27-28 of Tool/QuantumultX/Rewrite/RRSP.js containing hardcoded credential literals
  • Sink: Direct assignment of sensitive strings to TOKEN and UMID properties without external sourcing or sanitization
  • Missing control: No environment variable lookup, no external config file loading, no secret manager integration—credentials embedded directly in code
  • CWE: CWE-798: Use of Hard-Coded Credentials — sensitive authentication information stored in source code accessible to attackers with code repository access
  • Fix: Replaced hardcoded token string "rrtv-483e4fb0a5e14f0ef6e632f36db9c59704857993" and UMID "C6CFE97D-A15C-4FE0-8666-FB0036C5E32A" with empty defaults (""), eliminating credentials from version control and requiring manual user configuration

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

Hardcoded secrets in JavaScript configuration files represent one of the easiest vulnerabilities to exploit and one of the most damaging once compromised. The RRSP.js vulnerability demonstrates that even in specialized or internal tools, credentials embedded in source code create an immediate account takeover risk.

The fix—replacing hardcoded values with empty defaults—is simple but requires discipline in your development workflow. Combined with environment variables, external configuration management, and automated secret scanning, this approach eliminates the vulnerability class entirely.

For your own projects:
1. Audit your codebase for hardcoded secrets using git log -S and Semgrep
2. Implement .gitignore rules and pre-commit hooks to prevent future commits
3. Use environment variables or secret managers for all credentials
4. Rotate any exposed credentials immediately
5. Remove secrets from Git history using git-filter-repo

Secure authentication starts with keeping credentials out of code. Make that your first principle, and you'll prevent vulnerabilities before they become incidents.


Prevention and further reading

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #2

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 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.

critical

How stored XSS happens in TinyMCE plugins and how to fix it

The snippets plugin's `Main.ts` inserted raw, unsanitized snippet content directly into the TinyMCE editor via `editor.insertContent(snippet.content)`, allowing stored JavaScript payloads saved by any snippet editor to execute in every user's browser. The fix routes snippet content through TinyMCE's own parser and serializer before insertion, stripping dangerous markup while preserving legitimate formatting.