Comparing version 1.2.1 to 1.3.0
@@ -12,2 +12,8 @@ # Changelog | ||
## [1.3.0] - 2021-12-05 | ||
- Add support to configure the shell to escape for. | ||
- Fix escaping backticks for PowerShell. | ||
- Fix escaping `$` for PowerShell. | ||
## [1.2.1] - 2021-04-24 | ||
@@ -14,0 +20,0 @@ |
30
index.js
@@ -11,3 +11,3 @@ /** | ||
* @module shescape | ||
* @version 1.2.1 | ||
* @version 1.3.0 | ||
* @license MPL-2.0 | ||
@@ -27,2 +27,4 @@ * @author Eric Cornelissen <ericornelissen@gmail.com> | ||
* @param {string} arg The argument to escape. | ||
* @param {Object} [options] The escape options. | ||
* @param {string} [options.shell] The shell to escape the argument for. | ||
* @returns {string} The escaped argument. | ||
@@ -32,5 +34,6 @@ * @throws {TypeError} The argument is not stringable. | ||
*/ | ||
export function escape(arg) { | ||
export function escape(arg, options = {}) { | ||
const shell = options.shell; | ||
const platform = os.platform(); | ||
return main.escapeShellArgByPlatform(arg, platform); | ||
return main.escapeShellArgByPlatform(arg, platform, shell); | ||
} | ||
@@ -46,2 +49,4 @@ | ||
* @param {string[]} args The arguments to escape. | ||
* @param {Object} [options] The escape options. | ||
* @param {string} [options.shell] The shell to escape the arguments for. | ||
* @returns {string[]} The escaped arguments. | ||
@@ -51,9 +56,10 @@ * @throws {TypeError} One of the arguments is not stringable. | ||
*/ | ||
export function escapeAll(args) { | ||
export function escapeAll(args, options = {}) { | ||
if (!Array.isArray(args)) args = [args]; | ||
const shell = options.shell; | ||
const platform = os.platform(); | ||
const result = []; | ||
for (const arg of args) { | ||
const safeArg = main.escapeShellArgByPlatform(arg, platform); | ||
const safeArg = main.escapeShellArgByPlatform(arg, platform, shell); | ||
result.push(safeArg); | ||
@@ -72,2 +78,4 @@ } | ||
* @param {string} arg The argument to quote and escape. | ||
* @param {Object} [options] The escape and quote options. | ||
* @param {string} [options.shell] The shell to escape the argument for. | ||
* @returns {string} The quoted and escaped argument. | ||
@@ -77,5 +85,6 @@ * @throws {TypeError} The argument is not stringable. | ||
*/ | ||
export function quote(arg) { | ||
export function quote(arg, options = {}) { | ||
const shell = options.shell; | ||
const platform = os.platform(); | ||
return main.quoteShellArgByPlatform(arg, platform); | ||
return main.quoteShellArgByPlatform(arg, platform, shell); | ||
} | ||
@@ -91,2 +100,4 @@ | ||
* @param {string[]} args The arguments to quote and escape. | ||
* @param {Object} [options] The escape and quote options. | ||
* @param {string} [options.shell] The shell to escape the arguments for. | ||
* @returns {string[]} The quoted and escaped arguments. | ||
@@ -96,9 +107,10 @@ * @throws {TypeError} One of the arguments is not stringable. | ||
*/ | ||
export function quoteAll(args) { | ||
export function quoteAll(args, options = {}) { | ||
if (!Array.isArray(args)) args = [args]; | ||
const shell = options.shell; | ||
const platform = os.platform(); | ||
const result = []; | ||
for (const arg of args) { | ||
const safeArg = main.quoteShellArgByPlatform(arg, platform); | ||
const safeArg = main.quoteShellArgByPlatform(arg, platform, shell); | ||
result.push(safeArg); | ||
@@ -105,0 +117,0 @@ } |
{ | ||
"name": "shescape", | ||
"version": "1.2.1", | ||
"version": "1.3.0", | ||
"description": "simple shell escape library", | ||
@@ -15,14 +15,17 @@ "homepage": "https://ericcornelissen.github.io/shescape/", | ||
"scripts": { | ||
"clean": "rm -rf .corpus/ .nyc_output/ .stryker-tmp/ reports/ crash-* index.cjs", | ||
"format": "prettier --write ./**/*.{cjs,js,md,yml}", | ||
"prefuzz": "npm run transpile", | ||
"clean": "node script/clean.js", | ||
"coverage": "c8 --reports-dir=reports/coverage --reporter=lcov --reporter=text npm test", | ||
"format": "prettier --write ./**/*.{cjs,js,md,yml} --ignore-path .gitignore", | ||
"prefuzz": "npm run transpile && node script/prefuzz.js", | ||
"fuzz": "jsfuzz ./test/index.fuzz.cjs ./.corpus", | ||
"lint": "prettier --check ./**/*.{js,md,yml}", | ||
"_postinstall": "is-ci || husky install", | ||
"lint": "prettier --check ./**/*.{cjs,js,md,yml} --ignore-path .gitignore", | ||
"_postinstall": "is-ci || husky install script/hooks", | ||
"prepublishOnly": "pinst --disable && npm run transpile", | ||
"postpublish": "pinst --enable", | ||
"test": "mocha test/**/*.test.js", | ||
"test:coverage": "c8 --reports-dir=reports/coverage --reporter=lcov --reporter=text npm run test", | ||
"test:mutation": "stryker run", | ||
"transpile": "ncc build index.js && mv dist/index.js index.cjs && rm -rf dist" | ||
"test": "npm run test:unit", | ||
"test:mutation": "stryker run stryker.config.json", | ||
"test:property": "mocha test/**/*.prop.js --timeout 15000", | ||
"test:transpiled": "mocha test/**/*.test.cjs", | ||
"test:unit": "mocha test/**/*.test.js", | ||
"transpile": "rollup -c" | ||
}, | ||
@@ -47,12 +50,14 @@ "repository": { | ||
"devDependencies": { | ||
"@stryker-mutator/core": "^4.6.0", | ||
"@vercel/ncc": "^0.28.3", | ||
"@stryker-mutator/core": "^5.0.0", | ||
"c8": "^7.7.1", | ||
"husky": "^6.0.0", | ||
"dotenv": "^10.0.0", | ||
"fast-check": "^2.17.0", | ||
"husky": "^7.0.0", | ||
"is-ci": "^3.0.0", | ||
"jsfuzz": "^1.0.14", | ||
"mocha": "^8.2.0", | ||
"mocha": "^9.0.1", | ||
"pinst": "^2.1.1", | ||
"prettier": "^2.1.2", | ||
"sinon": "^10.0.0" | ||
"rollup": "^2.55.1", | ||
"sinon": "^12.0.0" | ||
}, | ||
@@ -59,0 +64,0 @@ "engines": { |
@@ -16,17 +16,19 @@ # Shescape | ||
Below is a basic example of how to use _Shescape_. In this example `spawn` is | ||
used to invoke a shell command and `shescape.escapeAll` is used to escape any | ||
_dangerous_ character in any of the arguments specified by the array | ||
`userInput`. | ||
Below is a basic example of how to use _Shescape_. In this example `execSync` is | ||
used to invoke a shell command and `shescape.quote` is used to quote and escape | ||
any _dangerous_ character in the user input used as command input. | ||
```js | ||
import cp from "child_process"; | ||
import { execSync } from "child_process"; | ||
import * as shescape from "shescape"; | ||
cp.spawn("command", shescape.escapeAll(userInput), options); | ||
const userInput = "&& ls"; | ||
const stdout = execSync(`echo Hello ${shescape.quote(userInput)}`); | ||
console.log(stdout.toString()); | ||
// Output: "Hello && ls" | ||
``` | ||
[shell injection]: https://portswigger.net/web-security/os-command-injection | ||
[ci-url]: https://github.com/ericcornelissen/shescape/actions?query=workflow%3A%22Test+and+Lint%22+branch%3Amain | ||
[ci-image]: https://img.shields.io/github/workflow/status/ericcornelissen/shescape/Test%20and%20Lint/main?logo=github | ||
[ci-url]: https://github.com/ericcornelissen/shescape/actions/workflows/push-checks.yml | ||
[ci-image]: https://img.shields.io/github/workflow/status/ericcornelissen/shescape/Push%20checks/main?logo=github | ||
[coverage-url]: https://codecov.io/gh/ericcornelissen/shescape | ||
@@ -33,0 +35,0 @@ [coverage-image]: https://codecov.io/gh/ericcornelissen/shescape/branch/main/graph/badge.svg |
@@ -8,2 +8,9 @@ /** | ||
/** | ||
* @constant {string} regexpPowerShell A {@link RegExp} to detect if the shell | ||
* to escape an argument for is "PowerShell". | ||
* @example regexpPowerShell.test("cmd.exe"); // -> false | ||
*/ | ||
export const regexpPowerShell = /powershell.exe$/; | ||
/** | ||
* @constant {string} typeError The error message for incorrect parameter types. | ||
@@ -10,0 +17,0 @@ */ |
@@ -33,6 +33,7 @@ /** | ||
* @param {string} platform The platform to escape the argument for. | ||
* @param {string} [shell] The shell to escape the argument for. | ||
* @returns {string} The escaped argument. | ||
* @throws {TypeError} The argument is not stringable. | ||
*/ | ||
export function escapeShellArgByPlatform(arg, platform) { | ||
export function escapeShellArgByPlatform(arg, platform, shell) { | ||
if (!isStringable(arg)) { | ||
@@ -45,3 +46,3 @@ throw new TypeError(typeError); | ||
case win32: | ||
return win.escapeShellArg(argAsString); | ||
return win.escapeShellArg(argAsString, shell); | ||
default: | ||
@@ -60,7 +61,8 @@ return unix.escapeShellArg(argAsString); | ||
* @param {string} platform The platform to escape and quote the argument for. | ||
* @param {string} [shell] The shell to escape and quote the argument for. | ||
* @returns {string} The escaped argument. | ||
* @throws {TypeError} The argument is not stringable. | ||
*/ | ||
export function quoteShellArgByPlatform(arg, platform) { | ||
const safeArg = escapeShellArgByPlatform(arg, platform); | ||
export function quoteShellArgByPlatform(arg, platform, shell) { | ||
const safeArg = escapeShellArgByPlatform(arg, platform, shell); | ||
switch (platform) { | ||
@@ -67,0 +69,0 @@ case win32: |
@@ -7,4 +7,6 @@ /** | ||
import { regexpPowerShell } from "./constants.js"; | ||
/** | ||
* Escape a shell argument. | ||
* Escape a shell argument for use in CMD. | ||
* | ||
@@ -14,4 +16,33 @@ * @param {string} arg The argument to escape. | ||
*/ | ||
export function escapeShellArg(arg) { | ||
function escapeShellArgsForCmd(arg) { | ||
return arg.replace(/\u{0}/gu, "").replace(/"/g, `""`); | ||
} | ||
/** | ||
* Escape a shell argument for use in PowerShell. | ||
* | ||
* @param {string} arg The argument to escape. | ||
* @returns {string} The escaped argument. | ||
*/ | ||
function escapeShellArgsForPowerShell(arg) { | ||
return arg | ||
.replace(/\u{0}/gu, "") | ||
.replace(/"/g, `""`) | ||
.replace(/`/g, "``") | ||
.replace(/\$/g, "`$"); | ||
} | ||
/** | ||
* Escape a shell argument. | ||
* | ||
* @param {string} arg The argument to escape. | ||
* @param {string} [shell] The shell to escape the argument for. | ||
* @returns {string} The escaped argument. | ||
*/ | ||
export function escapeShellArg(arg, shell) { | ||
if (regexpPowerShell.test(shell)) { | ||
return escapeShellArgsForPowerShell(arg); | ||
} else { | ||
return escapeShellArgsForCmd(arg); | ||
} | ||
} |
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
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
39852
496
41
12