Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

process: make process.config read only #43627

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
5 changes: 4 additions & 1 deletion doc/api/deprecations.md
Expand Up @@ -2889,12 +2889,15 @@ Prefer [`message.socket`][] over [`message.connection`][].

<!-- YAML
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/43627
description: End-of-Life.
- version: v16.0.0
pr-url: https://github.com/nodejs/node/pull/36902
description: Runtime deprecation.
-->

Type: Runtime
Type: End-of-Life

The `process.config` property provides access to Node.js compile-time settings.
However, the property is mutable and therefore subject to tampering. The ability
Expand Down
19 changes: 7 additions & 12 deletions doc/api/process.md
Expand Up @@ -1041,17 +1041,20 @@ This feature is not available in [`Worker`][] threads.
<!-- YAML
added: v0.7.7
changes:
- version: REPLACEME
pr-url: https://github.com/nodejs/node/pull/43627
description: The `process.config` object is now frozen.
- version: v16.0.0
pr-url: https://github.com/nodejs/node/pull/36902
description: Modifying process.config has been deprecated.
-->

* {Object}

The `process.config` property returns an `Object` containing the JavaScript
representation of the configure options used to compile the current Node.js
executable. This is the same as the `config.gypi` file that was produced when
running the `./configure` script.
The `process.config` property returns a frozen `Object` containing the
JavaScript representation of the configure options used to compile the current
Node.js executable. This is the same as the `config.gypi` file that was produced
when running the `./configure` script.

An example of the possible output looks like:

Expand Down Expand Up @@ -1085,14 +1088,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`

<!-- YAML
Expand Down
75 changes: 8 additions & 67 deletions lib/internal/bootstrap/node.js
Expand Up @@ -46,10 +46,8 @@ const {
JSONParse,
ObjectDefineProperty,
ObjectGetPrototypeOf,
ObjectPreventExtensions,
ObjectSetPrototypeOf,
ReflectGet,
ReflectSet,
ObjectFreeze,
SymbolToStringTag,
globalThis,
} = primordials;
Expand All @@ -72,75 +70,18 @@ process._exiting = false;
// process.config is serialized config.gypi
const nativeModule = internalBinding('native_module');

// TODO(@jasnell): Once this has gone through one full major
// release cycle, remove the Proxy and setter and update the
// getter to either return a read-only object or always return
// a freshly parsed version of nativeModule.config.

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);
const processConfig = JSONParse(nativeModule.config, (_key, value) => {
gribnoysup marked this conversation as resolved.
Show resolved Hide resolved
// The `reviver` argument of the JSONParse method will visit all the values of
// the parsed config, including the "root" object, so there is no need to
// explicitly freeze the config outside of this method
return ObjectFreeze(value);
});
aduh95 marked this conversation as resolved.
Show resolved Hide resolved

ObjectDefineProperty(process, 'config', {
__proto__: null,
enumerable: true,
configurable: true,
get() { return processConfig; },
set(value) {
deprecationHandler.maybeWarn();
processConfig = value;
}
value: processConfig,
});

require('internal/worker/js_transferable').setup();
Expand Down
2 changes: 1 addition & 1 deletion test/abort/test-abort-fatal-error.js
Expand Up @@ -28,7 +28,7 @@ const assert = require('assert');
const exec = require('child_process').exec;

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({}) }"';

exec(cmdline, function(err, stdout, stderr) {
Expand Down
1 change: 1 addition & 0 deletions test/fixtures/overwrite-config-preload-module.js
Expand Up @@ -2,4 +2,5 @@
const common = require('../common');
common.skipIfInspectorDisabled();

delete process.config;
process.config = {};
3 changes: 3 additions & 0 deletions test/parallel/test-process-config.js
Expand Up @@ -36,6 +36,9 @@ assert(Object.hasOwn(process, 'config'));
// Ensure that `process.config` is an Object.
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');

if (!fs.existsSync(configPath)) {
Expand Down