Skip to content
Closed
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 9 additions & 36 deletions .github/workflows/checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -299,42 +299,6 @@ jobs:
run: npm clean-install
- name: Run integration tests
run: npm run coverage:integration
test-mutation-unit:
name: Mutation (Unit)
runs-on: ubuntu-24.04
needs:
- test-unit
steps:
- name: Checkout repository
uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0
with:
persist-credentials: false
- name: Verify action checksums
uses: ./.github/actions/ghasum
- name: Install Node.js
uses: actions/setup-node@v6.3.0
with:
cache: npm
node-version-file: .nvmrc
- name: Cache Stryker incremental report
uses: actions/cache@v5.0.3
with:
path: .cache/stryker-incremental-unit.json
key: mutation-unit-${{ github.run_number }}
restore-keys: |
mutation-unit-
- name: Install dependencies
run: npm clean-install
- name: Run mutation tests
run: npm run mutation:unit
- name: Upload mutation report
uses: actions/upload-artifact@v7.0.0
if: ${{ failure() || success() }}
with:
name: mutation-unit-report
path: |
_reports/mutation/unit.html
.cache/stryker-incremental-unit.json
test-mutation-integration:
name: Mutation (integration)
runs-on: ubuntu-24.04
Expand Down Expand Up @@ -392,6 +356,15 @@ jobs:
node-version-file: .nvmrc
- name: Install dependencies
run: npm clean-install
- name: Run unit tests
run: |
cd node_modules/shescape-previous
git init .
git add .
git config --global user.name "John Doe"
git config --global user.email johndoe@example.com
git commit -m 'n/a'
git apply ../../2410.patch
- name: Run unit tests
run: npm run coverage:unit
transpile:
Expand Down
26 changes: 26 additions & 0 deletions 2410.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
diff --git a/src/internal/unix/bash.js b/src/internal/unix/bash.js
index 19fba6d..c1c57ce 100644
--- a/src/internal/unix/bash.js
+++ b/src/internal/unix/bash.js
@@ -15,7 +15,7 @@ function escapeArg(arg) {
.replace(/\n/gu, " ")
.replace(/\\/gu, "\\\\")
.replace(/(?<=^|\s)([#~])/gu, "\\$1")
- .replace(/(["$&'()*;<>?`{|])/gu, "\\$1")
+ .replace(/(["$&'()*;<>?`{|[\]])/gu, "\\$1")
.replace(/(?<=[:=])(~)(?=[\s+\-/0:=]|$)/gu, "\\$1")
.replace(/([\t ])/gu, "\\$1");
}
diff --git a/src/internal/unix/busybox.js b/src/internal/unix/busybox.js
index e61262f..0c91dc4 100644
--- a/src/internal/unix/busybox.js
+++ b/src/internal/unix/busybox.js
@@ -15,7 +15,7 @@ function escapeArg(arg) {
.replace(/\n/gu, " ")
.replace(/\\/gu, "\\\\")
.replace(/(?<=^|\s)([#~])/gu, "\\$1")
- .replace(/(["$&'()*;<>?`|])/gu, "\\$1")
+ .replace(/(["$&'()*;<>?`|[\]])/gu, "\\$1")
.replace(/([\t ])/gu, "\\$1");
}

11 changes: 5 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@
"prettier": "3.7.3",
"publint": "0.3.18",
"rollup": "4.60.0",
"shescape-previous": "npm:shescape@2.1.10",
"shescape-previous": "npm:shescape@2.1.7",
"sinon": "21.0.3"
},
"scripts": {
Expand Down Expand Up @@ -139,7 +139,7 @@
"mutation:unit": "stryker run config/stryker/unit.js",
"mutation:integration": "npm run transpile && stryker run config/stryker/integration.js",
"test": "npm-run-all test:*",
"test:unit": "ava test/unit/**/*.test.js",
"test:unit": "ava --timeout 59m test/unit/**/*.test.js",
"test:integration": "npm run transpile && ava test/integration/**/*.test.js --timeout 2m",
"test:e2e": "node script/busybox-sh.js && node script/double-link-sh.js && ava test/e2e/**/*.test.js --timeout 1m",
"test:compat": "npm-run-all test:compat:*",
Expand Down
55 changes: 55 additions & 0 deletions test/unit/unix/bash.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/**
* @overview Contains (additional) unit tests for the escaping functionality for
* the Bourne-again shell (Bash).
* @license MIT
*/

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

import * as old from "../../../node_modules/shescape-previous/src/internal/unix/bash.js";
import * as upd from "../../../src/internal/unix/bash.js";

const numRuns = 5_000_000;

testProp(
"escape functionality is unchanged",
[fc.string()],
(t, arg) => {
const updFn = upd.getEscapeFunction();
const oldFn = old.getEscapeFunction();

const got = updFn(arg).replace(/(?<=[:=])(\\~)(?!\\?[\s+\-/0:=]|$)/gu, "~");
const want = oldFn(arg);
t.is(got, want);
},
{ numRuns },
);

testProp(
"quote functionality is unchanged",
[fc.string()],
(t, arg) => {
const updFn = upd.getQuoteFunction();
const oldFn = old.getQuoteFunction();

const got = updFn[0](updFn[1](arg));
const want = oldFn[0](oldFn[1](arg));
t.is(got, want);
},
{ numRuns },
);

testProp(
"flag protection functionality is unchanged",
[fc.string()],
(t, arg) => {
const updFn = upd.getFlagProtectionFunction();
const oldFn = old.getFlagProtectionFunction();

const got = updFn(arg);
const want = oldFn(arg);
t.is(got, want);
},
{ numRuns },
);
55 changes: 55 additions & 0 deletions test/unit/unix/busybox.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/**
* @overview Contains (additional) unit tests for the escaping functionality for
* the BusyBox shell.
* @license MIT
*/

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

import * as old from "../../../node_modules/shescape-previous/src/internal/unix/busybox.js";
import * as upd from "../../../src/internal/unix/busybox.js";

const numRuns = 5_000_000;

testProp(
"escape functionality is unchanged",
[fc.string()],
(t, arg) => {
const updFn = upd.getEscapeFunction();
const oldFn = old.getEscapeFunction();

const got = updFn(arg);
const want = oldFn(arg);
t.is(got, want);
},
{ numRuns },
);

testProp(
"quote functionality is unchanged",
[fc.string()],
(t, arg) => {
const updFn = upd.getQuoteFunction();
const oldFn = old.getQuoteFunction();

const got = updFn[0](updFn[1](arg));
const want = oldFn[0](oldFn[1](arg));
t.is(got, want);
},
{ numRuns },
);

testProp(
"flag protection functionality is unchanged",
[fc.string()],
(t, arg) => {
const updFn = upd.getFlagProtectionFunction();
const oldFn = old.getFlagProtectionFunction();

const got = updFn(arg);
const want = oldFn(arg);
t.is(got, want);
},
{ numRuns },
);
44 changes: 44 additions & 0 deletions test/unit/unix/csh.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,54 @@ import { TextDecoder } from "node:util";
import { testProp } from "@fast-check/ava";
import * as fc from "fast-check";

import * as old from "../../../node_modules/shescape-previous/src/internal/unix/csh.js";
import * as csh from "../../../src/internal/unix/csh.js";

const numRuns = 5_000_000;
const textDecoder = new TextDecoder("utf-8", { fatal: true });

testProp(
"escape functionality is unchanged",
[fc.string()],
(t, arg) => {
const updFn = csh.getEscapeFunction();
const oldFn = old.getEscapeFunction();

const got = updFn(arg).replace(/\\!$/gu, "!");
const want = oldFn(arg);
t.is(got, want);
},
{ numRuns },
);

testProp(
"quote functionality is unchanged",
[fc.string()],
(t, arg) => {
const updFn = csh.getQuoteFunction();
const oldFn = old.getQuoteFunction();

const got = updFn[0](updFn[1](arg));
const want = oldFn[0](oldFn[1](arg));
t.is(got, want);
},
{ numRuns },
);

testProp(
"flag protection functionality is unchanged",
[fc.string()],
(t, arg) => {
const updFn = csh.getFlagProtectionFunction();
const oldFn = old.getFlagProtectionFunction();

const got = updFn(arg);
const want = oldFn(arg);
t.is(got, want);
},
{ numRuns },
);

testProp(
"characters with 0xA0 when utf-8 encoded",
[
Expand Down
31 changes: 31 additions & 0 deletions test/unit/unix/no-shell.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,39 @@
import { testProp } from "@fast-check/ava";
import * as fc from "fast-check";

import * as old from "../../../node_modules/shescape-previous/src/internal/unix/no-shell.js";
import * as nosh from "../../../src/internal/unix/no-shell.js";

const numRuns = 5_000_000;

testProp(
"escape functionality is unchanged",
[fc.string()],
(t, arg) => {
const updFn = nosh.getEscapeFunction();
const oldFn = old.getEscapeFunction();

const got = updFn(arg);
const want = oldFn(arg);
t.is(got, want);
},
{ numRuns },
);

testProp(
"flag protection functionality is unchanged",
[fc.string()],
(t, arg) => {
const updFn = nosh.getFlagProtectionFunction();
const oldFn = old.getFlagProtectionFunction();

const got = updFn(arg);
const want = oldFn(arg);
t.is(got, want);
},
{ numRuns },
);

testProp("quote function", [fc.string()], (t, arg) => {
const expected = {
instanceOf: Error,
Expand Down
55 changes: 55 additions & 0 deletions test/unit/win/cmd.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/**
* @overview Contains (additional) unit tests for the escaping functionality for
* the the Windows Command Prompt.
* @license MIT
*/

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

import * as old from "../../../node_modules/shescape-previous/src/internal/win/cmd.js";
import * as upd from "../../../src/internal/win/cmd.js";

const numRuns = 5_000_000;

testProp(
"escape functionality is unchanged",
[fc.string()],
(t, arg) => {
const updFn = upd.getEscapeFunction();
const oldFn = old.getEscapeFunction();

const got = updFn(arg);
const want = oldFn(arg);
t.is(got, want);
},
{ numRuns },
);

testProp(
"quote functionality is unchanged",
[fc.string()],
(t, arg) => {
const updFn = upd.getQuoteFunction();
const oldFn = old.getQuoteFunction();

const got = updFn[0](updFn[1](arg));
const want = oldFn[0](oldFn[1](arg));
t.is(got, want);
},
{ numRuns },
);

testProp(
"flag protection functionality is unchanged",
[fc.string()],
(t, arg) => {
const updFn = upd.getFlagProtectionFunction();
const oldFn = old.getFlagProtectionFunction();

const got = updFn(arg);
const want = oldFn(arg);
t.is(got, want);
},
{ numRuns },
);
Loading
Loading