mirror of
https://github.com/denoland/deno.git
synced 2025-02-07 23:06:50 -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::CliNpmResolverCreateOptions;
|
||||||
use crate::npm::CliNpmResolverManagedSnapshotOption;
|
use crate::npm::CliNpmResolverManagedSnapshotOption;
|
||||||
use crate::npm::CreateInNpmPkgCheckerOptions;
|
use crate::npm::CreateInNpmPkgCheckerOptions;
|
||||||
|
use crate::npm::NpmRegistryReadPermissionChecker;
|
||||||
|
use crate::npm::NpmRegistryReadPermissionCheckerMode;
|
||||||
use crate::resolver::CjsTracker;
|
use crate::resolver::CjsTracker;
|
||||||
use crate::resolver::CliDenoResolver;
|
use crate::resolver::CliDenoResolver;
|
||||||
use crate::resolver::CliNpmReqResolver;
|
use crate::resolver::CliNpmReqResolver;
|
||||||
|
@ -941,6 +943,19 @@ impl CliFactory {
|
||||||
let cjs_tracker = self.cjs_tracker()?.clone();
|
let cjs_tracker = self.cjs_tracker()?.clone();
|
||||||
let pkg_json_resolver = self.pkg_json_resolver().clone();
|
let pkg_json_resolver = self.pkg_json_resolver().clone();
|
||||||
let npm_req_resolver = self.npm_req_resolver().await?;
|
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(
|
Ok(CliMainWorkerFactory::new(
|
||||||
self.blob_store().clone(),
|
self.blob_store().clone(),
|
||||||
|
@ -968,13 +983,14 @@ impl CliFactory {
|
||||||
self.module_load_preparer().await?.clone(),
|
self.module_load_preparer().await?.clone(),
|
||||||
node_code_translator.clone(),
|
node_code_translator.clone(),
|
||||||
node_resolver.clone(),
|
node_resolver.clone(),
|
||||||
npm_req_resolver.clone(),
|
|
||||||
cli_npm_resolver.clone(),
|
|
||||||
NpmModuleLoader::new(
|
NpmModuleLoader::new(
|
||||||
self.cjs_tracker()?.clone(),
|
self.cjs_tracker()?.clone(),
|
||||||
fs.clone(),
|
fs.clone(),
|
||||||
node_code_translator.clone(),
|
node_code_translator.clone(),
|
||||||
),
|
),
|
||||||
|
npm_registry_permission_checker,
|
||||||
|
npm_req_resolver.clone(),
|
||||||
|
cli_npm_resolver.clone(),
|
||||||
self.parsed_source_cache().clone(),
|
self.parsed_source_cache().clone(),
|
||||||
self.resolver().await?.clone(),
|
self.resolver().await?.clone(),
|
||||||
self.sys(),
|
self.sys(),
|
||||||
|
|
|
@ -66,6 +66,7 @@ use crate::graph_util::ModuleGraphBuilder;
|
||||||
use crate::node::CliNodeCodeTranslator;
|
use crate::node::CliNodeCodeTranslator;
|
||||||
use crate::node::CliNodeResolver;
|
use crate::node::CliNodeResolver;
|
||||||
use crate::npm::CliNpmResolver;
|
use crate::npm::CliNpmResolver;
|
||||||
|
use crate::npm::NpmRegistryReadPermissionChecker;
|
||||||
use crate::resolver::CjsTracker;
|
use crate::resolver::CjsTracker;
|
||||||
use crate::resolver::CliNpmReqResolver;
|
use crate::resolver::CliNpmReqResolver;
|
||||||
use crate::resolver::CliResolver;
|
use crate::resolver::CliResolver;
|
||||||
|
@ -221,9 +222,10 @@ struct SharedCliModuleLoaderState {
|
||||||
module_load_preparer: Arc<ModuleLoadPreparer>,
|
module_load_preparer: Arc<ModuleLoadPreparer>,
|
||||||
node_code_translator: Arc<CliNodeCodeTranslator>,
|
node_code_translator: Arc<CliNodeCodeTranslator>,
|
||||||
node_resolver: Arc<CliNodeResolver>,
|
node_resolver: Arc<CliNodeResolver>,
|
||||||
|
npm_module_loader: NpmModuleLoader,
|
||||||
|
npm_registry_permission_checker: Arc<NpmRegistryReadPermissionChecker>,
|
||||||
npm_req_resolver: Arc<CliNpmReqResolver>,
|
npm_req_resolver: Arc<CliNpmReqResolver>,
|
||||||
npm_resolver: Arc<dyn CliNpmResolver>,
|
npm_resolver: Arc<dyn CliNpmResolver>,
|
||||||
npm_module_loader: NpmModuleLoader,
|
|
||||||
parsed_source_cache: Arc<ParsedSourceCache>,
|
parsed_source_cache: Arc<ParsedSourceCache>,
|
||||||
resolver: Arc<CliResolver>,
|
resolver: Arc<CliResolver>,
|
||||||
sys: CliSys,
|
sys: CliSys,
|
||||||
|
@ -281,9 +283,10 @@ impl CliModuleLoaderFactory {
|
||||||
module_load_preparer: Arc<ModuleLoadPreparer>,
|
module_load_preparer: Arc<ModuleLoadPreparer>,
|
||||||
node_code_translator: Arc<CliNodeCodeTranslator>,
|
node_code_translator: Arc<CliNodeCodeTranslator>,
|
||||||
node_resolver: Arc<CliNodeResolver>,
|
node_resolver: Arc<CliNodeResolver>,
|
||||||
|
npm_module_loader: NpmModuleLoader,
|
||||||
|
npm_registry_permission_checker: Arc<NpmRegistryReadPermissionChecker>,
|
||||||
npm_req_resolver: Arc<CliNpmReqResolver>,
|
npm_req_resolver: Arc<CliNpmReqResolver>,
|
||||||
npm_resolver: Arc<dyn CliNpmResolver>,
|
npm_resolver: Arc<dyn CliNpmResolver>,
|
||||||
npm_module_loader: NpmModuleLoader,
|
|
||||||
parsed_source_cache: Arc<ParsedSourceCache>,
|
parsed_source_cache: Arc<ParsedSourceCache>,
|
||||||
resolver: Arc<CliResolver>,
|
resolver: Arc<CliResolver>,
|
||||||
sys: CliSys,
|
sys: CliSys,
|
||||||
|
@ -307,9 +310,10 @@ impl CliModuleLoaderFactory {
|
||||||
module_load_preparer,
|
module_load_preparer,
|
||||||
node_code_translator,
|
node_code_translator,
|
||||||
node_resolver,
|
node_resolver,
|
||||||
|
npm_module_loader,
|
||||||
|
npm_registry_permission_checker,
|
||||||
npm_req_resolver,
|
npm_req_resolver,
|
||||||
npm_resolver,
|
npm_resolver,
|
||||||
npm_module_loader,
|
|
||||||
parsed_source_cache,
|
parsed_source_cache,
|
||||||
resolver,
|
resolver,
|
||||||
sys,
|
sys,
|
||||||
|
@ -348,7 +352,10 @@ impl CliModuleLoaderFactory {
|
||||||
sys: self.shared.sys.clone(),
|
sys: self.shared.sys.clone(),
|
||||||
graph_container,
|
graph_container,
|
||||||
in_npm_pkg_checker: self.shared.in_npm_pkg_checker.clone(),
|
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 {
|
CreateModuleLoaderResult {
|
||||||
module_loader,
|
module_loader,
|
||||||
|
@ -1095,7 +1102,7 @@ struct CliNodeRequireLoader<TGraphContainer: ModuleGraphContainer> {
|
||||||
sys: CliSys,
|
sys: CliSys,
|
||||||
graph_container: TGraphContainer,
|
graph_container: TGraphContainer,
|
||||||
in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
|
in_npm_pkg_checker: Arc<dyn InNpmPackageChecker>,
|
||||||
npm_resolver: Arc<dyn CliNpmResolver>,
|
npm_registry_permission_checker: Arc<NpmRegistryReadPermissionChecker>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<TGraphContainer: ModuleGraphContainer> NodeRequireLoader
|
impl<TGraphContainer: ModuleGraphContainer> NodeRequireLoader
|
||||||
|
@ -1112,7 +1119,9 @@ impl<TGraphContainer: ModuleGraphContainer> NodeRequireLoader
|
||||||
return Ok(std::borrow::Cow::Borrowed(path));
|
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(
|
fn load_text_file_lossy(
|
||||||
|
|
|
@ -1,15 +1,12 @@
|
||||||
// Copyright 2018-2025 the Deno authors. MIT license.
|
// Copyright 2018-2025 the Deno authors. MIT license.
|
||||||
|
|
||||||
use std::borrow::Cow;
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use deno_core::error::AnyError;
|
|
||||||
use deno_core::serde_json;
|
use deno_core::serde_json;
|
||||||
use deno_resolver::npm::ByonmNpmResolver;
|
use deno_resolver::npm::ByonmNpmResolver;
|
||||||
use deno_resolver::npm::ByonmNpmResolverCreateOptions;
|
use deno_resolver::npm::ByonmNpmResolverCreateOptions;
|
||||||
use deno_resolver::npm::CliNpmReqResolver;
|
use deno_resolver::npm::CliNpmReqResolver;
|
||||||
use deno_runtime::deno_node::NodePermissions;
|
|
||||||
use deno_runtime::ops::process::NpmProcessStateProvider;
|
use deno_runtime::ops::process::NpmProcessStateProvider;
|
||||||
use node_resolver::NpmPackageFolderResolver;
|
use node_resolver::NpmPackageFolderResolver;
|
||||||
|
|
||||||
|
@ -73,21 +70,6 @@ impl CliNpmResolver for CliByonmNpmResolver {
|
||||||
self.root_node_modules_dir()
|
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> {
|
fn check_state_hash(&self) -> Option<u64> {
|
||||||
// it is very difficult to determine the check state hash for byonm
|
// it is very difficult to determine the check state hash for byonm
|
||||||
// so we just return None to signify check caching is not supported
|
// 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_path_util::fs::canonicalize_path_maybe_not_exists;
|
||||||
use deno_resolver::npm::CliNpmReqResolver;
|
use deno_resolver::npm::CliNpmReqResolver;
|
||||||
use deno_runtime::colors;
|
use deno_runtime::colors;
|
||||||
use deno_runtime::deno_node::NodePermissions;
|
|
||||||
use deno_runtime::ops::process::NpmProcessStateProvider;
|
use deno_runtime::ops::process::NpmProcessStateProvider;
|
||||||
use deno_semver::package::PackageNv;
|
use deno_semver::package::PackageNv;
|
||||||
use deno_semver::package::PackageReq;
|
use deno_semver::package::PackageReq;
|
||||||
|
@ -167,6 +166,7 @@ fn create_inner(
|
||||||
sys.clone(),
|
sys.clone(),
|
||||||
npm_rc.clone(),
|
npm_rc.clone(),
|
||||||
));
|
));
|
||||||
|
|
||||||
let fs_resolver = create_npm_fs_resolver(
|
let fs_resolver = create_npm_fs_resolver(
|
||||||
npm_cache.clone(),
|
npm_cache.clone(),
|
||||||
&npm_install_deps_provider,
|
&npm_install_deps_provider,
|
||||||
|
@ -754,14 +754,6 @@ impl CliNpmResolver for ManagedCliNpmResolver {
|
||||||
self.fs_resolver.node_modules_path()
|
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> {
|
fn check_state_hash(&self) -> Option<u64> {
|
||||||
// We could go further and check all the individual
|
// We could go further and check all the individual
|
||||||
// npm packages, but that's probably overkill.
|
// npm packages, but that's probably overkill.
|
||||||
|
|
|
@ -3,30 +3,17 @@
|
||||||
pub mod bin_entries;
|
pub mod bin_entries;
|
||||||
pub mod lifecycle_scripts;
|
pub mod lifecycle_scripts;
|
||||||
|
|
||||||
use std::borrow::Cow;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::io::ErrorKind;
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::sync::Arc;
|
|
||||||
use std::sync::Mutex;
|
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use deno_ast::ModuleSpecifier;
|
use deno_ast::ModuleSpecifier;
|
||||||
use deno_core::anyhow::Context;
|
|
||||||
use deno_core::error::AnyError;
|
use deno_core::error::AnyError;
|
||||||
use deno_core::futures;
|
|
||||||
use deno_core::futures::StreamExt;
|
|
||||||
use deno_npm::NpmPackageCacheFolderId;
|
use deno_npm::NpmPackageCacheFolderId;
|
||||||
use deno_npm::NpmPackageId;
|
use deno_npm::NpmPackageId;
|
||||||
use deno_npm::NpmResolutionPackage;
|
|
||||||
use deno_runtime::deno_node::NodePermissions;
|
|
||||||
use node_resolver::errors::PackageFolderResolveError;
|
use node_resolver::errors::PackageFolderResolveError;
|
||||||
use sys_traits::FsCanonicalize;
|
|
||||||
|
|
||||||
use super::super::PackageCaching;
|
use super::super::PackageCaching;
|
||||||
use crate::npm::CliNpmTarballCache;
|
|
||||||
use crate::sys::CliSys;
|
|
||||||
|
|
||||||
/// Part of the resolution that interacts with the file system.
|
/// Part of the resolution that interacts with the file system.
|
||||||
#[async_trait(?Send)]
|
#[async_trait(?Send)]
|
||||||
|
@ -63,101 +50,4 @@ pub trait NpmPackageFsResolver: Send + Sync {
|
||||||
&self,
|
&self,
|
||||||
caching: PackageCaching<'a>,
|
caching: PackageCaching<'a>,
|
||||||
) -> Result<(), AnyError>;
|
) -> 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 async_trait::async_trait;
|
||||||
use deno_ast::ModuleSpecifier;
|
use deno_ast::ModuleSpecifier;
|
||||||
use deno_core::error::AnyError;
|
use deno_core::error::AnyError;
|
||||||
|
use deno_core::futures::stream::FuturesUnordered;
|
||||||
|
use deno_core::futures::StreamExt;
|
||||||
use deno_npm::NpmPackageCacheFolderId;
|
use deno_npm::NpmPackageCacheFolderId;
|
||||||
use deno_npm::NpmPackageId;
|
use deno_npm::NpmPackageId;
|
||||||
use deno_npm::NpmResolutionPackage;
|
use deno_npm::NpmResolutionPackage;
|
||||||
use deno_npm::NpmSystemInfo;
|
use deno_npm::NpmSystemInfo;
|
||||||
use deno_runtime::deno_node::NodePermissions;
|
|
||||||
use node_resolver::errors::PackageFolderResolveError;
|
use node_resolver::errors::PackageFolderResolveError;
|
||||||
use node_resolver::errors::PackageNotFoundError;
|
use node_resolver::errors::PackageNotFoundError;
|
||||||
use node_resolver::errors::ReferrerNotFoundError;
|
use node_resolver::errors::ReferrerNotFoundError;
|
||||||
|
|
||||||
use super::super::resolution::NpmResolution;
|
use super::super::resolution::NpmResolution;
|
||||||
use super::common::cache_packages;
|
|
||||||
use super::common::lifecycle_scripts::LifecycleScriptsStrategy;
|
use super::common::lifecycle_scripts::LifecycleScriptsStrategy;
|
||||||
use super::common::NpmPackageFsResolver;
|
use super::common::NpmPackageFsResolver;
|
||||||
use super::common::RegistryReadPermissionChecker;
|
|
||||||
use crate::args::LifecycleScriptsConfig;
|
use crate::args::LifecycleScriptsConfig;
|
||||||
use crate::cache::FastInsecureHasher;
|
use crate::cache::FastInsecureHasher;
|
||||||
use crate::colors;
|
use crate::colors;
|
||||||
use crate::npm::managed::PackageCaching;
|
use crate::npm::managed::PackageCaching;
|
||||||
use crate::npm::CliNpmCache;
|
use crate::npm::CliNpmCache;
|
||||||
use crate::npm::CliNpmTarballCache;
|
use crate::npm::CliNpmTarballCache;
|
||||||
use crate::sys::CliSys;
|
|
||||||
|
|
||||||
/// Resolves packages from the global npm cache.
|
/// Resolves packages from the global npm cache.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -39,7 +37,6 @@ pub struct GlobalNpmPackageResolver {
|
||||||
tarball_cache: Arc<CliNpmTarballCache>,
|
tarball_cache: Arc<CliNpmTarballCache>,
|
||||||
resolution: Arc<NpmResolution>,
|
resolution: Arc<NpmResolution>,
|
||||||
system_info: NpmSystemInfo,
|
system_info: NpmSystemInfo,
|
||||||
registry_read_permission_checker: RegistryReadPermissionChecker,
|
|
||||||
lifecycle_scripts: LifecycleScriptsConfig,
|
lifecycle_scripts: LifecycleScriptsConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,15 +45,10 @@ impl GlobalNpmPackageResolver {
|
||||||
cache: Arc<CliNpmCache>,
|
cache: Arc<CliNpmCache>,
|
||||||
tarball_cache: Arc<CliNpmTarballCache>,
|
tarball_cache: Arc<CliNpmTarballCache>,
|
||||||
resolution: Arc<NpmResolution>,
|
resolution: Arc<NpmResolution>,
|
||||||
sys: CliSys,
|
|
||||||
system_info: NpmSystemInfo,
|
system_info: NpmSystemInfo,
|
||||||
lifecycle_scripts: LifecycleScriptsConfig,
|
lifecycle_scripts: LifecycleScriptsConfig,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
registry_read_permission_checker: RegistryReadPermissionChecker::new(
|
|
||||||
sys,
|
|
||||||
cache.root_dir_path().to_path_buf(),
|
|
||||||
),
|
|
||||||
cache,
|
cache,
|
||||||
tarball_cache,
|
tarball_cache,
|
||||||
resolution,
|
resolution,
|
||||||
|
@ -186,16 +178,25 @@ impl NpmPackageFsResolver for GlobalNpmPackageResolver {
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn ensure_read_permission<'a>(
|
async fn cache_packages(
|
||||||
&self,
|
packages: &[NpmResolutionPackage],
|
||||||
permissions: &mut dyn NodePermissions,
|
tarball_cache: &Arc<CliNpmTarballCache>,
|
||||||
path: &'a Path,
|
) -> Result<(), AnyError> {
|
||||||
) -> Result<Cow<'a, Path>, AnyError> {
|
let mut futures_unordered = FuturesUnordered::new();
|
||||||
self
|
for package in packages {
|
||||||
.registry_read_permission_checker
|
futures_unordered.push(async move {
|
||||||
.ensure_registry_read_permission(permissions, path)
|
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> {
|
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::atomic_write_file_with_retries;
|
||||||
use deno_path_util::fs::canonicalize_path_maybe_not_exists;
|
use deno_path_util::fs::canonicalize_path_maybe_not_exists;
|
||||||
use deno_resolver::npm::normalize_pkg_name_for_node_modules_deno_folder;
|
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::package::PackageNv;
|
||||||
use deno_semver::StackString;
|
use deno_semver::StackString;
|
||||||
use node_resolver::errors::PackageFolderResolveError;
|
use node_resolver::errors::PackageFolderResolveError;
|
||||||
|
@ -47,7 +46,6 @@ use sys_traits::FsMetadata;
|
||||||
use super::super::resolution::NpmResolution;
|
use super::super::resolution::NpmResolution;
|
||||||
use super::common::bin_entries;
|
use super::common::bin_entries;
|
||||||
use super::common::NpmPackageFsResolver;
|
use super::common::NpmPackageFsResolver;
|
||||||
use super::common::RegistryReadPermissionChecker;
|
|
||||||
use crate::args::LifecycleScriptsConfig;
|
use crate::args::LifecycleScriptsConfig;
|
||||||
use crate::args::NpmInstallDepsProvider;
|
use crate::args::NpmInstallDepsProvider;
|
||||||
use crate::cache::CACHE_PERM;
|
use crate::cache::CACHE_PERM;
|
||||||
|
@ -75,7 +73,6 @@ pub struct LocalNpmPackageResolver {
|
||||||
root_node_modules_path: PathBuf,
|
root_node_modules_path: PathBuf,
|
||||||
root_node_modules_url: Url,
|
root_node_modules_url: Url,
|
||||||
system_info: NpmSystemInfo,
|
system_info: NpmSystemInfo,
|
||||||
registry_read_permission_checker: RegistryReadPermissionChecker,
|
|
||||||
lifecycle_scripts: LifecycleScriptsConfig,
|
lifecycle_scripts: LifecycleScriptsConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,10 +95,6 @@ impl LocalNpmPackageResolver {
|
||||||
progress_bar,
|
progress_bar,
|
||||||
resolution,
|
resolution,
|
||||||
tarball_cache,
|
tarball_cache,
|
||||||
registry_read_permission_checker: RegistryReadPermissionChecker::new(
|
|
||||||
sys.clone(),
|
|
||||||
node_modules_folder.clone(),
|
|
||||||
),
|
|
||||||
sys,
|
sys,
|
||||||
root_node_modules_url: Url::from_directory_path(&node_modules_folder)
|
root_node_modules_url: Url::from_directory_path(&node_modules_folder)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
|
@ -275,16 +268,6 @@ impl NpmPackageFsResolver for LocalNpmPackageResolver {
|
||||||
)
|
)
|
||||||
.await
|
.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>`
|
/// `node_modules/.deno/<package>/node_modules/<package_name>`
|
||||||
|
|
|
@ -48,7 +48,6 @@ pub fn create_npm_fs_resolver(
|
||||||
npm_cache,
|
npm_cache,
|
||||||
tarball_cache,
|
tarball_cache,
|
||||||
resolution,
|
resolution,
|
||||||
sys,
|
|
||||||
system_info,
|
system_info,
|
||||||
lifecycle_scripts,
|
lifecycle_scripts,
|
||||||
)),
|
)),
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
mod byonm;
|
mod byonm;
|
||||||
mod managed;
|
mod managed;
|
||||||
|
mod permission_checker;
|
||||||
|
|
||||||
use std::borrow::Cow;
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
@ -17,7 +17,6 @@ use deno_resolver::npm::ByonmInNpmPackageChecker;
|
||||||
use deno_resolver::npm::ByonmNpmResolver;
|
use deno_resolver::npm::ByonmNpmResolver;
|
||||||
use deno_resolver::npm::CliNpmReqResolver;
|
use deno_resolver::npm::CliNpmReqResolver;
|
||||||
use deno_resolver::npm::ResolvePkgFolderFromDenoReqError;
|
use deno_resolver::npm::ResolvePkgFolderFromDenoReqError;
|
||||||
use deno_runtime::deno_node::NodePermissions;
|
|
||||||
use deno_runtime::ops::process::NpmProcessStateProvider;
|
use deno_runtime::ops::process::NpmProcessStateProvider;
|
||||||
use deno_semver::package::PackageNv;
|
use deno_semver::package::PackageNv;
|
||||||
use deno_semver::package::PackageReq;
|
use deno_semver::package::PackageReq;
|
||||||
|
@ -34,6 +33,8 @@ pub use self::managed::CliManagedNpmResolverCreateOptions;
|
||||||
pub use self::managed::CliNpmResolverManagedSnapshotOption;
|
pub use self::managed::CliNpmResolverManagedSnapshotOption;
|
||||||
pub use self::managed::ManagedCliNpmResolver;
|
pub use self::managed::ManagedCliNpmResolver;
|
||||||
pub use self::managed::PackageCaching;
|
pub use self::managed::PackageCaching;
|
||||||
|
pub use self::permission_checker::NpmRegistryReadPermissionChecker;
|
||||||
|
pub use self::permission_checker::NpmRegistryReadPermissionCheckerMode;
|
||||||
use crate::file_fetcher::CliFileFetcher;
|
use crate::file_fetcher::CliFileFetcher;
|
||||||
use crate::http_util::HttpClientProvider;
|
use crate::http_util::HttpClientProvider;
|
||||||
use crate::sys::CliSys;
|
use crate::sys::CliSys;
|
||||||
|
@ -183,12 +184,6 @@ pub trait CliNpmResolver: NpmPackageFolderResolver + CliNpmReqResolver {
|
||||||
|
|
||||||
fn root_node_modules_path(&self) -> Option<&Path>;
|
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
|
/// Returns a hash returning the state of the npm resolver
|
||||||
/// or `None` if the state currently can't be determined.
|
/// or `None` if the state currently can't be determined.
|
||||||
fn check_state_hash(&self) -> Option<u64>;
|
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)]
|
#![allow(unused_imports)]
|
||||||
|
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
|
use std::path::PathBuf;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
@ -88,6 +89,8 @@ use crate::npm::CliNpmResolver;
|
||||||
use crate::npm::CliNpmResolverCreateOptions;
|
use crate::npm::CliNpmResolverCreateOptions;
|
||||||
use crate::npm::CliNpmResolverManagedSnapshotOption;
|
use crate::npm::CliNpmResolverManagedSnapshotOption;
|
||||||
use crate::npm::CreateInNpmPkgCheckerOptions;
|
use crate::npm::CreateInNpmPkgCheckerOptions;
|
||||||
|
use crate::npm::NpmRegistryReadPermissionChecker;
|
||||||
|
use crate::npm::NpmRegistryReadPermissionCheckerMode;
|
||||||
use crate::resolver::CjsTracker;
|
use crate::resolver::CjsTracker;
|
||||||
use crate::resolver::CliNpmReqResolver;
|
use crate::resolver::CliNpmReqResolver;
|
||||||
use crate::resolver::NpmModuleLoader;
|
use crate::resolver::NpmModuleLoader;
|
||||||
|
@ -123,6 +126,7 @@ struct SharedModuleLoaderState {
|
||||||
node_code_translator: Arc<CliNodeCodeTranslator>,
|
node_code_translator: Arc<CliNodeCodeTranslator>,
|
||||||
node_resolver: Arc<CliNodeResolver>,
|
node_resolver: Arc<CliNodeResolver>,
|
||||||
npm_module_loader: Arc<NpmModuleLoader>,
|
npm_module_loader: Arc<NpmModuleLoader>,
|
||||||
|
npm_registry_permission_checker: NpmRegistryReadPermissionChecker,
|
||||||
npm_req_resolver: Arc<CliNpmReqResolver>,
|
npm_req_resolver: Arc<CliNpmReqResolver>,
|
||||||
npm_resolver: Arc<dyn CliNpmResolver>,
|
npm_resolver: Arc<dyn CliNpmResolver>,
|
||||||
source_maps: SourceMapStore,
|
source_maps: SourceMapStore,
|
||||||
|
@ -557,7 +561,7 @@ impl NodeRequireLoader for EmbeddedModuleLoader {
|
||||||
|
|
||||||
self
|
self
|
||||||
.shared
|
.shared
|
||||||
.npm_resolver
|
.npm_registry_permission_checker
|
||||||
.ensure_read_permission(permissions, path)
|
.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 npm_global_cache_dir = root_path.join(".deno_compile_node_modules");
|
||||||
let cache_setting = CacheSetting::Only;
|
let cache_setting = CacheSetting::Only;
|
||||||
let pkg_json_resolver = Arc::new(CliPackageJsonResolver::new(sys.clone()));
|
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 {
|
let (in_npm_pkg_checker, npm_resolver) = match metadata.node_modules {
|
||||||
Some(binary::NodeModules::Managed { node_modules_dir }) => {
|
Some(binary::NodeModules::Managed { node_modules_dir }) => {
|
||||||
// create an npmrc that uses the fake npm_registry_url to resolve packages
|
// create an npmrc that uses the fake npm_registry_url to resolve packages
|
||||||
|
@ -889,6 +910,7 @@ pub async fn run(
|
||||||
fs.clone(),
|
fs.clone(),
|
||||||
node_code_translator,
|
node_code_translator,
|
||||||
)),
|
)),
|
||||||
|
npm_registry_permission_checker,
|
||||||
npm_resolver: npm_resolver.clone(),
|
npm_resolver: npm_resolver.clone(),
|
||||||
npm_req_resolver,
|
npm_req_resolver,
|
||||||
source_maps,
|
source_maps,
|
||||||
|
|
|
@ -76,7 +76,7 @@ async function createDenoDtsFile() {
|
||||||
|
|
||||||
file.insertStatements(
|
file.insertStatements(
|
||||||
0,
|
0,
|
||||||
"// Copyright 2018-2024 the Deno authors. MIT license.\n\n",
|
"// Copyright 2018-2025 the Deno authors. MIT license.\n\n",
|
||||||
);
|
);
|
||||||
|
|
||||||
file.saveSync();
|
file.saveSync();
|
||||||
|
|
Loading…
Add table
Reference in a new issue