Socket
Socket
Sign inDemoInstall

shell-quote

Package Overview
Dependencies
0
Maintainers
4
Versions
27
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 1.7.4 to 1.8.0

.nycrc

12

CHANGELOG.md

@@ -8,2 +8,14 @@ # Changelog

## [v1.8.0](https://github.com/ljharb/shell-quote/compare/v1.7.4...v1.8.0) - 2023-01-30
### Commits
- [New] extract `parse` and `quote` to their own deep imports [`553fdfc`](https://github.com/ljharb/shell-quote/commit/553fdfc32cc41b4c2f77e061b6957703958ca575)
- [Tests] add `nyc` coverage [`fd7ddcd`](https://github.com/ljharb/shell-quote/commit/fd7ddcdd84bfef064c6d9a06b055a95531b26897)
- [New] Add support for here strings (`<<<`) [`9802fb3`](https://github.com/ljharb/shell-quote/commit/9802fb37c7946e18c672b81122520dc296bde271)
- [New] `parse`: Add syntax support for duplicating input file descriptors [`216b198`](https://github.com/ljharb/shell-quote/commit/216b19894f76b14d164c4c5a68f05a51b06336c4)
- [Dev Deps] update `@ljharb/eslint-config`, `aud`, `tape` [`85f8e31`](https://github.com/ljharb/shell-quote/commit/85f8e31dd80e1dde63d58204b653e497a53857e6)
- [Tests] add `evalmd` [`c5549fc`](https://github.com/ljharb/shell-quote/commit/c5549fcd82d70046bdc2b1c34184ae9f9d0191f9)
- [actions] update checkout action [`62e9b49`](https://github.com/ljharb/shell-quote/commit/62e9b4958cfa2f9009b7069076612fe33528c1fb)
## [v1.7.4](https://github.com/ljharb/shell-quote/compare/1.7.3...v1.7.4) - 2022-10-12

@@ -10,0 +22,0 @@

203

index.js
'use strict';
exports.quote = function (xs) {
return xs.map(function (s) {
if (s && typeof s === 'object') {
return s.op.replace(/(.)/g, '\\$1');
} else if ((/["\s]/).test(s) && !(/'/).test(s)) {
return "'" + s.replace(/(['\\])/g, '\\$1') + "'";
} else if ((/["'\s]/).test(s)) {
return '"' + s.replace(/(["\\$`!])/g, '\\$1') + '"';
}
return String(s).replace(/([A-Za-z]:)?([#!"$&'()*,:;<=>?@[\\\]^`{|}])/g, '$1\\$2');
}).join(' ');
};
// '<(' is process substitution operator and
// can be parsed the same as control operator
var CONTROL = '(?:' + [
'\\|\\|', '\\&\\&', ';;', '\\|\\&', '\\<\\(', '>>', '>\\&', '[&;()|<>]'
].join('|') + ')';
var META = '|&;()<> \\t';
var BAREWORD = '(\\\\[\'"' + META + ']|[^\\s\'"' + META + '])+';
var SINGLE_QUOTE = '"((\\\\"|[^"])*?)"';
var DOUBLE_QUOTE = '\'((\\\\\'|[^\'])*?)\'';
var TOKEN = '';
for (var i = 0; i < 4; i++) {
TOKEN += (Math.pow(16, 8) * Math.random()).toString(16);
}
function parse(s, env, opts) {
var chunker = new RegExp([
'(' + CONTROL + ')', // control chars
'(' + BAREWORD + '|' + SINGLE_QUOTE + '|' + DOUBLE_QUOTE + ')*'
].join('|'), 'g');
var match = s.match(chunker).filter(Boolean);
if (!match) {
return [];
}
if (!env) {
env = {};
}
if (!opts) {
opts = {};
}
var commented = false;
function getVar(_, pre, key) {
var r = typeof env === 'function' ? env(key) : env[key];
if (r === undefined && key != '') {
r = '';
} else if (r === undefined) {
r = '$';
}
if (typeof r === 'object') {
return pre + TOKEN + JSON.stringify(r) + TOKEN;
}
return pre + r;
}
return match.map(function (s, j) {
if (commented) {
return void undefined;
}
if (RegExp('^' + CONTROL + '$').test(s)) {
return { op: s };
}
// Hand-written scanner/parser for Bash quoting rules:
//
// 1. inside single quotes, all characters are printed literally.
// 2. inside double quotes, all characters are printed literally
// except variables prefixed by '$' and backslashes followed by
// either a double quote or another backslash.
// 3. outside of any quotes, backslashes are treated as escape
// characters and not printed (unless they are themselves escaped)
// 4. quote context can switch mid-token if there is no whitespace
// between the two quote contexts (e.g. all'one'"token" parses as
// "allonetoken")
var SQ = "'";
var DQ = '"';
var DS = '$';
var BS = opts.escape || '\\';
var quote = false;
var esc = false;
var out = '';
var isGlob = false;
var i;
function parseEnvVar() {
i += 1;
var varend;
var varname;
// debugger
if (s.charAt(i) === '{') {
i += 1;
if (s.charAt(i) === '}') {
throw new Error('Bad substitution: ' + s.substr(i - 2, 3));
}
varend = s.indexOf('}', i);
if (varend < 0) {
throw new Error('Bad substitution: ' + s.substr(i));
}
varname = s.substr(i, varend - i);
i = varend;
} else if ((/[*@#?$!_-]/).test(s.charAt(i))) {
varname = s.charAt(i);
i += 1;
} else {
varend = s.substr(i).match(/[^\w\d_]/);
if (!varend) {
varname = s.substr(i);
i = s.length;
} else {
varname = s.substr(i, varend.index);
i += varend.index - 1;
}
}
return getVar(null, '', varname);
}
for (i = 0; i < s.length; i++) {
var c = s.charAt(i);
isGlob = isGlob || (!quote && (c === '*' || c === '?'));
if (esc) {
out += c;
esc = false;
} else if (quote) {
if (c === quote) {
quote = false;
} else if (quote == SQ) {
out += c;
} else { // Double quote
if (c === BS) {
i += 1;
c = s.charAt(i);
if (c === DQ || c === BS || c === DS) {
out += c;
} else {
out += BS + c;
}
} else if (c === DS) {
out += parseEnvVar();
} else {
out += c;
}
}
} else if (c === DQ || c === SQ) {
quote = c;
} else if (RegExp('^' + CONTROL + '$').test(c)) {
return { op: s };
} else if ((/^#$/).test(c)) {
commented = true;
if (out.length) {
return [out, { comment: s.slice(i + 1) + match.slice(j + 1).join(' ') }];
}
return [{ comment: s.slice(i + 1) + match.slice(j + 1).join(' ') }];
} else if (c === BS) {
esc = true;
} else if (c === DS) {
out += parseEnvVar();
} else {
out += c;
}
}
if (isGlob) {
return { op: 'glob', pattern: out };
}
return out;
}).reduce(function (prev, arg) { // finalize parsed aruments
if (arg === undefined) {
return prev;
}
return prev.concat(arg);
}, []);
}
exports.parse = function (s, env, opts) {
var mapped = parse(s, env, opts);
if (typeof env !== 'function') {
return mapped;
}
return mapped.reduce(function (acc, s) {
if (typeof s === 'object') {
return acc.concat(s);
}
var xs = s.split(RegExp('(' + TOKEN + '.*?' + TOKEN + ')', 'g'));
if (xs.length === 1) {
return acc.concat(xs[0]);
}
return acc.concat(xs.filter(Boolean).map(function (x) {
if (RegExp('^' + TOKEN).test(x)) {
return JSON.parse(x.split(TOKEN)[1]);
}
return x;
}));
}, []);
};
exports.quote = require('./quote');
exports.parse = require('./parse');
{
"name": "shell-quote",
"description": "quote and parse shell commands",
"version": "1.7.4",
"version": "1.8.0",
"author": {

@@ -15,10 +15,12 @@ "name": "James Halliday",

"devDependencies": {
"@ljharb/eslint-config": "^21.0.0",
"aud": "^2.0.1",
"@ljharb/eslint-config": "^21.0.1",
"aud": "^2.0.2",
"auto-changelog": "^2.4.0",
"eslint": "=8.8.0",
"evalmd": "^0.0.19",
"in-publish": "^2.0.1",
"npmignore": "^0.3.0",
"nyc": "^10.3.2",
"safe-publish-latest": "^2.0.0",
"tape": "^5.6.1"
"tape": "^5.6.3"
},

@@ -42,5 +44,6 @@ "homepage": "https://github.com/ljharb/shell-quote",

"prepublishOnly": "safe-publish-latest",
"prelint": "evalmd README.md",
"lint": "eslint --ext=js,mjs .",
"pretest": "npm run lint",
"tests-only": "tape 'test/**/*.js'",
"tests-only": "nyc tape 'test/**/*.js'",
"test": "npm run tests-only",

@@ -47,0 +50,0 @@ "posttest": "aud --production",

@@ -17,3 +17,3 @@ # shell-quote <sup>[![Version Badge][npm-version-svg]][package-url]</sup>

``` js
var quote = require('shell-quote').quote;
var quote = require('shell-quote/quote');
var s = quote([ 'a', 'b c d', '$f', '"g"' ]);

@@ -32,3 +32,3 @@ console.log(s);

``` js
var parse = require('shell-quote').parse;
var parse = require('shell-quote/parse');
var xs = parse('a "b c" \\$def \'it\\\'s great\'');

@@ -47,3 +47,3 @@ console.dir(xs);

``` js
var parse = require('shell-quote').parse;
var parse = require('shell-quote/parse');
var xs = parse('beep --boop="$PWD"', { PWD: '/home/robot' });

@@ -62,3 +62,3 @@ console.dir(xs);

``` js
var parse = require('shell-quote').parse;
var parse = require('shell-quote/parse');
var xs = parse('beep --boop="$PWD"', { PWD: '/home/robot' }, { escape: '^' });

@@ -77,3 +77,3 @@ console.dir(xs);

``` js
var parse = require('shell-quote').parse;
var parse = require('shell-quote/parse');
var xs = parse('beep || boop > /byte');

@@ -92,3 +92,3 @@ console.dir(xs);

``` js
var parse = require('shell-quote').parse;
var parse = require('shell-quote/parse');
var xs = parse('beep > boop # > kaboom');

@@ -107,4 +107,4 @@ console.dir(xs);

``` js
var quote = require('shell-quote').quote;
var parse = require('shell-quote').parse;
var quote = require('shell-quote/quote');
var parse = require('shell-quote/parse');
```

@@ -111,0 +111,0 @@

@@ -26,2 +26,11 @@ 'use strict';

test('expand environment variables within here-strings', function (t) {
t.same(parse('a <<< $x', { x: 'Joe' }), ['a', { op: '<<<' }, 'Joe']);
t.same(parse('a <<< ${x}', { x: 'Joe' }), ['a', { op: '<<<' }, 'Joe']);
t.same(parse('a <<< "$x"', { x: 'Joe' }), ['a', { op: '<<<' }, 'Joe']);
t.same(parse('a <<< "${x}"', { x: 'Joe' }), ['a', { op: '<<<' }, 'Joe']);
t.end();
});
test('environment variables with metacharacters', function (t) {

@@ -28,0 +37,0 @@ t.same(parse('a $XYZ c', { XYZ: '"b"' }), ['a', '"b"', 'c']);

@@ -72,2 +72,24 @@ 'use strict';

test('duplicating input file descriptors', function (t) {
// duplicating stdout to file descriptor 3
t.same(parse('beep 3<&1'), ['beep', '3', { op: '<&' }, '1']);
// duplicating stdout to file descriptor 0, i.e. stdin
t.same(parse('beep <&1'), ['beep', { op: '<&' }, '1']);
// closes stdin
t.same(parse('beep <&-'), ['beep', { op: '<&' }, '-']);
t.end();
});
test('here strings', function (t) {
t.same(parse('cat <<< "hello world"'), ['cat', { op: '<<<' }, 'hello world']);
t.same(parse('cat <<< hello'), ['cat', { op: '<<<' }, 'hello']);
t.same(parse('cat<<<hello'), ['cat', { op: '<<<' }, 'hello']);
t.same(parse('cat<<<"hello world"'), ['cat', { op: '<<<' }, 'hello world']);
t.end();
});
test('glob patterns', function (t) {

@@ -74,0 +96,0 @@ t.same(

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc