Comparing version 1.7.2 to 2.0.0-rc1
@@ -10,6 +10,5 @@ # Changelog | ||
- _No changes yet_ | ||
## [1.7.2] - 2023-07-07 | ||
- BREAKING CHANGE: Drop support for Node.js `^10.13.0`, `^12`, `14.0.0` through | ||
`14.18.0`, and `16.0.0` through `16.13.0`. ([#963]) | ||
- Bump dependency `which` from v2 to v3. ([#963]) | ||
- Fix incorrect escaping of `"` when escaping for CMD. ([#1022]) | ||
@@ -278,2 +277,3 @@ - Fix incorrect escaping of `"` when escaping for PowerShell. ([#1023]) | ||
[#936]: https://github.com/ericcornelissen/shescape/pull/936 | ||
[#963]: https://github.com/ericcornelissen/shescape/pull/963 | ||
[#969]: https://github.com/ericcornelissen/shescape/pull/969 | ||
@@ -280,0 +280,0 @@ [#982]: https://github.com/ericcornelissen/shescape/pull/982 |
@@ -9,16 +9,20 @@ # Shescape API | ||
## `quote(arg[, options])` | ||
## `Shescape([options])` | ||
The `quote` function escapes and quotes a single argument and optionally takes | ||
an [options] object. `quote` always returns a string, the escaped and quoted | ||
argument. | ||
The class to create a `shescape` instance for quoting and escaping. optionally | ||
takes an [options] object. | ||
### `Shescape#quote(arg)` | ||
The `quote` function escapes and quotes a single argument. Always returns a | ||
string, the escaped and quoted argument. | ||
Non-string arguments are converted to strings; an error is thrown if this is not | ||
possible. | ||
## `quoteAll(args[, options])` | ||
### `Shescape#quoteAll(args)` | ||
The `quoteAll` function escapes and quotes an array of arguments and optionally | ||
takes an [options] object. `quoteAll` always returns an array of strings (same | ||
length as the input array), the escaped and quoted arguments. | ||
The `quoteAll` function escapes and quotes an array of arguments. Always returns | ||
an array of strings (same length as the input array), the escaped and quoted | ||
arguments. | ||
@@ -28,6 +32,6 @@ Non-array inputs are converted to single-value arrays. Non-string arguments are | ||
## `escape(arg[, options])` | ||
### `Shescape#escape(arg)` | ||
The `escape` function escapes a single argument and optionally takes an | ||
[options] object. `escape` always returns a string, the escaped argument. | ||
The `escape` function escapes a single argument. Always returns a string, the | ||
escaped argument. | ||
@@ -37,7 +41,6 @@ Non-string arguments are converted to strings; an error is thrown if this is not | ||
## `escapeAll(args[, options])` | ||
### `Shescape#escapeAll(args)` | ||
The `escapeAll` function escapes an array of arguments and optionally takes an | ||
[options] object. `escapeAll` always returns an array of strings (same length as | ||
the input array), the escaped arguments. | ||
The `escapeAll` function escapes an array of arguments. Always returns an array | ||
of strings (same length as the input array), the escaped arguments. | ||
@@ -56,3 +59,3 @@ Non-array inputs are converted to single-value arrays. Non-string arguments are | ||
It is recommended to set this to `true` unless you use (and verified the command | ||
It is recommended to leave this `true` unless you use (and verified the command | ||
you invoke supports) the special `--` option. | ||
@@ -63,3 +66,3 @@ | ||
| Used | Yes | Yes | | ||
| Default | `false` | `false` | | ||
| Default | `true` | `true` | | ||
| Type | `boolean` | `boolean` | | ||
@@ -72,3 +75,3 @@ | ||
It is recommended to set this to `true` if you're unsure whether or not shell | ||
It is recommended to leave this `true` if you're unsure whether or not shell | ||
interpolation is enabled. | ||
@@ -79,3 +82,3 @@ | ||
| Used | Yes | No | | ||
| Default | `false` | n/a | | ||
| Default | `true` | n/a | | ||
| Type | `boolean` | n/a | | ||
@@ -82,0 +85,0 @@ |
@@ -19,12 +19,17 @@ # Shescape Recipes | ||
When using `child_process.exec` without the `options` argument, use | ||
`shescape.quote` to escape all user input in the command string. | ||
`Shescape#quote` to escape all user input in the command string. | ||
```javascript | ||
import { exec } from "node:child_process"; | ||
import * as shescape from "shescape"; | ||
import { Shescape } from "shescape"; | ||
/* 1. Collect user input */ | ||
/* 1. Set up */ | ||
const shescape = new Shescape({ | ||
interpolation: false, | ||
}); | ||
/* 2. Collect user input */ | ||
const userInput = "&& ls"; | ||
/* 2. Execute shell command */ | ||
/* 3. Execute shell command */ | ||
exec(`echo Hello ${shescape.quote(userInput)}`, (error, stdout) => { | ||
@@ -43,10 +48,10 @@ if (error) { | ||
When using `child_process.exec` with the `options` argument, use | ||
`shescape.quote` to escape all user input in the command string. Provide the | ||
`options` argument to `shescape.quote` as well. | ||
`Shescape#quote` to escape all user input in the command string. Provide the | ||
`options` argument to `Shescape#quote` as well. | ||
```javascript | ||
import { exec } from "node:child_process"; | ||
import * as shescape from "shescape"; | ||
import { Shescape } from "shescape"; | ||
/* 1. Set up configuration */ | ||
/* 1. Set up */ | ||
const execOptions = { | ||
@@ -56,7 +61,10 @@ // Example configuration for `exec` | ||
}; | ||
const shescapeOptions = { | ||
const shescape = new Shescape({ | ||
interpolation: false, | ||
// Set options for Shescape first, then add the options for `exec`. DO NOT set | ||
// any keys from the child_process API here. | ||
...execOptions, | ||
}; | ||
}); | ||
@@ -68,3 +76,3 @@ /* 2. Collect user input */ | ||
exec( | ||
`echo Hello ${shescape.quote(userInput, shescapeOptions)}`, | ||
`echo Hello ${shescape.quote(userInput)}`, | ||
execOptions, | ||
@@ -85,12 +93,17 @@ (error, stdout) => { | ||
When using `child_process.execSync` without the `options` argument, use | ||
`shescape.quote` to escape all user input in the command string. | ||
`Shescape#quote` to escape all user input in the command string. | ||
```javascript | ||
import { execSync } from "node:child_process"; | ||
import * as shescape from "shescape"; | ||
import { Shescape } from "shescape"; | ||
/* 1. Collect user input */ | ||
/* 1. Set up */ | ||
const shescape = new Shescape({ | ||
interpolation: false, | ||
}); | ||
/* 2. Collect user input */ | ||
const userInput = "&& ls"; | ||
/* 2. Execute shell command */ | ||
/* 3. Execute shell command */ | ||
try { | ||
@@ -108,10 +121,10 @@ const stdout = execSync(`echo Hello ${shescape.quote(userInput)}`); | ||
When using `child_process.execSync` with the `options` argument, use | ||
`shescape.quote` to escape all user input in the command string. Provide the | ||
`options` argument to `shescape.quote` as well. | ||
`Shescape#quote` to escape all user input in the command string. Provide the | ||
`options` argument to `Shescape#quote` as well. | ||
```javascript | ||
import { execSync } from "node:child_process"; | ||
import * as shescape from "shescape"; | ||
import { Shescape } from "shescape"; | ||
/* 1. Set up configuration */ | ||
/* 1. Set up */ | ||
const execOptions = { | ||
@@ -121,7 +134,10 @@ // Example configuration for `execSync` | ||
}; | ||
const shescapeOptions = { | ||
const shescape = new Shescape({ | ||
interpolation: false, | ||
// Set options for Shescape first, then add the options for `execSync`. DO NOT | ||
// set any keys from the child_process API here. | ||
...execOptions, | ||
}; | ||
}); | ||
@@ -134,3 +150,3 @@ /* 2. Collect user input */ | ||
const stdout = execSync( | ||
`echo Hello ${shescape.quote(userInput, shescapeOptions)}`, | ||
`echo Hello ${shescape.quote(userInput)}`, | ||
execOptions, | ||
@@ -145,9 +161,9 @@ ); | ||
#### With `shescape.escape` | ||
#### With `Shescape#escape` | ||
If you find yourself in a situation where the inputted argument to `exec` cannot | ||
be quoted, you can use `shescape.escape` with `interpolation: true` instead. | ||
be quoted, you can use `Shescape#escape` with `interpolation: true` instead. | ||
> **Warning**: If possible, it is advised to rewrite your code so that you can | ||
> use `shescape.quote` as shown above. Or use a different function from the | ||
> use `Shescape#quote` as shown above. Or use a different function from the | ||
> `child_process` API, as shown further down below. | ||
@@ -157,8 +173,8 @@ | ||
import { exec } from "node:child_process"; | ||
import * as shescape from "shescape"; | ||
import { Shescape } from "shescape"; | ||
/* 1. Set up configuration */ | ||
const options = { | ||
/* 1. Set up */ | ||
const shescape = new Shescape({ | ||
interpolation: true, | ||
}; | ||
}); | ||
@@ -169,3 +185,3 @@ /* 2. Collect user input */ | ||
/* 3. Execute shell command */ | ||
exec(`echo Hello ${shescape.escape(userInput, options)}`, (error, stdout) => { | ||
exec(`echo Hello ${shescape.escape(userInput)}`, (error, stdout) => { | ||
if (error) { | ||
@@ -185,3 +201,3 @@ console.error(`An error occurred: ${error}`); | ||
- On Windows, cmd.exe does not support whitespace preservation. So, if argument | ||
splitting is a concern, use `shescape.quote` instead. | ||
splitting is a concern, use `Shescape#quote` instead. | ||
- On Windows, PowerShell will strip whitespace at the beginning of arguments. | ||
@@ -194,12 +210,17 @@ | ||
When using `child_process.execFile` without the `options` argument, use | ||
`shescape.escapeAll` to escape all `args`. | ||
`Shescape#escapeAll` to escape all `args`. | ||
```javascript | ||
import { execFile } from "node:child_process"; | ||
import * as shescape from "shescape"; | ||
import { Shescape } from "shescape"; | ||
/* 1. Collect user input */ | ||
/* 1. Set up */ | ||
const shescape = new Shescape({ | ||
interpolation: false, | ||
}); | ||
/* 2. Collect user input */ | ||
const userInput = "\x00world"; | ||
/* 2. Execute shell command */ | ||
/* 3. Execute shell command */ | ||
execFile( | ||
@@ -223,4 +244,4 @@ "echo", | ||
the `options` argument to Shescape as well. If `options.shell` is set to a | ||
truthy value, use `shescape.quoteAll` to escape all `args`. If `options.shell` | ||
is set to a falsy value (or omitted), use `shescape.escapeAll` to escape all | ||
truthy value, use `Shescape#quoteAll` to escape all `args`. If `options.shell` | ||
is set to a falsy value (or omitted), use `Shescape#escapeAll` to escape all | ||
`args`. | ||
@@ -230,5 +251,5 @@ | ||
import { execFile } from "node:child_process"; | ||
import * as shescape from "shescape"; | ||
import { Shescape } from "shescape"; | ||
/* 1. Set up configuration */ | ||
/* 1. Set up */ | ||
const execFileOptions = { | ||
@@ -238,7 +259,10 @@ // Example configuration for `execFile` | ||
}; | ||
const shescapeOptions = { | ||
const shescape = new Shescape({ | ||
interpolation: false, | ||
// Set options for Shescape first, then add the options for `execFile`. DO NOT | ||
// set any keys from the child_process API here. | ||
...execFileOptions, | ||
}; | ||
}); | ||
@@ -253,5 +277,5 @@ /* 2. Collect user input */ | ||
? // When the `shell` option is configured, arguments should be quoted | ||
shescape.quoteAll(["Hello", userInput], shescapeOptions) | ||
shescape.quoteAll(["Hello", userInput]) | ||
: // When the `shell` option is NOT configured, arguments should NOT be quoted | ||
shescape.escapeAll(["Hello", userInput], shescapeOptions), | ||
shescape.escapeAll(["Hello", userInput]), | ||
execFileOptions, | ||
@@ -272,12 +296,17 @@ (error, stdout) => { | ||
When using `child_process.execFileSync` without the `options` argument, use | ||
`shescape.escapeAll` to escape all `args`. | ||
`Shescape#escapeAll` to escape all `args`. | ||
```javascript | ||
import { execFileSync } from "node:child_process"; | ||
import * as shescape from "shescape"; | ||
import { Shescape } from "shescape"; | ||
/* 1. Collect user input */ | ||
/* 1. Set up */ | ||
const shescape = new Shescape({ | ||
interpolation: false, | ||
}); | ||
/* 2. Collect user input */ | ||
const userInput = "\x00world"; | ||
/* 2. Execute shell command */ | ||
/* 3. Execute shell command */ | ||
try { | ||
@@ -299,4 +328,4 @@ const stdout = execFileSync( | ||
the `options` argument to Shescape as well. If `options.shell` is set to a | ||
truthy value, use `shescape.quoteAll` to escape all `args`. If `options.shell` | ||
is set to a falsy value (or omitted), use `shescape.escapeAll` to escape all | ||
truthy value, use `Shescape#quoteAll` to escape all `args`. If `options.shell` | ||
is set to a falsy value (or omitted), use `Shescape#escapeAll` to escape all | ||
`args`. | ||
@@ -310,5 +339,5 @@ | ||
import { execFileSync } from "node:child_process"; | ||
import * as shescape from "shescape"; | ||
import { Shescape } from "shescape"; | ||
/* 1. Set up configuration */ | ||
/* 1. Set up */ | ||
const execFileOptions = { | ||
@@ -318,7 +347,10 @@ // Example configuration for `execFileSync` | ||
}; | ||
const shescapeOptions = { | ||
const shescape = new Shescape({ | ||
interpolation: false, | ||
// Set options for Shescape first, then add the options for `execFileSync`. DO | ||
// NOT set any keys from the child_process API here. | ||
...execFileOptions, | ||
}; | ||
}); | ||
@@ -334,5 +366,5 @@ /* 2. Collect user input */ | ||
? // When the `shell` option is configured, arguments should be quoted | ||
shescape.quoteAll(["Hello", userInput], shescapeOptions) | ||
shescape.quoteAll(["Hello", userInput]) | ||
: // When the `shell` option is NOT configured, arguments should NOT be quoted | ||
shescape.escapeAll(["Hello", userInput], shescapeOptions), | ||
shescape.escapeAll(["Hello", userInput]), | ||
execFileOptions, | ||
@@ -352,3 +384,3 @@ ); | ||
When using `child_process.fork` without the `options` argument, use | ||
`shescape.escapeAll` to escape all `args`. | ||
`Shescape#escapeAll` to escape all `args`. | ||
@@ -360,3 +392,3 @@ ```javascript | ||
import { argv } from "node:process"; | ||
import * as shescape from "shescape"; | ||
import { Shescape } from "shescape"; | ||
@@ -367,6 +399,11 @@ if (argv[2] === "Hello") { | ||
} else { | ||
/* 1. Collect user input */ | ||
/* 1. Set up */ | ||
const shescape = new Shescape({ | ||
interpolation: false, | ||
}); | ||
/* 2. Collect user input */ | ||
const userInput = "\x00world"; | ||
/* 2. Execute a Node.js module */ | ||
/* 3. Execute a Node.js module */ | ||
const echo = fork("echo.js", shescape.escapeAll(["Hello", userInput, "!"])); | ||
@@ -382,3 +419,3 @@ echo.on("error", (error) => { | ||
When using `child_process.fork` with the `options` argument, use | ||
`shescape.escapeAll` to escape all `args`. | ||
`Shescape#escapeAll` to escape all `args`. | ||
@@ -390,3 +427,3 @@ ```javascript | ||
import { argv } from "node:process"; | ||
import * as shescape from "shescape"; | ||
import { Shescape } from "shescape"; | ||
@@ -397,3 +434,3 @@ if (argv[2] === "Hello") { | ||
} else { | ||
/* 1. Set up configuration */ | ||
/* 1. Set up */ | ||
const forkOptions = { | ||
@@ -403,7 +440,10 @@ // Example configuration for `fork` | ||
}; | ||
const shescapeOptions = { | ||
const shescape = new Shescape({ | ||
interpolation: false, | ||
// Set options for Shescape first, then add the options for `fork`. DO NOT set | ||
// any keys from the child_process API here. | ||
...forkOptions, | ||
}; | ||
}); | ||
@@ -416,3 +456,3 @@ /* 2. Collect user input */ | ||
"echo.js", | ||
shescape.escapeAll(["Hello", userInput, "!"], shescapeOptions), | ||
shescape.escapeAll(["Hello", userInput, "!"]), | ||
forkOptions, | ||
@@ -431,12 +471,17 @@ ); | ||
When using `child_process.spawn` without the `options` argument, use | ||
`shescape.escapeAll` to escape all `args`. | ||
`Shescape#escapeAll` to escape all `args`. | ||
```javascript | ||
import { spawn } from "node:child_process"; | ||
import * as shescape from "shescape"; | ||
import { Shescape } from "shescape"; | ||
/* 1. Collect user input */ | ||
/* 1. Set up */ | ||
const shescape = new Shescape({ | ||
interpolation: false, | ||
}); | ||
/* 2. Collect user input */ | ||
const userInput = "\x00world"; | ||
/* 2. Execute shell command */ | ||
/* 3. Execute shell command */ | ||
const echo = spawn("echo", shescape.escapeAll(["Hello", userInput, "!"])); | ||
@@ -456,4 +501,4 @@ echo.on("error", (error) => { | ||
the `options` argument to Shescape as well. If `options.shell` is set to a | ||
truthy value, use `shescape.quoteAll` to escape all `args`. If `options.shell` | ||
is set to a falsy value (or omitted), use `shescape.escapeAll` to escape all | ||
truthy value, use `Shescape#quoteAll` to escape all `args`. If `options.shell` | ||
is set to a falsy value (or omitted), use `Shescape#escapeAll` to escape all | ||
`args`. | ||
@@ -463,5 +508,5 @@ | ||
import { spawn } from "node:child_process"; | ||
import * as shescape from "shescape"; | ||
import { Shescape } from "shescape"; | ||
/* 1. Set up configuration */ | ||
/* 1. Set up */ | ||
const spawnOptions = { | ||
@@ -471,7 +516,10 @@ // Example configuration for `spawn` | ||
}; | ||
const shescapeOptions = { | ||
const shescape = new Shescape({ | ||
interpolation: false, | ||
// Set options for Shescape first, then add the options for `spawn`. DO NOT | ||
// set any keys from the child_process API here. | ||
...spawnOptions, | ||
}; | ||
}); | ||
@@ -486,5 +534,5 @@ /* 2. Collect user input */ | ||
? // When the `shell` option is configured, arguments should be quoted | ||
shescape.quoteAll(["Hello", userInput], shescapeOptions) | ||
shescape.quoteAll(["Hello", userInput]) | ||
: // When the `shell` option is NOT configured, arguments should NOT be quoted | ||
shescape.escapeAll(["Hello", userInput], shescapeOptions), | ||
shescape.escapeAll(["Hello", userInput]), | ||
spawnOptions, | ||
@@ -504,12 +552,17 @@ ); | ||
When using `child_process.spawnSync` without the `options` argument, use | ||
`shescape.escapeAll` to escape all `args`. | ||
`Shescape#escapeAll` to escape all `args`. | ||
```javascript | ||
import { spawnSync } from "node:child_process"; | ||
import * as shescape from "shescape"; | ||
import { Shescape } from "shescape"; | ||
/* 1. Collect user input */ | ||
/* 1. Set up */ | ||
const shescape = new Shescape({ | ||
interpolation: false, | ||
}); | ||
/* 2. Collect user input */ | ||
const userInput = "\x00world"; | ||
/* 2. Execute shell command */ | ||
/* 3. Execute shell command */ | ||
const echo = spawnSync("echo", shescape.escapeAll(["Hello", userInput, "!"])); | ||
@@ -528,4 +581,4 @@ if (echo.error) { | ||
the `options` argument to Shescape as well. If `options.shell` is set to a | ||
truthy value, use `shescape.quoteAll` to escape all `args`. If `options.shell` | ||
is set to a falsy value (or omitted), use `shescape.escapeAll` to escape all | ||
truthy value, use `Shescape#quoteAll` to escape all `args`. If `options.shell` | ||
is set to a falsy value (or omitted), use `Shescape#escapeAll` to escape all | ||
`args`. | ||
@@ -535,5 +588,5 @@ | ||
import { spawnSync } from "node:child_process"; | ||
import * as shescape from "shescape"; | ||
import { Shescape } from "shescape"; | ||
/* 1. Set up configuration */ | ||
/* 1. Set up */ | ||
const spawnOptions = { | ||
@@ -543,7 +596,10 @@ // Example configuration for `spawn` | ||
}; | ||
const shescapeOptions = { | ||
const shescape = new Shescape({ | ||
interpolation: false, | ||
// Set options for Shescape first, then add the options for `spawnSync`. DO | ||
// NOT set any keys from the child_process API here. | ||
...spawnOptions, | ||
}; | ||
}); | ||
@@ -558,5 +614,5 @@ /* 2. Collect user input */ | ||
? // When the `shell` option is configured, arguments should be quoted | ||
shescape.quoteAll(["Hello", userInput], shescapeOptions) | ||
shescape.quoteAll(["Hello", userInput]) | ||
: // When the `shell` option is NOT configured, arguments should NOT be quoted | ||
shescape.escapeAll(["Hello", userInput], shescapeOptions), | ||
shescape.escapeAll(["Hello", userInput]), | ||
spawnOptions, | ||
@@ -563,0 +619,0 @@ ); |
@@ -22,5 +22,6 @@ # Testing with Shescape | ||
import assert from "node:assert"; | ||
import { shescape as stubscape } from "shescape/testing"; | ||
import { Shescape as Stubscape } from "shescape/testing"; | ||
import { functionUnderTest } from "./my-module.js"; | ||
const stubscape = new Stubscape(); | ||
assert.ok(functionUnderTest(stubscape)); | ||
@@ -27,0 +28,0 @@ ``` |
@@ -22,4 +22,5 @@ # Tips | ||
import { exec } from "node:child_process"; | ||
import * as shescape from "shescape"; | ||
import { Shescape } from "shescape"; | ||
const shescape = new Shescape(); | ||
const userInput = "Yes"; | ||
@@ -100,3 +101,3 @@ | ||
import { exec } from "node:child_process"; | ||
import * as shescape from "shescape"; | ||
import { Shescape } from "shescape"; | ||
@@ -106,8 +107,8 @@ const userInput = "foobar.txt"; | ||
// Good | ||
let options = { flagProtection: true }; | ||
exec(`git clean -n ${shescape.quote(userInput, options)}`); | ||
let shescape = new Shescape({ flagProtection: true }); | ||
exec(`git clean -n ${shescape.quote(userInput)}`); | ||
// Better | ||
options = { flagProtection: false }; | ||
exec(`git clean -n -- ${shescape.quote(userInput, options)}`); | ||
shescape = new Shescape({ flagProtection: false }); | ||
exec(`git clean -n -- ${shescape.quote(userInput)}`); | ||
``` | ||
@@ -125,4 +126,5 @@ | ||
import { exec } from "node:child_process"; | ||
import * as shescape from "shescape"; | ||
import { Shescape } from "shescape"; | ||
const shescape = new Shescape(); | ||
const userInput = "&& ls"; | ||
@@ -129,0 +131,0 @@ |
199
index.d.ts
@@ -7,12 +7,7 @@ /** | ||
/** | ||
* Possible values of a shell. `false` and `undefined` mean no shell. `true` | ||
* means the default system shell, and any non-empty string configures a | ||
* particular shell. | ||
* Options for {@link Shescape}. | ||
* | ||
* @since 2.0.0 | ||
*/ | ||
type ShellOption = boolean | string | undefined; | ||
/** | ||
* Options for {@link escape} and {@link escapeAll}. | ||
*/ | ||
interface EscapeOptions { | ||
interface ShescapeOptions { | ||
/** | ||
@@ -22,4 +17,4 @@ * Whether or not to protect against flag and option (such as `--verbose`) | ||
* | ||
* @default false | ||
* @since 1.7.0 | ||
* @default true | ||
* @since 2.0.0 | ||
*/ | ||
@@ -31,4 +26,4 @@ readonly flagProtection?: boolean; | ||
* | ||
* @default false | ||
* @since 1.4.0 | ||
* @default true | ||
* @since 2.0.0 | ||
*/ | ||
@@ -38,39 +33,19 @@ readonly interpolation?: boolean; | ||
/** | ||
* The shell to escape for. | ||
* The shell to escape for. `false` and `undefined` mean no shell. `true` | ||
* means the default system shell, and any non-empty string configures a | ||
* particular shell. | ||
* | ||
* @default undefined | ||
* @since 1.3.0 | ||
* @since 2.0.0 | ||
*/ | ||
readonly shell?: ShellOption; | ||
readonly shell?: boolean | string; | ||
} | ||
/** | ||
* Options for {@link quote} and {@link quoteAll}. | ||
*/ | ||
interface QuoteOptions { | ||
/** | ||
* Whether or not to protect against flag and option (such as `--verbose`) | ||
* injection. | ||
* | ||
* @default false | ||
* @since 1.7.0 | ||
*/ | ||
readonly flagProtection?: boolean; | ||
/** | ||
* The shell to escape for. | ||
* | ||
* @default undefined | ||
* @since 1.3.0 | ||
*/ | ||
readonly shell?: ShellOption; | ||
} | ||
/** | ||
* Take a single value, the argument, and escape any dangerous characters. | ||
* A class to escape user-controlled inputs to shell commands to prevent shell | ||
* injection. | ||
* | ||
* Non-string inputs will be converted to strings using a `toString()` method. | ||
* | ||
* @example | ||
* import { spawn } from "node:child_process"; | ||
* const shescape = Shescape(); | ||
* spawn( | ||
@@ -81,22 +56,5 @@ * "echo", | ||
* ); | ||
* @param {string} arg The argument to escape. | ||
* @param {object} [options] The escape options. | ||
* @param {boolean} [options.flagProtection=false] Is flag protection enabled. | ||
* @param {boolean} [options.interpolation=false] Is interpolation enabled. | ||
* @param {boolean | string} [options.shell] The shell to escape for. | ||
* @returns {string} The escaped argument. | ||
* @throws {TypeError} The argument is not stringable. | ||
* @since 0.1.0 | ||
*/ | ||
export function escape(arg: string, options?: EscapeOptions): string; | ||
/** | ||
* Take a array of values, the arguments, and escape any dangerous characters in | ||
* every argument. | ||
* | ||
* Non-array inputs will be converted to one-value arrays and non-string values | ||
* will be converted to strings using a `toString()` method. | ||
* | ||
* @example | ||
* import { spawn } from "node:child_process"; | ||
* const shescape = Shescape(); | ||
* spawn( | ||
@@ -107,70 +65,85 @@ * "echo", | ||
* ); | ||
* @param {string[]} args The arguments to escape. | ||
* @param {object} [options] The escape options. | ||
* @param {boolean} [options.flagProtection=false] Is flag protection enabled. | ||
* @param {boolean} [options.interpolation=false] Is interpolation enabled. | ||
* @param {boolean | string} [options.shell] The shell to escape for. | ||
* @returns {string[]} The escaped arguments. | ||
* @throws {TypeError} One of the arguments is not stringable. | ||
* @since 1.1.0 | ||
*/ | ||
export function escapeAll(args: string[], options?: EscapeOptions): string[]; | ||
/** | ||
* Take a single value, the argument, put shell-specific quotes around it and | ||
* escape any dangerous characters. | ||
* | ||
* Non-string inputs will be converted to strings using a `toString()` method. | ||
* | ||
* @example | ||
* import { spawn } from "node:child_process"; | ||
* const spawnOptions = { shell: true }; // `options.shell` SHOULD be truthy | ||
* const shescapeOptions = { ...spawnOptions }; | ||
* const shescape = Shescape({ ...spawnOptions }); | ||
* spawn( | ||
* "echo", | ||
* ["Hello", shescape.quote(userInput, shescapeOptions)], | ||
* ["Hello", shescape.quote(userInput)], | ||
* spawnOptions | ||
* ); | ||
* @example | ||
* import { exec } from "node:child_process"; | ||
* const execOptions = null || { }; | ||
* const shescapeOptions = { ...execOptions }; | ||
* exec( | ||
* `echo Hello ${shescape.quote(userInput, shescapeOptions)}`, | ||
* execOptions | ||
* ); | ||
* @param {string} arg The argument to quote and escape. | ||
* @param {object} [options] The escape and quote options. | ||
* @param {boolean} [options.flagProtection=false] Is flag protection enabled. | ||
* @param {boolean | string} [options.shell] The shell to escape for. | ||
* @returns {string} The quoted and escaped argument. | ||
* @throws {TypeError} The argument is not stringable. | ||
* @since 0.3.0 | ||
*/ | ||
export function quote(arg: string, options?: QuoteOptions): string; | ||
/** | ||
* Take an array of values, the arguments, put shell-specific quotes around | ||
* every argument and escape any dangerous characters in every argument. | ||
* | ||
* Non-array inputs will be converted to one-value arrays and non-string values | ||
* will be converted to strings using a `toString()` method. | ||
* | ||
* @example | ||
* import { spawn } from "node:child_process"; | ||
* const spawnOptions = { shell: true }; // `options.shell` SHOULD be truthy | ||
* const shescapeOptions = { ...spawnOptions }; | ||
* const shescape = Shescape({ ...spawnOptions }); | ||
* spawn( | ||
* "echo", | ||
* shescape.quoteAll(["Hello", userInput], shescapeOptions), | ||
* shescape.quoteAll(["Hello", userInput]), | ||
* spawnOptions | ||
* ); | ||
* @param {string[]} args The arguments to quote and escape. | ||
* @param {object} [options] The escape and quote options. | ||
* @param {boolean} [options.flagProtection=false] Is flag protection enabled. | ||
* @param {boolean | string} [options.shell] The shell to escape for. | ||
* @returns {string[]} The quoted and escaped arguments. | ||
* @throws {TypeError} One of the arguments is not stringable. | ||
* @since 0.4.0 | ||
*/ | ||
export function quoteAll(args: string[], options?: QuoteOptions): string[]; | ||
interface Shescape { | ||
/** | ||
* Create a new {@link Shescape} instance. | ||
* | ||
* @param {object} [options] The escape options. | ||
* @param {boolean} [options.flagProtection=true] Is flag protection enabled. | ||
* @param {boolean} [options.interpolation=true] Is interpolation enabled. | ||
* @param {boolean | string} [options.shell] The shell to escape for. | ||
* @since 2.0.0 | ||
*/ | ||
new (options: ShescapeOptions): Shescape; | ||
/** | ||
* Take a single value, the argument, and escape any dangerous characters. | ||
* | ||
* Non-string inputs will be converted to strings using a `toString()` method. | ||
* | ||
* @param {string} arg The argument to escape. | ||
* @returns {string} The escaped argument. | ||
* @throws {TypeError} The argument is not stringable. | ||
* @since 2.0.0 | ||
*/ | ||
escape(arg: string): string; | ||
/** | ||
* Take a array of values, the arguments, and escape any dangerous characters | ||
* in every argument. | ||
* | ||
* Non-array inputs will be converted to one-value arrays and non-string | ||
* values will be converted to strings using a `toString()` method. | ||
* | ||
* @param {string[]} args The arguments to escape. | ||
* @returns {string[]} The escaped arguments. | ||
* @throws {TypeError} One of the arguments is not stringable. | ||
* @since 2.0.0 | ||
*/ | ||
escapeAll(args: string[]): string[]; | ||
/** | ||
* Take a single value, the argument, put shell-specific quotes around it and | ||
* escape any dangerous characters. | ||
* | ||
* Non-string inputs will be converted to strings using a `toString()` method. | ||
* | ||
* @param {string} arg The argument to quote and escape. | ||
* @returns {string} The quoted and escaped argument. | ||
* @throws {TypeError} The argument is not stringable. | ||
* @since 2.0.0 | ||
*/ | ||
quote(arg: string): string; | ||
/** | ||
* Take an array of values, the arguments, put shell-specific quotes around | ||
* every argument and escape any dangerous characters in every argument. | ||
* | ||
* Non-array inputs will be converted to one-value arrays and non-string | ||
* values will be converted to strings using a `toString()` method. | ||
* | ||
* @param {string[]} args The arguments to quote and escape. | ||
* @returns {string[]} The quoted and escaped arguments. | ||
* @throws {TypeError} One of the arguments is not stringable. | ||
* @since 2.0.0 | ||
*/ | ||
quoteAll(args: string[]): string[]; | ||
} |
242
index.js
@@ -7,8 +7,8 @@ /** | ||
* @module shescape | ||
* @version 1.7.2 | ||
* @version 1.7.1 | ||
* @license MPL-2.0 | ||
*/ | ||
import os from "os"; | ||
import process from "process"; | ||
import os from "node:os"; | ||
import process from "node:process"; | ||
@@ -20,22 +20,8 @@ import { parseOptions } from "./src/options.js"; | ||
/** | ||
* Get the helper functions for the current platform. | ||
* A class to escape user-controlled inputs to shell commands to prevent shell | ||
* injection. | ||
* | ||
* @returns {object} The helper functions for the current platform. | ||
*/ | ||
function getPlatformHelpers() { | ||
const platform = os.platform(); | ||
const helpers = getHelpersByPlatform({ env: process.env, platform }); | ||
return helpers; | ||
} | ||
/** | ||
* Take a single value, the argument, and escape any dangerous characters. | ||
* | ||
* Non-string inputs will be converted to strings using a `toString()` method. | ||
* | ||
* NOTE: when the `interpolation` option is set to `true`, whitespace is escaped | ||
* to prevent argument splitting except for cmd.exe (which does not support it). | ||
* | ||
* @example | ||
* import { spawn } from "node:child_process"; | ||
* const shescape = Shescape(); | ||
* spawn( | ||
@@ -46,37 +32,5 @@ * "echo", | ||
* ); | ||
* @param {string} arg The argument to escape. | ||
* @param {object} [options] The escape options. | ||
* @param {boolean} [options.flagProtection=false] Is flag protection enabled. | ||
* @param {boolean} [options.interpolation=false] Is interpolation enabled. | ||
* @param {boolean | string} [options.shell] The shell to escape for. | ||
* @returns {string} The escaped argument. | ||
* @throws {TypeError} The argument is not stringable. | ||
* @since 0.1.0 | ||
*/ | ||
export function escape(arg, options = {}) { | ||
const helpers = getPlatformHelpers(); | ||
const { flagProtection, interpolation, shellName } = parseOptions( | ||
{ options, process }, | ||
helpers, | ||
); | ||
const argAsString = checkedToString(arg); | ||
const escape = helpers.getEscapeFunction(shellName, { interpolation }); | ||
const escapedArg = escape(argAsString); | ||
if (flagProtection) { | ||
const flagProtect = helpers.getFlagProtectionFunction(shellName); | ||
return flagProtect(escapedArg); | ||
} else { | ||
return escapedArg; | ||
} | ||
} | ||
/** | ||
* Take a array of values, the arguments, and escape any dangerous characters in | ||
* every argument. | ||
* | ||
* Non-array inputs will be converted to one-value arrays and non-string values | ||
* will be converted to strings using a `toString()` method. | ||
* | ||
* @example | ||
* import { spawn } from "node:child_process"; | ||
* const shescape = Shescape(); | ||
* spawn( | ||
@@ -87,91 +41,125 @@ * "echo", | ||
* ); | ||
* @param {string[]} args The arguments to escape. | ||
* @param {object} [options] The escape options. | ||
* @param {boolean} [options.flagProtection=false] Is flag protection enabled. | ||
* @param {boolean} [options.interpolation=false] Is interpolation enabled. | ||
* @param {boolean | string} [options.shell] The shell to escape for. | ||
* @returns {string[]} The escaped arguments. | ||
* @throws {TypeError} One of the arguments is not stringable. | ||
* @since 1.1.0 | ||
*/ | ||
export function escapeAll(args, options = {}) { | ||
args = toArrayIfNecessary(args); | ||
return args.map((arg) => escape(arg, options)); | ||
} | ||
/** | ||
* Take a single value, the argument, put shell-specific quotes around it and | ||
* escape any dangerous characters. | ||
* | ||
* Non-string inputs will be converted to strings using a `toString()` method. | ||
* | ||
* @example | ||
* import { spawn } from "node:child_process"; | ||
* const spawnOptions = { shell: true }; // `options.shell` SHOULD be truthy | ||
* const shescapeOptions = { ...spawnOptions }; | ||
* const shescape = Shescape({ ...spawnOptions }); | ||
* spawn( | ||
* "echo", | ||
* ["Hello", shescape.quote(userInput, shescapeOptions)], | ||
* ["Hello", shescape.quote(userInput)], | ||
* spawnOptions | ||
* ); | ||
* @example | ||
* import { exec } from "node:child_process"; | ||
* const execOptions = null || { }; | ||
* const shescapeOptions = { ...execOptions }; | ||
* exec( | ||
* `echo Hello ${shescape.quote(userInput, shescapeOptions)}`, | ||
* execOptions | ||
* ); | ||
* @param {string} arg The argument to quote and escape. | ||
* @param {object} [options] The escape and quote options. | ||
* @param {boolean} [options.flagProtection=false] Is flag protection enabled. | ||
* @param {boolean | string} [options.shell] The shell to escape for. | ||
* @returns {string} The quoted and escaped argument. | ||
* @throws {TypeError} The argument is not stringable. | ||
* @since 0.3.0 | ||
*/ | ||
export function quote(arg, options = {}) { | ||
const helpers = getPlatformHelpers(); | ||
const { flagProtection, shellName } = parseOptions( | ||
{ options, process }, | ||
helpers, | ||
); | ||
const argAsString = checkedToString(arg); | ||
const [escape, quote] = helpers.getQuoteFunction(shellName); | ||
const escapedArg = escape(argAsString); | ||
if (flagProtection) { | ||
const flagProtect = helpers.getFlagProtectionFunction(shellName); | ||
return quote(flagProtect(escapedArg)); | ||
} else { | ||
return quote(escapedArg); | ||
} | ||
} | ||
/** | ||
* Take an array of values, the arguments, put shell-specific quotes around | ||
* every argument and escape any dangerous characters in every argument. | ||
* | ||
* Non-array inputs will be converted to one-value arrays and non-string values | ||
* will be converted to strings using a `toString()` method. | ||
* | ||
* @example | ||
* import { spawn } from "node:child_process"; | ||
* const spawnOptions = { shell: true }; // `options.shell` SHOULD be truthy | ||
* const shescapeOptions = { ...spawnOptions }; | ||
* const shescape = Shescape({ ...spawnOptions }); | ||
* spawn( | ||
* "echo", | ||
* shescape.quoteAll(["Hello", userInput], shescapeOptions), | ||
* shescape.quoteAll(["Hello", userInput]), | ||
* spawnOptions | ||
* ); | ||
* @param {string[]} args The arguments to quote and escape. | ||
* @param {object} [options] The escape and quote options. | ||
* @param {boolean} [options.flagProtection=false] Is flag protection enabled. | ||
* @param {boolean | string} [options.shell] The shell to escape for. | ||
* @returns {string[]} The quoted and escaped arguments. | ||
* @throws {TypeError} One of the arguments is not stringable. | ||
* @since 0.4.0 | ||
*/ | ||
export function quoteAll(args, options = {}) { | ||
args = toArrayIfNecessary(args); | ||
return args.map((arg) => quote(arg, options)); | ||
export class Shescape { | ||
/** | ||
* Create a new {@link Shescape} instance. | ||
* | ||
* @param {object} [options] The escape options. | ||
* @param {boolean} [options.flagProtection=true] Is flag protection enabled. | ||
* @param {boolean} [options.interpolation=true] Is interpolation enabled. | ||
* @param {boolean | string} [options.shell] The shell to escape for. | ||
* @since 2.0.0 | ||
*/ | ||
constructor(options = {}) { | ||
const platform = os.platform(); | ||
const helpers = getHelpersByPlatform({ env: process.env, platform }); | ||
const { flagProtection, interpolation, shellName } = parseOptions( | ||
{ options, process }, | ||
helpers, | ||
); | ||
{ | ||
const escape = helpers.getEscapeFunction(shellName, { interpolation }); | ||
if (flagProtection) { | ||
const flagProtect = helpers.getFlagProtectionFunction(shellName); | ||
this._escape = (arg) => flagProtect(escape(arg)); | ||
} else { | ||
this._escape = escape; | ||
} | ||
} | ||
{ | ||
const [escape, quote] = helpers.getQuoteFunction(shellName); | ||
if (flagProtection) { | ||
const flagProtect = helpers.getFlagProtectionFunction(shellName); | ||
this._quote = (arg) => quote(flagProtect(escape(arg))); | ||
} else { | ||
this._quote = (arg) => quote(escape(arg)); | ||
} | ||
} | ||
} | ||
/** | ||
* Take a single value, the argument, and escape any dangerous characters. | ||
* | ||
* Non-string inputs will be converted to strings using a `toString()` method. | ||
* | ||
* @param {string} arg The argument to escape. | ||
* @returns {string} The escaped argument. | ||
* @throws {TypeError} The argument is not stringable. | ||
* @since 2.0.0 | ||
*/ | ||
escape(arg) { | ||
const argAsString = checkedToString(arg); | ||
return this._escape(argAsString); | ||
} | ||
/** | ||
* Take a array of values, the arguments, and escape any dangerous characters | ||
* in every argument. | ||
* | ||
* Non-array inputs will be converted to one-value arrays and non-string | ||
* values will be converted to strings using a `toString()` method. | ||
* | ||
* @param {string[]} args The arguments to escape. | ||
* @returns {string[]} The escaped arguments. | ||
* @throws {TypeError} One of the arguments is not stringable. | ||
* @since 2.0.0 | ||
*/ | ||
escapeAll(args) { | ||
args = toArrayIfNecessary(args); | ||
return args.map((arg) => this.escape(arg)); | ||
} | ||
/** | ||
* Take a single value, the argument, put shell-specific quotes around it and | ||
* escape any dangerous characters. | ||
* | ||
* Non-string inputs will be converted to strings using a `toString()` method. | ||
* | ||
* @param {string} arg The argument to quote and escape. | ||
* @returns {string} The quoted and escaped argument. | ||
* @throws {TypeError} The argument is not stringable. | ||
* @since 2.0.0 | ||
*/ | ||
quote(arg) { | ||
const argAsString = checkedToString(arg); | ||
return this._quote(argAsString); | ||
} | ||
/** | ||
* Take an array of values, the arguments, put shell-specific quotes around | ||
* every argument and escape any dangerous characters in every argument. | ||
* | ||
* Non-array inputs will be converted to one-value arrays and non-string | ||
* values will be converted to strings using a `toString()` method. | ||
* | ||
* @param {string[]} args The arguments to quote and escape. | ||
* @returns {string[]} The quoted and escaped arguments. | ||
* @throws {TypeError} One of the arguments is not stringable. | ||
* @since 2.0.0 | ||
*/ | ||
quoteAll(args) { | ||
args = toArrayIfNecessary(args); | ||
return args.map((arg) => this.quote(arg)); | ||
} | ||
} |
{ | ||
"name": "shescape", | ||
"version": "1.7.2", | ||
"version": "2.0.0-rc1", | ||
"description": "simple shell escape library", | ||
@@ -8,3 +8,2 @@ "homepage": "https://github.com/ericcornelissen/shescape#readme", | ||
"type": "module", | ||
"main": "./index.cjs", | ||
"exports": { | ||
@@ -24,3 +23,3 @@ ".": { | ||
"engines": { | ||
"node": "^10.13.0 || ^12 || ^14 || ^16 || ^18 || ^19 || ^20" | ||
"node": "^14.18.0 || ^16.13.0 || ^18 || ^19 || ^20" | ||
}, | ||
@@ -45,3 +44,3 @@ "repository": { | ||
"dependencies": { | ||
"which": "^2.0.0" | ||
"which": "^3.0.0" | ||
}, | ||
@@ -60,3 +59,3 @@ "devDependencies": { | ||
"dotenv": "16.3.1", | ||
"eslint": "8.44.0", | ||
"eslint": "8.43.0", | ||
"eslint-plugin-ava": "14.0.0", | ||
@@ -119,3 +118,3 @@ "eslint-plugin-jsdoc": "46.4.3", | ||
"test:compat": "mocha test/compat/**/*.test.cjs", | ||
"test:compat-all": "nve 10.13.0,12.0.0,14.0.0,16.0.0,18.0.0,19.0.0,20.0.0 mocha test/compat/**/*.test.cjs", | ||
"test:compat-all": "nve 14.18.0,16.13.0,18.0.0,19.0.0,20.0.0 mocha test/compat/**/*.test.cjs", | ||
"test:e2e": "ava test/e2e/**/*.test.js --timeout 1m", | ||
@@ -122,0 +121,0 @@ "test:integration": "ava test/integration/**/*.test.js --timeout 1m", |
@@ -48,7 +48,13 @@ # Shescape | ||
```javascript | ||
import * as shescape from "shescape"; | ||
import { Shescape } from "shescape"; | ||
``` | ||
3. Use `shescape`. | ||
3. Initialize `Shescape`. | ||
```javascript | ||
const shescape = new Shescape(/* options */); | ||
``` | ||
4. Use `shescape`. | ||
### Recipes | ||
@@ -55,0 +61,0 @@ |
@@ -6,4 +6,4 @@ /** | ||
import * as fs from "fs"; | ||
import * as path from "path"; | ||
import * as fs from "node:fs"; | ||
import * as path from "node:path"; | ||
@@ -10,0 +10,0 @@ import which from "which"; |
@@ -6,3 +6,3 @@ /** | ||
import { TextEncoder } from "util"; | ||
import { TextEncoder } from "node:util"; | ||
@@ -9,0 +9,0 @@ /** |
@@ -6,4 +6,4 @@ /** | ||
import * as fs from "fs"; | ||
import * as path from "path"; | ||
import * as fs from "node:fs"; | ||
import * as path from "node:path"; | ||
@@ -10,0 +10,0 @@ import which from "which"; |
@@ -29,3 +29,3 @@ /** | ||
/** | ||
* A test stub of shescape that has the same input-output profile as the real | ||
* A test stub of Shescape that has the same input-output profile as the real | ||
* shescape implementation. | ||
@@ -38,10 +38,23 @@ * | ||
*/ | ||
export const shescape = { | ||
escape: (arg, _options) => checkedToString(arg), | ||
escapeAll: (args, _options) => { | ||
export class Shescape { | ||
constructor(_options) { | ||
// Nothing to do. | ||
} | ||
escape(arg) { | ||
return checkedToString(arg); | ||
} | ||
escapeAll(args) { | ||
args = toArrayIfNecessary(args); | ||
return args.map(shescape.escape); | ||
}, | ||
quote: (arg, _options) => shescape.escape(arg), | ||
quoteAll: (args, _options) => shescape.escapeAll(args), | ||
}; | ||
return args.map((arg) => this.escape(arg)); | ||
} | ||
quote(arg) { | ||
return this.escape(arg); | ||
} | ||
quoteAll(args) { | ||
return this.escapeAll(args); | ||
} | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Deprecated
MaintenanceThe maintainer of the package marked it as deprecated. This could indicate that a single version should not be used, or that the package is no longer maintained and any new vulnerabilities will not be fixed.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Deprecated
MaintenanceThe maintainer of the package marked it as deprecated. This could indicate that a single version should not be used, or that the package is no longer maintained and any new vulnerabilities will not be fixed.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
103
2
145286
2729
1
+ Addedwhich@3.0.1(transitive)
- Removedwhich@2.0.2(transitive)
Updatedwhich@^3.0.0