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 05802c2

Browse filesBrowse files
aduh95ruyadorno
authored andcommitted
module: protect against prototype mutation
Ensures that mutating the `Object` prototype does not influence the parsing of `package.json` files. PR-URL: #44007 Reviewed-By: Geoffrey Booth <webadmin@geoffreybooth.com>
1 parent 4755ad5 commit 05802c2
Copy full SHA for 05802c2

File tree

Expand file treeCollapse file tree

8 files changed

+96
-15
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

8 files changed

+96
-15
lines changed
Open diff view settings
Collapse file

‎lib/internal/modules/cjs/helpers.js‎

Copy file name to clipboardExpand all lines: lib/internal/modules/cjs/helpers.js
+2-1Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ const path = require('path');
2424
const { pathToFileURL, fileURLToPath, URL } = require('internal/url');
2525

2626
const { getOptionValue } = require('internal/options');
27+
const { setOwnProperty } = require('internal/util');
2728
const userConditions = getOptionValue('--conditions');
2829

2930
let debug = require('internal/util/debuglog').debuglog('module', (fn) => {
@@ -117,7 +118,7 @@ function makeRequireFunction(mod, redirects) {
117118

118119
resolve.paths = paths;
119120

120-
require.main = process.mainModule;
121+
setOwnProperty(require, 'main', process.mainModule);
121122

122123
// Enable support to add extra extension types.
123124
require.extensions = Module._extensions;
Collapse file

‎lib/internal/modules/cjs/loader.js‎

Copy file name to clipboardExpand all lines: lib/internal/modules/cjs/loader.js
+10-11Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ const {
7979
maybeCacheSourceMap,
8080
} = require('internal/source_map/source_map_cache');
8181
const { pathToFileURL, fileURLToPath, isURLInstance } = require('internal/url');
82-
const { deprecate, kEmptyObject } = require('internal/util');
82+
const { deprecate, kEmptyObject, filterOwnProperties, setOwnProperty } = require('internal/util');
8383
const vm = require('vm');
8484
const assert = require('internal/assert');
8585
const fs = require('fs');
@@ -172,7 +172,7 @@ const moduleParentCache = new SafeWeakMap();
172172
function Module(id = '', parent) {
173173
this.id = id;
174174
this.path = path.dirname(id);
175-
this.exports = {};
175+
setOwnProperty(this, 'exports', {});
176176
moduleParentCache.set(this, parent);
177177
updateChildren(parent, this, false);
178178
this.filename = null;
@@ -312,14 +312,13 @@ function readPackage(requestPath) {
312312
}
313313

314314
try {
315-
const parsed = JSONParse(json);
316-
const filtered = {
317-
name: parsed.name,
318-
main: parsed.main,
319-
exports: parsed.exports,
320-
imports: parsed.imports,
321-
type: parsed.type
322-
};
315+
const filtered = filterOwnProperties(JSONParse(json), [
316+
'name',
317+
'main',
318+
'exports',
319+
'imports',
320+
'type',
321+
]);
323322
packageJsonCache.set(jsonPath, filtered);
324323
return filtered;
325324
} catch (e) {
@@ -1185,7 +1184,7 @@ Module._extensions['.json'] = function(module, filename) {
11851184
}
11861185

11871186
try {
1188-
module.exports = JSONParse(stripBOM(content));
1187+
setOwnProperty(module, 'exports', JSONParse(stripBOM(content)));
11891188
} catch (err) {
11901189
err.message = filename + ': ' + err.message;
11911190
throw err;
Collapse file

‎lib/internal/modules/esm/package_config.js‎

Copy file name to clipboardExpand all lines: lib/internal/modules/esm/package_config.js
+4-2Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
const {
44
JSONParse,
5+
ObjectPrototypeHasOwnProperty,
56
SafeMap,
67
StringPrototypeEndsWith,
78
} = primordials;
@@ -11,6 +12,7 @@ const {
1112
} = require('internal/errors').codes;
1213

1314
const packageJsonReader = require('internal/modules/package_json_reader');
15+
const { filterOwnProperties } = require('internal/util');
1416

1517

1618
/**
@@ -66,8 +68,8 @@ function getPackageConfig(path, specifier, base) {
6668
);
6769
}
6870

69-
let { imports, main, name, type } = packageJSON;
70-
const { exports } = packageJSON;
71+
let { imports, main, name, type } = filterOwnProperties(packageJSON, ['imports', 'main', 'name', 'type']);
72+
const exports = ObjectPrototypeHasOwnProperty(packageJSON, 'exports') ? packageJSON.exports : undefined;
7173
if (typeof imports !== 'object' || imports === null) {
7274
imports = undefined;
7375
}
Collapse file

‎lib/internal/util.js‎

Copy file name to clipboardExpand all lines: lib/internal/util.js
+32Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const {
1414
ObjectGetOwnPropertyDescriptors,
1515
ObjectGetPrototypeOf,
1616
ObjectFreeze,
17+
ObjectPrototypeHasOwnProperty,
1718
ObjectSetPrototypeOf,
1819
Promise,
1920
ReflectApply,
@@ -507,6 +508,35 @@ ObjectFreeze(kEnumerableProperty);
507508

508509
const kEmptyObject = ObjectFreeze(ObjectCreate(null));
509510

511+
function filterOwnProperties(source, keys) {
512+
const filtered = ObjectCreate(null);
513+
for (let i = 0; i < keys.length; i++) {
514+
const key = keys[i];
515+
if (ObjectPrototypeHasOwnProperty(source, key)) {
516+
filtered[key] = source[key];
517+
}
518+
}
519+
520+
return filtered;
521+
}
522+
523+
/**
524+
* Mimics `obj[key] = value` but ignoring potential prototype inheritance.
525+
* @param {any} obj
526+
* @param {string} key
527+
* @param {any} value
528+
* @returns {any}
529+
*/
530+
function setOwnProperty(obj, key, value) {
531+
return ObjectDefineProperty(obj, key, {
532+
__proto__: null,
533+
configurable: true,
534+
enumerable: true,
535+
value,
536+
writable: true,
537+
});
538+
}
539+
510540
module.exports = {
511541
assertCrypto,
512542
cachedResult,
@@ -519,6 +549,7 @@ module.exports = {
519549
emitExperimentalWarning,
520550
exposeInterface,
521551
filterDuplicateStrings,
552+
filterOwnProperties,
522553
getConstructorOf,
523554
getSystemErrorMap,
524555
getSystemErrorName,
@@ -549,4 +580,5 @@ module.exports = {
549580

550581
kEmptyObject,
551582
kEnumerableProperty,
583+
setOwnProperty,
552584
};
Collapse file
+2-1Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import explicit from 'explicit-main';
22
import implicit from 'implicit-main';
33
import implicitModule from 'implicit-main-type-module';
4+
import noMain from 'no-main-field';
45

56
function getImplicitCommonjs () {
67
return import('implicit-main-type-commonjs');
78
}
89

9-
export {explicit, implicit, implicitModule, getImplicitCommonjs};
10+
export {explicit, implicit, implicitModule, getImplicitCommonjs, noMain};
1011
export default 'success';
Collapse file

‎test/fixtures/es-module-specifiers/node_modules/no-main-field/index.js‎

Copy file name to clipboardExpand all lines: test/fixtures/es-module-specifiers/node_modules/no-main-field/index.js
+2Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Collapse file

‎test/fixtures/es-module-specifiers/node_modules/no-main-field/package.json‎

Copy file name to clipboardExpand all lines: test/fixtures/es-module-specifiers/node_modules/no-main-field/package.json
+1Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Collapse file
+43Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
'use strict';
2+
const common = require('../common');
3+
const fixtures = require('../common/fixtures');
4+
const assert = require('assert');
5+
6+
Object.defineProperty(Object.prototype, 'name', {
7+
__proto__: null,
8+
get: common.mustNotCall('get %Object.prototype%.name'),
9+
set: common.mustNotCall('set %Object.prototype%.name'),
10+
enumerable: false,
11+
});
12+
Object.defineProperty(Object.prototype, 'main', {
13+
__proto__: null,
14+
get: common.mustNotCall('get %Object.prototype%.main'),
15+
set: common.mustNotCall('set %Object.prototype%.main'),
16+
enumerable: false,
17+
});
18+
Object.defineProperty(Object.prototype, 'type', {
19+
__proto__: null,
20+
get: common.mustNotCall('get %Object.prototype%.type'),
21+
set: common.mustNotCall('set %Object.prototype%.type'),
22+
enumerable: false,
23+
});
24+
Object.defineProperty(Object.prototype, 'exports', {
25+
__proto__: null,
26+
get: common.mustNotCall('get %Object.prototype%.exports'),
27+
set: common.mustNotCall('set %Object.prototype%.exports'),
28+
enumerable: false,
29+
});
30+
Object.defineProperty(Object.prototype, 'imports', {
31+
__proto__: null,
32+
get: common.mustNotCall('get %Object.prototype%.imports'),
33+
set: common.mustNotCall('set %Object.prototype%.imports'),
34+
enumerable: false,
35+
});
36+
37+
assert.strictEqual(
38+
require(fixtures.path('es-module-specifiers', 'node_modules', 'no-main-field')),
39+
'no main field'
40+
);
41+
42+
import(fixtures.fileURL('es-module-specifiers', 'index.mjs'))
43+
.then(common.mustCall((module) => assert.strictEqual(module.noMain, 'no main field')));

0 commit comments

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