mirror of
https://github.com/denoland/deno.git
synced 2025-01-20 04:36:16 -05:00
refactor(node/npm): separate out permission check from npm resolvers (#27511)
Decouples permissions from the npm resolvers (towards moving the resolvers out of the cli crate)
This commit is contained in:
parent
79c0b2ce73
commit
9215aa60a6
12 changed files with 185 additions and 191 deletions
|
@ -71,6 +71,8 @@ use crate::npm::CliNpmResolver;
|
|||
use crate::npm::CliNpmResolverCreateOptions;
|
||||
use crate::npm::CliNpmResolverManagedSnapshotOption;
|
||||
use crate::npm::CreateInNpmPkgCheckerOptions;
|
||||
use crate::npm::NpmRegistryReadPermissionChecker;
|
||||
use crate::npm::NpmRegistryReadPermissionCheckerMode;
|
||||
use crate::resolver::CjsTracker;
|
||||
use crate::resolver::CliDenoResolver;
|
||||
use crate::resolver::CliNpmReqResolver;
|
||||
|
@ -941,6 +943,19 @@ impl CliFactory {
|
|||
let cjs_tracker = self.cjs_tracker()?.clone();
|
||||
let pkg_json_resolver = self.pkg_json_resolver().clone();
|
||||
let npm_req_resolver = self.npm_req_resolver().await?;
|
||||
let npm_registry_permission_checker = {
|
||||
let mode = if cli_options.use_byonm() {
|
||||
NpmRegistryReadPermissionCheckerMode::Byonm
|
||||
} else if let Some(node_modules_dir) = cli_options.node_modules_dir_path()
|
||||
{
|
||||
NpmRegistryReadPermissionCheckerMode::Local(node_modules_dir.clone())
|
||||
} else {
|
||||
NpmRegistryReadPermissionCheckerMode::Global(
|
||||
self.npm_cache_dir()?.root_dir().to_path_buf(),
|
||||
)
|
||||
};
|
||||
Arc::new(NpmRegistryReadPermissionChecker::new(self.sys(), mode))
|
||||
};
|
||||
|
||||
Ok(CliMainWorkerFactory::new(
|
||||
self.blob_store().clone(),
|
||||
|
@ -968,13 +983,14 @@ impl CliFactory {
|
|||
self.module_load_preparer().await?.clone(),
|
||||
node_code_translator.clone(),
|
||||
node_resolver.clone(),
|
||||
npm_req_resolver.clone(),
|
||||
cli_npm_resolver.clone(),
|
||||
NpmModuleLoader::new(
|
||||
self.cjs_tracker()?.clone(),
|
||||
fs.clone(),
|
||||
node_code_translator.clone(),
|
||||
),
|
||||
npm_registry_permission_checker,
|
||||
npm_req_resolver.clone(),
|
||||
cli_npm_resolver.clone(),
|
||||
self.parsed_source_cache().clone(),
|
||||
self.resolver().await?.clone(),
|
||||
self.sys(),
|
||||
|
|
|
@ -66,6 +66,7 @@ use crate::graph_util::ModuleGraphBuilder;
|
|||
use crate::node::CliNodeCodeTranslator;
|
||||
use crate::node::CliNodeResolver;
|
||||
use crate::npm::CliNpmResolver;
|
||||
use crate::npm::NpmRegistryReadPermissionChecker;
|
||||
use crate::resolver::CjsTracker;
|
||||
use crate::resolver::CliNpmReqResolver;
|
||||
use crate::resolver::CliResolver;
|
||||
|
@ -221,9 +222,10 @@ struct SharedCliModuleLoaderState {
|
|||
module_load_preparer: Arc<ModuleLoadPreparer>,
|
||||
node_code_translator: Arc<CliNodeCodeTranslator>,
|
||||
node_resolver: Arc<CliNodeResolver>,
|
||||
npm_module_loader: NpmModuleLoader,
|
||||
npm_registry_permission_checker: Arc<NpmRegistryReadPermissionChecker>,
|
||||
npm_req_resolver: Arc<CliNpmReqResolver>,
|
||||
npm_resolver: Arc<dyn CliNpmResolver>,
|
||||
npm_module_loader: NpmModuleLoader,
|
||||
parsed_source_cache: Arc<ParsedSourceCache>,
|
||||
resolver: Arc<CliResolver>,
|
||||
sys: CliSys,
|
||||
|
@ -281,9 +283,10 @@ impl CliModuleLoaderFactory {
|
|||
module_load_preparer: Arc<ModuleLoadPreparer>,
|
||||
node_code_translator: Arc<CliNodeCodeTranslator>,
|
||||
node_resolver: Arc<CliNodeResolver>,
|
||||
npm_module_loader: NpmModuleLoader,
|
||||
npm_registry_permission_checker: Arc<NpmRegistryReadPermissionChecker>,
|
||||
npm_req_resolver: Arc<CliNpmReqResolver>,
|
||||
npm_resolver: Arc<dyn CliNpmResolver>,
|
||||
npm_module_loader: NpmModuleLoader,
|
||||
parsed_source_cache: Arc<ParsedSourceCache>,
|
||||
resolver: Arc<CliResolver>,
|
||||
sys: CliSys,
|
||||
|
@ -307,9 +310,10 @@ impl CliModuleLoaderFactory {
|
|||
module_load_preparer,
|
||||
node_code_translator,
|
||||
node_resolver,
|
||||
npm_module_loader,
|
||||
npm_registry_permission_checker,
|
||||
npm_req_resolver,
|
||||
npm_resolver,
|
||||
npm_module_loader,
|
||||
parsed_source_cache,
|
||||
resolver,
|
||||
sys,
|
||||
|
@ -348,7 +352,10 @@ impl CliModuleLoaderFactory {
|
|||
sys: self.shared.sys.clone(),
|
||||
graph_container,
|
||||
in_npm_pkg_checker: self.shared.in_npm_pkg_checker.clone(),
|
||||
npm_resolver: self.shared.npm_resolver.clone(),
|
||||
npm_registry_permission_checker: self
|
||||
.shared
|
||||
.npm_registry_permission_checker
|
||||
.clone(),
|
||||
});
|
||||
CreateModuleLoaderResult {
|
||||
module_loader,
|
||||
|
@ -1095,7 +1102,7 @@ struct CliNodeRequireLoader<TGraphContainer: ModuleGraphContainer> {
|
|||
sys: CliSys,
|
||||
graph_container: TGraphContainer,
|
||||
in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
|
||||
npm_resolver: Arc<dyn CliNpmResolver>,
|
||||
npm_registry_permission_checker: Arc<NpmRegistryReadPermissionChecker>,
|
||||
}
|
||||
|
||||
impl<TGraphContainer: ModuleGraphContainer> NodeRequireLoader
|
||||
|
@ -1112,7 +1119,9 @@ impl<TGraphContainer: ModuleGraphContainer> NodeRequireLoader
|
|||
return Ok(std::borrow::Cow::Borrowed(path));
|
||||
}
|
||||
}
|
||||
self.npm_resolver.ensure_read_permission(permissions, path)
|
||||
self
|
||||
.npm_registry_permission_checker
|
||||
.ensure_read_permission(permissions, path)
|
||||
}
|
||||
|
||||
fn load_text_file_lossy(
|
||||
|
|
|
@ -1,15 +1,12 @@
|
|||
// Copyright 2018-2025 the Deno authors. MIT license.
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::serde_json;
|
||||
use deno_resolver::npm::ByonmNpmResolver;
|
||||
use deno_resolver::npm::ByonmNpmResolverCreateOptions;
|
||||
use deno_resolver::npm::CliNpmReqResolver;
|
||||
use deno_runtime::deno_node::NodePermissions;
|
||||
use deno_runtime::ops::process::NpmProcessStateProvider;
|
||||
use node_resolver::NpmPackageFolderResolver;
|
||||
|
||||
|
@ -73,21 +70,6 @@ impl CliNpmResolver for CliByonmNpmResolver {
|
|||
self.root_node_modules_dir()
|
||||
}
|
||||
|
||||
fn ensure_read_permission<'a>(
|
||||
&self,
|
||||
permissions: &mut dyn NodePermissions,
|
||||
path: &'a Path,
|
||||
) -> Result<Cow<'a, Path>, AnyError> {
|
||||
if !path
|
||||
.components()
|
||||
.any(|c| c.as_os_str().to_ascii_lowercase() == "node_modules")
|
||||
{
|
||||
permissions.check_read_path(path).map_err(Into::into)
|
||||
} else {
|
||||
Ok(Cow::Borrowed(path))
|
||||
}
|
||||
}
|
||||
|
||||
fn check_state_hash(&self) -> Option<u64> {
|
||||
// it is very difficult to determine the check state hash for byonm
|
||||
// so we just return None to signify check caching is not supported
|
||||
|
|
|
@ -24,7 +24,6 @@ use deno_npm_cache::NpmCacheSetting;
|
|||
use deno_path_util::fs::canonicalize_path_maybe_not_exists;
|
||||
use deno_resolver::npm::CliNpmReqResolver;
|
||||
use deno_runtime::colors;
|
||||
use deno_runtime::deno_node::NodePermissions;
|
||||
use deno_runtime::ops::process::NpmProcessStateProvider;
|
||||
use deno_semver::package::PackageNv;
|
||||
use deno_semver::package::PackageReq;
|
||||
|
@ -167,6 +166,7 @@ fn create_inner(
|
|||
sys.clone(),
|
||||
npm_rc.clone(),
|
||||
));
|
||||
|
||||
let fs_resolver = create_npm_fs_resolver(
|
||||
npm_cache.clone(),
|
||||
&npm_install_deps_provider,
|
||||
|
@ -754,14 +754,6 @@ impl CliNpmResolver for ManagedCliNpmResolver {
|
|||
self.fs_resolver.node_modules_path()
|
||||
}
|
||||
|
||||
fn ensure_read_permission<'a>(
|
||||
&self,
|
||||
permissions: &mut dyn NodePermissions,
|
||||
path: &'a Path,
|
||||
) -> Result<Cow<'a, Path>, AnyError> {
|
||||
self.fs_resolver.ensure_read_permission(permissions, path)
|
||||
}
|
||||
|
||||
fn check_state_hash(&self) -> Option<u64> {
|
||||
// We could go further and check all the individual
|
||||
// npm packages, but that's probably overkill.
|
||||
|
|
|
@ -3,30 +3,17 @@
|
|||
pub mod bin_entries;
|
||||
pub mod lifecycle_scripts;
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
use std::io::ErrorKind;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use async_trait::async_trait;
|
||||
use deno_ast::ModuleSpecifier;
|
||||
use deno_core::anyhow::Context;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::futures;
|
||||
use deno_core::futures::StreamExt;
|
||||
use deno_npm::NpmPackageCacheFolderId;
|
||||
use deno_npm::NpmPackageId;
|
||||
use deno_npm::NpmResolutionPackage;
|
||||
use deno_runtime::deno_node::NodePermissions;
|
||||
use node_resolver::errors::PackageFolderResolveError;
|
||||
use sys_traits::FsCanonicalize;
|
||||
|
||||
use super::super::PackageCaching;
|
||||
use crate::npm::CliNpmTarballCache;
|
||||
use crate::sys::CliSys;
|
||||
|
||||
/// Part of the resolution that interacts with the file system.
|
||||
#[async_trait(?Send)]
|
||||
|
@ -63,101 +50,4 @@ pub trait NpmPackageFsResolver: Send + Sync {
|
|||
&self,
|
||||
caching: PackageCaching<'a>,
|
||||
) -> Result<(), AnyError>;
|
||||
|
||||
#[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
|
||||
fn ensure_read_permission<'a>(
|
||||
&self,
|
||||
permissions: &mut dyn NodePermissions,
|
||||
path: &'a Path,
|
||||
) -> Result<Cow<'a, Path>, AnyError>;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct RegistryReadPermissionChecker {
|
||||
sys: CliSys,
|
||||
cache: Mutex<HashMap<PathBuf, PathBuf>>,
|
||||
registry_path: PathBuf,
|
||||
}
|
||||
|
||||
impl RegistryReadPermissionChecker {
|
||||
pub fn new(sys: CliSys, registry_path: PathBuf) -> Self {
|
||||
Self {
|
||||
sys,
|
||||
registry_path,
|
||||
cache: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ensure_registry_read_permission<'a>(
|
||||
&self,
|
||||
permissions: &mut dyn NodePermissions,
|
||||
path: &'a Path,
|
||||
) -> Result<Cow<'a, Path>, AnyError> {
|
||||
if permissions.query_read_all() {
|
||||
return Ok(Cow::Borrowed(path)); // skip permissions checks below
|
||||
}
|
||||
|
||||
// allow reading if it's in the node_modules
|
||||
let is_path_in_node_modules = path.starts_with(&self.registry_path)
|
||||
&& path
|
||||
.components()
|
||||
.all(|c| !matches!(c, std::path::Component::ParentDir));
|
||||
|
||||
if is_path_in_node_modules {
|
||||
let mut cache = self.cache.lock().unwrap();
|
||||
let mut canonicalize =
|
||||
|path: &Path| -> Result<Option<PathBuf>, AnyError> {
|
||||
match cache.get(path) {
|
||||
Some(canon) => Ok(Some(canon.clone())),
|
||||
None => match self.sys.fs_canonicalize(path) {
|
||||
Ok(canon) => {
|
||||
cache.insert(path.to_path_buf(), canon.clone());
|
||||
Ok(Some(canon))
|
||||
}
|
||||
Err(e) => {
|
||||
if e.kind() == ErrorKind::NotFound {
|
||||
return Ok(None);
|
||||
}
|
||||
Err(AnyError::from(e)).with_context(|| {
|
||||
format!("failed canonicalizing '{}'", path.display())
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
if let Some(registry_path_canon) = canonicalize(&self.registry_path)? {
|
||||
if let Some(path_canon) = canonicalize(path)? {
|
||||
if path_canon.starts_with(registry_path_canon) {
|
||||
return Ok(Cow::Owned(path_canon));
|
||||
}
|
||||
} else if path.starts_with(registry_path_canon)
|
||||
|| path.starts_with(&self.registry_path)
|
||||
{
|
||||
return Ok(Cow::Borrowed(path));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
permissions.check_read_path(path).map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
/// Caches all the packages in parallel.
|
||||
pub async fn cache_packages(
|
||||
packages: &[NpmResolutionPackage],
|
||||
tarball_cache: &Arc<CliNpmTarballCache>,
|
||||
) -> Result<(), AnyError> {
|
||||
let mut futures_unordered = futures::stream::FuturesUnordered::new();
|
||||
for package in packages {
|
||||
futures_unordered.push(async move {
|
||||
tarball_cache
|
||||
.ensure_package(&package.id.nv, &package.dist)
|
||||
.await
|
||||
});
|
||||
}
|
||||
while let Some(result) = futures_unordered.next().await {
|
||||
// surface the first error
|
||||
result?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -10,27 +10,25 @@ use std::sync::Arc;
|
|||
use async_trait::async_trait;
|
||||
use deno_ast::ModuleSpecifier;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::futures::stream::FuturesUnordered;
|
||||
use deno_core::futures::StreamExt;
|
||||
use deno_npm::NpmPackageCacheFolderId;
|
||||
use deno_npm::NpmPackageId;
|
||||
use deno_npm::NpmResolutionPackage;
|
||||
use deno_npm::NpmSystemInfo;
|
||||
use deno_runtime::deno_node::NodePermissions;
|
||||
use node_resolver::errors::PackageFolderResolveError;
|
||||
use node_resolver::errors::PackageNotFoundError;
|
||||
use node_resolver::errors::ReferrerNotFoundError;
|
||||
|
||||
use super::super::resolution::NpmResolution;
|
||||
use super::common::cache_packages;
|
||||
use super::common::lifecycle_scripts::LifecycleScriptsStrategy;
|
||||
use super::common::NpmPackageFsResolver;
|
||||
use super::common::RegistryReadPermissionChecker;
|
||||
use crate::args::LifecycleScriptsConfig;
|
||||
use crate::cache::FastInsecureHasher;
|
||||
use crate::colors;
|
||||
use crate::npm::managed::PackageCaching;
|
||||
use crate::npm::CliNpmCache;
|
||||
use crate::npm::CliNpmTarballCache;
|
||||
use crate::sys::CliSys;
|
||||
|
||||
/// Resolves packages from the global npm cache.
|
||||
#[derive(Debug)]
|
||||
|
@ -39,7 +37,6 @@ pub struct GlobalNpmPackageResolver {
|
|||
tarball_cache: Arc<CliNpmTarballCache>,
|
||||
resolution: Arc<NpmResolution>,
|
||||
system_info: NpmSystemInfo,
|
||||
registry_read_permission_checker: RegistryReadPermissionChecker,
|
||||
lifecycle_scripts: LifecycleScriptsConfig,
|
||||
}
|
||||
|
||||
|
@ -48,15 +45,10 @@ impl GlobalNpmPackageResolver {
|
|||
cache: Arc<CliNpmCache>,
|
||||
tarball_cache: Arc<CliNpmTarballCache>,
|
||||
resolution: Arc<NpmResolution>,
|
||||
sys: CliSys,
|
||||
system_info: NpmSystemInfo,
|
||||
lifecycle_scripts: LifecycleScriptsConfig,
|
||||
) -> Self {
|
||||
Self {
|
||||
registry_read_permission_checker: RegistryReadPermissionChecker::new(
|
||||
sys,
|
||||
cache.root_dir_path().to_path_buf(),
|
||||
),
|
||||
cache,
|
||||
tarball_cache,
|
||||
resolution,
|
||||
|
@ -186,16 +178,25 @@ impl NpmPackageFsResolver for GlobalNpmPackageResolver {
|
|||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn ensure_read_permission<'a>(
|
||||
&self,
|
||||
permissions: &mut dyn NodePermissions,
|
||||
path: &'a Path,
|
||||
) -> Result<Cow<'a, Path>, AnyError> {
|
||||
self
|
||||
.registry_read_permission_checker
|
||||
.ensure_registry_read_permission(permissions, path)
|
||||
async fn cache_packages(
|
||||
packages: &[NpmResolutionPackage],
|
||||
tarball_cache: &Arc<CliNpmTarballCache>,
|
||||
) -> Result<(), AnyError> {
|
||||
let mut futures_unordered = FuturesUnordered::new();
|
||||
for package in packages {
|
||||
futures_unordered.push(async move {
|
||||
tarball_cache
|
||||
.ensure_package(&package.id.nv, &package.dist)
|
||||
.await
|
||||
});
|
||||
}
|
||||
while let Some(result) = futures_unordered.next().await {
|
||||
// surface the first error
|
||||
result?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
struct GlobalLifecycleScripts<'a> {
|
||||
|
|
|
@ -33,7 +33,6 @@ use deno_npm::NpmSystemInfo;
|
|||
use deno_path_util::fs::atomic_write_file_with_retries;
|
||||
use deno_path_util::fs::canonicalize_path_maybe_not_exists;
|
||||
use deno_resolver::npm::normalize_pkg_name_for_node_modules_deno_folder;
|
||||
use deno_runtime::deno_node::NodePermissions;
|
||||
use deno_semver::package::PackageNv;
|
||||
use deno_semver::StackString;
|
||||
use node_resolver::errors::PackageFolderResolveError;
|
||||
|
@ -47,7 +46,6 @@ use sys_traits::FsMetadata;
|
|||
use super::super::resolution::NpmResolution;
|
||||
use super::common::bin_entries;
|
||||
use super::common::NpmPackageFsResolver;
|
||||
use super::common::RegistryReadPermissionChecker;
|
||||
use crate::args::LifecycleScriptsConfig;
|
||||
use crate::args::NpmInstallDepsProvider;
|
||||
use crate::cache::CACHE_PERM;
|
||||
|
@ -75,7 +73,6 @@ pub struct LocalNpmPackageResolver {
|
|||
root_node_modules_path: PathBuf,
|
||||
root_node_modules_url: Url,
|
||||
system_info: NpmSystemInfo,
|
||||
registry_read_permission_checker: RegistryReadPermissionChecker,
|
||||
lifecycle_scripts: LifecycleScriptsConfig,
|
||||
}
|
||||
|
||||
|
@ -98,10 +95,6 @@ impl LocalNpmPackageResolver {
|
|||
progress_bar,
|
||||
resolution,
|
||||
tarball_cache,
|
||||
registry_read_permission_checker: RegistryReadPermissionChecker::new(
|
||||
sys.clone(),
|
||||
node_modules_folder.clone(),
|
||||
),
|
||||
sys,
|
||||
root_node_modules_url: Url::from_directory_path(&node_modules_folder)
|
||||
.unwrap(),
|
||||
|
@ -275,16 +268,6 @@ impl NpmPackageFsResolver for LocalNpmPackageResolver {
|
|||
)
|
||||
.await
|
||||
}
|
||||
|
||||
fn ensure_read_permission<'a>(
|
||||
&self,
|
||||
permissions: &mut dyn NodePermissions,
|
||||
path: &'a Path,
|
||||
) -> Result<Cow<'a, Path>, AnyError> {
|
||||
self
|
||||
.registry_read_permission_checker
|
||||
.ensure_registry_read_permission(permissions, path)
|
||||
}
|
||||
}
|
||||
|
||||
/// `node_modules/.deno/<package>/node_modules/<package_name>`
|
||||
|
|
|
@ -48,7 +48,6 @@ pub fn create_npm_fs_resolver(
|
|||
npm_cache,
|
||||
tarball_cache,
|
||||
resolution,
|
||||
sys,
|
||||
system_info,
|
||||
lifecycle_scripts,
|
||||
)),
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
mod byonm;
|
||||
mod managed;
|
||||
mod permission_checker;
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
|
||||
|
@ -17,7 +17,6 @@ use deno_resolver::npm::ByonmInNpmPackageChecker;
|
|||
use deno_resolver::npm::ByonmNpmResolver;
|
||||
use deno_resolver::npm::CliNpmReqResolver;
|
||||
use deno_resolver::npm::ResolvePkgFolderFromDenoReqError;
|
||||
use deno_runtime::deno_node::NodePermissions;
|
||||
use deno_runtime::ops::process::NpmProcessStateProvider;
|
||||
use deno_semver::package::PackageNv;
|
||||
use deno_semver::package::PackageReq;
|
||||
|
@ -34,6 +33,8 @@ pub use self::managed::CliManagedNpmResolverCreateOptions;
|
|||
pub use self::managed::CliNpmResolverManagedSnapshotOption;
|
||||
pub use self::managed::ManagedCliNpmResolver;
|
||||
pub use self::managed::PackageCaching;
|
||||
pub use self::permission_checker::NpmRegistryReadPermissionChecker;
|
||||
pub use self::permission_checker::NpmRegistryReadPermissionCheckerMode;
|
||||
use crate::file_fetcher::CliFileFetcher;
|
||||
use crate::http_util::HttpClientProvider;
|
||||
use crate::sys::CliSys;
|
||||
|
@ -183,12 +184,6 @@ pub trait CliNpmResolver: NpmPackageFolderResolver + CliNpmReqResolver {
|
|||
|
||||
fn root_node_modules_path(&self) -> Option<&Path>;
|
||||
|
||||
fn ensure_read_permission<'a>(
|
||||
&self,
|
||||
permissions: &mut dyn NodePermissions,
|
||||
path: &'a Path,
|
||||
) -> Result<Cow<'a, Path>, AnyError>;
|
||||
|
||||
/// Returns a hash returning the state of the npm resolver
|
||||
/// or `None` if the state currently can't be determined.
|
||||
fn check_state_hash(&self) -> Option<u64>;
|
||||
|
|
105
cli/npm/permission_checker.rs
Normal file
105
cli/npm/permission_checker.rs
Normal file
|
@ -0,0 +1,105 @@
|
|||
// Copyright 2018-2025 the Deno authors. MIT license.
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::collections::HashMap;
|
||||
use std::io::ErrorKind;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
|
||||
use deno_core::anyhow::Context;
|
||||
use deno_core::error::AnyError;
|
||||
use deno_core::parking_lot::Mutex;
|
||||
use deno_runtime::deno_node::NodePermissions;
|
||||
use sys_traits::FsCanonicalize;
|
||||
|
||||
use crate::sys::CliSys;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum NpmRegistryReadPermissionCheckerMode {
|
||||
Byonm,
|
||||
Global(PathBuf),
|
||||
Local(PathBuf),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct NpmRegistryReadPermissionChecker {
|
||||
sys: CliSys,
|
||||
cache: Mutex<HashMap<PathBuf, PathBuf>>,
|
||||
mode: NpmRegistryReadPermissionCheckerMode,
|
||||
}
|
||||
|
||||
impl NpmRegistryReadPermissionChecker {
|
||||
pub fn new(sys: CliSys, mode: NpmRegistryReadPermissionCheckerMode) -> Self {
|
||||
Self {
|
||||
sys,
|
||||
cache: Default::default(),
|
||||
mode,
|
||||
}
|
||||
}
|
||||
|
||||
#[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"]
|
||||
pub fn ensure_read_permission<'a>(
|
||||
&self,
|
||||
permissions: &mut dyn NodePermissions,
|
||||
path: &'a Path,
|
||||
) -> Result<Cow<'a, Path>, AnyError> {
|
||||
if permissions.query_read_all() {
|
||||
return Ok(Cow::Borrowed(path)); // skip permissions checks below
|
||||
}
|
||||
|
||||
match &self.mode {
|
||||
NpmRegistryReadPermissionCheckerMode::Byonm => {
|
||||
if path.components().any(|c| c.as_os_str() == "node_modules") {
|
||||
Ok(Cow::Borrowed(path))
|
||||
} else {
|
||||
permissions.check_read_path(path).map_err(Into::into)
|
||||
}
|
||||
}
|
||||
NpmRegistryReadPermissionCheckerMode::Global(registry_path)
|
||||
| NpmRegistryReadPermissionCheckerMode::Local(registry_path) => {
|
||||
// allow reading if it's in the node_modules
|
||||
let is_path_in_node_modules = path.starts_with(registry_path)
|
||||
&& path
|
||||
.components()
|
||||
.all(|c| !matches!(c, std::path::Component::ParentDir));
|
||||
|
||||
if is_path_in_node_modules {
|
||||
let mut cache = self.cache.lock();
|
||||
let mut canonicalize =
|
||||
|path: &Path| -> Result<Option<PathBuf>, AnyError> {
|
||||
match cache.get(path) {
|
||||
Some(canon) => Ok(Some(canon.clone())),
|
||||
None => match self.sys.fs_canonicalize(path) {
|
||||
Ok(canon) => {
|
||||
cache.insert(path.to_path_buf(), canon.clone());
|
||||
Ok(Some(canon))
|
||||
}
|
||||
Err(e) => {
|
||||
if e.kind() == ErrorKind::NotFound {
|
||||
return Ok(None);
|
||||
}
|
||||
Err(AnyError::from(e)).with_context(|| {
|
||||
format!("failed canonicalizing '{}'", path.display())
|
||||
})
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
if let Some(registry_path_canon) = canonicalize(registry_path)? {
|
||||
if let Some(path_canon) = canonicalize(path)? {
|
||||
if path_canon.starts_with(registry_path_canon) {
|
||||
return Ok(Cow::Owned(path_canon));
|
||||
}
|
||||
} else if path.starts_with(registry_path_canon)
|
||||
|| path.starts_with(registry_path)
|
||||
{
|
||||
return Ok(Cow::Borrowed(path));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
permissions.check_read_path(path).map_err(Into::into)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,6 +6,7 @@
|
|||
#![allow(unused_imports)]
|
||||
|
||||
use std::borrow::Cow;
|
||||
use std::path::PathBuf;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
|
@ -88,6 +89,8 @@ use crate::npm::CliNpmResolver;
|
|||
use crate::npm::CliNpmResolverCreateOptions;
|
||||
use crate::npm::CliNpmResolverManagedSnapshotOption;
|
||||
use crate::npm::CreateInNpmPkgCheckerOptions;
|
||||
use crate::npm::NpmRegistryReadPermissionChecker;
|
||||
use crate::npm::NpmRegistryReadPermissionCheckerMode;
|
||||
use crate::resolver::CjsTracker;
|
||||
use crate::resolver::CliNpmReqResolver;
|
||||
use crate::resolver::NpmModuleLoader;
|
||||
|
@ -123,6 +126,7 @@ struct SharedModuleLoaderState {
|
|||
node_code_translator: Arc<CliNodeCodeTranslator>,
|
||||
node_resolver: Arc<CliNodeResolver>,
|
||||
npm_module_loader: Arc<NpmModuleLoader>,
|
||||
npm_registry_permission_checker: NpmRegistryReadPermissionChecker,
|
||||
npm_req_resolver: Arc<CliNpmReqResolver>,
|
||||
npm_resolver: Arc<dyn CliNpmResolver>,
|
||||
source_maps: SourceMapStore,
|
||||
|
@ -557,7 +561,7 @@ impl NodeRequireLoader for EmbeddedModuleLoader {
|
|||
|
||||
self
|
||||
.shared
|
||||
.npm_resolver
|
||||
.npm_registry_permission_checker
|
||||
.ensure_read_permission(permissions, path)
|
||||
}
|
||||
|
||||
|
@ -662,6 +666,23 @@ pub async fn run(
|
|||
let npm_global_cache_dir = root_path.join(".deno_compile_node_modules");
|
||||
let cache_setting = CacheSetting::Only;
|
||||
let pkg_json_resolver = Arc::new(CliPackageJsonResolver::new(sys.clone()));
|
||||
let npm_registry_permission_checker = {
|
||||
let mode = match &metadata.node_modules {
|
||||
Some(binary::NodeModules::Managed {
|
||||
node_modules_dir: Some(path),
|
||||
}) => NpmRegistryReadPermissionCheckerMode::Local(PathBuf::from(path)),
|
||||
Some(binary::NodeModules::Byonm { .. }) => {
|
||||
NpmRegistryReadPermissionCheckerMode::Byonm
|
||||
}
|
||||
Some(binary::NodeModules::Managed {
|
||||
node_modules_dir: None,
|
||||
})
|
||||
| None => NpmRegistryReadPermissionCheckerMode::Global(
|
||||
npm_global_cache_dir.clone(),
|
||||
),
|
||||
};
|
||||
NpmRegistryReadPermissionChecker::new(sys.clone(), mode)
|
||||
};
|
||||
let (in_npm_pkg_checker, npm_resolver) = match metadata.node_modules {
|
||||
Some(binary::NodeModules::Managed { node_modules_dir }) => {
|
||||
// create an npmrc that uses the fake npm_registry_url to resolve packages
|
||||
|
@ -889,6 +910,7 @@ pub async fn run(
|
|||
fs.clone(),
|
||||
node_code_translator,
|
||||
)),
|
||||
npm_registry_permission_checker,
|
||||
npm_resolver: npm_resolver.clone(),
|
||||
npm_req_resolver,
|
||||
source_maps,
|
||||
|
|
|
@ -76,7 +76,7 @@ async function createDenoDtsFile() {
|
|||
|
||||
file.insertStatements(
|
||||
0,
|
||||
"// Copyright 2018-2024 the Deno authors. MIT license.\n\n",
|
||||
"// Copyright 2018-2025 the Deno authors. MIT license.\n\n",
|
||||
);
|
||||
|
||||
file.saveSync();
|
||||
|
|
Loading…
Add table
Reference in a new issue