CVE-2023-40185 (JavaScript, published 2023-08-23T21:15Z)
Description: value: shescape is simple shell escape library for JavaScript. This may impact users that use Shescape on Windows in a threaded context. The vulnerability can result in Shescape escaping (or quoting) for the wrong shell, thus allowing attackers to bypass protections depending on the combination of expected and used shell. This bug has been patched in version 1.7.4.
Severity: nan (CVSS v3 base score: 8.6)
Weakness: CWE-150 \u2014 Improper Neutralization of Escape, Meta, or Control Sequences
The product receives input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could be interpreted as escape, meta, or control character sequences when they are sent to a downstream component.
Repository: https://github.com/ericcornelissen/shescape
Files changed:
CHANGELOG.md
index.js
src/executables.js
src/options.js
src/unix.js
src/win.js
test/_constants.cjs
test/e2e/_.js
test/e2e/_common.js
test/e2e/exec-file.test.js
test/e2e/exec.test.js
test/e2e/fork.test.js
test/e2e/spawn.test.js
test/integration/_generators.js
test/integration/escape-all/bash.test.js
test/integration/escape-all/cmd-exe.test.js
test/integration/escape-all/cmd.test.js
test/integration/escape-all/commonjs.test.js
test/integration/escape-all/csh.test.js
test/integration/escape-all/dash.test.js
test/integration/escape-all/invalid.test.js
test/integration/escape-all/powershell-exe.test.js
test/integration/escape-all/powershell.test.js
test/integration/escape-all/valid.test.js
test/integration/escape-all/zsh.test.js
test/integration/escape/bash.test.js
test/integration/escape/cmd-exe.test.js
test/integration/escape/cmd.test.js
test/integration/escape/commonjs.test.js
test/integration/escape/csh.test.js
test/integration/escape/dash.test.js
test/integration/escape/invalid.test.js
test/integration/escape/powershell-exe.test.js
test/integration/escape/powershell.test.js
test/integration/escape/valid.test.js
test/integration/escape/zsh.test.js
test/integration/quote-all/bash.test.js
test/integration/quote-all/cmd-exe.test.js
test/integration/quote-all/cmd.test.js
test/integration/quote-all/commonjs.test.js
test/integration/quote-all/csh.test.js
test/integration/quote-all/dash.test.js
test/integration/quote-all/invalid.test.js
test/integration/quote-all/powershell-exe.test.js
test/integration/quote-all/powershell.test.js
test/integration/quote-all/valid.test.js
test/integration/quote-all/zsh.test.js
test/integration/quote/bash.test.js
test/integration/quote/cmd-exe.test.js
test/integration/quote/cmd.test.js
test/integration/quote/commonjs.test.js
test/integration/quote/csh.test.js
test/integration/quote/dash.test.js
test/integration/quote/invalid.test.js
test/integration/quote/powershell-exe.test.js
test/integration/quote/powershell.test.js
test/integration/quote/valid.test.js
test/integration/quote/zsh.test.js
test/integration/testing/commonjs.test.js
test/integration/testing/functional.test.js
test/unit/executables/_.js
test/unit/executables/resolve.test.js
test/unit/options/parse-options.test.js
test/unit/unix/index.test.js
test/unit/win/index.test.js
Commit message: Provide explicit `$PATH` value to which (#1142)

Update the implementation of `resolveExecutable` to accept the
environment variables so that they can explicitly be provided to
which [1]. All internal code and tests have been updated accordingly,
no external changes.

This is in an effort to fix a problem where environment variables aren't
always passed on correctly to subprocesses. The problem is described in
[2], though if Shescape is used in a forked process on Windows it would
face the same problem. (As a problem found through the test runner, use
of which [1] in tests has been updated similarly.

As a result of this change, the integration and e2e test on Windows are
quite a bit slower (because now they actually look up the executable).
Hence, both of these suites have been updated to use separated test
files in order to better leverage AVA's concurrency - speeding up
individual files and avoiding timeouts as well as running tests quicker
if cores are available.

This change also allowed for including executables without a file
extension in the integration and e2e tests, further improving coverage.
(This replaced the `.EXE` coverage as extensionless executables are
resolved to `.EXE`, thus reducing test count without reducing coverage.)

As a side note, it was funny to see that `getShellName` name unit tests
for both Unix and Windows already generated and provided environment
variables (:

--
1. https://www.npmjs.com/package/which
2. https://github.com/avajs/ava/issues/3014

Vulnerable code:
    { options, process },
    { options, process },
export function resolveExecutable({ executable }, { exists, readlink, which }) {
    executable = which(executable);
 * @param {object} args.process The `process` values.
 * @param {object} args.process.env The environment variables.
  { options: { flagProtection, interpolation, shell }, process: { env } },
  const shellName = getShellName({ shell }, { resolveExecutable });
export function getShellName({ shell }, { resolveExecutable }) {
    { executable: shell },
export function getShellName({ shell }, { resolveExecutable }) {
    { executable: shell },
  "cmd.EXE",
  "powershell.EXE",
import { injectionStrings } from "../../testing.js";
export { constants, macros, injectionStrings };
/**
 * @overview Contains end-to-end tests of using Shescape with the child_process
 * package.
 * @license MIT
 */

import test from "ava";
import isCI from "is-ci";
import which from "which";

import { constants, macros, injectionStrings } from "./_.js";

const systemShells = constants.isWindows
  ? constants.shellsWindows
  : constants.shellsUnix;

const testArgs = ["harmless", ...injectionStrings];
const testShells = [false, true, ...systemShells];

for (const arg of testArgs) {
  test(macros.fork, { arg });

  for (const shell of testShells) {
    let runTest = test;
    try {
      if (!isCI && typeof shell === "string") {
        which.sync(shell);
      }
    } catch (_) {
      runTest = test.skip;
    }

    runTest(macros.exec, { arg, shell });
    runTest(macros.execSync, { arg, shell });
    runTest(macros.execFile, { arg, shell });
    runTest(macros.execFileSync, { arg, shell });
    runTest(macros.spawn, { arg, shell });
    runTest(macros.spawnSync, { arg, shell });
  }
}
import common from "../_constants.cjs";
/**
 * Returns the shells officially supported by Shescape for the current platform.
 *
 * @yields {string} Supported shells for the current platform.
 */
export function* platformShells() {
  if (common.isWindows) {
    yield* common.shellsWindows;
  } else {
    yield* common.shellsUnix;
  }
}
  if (common.isWindows) {
  shell = shell.toLowerCase();
    escape: Object.values(fixtures.escape[shell]).flat(),
    flag: Object.values(fixtures.flag[shell]).flat(),
    quote: Object.values(fixtures.quote[shell]).flat(),
 * @overview Contains integration tests for `shescape.escapeAll`.
import test from "ava";
import { arbitrary, constants, generate, macros } from "./_.js";
import { escapeAll as escapeAllCjs } from "../../index.cjs";

for (const shell of generate.platformShells()) {
  test(`inputs are escaped for ${shell}`, (t) => {
    for (const { expected, input, options } of generate.escapeExamples(shell)) {
      const result = escapeAll([input], options);
      t.deepEqual(result, [expected]);
    }
  });
}

testProp("invalid arguments", [arbitrary.shescapeOptions()], (t, options) => {
  for (const { value } of constants.illegalArguments) {
    t.throws(() => escapeAll([value], options), { instanceOf: TypeError });
    t.throws(() => escapeAll(value, options), { instanceOf: TypeError });
  }
});

test(macros.prototypePollution, (_, payload) => {
  escapeAll(["a"], payload);
});

testProp(
  "esm === cjs",
  [fc.array(arbitrary.shescapeArg()), arbitrary.shescapeOptions()],
  (t, args, options) => {
    const resultEsm = escapeAll(args, options);
    const resultCjs = escapeAllCjs(args, options);
    t.deepEqual(resultEsm, resultCjs);
  },
);
/**
 * @overview Contains integration tests for `shescape.escape`.
 * @license MIT
 */

import { testProp } from "@fast-check/ava";
import test from "ava";

import { arbitrary, constants, generate, macros } from "./_.js";

import { escape as escape } from "shescape";
import { escape as escapeCjs } from "../../index.cjs";

for (const shell of generate.platformShells()) {
  test(`input is escaped for ${shell}`, (t) => {
    for (const { expected, input, options } of generate.escapeExamples(shell)) {
      const result = escape(input, options);
      t.is(result, expected);
    }
  });
}

testProp(
  "return values",
  [arbitrary.shescapeArg(), arbitrary.shescapeOptions()],
  (t, arg, options) => {
    const result = escape(arg, options);
    t.is(typeof result, "string");
  },
);

testProp("invalid arguments", [arbitrary.shescapeOptions()], (t, options) => {
  for (const { value } of constants.illegalArguments) {
    t.throws(() => escape(value, options), { instanceOf: TypeError });
  }
});

test(macros.prototypePollution, (_, payload) => {
  escape("a", payload);
});

testProp(
  "esm === cjs",
  [arbitrary.shescapeArg(), arbitrary.shescapeOptions()],
  (t, arg, options) => {
    const resultEsm = escape(arg, options);
    const resultCjs = escapeCjs(arg, options);
    t.is(resultEsm, resultCjs);
  },
);
 * @overview Contains integration tests for `shescape.quoteAll`.
import test from "ava";
import { arbitrary, constants, generate, macros } from "./_.js";
import { quoteAll as quoteAllCjs } from "../../index.cjs";

for (const shell of generate.platformShells()) {
  test(`inputs are quoted for ${shell}`, (t) => {
    for (const { expected, input, options } of generate.quoteExamples(shell)) {
      const result = quoteAll([input], options);
      t.deepEqual(result, [expected]);
    }
  });
}

testProp("invalid arguments", [arbitrary.shescapeOptions()], (t, options) => {
  for (const { value } of constants.illegalArguments) {
    t.throws(() => quoteAll([value], options), { instanceOf: TypeError });
    t.throws(() => quoteAll(value, options), { instanceOf: TypeError });
  }
});

test(macros.prototypePollution, (_, payload) => {
  quoteAll(["a"], payload);
});

testProp(
  "esm === cjs",
  [fc.array(arbitrary.shescapeArg()), arbitrary.shescapeOptions()],
  (t, args, options) => {
    const resultEsm = quoteAll(args, options);
    const resultCjs = quoteAllCjs(args, options);
    t.deepEqual(resultEsm, resultCjs);
  },
);
/**
 * @overview Contains integration tests for `shescape.quote`.
 * @license MIT
 */

import { testProp } from "@fast-check/ava";
import test from "ava";

import { arbitrary, constants, generate, macros } from "./_.js";

import { quote as quote } from "shescape";
import { quote as quoteCjs } from "../../index.cjs";

for (const shell of generate.platformShells()) {
  test(`input is quoted for ${shell}`, (t) => {
    for (const { expected, input, options } of generate.quoteExamples(shell)) {
      const result = quote(input, options);
      t.is(result, expected);
    }
  });
}

testProp(
  "return value",
  [arbitrary.shescapeArg(), arbitrary.shescapeOptions()],
  (t, arg, options) => {
    const result = quote(arg, options);
    t.is(typeof result, "string");
  },
);

testProp("invalid arguments", [arbitrary.shescapeOptions()], (t, options) => {
  for (const { value } of constants.illegalArguments) {
    t.throws(() => quote(value, options), { instanceOf: TypeError });
  }
});

test(macros.prototypePollution, (_, payload) => {
  quote("a", payload);
});

testProp(
  "esm === cjs",
  [arbitrary.shescapeArg(), arbitrary.shescapeOptions()],
  (t, arg, options) => {
    const resultEsm = quote(arg, options);
    const resultCjs = quoteCjs(arg, options);
    t.is(resultEsm, resultCjs);
  },
);
import { arbitrary } from "./_.js";
import { shescape as stubscapeCjs } from "../../testing.cjs";

testProp(
  "escape (esm === cjs)",
  [arbitrary.shescapeArg(), arbitrary.shescapeOptions()],
  (t, arg, options) => {
    const resultEsm = stubscape.escape(arg, options);
    const resultCjs = stubscapeCjs.escape(arg, options);
    t.is(resultEsm, resultCjs);
  },
);

testProp(
  "escapeAll (esm === cjs)",
  [fc.array(arbitrary.shescapeArg()), arbitrary.shescapeOptions()],
  (t, args, options) => {
    const resultEsm = stubscape.escapeAll(args, options);
    const resultCjs = stubscapeCjs.escapeAll(args, options);
    t.deepEqual(resultEsm, resultCjs);
  },
);

testProp(
  "quote (esm === cjs)",
  [arbitrary.shescapeArg(), arbitrary.shescapeOptions()],
  (t, arg, options) => {
    const resultEsm = stubscape.quote(arg, options);
    const resultCjs = stubscapeCjs.quote(arg, options);
    t.is(resultEsm, resultCjs);
  },
);

testProp(
  "quoteAll (esm === cjs)",
  [fc.array(arbitrary.shescapeArg()), arbitrary.shescapeOptions()],
  (t, args, options) => {
    const resultEsm = stubscape.quoteAll(args, options);
    const resultCjs = stubscapeCjs.quoteAll(args, options);
    t.deepEqual(resultEsm, resultCjs);
  },
);
  t.context = { executable, linkedExecutable, resolvedExecutable };
  const { executable } = t.context;
  const args = { executable };
  t.true(t.context.deps.which.calledWithExactly(executable));
  const { executable, resolvedExecutable } = t.context;
  const args = { executable };
  const { executable, resolvedExecutable } = t.context;
  const args = { executable };
  const { executable, linkedExecutable, resolvedExecutable } = t.context;
  const args = { executable };
      return { options, process: { env } };
    const env = args.process.env;
        { shell: defaultShell },
        { shell: providedShell },
        { executable: shell },
    resolveExecutable.returns(path.join(basePath, shell));
    t.is(result, shell);
        { executable: shell },

Fixed code:
- Fix potential silent executable lookup failure for Windows. ([#1142])
[#1142]: https://github.com/ericcornelissen/shescape/pull/1142
    { env: process.env, options },
    { env: process.env, options },
 * @param {object} args.env The environment variables.
export function resolveExecutable(
  { env, executable },
  { exists, readlink, which },
) {
    executable = which(executable, { path: env.PATH || env.Path });
 * @param {object} args.env The environment variables.
  { env, options: { flagProtection, interpolation, shell } },
  const shellName = getShellName({ env, shell }, { resolveExecutable });
 * @param {object} args.env The environment variables.
export function getShellName({ env, shell }, { resolveExecutable }) {
    { env, executable: shell },
 * @param {object} args.env The environment variables.
export function getShellName({ env, shell }, { resolveExecutable }) {
    { env, executable: shell },
module.exports.binCmdNoExt = "cmd";
module.exports.binPowerShellNoExt = "powershell";
  module.exports.binCmdNoExt,
  module.exports.binPowerShellNoExt,
import * as common from "./_common.js";
export { common, constants, macros };
/**
 * @overview Provides common utilities for end-to-end tests.
 * @license MIT
 */

import process from "node:process";

import test from "ava";
import isCI from "is-ci";
import which from "which";

import { injectionStrings } from "../../testing.js";
import * as constants from "../_constants.cjs";

/**
 * Get a list of strings to use as arguments in end-to-end tests.
 *
 * @returns {string[]} A list of test arguments.
 */
export function getTestArgs() {
  return ["harmless", ...injectionStrings];
}

/**
 * Get the AVA test function to use for the given shell.
 *
 * @param {string} shell The shell to run a test for.
 * @returns {Function} An AVA `test` function.
 */
export function getTestFn(shell) {
  try {
    if (!isCI && typeof shell === "string") {
      which.sync(shell, { path: process.env.PATH || process.env.Path });
    }

    return test;
  } catch (_) {
    return test.skip;
  }
}

/**
 * Get a list of `shell` option values to use in end-to-end tests.
 *
 * @returns {(boolean | string)[]} A list of `shell` option values.
 */
export function getTestShells() {
  const systemShells = constants.isWindows
    ? constants.shellsWindows
    : constants.shellsUnix;

  return [false, true, ...systemShells];
}
/**
 * @overview Contains end-to-end tests of using Shescape with the child_process
 * package's `execFile` (and `execFileSync`) functions.
 * @license MIT
 */

import { common, macros } from "./_.js";

for (const shell of common.getTestShells()) {
  const test = common.getTestFn(shell);
  for (const arg of common.getTestArgs()) {
    test(macros.execFile, { arg, shell });
    test(macros.execFileSync, { arg, shell });
  }
}
/**
 * @overview Contains end-to-end tests of using Shescape with the child_process
 * package's `exec` (and `execSync`) functions.
 * @license MIT
 */

import { common, macros } from "./_.js";

for (const shell of common.getTestShells()) {
  const test = common.getTestFn(shell);
  for (const arg of common.getTestArgs()) {
    test(macros.exec, { arg, shell });
    test(macros.execSync, { arg, shell });
  }
}
/**
 * @overview Contains end-to-end tests of using Shescape with the child_process
 * package's `fork` functions.
 * @license MIT
 */

import { common, macros } from "./_.js";

for (const arg of common.getTestArgs()) {
  const test = common.getTestFn(null);
  test(macros.fork, { arg });
}
/**
 * @overview Contains end-to-end tests of using Shescape with the child_process
 * package's `spawn` (and `spawnSync`) functions.
 * @license MIT
 */

import { common, macros } from "./_.js";

for (const shell of common.getTestShells()) {
  const test = common.getTestFn(shell);
  for (const arg of common.getTestArgs()) {
    test(macros.spawn, { arg, shell });
    test(macros.spawnSync, { arg, shell });
  }
}
import { constants } from "./_.js";
  if (constants.isWindows) {
  let shellName = shell.toLowerCase();
  if (constants.isWindows) {
    shellName = shellName.endsWith(".exe") ? shellName : `${shellName}.exe`;
  }
    escape: Object.values(fixtures.escape[shellName]).flat(),
    flag: Object.values(fixtures.flag[shellName]).flat(),
    quote: Object.values(fixtures.quote[shellName]).flat(),
/**
 * @overview Contains integration tests for `shescape.escapeAll` for the
 * Bourne-again shell (Bash).
 * @license MIT
 */

import test from "ava";

import { constants, generate } from "../_.js";

import { escapeAll } from "shescape";

const runTest = constants.isWindows ? test.skip : test;

runTest(`input is escaped for ${constants.binBash}`, (t) => {
  for (const scenario of generate.escapeExamples(constants.binBash)) {
    const { expected, input, options } = scenario;
    const result = escapeAll([input], options);
    t.deepEqual(result, [expected]);
  }
});
/**
 * @overview Contains integration tests for `shescape.escapeAll` for the Windows
 * Command Prompt (with extension).
 * @license MIT
 */

import test from "ava";

import { constants, generate } from "../_.js";

import { escapeAll } from "shescape";

const runTest = constants.isWindows ? test : test.skip;

runTest(`input is escaped for ${constants.binCmd}`, (t) => {
  for (const scenario of generate.escapeExamples(constants.binCmd)) {
    const { expected, input, options } = scenario;
    const result = escapeAll([input], options);
    t.deepEqual(result, [expected]);
  }
});
/**
 * @overview Contains integration tests for `shescape.escapeAll` for the Windows
 * Command Prompt (without extension).
 * @license MIT
 */

import test from "ava";

import { constants, generate } from "../_.js";

import { escapeAll } from "shescape";

const runTest = constants.isWindows ? test : test.skip;

runTest(`input is escaped for ${constants.binCmdNoExt}`, (t) => {
  for (const scenario of generate.escapeExamples(constants.binCmdNoExt)) {
    const { expected, input, options } = scenario;
    const result = escapeAll([input], options);
    t.deepEqual(result, [expected]);
  }
});
/**
 * @overview Contains integration test for the CommonJS version of
 * `shescape.escapeAll`.
 * @license MIT
 */

import { testProp } from "@fast-check/ava";
import * as fc from "fast-check";

import { arbitrary } from "../_.js";

import { escapeAll } from "shescape";
import { escapeAll as escapeAllCjs } from "../../../index.cjs";

testProp(
  "esm === cjs",
  [fc.array(arbitrary.shescapeArg()), arbitrary.shescapeOptions()],
  (t, args, options) => {
    const resultEsm = escapeAll(args, options);
    const resultCjs = escapeAllCjs(args, options);
    t.deepEqual(resultEsm, resultCjs);
  },
);
/**
 * @overview Contains integration tests for `shescape.escapeAll` for the C shell
 * (csh).
 * @license MIT
 */

import test from "ava";

import { constants, generate } from "../_.js";

import { escapeAll } from "shescape";

const runTest = constants.isWindows ? test.skip : test;

runTest(`input is escaped for ${constants.binCsh}`, (t) => {
  for (const scenario of generate.escapeExamples(constants.binCsh)) {
    const { expected, input, options } = scenario;
    const result = escapeAll([input], options);
    t.deepEqual(result, [expected]);
  }
});
/**
 * @overview Contains integration tests for `shescape.escapeAll` for the Debian
 * Almquist shell (Dash).
 * @license MIT
 */

import test from "ava";

import { constants, generate } from "../_.js";

import { escapeAll } from "shescape";

const runTest = constants.isWindows ? test.skip : test;

runTest(`input is escaped for ${constants.binDash}`, (t) => {
  for (const scenario of generate.escapeExamples(constants.binDash)) {
    const { expected, input, options } = scenario;
    const result = escapeAll([input], options);
    t.deepEqual(result, [expected]);
  }
});
/**
 * @overview Contains integration tests for invalid use of `shescape.escapeAll`.
 * @license MIT
 */

import { testProp } from "@fast-check/ava";
import test from "ava";

import { arbitrary, constants, macros } from "../_.js";

import { escapeAll } from "shescape";

testProp("invalid arguments", [arbitrary.shescapeOptions()], (t, options) => {
  for (const { value } of constants.illegalArguments) {
    t.throws(() => escapeAll([value], options), { instanceOf: TypeError });
    t.throws(() => escapeAll(value, options), { instanceOf: TypeError });
  }
});

test(macros.prototypePollution, (_, payload) => {
  escapeAll(["a"], payload);
});
/**
 * @overview Contains integration tests for `shescape.escapeAll` for Windows
 * PowerShell (with extension).
 * @license MIT
 */

import test from "ava";

import { constants, generate } from "../_.js";

import { escapeAll } from "shescape";

const runTest = constants.isWindows ? test : test.skip;

runTest(`input is escaped for ${constants.binPowerShell}`, (t) => {
  for (const scenario of generate.escapeExamples(constants.binPowerShell)) {
    const { expected, input, options } = scenario;
    const result = escapeAll([input], options);
    t.deepEqual(result, [expected]);
  }
});
/**
 * @overview Contains integration tests for `shescape.escapeAll` for Windows
 * PowerShell (without extension).
 * @license MIT
 */

import test from "ava";

import { constants, generate } from "../_.js";

import { escapeAll } from "shescape";

const runTest = constants.isWindows ? test : test.skip;

runTest(`input is escaped for ${constants.binPowerShellNoExt}`, (t) => {
  for (const scenario of generate.escapeExamples(
    constants.binPowerShellNoExt,
  )) {
    const { expected, input, options } = scenario;
    const result = escapeAll([input], options);
    t.deepEqual(result, [expected]);
  }
});
 * @overview Contains integration tests for valid use of `shescape.escapeAll`.
import { arbitrary } from "../_.js";
/**
 * @overview Contains integration tests for `shescape.escapeAll` for the Z shell
 * (Zsh).
 * @license MIT
 */

import test from "ava";

import { constants, generate } from "../_.js";

import { escapeAll } from "shescape";

const runTest = constants.isWindows ? test.skip : test;

runTest(`input is escaped for ${constants.binZsh}`, (t) => {
  for (const scenario of generate.escapeExamples(constants.binZsh)) {
    const { expected, input, options } = scenario;
    const result = escapeAll([input], options);
    t.deepEqual(result, [expected]);
  }
});
/**
 * @overview Contains integration tests for `shescape.quote` for the
 * Bourne-again shell (Bash).
 * @license MIT
 */

import test from "ava";

import { constants, generate } from "../_.js";

import { quote } from "shescape";

const runTest = constants.isWindows ? test.skip : test;

runTest(`input is escaped for ${constants.binBash}`, (t) => {
  for (const scenario of generate.quoteExamples(constants.binBash)) {
    const { expected, input, options } = scenario;
    const result = quote(input, options);
    t.is(result, expected);
  }
});
/**
 * @overview Contains integration tests for `shescape.escape` for the Windows
 * Command Prompt (with extension).
 * @license MIT
 */

import test from "ava";

import { constants, generate } from "../_.js";

import { escape } from "shescape";

const runTest = constants.isWindows ? test : test.skip;

runTest(`input is escaped for ${constants.binCmd}`, (t) => {
  for (const scenario of generate.escapeExamples(constants.binCmd)) {
    const { expected, input, options } = scenario;
    const result = escape(input, options);
    t.is(result, expected);
  }
});
/**
 * @overview Contains integration tests for `shescape.escape` for the Windows
 * Command Prompt (without extension).
 * @license MIT
 */

import test from "ava";

import { constants, generate } from "../_.js";

import { escape } from "shescape";

const runTest = constants.isWindows ? test : test.skip;

runTest(`input is escaped for ${constants.binCmdNoExt}`, (t) => {
  for (const scenario of generate.escapeExamples(constants.binCmdNoExt)) {
    const { expected, input, options } = scenario;
    const result = escape(input, options);
    t.is(result, expected);
  }
});
/**
 * @overview Contains integration tests for the CommonJS version of
 * `shescape.escape`.
 * @license MIT
 */

import { testProp } from "@fast-check/ava";

import { arbitrary } from "../_.js";

import { escape } from "shescape";
import { escape as escapeCjs } from "../../../index.cjs";

testProp(
  "esm === cjs",
  [arbitrary.shescapeArg(), arbitrary.shescapeOptions()],
  (t, arg, options) => {
    const resultEsm = escape(arg, options);
    const resultCjs = escapeCjs(arg, options);
    t.is(resultEsm, resultCjs);
  },
);
/**
 * @overview Contains integration tests for `shescape.escape` for the C shell
 * (csh).
 * @license MIT
 */

import test from "ava";

import { constants, generate } from "../_.js";

import { escape } from "shescape";

const runTest = constants.isWindows ? test.skip : test;

runTest(`input is escaped for ${constants.binCsh}`, (t) => {
  for (const scenario of generate.escapeExamples(constants.binCsh)) {
    const { expected, input, options } = scenario;
    const result = escape(input, options);
    t.is(result, expected);
  }
});
/**
 * @overview Contains integration tests for `shescape.escape` for the Debian
 * Almquist shell (Dash).
 * @license MIT
 */

import test from "ava";

import { constants, generate } from "../_.js";

import { escape } from "shescape";

const runTest = constants.isWindows ? test.skip : test;

runTest(`input is escaped for ${constants.binDash}`, (t) => {
  for (const scenario of generate.escapeExamples(constants.binDash)) {
    const { expected, input, options } = scenario;
    const result = escape(input, options);
    t.is(result, expected);
  }
});
/**
 * @overview Contains integration tests for invalid use of `shescape.escape`.
 * @license MIT
 */

import { testProp } from "@fast-check/ava";
import test from "ava";

import { arbitrary, constants, macros } from "../_.js";

import { escape } from "shescape";

testProp("invalid arguments", [arbitrary.shescapeOptions()], (t, options) => {
  for (const { value } of constants.illegalArguments) {
    t.throws(() => escape(value, options), { instanceOf: TypeError });
  }
});

test(macros.prototypePollution, (_, payload) => {
  escape("a", payload);
});
/**
 * @overview Contains integration tests for `shescape.escape` for Windows
 * PowerShell (with extension).
 * @license MIT
 */

import test from "ava";

import { constants, generate } from "../_.js";

import { escape } from "shescape";

const runTest = constants.isWindows ? test : test.skip;

runTest(`input is escaped for ${constants.binPowerShell}`, (t) => {
  for (const scenario of generate.escapeExamples(constants.binPowerShell)) {
    const { expected, input, options } = scenario;
    const result = escape(input, options);
    t.is(result, expected);
  }
});
/**
 * @overview Contains integration tests for `shescape.escape` for Windows
 * PowerShell (without extension).
 * @license MIT
 */

import test from "ava";

import { constants, generate } from "../_.js";

import { escape } from "shescape";

const runTest = constants.isWindows ? test : test.skip;

runTest(`input is escaped for ${constants.binPowerShellNoExt}`, (t) => {
  for (const scenario of generate.escapeExamples(
    constants.binPowerShellNoExt,
  )) {
    const { expected, input, options } = scenario;
    const result = escape(input, options);
    t.is(result, expected);
  }
});
/**
 * @overview Contains integration tests for valid use of `shescape.escape`.
 * @license MIT
 */

import { testProp } from "@fast-check/ava";

import { arbitrary } from "../_.js";

import { escape } from "shescape";

testProp(
  "return values",
  [arbitrary.shescapeArg(), arbitrary.shescapeOptions()],
  (t, arg, options) => {
    const result = escape(arg, options);
    t.is(typeof result, "string");
  },
);
/**
 * @overview Contains integration tests for `shescape.escape` for the Z shell
 * (Zsh).
 * @license MIT
 */

import test from "ava";

import { constants, generate } from "../_.js";

import { escape } from "shescape";

const runTest = constants.isWindows ? test.skip : test;

runTest(`input is escaped for ${constants.binZsh}`, (t) => {
  for (const scenario of generate.escapeExamples(constants.binZsh)) {
    const { expected, input, options } = scenario;
    const result = escape(input, options);
    t.is(result, expected);
  }
});
/**
 * @overview Contains integration tests for `shescape.quoteAll` for the
 * Bourne-again shell (Bash).
 * @license MIT
 */

import test from "ava";

import { constants, generate } from "../_.js";

import { quoteAll } from "shescape";

const runTest = constants.isWindows ? test.skip : test;

runTest(`input is escaped for ${constants.binBash}`, (t) => {
  for (const scenario of generate.quoteExamples(constants.binBash)) {
    const { expected, input, options } = scenario;
    const result = quoteAll([input], options);
    t.deepEqual(result, [expected]);
  }
});
/**
 * @overview Contains integration tests for `shescape.quoteAll` for the Windows
 * Command Prompt (with extension).
 * @license MIT
 */

import test from "ava";

import { constants, generate } from "../_.js";

import { quoteAll } from "shescape";

const runTest = constants.isWindows ? test : test.skip;

runTest(`input is escaped for ${constants.binCmd}`, (t) => {
  for (const scenario of generate.quoteExamples(constants.binCmd)) {
    const { expected, input, options } = scenario;
    const result = quoteAll([input], options);
    t.deepEqual(result, [expected]);
  }
});
/**
 * @overview Contains integration tests for `shescape.quoteAll` for the Windows
 * Command Prompt (with extension).
 * @license MIT
 */

import test from "ava";

import { constants, generate } from "../_.js";

import { quoteAll } from "shescape";

const runTest = constants.isWindows ? test : test.skip;

runTest(`input is escaped for ${constants.binCmdNoExt}`, (t) => {
  for (const scenario of generate.quoteExamples(constants.binCmdNoExt)) {
    const { expected, input, options } = scenario;
    const result = quoteAll([input], options);
    t.deepEqual(result, [expected]);
  }
});
/**
 * @overview Contains integration test for the CommonJS version of
 * `shescape.quoteAll`.
 * @license MIT
 */

import { testProp } from "@fast-check/ava";
import * as fc from "fast-check";

import { arbitrary } from "../_.js";

import { quoteAll } from "shescape";
import { quoteAll as quoteAllCjs } from "../../../index.cjs";

testProp(
  "esm === cjs",
  [fc.array(arbitrary.shescapeArg()), arbitrary.shescapeOptions()],
  (t, args, options) => {
    const resultEsm = quoteAll(args, options);
    const resultCjs = quoteAllCjs(args, options);
    t.deepEqual(resultEsm, resultCjs);
  },
);
/**
 * @overview Contains integration tests for `shescape.quoteAll` for the C shell
 * (csh).
 * @license MIT
 */

import test from "ava";

import { constants, generate } from "../_.js";

import { quoteAll } from "shescape";

const runTest = constants.isWindows ? test.skip : test;

runTest(`input is escaped for ${constants.binCsh}`, (t) => {
  for (const scenario of generate.quoteExamples(constants.binCsh)) {
    const { expected, input, options } = scenario;
    const result = quoteAll([input], options);
    t.deepEqual(result, [expected]);
  }
});
/**
 * @overview Contains integration tests for `shescape.quoteAll` for the Debian
 * Almquist shell (Dash).
 * @license MIT
 */

import test from "ava";

import { constants, generate } from "../_.js";

import { quoteAll } from "shescape";

const runTest = constants.isWindows ? test.skip : test;

runTest(`input is escaped for ${constants.binDash}`, (t) => {
  for (const scenario of generate.quoteExamples(constants.binDash)) {
    const { expected, input, options } = scenario;
    const result = quoteAll([input], options);
    t.deepEqual(result, [expected]);
  }
});
/**
 * @overview Contains integration tests for invalid use of `shescape.quote`.
 * @license MIT
 */

import { testProp } from "@fast-check/ava";
import test from "ava";

import { arbitrary, constants, macros } from "../_.js";

import { quoteAll } from "shescape";

testProp("invalid arguments", [arbitrary.shescapeOptions()], (t, options) => {
  for (const { value } of constants.illegalArguments) {
    t.throws(() => quoteAll([value], options), { instanceOf: TypeError });
    t.throws(() => quoteAll(value, options), { instanceOf: TypeError });
  }
});

test(macros.prototypePollution, (_, payload) => {
  quoteAll(["a"], payload);
});
/**
 * @overview Contains integration tests for `shescape.quoteAll` for Windows
 * PowerShell (with extension).
 * @license MIT
 */

import test from "ava";

import { constants, generate } from "../_.js";

import { quoteAll } from "shescape";

const runTest = constants.isWindows ? test : test.skip;

runTest(`input is escaped for ${constants.binPowerShell}`, (t) => {
  for (const scenario of generate.quoteExamples(constants.binPowerShell)) {
    const { expected, input, options } = scenario;
    const result = quoteAll([input], options);
    t.deepEqual(result, [expected]);
  }
});
/**
 * @overview Contains integration tests for `shescape.quoteAll` for Windows
 * PowerShell (with extension).
 * @license MIT
 */

import test from "ava";

import { constants, generate } from "../_.js";

import { quoteAll } from "shescape";

const runTest = constants.isWindows ? test : test.skip;

runTest(`input is escaped for ${constants.binPowerShellNoExt}`, (t) => {
  for (const scenario of generate.quoteExamples(constants.binPowerShellNoExt)) {
    const { expected, input, options } = scenario;
    const result = quoteAll([input], options);
    t.deepEqual(result, [expected]);
  }
});
 * @overview Contains integration tests for valid use of `shescape.quoteAll`.
import { arbitrary } from "../_.js";
/**
 * @overview Contains integration tests for `shescape.quoteAll` for the Z shell
 * (Zsh).
 * @license MIT
 */

import test from "ava";

import { constants, generate } from "../_.js";

import { quoteAll } from "shescape";

const runTest = constants.isWindows ? test.skip : test;

runTest(`input is escaped for ${constants.binZsh}`, (t) => {
  for (const scenario of generate.quoteExamples(constants.binZsh)) {
    const { expected, input, options } = scenario;
    const result = quoteAll([input], options);
    t.deepEqual(result, [expected]);
  }
});
/**
 * @overview Contains integration tests for `shescape.escape` for the
 * Bourne-again shell (Bash).
 * @license MIT
 */

import test from "ava";

import { constants, generate } from "../_.js";

import { escape } from "shescape";

const runTest = constants.isWindows ? test.skip : test;

runTest(`input is escaped for ${constants.binBash}`, (t) => {
  for (const scenario of generate.escapeExamples(constants.binBash)) {
    const { expected, input, options } = scenario;
    const result = escape(input, options);
    t.is(result, expected);
  }
});
/**
 * @overview Contains integration tests for `shescape.quote` for the Windows
 * Command Prompt (with extension).
 * @license MIT
 */

import test from "ava";

import { constants, generate } from "../_.js";

import { quote } from "shescape";

const runTest = constants.isWindows ? test : test.skip;

runTest(`input is escaped for ${constants.binCmd}`, (t) => {
  for (const scenario of generate.quoteExamples(constants.binCmd)) {
    const { expected, input, options } = scenario;
    const result = quote(input, options);
    t.is(result, expected);
  }
});
/**
 * @overview Contains integration tests for `shescape.quote` for the Windows
 * Command Prompt (without extension).
 * @license MIT
 */

import test from "ava";

import { constants, generate } from "../_.js";

import { quote } from "shescape";

const runTest = constants.isWindows ? test : test.skip;

runTest(`input is escaped for ${constants.binCmdNoExt}`, (t) => {
  for (const scenario of generate.quoteExamples(constants.binCmdNoExt)) {
    const { expected, input, options } = scenario;
    const result = quote(input, options);
    t.is(result, expected);
  }
});
/**
 * @overview Contains integration tests for the CommonJS version of
 * `shescape.quote`.
 * @license MIT
 */

import { testProp } from "@fast-check/ava";

import { arbitrary } from "../_.js";

import { quote } from "shescape";
import { quote as quoteCjs } from "../../../index.cjs";

testProp(
  "esm === cjs",
  [arbitrary.shescapeArg(), arbitrary.shescapeOptions()],
  (t, arg, options) => {
    const resultEsm = quote(arg, options);
    const resultCjs = quoteCjs(arg, options);
    t.is(resultEsm, resultCjs);
  },
);
/**
 * @overview Contains integration tests for `shescape.quote` for the C shell
 * (csh).
 * @license MIT
 */

import test from "ava";

import { constants, generate } from "../_.js";

import { quote } from "shescape";

const runTest = constants.isWindows ? test.skip : test;

runTest(`input is escaped for ${constants.binCsh}`, (t) => {
  for (const scenario of generate.quoteExamples(constants.binCsh)) {
    const { expected, input, options } = scenario;
    const result = quote(input, options);
    t.is(result, expected);
  }
});
/**
 * @overview Contains integration tests for `shescape.quote` for the Debian
 * Almquist shell (Dash).
 * @license MIT
 */

import test from "ava";

import { constants, generate } from "../_.js";

import { quote } from "shescape";

const runTest = constants.isWindows ? test.skip : test;

runTest(`input is escaped for ${constants.binDash}`, (t) => {
  for (const scenario of generate.quoteExamples(constants.binDash)) {
    const { expected, input, options } = scenario;
    const result = quote(input, options);
    t.is(result, expected);
  }
});
/**
 * @overview Contains integration tests for invalid use of `shescape.quote`.
 * @license MIT
 */

import { testProp } from "@fast-check/ava";
import test from "ava";

import { arbitrary, constants, macros } from "../_.js";

import { quote } from "shescape";

testProp("invalid arguments", [arbitrary.shescapeOptions()], (t, options) => {
  for (const { value } of constants.illegalArguments) {
    t.throws(() => quote(value, options), { instanceOf: TypeError });
  }
});

test(macros.prototypePollution, (_, payload) => {
  quote("a", payload);
});
/**
 * @overview Contains integration tests for `shescape.quote` for Windows
 * PowerShell (with extension).
 * @license MIT
 */

import test from "ava";

import { constants, generate } from "../_.js";

import { quote } from "shescape";

const runTest = constants.isWindows ? test : test.skip;

runTest(`input is escaped for ${constants.binPowerShell}`, (t) => {
  for (const scenario of generate.quoteExamples(constants.binPowerShell)) {
    const { expected, input, options } = scenario;
    const result = quote(input, options);
    t.is(result, expected);
  }
});
/**
 * @overview Contains integration tests for `shescape.quote` for Windows
 * PowerShell (without extension).
 * @license MIT
 */

import test from "ava";

import { constants, generate } from "../_.js";

import { quote } from "shescape";

const runTest = constants.isWindows ? test : test.skip;

runTest(`input is escaped for ${constants.binPowerShellNoExt}`, (t) => {
  for (const scenario of generate.quoteExamples(constants.binPowerShellNoExt)) {
    const { expected, input, options } = scenario;
    const result = quote(input, options);
    t.is(result, expected);
  }
});
/**
 * @overview Contains integration tests for valid use of `shescape.quote`.
 * @license MIT
 */

import { testProp } from "@fast-check/ava";

import { arbitrary } from "../_.js";

import { quote } from "shescape";

testProp(
  "return value",
  [arbitrary.shescapeArg(), arbitrary.shescapeOptions()],
  (t, arg, options) => {
    const result = quote(arg, options);
    t.is(typeof result, "string");
  },
);
/**
 * @overview Contains integration tests for `shescape.quote` for the Z shell
 * (Zsh).
 * @license MIT
 */

import test from "ava";

import { constants, generate } from "../_.js";

import { quote } from "shescape";

const runTest = constants.isWindows ? test.skip : test;

runTest(`input is escaped for ${constants.binZsh}`, (t) => {
  for (const scenario of generate.quoteExamples(constants.binZsh)) {
    const { expected, input, options } = scenario;
    const result = quote(input, options);
    t.is(result, expected);
  }
});
/**
 * @overview Contains integration tests for the CommonJS version of the testing
 * implementation of shescape.
 * @license MIT
 */

import { testProp } from "@fast-check/ava";
import * as fc from "fast-check";

import { arbitrary } from "../_.js";

import { shescape as stubscape } from "shescape/testing";
import { shescape as stubscapeCjs } from "../../../testing.cjs";

testProp(
  "escape (esm === cjs)",
  [arbitrary.shescapeArg(), arbitrary.shescapeOptions()],
  (t, arg, options) => {
    const resultEsm = stubscape.escape(arg, options);
    const resultCjs = stubscapeCjs.escape(arg, options);
    t.is(resultEsm, resultCjs);
  },
);

testProp(
  "escapeAll (esm === cjs)",
  [fc.array(arbitrary.shescapeArg()), arbitrary.shescapeOptions()],
  (t, args, options) => {
    const resultEsm = stubscape.escapeAll(args, options);
    const resultCjs = stubscapeCjs.escapeAll(args, options);
    t.deepEqual(resultEsm, resultCjs);
  },
);

testProp(
  "quote (esm === cjs)",
  [arbitrary.shescapeArg(), arbitrary.shescapeOptions()],
  (t, arg, options) => {
    const resultEsm = stubscape.quote(arg, options);
    const resultCjs = stubscapeCjs.quote(arg, options);
    t.is(resultEsm, resultCjs);
  },
);

testProp(
  "quoteAll (esm === cjs)",
  [fc.array(arbitrary.shescapeArg()), arbitrary.shescapeOptions()],
  (t, args, options) => {
    const resultEsm = stubscape.quoteAll(args, options);
    const resultCjs = stubscapeCjs.quoteAll(args, options);
    t.deepEqual(resultEsm, resultCjs);
  },
);
import { arbitrary } from "../_.js";
/**
 * @overview Provides testing utilities.
 * @license MIT
 */

import * as arbitrary from "../../_arbitraries.js";

export { arbitrary };
import { testProp } from "@fast-check/ava";
import * as fc from "fast-check";
import { arbitrary } from "./_.js";

  const env = {
    PATH: "/bin:/usr/bin",
  };

  t.context = { env, executable, linkedExecutable, resolvedExecutable };
testProp(
  "env.PATH is defined",
  [arbitrary.env({ keys: ["PATH", "Path"] }), fc.string({ minLength: 1 })],
  (t, env, envPath) => {
    t.context.deps.which.resetHistory();

    env.PATH = envPath;

    const { executable } = t.context;
    const args = { env, executable };

    resolveExecutable(args, t.context.deps);
    t.is(t.context.deps.which.callCount, 1);
    t.true(
      t.context.deps.which.calledWithExactly(sinon.match.any, {
        path: env.PATH,
      }),
    );
  },
);

testProp(
  "env.PATH is not defined",
  [arbitrary.env({ keys: ["PATH", "Path"] }), fc.string({ minLength: 1 })],
  (t, env, envPath) => {
    t.context.deps.which.resetHistory();

    delete env.PATH;
    env.Path = envPath;

    const { executable } = t.context;
    const args = { env, executable };

    resolveExecutable(args, t.context.deps);
    t.is(t.context.deps.which.callCount, 1);
    t.true(
      t.context.deps.which.calledWithExactly(sinon.match.any, {
        path: env.Path,
      }),
    );
  },
);

  const { env, executable } = t.context;
  const args = { env, executable };
  t.true(t.context.deps.which.calledWithExactly(executable, sinon.match.any));
  const { env, executable, resolvedExecutable } = t.context;
  const args = { env, executable };
  const { env, executable, resolvedExecutable } = t.context;
  const args = { env, executable };
  const { env, executable, linkedExecutable, resolvedExecutable } = t.context;
  const args = { env, executable };
      return { env, options };
    const env = args.env;
        { env, shell: defaultShell },
    const env = args.env;
        { env, shell: providedShell },
        { env, executable: shell },
    const executable = shell.toLowerCase().endsWith(".exe")
      ? shell
      : `${shell}.exe`;

    resolveExecutable.returns(path.join(basePath, executable));
    t.is(result, executable);
        { env, executable: shell },