1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2025-01-11 16:42:21 -05:00

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.
This commit is contained in:
Bartek Iwańczuk 2023-01-10 14:35:44 +01:00 committed by David Sherret
parent 49b5ac947f
commit 1c2db072b5
26 changed files with 306 additions and 93 deletions

View file

@ -33,6 +33,7 @@ use deno_core::ModuleSpecifier;
use deno_graph::GraphImport; use deno_graph::GraphImport;
use deno_graph::Resolved; use deno_graph::Resolved;
use deno_runtime::deno_node::NodeResolutionMode; use deno_runtime::deno_node::NodeResolutionMode;
use deno_runtime::permissions::PermissionsContainer;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use std::collections::BTreeMap; use std::collections::BTreeMap;
use std::collections::HashMap; use std::collections::HashMap;
@ -1004,6 +1005,7 @@ impl Documents {
referrer, referrer,
NodeResolutionMode::Types, NodeResolutionMode::Types,
npm_resolver, npm_resolver,
&mut PermissionsContainer::allow_all(),
) )
.ok() .ok()
.flatten(), .flatten(),
@ -1040,6 +1042,7 @@ impl Documents {
&npm_ref, &npm_ref,
NodeResolutionMode::Types, NodeResolutionMode::Types,
npm_resolver, npm_resolver,
&mut PermissionsContainer::allow_all(),
) )
.ok() .ok()
.flatten(), .flatten(),
@ -1208,6 +1211,7 @@ impl Documents {
&npm_ref, &npm_ref,
NodeResolutionMode::Types, NodeResolutionMode::Types,
npm_resolver, npm_resolver,
&mut PermissionsContainer::allow_all(),
) )
.ok() .ok()
.flatten(), .flatten(),

View file

@ -20,6 +20,7 @@ use deno_core::ModuleSource;
use deno_core::ModuleSpecifier; use deno_core::ModuleSpecifier;
use deno_core::ModuleType; use deno_core::ModuleType;
use deno_core::OpState; use deno_core::OpState;
use deno_core::ResolutionKind;
use deno_core::SourceMapGetter; use deno_core::SourceMapGetter;
use deno_runtime::permissions::PermissionsContainer; use deno_runtime::permissions::PermissionsContainer;
use std::cell::RefCell; use std::cell::RefCell;
@ -36,28 +37,38 @@ struct ModuleCodeSource {
pub struct CliModuleLoader { pub struct CliModuleLoader {
pub lib: TsTypeLib, pub lib: TsTypeLib,
/// The initial set of permissions used to resolve the static imports in the /// The initial set of permissions used to resolve the static imports in the
/// worker. They are decoupled from the worker (dynamic) permissions since /// worker. These are "allow all" for main worker, and parent thread
/// read access errors must be raised based on the parent thread permissions. /// permissions for Web Worker.
pub root_permissions: PermissionsContainer, 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, pub ps: ProcState,
} }
impl CliModuleLoader { impl CliModuleLoader {
pub fn new(ps: ProcState) -> Rc<Self> { pub fn new(
ps: ProcState,
root_permissions: PermissionsContainer,
dynamic_permissions: PermissionsContainer,
) -> Rc<Self> {
Rc::new(CliModuleLoader { Rc::new(CliModuleLoader {
lib: ps.options.ts_type_lib_window(), lib: ps.options.ts_type_lib_window(),
root_permissions: PermissionsContainer::allow_all(), root_permissions,
dynamic_permissions,
ps, ps,
}) })
} }
pub fn new_for_worker( pub fn new_for_worker(
ps: ProcState, ps: ProcState,
permissions: PermissionsContainer, root_permissions: PermissionsContainer,
dynamic_permissions: PermissionsContainer,
) -> Rc<Self> { ) -> Rc<Self> {
Rc::new(CliModuleLoader { Rc::new(CliModuleLoader {
lib: ps.options.ts_type_lib_worker(), lib: ps.options.ts_type_lib_worker(),
root_permissions: permissions, root_permissions,
dynamic_permissions,
ps, ps,
}) })
} }
@ -138,6 +149,7 @@ impl CliModuleLoader {
&self, &self,
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
maybe_referrer: Option<ModuleSpecifier>, maybe_referrer: Option<ModuleSpecifier>,
is_dynamic: bool,
) -> Result<ModuleSource, AnyError> { ) -> Result<ModuleSource, AnyError> {
let code_source = if self.ps.npm_resolver.in_npm_package(specifier) { let code_source = if self.ps.npm_resolver.in_npm_package(specifier) {
let file_path = specifier.to_file_path().unwrap(); 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 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 // translate cjs to esm if it's cjs and inject node globals
node::translate_cjs_to_esm( node::translate_cjs_to_esm(
&self.ps.file_fetcher, &self.ps.file_fetcher,
@ -160,6 +177,7 @@ impl CliModuleLoader {
MediaType::Cjs, MediaType::Cjs,
&self.ps.npm_resolver, &self.ps.npm_resolver,
&self.ps.node_analysis_cache, &self.ps.node_analysis_cache,
&mut permissions,
)? )?
} else { } else {
// only inject node globals for esm // only inject node globals for esm
@ -203,28 +221,35 @@ impl ModuleLoader for CliModuleLoader {
&self, &self,
specifier: &str, specifier: &str,
referrer: &str, referrer: &str,
_is_main: bool, kind: ResolutionKind,
) -> Result<ModuleSpecifier, AnyError> { ) -> Result<ModuleSpecifier, AnyError> {
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( fn load(
&self, &self,
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
maybe_referrer: Option<ModuleSpecifier>, maybe_referrer: Option<ModuleSpecifier>,
_is_dynamic: bool, is_dynamic: bool,
) -> Pin<Box<deno_core::ModuleSourceFuture>> { ) -> Pin<Box<deno_core::ModuleSourceFuture>> {
// NOTE: this block is async only because of `deno_core` interface // NOTE: this block is async only because of `deno_core` interface
// requirements; module was already loaded when constructing module graph // requirements; module was already loaded when constructing module graph
// during call to `prepare_load` so we can load it synchronously. // during call to `prepare_load` so we can load it synchronously.
Box::pin(deno_core::futures::future::ready( Box::pin(deno_core::futures::future::ready(self.load_sync(
self.load_sync(specifier, maybe_referrer), specifier,
)) maybe_referrer,
is_dynamic,
)))
} }
fn prepare_load( fn prepare_load(
&self, &self,
op_state: Rc<RefCell<OpState>>, _op_state: Rc<RefCell<OpState>>,
specifier: &ModuleSpecifier, specifier: &ModuleSpecifier,
_maybe_referrer: Option<String>, _maybe_referrer: Option<String>,
is_dynamic: bool, is_dynamic: bool,
@ -236,18 +261,15 @@ impl ModuleLoader for CliModuleLoader {
let specifier = specifier.clone(); let specifier = specifier.clone();
let ps = self.ps.clone(); let ps = self.ps.clone();
let state = op_state.borrow();
let dynamic_permissions = state.borrow::<PermissionsContainer>().clone(); let dynamic_permissions = self.dynamic_permissions.clone();
let root_permissions = if is_dynamic { let root_permissions = if is_dynamic {
dynamic_permissions.clone() self.dynamic_permissions.clone()
} else { } else {
self.root_permissions.clone() self.root_permissions.clone()
}; };
let lib = self.lib; let lib = self.lib;
drop(state);
async move { async move {
ps.prepare_module_load( ps.prepare_module_load(
vec![specifier], vec![specifier],

View file

@ -25,12 +25,14 @@ use deno_runtime::deno_node::package_imports_resolve;
use deno_runtime::deno_node::package_resolve; use deno_runtime::deno_node::package_resolve;
use deno_runtime::deno_node::path_to_declaration_path; use deno_runtime::deno_node::path_to_declaration_path;
use deno_runtime::deno_node::NodeModuleKind; use deno_runtime::deno_node::NodeModuleKind;
use deno_runtime::deno_node::NodePermissions;
use deno_runtime::deno_node::NodeResolutionMode; use deno_runtime::deno_node::NodeResolutionMode;
use deno_runtime::deno_node::PackageJson; use deno_runtime::deno_node::PackageJson;
use deno_runtime::deno_node::PathClean; use deno_runtime::deno_node::PathClean;
use deno_runtime::deno_node::RequireNpmResolver; use deno_runtime::deno_node::RequireNpmResolver;
use deno_runtime::deno_node::DEFAULT_CONDITIONS; use deno_runtime::deno_node::DEFAULT_CONDITIONS;
use deno_runtime::deno_node::NODE_GLOBAL_THIS_NAME; use deno_runtime::deno_node::NODE_GLOBAL_THIS_NAME;
use deno_runtime::permissions::PermissionsContainer;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use regex::Regex; use regex::Regex;
@ -440,6 +442,7 @@ pub fn node_resolve(
referrer: &ModuleSpecifier, referrer: &ModuleSpecifier,
mode: NodeResolutionMode, mode: NodeResolutionMode,
npm_resolver: &dyn RequireNpmResolver, npm_resolver: &dyn RequireNpmResolver,
permissions: &mut dyn NodePermissions,
) -> Result<Option<NodeResolution>, AnyError> { ) -> Result<Option<NodeResolution>, AnyError> {
// Note: if we are here, then the referrer is an esm module // 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 // TODO(bartlomieju): skipped "policy" part as we don't plan to support it
@ -481,6 +484,7 @@ pub fn node_resolve(
DEFAULT_CONDITIONS, DEFAULT_CONDITIONS,
mode, mode,
npm_resolver, npm_resolver,
permissions,
)?; )?;
let url = match url { let url = match url {
Some(url) => url, Some(url) => url,
@ -510,6 +514,7 @@ pub fn node_resolve_npm_reference(
reference: &NpmPackageReference, reference: &NpmPackageReference,
mode: NodeResolutionMode, mode: NodeResolutionMode,
npm_resolver: &NpmPackageResolver, npm_resolver: &NpmPackageResolver,
permissions: &mut dyn NodePermissions,
) -> Result<Option<NodeResolution>, AnyError> { ) -> Result<Option<NodeResolution>, AnyError> {
let package_folder = let package_folder =
npm_resolver.resolve_package_folder_from_deno_module(&reference.req)?; npm_resolver.resolve_package_folder_from_deno_module(&reference.req)?;
@ -525,6 +530,7 @@ pub fn node_resolve_npm_reference(
DEFAULT_CONDITIONS, DEFAULT_CONDITIONS,
mode, mode,
npm_resolver, npm_resolver,
permissions,
) )
.with_context(|| { .with_context(|| {
format!("Error resolving package config for '{}'", reference) format!("Error resolving package config for '{}'", reference)
@ -553,11 +559,13 @@ pub fn node_resolve_binary_export(
pkg_req: &NpmPackageReq, pkg_req: &NpmPackageReq,
bin_name: Option<&str>, bin_name: Option<&str>,
npm_resolver: &NpmPackageResolver, npm_resolver: &NpmPackageResolver,
permissions: &mut dyn NodePermissions,
) -> Result<NodeResolution, AnyError> { ) -> Result<NodeResolution, AnyError> {
let package_folder = let package_folder =
npm_resolver.resolve_package_folder_from_deno_module(pkg_req)?; npm_resolver.resolve_package_folder_from_deno_module(pkg_req)?;
let package_json_path = package_folder.join("package.json"); 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 { let bin = match &package_json.bin {
Some(bin) => bin, Some(bin) => bin,
None => bail!( None => bail!(
@ -665,11 +673,12 @@ fn package_config_resolve(
conditions: &[&str], conditions: &[&str],
mode: NodeResolutionMode, mode: NodeResolutionMode,
npm_resolver: &dyn RequireNpmResolver, npm_resolver: &dyn RequireNpmResolver,
permissions: &mut dyn NodePermissions,
) -> Result<Option<PathBuf>, AnyError> { ) -> Result<Option<PathBuf>, AnyError> {
let package_json_path = package_dir.join("package.json"); let package_json_path = package_dir.join("package.json");
let referrer = ModuleSpecifier::from_directory_path(package_dir).unwrap(); let referrer = ModuleSpecifier::from_directory_path(package_dir).unwrap();
let package_config = 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 { if let Some(exports) = &package_config.exports {
let result = package_exports_resolve( let result = package_exports_resolve(
&package_json_path, &package_json_path,
@ -680,6 +689,7 @@ fn package_config_resolve(
conditions, conditions,
mode, mode,
npm_resolver, npm_resolver,
permissions,
); );
match result { match result {
Ok(found) => return Ok(Some(found)), Ok(found) => return Ok(Some(found)),
@ -712,7 +722,11 @@ pub fn url_to_node_resolution(
if url_str.starts_with("http") { if url_str.starts_with("http") {
Ok(NodeResolution::Esm(url)) Ok(NodeResolution::Esm(url))
} else if url_str.ends_with(".js") || url_str.ends_with(".d.ts") { } 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" { if package_config.typ == "module" {
Ok(NodeResolution::Esm(url)) Ok(NodeResolution::Esm(url))
} else { } else {
@ -786,6 +800,7 @@ fn module_resolve(
conditions: &[&str], conditions: &[&str],
mode: NodeResolutionMode, mode: NodeResolutionMode,
npm_resolver: &dyn RequireNpmResolver, npm_resolver: &dyn RequireNpmResolver,
permissions: &mut dyn NodePermissions,
) -> Result<Option<ModuleSpecifier>, AnyError> { ) -> Result<Option<ModuleSpecifier>, AnyError> {
// note: if we're here, the referrer is an esm module // note: if we're here, the referrer is an esm module
let url = if should_be_treated_as_relative_or_absolute_path(specifier) { let url = if should_be_treated_as_relative_or_absolute_path(specifier) {
@ -811,6 +826,7 @@ fn module_resolve(
conditions, conditions,
mode, mode,
npm_resolver, npm_resolver,
permissions,
) )
.map(|p| ModuleSpecifier::from_file_path(p).unwrap())?, .map(|p| ModuleSpecifier::from_file_path(p).unwrap())?,
) )
@ -824,6 +840,7 @@ fn module_resolve(
conditions, conditions,
mode, mode,
npm_resolver, npm_resolver,
permissions,
)? )?
.map(|p| ModuleSpecifier::from_file_path(p).unwrap()) .map(|p| ModuleSpecifier::from_file_path(p).unwrap())
}; };
@ -879,6 +896,7 @@ pub fn translate_cjs_to_esm(
media_type: MediaType, media_type: MediaType,
npm_resolver: &NpmPackageResolver, npm_resolver: &NpmPackageResolver,
node_analysis_cache: &NodeAnalysisCache, node_analysis_cache: &NodeAnalysisCache,
permissions: &mut dyn NodePermissions,
) -> Result<String, AnyError> { ) -> Result<String, AnyError> {
fn perform_cjs_analysis( fn perform_cjs_analysis(
analysis_cache: &NodeAnalysisCache, analysis_cache: &NodeAnalysisCache,
@ -956,6 +974,7 @@ pub fn translate_cjs_to_esm(
&["deno", "require", "default"], &["deno", "require", "default"],
NodeResolutionMode::Execution, NodeResolutionMode::Execution,
npm_resolver, npm_resolver,
permissions,
)?; )?;
let reexport_specifier = let reexport_specifier =
ModuleSpecifier::from_file_path(resolved_reexport).unwrap(); ModuleSpecifier::from_file_path(resolved_reexport).unwrap();
@ -1027,6 +1046,7 @@ fn resolve(
conditions: &[&str], conditions: &[&str],
mode: NodeResolutionMode, mode: NodeResolutionMode,
npm_resolver: &dyn RequireNpmResolver, npm_resolver: &dyn RequireNpmResolver,
permissions: &mut dyn NodePermissions,
) -> Result<PathBuf, AnyError> { ) -> Result<PathBuf, AnyError> {
if specifier.starts_with('/') { if specifier.starts_with('/') {
todo!(); todo!();
@ -1056,7 +1076,7 @@ fn resolve(
let package_json_path = module_dir.join("package.json"); let package_json_path = module_dir.join("package.json");
if package_json_path.exists() { if package_json_path.exists() {
let package_json = 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 { if let Some(exports) = &package_json.exports {
return package_exports_resolve( return package_exports_resolve(
@ -1068,6 +1088,7 @@ fn resolve(
conditions, conditions,
mode, mode,
npm_resolver, npm_resolver,
permissions,
); );
} }
@ -1080,7 +1101,7 @@ fn resolve(
let package_json_path = d.join("package.json"); let package_json_path = d.join("package.json");
if package_json_path.exists() { if package_json_path.exists() {
let package_json = 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) { if let Some(main) = package_json.main(NodeModuleKind::Cjs) {
return Ok(d.join(main).clean()); return Ok(d.join(main).clean());
} }

View file

@ -10,6 +10,7 @@ use deno_core::error::AnyError;
use deno_core::futures; use deno_core::futures;
use deno_core::futures::future::BoxFuture; use deno_core::futures::future::BoxFuture;
use deno_core::url::Url; use deno_core::url::Url;
use deno_runtime::deno_node::NodePermissions;
use deno_runtime::deno_node::NodeResolutionMode; use deno_runtime::deno_node::NodeResolutionMode;
use crate::args::Lockfile; use crate::args::Lockfile;
@ -54,7 +55,11 @@ pub trait InnerNpmPackageResolver: Send + Sync {
fn cache_packages(&self) -> BoxFuture<'static, Result<(), AnyError>>; 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; fn snapshot(&self) -> NpmResolutionSnapshot;
@ -103,6 +108,7 @@ pub async fn cache_packages(
} }
pub fn ensure_registry_read_permission( pub fn ensure_registry_read_permission(
permissions: &mut dyn NodePermissions,
registry_path: &Path, registry_path: &Path,
path: &Path, path: &Path,
) -> Result<(), AnyError> { ) -> Result<(), AnyError> {
@ -126,10 +132,7 @@ pub fn ensure_registry_read_permission(
} }
} }
Err(deno_core::error::custom_error( permissions.check_read(path)
"PermissionDenied",
format!("Reading {} is not allowed", path.display()),
))
} }
/// Gets the corresponding @types package for the provided package name. /// Gets the corresponding @types package for the provided package name.

View file

@ -12,6 +12,7 @@ use deno_core::error::AnyError;
use deno_core::futures::future::BoxFuture; use deno_core::futures::future::BoxFuture;
use deno_core::futures::FutureExt; use deno_core::futures::FutureExt;
use deno_core::url::Url; use deno_core::url::Url;
use deno_runtime::deno_node::NodePermissions;
use deno_runtime::deno_node::NodeResolutionMode; use deno_runtime::deno_node::NodeResolutionMode;
use crate::args::Lockfile; use crate::args::Lockfile;
@ -154,9 +155,13 @@ impl InnerNpmPackageResolver for GlobalNpmPackageResolver {
async move { cache_packages_in_resolver(&resolver).await }.boxed() 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); let registry_path = self.cache.registry_folder(&self.registry_url);
ensure_registry_read_permission(&registry_path, path) ensure_registry_read_permission(permissions, &registry_path, path)
} }
fn snapshot(&self) -> NpmResolutionSnapshot { fn snapshot(&self) -> NpmResolutionSnapshot {

View file

@ -19,6 +19,7 @@ use deno_core::futures::future::BoxFuture;
use deno_core::futures::FutureExt; use deno_core::futures::FutureExt;
use deno_core::url::Url; use deno_core::url::Url;
use deno_runtime::deno_core::futures; use deno_runtime::deno_core::futures;
use deno_runtime::deno_node::NodePermissions;
use deno_runtime::deno_node::NodeResolutionMode; use deno_runtime::deno_node::NodeResolutionMode;
use deno_runtime::deno_node::PackageJson; use deno_runtime::deno_node::PackageJson;
use tokio::task::JoinHandle; use tokio::task::JoinHandle;
@ -245,8 +246,16 @@ impl InnerNpmPackageResolver for LocalNpmPackageResolver {
.boxed() .boxed()
} }
fn ensure_read_permission(&self, path: &Path) -> Result<(), AnyError> { fn ensure_read_permission(
ensure_registry_read_permission(&self.root_node_modules_path, path) &self,
permissions: &mut dyn NodePermissions,
path: &Path,
) -> Result<(), AnyError> {
ensure_registry_read_permission(
permissions,
&self.root_node_modules_path,
path,
)
} }
fn snapshot(&self) -> NpmResolutionSnapshot { fn snapshot(&self) -> NpmResolutionSnapshot {

View file

@ -11,6 +11,7 @@ use deno_core::error::custom_error;
use deno_core::error::AnyError; use deno_core::error::AnyError;
use deno_core::parking_lot::Mutex; use deno_core::parking_lot::Mutex;
use deno_core::serde_json; use deno_core::serde_json;
use deno_runtime::deno_node::NodePermissions;
use deno_runtime::deno_node::NodeResolutionMode; use deno_runtime::deno_node::NodeResolutionMode;
use deno_runtime::deno_node::PathClean; use deno_runtime::deno_node::PathClean;
use deno_runtime::deno_node::RequireNpmResolver; use deno_runtime::deno_node::RequireNpmResolver;
@ -367,8 +368,12 @@ impl RequireNpmResolver for NpmPackageResolver {
.is_ok() .is_ok()
} }
fn ensure_read_permission(&self, path: &Path) -> Result<(), AnyError> { fn ensure_read_permission(
self.inner.ensure_read_permission(path) &self,
permissions: &mut dyn NodePermissions,
path: &Path,
) -> Result<(), AnyError> {
self.inner.ensure_read_permission(permissions, path)
} }
} }

View file

@ -532,6 +532,7 @@ impl ProcState {
&self, &self,
specifier: &str, specifier: &str,
referrer: &str, referrer: &str,
permissions: &mut PermissionsContainer,
) -> Result<ModuleSpecifier, AnyError> { ) -> Result<ModuleSpecifier, AnyError> {
if let Ok(referrer) = deno_core::resolve_url_or_path(referrer) { if let Ok(referrer) = deno_core::resolve_url_or_path(referrer) {
if self.npm_resolver.in_npm_package(&referrer) { if self.npm_resolver.in_npm_package(&referrer) {
@ -542,6 +543,7 @@ impl ProcState {
&referrer, &referrer,
NodeResolutionMode::Execution, NodeResolutionMode::Execution,
&self.npm_resolver, &self.npm_resolver,
permissions,
)) ))
.with_context(|| { .with_context(|| {
format!("Could not resolve '{}' from '{}'.", specifier, referrer) format!("Could not resolve '{}' from '{}'.", specifier, referrer)
@ -575,6 +577,7 @@ impl ProcState {
&reference, &reference,
NodeResolutionMode::Execution, NodeResolutionMode::Execution,
&self.npm_resolver, &self.npm_resolver,
permissions,
)) ))
.with_context(|| format!("Could not resolve '{}'.", reference)); .with_context(|| format!("Could not resolve '{}'.", reference));
} else { } else {
@ -618,6 +621,7 @@ impl ProcState {
&reference, &reference,
deno_runtime::deno_node::NodeResolutionMode::Execution, deno_runtime::deno_node::NodeResolutionMode::Execution,
&self.npm_resolver, &self.npm_resolver,
permissions,
)) ))
.with_context(|| format!("Could not resolve '{}'.", reference)); .with_context(|| format!("Could not resolve '{}'.", reference));
} }

View file

@ -23,6 +23,7 @@ use deno_core::url::Url;
use deno_core::v8_set_flags; use deno_core::v8_set_flags;
use deno_core::ModuleLoader; use deno_core::ModuleLoader;
use deno_core::ModuleSpecifier; use deno_core::ModuleSpecifier;
use deno_core::ResolutionKind;
use deno_graph::source::Resolver; use deno_graph::source::Resolver;
use deno_runtime::deno_broadcast_channel::InMemoryBroadcastChannel; use deno_runtime::deno_broadcast_channel::InMemoryBroadcastChannel;
use deno_runtime::deno_tls::rustls_pemfile; use deno_runtime::deno_tls::rustls_pemfile;
@ -137,7 +138,7 @@ impl ModuleLoader for EmbeddedModuleLoader {
&self, &self,
specifier: &str, specifier: &str,
referrer: &str, referrer: &str,
_is_main: bool, _kind: ResolutionKind,
) -> Result<ModuleSpecifier, AnyError> { ) -> Result<ModuleSpecifier, AnyError> {
// Try to follow redirects when resolving. // Try to follow redirects when resolving.
let referrer = match self.eszip.get_module(referrer) { let referrer = match self.eszip.get_module(referrer) {

View file

@ -360,6 +360,13 @@ mod npm {
exit_code: 1, 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] #[test]
fn parallel_downloading() { fn parallel_downloading() {
let (out, _err) = util::run_and_collect_output_with_args( let (out, _err) = util::run_and_collect_output_with_args(

View file

@ -0,0 +1,4 @@
module.exports = {
"name": "foobar",
"version": "0.0.1",
};

View file

@ -0,0 +1,4 @@
{
"name": "foobar",
"version": "0.0.1"
}

View file

@ -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" }

View file

@ -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);

View file

@ -0,0 +1,5 @@
function loadConfigFile(fileName) {
return require(fileName);
}
module.exports.loadConfigFile = loadConfigFile;

View file

@ -0,0 +1,5 @@
{
"name": "@denotest/permissions-outside-package",
"version": "1.0.0",
"main": "./index.js"
}

View file

@ -33,6 +33,7 @@ use deno_core::RuntimeOptions;
use deno_core::Snapshot; use deno_core::Snapshot;
use deno_graph::Resolved; use deno_graph::Resolved;
use deno_runtime::deno_node::NodeResolutionMode; use deno_runtime::deno_node::NodeResolutionMode;
use deno_runtime::permissions::PermissionsContainer;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use std::borrow::Cow; use std::borrow::Cow;
use std::collections::HashMap; use std::collections::HashMap;
@ -647,6 +648,7 @@ fn op_resolve(
&referrer, &referrer,
NodeResolutionMode::Types, NodeResolutionMode::Types,
npm_resolver, npm_resolver,
&mut PermissionsContainer::allow_all(),
) )
.ok() .ok()
.flatten(), .flatten(),
@ -703,6 +705,7 @@ pub fn resolve_npm_package_reference_types(
npm_ref, npm_ref,
NodeResolutionMode::Types, NodeResolutionMode::Types,
npm_resolver, npm_resolver,
&mut PermissionsContainer::allow_all(),
)?; )?;
Ok(NodeResolution::into_specifier_and_media_type( Ok(NodeResolution::into_specifier_and_media_type(
maybe_resolution, maybe_resolution,

View file

@ -459,6 +459,7 @@ async fn create_main_worker_internal(
&package_ref.req, &package_ref.req,
package_ref.sub_path.as_deref(), package_ref.sub_path.as_deref(),
&ps.npm_resolver, &ps.npm_resolver,
&mut PermissionsContainer::allow_all(),
)?; )?;
let is_main_cjs = let is_main_cjs =
matches!(node_resolution, node::NodeResolution::CommonJs(_)); matches!(node_resolution, node::NodeResolution::CommonJs(_));
@ -473,7 +474,11 @@ async fn create_main_worker_internal(
(main_module, false) (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(); 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( let module_loader = CliModuleLoader::new_for_worker(
ps.clone(), ps.clone(),
args.parent_permissions.clone(), args.parent_permissions.clone(),
args.permissions.clone(),
); );
let create_web_worker_cb = let create_web_worker_cb =
create_web_worker_callback(ps.clone(), stdio.clone()); create_web_worker_callback(ps.clone(), stdio.clone());

View file

@ -13,6 +13,7 @@ use crate::modules::parse_import_assertions;
use crate::modules::validate_import_assertions; use crate::modules::validate_import_assertions;
use crate::modules::ImportAssertionsKind; use crate::modules::ImportAssertionsKind;
use crate::modules::ModuleMap; use crate::modules::ModuleMap;
use crate::modules::ResolutionKind;
use crate::ops::OpCtx; use crate::ops::OpCtx;
use crate::runtime::SnapshotOptions; use crate::runtime::SnapshotOptions;
use crate::JsRuntime; use crate::JsRuntime;
@ -378,7 +379,8 @@ fn import_meta_resolve(
return; return;
} }
match loader.resolve(&specifier_str, &referrer, false) { match loader.resolve(&specifier_str, &referrer, ResolutionKind::DynamicImport)
{
Ok(resolved) => { Ok(resolved) => {
let resolved_val = serde_v8::to_v8(scope, resolved.as_str()).unwrap(); let resolved_val = serde_v8::to_v8(scope, resolved.as_str()).unwrap();
rv.set(resolved_val); rv.set(resolved_val);

View file

@ -21,6 +21,7 @@ use deno_core::ModuleSource;
use deno_core::ModuleSourceFuture; use deno_core::ModuleSourceFuture;
use deno_core::ModuleSpecifier; use deno_core::ModuleSpecifier;
use deno_core::ModuleType; use deno_core::ModuleType;
use deno_core::ResolutionKind;
use deno_core::RuntimeOptions; use deno_core::RuntimeOptions;
use futures::FutureExt; use futures::FutureExt;
@ -31,7 +32,7 @@ impl ModuleLoader for TypescriptModuleLoader {
&self, &self,
specifier: &str, specifier: &str,
referrer: &str, referrer: &str,
_is_main: bool, _kind: ResolutionKind,
) -> Result<ModuleSpecifier, Error> { ) -> Result<ModuleSpecifier, Error> {
Ok(resolve_import(specifier, referrer)?) Ok(resolve_import(specifier, referrer)?)
} }

View file

@ -77,6 +77,7 @@ pub use crate::modules::ModuleSource;
pub use crate::modules::ModuleSourceFuture; pub use crate::modules::ModuleSourceFuture;
pub use crate::modules::ModuleType; pub use crate::modules::ModuleType;
pub use crate::modules::NoopModuleLoader; pub use crate::modules::NoopModuleLoader;
pub use crate::modules::ResolutionKind;
pub use crate::normalize_path::normalize_path; pub use crate::normalize_path::normalize_path;
pub use crate::ops::Op; pub use crate::ops::Op;
pub use crate::ops::OpAsyncFuture; pub use crate::ops::OpAsyncFuture;

View file

@ -202,6 +202,20 @@ pub type ModuleSourceFuture = dyn Future<Output = Result<ModuleSource, Error>>;
type ModuleLoadFuture = type ModuleLoadFuture =
dyn Future<Output = Result<(ModuleRequest, ModuleSource), Error>>; dyn Future<Output = Result<(ModuleRequest, ModuleSource), Error>>;
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 { pub trait ModuleLoader {
/// Returns an absolute URL. /// Returns an absolute URL.
/// When implementing an spec-complaint VM, this should be exactly the /// 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 /// `is_main` can be used to resolve from current working directory or
/// apply import map for child imports. /// apply import map for child imports.
///
/// `is_dyn_import` can be used to check permissions or deny
/// dynamic imports altogether.
fn resolve( fn resolve(
&self, &self,
specifier: &str, specifier: &str,
referrer: &str, referrer: &str,
_is_main: bool, kind: ResolutionKind,
) -> Result<ModuleSpecifier, Error>; ) -> Result<ModuleSpecifier, Error>;
/// Given ModuleSpecifier, load its source code. /// Given ModuleSpecifier, load its source code.
@ -256,7 +273,7 @@ impl ModuleLoader for NoopModuleLoader {
&self, &self,
_specifier: &str, _specifier: &str,
_referrer: &str, _referrer: &str,
_is_main: bool, _kind: ResolutionKind,
) -> Result<ModuleSpecifier, Error> { ) -> Result<ModuleSpecifier, Error> {
Err(generic_error("Module loading is not supported")) Err(generic_error("Module loading is not supported"))
} }
@ -284,7 +301,7 @@ impl ModuleLoader for FsModuleLoader {
&self, &self,
specifier: &str, specifier: &str,
referrer: &str, referrer: &str,
_is_main: bool, _kind: ResolutionKind,
) -> Result<ModuleSpecifier, Error> { ) -> Result<ModuleSpecifier, Error> {
Ok(resolve_import(specifier, referrer)?) Ok(resolve_import(specifier, referrer)?)
} }
@ -446,14 +463,16 @@ impl RecursiveModuleLoad {
fn resolve_root(&self) -> Result<ModuleSpecifier, Error> { fn resolve_root(&self) -> Result<ModuleSpecifier, Error> {
match self.init { match self.init {
LoadInit::Main(ref specifier) => { LoadInit::Main(ref specifier) => {
self.loader.resolve(specifier, ".", true) self
.loader
.resolve(specifier, ".", ResolutionKind::MainModule)
} }
LoadInit::Side(ref specifier) => { LoadInit::Side(ref specifier) => {
self.loader.resolve(specifier, ".", false) self.loader.resolve(specifier, ".", ResolutionKind::Import)
}
LoadInit::DynamicImport(ref specifier, ref referrer, _) => {
self.loader.resolve(specifier, referrer, false)
} }
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 op_state = self.op_state.clone();
let (module_specifier, maybe_referrer) = match self.init { let (module_specifier, maybe_referrer) = match self.init {
LoadInit::Main(ref specifier) => { LoadInit::Main(ref specifier) => {
let spec = self.loader.resolve(specifier, ".", true)?; let spec =
self
.loader
.resolve(specifier, ".", ResolutionKind::MainModule)?;
(spec, None) (spec, None)
} }
LoadInit::Side(ref specifier) => { LoadInit::Side(ref specifier) => {
let spec = self.loader.resolve(specifier, ".", false)?; let spec =
self
.loader
.resolve(specifier, ".", ResolutionKind::Import)?;
(spec, None) (spec, None)
} }
LoadInit::DynamicImport(ref specifier, ref referrer, _) => { 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())) (spec, Some(referrer.to_string()))
} }
}; };
@ -537,6 +566,7 @@ impl RecursiveModuleLoad {
self.is_currently_loading_main_module(), self.is_currently_loading_main_module(),
&module_source.module_url_found, &module_source.module_url_found,
&module_source.code, &module_source.code,
self.is_dynamic_import(),
)? )?
} }
ModuleType::Json => self.module_map_rc.borrow_mut().new_json_module( ModuleType::Json => self.module_map_rc.borrow_mut().new_json_module(
@ -868,6 +898,7 @@ impl ModuleMap {
main: bool, main: bool,
name: &str, name: &str,
source: &[u8], source: &[u8],
is_dynamic_import: bool,
) -> Result<ModuleId, ModuleError> { ) -> Result<ModuleId, ModuleError> {
let name_str = v8::String::new(scope, name).unwrap(); let name_str = v8::String::new(scope, name).unwrap();
let source_str = let source_str =
@ -918,11 +949,18 @@ impl ModuleMap {
return Err(ModuleError::Exception(exception)); return Err(ModuleError::Exception(exception));
} }
let module_specifier = let module_specifier = match self.loader.resolve(
match self.loader.resolve(&import_specifier, name, false) { &import_specifier,
Ok(s) => s, name,
Err(e) => return Err(ModuleError::Other(e)), if is_dynamic_import {
}; ResolutionKind::DynamicImport
} else {
ResolutionKind::Import
},
) {
Ok(s) => s,
Err(e) => return Err(ModuleError::Other(e)),
};
let asserted_module_type = let asserted_module_type =
get_asserted_module_type_from_assertions(&assertions); get_asserted_module_type_from_assertions(&assertions);
let request = ModuleRequest { let request = ModuleRequest {
@ -1082,10 +1120,11 @@ impl ModuleMap {
.borrow_mut() .borrow_mut()
.dynamic_import_map .dynamic_import_map
.insert(load.id, resolver_handle); .insert(load.id, resolver_handle);
let resolve_result = module_map_rc let resolve_result = module_map_rc.borrow().loader.resolve(
.borrow() specifier,
.loader referrer,
.resolve(specifier, referrer, false); ResolutionKind::DynamicImport,
);
let fut = match resolve_result { let fut = match resolve_result {
Ok(module_specifier) => { Ok(module_specifier) => {
if module_map_rc if module_map_rc
@ -1121,7 +1160,7 @@ impl ModuleMap {
) -> Option<v8::Local<'s, v8::Module>> { ) -> Option<v8::Local<'s, v8::Module>> {
let resolved_specifier = self let resolved_specifier = self
.loader .loader
.resolve(specifier, referrer, false) .resolve(specifier, referrer, ResolutionKind::Import)
.expect("Module should have been already resolved"); .expect("Module should have been already resolved");
let module_type = let module_type =
@ -1332,7 +1371,7 @@ import "/a.js";
&self, &self,
specifier: &str, specifier: &str,
referrer: &str, referrer: &str,
_is_root: bool, _kind: ResolutionKind,
) -> Result<ModuleSpecifier, Error> { ) -> Result<ModuleSpecifier, Error> {
let referrer = if referrer == "." { let referrer = if referrer == "." {
"file:///" "file:///"
@ -1448,7 +1487,7 @@ import "/a.js";
&self, &self,
specifier: &str, specifier: &str,
referrer: &str, referrer: &str,
_is_main: bool, _kind: ResolutionKind,
) -> Result<ModuleSpecifier, Error> { ) -> Result<ModuleSpecifier, Error> {
self.count.fetch_add(1, Ordering::Relaxed); self.count.fetch_add(1, Ordering::Relaxed);
assert_eq!(specifier, "./b.js"); assert_eq!(specifier, "./b.js");
@ -1521,6 +1560,7 @@ import "/a.js";
let control = 42; let control = 42;
Deno.core.ops.op_test(control); Deno.core.ops.op_test(control);
"#, "#,
false,
) )
.unwrap(); .unwrap();
@ -1540,6 +1580,7 @@ import "/a.js";
false, false,
"file:///b.js", "file:///b.js",
b"export function b() { return 'b' }", b"export function b() { return 'b' }",
false,
) )
.unwrap(); .unwrap();
let imports = module_map.get_requested_modules(mod_b).unwrap(); let imports = module_map.get_requested_modules(mod_b).unwrap();
@ -1570,7 +1611,7 @@ import "/a.js";
&self, &self,
specifier: &str, specifier: &str,
referrer: &str, referrer: &str,
_is_main: bool, _kind: ResolutionKind,
) -> Result<ModuleSpecifier, Error> { ) -> Result<ModuleSpecifier, Error> {
self.count.fetch_add(1, Ordering::Relaxed); self.count.fetch_add(1, Ordering::Relaxed);
assert_eq!(specifier, "./b.json"); assert_eq!(specifier, "./b.json");
@ -1627,6 +1668,7 @@ import "/a.js";
assert(jsonData.a == "b"); assert(jsonData.a == "b");
assert(jsonData.c.d == 10); assert(jsonData.c.d == 10);
"#, "#,
false,
) )
.unwrap(); .unwrap();
@ -1673,7 +1715,7 @@ import "/a.js";
&self, &self,
specifier: &str, specifier: &str,
referrer: &str, referrer: &str,
_is_main: bool, _kind: ResolutionKind,
) -> Result<ModuleSpecifier, Error> { ) -> Result<ModuleSpecifier, Error> {
self.count.fetch_add(1, Ordering::Relaxed); self.count.fetch_add(1, Ordering::Relaxed);
assert_eq!(specifier, "/foo.js"); assert_eq!(specifier, "/foo.js");
@ -1733,7 +1775,7 @@ import "/a.js";
&self, &self,
specifier: &str, specifier: &str,
referrer: &str, referrer: &str,
_is_main: bool, _kind: ResolutionKind,
) -> Result<ModuleSpecifier, Error> { ) -> Result<ModuleSpecifier, Error> {
let c = self.resolve_count.fetch_add(1, Ordering::Relaxed); let c = self.resolve_count.fetch_add(1, Ordering::Relaxed);
assert!(c < 7); assert!(c < 7);
@ -1866,7 +1908,7 @@ import "/a.js";
&self, &self,
specifier: &str, specifier: &str,
referrer: &str, referrer: &str,
_is_main: bool, _kind: ResolutionKind,
) -> Result<ModuleSpecifier, Error> { ) -> Result<ModuleSpecifier, Error> {
self.resolve_count.fetch_add(1, Ordering::Relaxed); self.resolve_count.fetch_add(1, Ordering::Relaxed);
let s = resolve_import(specifier, referrer).unwrap(); let s = resolve_import(specifier, referrer).unwrap();
@ -2242,7 +2284,7 @@ if (import.meta.url != 'file:///main_with_code.js') throw Error();
&self, &self,
specifier: &str, specifier: &str,
referrer: &str, referrer: &str,
_is_main: bool, _kind: ResolutionKind,
) -> Result<ModuleSpecifier, Error> { ) -> Result<ModuleSpecifier, Error> {
let s = resolve_import(specifier, referrer).unwrap(); let s = resolve_import(specifier, referrer).unwrap();
Ok(s) Ok(s)

View file

@ -2038,6 +2038,7 @@ impl JsRuntime {
true, true,
specifier.as_str(), specifier.as_str(),
code.as_bytes(), code.as_bytes(),
false,
) )
.map_err(|e| match e { .map_err(|e| match e {
ModuleError::Exception(exception) => { ModuleError::Exception(exception) => {
@ -2097,6 +2098,7 @@ impl JsRuntime {
false, false,
specifier.as_str(), specifier.as_str(),
code.as_bytes(), code.as_bytes(),
false,
) )
.map_err(|e| match e { .map_err(|e| match e {
ModuleError::Exception(exception) => { ModuleError::Exception(exception) => {
@ -2492,6 +2494,7 @@ pub mod tests {
use crate::modules::ModuleSource; use crate::modules::ModuleSource;
use crate::modules::ModuleSourceFuture; use crate::modules::ModuleSourceFuture;
use crate::modules::ModuleType; use crate::modules::ModuleType;
use crate::modules::ResolutionKind;
use crate::ZeroCopyBuf; use crate::ZeroCopyBuf;
use deno_ops::op; use deno_ops::op;
use futures::future::lazy; use futures::future::lazy;
@ -3132,7 +3135,7 @@ pub mod tests {
&self, &self,
specifier: &str, specifier: &str,
referrer: &str, referrer: &str,
_is_main: bool, _kind: ResolutionKind,
) -> Result<ModuleSpecifier, Error> { ) -> Result<ModuleSpecifier, Error> {
assert_eq!(specifier, "file:///main.js"); assert_eq!(specifier, "file:///main.js");
assert_eq!(referrer, "."); assert_eq!(referrer, ".");
@ -3302,7 +3305,7 @@ pub mod tests {
&self, &self,
specifier: &str, specifier: &str,
referrer: &str, referrer: &str,
_is_main: bool, _kind: ResolutionKind,
) -> Result<ModuleSpecifier, Error> { ) -> Result<ModuleSpecifier, Error> {
assert_eq!(specifier, "file:///main.js"); assert_eq!(specifier, "file:///main.js");
assert_eq!(referrer, "."); assert_eq!(referrer, ".");
@ -3869,7 +3872,7 @@ Deno.core.ops.op_async_serialize_object_with_numbers_as_keys({
&self, &self,
specifier: &str, specifier: &str,
referrer: &str, referrer: &str,
_is_main: bool, _kind: ResolutionKind,
) -> Result<ModuleSpecifier, Error> { ) -> Result<ModuleSpecifier, Error> {
assert_eq!(specifier, "file:///main.js"); assert_eq!(specifier, "file:///main.js");
assert_eq!(referrer, "."); assert_eq!(referrer, ".");
@ -4001,7 +4004,7 @@ Deno.core.ops.op_async_serialize_object_with_numbers_as_keys({
&self, &self,
specifier: &str, specifier: &str,
referrer: &str, referrer: &str,
_is_main: bool, _kind: ResolutionKind,
) -> Result<ModuleSpecifier, Error> { ) -> Result<ModuleSpecifier, Error> {
assert_eq!(specifier, "file:///main.js"); assert_eq!(specifier, "file:///main.js");
assert_eq!(referrer, "."); assert_eq!(referrer, ".");

View file

@ -53,7 +53,11 @@ pub trait RequireNpmResolver {
fn in_npm_package(&self, path: &Path) -> bool; 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"); pub const MODULE_ES_SHIM: &str = include_str!("./module_es_shim.js");
@ -95,7 +99,7 @@ pub fn init<P: NodePermissions + 'static>(
op_require_is_request_relative::decl(), op_require_is_request_relative::decl(),
op_require_resolve_lookup_paths::decl(), op_require_resolve_lookup_paths::decl(),
op_require_try_self_parent_path::decl::<P>(), op_require_try_self_parent_path::decl::<P>(),
op_require_try_self::decl(), op_require_try_self::decl::<P>(),
op_require_real_path::decl::<P>(), op_require_real_path::decl::<P>(),
op_require_path_is_absolute::decl(), op_require_path_is_absolute::decl(),
op_require_path_dirname::decl(), op_require_path_dirname::decl(),
@ -104,9 +108,9 @@ pub fn init<P: NodePermissions + 'static>(
op_require_path_basename::decl(), op_require_path_basename::decl(),
op_require_read_file::decl::<P>(), op_require_read_file::decl::<P>(),
op_require_as_file_path::decl(), op_require_as_file_path::decl(),
op_require_resolve_exports::decl(), op_require_resolve_exports::decl::<P>(),
op_require_read_closest_package_json::decl::<P>(), op_require_read_closest_package_json::decl::<P>(),
op_require_read_package_scope::decl(), op_require_read_package_scope::decl::<P>(),
op_require_package_imports_resolve::decl::<P>(), op_require_package_imports_resolve::decl::<P>(),
op_require_break_on_next_statement::decl(), op_require_break_on_next_statement::decl(),
]) ])
@ -130,11 +134,8 @@ where
let resolver = state.borrow::<Rc<dyn RequireNpmResolver>>(); let resolver = state.borrow::<Rc<dyn RequireNpmResolver>>();
resolver.clone() resolver.clone()
}; };
if resolver.ensure_read_permission(file_path).is_ok() { let permissions = state.borrow_mut::<P>();
return Ok(()); resolver.ensure_read_permission(permissions, file_path)
}
state.borrow_mut::<P>().check_read(file_path)
} }
#[op] #[op]
@ -459,19 +460,24 @@ where
} }
#[op] #[op]
fn op_require_try_self( fn op_require_try_self<P>(
state: &mut OpState, state: &mut OpState,
parent_path: Option<String>, parent_path: Option<String>,
request: String, request: String,
) -> Result<Option<String>, AnyError> { ) -> Result<Option<String>, AnyError>
where
P: NodePermissions + 'static,
{
if parent_path.is_none() { if parent_path.is_none() {
return Ok(None); return Ok(None);
} }
let resolver = state.borrow::<Rc<dyn RequireNpmResolver>>().clone(); let resolver = state.borrow::<Rc<dyn RequireNpmResolver>>().clone();
let permissions = state.borrow_mut::<P>();
let pkg = resolution::get_package_scope_config( let pkg = resolution::get_package_scope_config(
&Url::from_file_path(parent_path.unwrap()).unwrap(), &Url::from_file_path(parent_path.unwrap()).unwrap(),
&*resolver, &*resolver,
permissions,
) )
.ok(); .ok();
if pkg.is_none() { if pkg.is_none() {
@ -508,6 +514,7 @@ fn op_require_try_self(
resolution::REQUIRE_CONDITIONS, resolution::REQUIRE_CONDITIONS,
NodeResolutionMode::Execution, NodeResolutionMode::Execution,
&*resolver, &*resolver,
permissions,
) )
.map(|r| Some(r.to_string_lossy().to_string())) .map(|r| Some(r.to_string_lossy().to_string()))
} else { } else {
@ -540,7 +547,7 @@ pub fn op_require_as_file_path(file_or_url: String) -> String {
} }
#[op] #[op]
fn op_require_resolve_exports( fn op_require_resolve_exports<P>(
state: &mut OpState, state: &mut OpState,
uses_local_node_modules_dir: bool, uses_local_node_modules_dir: bool,
modules_path: String, modules_path: String,
@ -548,8 +555,12 @@ fn op_require_resolve_exports(
name: String, name: String,
expansion: String, expansion: String,
parent_path: String, parent_path: String,
) -> Result<Option<String>, AnyError> { ) -> Result<Option<String>, AnyError>
where
P: NodePermissions + 'static,
{
let resolver = state.borrow::<Rc<dyn RequireNpmResolver>>().clone(); let resolver = state.borrow::<Rc<dyn RequireNpmResolver>>().clone();
let permissions = state.borrow_mut::<P>();
let pkg_path = if resolver.in_npm_package(&PathBuf::from(&modules_path)) let pkg_path = if resolver.in_npm_package(&PathBuf::from(&modules_path))
&& !uses_local_node_modules_dir && !uses_local_node_modules_dir
@ -560,6 +571,7 @@ fn op_require_resolve_exports(
}; };
let pkg = PackageJson::load( let pkg = PackageJson::load(
&*resolver, &*resolver,
permissions,
PathBuf::from(&pkg_path).join("package.json"), PathBuf::from(&pkg_path).join("package.json"),
)?; )?;
@ -574,6 +586,7 @@ fn op_require_resolve_exports(
resolution::REQUIRE_CONDITIONS, resolution::REQUIRE_CONDITIONS,
NodeResolutionMode::Execution, NodeResolutionMode::Execution,
&*resolver, &*resolver,
permissions,
) )
.map(|r| Some(r.to_string_lossy().to_string())) .map(|r| Some(r.to_string_lossy().to_string()))
} else { } else {
@ -594,20 +607,26 @@ where
PathBuf::from(&filename).parent().unwrap(), PathBuf::from(&filename).parent().unwrap(),
)?; )?;
let resolver = state.borrow::<Rc<dyn RequireNpmResolver>>().clone(); let resolver = state.borrow::<Rc<dyn RequireNpmResolver>>().clone();
let permissions = state.borrow_mut::<P>();
resolution::get_closest_package_json( resolution::get_closest_package_json(
&Url::from_file_path(filename).unwrap(), &Url::from_file_path(filename).unwrap(),
&*resolver, &*resolver,
permissions,
) )
} }
#[op] #[op]
fn op_require_read_package_scope( fn op_require_read_package_scope<P>(
state: &mut OpState, state: &mut OpState,
package_json_path: String, package_json_path: String,
) -> Option<PackageJson> { ) -> Option<PackageJson>
where
P: NodePermissions + 'static,
{
let resolver = state.borrow::<Rc<dyn RequireNpmResolver>>().clone(); let resolver = state.borrow::<Rc<dyn RequireNpmResolver>>().clone();
let permissions = state.borrow_mut::<P>();
let package_json_path = PathBuf::from(package_json_path); 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] #[op]
@ -622,7 +641,12 @@ where
let parent_path = PathBuf::from(&parent_filename); let parent_path = PathBuf::from(&parent_filename);
ensure_read_permission::<P>(state, &parent_path)?; ensure_read_permission::<P>(state, &parent_path)?;
let resolver = state.borrow::<Rc<dyn RequireNpmResolver>>().clone(); let resolver = state.borrow::<Rc<dyn RequireNpmResolver>>().clone();
let pkg = PackageJson::load(&*resolver, parent_path.join("package.json"))?; let permissions = state.borrow_mut::<P>();
let pkg = PackageJson::load(
&*resolver,
permissions,
parent_path.join("package.json"),
)?;
if pkg.imports.is_some() { if pkg.imports.is_some() {
let referrer = let referrer =
@ -634,6 +658,7 @@ where
resolution::REQUIRE_CONDITIONS, resolution::REQUIRE_CONDITIONS,
NodeResolutionMode::Execution, NodeResolutionMode::Execution,
&*resolver, &*resolver,
permissions,
) )
.map(|r| Some(Url::from_file_path(r).unwrap().to_string())); .map(|r| Some(Url::from_file_path(r).unwrap().to_string()));
state.put(resolver); state.put(resolver);

View file

@ -1,6 +1,7 @@
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license. // Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
use crate::NodeModuleKind; use crate::NodeModuleKind;
use crate::NodePermissions;
use super::RequireNpmResolver; use super::RequireNpmResolver;
use deno_core::anyhow; use deno_core::anyhow;
@ -47,9 +48,10 @@ impl PackageJson {
pub fn load( pub fn load(
resolver: &dyn RequireNpmResolver, resolver: &dyn RequireNpmResolver,
permissions: &mut dyn NodePermissions,
path: PathBuf, path: PathBuf,
) -> Result<PackageJson, AnyError> { ) -> Result<PackageJson, AnyError> {
resolver.ensure_read_permission(&path)?; resolver.ensure_read_permission(permissions, &path)?;
Self::load_skip_read_permission(path) Self::load_skip_read_permission(path)
} }

View file

@ -15,6 +15,7 @@ use regex::Regex;
use crate::errors; use crate::errors;
use crate::package_json::PackageJson; use crate::package_json::PackageJson;
use crate::path::PathClean; use crate::path::PathClean;
use crate::NodePermissions;
use crate::RequireNpmResolver; use crate::RequireNpmResolver;
pub static DEFAULT_CONDITIONS: &[&str] = &["deno", "node", "import"]; pub static DEFAULT_CONDITIONS: &[&str] = &["deno", "node", "import"];
@ -188,6 +189,7 @@ pub fn package_imports_resolve(
conditions: &[&str], conditions: &[&str],
mode: NodeResolutionMode, mode: NodeResolutionMode,
npm_resolver: &dyn RequireNpmResolver, npm_resolver: &dyn RequireNpmResolver,
permissions: &mut dyn NodePermissions,
) -> Result<PathBuf, AnyError> { ) -> Result<PathBuf, AnyError> {
if name == "#" || name.starts_with("#/") || name.ends_with('/') { if name == "#" || name.starts_with("#/") || name.ends_with('/') {
let reason = "is not a valid internal imports specifier name"; 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; let mut package_json_path = None;
if package_config.exists { if package_config.exists {
package_json_path = Some(package_config.path.clone()); package_json_path = Some(package_config.path.clone());
@ -216,6 +219,7 @@ pub fn package_imports_resolve(
conditions, conditions,
mode, mode,
npm_resolver, npm_resolver,
permissions,
)?; )?;
if let Some(resolved) = maybe_resolved { if let Some(resolved) = maybe_resolved {
return Ok(resolved); return Ok(resolved);
@ -258,6 +262,7 @@ pub fn package_imports_resolve(
conditions, conditions,
mode, mode,
npm_resolver, npm_resolver,
permissions,
)?; )?;
if let Some(resolved) = maybe_resolved { if let Some(resolved) = maybe_resolved {
return Ok(resolved); return Ok(resolved);
@ -322,6 +327,7 @@ fn resolve_package_target_string(
conditions: &[&str], conditions: &[&str],
mode: NodeResolutionMode, mode: NodeResolutionMode,
npm_resolver: &dyn RequireNpmResolver, npm_resolver: &dyn RequireNpmResolver,
permissions: &mut dyn NodePermissions,
) -> Result<PathBuf, AnyError> { ) -> Result<PathBuf, AnyError> {
if !subpath.is_empty() && !pattern && !target.ends_with('/') { if !subpath.is_empty() && !pattern && !target.ends_with('/') {
return Err(throw_invalid_package_target( return Err(throw_invalid_package_target(
@ -355,6 +361,7 @@ fn resolve_package_target_string(
conditions, conditions,
mode, mode,
npm_resolver, npm_resolver,
permissions,
) { ) {
Ok(Some(path)) => Ok(path), Ok(Some(path)) => Ok(path),
Ok(None) => Err(generic_error("not found")), Ok(None) => Err(generic_error("not found")),
@ -430,6 +437,7 @@ fn resolve_package_target(
conditions: &[&str], conditions: &[&str],
mode: NodeResolutionMode, mode: NodeResolutionMode,
npm_resolver: &dyn RequireNpmResolver, npm_resolver: &dyn RequireNpmResolver,
permissions: &mut dyn NodePermissions,
) -> Result<Option<PathBuf>, AnyError> { ) -> Result<Option<PathBuf>, AnyError> {
if let Some(target) = target.as_str() { if let Some(target) = target.as_str() {
return resolve_package_target_string( return resolve_package_target_string(
@ -444,6 +452,7 @@ fn resolve_package_target(
conditions, conditions,
mode, mode,
npm_resolver, npm_resolver,
permissions,
) )
.map(|path| { .map(|path| {
if mode.is_types() { if mode.is_types() {
@ -471,6 +480,7 @@ fn resolve_package_target(
conditions, conditions,
mode, mode,
npm_resolver, npm_resolver,
permissions,
); );
match resolved_result { match resolved_result {
@ -520,6 +530,7 @@ fn resolve_package_target(
conditions, conditions,
mode, mode,
npm_resolver, npm_resolver,
permissions,
)?; )?;
match resolved { match resolved {
Some(resolved) => return Ok(Some(resolved)), Some(resolved) => return Ok(Some(resolved)),
@ -564,6 +575,7 @@ pub fn package_exports_resolve(
conditions: &[&str], conditions: &[&str],
mode: NodeResolutionMode, mode: NodeResolutionMode,
npm_resolver: &dyn RequireNpmResolver, npm_resolver: &dyn RequireNpmResolver,
permissions: &mut dyn NodePermissions,
) -> Result<PathBuf, AnyError> { ) -> Result<PathBuf, AnyError> {
if package_exports.contains_key(&package_subpath) if package_exports.contains_key(&package_subpath)
&& package_subpath.find('*').is_none() && package_subpath.find('*').is_none()
@ -582,6 +594,7 @@ pub fn package_exports_resolve(
conditions, conditions,
mode, mode,
npm_resolver, npm_resolver,
permissions,
)?; )?;
if resolved.is_none() { if resolved.is_none() {
return Err(throw_exports_not_found( return Err(throw_exports_not_found(
@ -641,6 +654,7 @@ pub fn package_exports_resolve(
conditions, conditions,
mode, mode,
npm_resolver, npm_resolver,
permissions,
)?; )?;
if let Some(resolved) = maybe_resolved { if let Some(resolved) = maybe_resolved {
return Ok(resolved); return Ok(resolved);
@ -718,12 +732,14 @@ pub fn package_resolve(
conditions: &[&str], conditions: &[&str],
mode: NodeResolutionMode, mode: NodeResolutionMode,
npm_resolver: &dyn RequireNpmResolver, npm_resolver: &dyn RequireNpmResolver,
permissions: &mut dyn NodePermissions,
) -> Result<Option<PathBuf>, AnyError> { ) -> Result<Option<PathBuf>, AnyError> {
let (package_name, package_subpath, _is_scoped) = let (package_name, package_subpath, _is_scoped) =
parse_package_name(specifier, referrer)?; parse_package_name(specifier, referrer)?;
// ResolveSelf // 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 if package_config.exists
&& package_config.name.as_ref() == Some(&package_name) && package_config.name.as_ref() == Some(&package_name)
{ {
@ -737,6 +753,7 @@ pub fn package_resolve(
conditions, conditions,
mode, mode,
npm_resolver, npm_resolver,
permissions,
) )
.map(Some); .map(Some);
} }
@ -763,7 +780,8 @@ pub fn package_resolve(
// )) // ))
// Package match. // 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 { if let Some(exports) = &package_json.exports {
return package_exports_resolve( return package_exports_resolve(
&package_json.path, &package_json.path,
@ -774,6 +792,7 @@ pub fn package_resolve(
conditions, conditions,
mode, mode,
npm_resolver, npm_resolver,
permissions,
) )
.map(Some); .map(Some);
} }
@ -795,19 +814,21 @@ pub fn package_resolve(
pub fn get_package_scope_config( pub fn get_package_scope_config(
referrer: &ModuleSpecifier, referrer: &ModuleSpecifier,
npm_resolver: &dyn RequireNpmResolver, npm_resolver: &dyn RequireNpmResolver,
permissions: &mut dyn NodePermissions,
) -> Result<PackageJson, AnyError> { ) -> Result<PackageJson, AnyError> {
let root_folder = npm_resolver let root_folder = npm_resolver
.resolve_package_folder_from_path(&referrer.to_file_path().unwrap())?; .resolve_package_folder_from_path(&referrer.to_file_path().unwrap())?;
let package_json_path = root_folder.join("package.json"); 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( pub fn get_closest_package_json(
url: &ModuleSpecifier, url: &ModuleSpecifier,
npm_resolver: &dyn RequireNpmResolver, npm_resolver: &dyn RequireNpmResolver,
permissions: &mut dyn NodePermissions,
) -> Result<PackageJson, AnyError> { ) -> Result<PackageJson, AnyError> {
let package_json_path = get_closest_package_json_path(url, npm_resolver)?; 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( fn get_closest_package_json_path(