From 1c2db072b5bf6427948d65b54ec9c8f9564e3bbe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Tue, 10 Jan 2023 14:35:44 +0100 Subject: [PATCH] fix(npm): allow to read package.json if permissions are granted (#17209) This commit changes signature of "deno_core::ModuleLoader::resolve" to pass an enum indicating whether or not we're resolving a specifier for dynamic import. Additionally "CliModuleLoader" was changes to store both "parent permissions" (or "root permissions") as well as "dynamic permissions" that allow to check for permissions in top-level module load an dynamic imports. Then all code paths that have anything to do with Node/npm compat are now checking for permissions which are passed from module loader instance associated with given worker. --- cli/lsp/documents.rs | 4 + cli/module_loader.rs | 58 +++++++---- cli/node/mod.rs | 31 +++++- cli/npm/resolvers/common.rs | 13 ++- cli/npm/resolvers/global.rs | 9 +- cli/npm/resolvers/local.rs | 13 ++- cli/npm/resolvers/mod.rs | 9 +- cli/proc_state.rs | 4 + cli/standalone.rs | 3 +- cli/tests/npm_tests.rs | 7 ++ .../permissions_outside_package/foo/config.js | 4 + .../foo/package.json | 4 + .../npm/permissions_outside_package/main.out | 3 + .../npm/permissions_outside_package/main.ts | 5 + .../1.0.0/index.js | 5 + .../1.0.0/package.json | 5 + cli/tsc/mod.rs | 3 + cli/worker.rs | 8 +- core/bindings.rs | 4 +- core/examples/ts_module_loader.rs | 3 +- core/lib.rs | 1 + core/modules.rs | 98 +++++++++++++------ core/runtime.rs | 11 ++- ext/node/lib.rs | 59 +++++++---- ext/node/package_json.rs | 4 +- ext/node/resolution.rs | 31 +++++- 26 files changed, 306 insertions(+), 93 deletions(-) create mode 100644 cli/tests/testdata/npm/permissions_outside_package/foo/config.js create mode 100644 cli/tests/testdata/npm/permissions_outside_package/foo/package.json create mode 100644 cli/tests/testdata/npm/permissions_outside_package/main.out create mode 100644 cli/tests/testdata/npm/permissions_outside_package/main.ts create mode 100644 cli/tests/testdata/npm/registry/@denotest/permissions-outside-package/1.0.0/index.js create mode 100644 cli/tests/testdata/npm/registry/@denotest/permissions-outside-package/1.0.0/package.json diff --git a/cli/lsp/documents.rs b/cli/lsp/documents.rs index bedd1f9a7a..92dfdf5433 100644 --- a/cli/lsp/documents.rs +++ b/cli/lsp/documents.rs @@ -33,6 +33,7 @@ use deno_core::ModuleSpecifier; use deno_graph::GraphImport; use deno_graph::Resolved; use deno_runtime::deno_node::NodeResolutionMode; +use deno_runtime::permissions::PermissionsContainer; use once_cell::sync::Lazy; use std::collections::BTreeMap; use std::collections::HashMap; @@ -1004,6 +1005,7 @@ impl Documents { referrer, NodeResolutionMode::Types, npm_resolver, + &mut PermissionsContainer::allow_all(), ) .ok() .flatten(), @@ -1040,6 +1042,7 @@ impl Documents { &npm_ref, NodeResolutionMode::Types, npm_resolver, + &mut PermissionsContainer::allow_all(), ) .ok() .flatten(), @@ -1208,6 +1211,7 @@ impl Documents { &npm_ref, NodeResolutionMode::Types, npm_resolver, + &mut PermissionsContainer::allow_all(), ) .ok() .flatten(), diff --git a/cli/module_loader.rs b/cli/module_loader.rs index d452c30cf9..d507a5b154 100644 --- a/cli/module_loader.rs +++ b/cli/module_loader.rs @@ -20,6 +20,7 @@ use deno_core::ModuleSource; use deno_core::ModuleSpecifier; use deno_core::ModuleType; use deno_core::OpState; +use deno_core::ResolutionKind; use deno_core::SourceMapGetter; use deno_runtime::permissions::PermissionsContainer; use std::cell::RefCell; @@ -36,28 +37,38 @@ struct ModuleCodeSource { pub struct CliModuleLoader { pub lib: TsTypeLib, /// The initial set of permissions used to resolve the static imports in the - /// worker. They are decoupled from the worker (dynamic) permissions since - /// read access errors must be raised based on the parent thread permissions. + /// worker. These are "allow all" for main worker, and parent thread + /// permissions for Web Worker. pub root_permissions: PermissionsContainer, + /// Permissions used to resolve dynamic imports, these get passed as + /// "root permissions" for Web Worker. + dynamic_permissions: PermissionsContainer, pub ps: ProcState, } impl CliModuleLoader { - pub fn new(ps: ProcState) -> Rc { + pub fn new( + ps: ProcState, + root_permissions: PermissionsContainer, + dynamic_permissions: PermissionsContainer, + ) -> Rc { Rc::new(CliModuleLoader { lib: ps.options.ts_type_lib_window(), - root_permissions: PermissionsContainer::allow_all(), + root_permissions, + dynamic_permissions, ps, }) } pub fn new_for_worker( ps: ProcState, - permissions: PermissionsContainer, + root_permissions: PermissionsContainer, + dynamic_permissions: PermissionsContainer, ) -> Rc { Rc::new(CliModuleLoader { lib: ps.options.ts_type_lib_worker(), - root_permissions: permissions, + root_permissions, + dynamic_permissions, ps, }) } @@ -138,6 +149,7 @@ impl CliModuleLoader { &self, specifier: &ModuleSpecifier, maybe_referrer: Option, + is_dynamic: bool, ) -> Result { let code_source = if self.ps.npm_resolver.in_npm_package(specifier) { let file_path = specifier.to_file_path().unwrap(); @@ -152,6 +164,11 @@ impl CliModuleLoader { })?; let code = if self.ps.cjs_resolutions.lock().contains(specifier) { + let mut permissions = if is_dynamic { + self.dynamic_permissions.clone() + } else { + self.root_permissions.clone() + }; // translate cjs to esm if it's cjs and inject node globals node::translate_cjs_to_esm( &self.ps.file_fetcher, @@ -160,6 +177,7 @@ impl CliModuleLoader { MediaType::Cjs, &self.ps.npm_resolver, &self.ps.node_analysis_cache, + &mut permissions, )? } else { // only inject node globals for esm @@ -203,28 +221,35 @@ impl ModuleLoader for CliModuleLoader { &self, specifier: &str, referrer: &str, - _is_main: bool, + kind: ResolutionKind, ) -> Result { - self.ps.resolve(specifier, referrer) + let mut permissions = if matches!(kind, ResolutionKind::DynamicImport) { + self.dynamic_permissions.clone() + } else { + self.root_permissions.clone() + }; + self.ps.resolve(specifier, referrer, &mut permissions) } fn load( &self, specifier: &ModuleSpecifier, maybe_referrer: Option, - _is_dynamic: bool, + is_dynamic: bool, ) -> Pin> { // NOTE: this block is async only because of `deno_core` interface // requirements; module was already loaded when constructing module graph // during call to `prepare_load` so we can load it synchronously. - Box::pin(deno_core::futures::future::ready( - self.load_sync(specifier, maybe_referrer), - )) + Box::pin(deno_core::futures::future::ready(self.load_sync( + specifier, + maybe_referrer, + is_dynamic, + ))) } fn prepare_load( &self, - op_state: Rc>, + _op_state: Rc>, specifier: &ModuleSpecifier, _maybe_referrer: Option, is_dynamic: bool, @@ -236,18 +261,15 @@ impl ModuleLoader for CliModuleLoader { let specifier = specifier.clone(); let ps = self.ps.clone(); - let state = op_state.borrow(); - let dynamic_permissions = state.borrow::().clone(); + let dynamic_permissions = self.dynamic_permissions.clone(); let root_permissions = if is_dynamic { - dynamic_permissions.clone() + self.dynamic_permissions.clone() } else { self.root_permissions.clone() }; let lib = self.lib; - drop(state); - async move { ps.prepare_module_load( vec![specifier], diff --git a/cli/node/mod.rs b/cli/node/mod.rs index e6cc222555..aed639bc48 100644 --- a/cli/node/mod.rs +++ b/cli/node/mod.rs @@ -25,12 +25,14 @@ use deno_runtime::deno_node::package_imports_resolve; use deno_runtime::deno_node::package_resolve; use deno_runtime::deno_node::path_to_declaration_path; use deno_runtime::deno_node::NodeModuleKind; +use deno_runtime::deno_node::NodePermissions; use deno_runtime::deno_node::NodeResolutionMode; use deno_runtime::deno_node::PackageJson; use deno_runtime::deno_node::PathClean; use deno_runtime::deno_node::RequireNpmResolver; use deno_runtime::deno_node::DEFAULT_CONDITIONS; use deno_runtime::deno_node::NODE_GLOBAL_THIS_NAME; +use deno_runtime::permissions::PermissionsContainer; use once_cell::sync::Lazy; use regex::Regex; @@ -440,6 +442,7 @@ pub fn node_resolve( referrer: &ModuleSpecifier, mode: NodeResolutionMode, npm_resolver: &dyn RequireNpmResolver, + permissions: &mut dyn NodePermissions, ) -> Result, AnyError> { // Note: if we are here, then the referrer is an esm module // TODO(bartlomieju): skipped "policy" part as we don't plan to support it @@ -481,6 +484,7 @@ pub fn node_resolve( DEFAULT_CONDITIONS, mode, npm_resolver, + permissions, )?; let url = match url { Some(url) => url, @@ -510,6 +514,7 @@ pub fn node_resolve_npm_reference( reference: &NpmPackageReference, mode: NodeResolutionMode, npm_resolver: &NpmPackageResolver, + permissions: &mut dyn NodePermissions, ) -> Result, AnyError> { let package_folder = npm_resolver.resolve_package_folder_from_deno_module(&reference.req)?; @@ -525,6 +530,7 @@ pub fn node_resolve_npm_reference( DEFAULT_CONDITIONS, mode, npm_resolver, + permissions, ) .with_context(|| { format!("Error resolving package config for '{}'", reference) @@ -553,11 +559,13 @@ pub fn node_resolve_binary_export( pkg_req: &NpmPackageReq, bin_name: Option<&str>, npm_resolver: &NpmPackageResolver, + permissions: &mut dyn NodePermissions, ) -> Result { let package_folder = npm_resolver.resolve_package_folder_from_deno_module(pkg_req)?; let package_json_path = package_folder.join("package.json"); - let package_json = PackageJson::load(npm_resolver, package_json_path)?; + let package_json = + PackageJson::load(npm_resolver, permissions, package_json_path)?; let bin = match &package_json.bin { Some(bin) => bin, None => bail!( @@ -665,11 +673,12 @@ fn package_config_resolve( conditions: &[&str], mode: NodeResolutionMode, npm_resolver: &dyn RequireNpmResolver, + permissions: &mut dyn NodePermissions, ) -> Result, AnyError> { let package_json_path = package_dir.join("package.json"); let referrer = ModuleSpecifier::from_directory_path(package_dir).unwrap(); let package_config = - PackageJson::load(npm_resolver, package_json_path.clone())?; + PackageJson::load(npm_resolver, permissions, package_json_path.clone())?; if let Some(exports) = &package_config.exports { let result = package_exports_resolve( &package_json_path, @@ -680,6 +689,7 @@ fn package_config_resolve( conditions, mode, npm_resolver, + permissions, ); match result { Ok(found) => return Ok(Some(found)), @@ -712,7 +722,11 @@ pub fn url_to_node_resolution( if url_str.starts_with("http") { Ok(NodeResolution::Esm(url)) } else if url_str.ends_with(".js") || url_str.ends_with(".d.ts") { - let package_config = get_closest_package_json(&url, npm_resolver)?; + let package_config = get_closest_package_json( + &url, + npm_resolver, + &mut PermissionsContainer::allow_all(), + )?; if package_config.typ == "module" { Ok(NodeResolution::Esm(url)) } else { @@ -786,6 +800,7 @@ fn module_resolve( conditions: &[&str], mode: NodeResolutionMode, npm_resolver: &dyn RequireNpmResolver, + permissions: &mut dyn NodePermissions, ) -> Result, AnyError> { // note: if we're here, the referrer is an esm module let url = if should_be_treated_as_relative_or_absolute_path(specifier) { @@ -811,6 +826,7 @@ fn module_resolve( conditions, mode, npm_resolver, + permissions, ) .map(|p| ModuleSpecifier::from_file_path(p).unwrap())?, ) @@ -824,6 +840,7 @@ fn module_resolve( conditions, mode, npm_resolver, + permissions, )? .map(|p| ModuleSpecifier::from_file_path(p).unwrap()) }; @@ -879,6 +896,7 @@ pub fn translate_cjs_to_esm( media_type: MediaType, npm_resolver: &NpmPackageResolver, node_analysis_cache: &NodeAnalysisCache, + permissions: &mut dyn NodePermissions, ) -> Result { fn perform_cjs_analysis( analysis_cache: &NodeAnalysisCache, @@ -956,6 +974,7 @@ pub fn translate_cjs_to_esm( &["deno", "require", "default"], NodeResolutionMode::Execution, npm_resolver, + permissions, )?; let reexport_specifier = ModuleSpecifier::from_file_path(resolved_reexport).unwrap(); @@ -1027,6 +1046,7 @@ fn resolve( conditions: &[&str], mode: NodeResolutionMode, npm_resolver: &dyn RequireNpmResolver, + permissions: &mut dyn NodePermissions, ) -> Result { if specifier.starts_with('/') { todo!(); @@ -1056,7 +1076,7 @@ fn resolve( let package_json_path = module_dir.join("package.json"); if package_json_path.exists() { let package_json = - PackageJson::load(npm_resolver, package_json_path.clone())?; + PackageJson::load(npm_resolver, permissions, package_json_path.clone())?; if let Some(exports) = &package_json.exports { return package_exports_resolve( @@ -1068,6 +1088,7 @@ fn resolve( conditions, mode, npm_resolver, + permissions, ); } @@ -1080,7 +1101,7 @@ fn resolve( let package_json_path = d.join("package.json"); if package_json_path.exists() { let package_json = - PackageJson::load(npm_resolver, package_json_path)?; + PackageJson::load(npm_resolver, permissions, package_json_path)?; if let Some(main) = package_json.main(NodeModuleKind::Cjs) { return Ok(d.join(main).clean()); } diff --git a/cli/npm/resolvers/common.rs b/cli/npm/resolvers/common.rs index ff8a63f9b8..7fe9c3fa49 100644 --- a/cli/npm/resolvers/common.rs +++ b/cli/npm/resolvers/common.rs @@ -10,6 +10,7 @@ use deno_core::error::AnyError; use deno_core::futures; use deno_core::futures::future::BoxFuture; use deno_core::url::Url; +use deno_runtime::deno_node::NodePermissions; use deno_runtime::deno_node::NodeResolutionMode; use crate::args::Lockfile; @@ -54,7 +55,11 @@ pub trait InnerNpmPackageResolver: Send + Sync { fn cache_packages(&self) -> BoxFuture<'static, Result<(), AnyError>>; - fn ensure_read_permission(&self, path: &Path) -> Result<(), AnyError>; + fn ensure_read_permission( + &self, + permissions: &mut dyn NodePermissions, + path: &Path, + ) -> Result<(), AnyError>; fn snapshot(&self) -> NpmResolutionSnapshot; @@ -103,6 +108,7 @@ pub async fn cache_packages( } pub fn ensure_registry_read_permission( + permissions: &mut dyn NodePermissions, registry_path: &Path, path: &Path, ) -> Result<(), AnyError> { @@ -126,10 +132,7 @@ pub fn ensure_registry_read_permission( } } - Err(deno_core::error::custom_error( - "PermissionDenied", - format!("Reading {} is not allowed", path.display()), - )) + permissions.check_read(path) } /// Gets the corresponding @types package for the provided package name. diff --git a/cli/npm/resolvers/global.rs b/cli/npm/resolvers/global.rs index a42ccdd532..d73ccbe9b6 100644 --- a/cli/npm/resolvers/global.rs +++ b/cli/npm/resolvers/global.rs @@ -12,6 +12,7 @@ use deno_core::error::AnyError; use deno_core::futures::future::BoxFuture; use deno_core::futures::FutureExt; use deno_core::url::Url; +use deno_runtime::deno_node::NodePermissions; use deno_runtime::deno_node::NodeResolutionMode; use crate::args::Lockfile; @@ -154,9 +155,13 @@ impl InnerNpmPackageResolver for GlobalNpmPackageResolver { async move { cache_packages_in_resolver(&resolver).await }.boxed() } - fn ensure_read_permission(&self, path: &Path) -> Result<(), AnyError> { + fn ensure_read_permission( + &self, + permissions: &mut dyn NodePermissions, + path: &Path, + ) -> Result<(), AnyError> { let registry_path = self.cache.registry_folder(&self.registry_url); - ensure_registry_read_permission(®istry_path, path) + ensure_registry_read_permission(permissions, ®istry_path, path) } fn snapshot(&self) -> NpmResolutionSnapshot { diff --git a/cli/npm/resolvers/local.rs b/cli/npm/resolvers/local.rs index 04539e4622..b702d3bb35 100644 --- a/cli/npm/resolvers/local.rs +++ b/cli/npm/resolvers/local.rs @@ -19,6 +19,7 @@ use deno_core::futures::future::BoxFuture; use deno_core::futures::FutureExt; use deno_core::url::Url; use deno_runtime::deno_core::futures; +use deno_runtime::deno_node::NodePermissions; use deno_runtime::deno_node::NodeResolutionMode; use deno_runtime::deno_node::PackageJson; use tokio::task::JoinHandle; @@ -245,8 +246,16 @@ impl InnerNpmPackageResolver for LocalNpmPackageResolver { .boxed() } - fn ensure_read_permission(&self, path: &Path) -> Result<(), AnyError> { - ensure_registry_read_permission(&self.root_node_modules_path, path) + fn ensure_read_permission( + &self, + permissions: &mut dyn NodePermissions, + path: &Path, + ) -> Result<(), AnyError> { + ensure_registry_read_permission( + permissions, + &self.root_node_modules_path, + path, + ) } fn snapshot(&self) -> NpmResolutionSnapshot { diff --git a/cli/npm/resolvers/mod.rs b/cli/npm/resolvers/mod.rs index 023ef625a0..767187f5e5 100644 --- a/cli/npm/resolvers/mod.rs +++ b/cli/npm/resolvers/mod.rs @@ -11,6 +11,7 @@ use deno_core::error::custom_error; use deno_core::error::AnyError; use deno_core::parking_lot::Mutex; use deno_core::serde_json; +use deno_runtime::deno_node::NodePermissions; use deno_runtime::deno_node::NodeResolutionMode; use deno_runtime::deno_node::PathClean; use deno_runtime::deno_node::RequireNpmResolver; @@ -367,8 +368,12 @@ impl RequireNpmResolver for NpmPackageResolver { .is_ok() } - fn ensure_read_permission(&self, path: &Path) -> Result<(), AnyError> { - self.inner.ensure_read_permission(path) + fn ensure_read_permission( + &self, + permissions: &mut dyn NodePermissions, + path: &Path, + ) -> Result<(), AnyError> { + self.inner.ensure_read_permission(permissions, path) } } diff --git a/cli/proc_state.rs b/cli/proc_state.rs index 0dd97b5e3a..bbfc3cbada 100644 --- a/cli/proc_state.rs +++ b/cli/proc_state.rs @@ -532,6 +532,7 @@ impl ProcState { &self, specifier: &str, referrer: &str, + permissions: &mut PermissionsContainer, ) -> Result { if let Ok(referrer) = deno_core::resolve_url_or_path(referrer) { if self.npm_resolver.in_npm_package(&referrer) { @@ -542,6 +543,7 @@ impl ProcState { &referrer, NodeResolutionMode::Execution, &self.npm_resolver, + permissions, )) .with_context(|| { format!("Could not resolve '{}' from '{}'.", specifier, referrer) @@ -575,6 +577,7 @@ impl ProcState { &reference, NodeResolutionMode::Execution, &self.npm_resolver, + permissions, )) .with_context(|| format!("Could not resolve '{}'.", reference)); } else { @@ -618,6 +621,7 @@ impl ProcState { &reference, deno_runtime::deno_node::NodeResolutionMode::Execution, &self.npm_resolver, + permissions, )) .with_context(|| format!("Could not resolve '{}'.", reference)); } diff --git a/cli/standalone.rs b/cli/standalone.rs index 2b0a77e18c..18d134293f 100644 --- a/cli/standalone.rs +++ b/cli/standalone.rs @@ -23,6 +23,7 @@ use deno_core::url::Url; use deno_core::v8_set_flags; use deno_core::ModuleLoader; use deno_core::ModuleSpecifier; +use deno_core::ResolutionKind; use deno_graph::source::Resolver; use deno_runtime::deno_broadcast_channel::InMemoryBroadcastChannel; use deno_runtime::deno_tls::rustls_pemfile; @@ -137,7 +138,7 @@ impl ModuleLoader for EmbeddedModuleLoader { &self, specifier: &str, referrer: &str, - _is_main: bool, + _kind: ResolutionKind, ) -> Result { // Try to follow redirects when resolving. let referrer = match self.eszip.get_module(referrer) { diff --git a/cli/tests/npm_tests.rs b/cli/tests/npm_tests.rs index aa0563ef93..c0ff56f2d7 100644 --- a/cli/tests/npm_tests.rs +++ b/cli/tests/npm_tests.rs @@ -360,6 +360,13 @@ mod npm { exit_code: 1, }); + itest!(permissions_outside_package { + args: "run --allow-read npm/permissions_outside_package/main.ts", + output: "npm/permissions_outside_package/main.out", + envs: env_vars_for_npm_tests(), + http_server: true, + }); + #[test] fn parallel_downloading() { let (out, _err) = util::run_and_collect_output_with_args( diff --git a/cli/tests/testdata/npm/permissions_outside_package/foo/config.js b/cli/tests/testdata/npm/permissions_outside_package/foo/config.js new file mode 100644 index 0000000000..e667790d26 --- /dev/null +++ b/cli/tests/testdata/npm/permissions_outside_package/foo/config.js @@ -0,0 +1,4 @@ +module.exports = { + "name": "foobar", + "version": "0.0.1", +}; diff --git a/cli/tests/testdata/npm/permissions_outside_package/foo/package.json b/cli/tests/testdata/npm/permissions_outside_package/foo/package.json new file mode 100644 index 0000000000..cc049e6ce9 --- /dev/null +++ b/cli/tests/testdata/npm/permissions_outside_package/foo/package.json @@ -0,0 +1,4 @@ +{ + "name": "foobar", + "version": "0.0.1" +} diff --git a/cli/tests/testdata/npm/permissions_outside_package/main.out b/cli/tests/testdata/npm/permissions_outside_package/main.out new file mode 100644 index 0000000000..4edf66ae9e --- /dev/null +++ b/cli/tests/testdata/npm/permissions_outside_package/main.out @@ -0,0 +1,3 @@ +Download http://localhost:4545/npm/registry/@denotest/permissions-outside-package +Download http://localhost:4545/npm/registry/@denotest/permissions-outside-package/1.0.0.tgz +{ name: "foobar", version: "0.0.1" } diff --git a/cli/tests/testdata/npm/permissions_outside_package/main.ts b/cli/tests/testdata/npm/permissions_outside_package/main.ts new file mode 100644 index 0000000000..b0b82b6260 --- /dev/null +++ b/cli/tests/testdata/npm/permissions_outside_package/main.ts @@ -0,0 +1,5 @@ +import { loadConfigFile } from "npm:@denotest/permissions-outside-package"; + +const fileName = `${Deno.cwd()}/npm/permissions_outside_package/foo/config.js`; +const config = loadConfigFile(fileName); +console.log(config); diff --git a/cli/tests/testdata/npm/registry/@denotest/permissions-outside-package/1.0.0/index.js b/cli/tests/testdata/npm/registry/@denotest/permissions-outside-package/1.0.0/index.js new file mode 100644 index 0000000000..ec854713f0 --- /dev/null +++ b/cli/tests/testdata/npm/registry/@denotest/permissions-outside-package/1.0.0/index.js @@ -0,0 +1,5 @@ +function loadConfigFile(fileName) { + return require(fileName); +} + +module.exports.loadConfigFile = loadConfigFile; \ No newline at end of file diff --git a/cli/tests/testdata/npm/registry/@denotest/permissions-outside-package/1.0.0/package.json b/cli/tests/testdata/npm/registry/@denotest/permissions-outside-package/1.0.0/package.json new file mode 100644 index 0000000000..447a119e43 --- /dev/null +++ b/cli/tests/testdata/npm/registry/@denotest/permissions-outside-package/1.0.0/package.json @@ -0,0 +1,5 @@ +{ + "name": "@denotest/permissions-outside-package", + "version": "1.0.0", + "main": "./index.js" +} diff --git a/cli/tsc/mod.rs b/cli/tsc/mod.rs index c08b4e1aeb..6ea037522d 100644 --- a/cli/tsc/mod.rs +++ b/cli/tsc/mod.rs @@ -33,6 +33,7 @@ use deno_core::RuntimeOptions; use deno_core::Snapshot; use deno_graph::Resolved; use deno_runtime::deno_node::NodeResolutionMode; +use deno_runtime::permissions::PermissionsContainer; use once_cell::sync::Lazy; use std::borrow::Cow; use std::collections::HashMap; @@ -647,6 +648,7 @@ fn op_resolve( &referrer, NodeResolutionMode::Types, npm_resolver, + &mut PermissionsContainer::allow_all(), ) .ok() .flatten(), @@ -703,6 +705,7 @@ pub fn resolve_npm_package_reference_types( npm_ref, NodeResolutionMode::Types, npm_resolver, + &mut PermissionsContainer::allow_all(), )?; Ok(NodeResolution::into_specifier_and_media_type( maybe_resolution, diff --git a/cli/worker.rs b/cli/worker.rs index 2d29a7a53d..2f8a9b6873 100644 --- a/cli/worker.rs +++ b/cli/worker.rs @@ -459,6 +459,7 @@ async fn create_main_worker_internal( &package_ref.req, package_ref.sub_path.as_deref(), &ps.npm_resolver, + &mut PermissionsContainer::allow_all(), )?; let is_main_cjs = matches!(node_resolution, node::NodeResolution::CommonJs(_)); @@ -473,7 +474,11 @@ async fn create_main_worker_internal( (main_module, false) }; - let module_loader = CliModuleLoader::new(ps.clone()); + let module_loader = CliModuleLoader::new( + ps.clone(), + PermissionsContainer::allow_all(), + permissions.clone(), + ); let maybe_inspector_server = ps.maybe_inspector_server.clone(); @@ -649,6 +654,7 @@ fn create_web_worker_callback( let module_loader = CliModuleLoader::new_for_worker( ps.clone(), args.parent_permissions.clone(), + args.permissions.clone(), ); let create_web_worker_cb = create_web_worker_callback(ps.clone(), stdio.clone()); diff --git a/core/bindings.rs b/core/bindings.rs index b9285a402d..6707f115c9 100644 --- a/core/bindings.rs +++ b/core/bindings.rs @@ -13,6 +13,7 @@ use crate::modules::parse_import_assertions; use crate::modules::validate_import_assertions; use crate::modules::ImportAssertionsKind; use crate::modules::ModuleMap; +use crate::modules::ResolutionKind; use crate::ops::OpCtx; use crate::runtime::SnapshotOptions; use crate::JsRuntime; @@ -378,7 +379,8 @@ fn import_meta_resolve( return; } - match loader.resolve(&specifier_str, &referrer, false) { + match loader.resolve(&specifier_str, &referrer, ResolutionKind::DynamicImport) + { Ok(resolved) => { let resolved_val = serde_v8::to_v8(scope, resolved.as_str()).unwrap(); rv.set(resolved_val); diff --git a/core/examples/ts_module_loader.rs b/core/examples/ts_module_loader.rs index d9b32d5be2..c78c1f8687 100644 --- a/core/examples/ts_module_loader.rs +++ b/core/examples/ts_module_loader.rs @@ -21,6 +21,7 @@ use deno_core::ModuleSource; use deno_core::ModuleSourceFuture; use deno_core::ModuleSpecifier; use deno_core::ModuleType; +use deno_core::ResolutionKind; use deno_core::RuntimeOptions; use futures::FutureExt; @@ -31,7 +32,7 @@ impl ModuleLoader for TypescriptModuleLoader { &self, specifier: &str, referrer: &str, - _is_main: bool, + _kind: ResolutionKind, ) -> Result { Ok(resolve_import(specifier, referrer)?) } diff --git a/core/lib.rs b/core/lib.rs index d764718a2c..308724fdd0 100644 --- a/core/lib.rs +++ b/core/lib.rs @@ -77,6 +77,7 @@ pub use crate::modules::ModuleSource; pub use crate::modules::ModuleSourceFuture; pub use crate::modules::ModuleType; pub use crate::modules::NoopModuleLoader; +pub use crate::modules::ResolutionKind; pub use crate::normalize_path::normalize_path; pub use crate::ops::Op; pub use crate::ops::OpAsyncFuture; diff --git a/core/modules.rs b/core/modules.rs index 8abced2009..917649c85b 100644 --- a/core/modules.rs +++ b/core/modules.rs @@ -202,6 +202,20 @@ pub type ModuleSourceFuture = dyn Future>; type ModuleLoadFuture = dyn Future>; +pub enum ResolutionKind { + /// This kind is used in only one situation: when a module is loaded via + /// `JsRuntime::load_main_module` and is the top-level module, ie. the one + /// passed as an argument to `JsRuntime::load_main_module`. + MainModule, + /// This kind is returned for all other modules during module load, that are + /// static imports. + Import, + /// This kind is returned for all modules that are loaded as a result of a + /// call to `import()` API (ie. top-level module as well as all its + /// dependencies, and any other `import()` calls from that load). + DynamicImport, +} + pub trait ModuleLoader { /// Returns an absolute URL. /// When implementing an spec-complaint VM, this should be exactly the @@ -210,11 +224,14 @@ pub trait ModuleLoader { /// /// `is_main` can be used to resolve from current working directory or /// apply import map for child imports. + /// + /// `is_dyn_import` can be used to check permissions or deny + /// dynamic imports altogether. fn resolve( &self, specifier: &str, referrer: &str, - _is_main: bool, + kind: ResolutionKind, ) -> Result; /// Given ModuleSpecifier, load its source code. @@ -256,7 +273,7 @@ impl ModuleLoader for NoopModuleLoader { &self, _specifier: &str, _referrer: &str, - _is_main: bool, + _kind: ResolutionKind, ) -> Result { Err(generic_error("Module loading is not supported")) } @@ -284,7 +301,7 @@ impl ModuleLoader for FsModuleLoader { &self, specifier: &str, referrer: &str, - _is_main: bool, + _kind: ResolutionKind, ) -> Result { Ok(resolve_import(specifier, referrer)?) } @@ -446,14 +463,16 @@ impl RecursiveModuleLoad { fn resolve_root(&self) -> Result { match self.init { LoadInit::Main(ref specifier) => { - self.loader.resolve(specifier, ".", true) + self + .loader + .resolve(specifier, ".", ResolutionKind::MainModule) } LoadInit::Side(ref specifier) => { - self.loader.resolve(specifier, ".", false) - } - LoadInit::DynamicImport(ref specifier, ref referrer, _) => { - self.loader.resolve(specifier, referrer, false) + self.loader.resolve(specifier, ".", ResolutionKind::Import) } + LoadInit::DynamicImport(ref specifier, ref referrer, _) => self + .loader + .resolve(specifier, referrer, ResolutionKind::DynamicImport), } } @@ -461,15 +480,25 @@ impl RecursiveModuleLoad { let op_state = self.op_state.clone(); let (module_specifier, maybe_referrer) = match self.init { LoadInit::Main(ref specifier) => { - let spec = self.loader.resolve(specifier, ".", true)?; + let spec = + self + .loader + .resolve(specifier, ".", ResolutionKind::MainModule)?; (spec, None) } LoadInit::Side(ref specifier) => { - let spec = self.loader.resolve(specifier, ".", false)?; + let spec = + self + .loader + .resolve(specifier, ".", ResolutionKind::Import)?; (spec, None) } LoadInit::DynamicImport(ref specifier, ref referrer, _) => { - let spec = self.loader.resolve(specifier, referrer, false)?; + let spec = self.loader.resolve( + specifier, + referrer, + ResolutionKind::DynamicImport, + )?; (spec, Some(referrer.to_string())) } }; @@ -537,6 +566,7 @@ impl RecursiveModuleLoad { self.is_currently_loading_main_module(), &module_source.module_url_found, &module_source.code, + self.is_dynamic_import(), )? } ModuleType::Json => self.module_map_rc.borrow_mut().new_json_module( @@ -868,6 +898,7 @@ impl ModuleMap { main: bool, name: &str, source: &[u8], + is_dynamic_import: bool, ) -> Result { let name_str = v8::String::new(scope, name).unwrap(); let source_str = @@ -918,11 +949,18 @@ impl ModuleMap { return Err(ModuleError::Exception(exception)); } - let module_specifier = - match self.loader.resolve(&import_specifier, name, false) { - Ok(s) => s, - Err(e) => return Err(ModuleError::Other(e)), - }; + let module_specifier = match self.loader.resolve( + &import_specifier, + name, + if is_dynamic_import { + ResolutionKind::DynamicImport + } else { + ResolutionKind::Import + }, + ) { + Ok(s) => s, + Err(e) => return Err(ModuleError::Other(e)), + }; let asserted_module_type = get_asserted_module_type_from_assertions(&assertions); let request = ModuleRequest { @@ -1082,10 +1120,11 @@ impl ModuleMap { .borrow_mut() .dynamic_import_map .insert(load.id, resolver_handle); - let resolve_result = module_map_rc - .borrow() - .loader - .resolve(specifier, referrer, false); + let resolve_result = module_map_rc.borrow().loader.resolve( + specifier, + referrer, + ResolutionKind::DynamicImport, + ); let fut = match resolve_result { Ok(module_specifier) => { if module_map_rc @@ -1121,7 +1160,7 @@ impl ModuleMap { ) -> Option> { let resolved_specifier = self .loader - .resolve(specifier, referrer, false) + .resolve(specifier, referrer, ResolutionKind::Import) .expect("Module should have been already resolved"); let module_type = @@ -1332,7 +1371,7 @@ import "/a.js"; &self, specifier: &str, referrer: &str, - _is_root: bool, + _kind: ResolutionKind, ) -> Result { let referrer = if referrer == "." { "file:///" @@ -1448,7 +1487,7 @@ import "/a.js"; &self, specifier: &str, referrer: &str, - _is_main: bool, + _kind: ResolutionKind, ) -> Result { self.count.fetch_add(1, Ordering::Relaxed); assert_eq!(specifier, "./b.js"); @@ -1521,6 +1560,7 @@ import "/a.js"; let control = 42; Deno.core.ops.op_test(control); "#, + false, ) .unwrap(); @@ -1540,6 +1580,7 @@ import "/a.js"; false, "file:///b.js", b"export function b() { return 'b' }", + false, ) .unwrap(); let imports = module_map.get_requested_modules(mod_b).unwrap(); @@ -1570,7 +1611,7 @@ import "/a.js"; &self, specifier: &str, referrer: &str, - _is_main: bool, + _kind: ResolutionKind, ) -> Result { self.count.fetch_add(1, Ordering::Relaxed); assert_eq!(specifier, "./b.json"); @@ -1627,6 +1668,7 @@ import "/a.js"; assert(jsonData.a == "b"); assert(jsonData.c.d == 10); "#, + false, ) .unwrap(); @@ -1673,7 +1715,7 @@ import "/a.js"; &self, specifier: &str, referrer: &str, - _is_main: bool, + _kind: ResolutionKind, ) -> Result { self.count.fetch_add(1, Ordering::Relaxed); assert_eq!(specifier, "/foo.js"); @@ -1733,7 +1775,7 @@ import "/a.js"; &self, specifier: &str, referrer: &str, - _is_main: bool, + _kind: ResolutionKind, ) -> Result { let c = self.resolve_count.fetch_add(1, Ordering::Relaxed); assert!(c < 7); @@ -1866,7 +1908,7 @@ import "/a.js"; &self, specifier: &str, referrer: &str, - _is_main: bool, + _kind: ResolutionKind, ) -> Result { self.resolve_count.fetch_add(1, Ordering::Relaxed); let s = resolve_import(specifier, referrer).unwrap(); @@ -2242,7 +2284,7 @@ if (import.meta.url != 'file:///main_with_code.js') throw Error(); &self, specifier: &str, referrer: &str, - _is_main: bool, + _kind: ResolutionKind, ) -> Result { let s = resolve_import(specifier, referrer).unwrap(); Ok(s) diff --git a/core/runtime.rs b/core/runtime.rs index baa7de1a14..6a6bce3292 100644 --- a/core/runtime.rs +++ b/core/runtime.rs @@ -2038,6 +2038,7 @@ impl JsRuntime { true, specifier.as_str(), code.as_bytes(), + false, ) .map_err(|e| match e { ModuleError::Exception(exception) => { @@ -2097,6 +2098,7 @@ impl JsRuntime { false, specifier.as_str(), code.as_bytes(), + false, ) .map_err(|e| match e { ModuleError::Exception(exception) => { @@ -2492,6 +2494,7 @@ pub mod tests { use crate::modules::ModuleSource; use crate::modules::ModuleSourceFuture; use crate::modules::ModuleType; + use crate::modules::ResolutionKind; use crate::ZeroCopyBuf; use deno_ops::op; use futures::future::lazy; @@ -3132,7 +3135,7 @@ pub mod tests { &self, specifier: &str, referrer: &str, - _is_main: bool, + _kind: ResolutionKind, ) -> Result { assert_eq!(specifier, "file:///main.js"); assert_eq!(referrer, "."); @@ -3302,7 +3305,7 @@ pub mod tests { &self, specifier: &str, referrer: &str, - _is_main: bool, + _kind: ResolutionKind, ) -> Result { assert_eq!(specifier, "file:///main.js"); assert_eq!(referrer, "."); @@ -3869,7 +3872,7 @@ Deno.core.ops.op_async_serialize_object_with_numbers_as_keys({ &self, specifier: &str, referrer: &str, - _is_main: bool, + _kind: ResolutionKind, ) -> Result { assert_eq!(specifier, "file:///main.js"); assert_eq!(referrer, "."); @@ -4001,7 +4004,7 @@ Deno.core.ops.op_async_serialize_object_with_numbers_as_keys({ &self, specifier: &str, referrer: &str, - _is_main: bool, + _kind: ResolutionKind, ) -> Result { assert_eq!(specifier, "file:///main.js"); assert_eq!(referrer, "."); diff --git a/ext/node/lib.rs b/ext/node/lib.rs index a670586d17..8a36e95fa2 100644 --- a/ext/node/lib.rs +++ b/ext/node/lib.rs @@ -53,7 +53,11 @@ pub trait RequireNpmResolver { fn in_npm_package(&self, path: &Path) -> bool; - fn ensure_read_permission(&self, path: &Path) -> Result<(), AnyError>; + fn ensure_read_permission( + &self, + permissions: &mut dyn NodePermissions, + path: &Path, + ) -> Result<(), AnyError>; } pub const MODULE_ES_SHIM: &str = include_str!("./module_es_shim.js"); @@ -95,7 +99,7 @@ pub fn init( op_require_is_request_relative::decl(), op_require_resolve_lookup_paths::decl(), op_require_try_self_parent_path::decl::

(), - op_require_try_self::decl(), + op_require_try_self::decl::

(), op_require_real_path::decl::

(), op_require_path_is_absolute::decl(), op_require_path_dirname::decl(), @@ -104,9 +108,9 @@ pub fn init( op_require_path_basename::decl(), op_require_read_file::decl::

(), op_require_as_file_path::decl(), - op_require_resolve_exports::decl(), + op_require_resolve_exports::decl::

(), op_require_read_closest_package_json::decl::

(), - op_require_read_package_scope::decl(), + op_require_read_package_scope::decl::

(), op_require_package_imports_resolve::decl::

(), op_require_break_on_next_statement::decl(), ]) @@ -130,11 +134,8 @@ where let resolver = state.borrow::>(); resolver.clone() }; - if resolver.ensure_read_permission(file_path).is_ok() { - return Ok(()); - } - - state.borrow_mut::

().check_read(file_path) + let permissions = state.borrow_mut::

(); + resolver.ensure_read_permission(permissions, file_path) } #[op] @@ -459,19 +460,24 @@ where } #[op] -fn op_require_try_self( +fn op_require_try_self

( state: &mut OpState, parent_path: Option, request: String, -) -> Result, AnyError> { +) -> Result, AnyError> +where + P: NodePermissions + 'static, +{ if parent_path.is_none() { return Ok(None); } let resolver = state.borrow::>().clone(); + let permissions = state.borrow_mut::

(); let pkg = resolution::get_package_scope_config( &Url::from_file_path(parent_path.unwrap()).unwrap(), &*resolver, + permissions, ) .ok(); if pkg.is_none() { @@ -508,6 +514,7 @@ fn op_require_try_self( resolution::REQUIRE_CONDITIONS, NodeResolutionMode::Execution, &*resolver, + permissions, ) .map(|r| Some(r.to_string_lossy().to_string())) } else { @@ -540,7 +547,7 @@ pub fn op_require_as_file_path(file_or_url: String) -> String { } #[op] -fn op_require_resolve_exports( +fn op_require_resolve_exports

( state: &mut OpState, uses_local_node_modules_dir: bool, modules_path: String, @@ -548,8 +555,12 @@ fn op_require_resolve_exports( name: String, expansion: String, parent_path: String, -) -> Result, AnyError> { +) -> Result, AnyError> +where + P: NodePermissions + 'static, +{ let resolver = state.borrow::>().clone(); + let permissions = state.borrow_mut::

(); let pkg_path = if resolver.in_npm_package(&PathBuf::from(&modules_path)) && !uses_local_node_modules_dir @@ -560,6 +571,7 @@ fn op_require_resolve_exports( }; let pkg = PackageJson::load( &*resolver, + permissions, PathBuf::from(&pkg_path).join("package.json"), )?; @@ -574,6 +586,7 @@ fn op_require_resolve_exports( resolution::REQUIRE_CONDITIONS, NodeResolutionMode::Execution, &*resolver, + permissions, ) .map(|r| Some(r.to_string_lossy().to_string())) } else { @@ -594,20 +607,26 @@ where PathBuf::from(&filename).parent().unwrap(), )?; let resolver = state.borrow::>().clone(); + let permissions = state.borrow_mut::

(); resolution::get_closest_package_json( &Url::from_file_path(filename).unwrap(), &*resolver, + permissions, ) } #[op] -fn op_require_read_package_scope( +fn op_require_read_package_scope

( state: &mut OpState, package_json_path: String, -) -> Option { +) -> Option +where + P: NodePermissions + 'static, +{ let resolver = state.borrow::>().clone(); + let permissions = state.borrow_mut::

(); let package_json_path = PathBuf::from(package_json_path); - PackageJson::load(&*resolver, package_json_path).ok() + PackageJson::load(&*resolver, permissions, package_json_path).ok() } #[op] @@ -622,7 +641,12 @@ where let parent_path = PathBuf::from(&parent_filename); ensure_read_permission::

(state, &parent_path)?; let resolver = state.borrow::>().clone(); - let pkg = PackageJson::load(&*resolver, parent_path.join("package.json"))?; + let permissions = state.borrow_mut::

(); + let pkg = PackageJson::load( + &*resolver, + permissions, + parent_path.join("package.json"), + )?; if pkg.imports.is_some() { let referrer = @@ -634,6 +658,7 @@ where resolution::REQUIRE_CONDITIONS, NodeResolutionMode::Execution, &*resolver, + permissions, ) .map(|r| Some(Url::from_file_path(r).unwrap().to_string())); state.put(resolver); diff --git a/ext/node/package_json.rs b/ext/node/package_json.rs index f0a2b4f4dc..5894b88313 100644 --- a/ext/node/package_json.rs +++ b/ext/node/package_json.rs @@ -1,6 +1,7 @@ // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. use crate::NodeModuleKind; +use crate::NodePermissions; use super::RequireNpmResolver; use deno_core::anyhow; @@ -47,9 +48,10 @@ impl PackageJson { pub fn load( resolver: &dyn RequireNpmResolver, + permissions: &mut dyn NodePermissions, path: PathBuf, ) -> Result { - resolver.ensure_read_permission(&path)?; + resolver.ensure_read_permission(permissions, &path)?; Self::load_skip_read_permission(path) } diff --git a/ext/node/resolution.rs b/ext/node/resolution.rs index 7500f0f319..e930215fd3 100644 --- a/ext/node/resolution.rs +++ b/ext/node/resolution.rs @@ -15,6 +15,7 @@ use regex::Regex; use crate::errors; use crate::package_json::PackageJson; use crate::path::PathClean; +use crate::NodePermissions; use crate::RequireNpmResolver; pub static DEFAULT_CONDITIONS: &[&str] = &["deno", "node", "import"]; @@ -188,6 +189,7 @@ pub fn package_imports_resolve( conditions: &[&str], mode: NodeResolutionMode, npm_resolver: &dyn RequireNpmResolver, + permissions: &mut dyn NodePermissions, ) -> Result { if name == "#" || name.starts_with("#/") || name.ends_with('/') { let reason = "is not a valid internal imports specifier name"; @@ -198,7 +200,8 @@ pub fn package_imports_resolve( )); } - let package_config = get_package_scope_config(referrer, npm_resolver)?; + let package_config = + get_package_scope_config(referrer, npm_resolver, permissions)?; let mut package_json_path = None; if package_config.exists { package_json_path = Some(package_config.path.clone()); @@ -216,6 +219,7 @@ pub fn package_imports_resolve( conditions, mode, npm_resolver, + permissions, )?; if let Some(resolved) = maybe_resolved { return Ok(resolved); @@ -258,6 +262,7 @@ pub fn package_imports_resolve( conditions, mode, npm_resolver, + permissions, )?; if let Some(resolved) = maybe_resolved { return Ok(resolved); @@ -322,6 +327,7 @@ fn resolve_package_target_string( conditions: &[&str], mode: NodeResolutionMode, npm_resolver: &dyn RequireNpmResolver, + permissions: &mut dyn NodePermissions, ) -> Result { if !subpath.is_empty() && !pattern && !target.ends_with('/') { return Err(throw_invalid_package_target( @@ -355,6 +361,7 @@ fn resolve_package_target_string( conditions, mode, npm_resolver, + permissions, ) { Ok(Some(path)) => Ok(path), Ok(None) => Err(generic_error("not found")), @@ -430,6 +437,7 @@ fn resolve_package_target( conditions: &[&str], mode: NodeResolutionMode, npm_resolver: &dyn RequireNpmResolver, + permissions: &mut dyn NodePermissions, ) -> Result, AnyError> { if let Some(target) = target.as_str() { return resolve_package_target_string( @@ -444,6 +452,7 @@ fn resolve_package_target( conditions, mode, npm_resolver, + permissions, ) .map(|path| { if mode.is_types() { @@ -471,6 +480,7 @@ fn resolve_package_target( conditions, mode, npm_resolver, + permissions, ); match resolved_result { @@ -520,6 +530,7 @@ fn resolve_package_target( conditions, mode, npm_resolver, + permissions, )?; match resolved { Some(resolved) => return Ok(Some(resolved)), @@ -564,6 +575,7 @@ pub fn package_exports_resolve( conditions: &[&str], mode: NodeResolutionMode, npm_resolver: &dyn RequireNpmResolver, + permissions: &mut dyn NodePermissions, ) -> Result { if package_exports.contains_key(&package_subpath) && package_subpath.find('*').is_none() @@ -582,6 +594,7 @@ pub fn package_exports_resolve( conditions, mode, npm_resolver, + permissions, )?; if resolved.is_none() { return Err(throw_exports_not_found( @@ -641,6 +654,7 @@ pub fn package_exports_resolve( conditions, mode, npm_resolver, + permissions, )?; if let Some(resolved) = maybe_resolved { return Ok(resolved); @@ -718,12 +732,14 @@ pub fn package_resolve( conditions: &[&str], mode: NodeResolutionMode, npm_resolver: &dyn RequireNpmResolver, + permissions: &mut dyn NodePermissions, ) -> Result, AnyError> { let (package_name, package_subpath, _is_scoped) = parse_package_name(specifier, referrer)?; // ResolveSelf - let package_config = get_package_scope_config(referrer, npm_resolver)?; + let package_config = + get_package_scope_config(referrer, npm_resolver, permissions)?; if package_config.exists && package_config.name.as_ref() == Some(&package_name) { @@ -737,6 +753,7 @@ pub fn package_resolve( conditions, mode, npm_resolver, + permissions, ) .map(Some); } @@ -763,7 +780,8 @@ pub fn package_resolve( // )) // Package match. - let package_json = PackageJson::load(npm_resolver, package_json_path)?; + let package_json = + PackageJson::load(npm_resolver, permissions, package_json_path)?; if let Some(exports) = &package_json.exports { return package_exports_resolve( &package_json.path, @@ -774,6 +792,7 @@ pub fn package_resolve( conditions, mode, npm_resolver, + permissions, ) .map(Some); } @@ -795,19 +814,21 @@ pub fn package_resolve( pub fn get_package_scope_config( referrer: &ModuleSpecifier, npm_resolver: &dyn RequireNpmResolver, + permissions: &mut dyn NodePermissions, ) -> Result { let root_folder = npm_resolver .resolve_package_folder_from_path(&referrer.to_file_path().unwrap())?; let package_json_path = root_folder.join("package.json"); - PackageJson::load(npm_resolver, package_json_path) + PackageJson::load(npm_resolver, permissions, package_json_path) } pub fn get_closest_package_json( url: &ModuleSpecifier, npm_resolver: &dyn RequireNpmResolver, + permissions: &mut dyn NodePermissions, ) -> Result { let package_json_path = get_closest_package_json_path(url, npm_resolver)?; - PackageJson::load(npm_resolver, package_json_path) + PackageJson::load(npm_resolver, permissions, package_json_path) } fn get_closest_package_json_path(