Skip to content

Commit 19374fd

Browse files
ryzokukenaddaleax
authored andcommitted
fs: improve argument handling for ReadStream
Improve handling of erratic arguments in fs.ReadStream Refs: #19732 PR-URL: #19898 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Ron Korving <ron@ronkorving.nl> Reviewed-By: Ruben Bridgewater <ruben@bridgewater.de>
1 parent 745463a commit 19374fd

5 files changed

+96
-38
lines changed

doc/api/fs.md

+5
Original file line numberDiff line numberDiff line change
@@ -1299,6 +1299,11 @@ fs.copyFileSync('source.txt', 'destination.txt', COPYFILE_EXCL);
12991299
<!-- YAML
13001300
added: v0.1.31
13011301
changes:
1302+
- version: REPLACEME
1303+
pr-url: https://github.com/nodejs/node/pull/19898
1304+
description: Impose new restrictions on `start` and `end`, throwing
1305+
more appropriate errors in cases when we cannot reasonably
1306+
handle the input values.
13021307
- version: v7.6.0
13031308
pr-url: https://github.com/nodejs/node/pull/10739
13041309
description: The `path` parameter can be a WHATWG `URL` object using

lib/fs.js

+38-17
Original file line numberDiff line numberDiff line change
@@ -2031,30 +2031,51 @@ function ReadStream(path, options) {
20312031
this.closed = false;
20322032

20332033
if (this.start !== undefined) {
2034-
if (typeof this.start !== 'number' || Number.isNaN(this.start)) {
2035-
throw new ERR_INVALID_ARG_TYPE('start', 'number', this.start);
2036-
}
2037-
if (this.end === undefined) {
2038-
this.end = Infinity;
2039-
} else if (typeof this.end !== 'number' || Number.isNaN(this.end)) {
2040-
throw new ERR_INVALID_ARG_TYPE('end', 'number', this.end);
2034+
if (!Number.isSafeInteger(this.start)) {
2035+
if (typeof this.start !== 'number')
2036+
throw new ERR_INVALID_ARG_TYPE('start', 'number', this.start);
2037+
if (!Number.isInteger(this.start))
2038+
throw new ERR_OUT_OF_RANGE('start', 'an integer', this.start);
2039+
throw new ERR_OUT_OF_RANGE(
2040+
'start',
2041+
'>= 0 and <= 2 ** 53 - 1',
2042+
this.start
2043+
);
20412044
}
2042-
2043-
if (this.start > this.end) {
2044-
const errVal = `{start: ${this.start}, end: ${this.end}}`;
2045-
throw new ERR_OUT_OF_RANGE('start', '<= "end"', errVal);
2045+
if (this.start < 0) {
2046+
throw new ERR_OUT_OF_RANGE(
2047+
'start',
2048+
'>= 0 and <= 2 ** 53 - 1',
2049+
this.start
2050+
);
20462051
}
20472052

20482053
this.pos = this.start;
20492054
}
20502055

2051-
// Backwards compatibility: Make sure `end` is a number regardless of `start`.
2052-
// TODO(addaleax): Make the above typecheck not depend on `start` instead.
2053-
// (That is a semver-major change).
2054-
if (typeof this.end !== 'number')
2056+
if (this.end === undefined) {
20552057
this.end = Infinity;
2056-
else if (Number.isNaN(this.end))
2057-
throw new ERR_INVALID_ARG_TYPE('end', 'number', this.end);
2058+
} else if (this.end !== Infinity) {
2059+
if (!Number.isSafeInteger(this.end)) {
2060+
if (typeof this.end !== 'number')
2061+
throw new ERR_INVALID_ARG_TYPE('end', 'number', this.end);
2062+
if (!Number.isInteger(this.end))
2063+
throw new ERR_OUT_OF_RANGE('end', 'an integer', this.end);
2064+
throw new ERR_OUT_OF_RANGE('end', '>= 0 and <= 2 ** 53 - 1', this.end);
2065+
}
2066+
2067+
if (this.end < 0) {
2068+
throw new ERR_OUT_OF_RANGE('end', '>= 0 and <= 2 ** 53 - 1', this.end);
2069+
}
2070+
2071+
if (this.start !== undefined && this.start > this.end) {
2072+
throw new ERR_OUT_OF_RANGE(
2073+
'start',
2074+
`<= "end" (here: ${this.end})`,
2075+
this.start
2076+
);
2077+
}
2078+
}
20582079

20592080
if (typeof this.fd !== 'number')
20602081
this.open();

test/parallel/test-fs-read-stream-inherit.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,8 @@ const rangeFile = fixtures.path('x.txt');
110110

111111
{
112112
const message =
113-
'The value of "start" is out of range. It must be <= "end". ' +
114-
'Received {start: 10, end: 2}';
113+
'The value of "start" is out of range. It must be <= "end" (here: 2).' +
114+
' Received 10';
115115

116116
common.expectsError(
117117
() => {

test/parallel/test-fs-read-stream-throw-type-error.js

+49-17
Original file line numberDiff line numberDiff line change
@@ -13,23 +13,55 @@ fs.createReadStream(example, null);
1313
fs.createReadStream(example, 'utf8');
1414
fs.createReadStream(example, { encoding: 'utf8' });
1515

16-
const createReadStreamErr = (path, opt) => {
17-
common.expectsError(
18-
() => {
19-
fs.createReadStream(path, opt);
20-
},
21-
{
22-
code: 'ERR_INVALID_ARG_TYPE',
23-
type: TypeError
24-
});
16+
const createReadStreamErr = (path, opt, error) => {
17+
common.expectsError(() => {
18+
fs.createReadStream(path, opt);
19+
}, error);
2520
};
2621

27-
createReadStreamErr(example, 123);
28-
createReadStreamErr(example, 0);
29-
createReadStreamErr(example, true);
30-
createReadStreamErr(example, false);
22+
const typeError = {
23+
code: 'ERR_INVALID_ARG_TYPE',
24+
type: TypeError
25+
};
26+
27+
const rangeError = {
28+
code: 'ERR_OUT_OF_RANGE',
29+
type: RangeError
30+
};
31+
32+
[123, 0, true, false].forEach((opts) =>
33+
createReadStreamErr(example, opts, typeError)
34+
);
35+
36+
// Case 0: Should not throw if either start or end is undefined
37+
[{}, { start: 0 }, { end: Infinity }].forEach((opts) =>
38+
fs.createReadStream(example, opts)
39+
);
40+
41+
// Case 1: Should throw TypeError if either start or end is not of type 'number'
42+
[
43+
{ start: 'invalid' },
44+
{ end: 'invalid' },
45+
{ start: 'invalid', end: 'invalid' }
46+
].forEach((opts) => createReadStreamErr(example, opts, typeError));
47+
48+
// Case 2: Should throw RangeError if either start or end is NaN
49+
[{ start: NaN }, { end: NaN }, { start: NaN, end: NaN }].forEach((opts) =>
50+
createReadStreamErr(example, opts, rangeError)
51+
);
52+
53+
// Case 3: Should throw RangeError if either start or end is negative
54+
[{ start: -1 }, { end: -1 }, { start: -1, end: -1 }].forEach((opts) =>
55+
createReadStreamErr(example, opts, rangeError)
56+
);
57+
58+
// Case 4: Should throw RangeError if either start or end is fractional
59+
[{ start: 0.1 }, { end: 0.1 }, { start: 0.1, end: 0.1 }].forEach((opts) =>
60+
createReadStreamErr(example, opts, rangeError)
61+
);
62+
63+
// Case 5: Should not throw if both start and end are whole numbers
64+
fs.createReadStream(example, { start: 1, end: 5 });
3165

32-
// createReadSteam _should_ throw on NaN
33-
createReadStreamErr(example, { start: NaN });
34-
createReadStreamErr(example, { end: NaN });
35-
createReadStreamErr(example, { start: NaN, end: NaN });
66+
// Case 6: Should throw RangeError if start is greater than end
67+
createReadStreamErr(example, { start: 5, end: 1 }, rangeError);

test/parallel/test-fs-read-stream.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -148,8 +148,8 @@ common.expectsError(
148148
},
149149
{
150150
code: 'ERR_OUT_OF_RANGE',
151-
message: 'The value of "start" is out of range. It must be <= "end". ' +
152-
'Received {start: 10, end: 2}',
151+
message: 'The value of "start" is out of range. It must be <= "end"' +
152+
' (here: 2). Received 10',
153153
type: RangeError
154154
});
155155

0 commit comments

Comments
 (0)