Trust boundary JavaScript → operating system

Security model

node-cmd starts operating-system processes. It makes the execution boundary explicit; it does not sanitize commands, grant authorization, contain programs, or supervise an entire process tree.

01 / Responsibility

What node-cmd is—and is not

It does

  • Expose shell, direct-file, buffered, synchronous, Promise, and streaming execution paths.
  • Forward caller-selected Node child-process options.
  • Return the real process handle where Node provides one.
  • Keep direct arguments separate unless a shell is explicitly enabled.

It does not

  • Sanitize a shell command or decide whether input is trusted.
  • Provide a privilege, sandbox, container, or authorization boundary.
  • Hide secrets in arguments, environment variables, or captured output.
  • Guarantee a hard deadline or terminate every descendant process.
02 / Shell

Strings are programs interpreted by a shell

run(), runPromise(), and runSync() execute a command string through the platform shell. Metacharacters, substitutions, redirection, pipelines, and separators are executable syntax.

Unsafe interpolation

// userValue can alter the command
cmd.run(`tool --name ${userValue}`);

Separated argument

// userValue remains one argument
cmd.runFile('tool', [
    '--name', userValue
]);
Do not build shell commands from untrusted data. Quoting is shell- and platform-specific and is easy to get wrong. Prefer direct execution, validate allowed values, and avoid enabling options.shell.
03 / Direct

Direct methods reduce shell-injection exposure

runFile(), runFilePromise(), runFileSync(), and runStream() pass an executable and argument array to Node without a shell by default. Spaces, semicolons, ampersands, and quotes remain characters inside an argument.

This does not make every executable safe. The launched program may interpret file paths, configuration, flags, response files, templates, URLs, or other arguments as its own control language. Validate executable selection and the meaning of every value.

BoundaryDefault parserPrimary riskPreferred use
run*Platform shellShell injection and platform-specific parsingIntentional pipelines, redirection, and shell built-ins
runFile*Target executableExecutable-specific argument interpretationBuffered calls with exact arguments
runStreamTarget executableExecutable behavior and unbounded stream handlingInteractive, long-running, or high-output processes
04 / Context

Working directory, environment, and executable resolution

  • Resolve cwd deliberately. Do not let another user select an arbitrary directory. Confirm the target exists and stays inside the intended workspace.
  • Minimize env. Child processes may inherit tokens, credentials, proxy settings, search paths, and debug flags. Pass only values the program needs.
  • Control executable lookup. A modified PATH can select a different program with the same name. Use an absolute path when executable identity matters.
  • Avoid attacker-controlled configuration. Programs often load project-local files, plugins, startup scripts, or environment-based hooks from the selected directory.
05 / Data

Secrets and output are caller-owned

Command lines can be visible to process inspectors, diagnostic tools, audit logs, crash reports, and shell history. Prefer stdin, file descriptors, or purpose-built credential channels over putting secrets in command strings or argument arrays.

Captured stdout and stderr can contain credentials, personal data, build artifacts, or terminal-control sequences. Bound buffered output with maxBuffer, sanitize before rendering in HTML or logs, and apply retention controls to stored output.

Backpressure. Use runStream() when output may be large. Consume or redirect every piped stream so a child cannot stall indefinitely on a full pipe.
06 / Lifecycle

Timeout and cancellation request termination

const controller = new AbortController();
const task = cmd.runPromise(command, {
    signal: controller.signal,
    timeout: 30_000,
    killSignal: 'SIGTERM'
});

controller.abort();

A timeout causes Node to request termination after the configured interval. Abort signals and child.kill() also target the immediate child according to Node and operating-system semantics. None is a guaranteed hard execution deadline, and descendant processes can survive.

  • Listen for both error and close.
  • Design a platform-specific escalation policy if termination must be enforced.
  • Use external isolation—containers, job objects, cgroups, service supervisors, or restricted accounts—when the process must be contained.
07 / Privilege

No elevation is requested

node-cmd does not request administrator, root, sudo, or UAC access. Children inherit the privileges and accessible resources of the Node.js process. Run the parent with the least privilege necessary; do not treat library calls as an elevation boundary.

08 / Platform

Review behavior on every target OS

Unix shells and Windows ComSpec differ in quoting, variable expansion, separators, built-ins, signals, executable lookup, and script-file behavior. Windows .bat and .cmd files require a shell. Test the exact command, input classes, cancellation path, output volume, and error handling on each supported operating system.

09 / Reporting

Report vulnerabilities privately

Use GitHub’s private vulnerability reporting form. Include the affected version, platform, Node version, minimal reproduction, security impact, and any known mitigations. Do not open a public issue for an unpatched vulnerability.