Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions 1 lib/.eslintrc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,4 @@ globals:
module: false
internalBinding: false
primordials: false
runInPrivilegedScope: false
11 changes: 9 additions & 2 deletions 11 lib/internal/bootstrap/loaders.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@

// This file is compiled as if it's wrapped in a function with arguments
// passed by node::RunBootstrapping()
/* global process, getLinkedBinding, getInternalBinding, primordials */
/* global process, getLinkedBinding, getInternalBinding, primordials,
runInPrivilegedScope */

const {
ArrayPrototypeMap,
Expand Down Expand Up @@ -280,7 +281,13 @@ class NativeModule {
requireWithFallbackInDeps : nativeModuleRequire;

const fn = compileFunction(id);
fn(this.exports, requireFn, this, process, internalBinding, primordials);
fn(this.exports,
requireFn,
this,
process,
internalBinding,
primordials,
runInPrivilegedScope);

this.loaded = true;
} finally {
Expand Down
5 changes: 3 additions & 2 deletions 5 lib/internal/bootstrap/node.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,9 @@
'use strict';

// This file is compiled as if it's wrapped in a function with arguments
// passed by node::RunBootstrapping()
/* global process, require, internalBinding, primordials */
// passed by node::RunBootstrapping():
// global process, require, internalBinding, primordials,
// runInPriviledgedScope

setupPrepareStackTrace();

Expand Down
12 changes: 10 additions & 2 deletions 12 lib/internal/bootstrap/pre_execution.js
Original file line number Diff line number Diff line change
Expand Up @@ -356,6 +356,7 @@ function initializeClusterIPC() {

function initializePolicy() {
const experimentalPolicy = getOptionValue('--experimental-policy');
const { setup, check, deny } = require('internal/process/policy');
if (experimentalPolicy) {
process.emitWarning('Policies are experimental.',
'ExperimentalWarning');
Expand Down Expand Up @@ -398,9 +399,16 @@ function initializePolicy() {
throw new ERR_MANIFEST_ASSERT_INTEGRITY(manifestURL, realIntegrities);
}
}
require('internal/process/policy')
.setup(src, manifestURL.href);
setup(src, manifestURL.href);
}
ObjectDefineProperty(process, 'policy', {
enumerable: true,
configurable: false,
value: {
deny,
check,
}
});
}

function initializeWASI() {
Expand Down
29 changes: 29 additions & 0 deletions 29 lib/internal/process/policy.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@ const {

const {
ERR_MANIFEST_TDZ,
ERR_INVALID_ARG_TYPE,
ERR_INVALID_ARG_VALUE,
} = require('internal/errors').codes;

const policy = internalBinding('policy');

const { Manifest } = require('internal/policy/manifest');
let manifest;
let manifestSrc;
Expand Down Expand Up @@ -57,5 +62,29 @@ module.exports = ObjectFreeze({

assertIntegrity(moduleURL, content) {
this.manifest.assertIntegrity(moduleURL, content);
},

deny(permissions) {
jasnell marked this conversation as resolved.
Outdated
Show resolved Hide resolved
if (typeof permissions !== 'string')
throw new ERR_INVALID_ARG_TYPE('permissions', 'string', permissions);
const ret = policy.deny(permissions);
if (ret === undefined)
throw new ERR_INVALID_ARG_VALUE('permissions', permissions);
},

fastCheck(permission) {
// This should only be used by internal code. Skips explicit
// type checking to improve performance. The permission
// argument must be a Int32
return policy.fastCheck(permission);
},

check(permissions) {
if (typeof permissions !== 'string')
throw new ERR_INVALID_ARG_TYPE('permission', 'string', permissions);
const ret = policy.check(permissions);
if (ret === undefined)
throw new ERR_INVALID_ARG_VALUE('permissions', permissions);
return ret;
}
});
7 changes: 7 additions & 0 deletions 7 lib/internal/test/policy.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
'use strict';

process.emitWarning(
'These APIs are for internal testing only. Do not use them.',
'internal/test/policy');

module.exports = { runInPrivilegedScope };
3 changes: 3 additions & 0 deletions 3 node.gyp
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@
'lib/internal/source_map/source_map.js',
'lib/internal/source_map/source_map_cache.js',
'lib/internal/test/binding.js',
'lib/internal/test/policy.js',
'lib/internal/timers.js',
'lib/internal/tls.js',
'lib/internal/trace_events_async_hooks.js',
Expand Down Expand Up @@ -660,6 +661,7 @@
'src/stream_wrap.cc',
'src/string_bytes.cc',
'src/string_decoder.cc',
'src/policy/policy.cc',
'src/tcp_wrap.cc',
'src/timers.cc',
'src/timer_wrap.cc',
Expand Down Expand Up @@ -735,6 +737,7 @@
'src/node_perf.h',
'src/node_perf_common.h',
'src/node_platform.h',
'src/policy/policy.h',
'src/node_process.h',
'src/node_report.h',
'src/node_revert.h',
Expand Down
13 changes: 13 additions & 0 deletions 13 src/env-inl.h
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,19 @@ inline size_t Environment::async_callback_scope_depth() const {
return async_callback_scope_depth_;
}

inline void Environment::set_in_privileged_scope(bool on) {
if (on)
in_privileged_scope_++;
else {
CHECK_GT(in_privileged_scope_, 0);
in_privileged_scope_--;
}
}

inline bool Environment::in_privileged_scope() const {
return in_privileged_scope_ > 0;
}

inline void Environment::PushAsyncCallbackScope() {
async_callback_scope_depth_++;
}
Expand Down
8 changes: 8 additions & 0 deletions 8 src/env.h
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,7 @@ constexpr size_t kFsStatsBufferLength =
V(replacement_string, "replacement") \
V(require_string, "require") \
V(retry_string, "retry") \
V(run_in_privileged_scope_string, "runInPrivilegedScope") \
V(scheme_string, "scheme") \
V(scopeid_string, "scopeid") \
V(serial_number_string, "serialNumber") \
Expand Down Expand Up @@ -547,6 +548,7 @@ constexpr size_t kFsStatsBufferLength =
V(primordials, v8::Object) \
V(promise_hook_handler, v8::Function) \
V(promise_reject_callback, v8::Function) \
V(run_in_privileged_scope, v8::Function) \
V(script_data_constructor_function, v8::Function) \
V(source_map_cache_getter, v8::Function) \
V(tick_callback_function, v8::Function) \
Expand Down Expand Up @@ -977,6 +979,7 @@ class Environment : public MemoryRetainer {
v8::MaybeLocal<v8::Value> BootstrapInternalLoaders();
v8::MaybeLocal<v8::Value> BootstrapNode();
v8::MaybeLocal<v8::Value> RunBootstrapping();
bool BootstrapPrivilegedAccessContext();

inline size_t async_callback_scope_depth() const;
inline void PushAsyncCallbackScope();
Expand Down Expand Up @@ -1183,6 +1186,9 @@ class Environment : public MemoryRetainer {
inline node_module* extra_linked_bindings_head();
inline const Mutex& extra_linked_bindings_mutex() const;

inline void set_in_privileged_scope(bool on = true);
inline bool in_privileged_scope() const;

inline bool filehandle_close_warning() const;
inline void set_filehandle_close_warning(bool on);

Expand Down Expand Up @@ -1418,6 +1424,8 @@ class Environment : public MemoryRetainer {
size_t async_callback_scope_depth_ = 0;
std::vector<double> destroy_async_id_list_;

size_t in_privileged_scope_ = 0;

#if HAVE_INSPECTOR
std::unique_ptr<profiler::V8CoverageConnection> coverage_connection_;
std::unique_ptr<profiler::V8CpuProfilerConnection> cpu_profiler_connection_;
Expand Down
41 changes: 35 additions & 6 deletions 41 src/node.cc
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
#include "node_revert.h"
#include "node_v8_platform-inl.h"
#include "node_version.h"
#include "policy/policy.h"

#if HAVE_OPENSSL
#include "allocated_buffer-inl.h" // Inlined functions needed by node_crypto.h
Expand Down Expand Up @@ -294,6 +295,16 @@ void Environment::InitializeDiagnostics() {
#endif
}

bool Environment::BootstrapPrivilegedAccessContext() {
Local<Function> run_in_privileged_scope;
MaybeLocal<Function> maybe_run_in_privileged_scope =
Function::New(context(), policy::RunInPrivilegedScope);
if (!maybe_run_in_privileged_scope.ToLocal(&run_in_privileged_scope))
return false;
set_run_in_privileged_scope(run_in_privileged_scope);
return true;
}

MaybeLocal<Value> Environment::BootstrapInternalLoaders() {
EscapableHandleScope scope(isolate_);

Expand All @@ -302,7 +313,8 @@ MaybeLocal<Value> Environment::BootstrapInternalLoaders() {
process_string(),
FIXED_ONE_BYTE_STRING(isolate_, "getLinkedBinding"),
FIXED_ONE_BYTE_STRING(isolate_, "getInternalBinding"),
primordials_string()};
primordials_string(),
run_in_privileged_scope_string()};
std::vector<Local<Value>> loaders_args = {
process_object(),
NewFunctionTemplate(binding::GetLinkedBinding)
Expand All @@ -311,7 +323,8 @@ MaybeLocal<Value> Environment::BootstrapInternalLoaders() {
NewFunctionTemplate(binding::GetInternalBinding)
->GetFunction(context())
.ToLocalChecked(),
primordials()};
primordials(),
run_in_privileged_scope()};

// Bootstrap internal loaders
Local<Value> loader_exports;
Expand Down Expand Up @@ -348,12 +361,14 @@ MaybeLocal<Value> Environment::BootstrapNode() {
process_string(),
require_string(),
internal_binding_string(),
primordials_string()};
primordials_string(),
run_in_privileged_scope_string()};
std::vector<Local<Value>> node_args = {
process_object(),
native_module_require(),
internal_binding_loader(),
primordials()};
primordials(),
run_in_privileged_scope()};

MaybeLocal<Value> result = ExecuteBootstrapper(
this, "internal/bootstrap/node", &node_params, &node_args);
Expand Down Expand Up @@ -399,6 +414,10 @@ MaybeLocal<Value> Environment::RunBootstrapping() {

CHECK(!has_run_bootstrapping_code());

if (!BootstrapPrivilegedAccessContext()) {
return MaybeLocal<Value>();
}

if (BootstrapInternalLoaders().IsEmpty()) {
return MaybeLocal<Value>();
}
Expand Down Expand Up @@ -436,7 +455,8 @@ MaybeLocal<Value> StartExecution(Environment* env, const char* main_script_id) {
env->require_string(),
env->internal_binding_string(),
env->primordials_string(),
FIXED_ONE_BYTE_STRING(env->isolate(), "markBootstrapComplete")};
FIXED_ONE_BYTE_STRING(env->isolate(), "markBootstrapComplete"),
env->run_in_privileged_scope_string()};

std::vector<Local<Value>> arguments = {
env->process_object(),
Expand All @@ -445,7 +465,8 @@ MaybeLocal<Value> StartExecution(Environment* env, const char* main_script_id) {
env->primordials(),
env->NewFunctionTemplate(MarkBootstrapComplete)
->GetFunction(env->context())
.ToLocalChecked()};
.ToLocalChecked(),
env->run_in_privileged_scope()};

return scope.EscapeMaybe(
ExecuteBootstrapper(env, main_script_id, &parameters, &arguments));
Expand Down Expand Up @@ -795,6 +816,14 @@ int ProcessGlobalArgs(std::vector<std::string>* args,
}
}

if (per_process::root_policy.Apply(
per_process::cli_options->policy_deny,
per_process::cli_options->policy_grant).IsNothing()) {
errors->emplace_back(
"invalid permissions passed to --policy-deny or --policy-grant");
return 12;
}

if (per_process::cli_options->disable_proto != "delete" &&
per_process::cli_options->disable_proto != "throw" &&
per_process::cli_options->disable_proto != "") {
Expand Down
1 change: 1 addition & 0 deletions 1 src/node_binding.cc
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
V(os) \
V(performance) \
V(pipe_wrap) \
V(policy) \
V(process_wrap) \
V(process_methods) \
V(report) \
Expand Down
2 changes: 2 additions & 0 deletions 2 src/node_errors.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ void OnFatalError(const char* location, const char* message);
// a `Local<Value>` containing the TypeError with proper code and message

#define ERRORS_WITH_CODE(V) \
V(ERR_ACCESS_DENIED, Error) \
V(ERR_BUFFER_CONTEXT_NOT_AVAILABLE, Error) \
V(ERR_BUFFER_OUT_OF_BOUNDS, RangeError) \
V(ERR_BUFFER_TOO_LARGE, Error) \
Expand Down Expand Up @@ -105,6 +106,7 @@ void OnFatalError(const char* location, const char* message);
// Errors with predefined static messages

#define PREDEFINED_ERROR_MESSAGES(V) \
V(ERR_ACCESS_DENIED, "Access is denied") \
V(ERR_BUFFER_CONTEXT_NOT_AVAILABLE, \
"Buffer is not available for the current Context") \
V(ERR_CONSTRUCT_CALL_INVALID, "Constructor cannot be called") \
Expand Down
1 change: 1 addition & 0 deletions 1 src/node_external_reference.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ class ExternalReferenceRegistry {
V(handle_wrap) \
V(messaging) \
V(native_module) \
V(policy) \
V(process_methods) \
V(process_object) \
V(task_queue) \
Expand Down
3 changes: 2 additions & 1 deletion 3 src/node_native_module.cc
Original file line number Diff line number Diff line change
Expand Up @@ -183,7 +183,8 @@ MaybeLocal<Function> NativeModuleLoader::CompileAsModule(
FIXED_ONE_BYTE_STRING(isolate, "module"),
FIXED_ONE_BYTE_STRING(isolate, "process"),
FIXED_ONE_BYTE_STRING(isolate, "internalBinding"),
FIXED_ONE_BYTE_STRING(isolate, "primordials")};
FIXED_ONE_BYTE_STRING(isolate, "primordials"),
FIXED_ONE_BYTE_STRING(isolate, "runInPrivilegedScope")};
return LookupAndCompile(context, id, &parameters, result);
}

Expand Down
8 changes: 8 additions & 0 deletions 8 src/node_options.cc
Original file line number Diff line number Diff line change
Expand Up @@ -704,6 +704,14 @@ PerProcessOptionsParser::PerProcessOptionsParser(
"generate diagnostic report on fatal (internal) errors",
&PerProcessOptions::report_on_fatalerror,
kAllowedInEnvironment);
AddOption("--policy-deny",
"denied permissions",
&PerProcessOptions::policy_deny,
kAllowedInEnvironment);
AddOption("--policy-grant",
"granted permissions",
&PerProcessOptions::policy_grant,
kAllowedInEnvironment);

#ifdef NODE_HAVE_I18N_SUPPORT
AddOption("--icu-data-dir",
Expand Down
3 changes: 3 additions & 0 deletions 3 src/node_options.h
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,9 @@ class PerProcessOptions : public Options {
bool trace_sigint = false;
std::vector<std::string> cmdline;

std::string policy_grant;
std::string policy_deny;

inline PerIsolateOptions* get_per_isolate_options();
void CheckOptions(std::vector<std::string>* errors) override;
};
Expand Down
Loading
Morty Proxy This is a proxified and sanitized view of the page, visit original site.