mirror of
https://github.com/nodejs/node.git
synced 2024-11-21 10:59:27 +00:00
assert: enforce type check in deepStrictEqual
Add checks for the built-in type tags to catch objects with faked prototypes. See https://tc39.github.io/ecma262/#sec-object.prototype.tostring for a partial list of built-in tags. Fixes: https://github.com/nodejs/node/issues/10258 PR-URL: https://github.com/nodejs/node/pull/10282 Reviewed-By: Rich Trott <rtrott@gmail.com> Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: James M Snell <jasnell@gmail.com>
This commit is contained in:
parent
055482c21e
commit
efec14a7d1
@ -125,12 +125,13 @@ changes:
|
||||
* `expected` {any}
|
||||
* `message` {any}
|
||||
|
||||
Generally identical to `assert.deepEqual()` with two exceptions:
|
||||
Generally identical to `assert.deepEqual()` with three exceptions:
|
||||
|
||||
1. Primitive values are compared using the [Strict Equality Comparison][]
|
||||
( `===` ).
|
||||
2. [`[[Prototype]]`][prototype-spec] of objects are compared using
|
||||
the [Strict Equality Comparison][] too.
|
||||
3. [Type tags][Object.prototype.toString()] of objects should be the same.
|
||||
|
||||
```js
|
||||
const assert = require('assert');
|
||||
@ -141,6 +142,25 @@ assert.deepEqual({a: 1}, {a: '1'});
|
||||
assert.deepStrictEqual({a: 1}, {a: '1'});
|
||||
// AssertionError: { a: 1 } deepStrictEqual { a: '1' }
|
||||
// because 1 !== '1' using strict equality
|
||||
|
||||
// The following objects don't have own properties
|
||||
const date = new Date();
|
||||
const object = {};
|
||||
const fakeDate = {};
|
||||
|
||||
Object.setPrototypeOf(fakeDate, Date.prototype);
|
||||
|
||||
assert.deepEqual(object, fakeDate);
|
||||
// OK, doesn't check [[Prototype]]
|
||||
assert.deepStrictEqual(object, fakeDate);
|
||||
// AssertionError: {} deepStrictEqual Date {}
|
||||
// Different [[Prototype]]
|
||||
|
||||
assert.deepEqual(date, fakeDate);
|
||||
// OK, doesn't check type tags
|
||||
assert.deepStrictEqual(date, fakeDate);
|
||||
// AssertionError: 2017-03-11T14:25:31.849Z deepStrictEqual Date {}
|
||||
// Different type tags
|
||||
```
|
||||
|
||||
If the values are not equal, an `AssertionError` is thrown with a `message`
|
||||
@ -579,4 +599,5 @@ For more information, see
|
||||
[SameValueZero]: https://tc39.github.io/ecma262/#sec-samevaluezero
|
||||
[prototype-spec]: https://tc39.github.io/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots
|
||||
[mdn-equality-guide]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Equality_comparisons_and_sameness
|
||||
[enumerable "own" properties]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Enumerability_and_ownership_of_properties
|
||||
[enumerable "own" properties]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Enumerability_and_ownership_of_properties
|
||||
[Object.prototype.toString()]: https://tc39.github.io/ecma262/#sec-object.prototype.tostring
|
||||
|
@ -200,6 +200,10 @@ function _deepEqual(actual, expected, strict, memos) {
|
||||
if (Object.getPrototypeOf(actual) !== Object.getPrototypeOf(expected)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (actualTag !== expectedTag) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Do fast checks for builtin types.
|
||||
|
61
test/parallel/test-assert-checktag.js
Normal file
61
test/parallel/test-assert-checktag.js
Normal file
@ -0,0 +1,61 @@
|
||||
'use strict';
|
||||
require('../common');
|
||||
const assert = require('assert');
|
||||
const util = require('util');
|
||||
|
||||
// Template tag function turning an error message into a RegExp
|
||||
// for assert.throws()
|
||||
function re(literals, ...values) {
|
||||
let result = literals[0];
|
||||
for (const [i, value] of values.entries()) {
|
||||
const str = util.inspect(value);
|
||||
// Need to escape special characters.
|
||||
result += str.replace(/[\\^$.*+?()[\]{}|=!<>:-]/g, '\\$&');
|
||||
result += literals[i + 1];
|
||||
}
|
||||
return new RegExp('^AssertionError: ' + result + '$');
|
||||
}
|
||||
|
||||
// Turn off no-restricted-properties because we are testing deepEqual!
|
||||
/* eslint-disable no-restricted-properties */
|
||||
|
||||
// See https://github.com/nodejs/node/issues/10258
|
||||
{
|
||||
const date = new Date('2016');
|
||||
function FakeDate() {}
|
||||
FakeDate.prototype = Date.prototype;
|
||||
const fake = new FakeDate();
|
||||
|
||||
assert.doesNotThrow(() => assert.deepEqual(date, fake));
|
||||
assert.doesNotThrow(() => assert.deepEqual(fake, date));
|
||||
|
||||
// For deepStrictEqual we check the runtime type,
|
||||
// then reveal the fakeness of the fake date
|
||||
assert.throws(() => assert.deepStrictEqual(date, fake),
|
||||
re`${date} deepStrictEqual Date {}`);
|
||||
assert.throws(() => assert.deepStrictEqual(fake, date),
|
||||
re`Date {} deepStrictEqual ${date}`);
|
||||
}
|
||||
|
||||
{ // At the moment global has its own type tag
|
||||
const fakeGlobal = {};
|
||||
Object.setPrototypeOf(fakeGlobal, Object.getPrototypeOf(global));
|
||||
for (const prop of Object.keys(global)) {
|
||||
fakeGlobal[prop] = global[prop];
|
||||
}
|
||||
assert.doesNotThrow(() => assert.deepEqual(fakeGlobal, global));
|
||||
// Message will be truncated anyway, don't validate
|
||||
assert.throws(() => assert.deepStrictEqual(fakeGlobal, global));
|
||||
}
|
||||
|
||||
{ // At the moment process has its own type tag
|
||||
const fakeProcess = {};
|
||||
Object.setPrototypeOf(fakeProcess, Object.getPrototypeOf(process));
|
||||
for (const prop of Object.keys(process)) {
|
||||
fakeProcess[prop] = process[prop];
|
||||
}
|
||||
assert.doesNotThrow(() => assert.deepEqual(fakeProcess, process));
|
||||
// Message will be truncated anyway, don't validate
|
||||
assert.throws(() => assert.deepStrictEqual(fakeProcess, process));
|
||||
}
|
||||
/* eslint-enable */
|
Loading…
Reference in New Issue
Block a user