policy: disable process.binding() when enabled

process.binding() can be used to trivially bypass restrictions imposed
through a policy. Since the function is deprecated already, simply
replace it with a stub when a policy is being enabled.

Fixes: https://hackerone.com/bugs?report_id=1946470
PR-URL: https://github.com/nodejs-private/node-private/pull/397
Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com>
CVE-ID: CVE-2023-32559
This commit is contained in:
Tobias Nießen 2023-04-16 22:26:47 +02:00 committed by RafaelGSS
parent 4fdf70f9ab
commit 4aa0eff787
6 changed files with 65 additions and 0 deletions

View file

@ -2215,6 +2215,9 @@ Type: Documentation-only (supports [`--pending-deprecation`][])
`process.binding()` is for use by Node.js internal code only. `process.binding()` is for use by Node.js internal code only.
While `process.binding()` has not reached End-of-Life status in general, it is
unavailable when [policies][] are enabled.
### DEP0112: `dgram` private APIs ### DEP0112: `dgram` private APIs
<!-- YAML <!-- YAML
@ -3532,6 +3535,7 @@ Consider using alternatives such as the [`mock`][] helper function.
[from_string_encoding]: buffer.md#static-method-bufferfromstring-encoding [from_string_encoding]: buffer.md#static-method-bufferfromstring-encoding
[legacy URL API]: url.md#legacy-url-api [legacy URL API]: url.md#legacy-url-api
[legacy `urlObject`]: url.md#legacy-urlobject [legacy `urlObject`]: url.md#legacy-urlobject
[policies]: permissions.md#policies
[static methods of `crypto.Certificate()`]: crypto.md#class-certificate [static methods of `crypto.Certificate()`]: crypto.md#class-certificate
[subpath exports]: packages.md#subpath-exports [subpath exports]: packages.md#subpath-exports
[subpath imports]: packages.md#subpath-imports [subpath imports]: packages.md#subpath-imports

View file

@ -957,6 +957,9 @@ module.exports = {
// //
// Note: Node.js specific errors must begin with the prefix ERR_ // Note: Node.js specific errors must begin with the prefix ERR_
E('ERR_ACCESS_DENIED',
'Access to this API has been restricted. Permission: %s',
Error);
E('ERR_AMBIGUOUS_ARGUMENT', 'The "%s" argument is ambiguous. %s', TypeError); E('ERR_AMBIGUOUS_ARGUMENT', 'The "%s" argument is ambiguous. %s', TypeError);
E('ERR_ARG_NOT_ITERABLE', '%s must be iterable', TypeError); E('ERR_ARG_NOT_ITERABLE', '%s must be iterable', TypeError);
E('ERR_ASSERTION', '%s', Error); E('ERR_ASSERTION', '%s', Error);

View file

@ -7,6 +7,7 @@ const {
} = primordials; } = primordials;
const { const {
ERR_ACCESS_DENIED,
ERR_MANIFEST_TDZ, ERR_MANIFEST_TDZ,
} = require('internal/errors').codes; } = require('internal/errors').codes;
const { Manifest } = require('internal/policy/manifest'); const { Manifest } = require('internal/policy/manifest');
@ -32,6 +33,15 @@ module.exports = ObjectFreeze({
return o; return o;
}); });
manifest = new Manifest(json, url); manifest = new Manifest(json, url);
// process.binding() is deprecated (DEP0111) and trivially allows bypassing
// policies, so if policies are enabled, make this API unavailable.
process.binding = function binding(_module) {
throw new ERR_ACCESS_DENIED('process.binding');
};
process._linkedBinding = function _linkedBinding(_module) {
throw new ERR_ACCESS_DENIED('process._linkedBinding');
};
}, },
get manifest() { get manifest() {

View file

@ -0,0 +1,10 @@
'use strict';
const assert = require('assert');
assert.throws(() => { process.binding(); }, {
code: 'ERR_ACCESS_DENIED'
});
assert.throws(() => { process._linkedBinding(); }, {
code: 'ERR_ACCESS_DENIED'
});

View file

@ -0,0 +1,10 @@
{
"resources": {
"./app.js": {
"integrity": true,
"dependencies": {
"assert": true
}
}
}
}

View file

@ -0,0 +1,28 @@
'use strict';
const common = require('../common');
common.requireNoPackageJSONAbove();
if (!common.hasCrypto)
common.skip('missing crypto');
const fixtures = require('../common/fixtures');
const assert = require('node:assert');
const { spawnSync } = require('node:child_process');
const dep = fixtures.path('policy', 'process-binding', 'app.js');
const depPolicy = fixtures.path(
'policy',
'process-binding',
'policy.json');
const { status } = spawnSync(
process.execPath,
[
'--experimental-policy', depPolicy, dep,
],
{
stdio: 'inherit'
},
);
assert.strictEqual(status, 0);