Skip to content

Commit

Permalink
process: make process.config read only
Browse files Browse the repository at this point in the history
  • Loading branch information
gribnoysup committed Sep 9, 2022
1 parent 49a5e81 commit 9a4195d
Show file tree
Hide file tree
Showing 6 changed files with 24 additions and 81 deletions.
5 changes: 4 additions & 1 deletion doc/api/deprecations.md
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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) => {
// 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);
});

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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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

0 comments on commit 9a4195d

Please sign in to comment.