Skip to content

Commit 6960cf5

Browse files
committed
feat: add --allow-env option for environment variable permissions
Signed-off-by: nabeel378 <[email protected]>
1 parent 58a8e1d commit 6960cf5

14 files changed

Lines changed: 283 additions & 3 deletions

lib/internal/process/permission.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ module.exports = ObjectFreeze({
4646
'--allow-fs-write',
4747
'--allow-addons',
4848
'--allow-child-process',
49+
'--allow-env',
4950
'--allow-net',
5051
'--allow-inspector',
5152
'--allow-wasi',

node.gyp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@
173173
'src/path.cc',
174174
'src/permission/child_process_permission.cc',
175175
'src/permission/ffi_permission.cc',
176+
'src/permission/env_var_permission.cc',
176177
'src/permission/fs_permission.cc',
177178
'src/permission/inspector_permission.cc',
178179
'src/permission/permission.cc',
@@ -309,6 +310,7 @@
309310
'src/path.h',
310311
'src/permission/child_process_permission.h',
311312
'src/permission/ffi_permission.h',
313+
'src/permission/env_var_permission.h',
312314
'src/permission/fs_permission.h',
313315
'src/permission/inspector_permission.h',
314316
'src/permission/permission.h',

src/env.cc

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -949,6 +949,13 @@ Environment::Environment(IsolateData* isolate_data,
949949
permission()->Apply(this, {"*"}, permission::PermissionScope::kWASI);
950950
}
951951

952+
if (!options_->allow_env.empty()) {
953+
permission()->Apply(
954+
this, options_->allow_env, permission::PermissionScope::kEnvVar);
955+
} else {
956+
permission()->Apply(this, {}, permission::PermissionScope::kEnvVar);
957+
}
958+
952959
// Implicit allow entrypoint to kFileSystemRead
953960
if (!options_->has_eval_string && !options_->force_repl) {
954961
std::string first_argv;

src/node_env_var.cc

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
#include "node_external_reference.h"
55
#include "node_i18n.h"
66
#include "node_process-inl.h"
7+
#include "permission/permission.h"
78
#include "util.h"
89

910
#include <time.h> // tzset(), _tzset()
@@ -435,6 +436,14 @@ static Intercepted EnvGetter(Local<Name> property,
435436
return Intercepted::kYes;
436437
}
437438
CHECK(property->IsString());
439+
440+
Utf8Value key(env->isolate(), property);
441+
if (env->permission()->enabled() &&
442+
!env->permission()->is_granted(
443+
env, permission::PermissionScope::kEnvVar, key.ToStringView())) {
444+
return Intercepted::kNo;
445+
}
446+
438447
MaybeLocal<String> value_string =
439448
env->env_vars()->Get(env->isolate(), property.As<String>());
440449

@@ -453,6 +462,16 @@ static Intercepted EnvSetter(Local<Name> property,
453462
const PropertyCallbackInfo<void>& info) {
454463
Environment* env = Environment::GetCurrent(info);
455464
CHECK(env->has_run_bootstrapping_code());
465+
466+
if (property->IsString()) {
467+
Utf8Value key(env->isolate(), property);
468+
THROW_IF_INSUFFICIENT_PERMISSIONS(
469+
env,
470+
permission::PermissionScope::kEnvVar,
471+
key.ToStringView(),
472+
Intercepted::kYes);
473+
}
474+
456475
// calling env->EmitProcessEnvWarning() sets a variable indicating that
457476
// warnings have been emitted. It should be called last after other
458477
// conditions leading to a warning have been met.
@@ -489,6 +508,13 @@ static Intercepted EnvQuery(Local<Name> property,
489508
Environment* env = Environment::GetCurrent(info);
490509
CHECK(env->has_run_bootstrapping_code());
491510
if (property->IsString()) {
511+
Utf8Value key(env->isolate(), property);
512+
if (env->permission()->enabled() &&
513+
!env->permission()->is_granted(
514+
env, permission::PermissionScope::kEnvVar, key.ToStringView())) {
515+
return Intercepted::kNo;
516+
}
517+
492518
int32_t rc = env->env_vars()->Query(env->isolate(), property.As<String>());
493519
bool has_env = (rc != -1);
494520
TraceEnvVar(env, "query", property.As<String>());
@@ -506,6 +532,13 @@ static Intercepted EnvDeleter(Local<Name> property,
506532
Environment* env = Environment::GetCurrent(info);
507533
CHECK(env->has_run_bootstrapping_code());
508534
if (property->IsString()) {
535+
Utf8Value key(env->isolate(), property);
536+
THROW_IF_INSUFFICIENT_PERMISSIONS(
537+
env,
538+
permission::PermissionScope::kEnvVar,
539+
key.ToStringView(),
540+
Intercepted::kYes);
541+
509542
env->env_vars()->Delete(env->isolate(), property.As<String>());
510543

511544
TraceEnvVar(env, "delete", property.As<String>());
@@ -525,7 +558,24 @@ static void EnvEnumerator(const PropertyCallbackInfo<Array>& info) {
525558

526559
Local<Array> ret;
527560
if (env->env_vars()->Enumerate(env->isolate()).ToLocal(&ret)) {
528-
info.GetReturnValue().Set(ret);
561+
if (env->permission()->enabled()) {
562+
LocalVector<Value> filtered(env->isolate());
563+
for (uint32_t i = 0; i < ret->Length(); i++) {
564+
Local<Value> elem;
565+
if (!ret->Get(env->context(), i).ToLocal(&elem)) continue;
566+
Utf8Value key(env->isolate(), elem);
567+
if (env->permission()->is_granted(
568+
env,
569+
permission::PermissionScope::kEnvVar,
570+
key.ToStringView())) {
571+
filtered.push_back(elem);
572+
}
573+
}
574+
info.GetReturnValue().Set(
575+
Array::New(env->isolate(), filtered.data(), filtered.size()));
576+
} else {
577+
info.GetReturnValue().Set(ret);
578+
}
529579
}
530580
}
531581

src/node_options.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -712,6 +712,12 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() {
712712
kAllowedInEnvvar,
713713
false,
714714
OptionNamespaces::kPermissionNamespace);
715+
AddOption("--allow-env",
716+
"allow access to environment variables when any permissions are "
717+
"set",
718+
&EnvironmentOptions::allow_env,
719+
kAllowedInEnvvar,
720+
OptionNamespaces::kPermissionNamespace);
715721
AddOption("--experimental-repl-await",
716722
"experimental await keyword support in REPL",
717723
&EnvironmentOptions::experimental_repl_await,

src/node_options.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,7 @@ class EnvironmentOptions : public Options {
147147
bool allow_wasi = false;
148148
bool allow_ffi = false;
149149
bool allow_worker_threads = false;
150+
std::vector<std::string> allow_env;
150151
bool experimental_repl_await = true;
151152
bool experimental_vm_modules = false;
152153
bool async_context_frame = true;
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
#include "env_var_permission.h"
2+
3+
#include <string>
4+
#include <vector>
5+
6+
namespace node {
7+
8+
namespace permission {
9+
10+
void EnvVarPermission::Apply(Environment* env,
11+
const std::vector<std::string>& allow,
12+
PermissionScope scope) {
13+
deny_all_ = true;
14+
if (!allow.empty()) {
15+
if (allow.size() == 1 && allow[0] == "*") {
16+
allow_all_ = true;
17+
return;
18+
}
19+
for (const std::string& arg : allow) {
20+
allowed_env_vars_.insert(arg);
21+
}
22+
}
23+
}
24+
25+
bool EnvVarPermission::is_granted(Environment* env,
26+
PermissionScope perm,
27+
const std::string_view& param) const {
28+
if (deny_all_) {
29+
if (allow_all_) {
30+
return true;
31+
}
32+
if (param.empty()) {
33+
return false;
34+
}
35+
return allowed_env_vars_.find(std::string(param)) !=
36+
allowed_env_vars_.end();
37+
}
38+
return true;
39+
}
40+
41+
} // namespace permission
42+
} // namespace node
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#ifndef SRC_PERMISSION_ENV_VAR_PERMISSION_H_
2+
#define SRC_PERMISSION_ENV_VAR_PERMISSION_H_
3+
4+
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
5+
6+
#include <set>
7+
#include <string>
8+
#include <vector>
9+
#include "permission/permission_base.h"
10+
11+
namespace node {
12+
13+
namespace permission {
14+
15+
class EnvVarPermission final : public PermissionBase {
16+
public:
17+
void Apply(Environment* env,
18+
const std::vector<std::string>& allow,
19+
PermissionScope scope) override;
20+
bool is_granted(Environment* env,
21+
PermissionScope perm,
22+
const std::string_view& param = "") const override;
23+
24+
private:
25+
bool deny_all_ = false;
26+
bool allow_all_ = false;
27+
std::set<std::string> allowed_env_vars_;
28+
};
29+
30+
} // namespace permission
31+
32+
} // namespace node
33+
34+
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
35+
#endif // SRC_PERMISSION_ENV_VAR_PERMISSION_H_

src/permission/permission.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ constexpr std::string_view GetDiagnosticsChannelName(PermissionScope scope) {
4848
return "node:permission-model:addon";
4949
case PermissionScope::kFFI:
5050
return "node:permission-model:ffi";
51+
case PermissionScope::kEnvVar:
52+
return "node:permission-model:env";
5153
default:
5254
return {};
5355
}
@@ -108,6 +110,8 @@ Permission::Permission() : enabled_(false), warning_only_(false) {
108110
std::shared_ptr<PermissionBase> net = std::make_shared<NetPermission>();
109111
std::shared_ptr<PermissionBase> addon = std::make_shared<AddonPermission>();
110112
std::shared_ptr<FFIPermission> ffi = std::make_shared<FFIPermission>();
113+
std::shared_ptr<PermissionBase> env_var =
114+
std::make_shared<EnvVarPermission>();
111115
#define V(Name, _, __, ___) \
112116
nodes_.insert(std::make_pair(PermissionScope::k##Name, fs));
113117
FILESYSTEM_PERMISSIONS(V)
@@ -139,6 +143,8 @@ Permission::Permission() : enabled_(false), warning_only_(false) {
139143
#define V(Name, _, __, ___) \
140144
nodes_.insert(std::make_pair(PermissionScope::k##Name, ffi));
141145
FFI_PERMISSIONS(V)
146+
nodes_.insert(std::make_pair(PermissionScope::k##Name, env_var));
147+
ENV_VAR_PERMISSIONS(V)
142148
#undef V
143149
}
144150

src/permission/permission.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
#include "permission/addon_permission.h"
1010
#include "permission/child_process_permission.h"
1111
#include "permission/ffi_permission.h"
12+
#include "permission/env_var_permission.h"
1213
#include "permission/fs_permission.h"
1314
#include "permission/inspector_permission.h"
1415
#include "permission/net_permission.h"

0 commit comments

Comments
 (0)