/src/node/src/permission/permission.cc
Line | Count | Source |
1 | | #include "permission.h" |
2 | | #include "base_object-inl.h" |
3 | | #include "env-inl.h" |
4 | | #include "memory_tracker-inl.h" |
5 | | #include "node.h" |
6 | | #include "node_errors.h" |
7 | | #include "node_external_reference.h" |
8 | | #include "node_file.h" |
9 | | |
10 | | #include "v8.h" |
11 | | |
12 | | #include <memory> |
13 | | #include <string> |
14 | | #include <vector> |
15 | | |
16 | | namespace node { |
17 | | |
18 | | using v8::Context; |
19 | | using v8::FunctionCallbackInfo; |
20 | | using v8::IntegrityLevel; |
21 | | using v8::Local; |
22 | | using v8::MaybeLocal; |
23 | | using v8::Object; |
24 | | using v8::Value; |
25 | | |
26 | | namespace permission { |
27 | | |
28 | | namespace { |
29 | | |
30 | | // permission.has('fs.in', '/tmp/') |
31 | | // permission.has('fs.in') |
32 | 0 | static void Has(const FunctionCallbackInfo<Value>& args) { |
33 | 0 | Environment* env = Environment::GetCurrent(args); |
34 | 0 | CHECK(args[0]->IsString()); |
35 | | |
36 | 0 | const std::string deny_scope = Utf8Value(env->isolate(), args[0]).ToString(); |
37 | 0 | PermissionScope scope = Permission::StringToPermission(deny_scope); |
38 | 0 | if (scope == PermissionScope::kPermissionsRoot) { |
39 | 0 | return args.GetReturnValue().Set(false); |
40 | 0 | } |
41 | | |
42 | 0 | if (args.Length() > 1 && !args[1]->IsUndefined()) { |
43 | 0 | Utf8Value utf8_arg(env->isolate(), args[1]); |
44 | 0 | if (utf8_arg.length() == 0) { |
45 | 0 | args.GetReturnValue().Set(false); |
46 | 0 | return; |
47 | 0 | } |
48 | 0 | return args.GetReturnValue().Set( |
49 | 0 | env->permission()->is_granted(env, scope, utf8_arg.ToStringView())); |
50 | 0 | } |
51 | | |
52 | 0 | return args.GetReturnValue().Set(env->permission()->is_granted(env, scope)); |
53 | 0 | } |
54 | | |
55 | | } // namespace |
56 | | |
57 | | #define V(Name, label, _, __) \ |
58 | 0 | if (perm == PermissionScope::k##Name) return #Name; |
59 | 0 | const char* Permission::PermissionToString(const PermissionScope perm) { |
60 | 0 | PERMISSIONS(V) |
61 | 0 | return nullptr; |
62 | 0 | } |
63 | | #undef V |
64 | | |
65 | | #define V(Name, label, _, __) \ |
66 | 0 | if (perm == label) return PermissionScope::k##Name; |
67 | 0 | PermissionScope Permission::StringToPermission(const std::string& perm) { |
68 | 0 | PERMISSIONS(V) |
69 | 0 | return PermissionScope::kPermissionsRoot; |
70 | 0 | } |
71 | | #undef V |
72 | | |
73 | 35 | Permission::Permission() : enabled_(false) { |
74 | 35 | std::shared_ptr<PermissionBase> fs = std::make_shared<FSPermission>(); |
75 | 35 | std::shared_ptr<PermissionBase> child_p = |
76 | 35 | std::make_shared<ChildProcessPermission>(); |
77 | 35 | std::shared_ptr<PermissionBase> worker_t = |
78 | 35 | std::make_shared<WorkerPermission>(); |
79 | 35 | std::shared_ptr<PermissionBase> inspector = |
80 | 35 | std::make_shared<InspectorPermission>(); |
81 | 35 | std::shared_ptr<PermissionBase> wasi = std::make_shared<WASIPermission>(); |
82 | 35 | std::shared_ptr<PermissionBase> net = std::make_shared<NetPermission>(); |
83 | 35 | std::shared_ptr<PermissionBase> addon = std::make_shared<AddonPermission>(); |
84 | 35 | #define V(Name, _, __, ___) \ |
85 | 105 | nodes_.insert(std::make_pair(PermissionScope::k##Name, fs)); |
86 | 105 | FILESYSTEM_PERMISSIONS(V) |
87 | 35 | #undef V |
88 | 35 | #define V(Name, _, __, ___) \ |
89 | 35 | nodes_.insert(std::make_pair(PermissionScope::k##Name, child_p)); |
90 | 35 | CHILD_PROCESS_PERMISSIONS(V) |
91 | 35 | #undef V |
92 | 35 | #define V(Name, _, __, ___) \ |
93 | 35 | nodes_.insert(std::make_pair(PermissionScope::k##Name, worker_t)); |
94 | 35 | WORKER_THREADS_PERMISSIONS(V) |
95 | 35 | #undef V |
96 | 35 | #define V(Name, _, __, ___) \ |
97 | 35 | nodes_.insert(std::make_pair(PermissionScope::k##Name, inspector)); |
98 | 35 | INSPECTOR_PERMISSIONS(V) |
99 | 35 | #undef V |
100 | 35 | #define V(Name, _, __, ___) \ |
101 | 35 | nodes_.insert(std::make_pair(PermissionScope::k##Name, wasi)); |
102 | 35 | WASI_PERMISSIONS(V) |
103 | 35 | #undef V |
104 | 35 | #define V(Name, _, __, ___) \ |
105 | 35 | nodes_.insert(std::make_pair(PermissionScope::k##Name, net)); |
106 | 35 | NET_PERMISSIONS(V) |
107 | 35 | #undef V |
108 | 35 | #define V(Name, _, __, ___) \ |
109 | 35 | nodes_.insert(std::make_pair(PermissionScope::k##Name, addon)); |
110 | 35 | ADDON_PERMISSIONS(V) |
111 | 35 | #undef V |
112 | 35 | } |
113 | | |
114 | 0 | const char* GetErrorFlagSuggestion(node::permission::PermissionScope perm) { |
115 | 0 | switch (perm) { |
116 | 0 | #define V(Name, _, __, Flag) \ |
117 | 0 | case node::permission::PermissionScope::k##Name: \ |
118 | 0 | return Flag[0] != '\0' ? "Use " Flag " to manage permissions." : ""; |
119 | 0 | PERMISSIONS(V) |
120 | 0 | #undef V |
121 | 0 | default: |
122 | 0 | return ""; |
123 | 0 | } |
124 | 0 | } |
125 | | |
126 | | MaybeLocal<Value> CreateAccessDeniedError(Environment* env, |
127 | | PermissionScope perm, |
128 | 0 | const std::string_view& res) { |
129 | 0 | const char* suggestion = GetErrorFlagSuggestion(perm); |
130 | 0 | Local<Object> err = ERR_ACCESS_DENIED( |
131 | 0 | env->isolate(), "Access to this API has been restricted. %s", suggestion); |
132 | |
|
133 | 0 | Local<Value> perm_string; |
134 | 0 | Local<Value> resource_string; |
135 | 0 | std::string_view perm_str = Permission::PermissionToString(perm); |
136 | 0 | if (!ToV8Value(env->context(), perm_str, env->isolate()) |
137 | 0 | .ToLocal(&perm_string) || |
138 | 0 | !ToV8Value(env->context(), res, env->isolate()) |
139 | 0 | .ToLocal(&resource_string) || |
140 | 0 | err->Set(env->context(), env->permission_string(), perm_string) |
141 | 0 | .IsNothing() || |
142 | 0 | err->Set(env->context(), env->resource_string(), resource_string) |
143 | 0 | .IsNothing()) { |
144 | 0 | return MaybeLocal<Value>(); |
145 | 0 | } |
146 | 0 | return err; |
147 | 0 | } |
148 | | |
149 | | void Permission::ThrowAccessDenied(Environment* env, |
150 | | PermissionScope perm, |
151 | 0 | const std::string_view& res) { |
152 | 0 | Local<Value> err; |
153 | 0 | if (CreateAccessDeniedError(env, perm, res).ToLocal(&err)) { |
154 | 0 | env->isolate()->ThrowException(err); |
155 | 0 | } |
156 | | // If ToLocal returned false, then v8 will have scheduled a |
157 | | // superseding error to be thrown. |
158 | 0 | } |
159 | | |
160 | | void Permission::AsyncThrowAccessDenied(Environment* env, |
161 | | fs::FSReqBase* req_wrap, |
162 | | PermissionScope perm, |
163 | 0 | const std::string_view& res) { |
164 | 0 | Local<Value> err; |
165 | 0 | if (CreateAccessDeniedError(env, perm, res).ToLocal(&err)) { |
166 | 0 | return req_wrap->Reject(err); |
167 | 0 | } |
168 | | // If ToLocal returned false, then v8 will have scheduled a |
169 | | // superseding error to be thrown. |
170 | 0 | } |
171 | | |
172 | 0 | void Permission::EnablePermissions() { |
173 | 0 | if (!enabled_) { |
174 | 0 | enabled_ = true; |
175 | 0 | } |
176 | 0 | } |
177 | | |
178 | | void Permission::Apply(Environment* env, |
179 | | const std::vector<std::string>& allow, |
180 | 0 | PermissionScope scope) { |
181 | 0 | auto permission = nodes_.find(scope); |
182 | 0 | if (permission != nodes_.end()) { |
183 | 0 | permission->second->Apply(env, allow, scope); |
184 | 0 | } |
185 | 0 | } |
186 | | |
187 | | void Initialize(Local<Object> target, |
188 | | Local<Value> unused, |
189 | | Local<Context> context, |
190 | 35 | void* priv) { |
191 | 35 | SetMethodNoSideEffect(context, target, "has", Has); |
192 | | |
193 | 35 | target->SetIntegrityLevel(context, IntegrityLevel::kFrozen).FromJust(); |
194 | 35 | } |
195 | | |
196 | 0 | void RegisterExternalReferences(ExternalReferenceRegistry* registry) { |
197 | 0 | registry->Register(Has); |
198 | 0 | } |
199 | | |
200 | | } // namespace permission |
201 | | } // namespace node |
202 | | |
203 | | NODE_BINDING_CONTEXT_AWARE_INTERNAL(permission, node::permission::Initialize) |
204 | | NODE_BINDING_EXTERNAL_REFERENCE(permission, |
205 | | node::permission::RegisterExternalReferences) |