Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit beb75bd

Browse filesBrowse files
ljharbruyadorno
authored andcommitted
fs: loosen validation to allow objects with an own toString function
PR-URL: #34993 Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com> Reviewed-By: Bradley Farias <bradley.meck@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Derek Lewis <DerekNonGeneric@inf.is>
1 parent 76e24f9 commit beb75bd
Copy full SHA for beb75bd

File tree

Expand file treeCollapse file tree

5 files changed

+113
-28
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

5 files changed

+113
-28
lines changed
Open diff view settings
Collapse file

‎doc/api/fs.md‎

Copy file name to clipboardExpand all lines: doc/api/fs.md
+61-19Lines changed: 61 additions & 19 deletions
  • Display the source diff
  • Display the rich diff
Original file line numberDiff line numberDiff line change
@@ -4143,6 +4143,10 @@ This happens when:
41434143
<!-- YAML
41444144
added: v0.0.2
41454145
changes:
4146+
- version: REPLACEME
4147+
pr-url: https://github.com/nodejs/node/pull/34993
4148+
description: The `buffer` parameter will stringify an object with an
4149+
explicit `toString` function.
41464150
- version: v14.0.0
41474151
pr-url: https://github.com/nodejs/node/pull/31030
41484152
description: The `buffer` parameter won't coerce unsupported input to
@@ -4168,7 +4172,7 @@ changes:
41684172
-->
41694173

41704174
* `fd` {integer}
4171-
* `buffer` {Buffer|TypedArray|DataView}
4175+
* `buffer` {Buffer|TypedArray|DataView|string|Object}
41724176
* `offset` {integer}
41734177
* `length` {integer}
41744178
* `position` {integer}
@@ -4177,7 +4181,8 @@ changes:
41774181
* `bytesWritten` {integer}
41784182
* `buffer` {Buffer|TypedArray|DataView}
41794183

4180-
Write `buffer` to the file specified by `fd`.
4184+
Write `buffer` to the file specified by `fd`. If `buffer` is a normal object, it
4185+
must have an own `toString` function property.
41814186

41824187
`offset` determines the part of the buffer to be written, and `length` is
41834188
an integer specifying the number of bytes to write.
@@ -4204,6 +4209,10 @@ the end of the file.
42044209
<!-- YAML
42054210
added: v0.11.5
42064211
changes:
4212+
- version: REPLACEME
4213+
pr-url: https://github.com/nodejs/node/pull/34993
4214+
description: The `string` parameter will stringify an object with an
4215+
explicit `toString` function.
42074216
- version: v14.0.0
42084217
pr-url: https://github.com/nodejs/node/pull/31030
42094218
description: The `string` parameter won't coerce unsupported input to
@@ -4222,16 +4231,16 @@ changes:
42224231
-->
42234232

42244233
* `fd` {integer}
4225-
* `string` {string}
4234+
* `string` {string|Object}
42264235
* `position` {integer}
42274236
* `encoding` {string} **Default:** `'utf8'`
42284237
* `callback` {Function}
42294238
* `err` {Error}
42304239
* `written` {integer}
42314240
* `string` {string}
42324241

4233-
Write `string` to the file specified by `fd`. If `string` is not a string, then
4234-
an exception will be thrown.
4242+
Write `string` to the file specified by `fd`. If `string` is not a string, or an
4243+
object with an own `toString` function property, then an exception is thrown.
42354244

42364245
`position` refers to the offset from the beginning of the file where this data
42374246
should be written. If `typeof position !== 'number'` the data will be written at
@@ -4263,6 +4272,10 @@ details.
42634272
<!-- YAML
42644273
added: v0.1.29
42654274
changes:
4275+
- version: REPLACEME
4276+
pr-url: https://github.com/nodejs/node/pull/34993
4277+
description: The `data` parameter will stringify an object with an
4278+
explicit `toString` function.
42664279
- version: v14.0.0
42674280
pr-url: https://github.com/nodejs/node/pull/31030
42684281
description: The `data` parameter won't coerce unsupported input to
@@ -4288,7 +4301,7 @@ changes:
42884301
-->
42894302

42904303
* `file` {string|Buffer|URL|integer} filename or file descriptor
4291-
* `data` {string|Buffer|TypedArray|DataView}
4304+
* `data` {string|Buffer|TypedArray|DataView|Object}
42924305
* `options` {Object|string}
42934306
* `encoding` {string|null} **Default:** `'utf8'`
42944307
* `mode` {integer} **Default:** `0o666`
@@ -4304,6 +4317,7 @@ When `file` is a file descriptor, the behavior is similar to calling
43044317
a file descriptor.
43054318

43064319
The `encoding` option is ignored if `data` is a buffer.
4320+
If `data` is a normal object, it must have an own `toString` function property.
43074321

43084322
```js
43094323
const data = new Uint8Array(Buffer.from('Hello Node.js'));
@@ -4353,6 +4367,10 @@ to contain only `', World'`.
43534367
<!-- YAML
43544368
added: v0.1.29
43554369
changes:
4370+
- version: REPLACEME
4371+
pr-url: https://github.com/nodejs/node/pull/34993
4372+
description: The `data` parameter will stringify an object with an
4373+
explicit `toString` function.
43564374
- version: v14.0.0
43574375
pr-url: https://github.com/nodejs/node/pull/31030
43584376
description: The `data` parameter won't coerce unsupported input to
@@ -4370,7 +4388,7 @@ changes:
43704388
-->
43714389

43724390
* `file` {string|Buffer|URL|integer} filename or file descriptor
4373-
* `data` {string|Buffer|TypedArray|DataView}
4391+
* `data` {string|Buffer|TypedArray|DataView|Object}
43744392
* `options` {Object|string}
43754393
* `encoding` {string|null} **Default:** `'utf8'`
43764394
* `mode` {integer} **Default:** `0o666`
@@ -4385,6 +4403,10 @@ this API: [`fs.writeFile()`][].
43854403
<!-- YAML
43864404
added: v0.1.21
43874405
changes:
4406+
- version: REPLACEME
4407+
pr-url: https://github.com/nodejs/node/pull/34993
4408+
description: The `buffer` parameter will stringify an object with an
4409+
explicit `toString` function.
43884410
- version: v14.0.0
43894411
pr-url: https://github.com/nodejs/node/pull/31030
43904412
description: The `buffer` parameter won't coerce unsupported input to
@@ -4402,7 +4424,7 @@ changes:
44024424
-->
44034425

44044426
* `fd` {integer}
4405-
* `buffer` {Buffer|TypedArray|DataView}
4427+
* `buffer` {Buffer|TypedArray|DataView|string|Object}
44064428
* `offset` {integer}
44074429
* `length` {integer}
44084430
* `position` {integer}
@@ -4415,6 +4437,10 @@ this API: [`fs.write(fd, buffer...)`][].
44154437
<!-- YAML
44164438
added: v0.11.5
44174439
changes:
4440+
- version: REPLACEME
4441+
pr-url: https://github.com/nodejs/node/pull/34993
4442+
description: The `string` parameter will stringify an object with an
4443+
explicit `toString` function.
44184444
- version: v14.0.0
44194445
pr-url: https://github.com/nodejs/node/pull/31030
44204446
description: The `string` parameter won't coerce unsupported input to
@@ -4425,7 +4451,7 @@ changes:
44254451
-->
44264452

44274453
* `fd` {integer}
4428-
* `string` {string}
4454+
* `string` {string|Object}
44294455
* `position` {integer}
44304456
* `encoding` {string}
44314457
* Returns: {number} The number of bytes written.
@@ -4788,13 +4814,17 @@ This function does not work on AIX versions before 7.1, it will resolve the
47884814
<!-- YAML
47894815
added: v10.0.0
47904816
changes:
4817+
- version: REPLACEME
4818+
pr-url: https://github.com/nodejs/node/pull/34993
4819+
description: The `buffer` parameter will stringify an object with an
4820+
explicit `toString` function.
47914821
- version: v14.0.0
47924822
pr-url: https://github.com/nodejs/node/pull/31030
47934823
description: The `buffer` parameter won't coerce unsupported input to
47944824
buffers anymore.
47954825
-->
47964826

4797-
* `buffer` {Buffer|Uint8Array}
4827+
* `buffer` {Buffer|Uint8Array|string|Object}
47984828
* `offset` {integer}
47994829
* `length` {integer}
48004830
* `position` {integer}
@@ -4825,19 +4855,23 @@ the end of the file.
48254855
<!-- YAML
48264856
added: v10.0.0
48274857
changes:
4858+
- version: REPLACEME
4859+
pr-url: https://github.com/nodejs/node/pull/34993
4860+
description: The `string` parameter will stringify an object with an
4861+
explicit `toString` function.
48284862
- version: v14.0.0
48294863
pr-url: https://github.com/nodejs/node/pull/31030
48304864
description: The `string` parameter won't coerce unsupported input to
48314865
strings anymore.
48324866
-->
48334867

4834-
* `string` {string}
4868+
* `string` {string|Object}
48354869
* `position` {integer}
48364870
* `encoding` {string} **Default:** `'utf8'`
48374871
* Returns: {Promise}
48384872

4839-
Write `string` to the file. If `string` is not a string, then
4840-
an exception will be thrown.
4873+
Write `string` to the file. If `string` is not a string, or an
4874+
object with an own `toString` function property, then an exception is thrown.
48414875

48424876
The `Promise` is resolved with an object containing a `bytesWritten` property
48434877
identifying the number of bytes written, and a `buffer` property containing
@@ -4861,20 +4895,24 @@ the end of the file.
48614895
<!-- YAML
48624896
added: v10.0.0
48634897
changes:
4898+
- version: REPLACEME
4899+
pr-url: https://github.com/nodejs/node/pull/34993
4900+
description: The `data` parameter will stringify an object with an
4901+
explicit `toString` function.
48644902
- version: v14.0.0
48654903
pr-url: https://github.com/nodejs/node/pull/31030
48664904
description: The `data` parameter won't coerce unsupported input to
48674905
strings anymore.
48684906
-->
48694907

4870-
* `data` {string|Buffer|Uint8Array}
4908+
* `data` {string|Buffer|Uint8Array|Object}
48714909
* `options` {Object|string}
48724910
* `encoding` {string|null} **Default:** `'utf8'`
48734911
* Returns: {Promise}
48744912

48754913
Asynchronously writes data to a file, replacing the file if it already exists.
4876-
`data` can be a string or a buffer. The `Promise` will be resolved with no
4877-
arguments upon success.
4914+
`data` can be a string, a buffer, or an object with an own `toString` function
4915+
property. The `Promise` is resolved with no arguments upon success.
48784916

48794917
The `encoding` option is ignored if `data` is a buffer.
48804918

@@ -5492,23 +5530,27 @@ The `atime` and `mtime` arguments follow these rules:
54925530
<!-- YAML
54935531
added: v10.0.0
54945532
changes:
5533+
- version: REPLACEME
5534+
pr-url: https://github.com/nodejs/node/pull/34993
5535+
description: The `data` parameter will stringify an object with an
5536+
explicit `toString` function.
54955537
- version: v14.0.0
54965538
pr-url: https://github.com/nodejs/node/pull/31030
54975539
description: The `data` parameter won't coerce unsupported input to
54985540
strings anymore.
54995541
-->
55005542

55015543
* `file` {string|Buffer|URL|FileHandle} filename or `FileHandle`
5502-
* `data` {string|Buffer|Uint8Array}
5544+
* `data` {string|Buffer|Uint8Array|Object}
55035545
* `options` {Object|string}
55045546
* `encoding` {string|null} **Default:** `'utf8'`
55055547
* `mode` {integer} **Default:** `0o666`
55065548
* `flag` {string} See [support of file system `flags`][]. **Default:** `'w'`.
55075549
* Returns: {Promise}
55085550

55095551
Asynchronously writes data to a file, replacing the file if it already exists.
5510-
`data` can be a string or a buffer. The `Promise` will be resolved with no
5511-
arguments upon success.
5552+
`data` can be a string, a buffer, or an object with an own `toString` function
5553+
property. The `Promise` is resolved with no arguments upon success.
55125554

55135555
The `encoding` option is ignored if `data` is a buffer.
55145556

Collapse file

‎lib/fs.js‎

Copy file name to clipboardExpand all lines: lib/fs.js
+4-3Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ const {
3737
ObjectDefineProperties,
3838
ObjectDefineProperty,
3939
Promise,
40+
String,
4041
} = primordials;
4142

4243
const { fs: constants } = internalBinding('constants');
@@ -663,7 +664,7 @@ function write(fd, buffer, offset, length, position, callback) {
663664

664665
const req = new FSReqCallback();
665666
req.oncomplete = wrapper;
666-
return binding.writeString(fd, buffer, offset, length, req);
667+
return binding.writeString(fd, String(buffer), offset, length, req);
667668
}
668669

669670
ObjectDefineProperty(write, internalUtil.customPromisifyArgs,
@@ -1383,7 +1384,7 @@ function writeFile(path, data, options, callback) {
13831384

13841385
if (!isArrayBufferView(data)) {
13851386
validateStringAfterArrayBufferView(data, 'data');
1386-
data = Buffer.from(data, options.encoding || 'utf8');
1387+
data = Buffer.from(String(data), options.encoding || 'utf8');
13871388
}
13881389

13891390
if (isFd(path)) {
@@ -1407,7 +1408,7 @@ function writeFileSync(path, data, options) {
14071408

14081409
if (!isArrayBufferView(data)) {
14091410
validateStringAfterArrayBufferView(data, 'data');
1410-
data = Buffer.from(data, options.encoding || 'utf8');
1411+
data = Buffer.from(String(data), options.encoding || 'utf8');
14111412
}
14121413

14131414
const flag = options.flag || 'w';
Collapse file

‎lib/internal/fs/utils.js‎

Copy file name to clipboardExpand all lines: lib/internal/fs/utils.js
+18-6Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ const {
55
BigInt,
66
DateNow,
77
Error,
8+
ObjectPrototypeHasOwnProperty,
89
Number,
910
NumberIsFinite,
1011
MathMin,
@@ -697,13 +698,24 @@ const getValidMode = hideStackFrames((mode, type) => {
697698
});
698699

699700
const validateStringAfterArrayBufferView = hideStackFrames((buffer, name) => {
700-
if (typeof buffer !== 'string') {
701-
throw new ERR_INVALID_ARG_TYPE(
702-
name,
703-
['string', 'Buffer', 'TypedArray', 'DataView'],
704-
buffer
705-
);
701+
if (typeof buffer === 'string') {
702+
return;
703+
}
704+
705+
if (
706+
typeof buffer === 'object' &&
707+
buffer !== null &&
708+
typeof buffer.toString === 'function' &&
709+
ObjectPrototypeHasOwnProperty(buffer, 'toString')
710+
) {
711+
return;
706712
}
713+
714+
throw new ERR_INVALID_ARG_TYPE(
715+
name,
716+
['string', 'Buffer', 'TypedArray', 'DataView'],
717+
buffer
718+
);
707719
});
708720

709721
module.exports = {
Collapse file

‎test/parallel/test-fs-write-file-sync.js‎

Copy file name to clipboardExpand all lines: test/parallel/test-fs-write-file-sync.js
+14Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,3 +101,17 @@ tmpdir.refresh();
101101
const content = fs.readFileSync(file, { encoding: 'utf8' });
102102
assert.strictEqual(content, 'hello world!');
103103
}
104+
105+
// Test writeFileSync with an object with an own toString function
106+
{
107+
const file = path.join(tmpdir.path, 'testWriteFileSyncStringify.txt');
108+
const data = {
109+
toString() {
110+
return 'hello world!';
111+
}
112+
};
113+
114+
fs.writeFileSync(file, data, { encoding: 'utf8', flag: 'a' });
115+
const content = fs.readFileSync(file, { encoding: 'utf8' });
116+
assert.strictEqual(content, String(data));
117+
}
Collapse file

‎test/parallel/test-fs-write.js‎

Copy file name to clipboardExpand all lines: test/parallel/test-fs-write.js
+16Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ tmpdir.refresh();
3232
const fn = path.join(tmpdir.path, 'write.txt');
3333
const fn2 = path.join(tmpdir.path, 'write2.txt');
3434
const fn3 = path.join(tmpdir.path, 'write3.txt');
35+
const fn4 = path.join(tmpdir.path, 'write4.txt');
3536
const expected = 'ümlaut.';
3637
const constants = fs.constants;
3738

@@ -134,6 +135,21 @@ fs.open(fn3, 'w', 0o644, common.mustCall((err, fd) => {
134135
fs.write(fd, expected, done);
135136
}));
136137

138+
fs.open(fn4, 'w', 0o644, common.mustCall((err, fd) => {
139+
assert.ifError(err);
140+
141+
const done = common.mustCall((err, written) => {
142+
assert.ifError(err);
143+
assert.strictEqual(written, Buffer.byteLength(expected));
144+
fs.closeSync(fd);
145+
});
146+
147+
const data = {
148+
toString() { return expected; }
149+
};
150+
fs.write(fd, data, done);
151+
}));
152+
137153
[false, 'test', {}, [], null, undefined].forEach((i) => {
138154
assert.throws(
139155
() => fs.write(i, common.mustNotCall()),

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.