/
githubmirror
/
node
Обзор
Документация
Войти
/
githubmirror
/
node
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/permission/permission.cc
379 строк
13 KB
James M Snell
src: update repeated use strings to env
05 авг 2026, 08:36
Не верифицирован
05 авг 2026, 08:36
41afbd3
Код
Авторство
О чём код?
#include "permission.h" #include "base_object-inl.h" #include "env-inl.h" #include "memory_tracker-inl.h" #include "node.h" #include "node_diagnostics_channel.h" #include "node_errors.h" #include "node_external_reference.h" #include "node_file.h" #include "v8.h" #include <memory> #include <string> #include <vector> namespace node { using v8::Context; using v8::FunctionCallbackInfo; using v8::IntegrityLevel; using v8::Local; using v8::MaybeLocal; using v8::Object; using v8::Value; namespace permission { namespace { constexpr std::string_view GetDiagnosticsChannelName(PermissionScope scope) { switch (scope) { case PermissionScope::kFileSystem: case PermissionScope::kFileSystemRead: case PermissionScope::kFileSystemWrite: return "node:permission-model:fs"; case PermissionScope::kChildProcess: return "node:permission-model:child"; case PermissionScope::kWorkerThreads: return "node:permission-model:worker"; case PermissionScope::kNet: return "node:permission-model:net"; case PermissionScope::kInspector: return "node:permission-model:inspector"; case PermissionScope::kWASI: return "node:permission-model:wasi"; case PermissionScope::kAddon: return "node:permission-model:addon"; case PermissionScope::kFFI: return "node:permission-model:ffi"; case PermissionScope::kOpenSSLStore: return "node:permission-model:openssl-store"; default: return {}; } } // permission.drop('fs.read', '/tmp/') // permission.drop('child') static void Drop(const FunctionCallbackInfo<Value>& args) { Environment* env = Environment::GetCurrent(args); CHECK(args[0]->IsString()); const std::string deny_scope = Utf8Value(env->isolate(), args[0]).ToString(); PermissionScope scope = Permission::StringToPermission(deny_scope); if (scope == PermissionScope::kPermissionsRoot) { return; } if (args.Length() > 1 && !args[1]->IsUndefined()) { Utf8Value utf8_arg(env->isolate(), args[1]); if (utf8_arg.length() > 0) { env->permission()->Drop(env, scope, utf8_arg.ToStringView()); return; } } env->permission()->Drop(env, scope); } // permission.has('fs.in', '/tmp/') // permission.has('fs.in') static void Has(const FunctionCallbackInfo<Value>& args) { Environment* env = Environment::GetCurrent(args); CHECK(args[0]->IsString()); const std::string deny_scope = Utf8Value(env->isolate(), args[0]).ToString(); PermissionScope scope = Permission::StringToPermission(deny_scope); if (scope == PermissionScope::kPermissionsRoot) { return args.GetReturnValue().Set(false); } if (args.Length() > 1 && !args[1]->IsUndefined()) { Utf8Value utf8_arg(env->isolate(), args[1]); if (utf8_arg.length() == 0) { args.GetReturnValue().Set(false); return; } return args.GetReturnValue().Set( env->permission()->is_granted(env, scope, utf8_arg.ToStringView())); } return args.GetReturnValue().Set(env->permission()->is_granted(env, scope)); } } // namespace #define V(Name, label, _, __) \ if (perm == PermissionScope::k##Name) return #Name; const char* Permission::PermissionToString(const PermissionScope perm) { PERMISSIONS(V) return nullptr; } #undef V #define V(Name, label, _, __) \ if (perm == label) return PermissionScope::k##Name; PermissionScope Permission::StringToPermission(const std::string& perm) { PERMISSIONS(V) return PermissionScope::kPermissionsRoot; } #undef V Permission::Permission() : enabled_(false), warning_only_(false) { std::shared_ptr<PermissionBase> fs = std::make_shared<FSPermission>(); std::shared_ptr<PermissionBase> child_p = std::make_shared<ChildProcessPermission>(); std::shared_ptr<PermissionBase> worker_t = std::make_shared<WorkerPermission>(); std::shared_ptr<PermissionBase> inspector = std::make_shared<InspectorPermission>(); std::shared_ptr<PermissionBase> wasi = std::make_shared<WASIPermission>(); std::shared_ptr<PermissionBase> net = std::make_shared<NetPermission>(); std::shared_ptr<PermissionBase> addon = std::make_shared<AddonPermission>(); std::shared_ptr<FFIPermission> ffi = std::make_shared<FFIPermission>(); std::shared_ptr<PermissionBase> openssl_store = std::make_shared<OpenSSLStorePermission>(); #define V(Name, _, __, ___) \ nodes_.insert(std::make_pair(PermissionScope::k##Name, fs)); FILESYSTEM_PERMISSIONS(V) #undef V #define V(Name, _, __, ___) \ nodes_.insert(std::make_pair(PermissionScope::k##Name, child_p)); CHILD_PROCESS_PERMISSIONS(V) #undef V #define V(Name, _, __, ___) \ nodes_.insert(std::make_pair(PermissionScope::k##Name, worker_t)); WORKER_THREADS_PERMISSIONS(V) #undef V #define V(Name, _, __, ___) \ nodes_.insert(std::make_pair(PermissionScope::k##Name, inspector)); INSPECTOR_PERMISSIONS(V) #undef V #define V(Name, _, __, ___) \ nodes_.insert(std::make_pair(PermissionScope::k##Name, wasi)); WASI_PERMISSIONS(V) #undef V #define V(Name, _, __, ___) \ nodes_.insert(std::make_pair(PermissionScope::k##Name, net)); NET_PERMISSIONS(V) #undef V #define V(Name, _, __, ___) \ nodes_.insert(std::make_pair(PermissionScope::k##Name, addon)); ADDON_PERMISSIONS(V) #undef V #define V(Name, _, __, ___) \ nodes_.insert(std::make_pair(PermissionScope::k##Name, ffi)); FFI_PERMISSIONS(V) #undef V #define V(Name, _, __, ___) \ nodes_.insert(std::make_pair(PermissionScope::k##Name, openssl_store)); OPENSSL_STORE_PERMISSIONS(V) #undef V } const char* GetErrorFlagSuggestion(node::permission::PermissionScope perm) { switch (perm) { #define V(Name, _, __, Flag) \ case node::permission::PermissionScope::k##Name: \ return Flag[0] != '\0' ? "Use " Flag " to manage permissions." : ""; PERMISSIONS(V) #undef V default: return ""; } } MaybeLocal<Value> CreateAccessDeniedError(Environment* env, PermissionScope perm, const std::string_view& res) { const char* suggestion = GetErrorFlagSuggestion(perm); Local<Object> err = ERR_ACCESS_DENIED( env->isolate(), "Access to this API has been restricted. %s", suggestion); Local<Value> perm_string; Local<Value> resource_string; std::string_view perm_str = Permission::PermissionToString(perm); if (!ToV8Value(env->context(), perm_str, env->isolate()) .ToLocal(&perm_string) || !ToV8Value(env->context(), res, env->isolate()) .ToLocal(&resource_string) || err->Set(env->context(), env->permission_string(), perm_string) .IsNothing() || err->Set(env->context(), env->resource_string(), resource_string) .IsNothing()) { return MaybeLocal<Value>(); } return err; } void Permission::ThrowAccessDenied(Environment* env, PermissionScope perm, const std::string_view& res) { Local<Value> err; if (CreateAccessDeniedError(env, perm, res).ToLocal(&err)) { env->isolate()->ThrowException(err); } // If ToLocal returned false, then v8 will have scheduled a // superseding error to be thrown. } void Permission::AsyncThrowAccessDenied(Environment* env, fs::FSReqBase* req_wrap, PermissionScope perm, const std::string_view& res) { Local<Value> err; if (CreateAccessDeniedError(env, perm, res).ToLocal(&err)) { return req_wrap->Reject(err); } // If ToLocal returned false, then v8 will have scheduled a // superseding error to be thrown. } void Permission::EnablePermissions() { if (!enabled_) { enabled_ = true; } } void Permission::EnableWarningOnly() { if (!warning_only_) { warning_only_ = true; } } bool Permission::is_scope_granted(Environment* env, const PermissionScope permission, const std::string_view& res) const { auto perm_node = nodes_.find(permission); bool result = false; if (perm_node != nodes_.end()) { result = perm_node->second->is_granted(env, permission, res); } if (!result && !publishing_) { auto channel_name = GetDiagnosticsChannelName(permission); if (!channel_name.empty()) { auto ch = GetOrCreateChannel(env, permission); if (ch && ch->HasSubscribers()) { publishing_ = true; v8::Isolate* isolate = env->isolate(); v8::HandleScope handle_scope(isolate); v8::Local<v8::Context> context = env->context(); v8::Local<v8::Object> msg = v8::Object::New(isolate, v8::Null(isolate), nullptr, nullptr, 0); const char* perm_str = PermissionToString(permission); msg->Set(context, env->permission_string(), v8::String::NewFromUtf8(isolate, perm_str).ToLocalChecked()) .Check(); msg->Set(context, env->resource_string(), v8::String::NewFromUtf8(isolate, res.data(), v8::NewStringType::kNormal, static_cast<int>(res.size())) .ToLocalChecked()) .Check(); ch->Publish(env, msg); publishing_ = false; } } } return result; } BaseObjectPtr<diagnostics_channel::Channel> Permission::GetOrCreateChannel( Environment* env, PermissionScope scope) const { auto it = channels_.find(scope); if (it != channels_.end()) { // Promote weak ref to strong for the duration of this call. BaseObjectPtr<diagnostics_channel::Channel> ptr(it->second.get()); if (ptr) return ptr; channels_.erase(it); } auto channel_name = GetDiagnosticsChannelName(scope); diagnostics_channel::Channel* ch = diagnostics_channel::Channel::Get(env, channel_name.data()); if (ch != nullptr) { channels_.emplace(scope, BaseObjectWeakPtr<diagnostics_channel::Channel>(ch)); return BaseObjectPtr<diagnostics_channel::Channel>(ch); } return {}; } void Permission::Apply(Environment* env, const std::vector<std::string>& allow, PermissionScope scope) { auto permission = nodes_.find(scope); if (permission != nodes_.end()) { permission->second->Apply(env, allow, scope); } } void Permission::Drop(Environment* env, PermissionScope scope, const std::string_view& param) { auto permission = nodes_.find(scope); if (permission != nodes_.end()) { permission->second->Drop(env, scope, param); } // Publish to diagnostics channel so observers can track drops auto channel_name = GetDiagnosticsChannelName(scope); if (!channel_name.empty() && !publishing_) { auto ch = GetOrCreateChannel(env, scope); if (ch && ch->HasSubscribers()) { publishing_ = true; v8::Isolate* isolate = env->isolate(); v8::HandleScope handle_scope(isolate); v8::Local<v8::Context> context = env->context(); v8::Local<v8::Object> msg = v8::Object::New(isolate, v8::Null(isolate), nullptr, nullptr, 0); const char* perm_str = PermissionToString(scope); msg->Set(context, env->permission_string(), v8::String::NewFromUtf8(isolate, perm_str).ToLocalChecked()) .Check(); msg->Set(context, env->resource_string(), v8::String::NewFromUtf8(isolate, param.data(), v8::NewStringType::kNormal, static_cast<int>(param.size())) .ToLocalChecked()) .Check(); msg->Set(context, FIXED_ONE_BYTE_STRING(isolate, "drop"), v8::Boolean::New(isolate, true)) .Check(); ch->Publish(env, msg); publishing_ = false; } } } void Initialize(Local<Object> target, Local<Value> unused, Local<Context> context, void* priv) { SetMethodNoSideEffect(context, target, "has", Has); SetMethod(context, target, "drop", Drop); target->SetIntegrityLevel(context, IntegrityLevel::kFrozen).FromJust(); } void RegisterExternalReferences(ExternalReferenceRegistry* registry) { registry->Register(Has); registry->Register(Drop); } } // namespace permission } // namespace node NODE_BINDING_CONTEXT_AWARE_INTERNAL(permission, node::permission::Initialize) NODE_BINDING_EXTERNAL_REFERENCE(permission, node::permission::RegisterExternalReferences)