Repository: denoland/deno
File: ext/node/polyfills/internal/child_process.ts (TypeScript)

Code before:
    validateBoolean(options.windowsHide, "options.windowsHide");
  }

  // Validate windowsVerbatimArguments, if present.
  let { windowsVerbatimArguments } = options;
  if (windowsVerbatimArguments != null) {
    validateBoolean(
      windowsVerbatimArguments,
      "options.windowsVerbatimArguments",
    );
  }

  validateOneOf(options.serialization, "options.serialization", [
    undefined,
    "json",
    "advanced",
  ]);

  if (options.shell) {
    let command = ArrayPrototypeJoin([file, ...args], " ");
    // Transform Node.js flags to Deno equivalents in shell commands that invoke Deno
    command = transformDenoShellCommand(command, options.env);
    // Set the shell, switches, and commands.
    if (process.platform === "win32") {
      if (typeof options.shell === "string") {
        file = options.shell;
      } else {
        file = Deno.env.get("comspec") || "cmd.exe";
      }
      // '/d /s /c' is used only for cmd.exe.
      if (/^(?:.*\\)?cmd(?:\.exe)?$/i.exec(file) !== null) {
        args = ["/d", "/s", "/c", `"${command}"`];
        windowsVerbatimArguments = true;
      } else {
        args = ["-c", command];
      }
    } else {
      /** TODO: add Android condition */
      if (typeof options.shell === "string") {
        file = options.shell;
      } else {
        file = "/bin/sh";
      }
      args = ["-c", command];
    }
  }

  if (typeof options.argv0 === "string") {
    ArrayPrototypeUnshift(args, options.argv0);
  } else {
    ArrayPrototypeUnshift(args, file);

Review comment (security):
This will escape if args.length is 0 right? In Node, it doesn't seem to escape:

```js
spawnSync(`echo test; touch ${"marker"}`, [], {
  shell: true,
});
```

Code after:
    validateBoolean(options.windowsHide, "options.windowsHide");
  }

  // Validate windowsVerbatimArguments, if present.
  let { windowsVerbatimArguments } = options;
  if (windowsVerbatimArguments != null) {
    validateBoolean(
      windowsVerbatimArguments,
      "options.windowsVerbatimArguments",
    );
  }

  validateOneOf(options.serialization, "options.serialization", [
    undefined,
    "json",
    "advanced",
  ]);

  if (options.shell) {
    // When args are provided, escape them to prevent shell injection.
    // When no args are provided (just a string command), the user intends
    // for shell interpretation, so don't escape.
    let command;
    if (args.length > 0) {
      const escapedParts = [escapeShellArg(file), ...args.map(escapeShellArg)];
      command = ArrayPrototypeJoin(escapedParts, " ");
    } else {
      command = file;
    }
    // Transform Node.js flags to Deno equivalents in shell commands that invoke Deno
    command = transformDenoShellCommand(command, options.env);
    // Set the shell, switches, and commands.
    if (process.platform === "win32") {
      if (typeof options.shell === "string") {
        file = options.shell;
      } else {
        file = Deno.env.get("comspec") || "cmd.exe";
      }
      // '/d /s /c' is used only for cmd.exe.
      if (/^(?:.*\\)?cmd(?:\.exe)?$/i.exec(file) !== null) {
        args = ["/d", "/s", "/c", `"${command}"`];
        windowsVerbatimArguments = true;
      } else {
        args = ["-c", command];
      }
    } else {
      /** TODO: add Android condition */
      if (typeof options.shell === "string") {
        file = options.shell;
      } else {
        file = "/bin/sh";