Skip to content

[v20.x] permission: ignore internalModuleStat on module loading #58185

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: v20.x-staging
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 0 additions & 7 deletions doc/api/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -217,15 +217,8 @@ The initializer module also needs to be allowed. Consider the following example:

```console
$ node --experimental-permission t.js
node:internal/modules/cjs/loader:162
const result = internalModuleStat(filename);
^

Error: Access to this API has been restricted
at stat (node:internal/modules/cjs/loader:162:18)
at Module._findPath (node:internal/modules/cjs/loader:640:16)
at resolveMainPath (node:internal/modules/run_main:15:25)
at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:53:24)
at node:internal/main/run_main_module:23:47 {
code: 'ERR_ACCESS_DENIED',
permission: 'FileSystemRead',
Expand Down
7 changes: 0 additions & 7 deletions doc/api/permissions.md
Original file line number Diff line number Diff line change
Expand Up @@ -500,15 +500,8 @@ will be restricted.

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

Error: Access to this API has been restricted
at stat (node:internal/modules/cjs/loader:171:18)
at Module._findPath (node:internal/modules/cjs/loader:627:16)
at resolveMainPath (node:internal/modules/run_main:19:25)
at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:76:24)
at node:internal/main/run_main_module:23:47 {
code: 'ERR_ACCESS_DENIED',
permission: 'FileSystemRead',
Expand Down
6 changes: 1 addition & 5 deletions lib/internal/modules/cjs/loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,6 @@ const policy = getLazy(
);
const shouldReportRequiredModules = getLazy(() => process.env.WATCH_REPORT_DEPENDENCIES);

const permission = require('internal/process/permission');
const {
vm_dynamic_import_default_internal,
} = internalBinding('symbols');
Expand Down Expand Up @@ -704,11 +703,8 @@ Module._findPath = function(request, paths, isMain) {
// For each path
for (let i = 0; i < paths.length; i++) {
// Don't search further if path doesn't exist
// or doesn't have permission to it
const curPath = paths[i];
if (insidePath && curPath &&
((permission.isEnabled() && !permission.has('fs.read', curPath)) || _stat(curPath) < 1)
) {
if (insidePath && curPath && _stat(curPath) < 1) {
continue;
}

Expand Down
4 changes: 2 additions & 2 deletions src/node_file.cc
Original file line number Diff line number Diff line change
Expand Up @@ -1045,15 +1045,15 @@ static void ExistsSync(const FunctionCallbackInfo<Value>& args) {
}

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

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

if (strlen(*path) != path.length()) {
args.GetReturnValue().Set(Array::New(isolate));
Expand Down
7 changes: 7 additions & 0 deletions test/fixtures/permission/fs-read.js
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,13 @@ const regularFile = __filename;
permission: 'FileSystemRead',
// resource: path.toNamespacedPath(blockedFolder),
}));
assert.throws(() => {
fs.readdirSync(blockedFolder, { recursive: true });
}, common.expectsError({
code: 'ERR_ACCESS_DENIED',
permission: 'FileSystemRead',
resource: path.toNamespacedPath(blockedFolder),
}));
assert.throws(() => {
fs.readdirSync(blockedFolder);
}, common.expectsError({
Expand Down
1 change: 1 addition & 0 deletions test/fixtures/permission/main-module.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
require('./required-module');
1 change: 1 addition & 0 deletions test/fixtures/permission/main-module.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import './required-module.mjs';
1 change: 1 addition & 0 deletions test/fixtures/permission/required-module.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
console.log('ok');
1 change: 1 addition & 0 deletions test/fixtures/permission/required-module.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
console.log('ok');
19 changes: 19 additions & 0 deletions test/parallel/test-permission-fs-internal-module-stat.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// Flags: --expose-internals --experimental-permission --allow-fs-read=test/common* --allow-fs-read=tools* --allow-fs-read=test/parallel* --allow-child-process
'use strict';

const common = require('../common');
common.skipIfWorker();

if (!common.hasCrypto) {
common.skip('no crypto');
}

const { internalBinding } = require('internal/test/binding');
const fixtures = require('../common/fixtures');

const blockedFile = fixtures.path('permission', 'deny', 'protected-file.md');
const internalFsBinding = internalBinding('fs');

{
internalFsBinding.internalModuleStat(internalFsBinding, blockedFile);
}
76 changes: 76 additions & 0 deletions test/parallel/test-permission-fs-require.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// Flags: --experimental-permission --allow-fs-read=* --allow-child-process
'use strict';

const common = require('../common');
common.skipIfWorker();
const fixtures = require('../common/fixtures');

const assert = require('node:assert');
const { spawnSync } = require('node:child_process');

{
const mainModule = fixtures.path('permission', 'main-module.js');
const requiredModule = fixtures.path('permission', 'required-module.js');
const { status, stdout, stderr } = spawnSync(
process.execPath,
[
'--experimental-permission',
'--allow-fs-read', mainModule,
'--allow-fs-read', requiredModule,
mainModule,
]
);

assert.strictEqual(status, 0, stderr.toString());

Check failure on line 24 in test/parallel/test-permission-fs-require.js

View workflow job for this annotation

GitHub Actions / test-linux (ubuntu-24.04)

--- stderr --- node:assert:90 throw new AssertionError(obj); ^ AssertionError [ERR_ASSERTION]: node:internal/modules/cjs/loader:231 const result = internalModuleStat(filename); ^ Error: Access to this API has been restricted at stat (node:internal/modules/cjs/loader:231:18) at Module._findPath (node:internal/modules/cjs/loader:707:34) at Module._resolveFilename (node:internal/modules/cjs/loader:1194:27) at Module._load (node:internal/modules/cjs/loader:1039:27) at Module.require (node:internal/modules/cjs/loader:1294:19) at require (node:internal/modules/helpers:182:18) at Object.<anonymous> (/home/runner/work/node/node/node/test/fixtures/permission/main-module.js:1:1) at Module._compile (node:internal/modules/cjs/loader:1525:14) at Module._extensions..js (node:internal/modules/cjs/loader:1609:10) at Module.load (node:internal/modules/cjs/loader:1271:32) { code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: '/home/runner/work/node/node/node/test/fixtures/permission' } Node.js v20.19.2-pre 1 !== 0 at Object.<anonymous> (/home/runner/work/node/node/node/test/parallel/test-permission-fs-require.js:24:10) at Module._compile (node:internal/modules/cjs/loader:1525:14) at Module._extensions..js (node:internal/modules/cjs/loader:1609:10) at Module.load (node:internal/modules/cjs/loader:1271:32) at Module._load (node:internal/modules/cjs/loader:1092:12) at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:164:12) at node:internal/main/run_main_module:28:49 { generatedMessage: false, code: 'ERR_ASSERTION', actual: 1, expected: 0, operator: 'strictEqual' } Node.js v20.19.2-pre Command: out/Release/node --experimental-permission --allow-fs-read=* --allow-child-process --test-reporter=./tools/github_reporter/index.js --test-reporter-destination=stdout /home/runner/work/node/node/node/test/parallel/test-permission-fs-require.js

Check failure on line 24 in test/parallel/test-permission-fs-require.js

View workflow job for this annotation

GitHub Actions / test-linux (ubuntu-24.04-arm)

--- stderr --- node:assert:90 throw new AssertionError(obj); ^ AssertionError [ERR_ASSERTION]: node:internal/modules/cjs/loader:231 const result = internalModuleStat(filename); ^ Error: Access to this API has been restricted at stat (node:internal/modules/cjs/loader:231:18) at Module._findPath (node:internal/modules/cjs/loader:707:34) at Module._resolveFilename (node:internal/modules/cjs/loader:1194:27) at Module._load (node:internal/modules/cjs/loader:1039:27) at Module.require (node:internal/modules/cjs/loader:1294:19) at require (node:internal/modules/helpers:182:18) at Object.<anonymous> (/home/runner/work/node/node/node/test/fixtures/permission/main-module.js:1:1) at Module._compile (node:internal/modules/cjs/loader:1525:14) at Module._extensions..js (node:internal/modules/cjs/loader:1609:10) at Module.load (node:internal/modules/cjs/loader:1271:32) { code: 'ERR_ACCESS_DENIED', permission: 'FileSystemRead', resource: '/home/runner/work/node/node/node/test/fixtures/permission' } Node.js v20.19.2-pre 1 !== 0 at Object.<anonymous> (/home/runner/work/node/node/node/test/parallel/test-permission-fs-require.js:24:10) at Module._compile (node:internal/modules/cjs/loader:1525:14) at Module._extensions..js (node:internal/modules/cjs/loader:1609:10) at Module.load (node:internal/modules/cjs/loader:1271:32) at Module._load (node:internal/modules/cjs/loader:1092:12) at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:164:12) at node:internal/main/run_main_module:28:49 { generatedMessage: false, code: 'ERR_ASSERTION', actual: 1, expected: 0, operator: 'strictEqual' } Node.js v20.19.2-pre Command: out/Release/node --experimental-permission --allow-fs-read=* --allow-child-process --test-reporter=./tools/github_reporter/index.js --test-reporter-destination=stdout /home/runner/work/node/node/node/test/parallel/test-permission-fs-require.js
assert.strictEqual(stdout.toString(), 'ok\n');
}

{
// When required module is not passed as allowed path
const mainModule = fixtures.path('permission', 'main-module.js');
const { status, stderr } = spawnSync(
process.execPath,
[
'--experimental-permission',
'--allow-fs-read', mainModule,
mainModule,
]
);

assert.strictEqual(status, 1, stderr.toString());
assert.match(stderr.toString(), /Error: Access to this API has been restricted/);
}

{
// ESM loader test
const mainModule = fixtures.path('permission', 'main-module.mjs');
const requiredModule = fixtures.path('permission', 'required-module.mjs');
const { status, stdout, stderr } = spawnSync(
process.execPath,
[
'--experimental-permission',
'--allow-fs-read', mainModule,
'--allow-fs-read', requiredModule,
mainModule,
]
);

assert.strictEqual(status, 0, stderr.toString());
assert.strictEqual(stdout.toString(), 'ok\n');
}

{
// When required module is not passed as allowed path (ESM)
const mainModule = fixtures.path('permission', 'main-module.mjs');
const { status, stderr } = spawnSync(
process.execPath,
[
'--experimental-permission',
'--allow-fs-read', mainModule,
mainModule,
]
);

assert.strictEqual(status, 1, stderr.toString());
assert.match(stderr.toString(), /Error: Access to this API has been restricted/);
}
Loading