mirror of https://github.com/nodejs/node.git
policy: handle Module.constructor and main.extensions bypass
Signed-off-by: RafaelGSS <rafael.nunu@hotmail.com> PR-URL: https://github.com/nodejs-private/node-private/pull/417 Refs: https://hackerone.com/bugs?subject=nodejs&report_id=1960870 Refs: https://hackerone.com/bugs?subject=nodejs&report_id=2043807 Reviewed-By: Geoffrey Booth <webadmin@geoffreybooth.com> CVE-ID: CVE-2023-32002,CVE-2023-32006
This commit is contained in:
parent
3868ae0f4f
commit
b68e5e7981
|
@ -152,8 +152,8 @@ const isWindows = process.platform === 'win32';
|
|||
const relativeResolveCache = { __proto__: null };
|
||||
|
||||
let requireDepth = 0;
|
||||
let statCache = null;
|
||||
let isPreloading = false;
|
||||
let statCache = null;
|
||||
|
||||
function internalRequire(module, id) {
|
||||
validateString(id, 'id');
|
||||
|
@ -1429,5 +1429,14 @@ Module.syncBuiltinESMExports = function syncBuiltinESMExports() {
|
|||
}
|
||||
};
|
||||
|
||||
ObjectDefineProperty(Module.prototype, 'constructor', {
|
||||
__proto__: null,
|
||||
get: function() {
|
||||
return policy() ? undefined : Module;
|
||||
},
|
||||
configurable: false,
|
||||
enumerable: false,
|
||||
});
|
||||
|
||||
// Backwards compatibility
|
||||
Module.Module = Module;
|
||||
|
|
|
@ -14,6 +14,7 @@ const {
|
|||
StringPrototypeStartsWith,
|
||||
} = primordials;
|
||||
const {
|
||||
ERR_INVALID_ARG_TYPE,
|
||||
ERR_MANIFEST_DEPENDENCY_MISSING,
|
||||
ERR_UNKNOWN_BUILTIN_MODULE,
|
||||
} = require('internal/errors').codes;
|
||||
|
@ -68,12 +69,22 @@ function loadBuiltinModule(filename, request) {
|
|||
return mod;
|
||||
}
|
||||
|
||||
let $Module = null;
|
||||
function lazyModule() {
|
||||
$Module = $Module || require('internal/modules/cjs/loader').Module;
|
||||
return $Module;
|
||||
}
|
||||
|
||||
// Invoke with makeRequireFunction(module) where |module| is the Module object
|
||||
// to use as the context for the require() function.
|
||||
// Use redirects to set up a mapping from a policy and restrict dependencies
|
||||
const urlToFileCache = new SafeMap();
|
||||
function makeRequireFunction(mod, redirects) {
|
||||
const Module = mod.constructor;
|
||||
// lazy due to cycle
|
||||
const Module = lazyModule();
|
||||
if (mod instanceof Module !== true) {
|
||||
throw new ERR_INVALID_ARG_TYPE('mod', 'Module', mod);
|
||||
}
|
||||
|
||||
let require;
|
||||
if (redirects) {
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
const os = module.constructor.createRequire('file:///os-access-module.js')('os')
|
||||
os.cpus()
|
|
@ -0,0 +1,2 @@
|
|||
const m = new require.main.constructor();
|
||||
m.require('./invalid-module')
|
|
@ -0,0 +1,2 @@
|
|||
const m = new require.main.constructor();
|
||||
require.extensions['.js'](m, './invalid-module')
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"resources": {
|
||||
"./createRequire-bypass.js": {
|
||||
"integrity": true
|
||||
},
|
||||
"/os-access-module.js": {
|
||||
"integrity": true,
|
||||
"dependencies": {
|
||||
"os": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
module.constructor._load('node:child_process');
|
|
@ -81,3 +81,58 @@ const fixtures = require('../common/fixtures.js');
|
|||
assert.match(stderr, /ERR_MANIFEST_DEPENDENCY_MISSING/);
|
||||
assert.match(stderr, /does not list os as a dependency specifier for conditions: require, node, node-addons/);
|
||||
}
|
||||
|
||||
{
|
||||
const policyFilepath = fixtures.path('policy-manifest', 'onerror-exit.json');
|
||||
const mainModuleBypass = fixtures.path('policy-manifest', 'module-constructor-bypass.js');
|
||||
const result = spawnSync(process.execPath, [
|
||||
'--experimental-policy',
|
||||
policyFilepath,
|
||||
mainModuleBypass,
|
||||
]);
|
||||
assert.notStrictEqual(result.status, 0);
|
||||
const stderr = result.stderr.toString();
|
||||
assert.match(stderr, /TypeError/);
|
||||
}
|
||||
|
||||
{
|
||||
const policyFilepath = fixtures.path('policy-manifest', 'manifest-impersonate.json');
|
||||
const createRequireBypass = fixtures.path('policy-manifest', 'createRequire-bypass.js');
|
||||
const result = spawnSync(process.execPath, [
|
||||
'--experimental-policy',
|
||||
policyFilepath,
|
||||
createRequireBypass,
|
||||
]);
|
||||
|
||||
assert.notStrictEqual(result.status, 0);
|
||||
const stderr = result.stderr.toString();
|
||||
assert.match(stderr, /TypeError/);
|
||||
}
|
||||
|
||||
{
|
||||
const policyFilepath = fixtures.path('policy-manifest', 'onerror-exit.json');
|
||||
const mainModuleBypass = fixtures.path('policy-manifest', 'main-constructor-bypass.js');
|
||||
const result = spawnSync(process.execPath, [
|
||||
'--experimental-policy',
|
||||
policyFilepath,
|
||||
mainModuleBypass,
|
||||
]);
|
||||
|
||||
assert.notStrictEqual(result.status, 0);
|
||||
const stderr = result.stderr.toString();
|
||||
assert.match(stderr, /TypeError/);
|
||||
}
|
||||
|
||||
{
|
||||
const policyFilepath = fixtures.path('policy-manifest', 'onerror-exit.json');
|
||||
const mainModuleBypass = fixtures.path('policy-manifest', 'main-constructor-extensions-bypass.js');
|
||||
const result = spawnSync(process.execPath, [
|
||||
'--experimental-policy',
|
||||
policyFilepath,
|
||||
mainModuleBypass,
|
||||
]);
|
||||
|
||||
assert.notStrictEqual(result.status, 0);
|
||||
const stderr = result.stderr.toString();
|
||||
assert.match(stderr, /TypeError/);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue