Skip to content

Commit 900cf10

Browse files
committed
fix: fix certain arguments not being correctly escaped or causing batch syntax error
More specifically: - Fix a bug that made it impossible to escape an argument that contained quotes followed by `>` or other special chars, e.g.: `"foo|bar"`, fixes #82 - Fix a bug were a command containing `%x%` would be replaced with the contents of the `x` environment variable, fixes #51 This was resolved by using `^` to escape all meta chars. Additionally, double escaping was necessary for cmd-shim files located in `node_modules./bin/`. Also, this commit was a major overhaul: - Upgrade tooling - Upgrate project to es6 (node v4) - Fix commands as posix unix relatixe paths not working correctly - Fix `options` argument being mutated - Improve compliance with node's ENOENT errors - Improve detection of node's shell option support - Migrate project to moxystudio BREAKING CHANGE: remove support for older nodejs versions, only node >= 4 is supported Fixes #82, #51
1 parent a00d9e2 commit 900cf10

38 files changed

+8543
-922
lines changed

.eslintignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
coverage/

.eslintrc

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"root": true,
33
"extends": [
4-
"@satazor/eslint-config/es5",
5-
"@satazor/eslint-config/addons/node"
4+
"eslint-config-moxy/es6",
5+
"eslint-config-moxy/addons/node"
66
]
7-
}
7+
}

.gitignore

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
node_modules/
22
npm-debug.*
3+
coverage/
34
test/fixtures/(*
4-
test/fixtures/shebang_noenv
5-
test/tmp
5+
test/tmp/

.travis.yml

+9-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
language: node_js
22
node_js:
3-
- '0.10'
4-
- '0.12'
5-
- '4'
3+
- '4.7'
4+
- '4.8'
5+
- '5.6'
6+
- '5.7'
67
- '6'
7-
- '7'
8+
- 'node'
9+
- 'lts/*'
10+
after_success:
11+
- "npm i codecov"
12+
- "node_modules/.bin/codecov"

CHANGELOG.md

+18-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,21 @@
1-
## 5.0.0 - 2016-10-30
1+
# Change Log
2+
3+
All notable changes to this project will be documented in this file. See [standard-version](https://github.com./conventional-changelog/standard-version) for commit guidelines.
4+
5+
<a name="5.1.0"></a>
6+
## [5.1.0](https://github.com./moxystudio/node-cross-spawn/compare/5.0.1...5.1.0) (2017-02-26)
7+
8+
- Fix `options.shell` support for NodeJS [v4.8](https://github.com./nodejs/node/blob/master/doc/changelogs/CHANGELOG_V4.md#4.8.0)
9+
10+
11+
<a name="5.0.1"></a>
12+
## [5.0.1](https://github.com./moxystudio/node-cross-spawn/compare/5.0.0...5.0.1) (2016-11-04)
13+
14+
- Fix `options.shell` support for NodeJS v7
15+
16+
17+
<a name="5.0.0"></a>
18+
# [5.0.0](https://github.com./moxystudio/node-cross-spawn/compare/4.0.2...5.0.0) (2016-10-30)
219

320
- Add support for `options.shell`
421
- Improve parsing of shebangs by using [`shebang-command`](https://github.com./kevva/shebang-command) module

README.md

+20-16
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@
55
[npm-url]:https://npmjs.org/package/cross-spawn
66
[downloads-image]:http://img.shields.io/npm/dm/cross-spawn.svg
77
[npm-image]:http://img.shields.io/npm/v/cross-spawn.svg
8-
[travis-url]:https://travis-ci.org/IndigoUnited/node-cross-spawn
9-
[travis-image]:http://img.shields.io/travis/IndigoUnited/node-cross-spawn/master.svg
8+
[travis-url]:https://travis-ci.org/moxystudio/node-cross-spawn
9+
[travis-image]:http://img.shields.io/travis/moxystudio/node-cross-spawn/master.svg
1010
[appveyor-url]:https://ci.appveyor.com/project/satazor/node-cross-spawn
1111
[appveyor-image]:https://img.shields.io/appveyor/ci/satazor/node-cross-spawn/master.svg
12-
[david-dm-url]:https://david-dm.org/IndigoUnited/node-cross-spawn
13-
[david-dm-image]:https://img.shields.io/david/IndigoUnited/node-cross-spawn.svg
14-
[david-dm-dev-url]:https://david-dm.org/IndigoUnited/node-cross-spawn?type=dev
15-
[david-dm-dev-image]:https://img.shields.io/david/dev/IndigoUnited/node-cross-spawn.svg
16-
[greenkeeper-image]:https://badges.greenkeeper.io/IndigoUnited/node-cross-spawn.svg
12+
[david-dm-url]:https://david-dm.org/moxystudio/node-cross-spawn
13+
[david-dm-image]:https://img.shields.io/david/moxystudio/node-cross-spawn.svg
14+
[david-dm-dev-url]:https://david-dm.org/moxystudio/node-cross-spawn?type=dev
15+
[david-dm-dev-image]:https://img.shields.io/david/dev/moxystudio/node-cross-spawn.svg
16+
[greenkeeper-image]:https://badges.greenkeeper.io/moxystudio/node-cross-spawn.svg
1717
[greenkeeper-url]:https://greenkeeper.io/
1818

1919
A cross platform solution to node's spawn and spawnSync.
@@ -23,10 +23,6 @@ A cross platform solution to node's spawn and spawnSync.
2323

2424
`$ npm install cross-spawn`
2525

26-
If you are using `spawnSync` on node 0.10 or older, you will also need to install `spawn-sync`:
27-
28-
`$ npm install spawn-sync`
29-
3026

3127
## Why
3228

@@ -35,7 +31,9 @@ Node has issues when using spawn on Windows:
3531
- It ignores [PATHEXT](https://github.com./joyent/node/issues/2318)
3632
- It does not support [shebangs](http://pt.wikipedia.org/wiki/Shebang)
3733
- No `options.shell` support on node `<v4.8`
38-
- It does not allow you to run `del` or `dir`
34+
- Has problems running commands with [spaces](https://github.com./nodejs/node/issues/7367)
35+
- Has problems running commands with posix relative paths (e.g.: `my-folder/my-executable`)
36+
- Circuvents an [issue](https://github.com./moxystudio/node-cross-spawn/issues/82) around command shims (files in node_modules/.bin/), where arguments with quotes and parenthesis would result in an [invalid syntax error](ADD_LINK_TO_TESTS)
3937

4038
All these issues are handled correctly by `cross-spawn`.
4139
There are some known modules, such as [win-spawn](https://github.com./ForbesLindesay/win-spawn), that try to solve this but they are either broken or provide faulty escaping of shell arguments.
@@ -59,20 +57,26 @@ var results = spawn.sync('npm', ['list', '-g', '-depth', '0'], { stdio: 'inherit
5957

6058
## Caveats
6159

62-
#### `options.shell` as an alternative to `cross-spawn`
60+
### Using `options.shell` as an alternative to `cross-spawn`
6361

6462
Starting from node `v4.8`, `spawn` has a `shell` option that allows you run commands from within a shell. This new option solves most of the problems that `cross-spawn` attempts to solve, but:
6563

6664
- It's not supported in node `<v4.8`
67-
- It has no support for shebangs on Windows
6865
- You must manually escape the command and arguments which is very error prone, specially when passing user input
66+
- It just solves the [PATHEXT](https://github.com./joyent/node/issues/2318) issue from the [Why](#why) section
6967

7068
If you are using the `shell` option to spawn a command in a cross platform way, consider using `cross-spawn` instead. You have been warned.
7169

70+
### `options.shell` support
71+
72+
While `cross-spawn` adds support for `options.shell` in node `<v4.8`, all of its enhancements are disabled.
73+
74+
This mimics the Node.js behavior. More specifically, the command and its arguments will not be automatically escaped nor shebang support will be offered. This is by design because if you are using `options.shell` you are probably targeting a specific platform anyway and you don't want things to get into your way.
7275

73-
#### Shebangs
76+
### Shebangs support
7477

75-
While `cross-spawn` handles shebangs on Windows, its support is limited: e.g.: it doesn't handle arguments after the path, e.g.: `#!/bin/bash -e`.
78+
While `cross-spawn` handles shebangs on Windows, its support is limited. More specifically, it just supports `#!/usr/bin/env <program>` where `<program>` must not contain any arguments.
79+
If you would like to have the shebang support improved, feel free to contribute via a pull-request.
7680

7781
Remember to always test your code on Windows!
7882

appveyor.yml

+22-11
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,32 @@
1-
# appveyor file
21
# http://www.appveyor.com/docs/appveyor-yml
32

4-
# build version format
5-
version: "{build}"
6-
7-
# fix lineendings in Windows
3+
# Fix line endings in Windows
84
init:
95
- git config --global core.autocrlf input
106

11-
# what combinations to test
7+
# If we are running on Node <6, we must install npm v3 otherwise
8+
# there will be intermitent errors when running `npm install`
129
environment:
1310
matrix:
14-
- nodejs_version: 0.10
15-
- nodejs_version: 0.12
16-
- nodejs_version: 4
11+
- nodejs_version: 4.7
12+
npm_version: ^3.0.0
13+
- nodejs_version: 4.8
14+
npm_version: ^3.0.0
15+
- nodejs_version: 5.6
16+
npm_version: ^3.0.0
17+
- nodejs_version: 5.7
18+
npm_version: ^3.0.0
1719
- nodejs_version: 6
18-
- nodejs_version: 7
20+
- nodejs_version: 8
21+
- nodejs_version: 9
1922

20-
# get the latest stable version of Node 0.STABLE.latest
2123
install:
2224
- ps: Install-Product node $env:nodejs_version
25+
- ps: |
26+
if ($env:npm_version)
27+
{
28+
npm install -g npm@$env:npm_version
29+
}
2330
- npm install
2431

2532
build: off
@@ -28,3 +35,7 @@ test_script:
2835
- node --version
2936
- npm --version
3037
- cmd: npm test --no-color
38+
39+
after_test:
40+
- "npm i codecov"
41+
- "node_modules/.bin/codecov"

index.js

+8-28
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,15 @@
11
'use strict';
22

3-
var cp = require('child_process');
4-
var parse = require('./lib/parse');
5-
var enoent = require('./lib/enoent');
6-
7-
var cpSpawnSync = cp.spawnSync;
3+
const cp = require('child_process');
4+
const parse = require('./lib/parse');
5+
const enoent = require('./lib/enoent');
86

97
function spawn(command, args, options) {
10-
var parsed;
11-
var spawned;
12-
138
// Parse the arguments
14-
parsed = parse(command, args, options);
9+
const parsed = parse(command, args, options);
1510

1611
// Spawn the child process
17-
spawned = cp.spawn(parsed.command, parsed.args, parsed.options);
12+
const spawned = cp.spawn(parsed.command, parsed.args, parsed.options);
1813

1914
// Hook into child process "exit" event to emit an error if the command
2015
// does not exists, see: https://github.com./IndigoUnited/node-cross-spawn/issues/16
@@ -24,28 +19,13 @@ function spawn(command, args, options) {
2419
}
2520

2621
function spawnSync(command, args, options) {
27-
var parsed;
28-
var result;
29-
30-
if (!cpSpawnSync) {
31-
try {
32-
cpSpawnSync = require('spawn-sync'); // eslint-disable-line global-require
33-
} catch (ex) {
34-
throw new Error(
35-
'In order to use spawnSync on node 0.10 or older, you must ' +
36-
'install spawn-sync:\n\n' +
37-
' npm install spawn-sync --save'
38-
);
39-
}
40-
}
41-
4222
// Parse the arguments
43-
parsed = parse(command, args, options);
23+
const parsed = parse(command, args, options);
4424

4525
// Spawn the child process
46-
result = cpSpawnSync(parsed.command, parsed.args, parsed.options);
26+
const result = cp.spawnSync(parsed.command, parsed.args, parsed.options);
4727

48-
// Analyze if the command does not exists, see: https://github.com./IndigoUnited/node-cross-spawn/issues/16
28+
// Analyze if the command does not exist, see: https://github.com./IndigoUnited/node-cross-spawn/issues/16
4929
result.error = result.error || enoent.verifyENOENTSync(result.status, parsed);
5030

5131
return result;

lib/enoent.js

+21-35
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,37 @@
11
'use strict';
22

3-
var isWin = process.platform === 'win32';
4-
var resolveCommand = require('./util/resolveCommand');
5-
6-
var isNode10 = process.version.indexOf('v0.10.') === 0;
7-
8-
function notFoundError(command, syscall) {
9-
var err;
10-
11-
err = new Error(syscall + ' ' + command + ' ENOENT');
12-
err.code = err.errno = 'ENOENT';
13-
err.syscall = syscall + ' ' + command;
14-
15-
return err;
3+
const isWin = process.platform === 'win32';
4+
5+
function notFoundError(original, syscall) {
6+
return Object.assign(new Error(`${syscall} ${original.command} ENOENT`), {
7+
code: 'ENOENT',
8+
errno: 'ENOENT',
9+
syscall: `${syscall} ${original.command}`,
10+
path: original.command,
11+
spawnargs: original.args,
12+
});
1613
}
1714

1815
function hookChildProcess(cp, parsed) {
19-
var originalEmit;
20-
2116
if (!isWin) {
2217
return;
2318
}
2419

25-
originalEmit = cp.emit;
26-
cp.emit = function (name, arg1) {
27-
var err;
20+
const originalEmit = cp.emit;
2821

22+
cp.emit = function (name, arg1) {
2923
// If emitting "exit" event and exit code is 1, we need to check if
3024
// the command exists and emit an "error" instead
31-
// See: https://github.com./IndigoUnited/node-cross-spawn/issues/16
25+
// See https://github.com./IndigoUnited/node-cross-spawn/issues/16
3226
if (name === 'exit') {
33-
err = verifyENOENT(arg1, parsed, 'spawn');
27+
const err = verifyENOENT(arg1, parsed, 'spawn');
3428

3529
if (err) {
3630
return originalEmit.call(cp, 'error', err);
3731
}
3832
}
3933

40-
return originalEmit.apply(cp, arguments);
34+
return originalEmit.apply(cp, arguments); // eslint-disable-line prefer-rest-params
4135
};
4236
}
4337

@@ -54,20 +48,12 @@ function verifyENOENTSync(status, parsed) {
5448
return notFoundError(parsed.original, 'spawnSync');
5549
}
5650

57-
// If we are in node 10, then we are using spawn-sync; if it exited
58-
// with -1 it probably means that the command does not exist
59-
if (isNode10 && status === -1) {
60-
parsed.file = isWin ? parsed.file : resolveCommand(parsed.original);
61-
62-
if (!parsed.file) {
63-
return notFoundError(parsed.original, 'spawnSync');
64-
}
65-
}
66-
6751
return null;
6852
}
6953

70-
module.exports.hookChildProcess = hookChildProcess;
71-
module.exports.verifyENOENT = verifyENOENT;
72-
module.exports.verifyENOENTSync = verifyENOENTSync;
73-
module.exports.notFoundError = notFoundError;
54+
module.exports = {
55+
hookChildProcess,
56+
verifyENOENT,
57+
verifyENOENTSync,
58+
notFoundError,
59+
};

0 commit comments

Comments
 (0)