process: make process.config read only

Fixes: https://github.com/nodejs/node/issues/43581
PR-URL: https://github.com/nodejs/node/pull/43627
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Tobias Nießen <tniessen@tnie.de>
This commit is contained in:
Sergey Petushkov 2022-09-11 00:00:23 +02:00 committed by GitHub
parent 22b9a0c4ba
commit e0ab8dd637
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 24 additions and 81 deletions

View File

@ -2889,12 +2889,15 @@ Prefer [`message.socket`][] over [`message.connection`][].
<!-- YAML <!-- YAML
changes: changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/43627
description: End-of-Life.
- version: v16.0.0 - version: v16.0.0
pr-url: https://github.com/nodejs/node/pull/36902 pr-url: https://github.com/nodejs/node/pull/36902
description: Runtime deprecation. description: Runtime deprecation.
--> -->
Type: Runtime Type: End-of-Life
The `process.config` property provides access to Node.js compile-time settings. The `process.config` property provides access to Node.js compile-time settings.
However, the property is mutable and therefore subject to tampering. The ability However, the property is mutable and therefore subject to tampering. The ability

View File

@ -1041,6 +1041,9 @@ This feature is not available in [`Worker`][] threads.
<!-- YAML <!-- YAML
added: v0.7.7 added: v0.7.7
changes: changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/43627
description: The `process.config` object is now frozen.
- version: v16.0.0 - version: v16.0.0
pr-url: https://github.com/nodejs/node/pull/36902 pr-url: https://github.com/nodejs/node/pull/36902
description: Modifying process.config has been deprecated. description: Modifying process.config has been deprecated.
@ -1048,10 +1051,10 @@ changes:
* {Object} * {Object}
The `process.config` property returns an `Object` containing the JavaScript The `process.config` property returns a frozen `Object` containing the
representation of the configure options used to compile the current Node.js JavaScript representation of the configure options used to compile the current
executable. This is the same as the `config.gypi` file that was produced when Node.js executable. This is the same as the `config.gypi` file that was produced
running the `./configure` script. when running the `./configure` script.
An example of the possible output looks like: An example of the possible output looks like:
@ -1084,14 +1087,6 @@ An example of the possible output looks like:
} }
``` ```
The `process.config` property is **not** read-only and there are existing
modules in the ecosystem that are known to extend, modify, or entirely replace
the value of `process.config`.
Modifying the `process.config` property, or any child-property of the
`process.config` object has been deprecated. The `process.config` will be made
read-only in a future release.
## `process.connected` ## `process.connected`
<!-- YAML <!-- YAML

View File

@ -46,10 +46,8 @@ const {
JSONParse, JSONParse,
ObjectDefineProperty, ObjectDefineProperty,
ObjectGetPrototypeOf, ObjectGetPrototypeOf,
ObjectPreventExtensions,
ObjectSetPrototypeOf, ObjectSetPrototypeOf,
ReflectGet, ObjectFreeze,
ReflectSet,
SymbolToStringTag, SymbolToStringTag,
globalThis, globalThis,
} = primordials; } = primordials;
@ -91,75 +89,18 @@ process._exiting = false;
// process.config is serialized config.gypi // process.config is serialized config.gypi
const nativeModule = internalBinding('builtins'); const nativeModule = internalBinding('builtins');
// TODO(@jasnell): Once this has gone through one full major const processConfig = JSONParse(nativeModule.config, (_key, value) => {
// release cycle, remove the Proxy and setter and update the // The `reviver` argument of the JSONParse method will visit all the values of
// getter to either return a read-only object or always return // the parsed config, including the "root" object, so there is no need to
// a freshly parsed version of nativeModule.config. // explicitly freeze the config outside of this method
return ObjectFreeze(value);
const deprecationHandler = { });
warned: false,
message: 'Setting process.config is deprecated. ' +
'In the future the property will be read-only.',
code: 'DEP0150',
maybeWarn() {
if (!this.warned) {
process.emitWarning(this.message, {
type: 'DeprecationWarning',
code: this.code
});
this.warned = true;
}
},
defineProperty(target, key, descriptor) {
this.maybeWarn();
return ObjectDefineProperty(target, key, descriptor);
},
deleteProperty(target, key) {
this.maybeWarn();
delete target[key];
},
preventExtensions(target) {
this.maybeWarn();
return ObjectPreventExtensions(target);
},
set(target, key, value) {
this.maybeWarn();
return ReflectSet(target, key, value);
},
get(target, key, receiver) {
const val = ReflectGet(target, key, receiver);
if (val != null && typeof val === 'object') {
// eslint-disable-next-line node-core/prefer-primordials
return new Proxy(val, deprecationHandler);
}
return val;
},
setPrototypeOf(target, proto) {
this.maybeWarn();
return ObjectSetPrototypeOf(target, proto);
}
};
// eslint-disable-next-line node-core/prefer-primordials
let processConfig = new Proxy(
JSONParse(nativeModule.config),
deprecationHandler);
ObjectDefineProperty(process, 'config', { ObjectDefineProperty(process, 'config', {
__proto__: null, __proto__: null,
enumerable: true, enumerable: true,
configurable: true, configurable: true,
get() { return processConfig; }, value: processConfig,
set(value) {
deprecationHandler.maybeWarn();
processConfig = value;
}
}); });
require('internal/worker/js_transferable').setup(); require('internal/worker/js_transferable').setup();

View File

@ -28,7 +28,7 @@ const assert = require('assert');
const exec = require('child_process').exec; const exec = require('child_process').exec;
let cmdline = `ulimit -c 0; ${process.execPath}`; let cmdline = `ulimit -c 0; ${process.execPath}`;
cmdline += ' --max-old-space-size=4 --max-semi-space-size=1'; cmdline += ' --max-old-space-size=16 --max-semi-space-size=4';
cmdline += ' -e "a = []; for (i = 0; i < 1e9; i++) { a.push({}) }"'; cmdline += ' -e "a = []; for (i = 0; i < 1e9; i++) { a.push({}) }"';
exec(cmdline, function(err, stdout, stderr) { exec(cmdline, function(err, stdout, stderr) {

View File

@ -2,4 +2,5 @@
const common = require('../common'); const common = require('../common');
common.skipIfInspectorDisabled(); common.skipIfInspectorDisabled();
delete process.config;
process.config = {}; process.config = {};

View File

@ -36,6 +36,9 @@ assert(Object.hasOwn(process, 'config'));
// Ensure that `process.config` is an Object. // Ensure that `process.config` is an Object.
assert.strictEqual(Object(process.config), process.config); assert.strictEqual(Object(process.config), process.config);
// Ensure that you can't change config values
assert.throws(() => { process.config.variables = 42; }, TypeError);
const configPath = path.resolve(__dirname, '..', '..', 'config.gypi'); const configPath = path.resolve(__dirname, '..', '..', 'config.gypi');
if (!fs.existsSync(configPath)) { if (!fs.existsSync(configPath)) {