Skip to content

Commit e65bb10

Browse files
committed
process: disallow some uses of Object.defineProperty() on process.env
Disallow the use of Object.defineProperty() to hide entries in process.env or make them immutable.
1 parent 6b7b0b7 commit e65bb10

6 files changed

Lines changed: 130 additions & 3 deletions

File tree

doc/api/errors.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1919,6 +1919,12 @@ valid.
19191919
The imported module string is an invalid URL, package name, or package subpath
19201920
specifier.
19211921

1922+
<a id="ERR_INVALID_OBJECT_DEFINE_PROPERTY"></a>
1923+
### ERR_INVALID_OBJECT_DEFINE_PROPERTY
1924+
1925+
An error occurred while setting an invalid attribute on the property of
1926+
an object.
1927+
19221928
<a id="ERR_INVALID_PACKAGE_CONFIG"></a>
19231929

19241930
### `ERR_INVALID_PACKAGE_CONFIG`

src/node_env_var.cc

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ using v8::Nothing;
2828
using v8::Object;
2929
using v8::ObjectTemplate;
3030
using v8::PropertyCallbackInfo;
31+
using v8::PropertyDescriptor;
3132
using v8::PropertyHandlerFlags;
3233
using v8::ReadOnly;
3334
using v8::String;
@@ -396,11 +397,39 @@ static void EnvEnumerator(const PropertyCallbackInfo<Array>& info) {
396397
env->env_vars()->Enumerate(env->isolate()));
397398
}
398399

400+
static void EnvDefiner(Local<Name> property,
401+
const PropertyDescriptor& desc,
402+
const PropertyCallbackInfo<Value>& info) {
403+
Environment* env = Environment::GetCurrent(info);
404+
if (desc.has_value() && !desc.configurable() && !desc.enumerable() &&
405+
!desc.writable()) {
406+
THROW_ERR_INVALID_OBJECT_DEFINE_PROPERTY(env,
407+
"Must set all attributes with true to 'value'"
408+
" in 'process.env'");
409+
} else if (desc.has_get() || desc.has_set() ||
410+
(desc.has_configurable() && !desc.configurable()) ||
411+
(desc.has_enumerable() && !desc.enumerable()) ||
412+
(desc.has_writable() && !desc.writable())) {
413+
THROW_ERR_INVALID_OBJECT_DEFINE_PROPERTY(env,
414+
"Cannot set attributes other than 'value'"
415+
" for properties in 'process.env'");
416+
} else {
417+
EnvSetter(property, desc.value(), info);
418+
}
419+
}
420+
399421
MaybeLocal<Object> CreateEnvVarProxy(Local<Context> context, Isolate* isolate) {
400422
EscapableHandleScope scope(isolate);
401423
Local<ObjectTemplate> env_proxy_template = ObjectTemplate::New(isolate);
402424
env_proxy_template->SetHandler(NamedPropertyHandlerConfiguration(
403-
EnvGetter, EnvSetter, EnvQuery, EnvDeleter, EnvEnumerator, Local<Value>(),
425+
EnvGetter,
426+
EnvSetter,
427+
EnvQuery,
428+
EnvDeleter,
429+
EnvEnumerator,
430+
EnvDefiner,
431+
nullptr,
432+
Local<Value>(),
404433
PropertyHandlerFlags::kHasNoSideEffect));
405434
return scope.EscapeMaybe(env_proxy_template->NewInstance(context));
406435
}
@@ -411,6 +440,7 @@ void RegisterEnvVarExternalReferences(ExternalReferenceRegistry* registry) {
411440
registry->Register(EnvQuery);
412441
registry->Register(EnvDeleter);
413442
registry->Register(EnvEnumerator);
443+
registry->Register(EnvDefiner);
414444
}
415445
} // namespace node
416446

src/node_errors.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ void OnFatalError(const char* location, const char* message);
6464
V(ERR_INVALID_ARG_VALUE, TypeError) \
6565
V(ERR_OSSL_EVP_INVALID_DIGEST, Error) \
6666
V(ERR_INVALID_ARG_TYPE, TypeError) \
67+
V(ERR_INVALID_OBJECT_DEFINE_PROPERTY, TypeError) \
6768
V(ERR_INVALID_MODULE, Error) \
6869
V(ERR_INVALID_THIS, TypeError) \
6970
V(ERR_INVALID_TRANSFER_OBJECT, TypeError) \
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
'use strict';
2+
require('../common');
3+
const assert = require('assert');
4+
5+
process.env.foo = 'foo';
6+
assert.strictEqual(process.env.foo, 'foo');
7+
process.env.foo = undefined;
8+
assert.strictEqual(process.env.foo, 'undefined');
9+
10+
process.env.foo = 'foo';
11+
assert.strictEqual(process.env.foo, 'foo');
12+
delete process.env.foo;
13+
assert.strictEqual(process.env.foo, undefined);
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
'use strict';
2+
require('../common');
3+
const assert = require('assert');
4+
5+
assert.throws(
6+
() => {
7+
Object.defineProperty(process.env, 'foo', {
8+
value: 'foo1'
9+
});
10+
},
11+
{
12+
code: 'ERR_INVALID_OBJECT_DEFINE_PROPERTY',
13+
name: 'TypeError',
14+
message: 'Must set all attributes with true to \'value\' ' +
15+
'in \'process.env\''
16+
}
17+
);
18+
19+
assert.strictEqual(process.env.foo, undefined);
20+
process.env.foo = 'foo2';
21+
assert.strictEqual(process.env.foo, 'foo2');
22+
23+
assert.throws(
24+
() => {
25+
Object.defineProperty(process.env, 'goo', {
26+
get() {
27+
return 'goo';
28+
},
29+
set() {}
30+
});
31+
},
32+
{
33+
code: 'ERR_INVALID_OBJECT_DEFINE_PROPERTY',
34+
name: 'TypeError',
35+
message: 'Cannot set attributes other than \'value\' ' +
36+
'for properties in \'process.env\''
37+
}
38+
);
39+
40+
const attributes = ['configurable', 'writable', 'enumerable'];
41+
42+
attributes.forEach((attribute) => {
43+
assert.throws(
44+
() => {
45+
Object.defineProperty(process.env, 'goo', {
46+
[attribute]: false
47+
});
48+
},
49+
{
50+
code: 'ERR_INVALID_OBJECT_DEFINE_PROPERTY',
51+
name: 'TypeError',
52+
message: 'Cannot set attributes other than \'value\' ' +
53+
'for properties in \'process.env\''
54+
}
55+
);
56+
});
57+
58+
assert.strictEqual(process.env.goo, undefined);
59+
Object.defineProperty(process.env, 'goo', {
60+
value: 'goo',
61+
configurable: true,
62+
writable: true,
63+
enumerable: true
64+
});
65+
assert.strictEqual(process.env.goo, 'goo');

test/parallel/test-worker-process-env.js

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,20 @@ if (!workerData && process.argv[2] !== 'child') {
3939
process.env.SET_IN_WORKER = 'set';
4040
assert.strictEqual(process.env.SET_IN_WORKER, 'set');
4141

42-
Object.defineProperty(process.env, 'DEFINED_IN_WORKER', { value: 42 });
43-
assert.strictEqual(process.env.DEFINED_IN_WORKER, '42');
42+
assert.throws(
43+
() => {
44+
Object.defineProperty(process.env, 'DEFINED_IN_WORKER', {
45+
value: 42
46+
});
47+
},
48+
{
49+
code: 'ERR_INVALID_OBJECT_DEFINE_PROPERTY',
50+
name: 'TypeError',
51+
message: 'Must set all attributes with true to \'value\' ' +
52+
'in \'process.env\''
53+
}
54+
);
55+
4456

4557
const { stderr } =
4658
child_process.spawnSync(process.execPath, [__filename, 'child']);

0 commit comments

Comments
 (0)