Skip to content

Commit 994eb5d

Browse files
committed
permission: ignore internalModuleStat on module loading
This improves Permission Model usage when allowing read access to specifi modules. To achieve that, the permission model check on internalModuleStat has been removed meaning that on module loading, uv_fs_stat is performed on files and folders even when the permission model is enabled. Although a uv_fs_stat is performed, reading/executing the module will still pass by the permission model check. Without this PR when an app tries to --allow-fs-read=./a.js --allow-fs-read=./b.js where `a` attempt to load b, it will fails as it reads $pwd and no permission has been given to this path. PR-URL: nodejs#55797 Backport-PR-URL: nodejs#58185 Reviewed-By: Yagiz Nizipli <[email protected]> Reviewed-By: Ulises Gascón <[email protected]>
1 parent 5357d0b commit 994eb5d

File tree

11 files changed

+552
-194
lines changed

11 files changed

+552
-194
lines changed

doc/api/cli.md

Lines changed: 443 additions & 180 deletions
Large diffs are not rendered by default.

doc/api/permissions.md

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -500,15 +500,8 @@ will be restricted.
500500

501501
```console
502502
$ node --experimental-permission index.js
503-
node:internal/modules/cjs/loader:171
504-
const result = internalModuleStat(filename);
505-
^
506503

507504
Error: Access to this API has been restricted
508-
at stat (node:internal/modules/cjs/loader:171:18)
509-
at Module._findPath (node:internal/modules/cjs/loader:627:16)
510-
at resolveMainPath (node:internal/modules/run_main:19:25)
511-
at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:76:24)
512505
at node:internal/main/run_main_module:23:47 {
513506
code: 'ERR_ACCESS_DENIED',
514507
permission: 'FileSystemRead',

lib/internal/modules/cjs/loader.js

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,6 @@ const policy = getLazy(
164164
);
165165
const shouldReportRequiredModules = getLazy(() => process.env.WATCH_REPORT_DEPENDENCIES);
166166

167-
const permission = require('internal/process/permission');
168167
const {
169168
vm_dynamic_import_default_internal,
170169
} = internalBinding('symbols');
@@ -704,11 +703,8 @@ Module._findPath = function(request, paths, isMain) {
704703
// For each path
705704
for (let i = 0; i < paths.length; i++) {
706705
// Don't search further if path doesn't exist
707-
// or doesn't have permission to it
708706
const curPath = paths[i];
709-
if (insidePath && curPath &&
710-
((permission.isEnabled() && !permission.has('fs.read', curPath)) || _stat(curPath) < 1)
711-
) {
707+
if (insidePath && curPath && _stat(curPath) < 1) {
712708
continue;
713709
}
714710

src/node_file.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1045,15 +1045,15 @@ static void ExistsSync(const FunctionCallbackInfo<Value>& args) {
10451045
}
10461046

10471047
// Used to speed up module loading. Returns an array [string, boolean]
1048+
// Do not expose this function through public API as it doesn't hold
1049+
// Permission Model checks.
10481050
static void InternalModuleReadJSON(const FunctionCallbackInfo<Value>& args) {
10491051
Environment* env = Environment::GetCurrent(args);
10501052
Isolate* isolate = env->isolate();
10511053
uv_loop_t* loop = env->event_loop();
10521054

10531055
CHECK(args[0]->IsString());
10541056
node::Utf8Value path(isolate, args[0]);
1055-
THROW_IF_INSUFFICIENT_PERMISSIONS(
1056-
env, permission::PermissionScope::kFileSystemRead, path.ToStringView());
10571057

10581058
if (strlen(*path) != path.length()) {
10591059
args.GetReturnValue().Set(Array::New(isolate));

test/fixtures/permission/fs-read.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,13 @@ const regularFile = __filename;
281281
permission: 'FileSystemRead',
282282
// resource: path.toNamespacedPath(blockedFolder),
283283
}));
284+
assert.throws(() => {
285+
fs.readdirSync(blockedFolder, { recursive: true });
286+
}, common.expectsError({
287+
code: 'ERR_ACCESS_DENIED',
288+
permission: 'FileSystemRead',
289+
resource: path.toNamespacedPath(blockedFolder),
290+
}));
284291
assert.throws(() => {
285292
fs.readdirSync(blockedFolder);
286293
}, common.expectsError({
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
require('./required-module');
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
import './required-module.mjs';
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
console.log('ok');
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
console.log('ok');
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// Flags: --expose-internals --experimental-permission --allow-fs-read=test/common* --allow-fs-read=tools* --allow-fs-read=test/parallel* --allow-child-process
2+
'use strict';
3+
4+
const common = require('../common');
5+
common.skipIfWorker();
6+
7+
if (!common.hasCrypto) {
8+
common.skip('no crypto');
9+
}
10+
11+
const { internalBinding } = require('internal/test/binding');
12+
const fixtures = require('../common/fixtures');
13+
14+
const blockedFile = fixtures.path('permission', 'deny', 'protected-file.md');
15+
const internalFsBinding = internalBinding('fs');
16+
17+
{
18+
internalFsBinding.internalModuleStat(internalFsBinding, blockedFile);
19+
}

0 commit comments

Comments
 (0)