From 636352e0ca1e611c7673f2ab68538e1ddb2dc5b7 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: 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. --- ext/node/lib.rs | 59 ++++++++++++++++++++++++++++++++++-------------- ext/node/package_json.rs | 4 +++- ext/node/resolution.rs | 31 +++++++++++++++++++++---- 3 files changed, 71 insertions(+), 23 deletions(-) (limited to 'ext/node') diff --git a/ext/node/lib.rs b/ext/node/lib.rs index a670586d1..8a36e95fa 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 f0a2b4f4d..5894b8831 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 7500f0f31..e930215fd 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( -- cgit v1.2.3