Skip to content

Commit 3df13d5

Browse files
committed
src,permission: restrict inspector when pm enabled
PR-URL: nodejs-private/node-private#410 Refs: https://hackerone.com/bugs?subject=nodejs&report_id=1962701 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> CVE-ID: CVE-2023-30587
1 parent d274c3b commit 3df13d5

File tree

11 files changed

+125
-6
lines changed

11 files changed

+125
-6
lines changed

doc/api/permissions.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -466,7 +466,8 @@ flag.
466466

467467
When starting Node.js with `--experimental-permission`,
468468
the ability to access the file system through the `fs` module, spawn processes,
469-
and use `node:worker_threads` will be restricted.
469+
use `node:worker_threads` and enable the runtime inspector
470+
will be restricted.
470471

471472
```console
472473
$ node --experimental-permission index.js

node.gyp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@
139139
'src/node_zlib.cc',
140140
'src/permission/child_process_permission.cc',
141141
'src/permission/fs_permission.cc',
142+
'src/permission/inspector_permission.cc',
142143
'src/permission/permission.cc',
143144
'src/permission/worker_permission.cc',
144145
'src/pipe_wrap.cc',
@@ -258,6 +259,7 @@
258259
'src/node_worker.h',
259260
'src/permission/child_process_permission.h',
260261
'src/permission/fs_permission.h',
262+
'src/permission/inspector_permission.h',
261263
'src/permission/permission.h',
262264
'src/permission/worker_permission.h',
263265
'src/pipe_wrap.h',

src/env.cc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -792,9 +792,12 @@ Environment::Environment(IsolateData* isolate_data,
792792
if (options_->experimental_permission) {
793793
permission()->EnablePermissions();
794794
// If any permission is set the process shouldn't be able to neither
795-
// spawn/worker nor use addons unless explicitly allowed by the user
795+
// spawn/worker nor use addons or enable inspector
796+
// unless explicitly allowed by the user
796797
if (!options_->allow_fs_read.empty() || !options_->allow_fs_write.empty()) {
797798
options_->allow_native_addons = false;
799+
flags_ = flags_ | EnvironmentFlags::kNoCreateInspector;
800+
permission()->Apply("*", permission::PermissionScope::kInspector);
798801
if (!options_->allow_child_process) {
799802
permission()->Apply("*", permission::PermissionScope::kChildProcess);
800803
}

src/inspector_agent.cc

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,9 @@
1414
#include "node_options-inl.h"
1515
#include "node_process-inl.h"
1616
#include "node_url.h"
17-
#include "util-inl.h"
17+
#include "permission/permission.h"
1818
#include "timer_wrap-inl.h"
19+
#include "util-inl.h"
1920
#include "v8-inspector.h"
2021
#include "v8-platform.h"
2122

@@ -755,6 +756,10 @@ bool Agent::StartIoThread() {
755756
if (io_ != nullptr)
756757
return true;
757758

759+
THROW_IF_INSUFFICIENT_PERMISSIONS(parent_env_,
760+
permission::PermissionScope::kInspector,
761+
"StartIoThread",
762+
false);
758763
if (!parent_env_->should_create_inspector() && !client_) {
759764
ThrowUninitializedInspectorError(parent_env_);
760765
return false;
@@ -780,6 +785,10 @@ void Agent::Stop() {
780785
std::unique_ptr<InspectorSession> Agent::Connect(
781786
std::unique_ptr<InspectorSessionDelegate> delegate,
782787
bool prevent_shutdown) {
788+
THROW_IF_INSUFFICIENT_PERMISSIONS(parent_env_,
789+
permission::PermissionScope::kInspector,
790+
"Connect",
791+
std::unique_ptr<InspectorSession>{});
783792
if (!parent_env_->should_create_inspector() && !client_) {
784793
ThrowUninitializedInspectorError(parent_env_);
785794
return std::unique_ptr<InspectorSession>{};
@@ -796,6 +805,10 @@ std::unique_ptr<InspectorSession> Agent::Connect(
796805
std::unique_ptr<InspectorSession> Agent::ConnectToMainThread(
797806
std::unique_ptr<InspectorSessionDelegate> delegate,
798807
bool prevent_shutdown) {
808+
THROW_IF_INSUFFICIENT_PERMISSIONS(parent_env_,
809+
permission::PermissionScope::kInspector,
810+
"ConnectToMainThread",
811+
std::unique_ptr<InspectorSession>{});
799812
if (!parent_env_->should_create_inspector() && !client_) {
800813
ThrowUninitializedInspectorError(parent_env_);
801814
return std::unique_ptr<InspectorSession>{};
@@ -810,6 +823,9 @@ std::unique_ptr<InspectorSession> Agent::ConnectToMainThread(
810823
}
811824

812825
void Agent::WaitForDisconnect() {
826+
THROW_IF_INSUFFICIENT_PERMISSIONS(parent_env_,
827+
permission::PermissionScope::kInspector,
828+
"WaitForDisconnect");
813829
if (!parent_env_->should_create_inspector() && !client_) {
814830
ThrowUninitializedInspectorError(parent_env_);
815831
return;
@@ -963,6 +979,10 @@ void Agent::SetParentHandle(
963979

964980
std::unique_ptr<ParentInspectorHandle> Agent::GetParentHandle(
965981
uint64_t thread_id, const std::string& url, const std::string& name) {
982+
THROW_IF_INSUFFICIENT_PERMISSIONS(parent_env_,
983+
permission::PermissionScope::kInspector,
984+
"GetParentHandle",
985+
std::unique_ptr<ParentInspectorHandle>{});
966986
if (!parent_env_->should_create_inspector() && !client_) {
967987
ThrowUninitializedInspectorError(parent_env_);
968988
return std::unique_ptr<ParentInspectorHandle>{};
@@ -977,6 +997,8 @@ std::unique_ptr<ParentInspectorHandle> Agent::GetParentHandle(
977997
}
978998

979999
void Agent::WaitForConnect() {
1000+
THROW_IF_INSUFFICIENT_PERMISSIONS(
1001+
parent_env_, permission::PermissionScope::kInspector, "WaitForConnect");
9801002
if (!parent_env_->should_create_inspector() && !client_) {
9811003
ThrowUninitializedInspectorError(parent_env_);
9821004
return;
@@ -987,6 +1009,10 @@ void Agent::WaitForConnect() {
9871009
}
9881010

9891011
std::shared_ptr<WorkerManager> Agent::GetWorkerManager() {
1012+
THROW_IF_INSUFFICIENT_PERMISSIONS(parent_env_,
1013+
permission::PermissionScope::kInspector,
1014+
"GetWorkerManager",
1015+
std::unique_ptr<WorkerManager>{});
9901016
if (!parent_env_->should_create_inspector() && !client_) {
9911017
ThrowUninitializedInspectorError(parent_env_);
9921018
return std::unique_ptr<WorkerManager>{};

src/node_worker.cc

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,13 @@ Worker::Worker(Environment* env,
8585
Number::New(env->isolate(), static_cast<double>(thread_id_.id)))
8686
.Check();
8787

88-
inspector_parent_handle_ =
89-
GetInspectorParentHandle(env, thread_id_, url.c_str(), name.c_str());
88+
// Without this check, to use the permission model with
89+
// workers (--allow-worker) one would need to pass --allow-inspector as well
90+
if (env->permission()->is_granted(
91+
node::permission::PermissionScope::kInspector)) {
92+
inspector_parent_handle_ =
93+
GetInspectorParentHandle(env, thread_id_, url.c_str(), name.c_str());
94+
}
9095

9196
argv_ = std::vector<std::string>{env->argv()[0]};
9297
// Mark this Worker object as weak until we actually start the thread.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#include "inspector_permission.h"
2+
3+
#include <string>
4+
5+
namespace node {
6+
7+
namespace permission {
8+
9+
// Currently, Inspector manage a single state
10+
// Once denied, it's always denied
11+
void InspectorPermission::Apply(const std::string& allow,
12+
PermissionScope scope) {
13+
deny_all_ = true;
14+
}
15+
16+
bool InspectorPermission::is_granted(PermissionScope perm,
17+
const std::string_view& param) {
18+
return deny_all_ == false;
19+
}
20+
21+
} // namespace permission
22+
} // namespace node

src/permission/inspector_permission.h

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#ifndef SRC_PERMISSION_INSPECTOR_PERMISSION_H_
2+
#define SRC_PERMISSION_INSPECTOR_PERMISSION_H_
3+
4+
#if defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
5+
6+
#include <string>
7+
#include "permission/permission_base.h"
8+
9+
namespace node {
10+
11+
namespace permission {
12+
13+
class InspectorPermission final : public PermissionBase {
14+
public:
15+
void Apply(const std::string& allow, PermissionScope scope) override;
16+
bool is_granted(PermissionScope perm,
17+
const std::string_view& param = "") override;
18+
19+
private:
20+
bool deny_all_;
21+
};
22+
23+
} // namespace permission
24+
25+
} // namespace node
26+
27+
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS
28+
#endif // SRC_PERMISSION_INSPECTOR_PERMISSION_H_

src/permission/permission.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ Permission::Permission() : enabled_(false) {
7979
std::make_shared<ChildProcessPermission>();
8080
std::shared_ptr<PermissionBase> worker_t =
8181
std::make_shared<WorkerPermission>();
82+
std::shared_ptr<PermissionBase> inspector =
83+
std::make_shared<InspectorPermission>();
8284
#define V(Name, _, __) \
8385
nodes_.insert(std::make_pair(PermissionScope::k##Name, fs));
8486
FILESYSTEM_PERMISSIONS(V)
@@ -91,6 +93,10 @@ Permission::Permission() : enabled_(false) {
9193
nodes_.insert(std::make_pair(PermissionScope::k##Name, worker_t));
9294
WORKER_THREADS_PERMISSIONS(V)
9395
#undef V
96+
#define V(Name, _, __) \
97+
nodes_.insert(std::make_pair(PermissionScope::k##Name, inspector));
98+
INSPECTOR_PERMISSIONS(V)
99+
#undef V
94100
}
95101

96102
void Permission::ThrowAccessDenied(Environment* env,

src/permission/permission.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include "node_options.h"
88
#include "permission/child_process_permission.h"
99
#include "permission/fs_permission.h"
10+
#include "permission/inspector_permission.h"
1011
#include "permission/permission_base.h"
1112
#include "permission/worker_permission.h"
1213
#include "v8.h"

src/permission/permission_base.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,13 @@ namespace permission {
2222
#define WORKER_THREADS_PERMISSIONS(V) \
2323
V(WorkerThreads, "worker", PermissionsRoot)
2424

25+
#define INSPECTOR_PERMISSIONS(V) V(Inspector, "inspector", PermissionsRoot)
26+
2527
#define PERMISSIONS(V) \
2628
FILESYSTEM_PERMISSIONS(V) \
2729
CHILD_PROCESS_PERMISSIONS(V) \
28-
WORKER_THREADS_PERMISSIONS(V)
30+
WORKER_THREADS_PERMISSIONS(V) \
31+
INSPECTOR_PERMISSIONS(V)
2932

3033
#define V(name, _, __) k##name,
3134
enum class PermissionScope {

0 commit comments

Comments
 (0)