File CVE-2023-32002.patch of Package nodejs14.32692

commit d8ccfe9ad4dce9da900cff9dd2b934dfa3600b8b
Author: RafaelGSS <rafael.nunu@hotmail.com>
Date:   Mon May 29 19:45:33 2023 -0300

    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/445
    Refs: https://hackerone.com/bugs?subject=nodejs&report_id=1960870
    Refs: https://hackerone.com/bugs?subject=nodejs&report_id=2043807
    CVE-ID: CVE-2023-32002,CVE-2023-32006

Index: node-v14.21.3/lib/internal/modules/cjs/helpers.js
===================================================================
--- node-v14.21.3.orig/lib/internal/modules/cjs/helpers.js
+++ node-v14.21.3/lib/internal/modules/cjs/helpers.js
@@ -13,6 +13,7 @@ const {
   StringPrototypeStartsWith,
 } = primordials;
 const {
+  ERR_INVALID_ARG_TYPE,
   ERR_MANIFEST_DEPENDENCY_MISSING,
   ERR_UNKNOWN_BUILTIN_MODULE
 } = require('internal/errors').codes;
@@ -55,12 +56,22 @@ function loadNativeModule(filename, requ
   }
 }
 
+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) {
Index: node-v14.21.3/lib/internal/modules/cjs/loader.js
===================================================================
--- node-v14.21.3.orig/lib/internal/modules/cjs/loader.js
+++ node-v14.21.3/lib/internal/modules/cjs/loader.js
@@ -147,8 +147,8 @@ const isWindows = process.platform === '
 const relativeResolveCache = ObjectCreate(null);
 
 let requireDepth = 0;
-let statCache = null;
 let isPreloading = false;
+let statCache = null;
 
 function internalRequire(module, id) {
   validateString(id, 'id');
@@ -1284,5 +1284,14 @@ Module.syncBuiltinESMExports = function
   }
 };
 
+ObjectDefineProperty(Module.prototype, 'constructor', {
+  __proto__: null,
+  get: function() {
+    return policy ? undefined : Module;
+  },
+  configurable: false,
+  enumerable: false,
+});
+
 // Backwards compatibility
 Module.Module = Module;
Index: node-v14.21.3/test/fixtures/policy-manifest/createRequire-bypass.js
===================================================================
--- /dev/null
+++ node-v14.21.3/test/fixtures/policy-manifest/createRequire-bypass.js
@@ -0,0 +1,2 @@
+const os = module.constructor.createRequire('file:///os-access-module.js')('os')
+os.cpus()
\ No newline at end of file
Index: node-v14.21.3/test/fixtures/policy-manifest/main-constructor-bypass.js
===================================================================
--- /dev/null
+++ node-v14.21.3/test/fixtures/policy-manifest/main-constructor-bypass.js
@@ -0,0 +1,2 @@
+const m = new require.main.constructor();
+m.require('./invalid-module')
Index: node-v14.21.3/test/fixtures/policy-manifest/main-constructor-extensions-bypass.js
===================================================================
--- /dev/null
+++ node-v14.21.3/test/fixtures/policy-manifest/main-constructor-extensions-bypass.js
@@ -0,0 +1,2 @@
+const m = new require.main.constructor();
+require.extensions['.js'](m, './invalid-module')
Index: node-v14.21.3/test/fixtures/policy-manifest/manifest-impersonate.json
===================================================================
--- /dev/null
+++ node-v14.21.3/test/fixtures/policy-manifest/manifest-impersonate.json
@@ -0,0 +1,13 @@
+{
+  "resources": {
+    "./createRequire-bypass.js": {
+      "integrity": true
+    },
+    "/os-access-module.js": {
+      "integrity": true,
+      "dependencies": {
+        "os": true
+      }
+    }
+  }
+}
\ No newline at end of file
Index: node-v14.21.3/test/fixtures/policy-manifest/module-constructor-bypass.js
===================================================================
--- /dev/null
+++ node-v14.21.3/test/fixtures/policy-manifest/module-constructor-bypass.js
@@ -0,0 +1 @@
+module.constructor._load('node:child_process');
Index: node-v14.21.3/test/parallel/test-policy-manifest.js
===================================================================
--- node-v14.21.3.orig/test/parallel/test-policy-manifest.js
+++ node-v14.21.3/test/parallel/test-policy-manifest.js
@@ -76,3 +76,58 @@ const fixtures = require('../common/fixt
   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/);
+}
openSUSE Build Service is sponsored by