T09 · Insecure Skill Coding Practices
- Location
index.js:26- Finding
OS Command Injection Through the URL Argument
- Content
View full analysis
Vulnerability Details
File Location:
index.js, lines 26–29
Vulnerability Type: OS command injection
Risk Level: CriticalVulnerable Code
js const url = request.params.arguments.url; try { // Execute the scraper script and capture its output const stdout = execSync(`node scripts/playwright-stealth.js "${url}"`, { encoding: 'utf8' });Technical Analysis
The
stealth_scrapeMCP tool accepts an attacker-controlledurlvalue and interpolates it directly into a command string passed tochild_process.execSync(). This API executes the string through a system shell.Wrapping the value in double quotes does not make it safe. An attacker can include a double quote to terminate the intended argument and then append shell operators and arbitrary commands. Neither the MCP input schema nor the request handler validates the value as an HTTP/HTTPS URL or safely escapes it for the host shell.
The referenced
scripts/playwright-stealth.jsfile is absent from the audited package. Although this prevents the advertised scraper from operating normally, it does not reliably mitigate the injection: the shell can still parse and execute appended commands.Attack Path
- An attacker or compromised agent invokes the exposed
stealth_scrapeMCP tool. - The attacker supplies a crafted
url, for example:text "; id; # - The server constructs a command equivalent to:
sh node scripts/playwright-stealth.js ""; id; #" execSync()passes that command to the system shell.- The shell executes the injected
idcommand under the MCP server process's operating-system identity. - An attacker can replace
idwith other commands available to that account.
Impact Assessment
Successful exploitation provides arbitrary command execution with the privileges of the Node.js MCP server. The attacker could read or alter files accessible to that account, ...[truncated 425 chars]
- An attacker or compromised agent invokes the exposed
- Remediation
View remediation
Remediation Suggestions
-
Do not construct a shell command using user-controlled data. Invoke Node.js directly with an argument array and disable shell processing:
js import { execFileSync } from "node:child_process"; const stdout = execFileSync( process.execPath, ["scripts/playwright-stealth.js", validatedUrl], { encoding: "utf8", shell: false, timeout: 30_000, maxBuffer: 5 * 1024 * 1024 } ); -
Parse and validate the input before execution:
js function validateUrl(value) { if (typeof value !== "string" || value.length > 2048) { throw new Error("Invalid URL"); } const parsed = new URL(value); if (!["http:", "https:"].includes(parsed.protocol)) { throw new Error("Only HTTP and HTTPS URLs are allowed"); } return parsed.href; } -
Consider blocking loopback, link-local, private-network, and cloud metadata destinations if arbitrary internal-resource access is not an intended feature.
-
Run the browser and MCP process under a dedicated, unprivileged account with filesystem and network restrictions.
-
Add regression tests using quote characters, command separators, command substitutions, newlines, and platform-specific shell metacharacters.
-
Include and review the referenced scraper script before deployment; the audited package does not contain
scripts/playwright-stealth.js.
-
