esm: protect ESM loader from prototype pollution

Fixes: https://github.com/nodejs/node/issues/45035
PR-URL: https://github.com/nodejs/node/pull/45044
Reviewed-By: Jacob Smith <jacob@frende.me>
Reviewed-By: Geoffrey Booth <webadmin@geoffreybooth.com>
Reviewed-By: Moshe Atlow <moshe@atlow.co.il>
Reviewed-By: Darshan Sen <raisinten@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
This commit is contained in:
Antoine du Hamel 2022-10-19 13:39:16 -05:00 committed by GitHub
parent eb298dff8d
commit f2aac0b36b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 67 additions and 6 deletions

View File

@ -53,6 +53,7 @@ const {
ObjectDefineProperty,
ObjectKeys,
ObjectPrototypeHasOwnProperty,
ObjectSetPrototypeOf,
ReflectGet,
SafeMap,
SafeSet,
@ -281,6 +282,8 @@ class BuiltinModule {
getESMFacade() {
if (this.module) return this.module;
const { ModuleWrap } = internalBinding('module_wrap');
// TODO(aduh95): move this to C++, alongside the initialization of the class.
ObjectSetPrototypeOf(ModuleWrap.prototype, null);
const url = `node:${this.id}`;
const nativeModule = this;
const exportsKeys = ArrayPrototypeSlice(this.exportKeys);

View File

@ -59,7 +59,7 @@ async function getSource(url, context) {
if (policy?.manifest) {
policy.manifest.assertIntegrity(parsed, source);
}
return { responseURL, source };
return { __proto__: null, responseURL, source };
}
@ -93,6 +93,7 @@ async function defaultLoad(url, context) {
}
return {
__proto__: null,
format,
responseURL,
source,

View File

@ -664,6 +664,7 @@ class ESMLoader {
}
return {
__proto__: null,
format,
responseURL,
source,
@ -880,6 +881,7 @@ class ESMLoader {
}
return {
__proto__: null,
format,
url,
};

View File

@ -215,7 +215,7 @@ class ModuleJob {
}
throw e;
}
return { module: this.module };
return { __proto__: null, module: this.module };
}
}
ObjectSetPrototypeOf(ModuleJob.prototype, null);

View File

@ -1017,7 +1017,7 @@ async function defaultResolve(specifier, context = {}) {
)
)
) {
return { url: parsed.href };
return { __proto__: null, url: parsed.href };
}
} catch {
// Ignore exception

View File

@ -0,0 +1,17 @@
'use strict';
const { mustNotCall, mustCall } = require('../common');
Object.defineProperties(Array.prototype, {
// %Promise.all% and %Promise.allSettled% are depending on the value of
// `%Array.prototype%.then`.
then: {},
});
Object.defineProperties(Object.prototype, {
then: {
set: mustNotCall('set %Object.prototype%.then'),
get: mustNotCall('get %Object.prototype%.then'),
},
});
import('data:text/javascript,').then(mustCall());

View File

@ -0,0 +1,15 @@
import { mustNotCall, mustCall } from '../common/index.mjs';
Object.defineProperties(Array.prototype, {
// %Promise.all% and %Promise.allSettled% are depending on the value of
// `%Array.prototype%.then`.
then: {},
});
Object.defineProperties(Object.prototype, {
then: {
set: mustNotCall('set %Object.prototype%.then'),
get: mustNotCall('get %Object.prototype%.then'),
},
});
import('data:text/javascript,').then(mustCall());

View File

@ -18,9 +18,32 @@ Promise.all = common.mustNotCall('%Promise%.all');
Promise.allSettled = common.mustNotCall('%Promise%.allSettled');
Promise.any = common.mustNotCall('%Promise%.any');
Promise.race = common.mustNotCall('%Promise%.race');
Promise.prototype.catch = common.mustNotCall('%Promise.prototype%.catch');
Promise.prototype.finally = common.mustNotCall('%Promise.prototype%.finally');
Promise.prototype.then = common.mustNotCall('%Promise.prototype%.then');
Object.defineProperties(Promise.prototype, {
catch: {
set: common.mustNotCall('set %Promise.prototype%.catch'),
get: common.mustNotCall('get %Promise.prototype%.catch'),
},
finally: {
set: common.mustNotCall('set %Promise.prototype%.finally'),
get: common.mustNotCall('get %Promise.prototype%.finally'),
},
then: {
set: common.mustNotCall('set %Promise.prototype%.then'),
get: common.mustNotCall('get %Promise.prototype%.then'),
},
});
Object.defineProperties(Array.prototype, {
// %Promise.all% and %Promise.allSettled% are depending on the value of
// `%Array.prototype%.then`.
then: {},
});
Object.defineProperties(Object.prototype, {
then: {
set: common.mustNotCall('set %Object.prototype%.then'),
get: common.mustNotCall('get %Object.prototype%.then'),
},
});
assertIsPromise(PromisePrototypeThen(test(), common.mustCall()));
assertIsPromise(SafePromisePrototypeFinally(test(), common.mustCall()));