src,permission: make ERR_ACCESS_DENIED more descriptive

This commit also adds a suggestion flag (if exists)
when ERR_ACCESS_DENIED is thrown, so users don't need
to jump into the documentation to see how to manage
that permission error.

PR-URL: https://github.com/nodejs/node/pull/57585
Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
This commit is contained in:
Rafael Gonzaga 2025-04-03 19:24:39 -03:00 committed by GitHub
parent 538c813fa3
commit 24e4a54471
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 55 additions and 16 deletions

View File

@ -59,7 +59,7 @@ static void Has(const FunctionCallbackInfo<Value>& args) {
} // namespace
#define V(Name, label, _) \
#define V(Name, label, _, __) \
if (perm == PermissionScope::k##Name) return #Name;
const char* Permission::PermissionToString(const PermissionScope perm) {
PERMISSIONS(V)
@ -67,7 +67,7 @@ const char* Permission::PermissionToString(const PermissionScope perm) {
}
#undef V
#define V(Name, label, _) \
#define V(Name, label, _, __) \
if (perm == label) return PermissionScope::k##Name;
PermissionScope Permission::StringToPermission(const std::string& perm) {
PERMISSIONS(V)
@ -84,32 +84,47 @@ Permission::Permission() : enabled_(false) {
std::shared_ptr<PermissionBase> inspector =
std::make_shared<InspectorPermission>();
std::shared_ptr<PermissionBase> wasi = std::make_shared<WASIPermission>();
#define V(Name, _, __) \
#define V(Name, _, __, ___) \
nodes_.insert(std::make_pair(PermissionScope::k##Name, fs));
FILESYSTEM_PERMISSIONS(V)
#undef V
#define V(Name, _, __) \
#define V(Name, _, __, ___) \
nodes_.insert(std::make_pair(PermissionScope::k##Name, child_p));
CHILD_PROCESS_PERMISSIONS(V)
#undef V
#define V(Name, _, __) \
#define V(Name, _, __, ___) \
nodes_.insert(std::make_pair(PermissionScope::k##Name, worker_t));
WORKER_THREADS_PERMISSIONS(V)
#undef V
#define V(Name, _, __) \
#define V(Name, _, __, ___) \
nodes_.insert(std::make_pair(PermissionScope::k##Name, inspector));
INSPECTOR_PERMISSIONS(V)
#undef V
#define V(Name, _, __) \
#define V(Name, _, __, ___) \
nodes_.insert(std::make_pair(PermissionScope::k##Name, wasi));
WASI_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) {
Local<Object> err = ERR_ACCESS_DENIED(env->isolate());
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);

View File

@ -15,18 +15,19 @@ class Environment;
namespace permission {
#define FILESYSTEM_PERMISSIONS(V) \
V(FileSystem, "fs", PermissionsRoot) \
V(FileSystemRead, "fs.read", FileSystem) \
V(FileSystemWrite, "fs.write", FileSystem)
V(FileSystem, "fs", PermissionsRoot, "") \
V(FileSystemRead, "fs.read", FileSystem, "--allow-fs-read") \
V(FileSystemWrite, "fs.write", FileSystem, "--allow-fs-write")
#define CHILD_PROCESS_PERMISSIONS(V) V(ChildProcess, "child", PermissionsRoot)
#define CHILD_PROCESS_PERMISSIONS(V) \
V(ChildProcess, "child", PermissionsRoot, "--allow-child-process")
#define WASI_PERMISSIONS(V) V(WASI, "wasi", PermissionsRoot)
#define WASI_PERMISSIONS(V) V(WASI, "wasi", PermissionsRoot, "--allow-wasi")
#define WORKER_THREADS_PERMISSIONS(V) \
V(WorkerThreads, "worker", PermissionsRoot)
V(WorkerThreads, "worker", PermissionsRoot, "--allow-worker")
#define INSPECTOR_PERMISSIONS(V) V(Inspector, "inspector", PermissionsRoot)
#define INSPECTOR_PERMISSIONS(V) V(Inspector, "inspector", PermissionsRoot, "")
#define PERMISSIONS(V) \
FILESYSTEM_PERMISSIONS(V) \
@ -35,7 +36,7 @@ namespace permission {
WORKER_THREADS_PERMISSIONS(V) \
INSPECTOR_PERMISSIONS(V)
#define V(name, _, __) k##name,
#define V(name, _, __, ___) k##name,
enum class PermissionScope {
kPermissionsRoot = -1,
PERMISSIONS(V) kPermissionsCount

View File

@ -14,6 +14,16 @@ const blockedFolder = process.env.BLOCKEDFOLDER;
const allowedFolder = process.env.ALLOWEDFOLDER;
const regularFile = __filename;
// Guarantee the error message suggest the --allow-fs-read
{
fs.readFile(blockedFile, common.expectsError({
message: 'Access to this API has been restricted. Use --allow-fs-read to manage permissions.',
code: 'ERR_ACCESS_DENIED',
permission: 'FileSystemRead',
resource: path.toNamespacedPath(blockedFile),
}));
}
// fs.readFile
{
fs.readFile(blockedFile, common.expectsError({

View File

@ -25,6 +25,15 @@ const relativeProtectedFolder = process.env.RELATIVEBLOCKEDFOLDER;
assert.ok(!process.permission.has('fs.write', blockedFile));
}
// Guarantee the error message suggest the --allow-fs-write
{
fs.writeFile(blockedFile, 'example', common.expectsError({
message: 'Access to this API has been restricted. Use --allow-fs-write to manage permissions.',
code: 'ERR_ACCESS_DENIED',
permission: 'FileSystemWrite',
}));
}
// fs.writeFile
{
assert.throws(() => {

View File

@ -26,6 +26,7 @@ if (process.argv[2] === 'child') {
assert.throws(() => {
childProcess.spawn(process.execPath, ['--version']);
}, common.expectsError({
message: 'Access to this API has been restricted. Use --allow-child-process to manage permissions.',
code: 'ERR_ACCESS_DENIED',
permission: 'ChildProcess',
}));

View File

@ -22,6 +22,7 @@ if (!common.hasCrypto)
const session = new Session();
session.connect();
}, common.expectsError({
message: 'Access to this API has been restricted. ',
code: 'ERR_ACCESS_DENIED',
permission: 'Inspector',
}));

View File

@ -13,6 +13,7 @@ const { WASI } = require('wasi');
preopens: { '/': '/' },
});
}, common.expectsError({
message: 'Access to this API has been restricted. Use --allow-wasi to manage permissions.',
code: 'ERR_ACCESS_DENIED',
permission: 'WASI',
}));

View File

@ -22,6 +22,7 @@ if (isMainThread) {
assert.throws(() => {
new Worker(__filename);
}, common.expectsError({
message: 'Access to this API has been restricted. Use --allow-worker to manage permissions.',
code: 'ERR_ACCESS_DENIED',
permission: 'WorkerThreads',
}));