GeezerVison is showing github.com — esm: protect ESM loader from prototype pollution · nodejs/node@46ded6b
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: lib/internal/bootstrap/loaders.js +3Lines changed: 3 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -53,6 +53,7 @@ const { | 53 | 53 | ObjectDefineProperty, | 54 | 54 | ObjectKeys, | 55 | 55 | ObjectPrototypeHasOwnProperty, | | 56 | + ObjectSetPrototypeOf, | 56 | 57 | ReflectGet, | 57 | 58 | SafeMap, | 58 | 59 | SafeSet, | @@ -281,6 +282,8 @@ class BuiltinModule { | 281 | 282 | getESMFacade() { | 282 | 283 | if (this.module) return this.module; | 283 | 284 | const { ModuleWrap } = internalBinding('module_wrap'); | | 285 | + // TODO(aduh95): move this to C++, alongside the initialization of the class. | | 286 | + ObjectSetPrototypeOf(ModuleWrap.prototype, null); | 284 | 287 | const url = `node:${this.id}`; | 285 | 288 | const nativeModule = this; | 286 | 289 | const exportsKeys = ArrayPrototypeSlice(this.exportKeys); | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/modules/esm/load.js +2-1Lines changed: 2 additions & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -59,7 +59,7 @@ async function getSource(url, context) { | 59 | 59 | if (policy?.manifest) { | 60 | 60 | policy.manifest.assertIntegrity(parsed, source); | 61 | 61 | } | 62 | | - return { responseURL, source }; | | 62 | + return { __proto__: null, responseURL, source }; | 63 | 63 | } | 64 | 64 |
| 65 | 65 |
| @@ -93,6 +93,7 @@ async function defaultLoad(url, context) { | 93 | 93 | } | 94 | 94 |
| 95 | 95 | return { | | 96 | + __proto__: null, | 96 | 97 | format, | 97 | 98 | responseURL, | 98 | 99 | source, | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/modules/esm/loader.js +2Lines changed: 2 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -676,6 +676,7 @@ class ESMLoader { | 676 | 676 | } | 677 | 677 |
| 678 | 678 | return { | | 679 | + __proto__: null, | 679 | 680 | format, | 680 | 681 | responseURL, | 681 | 682 | source, | @@ -892,6 +893,7 @@ class ESMLoader { | 892 | 893 | } | 893 | 894 |
| 894 | 895 | return { | | 896 | + __proto__: null, | 895 | 897 | format, | 896 | 898 | url, | 897 | 899 | }; | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/modules/esm/module_job.js +1-1Lines changed: 1 addition & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -215,7 +215,7 @@ class ModuleJob { | 215 | 215 | } | 216 | 216 | throw e; | 217 | 217 | } | 218 | | - return { module: this.module }; | | 218 | + return { __proto__: null, module: this.module }; | 219 | 219 | } | 220 | 220 | } | 221 | 221 | ObjectSetPrototypeOf(ModuleJob.prototype, null); | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/modules/esm/resolve.js +1-1Lines changed: 1 addition & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -1083,7 +1083,7 @@ async function defaultResolve(specifier, context = {}) { | 1083 | 1083 | ) | 1084 | 1084 | ) | 1085 | 1085 | ) { | 1086 | | - return { url: parsed.href }; | | 1086 | + return { __proto__: null, url: parsed.href }; | 1087 | 1087 | } | 1088 | 1088 | } catch { | 1089 | 1089 | // Ignore exception | |
Collapse file Copy file name to clipboard +17Lines changed: 17 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| | 1 | +'use strict'; | | 2 | + | | 3 | +const { mustNotCall, mustCall } = require('../common'); | | 4 | + | | 5 | +Object.defineProperties(Array.prototype, { | | 6 | + // %Promise.all% and %Promise.allSettled% are depending on the value of | | 7 | + // `%Array.prototype%.then`. | | 8 | + then: {}, | | 9 | +}); | | 10 | +Object.defineProperties(Object.prototype, { | | 11 | + then: { | | 12 | + set: mustNotCall('set %Object.prototype%.then'), | | 13 | + get: mustNotCall('get %Object.prototype%.then'), | | 14 | + }, | | 15 | +}); | | 16 | + | | 17 | +import('data:text/javascript,').then(mustCall()); |
Collapse file Copy file name to clipboard +15Lines changed: 15 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| | 1 | +import { mustNotCall, mustCall } from '../common/index.mjs'; | | 2 | + | | 3 | +Object.defineProperties(Array.prototype, { | | 4 | + // %Promise.all% and %Promise.allSettled% are depending on the value of | | 5 | + // `%Array.prototype%.then`. | | 6 | + then: {}, | | 7 | +}); | | 8 | +Object.defineProperties(Object.prototype, { | | 9 | + then: { | | 10 | + set: mustNotCall('set %Object.prototype%.then'), | | 11 | + get: mustNotCall('get %Object.prototype%.then'), | | 12 | + }, | | 13 | +}); | | 14 | + | | 15 | +import('data:text/javascript,').then(mustCall()); |
Collapse file Copy file name to clipboardExpand all lines: test/parallel/test-primordials-promise.js +26-3Lines changed: 26 additions & 3 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -18,9 +18,32 @@ Promise.all = common.mustNotCall('%Promise%.all'); | 18 | 18 | Promise.allSettled = common.mustNotCall('%Promise%.allSettled'); | 19 | 19 | Promise.any = common.mustNotCall('%Promise%.any'); | 20 | 20 | Promise.race = common.mustNotCall('%Promise%.race'); | 21 | | -Promise.prototype.catch = common.mustNotCall('%Promise.prototype%.catch'); | 22 | | -Promise.prototype.finally = common.mustNotCall('%Promise.prototype%.finally'); | 23 | | -Promise.prototype.then = common.mustNotCall('%Promise.prototype%.then'); | | 21 | + | | 22 | +Object.defineProperties(Promise.prototype, { | | 23 | + catch: { | | 24 | + set: common.mustNotCall('set %Promise.prototype%.catch'), | | 25 | + get: common.mustNotCall('get %Promise.prototype%.catch'), | | 26 | + }, | | 27 | + finally: { | | 28 | + set: common.mustNotCall('set %Promise.prototype%.finally'), | | 29 | + get: common.mustNotCall('get %Promise.prototype%.finally'), | | 30 | + }, | | 31 | + then: { | | 32 | + set: common.mustNotCall('set %Promise.prototype%.then'), | | 33 | + get: common.mustNotCall('get %Promise.prototype%.then'), | | 34 | + }, | | 35 | +}); | | 36 | +Object.defineProperties(Array.prototype, { | | 37 | + // %Promise.all% and %Promise.allSettled% are depending on the value of | | 38 | + // `%Array.prototype%.then`. | | 39 | + then: {}, | | 40 | +}); | | 41 | +Object.defineProperties(Object.prototype, { | | 42 | + then: { | | 43 | + set: common.mustNotCall('set %Object.prototype%.then'), | | 44 | + get: common.mustNotCall('get %Object.prototype%.then'), | | 45 | + }, | | 46 | +}); | 24 | 47 |
| 25 | 48 | assertIsPromise(PromisePrototypeThen(test(), common.mustCall())); | 26 | 49 | assertIsPromise(SafePromisePrototypeFinally(test(), common.mustCall())); | |
|