-
Notifications
You must be signed in to change notification settings - Fork 30k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
child_process: spawn() and spawnSync() validation #8312
Conversation
// Validate the cwd, if present. | ||
if (options.cwd !== undefined && | ||
options.cwd !== null && | ||
!typeof options.cwd === 'string') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe a helper would be better?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds like string, null and undefined are allowed.
On Aug 29, 2016 12:09 AM, "Jeremiah Senkpiel" notifications@github.com
wrote:
In lib/child_process.js
#8312 (comment):@@ -329,6 +329,49 @@ function normalizeSpawnArguments(file /, args, options/) {
else if (options === null || typeof options !== 'object')
throw new TypeError('"options" argument must be an object');
- // Validate the cwd, if present.
- if (options.cwd !== undefined &&
options.cwd !== null &&
!typeof options.cwd === 'string')
Maybe a helper would be better? won't !typeof options.cwd === 'string'
cover all these 3 cases?—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
https://github.com/nodejs/node/pull/8312/files/b4a6558fcbcf6cde50ff29af2d3487896ad91c68#r76536916,
or mute the thread
https://github.com/notifications/unsubscribe-auth/ABjB841u9wl3B9jlsDIb7RdsDKU0iWTcks5qkdXdgaJpZM4Ju-4I
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do you mean another function like isOptionalString()
(where the optional part covers null
and undefined
)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
!typeof options.cwd === 'string'
is always false, it should be !(typeof options.cwd === 'string')
or (better) typeof options.cwd !== 'string'
.
@bnoordhuis any opinion on the approach here? |
The Moving the validation logic to JS land basically LGTM though. |
@cjihrig Ping. |
I plan on getting back to this next week. Sorry for the delay. |
c133999
to
83c7a88
Compare
af5c2f5
to
76d7181
Compare
50f599e
to
146129b
Compare
@bnoordhuis updated, PTAL. |
@bnoordhuis ping |
ping @nodejs/collaborators |
I'll look at this, but because the test was added in the same commit as the additional checks, its hard to evaluate what is different, i.e., what the behaviour used to be. If the test was added, then the commit that changed the behaviour changed the test... it would be easy to understand the change. |
Many thumbs up for solid testing of these APIs, and better input validation. cwd, detached, uid, gid are all implemented in Its not clear what tests should go where, and its also very difficult to determine what kind of test coverage there is, not in the line sense, but in the sense of: are the tests against spawnSync here valid for any of the other Sync calls? Any other spawn calls? Its painful, but maybe the tests should be factored so they can be applied to all the child_process functions that use the same normalize? Or perhaps the tests should all just be in one file, with a comment in the test saying that even though it appears that only one API is tested, the underlying code is the same, so its hitting every API? Unrelated to this PR, but I'm pretty disappointed that the arg permutation tests in test-child-process-spawn-typeerror.js were never extended to include *Sync, since arg permutations was such a rich source of bugs and inconsistencies, and I'm poking around, but I can't see any tests for their arg permutations. But even though that problem is unrelated, its why I suggest that having the tests be organized in a way that its (EDIT) "easy" for a reader to verify the coverage scenarios is important. |
|
||
// Validate windowsVerbatimArguments, if present. | ||
if (options.windowsVerbatimArguments != null && | ||
typeof options.windowsVerbatimArguments !== 'boolean') { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does this have to be, literally, boolean? I would expect it to have to be truthy/falsy
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It could probably be coerced to a Boolean, but the extra strictness is good IMO. Not that it matters much, but windowsVerbatimArguments
isn't documented anyway, IIRC.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree, its internal-only, so maybe not so important, and better to start strict. I don't agree strictness is generally good in this case, btw. It creates a bizarre API wherein truthy is sometimes true/false, sometimes null/undefined/0/"" are also false, sometimes not, and sometimes function/[]/{}/non-zero/"strings" are also true, and sometimes null or undefined are a "third way", neither true or false but some kind of other behaviour. Ouch. Anyhow, as a semi-private (but used by modules on npmjs.org none-the-less) API, I'm OK with this restriction.
I just did that because
I agree that the tests are kind of all over the place. This PR is a step towards consolidating the behavior of
I think just about everything goes through |
The tests you add seemed like an obvious candidate to be in a "spawn typeerror" test, but I understand the distinction you draw above. If you do draw that distinction, my request is that the name of the tests (and a comment at their top) clearly communicate it: the names/comments should indicate what that test is for, so that we don't get diffusion and confusion of tests over time. For example:
I suggest a rename like this because it would be a disaster if someone created a |
|
||
pass('uid', undefined); | ||
pass('uid', null); | ||
pass('uid', process.getuid()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assert NaN
is not valid. Ditto below.
pass('shell', undefined); | ||
pass('shell', null); | ||
pass('shell', false); | ||
fail('shell', 0, err); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
0
and 1
are usually acceptable as truthy values, I think they should be accepted here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd personally go for strict.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Good test coverage.
pass('shell', undefined); | ||
pass('shell', null); | ||
pass('shell', false); | ||
fail('shell', 0, err); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd personally go for strict.
@@ -315,6 +315,9 @@ function _convertCustomFds(options) { | |||
function normalizeSpawnArguments(file /*, args, options*/) { | |||
var args, options; | |||
|
|||
if (typeof file !== 'string' || file.length === 0) | |||
throw new TypeError('"file" argument must be a string'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: If the input is an empty string, the error message might not be exactly appropriate.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated to specify "non-empty string"
CI: https://ci.nodejs.org/job/node-test-pull-request/5539/ New CI because Windows: https://ci.nodejs.org/job/node-test-pull-request/5545/ |
CI is all green. |
This commit verifies that the child process handle is of the correct type before trying to close it in CloseHandlesAndDeleteLoop(). This catches the case where input validation failed, and the child process was never actually spawned. Fixes: nodejs#8096 Fixes: nodejs#8539 Refs: nodejs#9722 PR-URL: nodejs#8312 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
This commit applies stricter input validation in normalizeSpawnArguments(), which is run by all of the child_process methods. Additional checks are added for spawnSync() specific inputs. Fixes: nodejs#8096 Fixes: nodejs#8539 Refs: nodejs#9722 PR-URL: nodejs#8312 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
This commit removes C++ checks from spawn() and spawnSync() that are duplicates of the JavaScript type checking. Fixes: nodejs#8096 Fixes: nodejs#8539 Refs: nodejs#9722 PR-URL: nodejs#8312 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
@Fishrock123 the bot should likely not be adding lts labels to anything labelled semver major |
This commit verifies that the child process handle is of the correct type before trying to close it in CloseHandlesAndDeleteLoop(). This catches the case where input validation failed, and the child process was never actually spawned. Fixes: nodejs#8096 Fixes: nodejs#8539 Refs: nodejs#9722 PR-URL: nodejs#8312 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
This commit applies stricter input validation in normalizeSpawnArguments(), which is run by all of the child_process methods. Additional checks are added for spawnSync() specific inputs. Fixes: nodejs#8096 Fixes: nodejs#8539 Refs: nodejs#9722 PR-URL: nodejs#8312 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
This commit removes C++ checks from spawn() and spawnSync() that are duplicates of the JavaScript type checking. Fixes: nodejs#8096 Fixes: nodejs#8539 Refs: nodejs#9722 PR-URL: nodejs#8312 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Checklist
make -j4 test
(UNIX), orvcbuild test nosign
(Windows) passesAffected core subsystem(s)
child_process, src
Description of change
This PR is intended to fix #8096. The first commit prevents the abort from #8096 by verifying that the handle type is correct before trying to close it. This commit is semver-patch.
This leaves an uninformative
EINVAL
error being thrown due to the bad input. So, the second commit in this PR addresses that by producing more helpful errors in the JavaScript layer. In doing so, it better unifies thespawn()
andspawnSync()
input checking (which also nicely bubbles up to the otherchild_process
methods). AdditionalspawnSync()
specific checks are also provided. This commit is semver-major due to the changes in error messages.The third commit removes redundant C++ type checks that are now done in the JS layer.
cc: @bnoordhuis, @targos, and @retrohacker from #8096
Fixes: #8096
Fixes: #8539