summaryrefslogtreecommitdiff
path: root/cli
diff options
context:
space:
mode:
authorBartek Iwańczuk <biwanczuk@gmail.com>2024-09-26 02:50:54 +0100
committerGitHub <noreply@github.com>2024-09-26 01:50:54 +0000
commit5504acea6751480f1425c88353ad5d36257bdce7 (patch)
treefa02e6c546eae469aac894bfc71600ab4eccad28 /cli
parent05415bb9de475aa8646985a545f30fe93136207e (diff)
feat: add `--allow-import` flag (#25469)
This replaces `--allow-net` for import permissions and makes the security sandbox stricter by also checking permissions for statically analyzable imports. By default, this has a value of `--allow-import=deno.land:443,jsr.io:443,esm.sh:443,raw.githubusercontent.com:443,gist.githubusercontent.com:443`, but that can be overridden by providing a different set of hosts. Additionally, when no value is provided, import permissions are inferred from the CLI arguments so the following works because `fresh.deno.dev:443` will be added to the list of allowed imports: ```ts deno run -A -r https://fresh.deno.dev ``` --------- Co-authored-by: David Sherret <dsherret@gmail.com>
Diffstat (limited to 'cli')
-rw-r--r--cli/Cargo.toml2
-rw-r--r--cli/args/flags.rs253
-rw-r--r--cli/args/mod.rs102
-rw-r--r--cli/cache/mod.rs42
-rw-r--r--cli/factory.rs25
-rw-r--r--cli/file_fetcher.rs48
-rw-r--r--cli/graph_container.rs7
-rw-r--r--cli/graph_util.rs38
-rw-r--r--cli/lsp/language_server.rs5
-rw-r--r--cli/module_loader.rs41
-rw-r--r--cli/standalone/mod.rs19
-rw-r--r--cli/tools/check.rs3
-rw-r--r--cli/tools/compile.rs2
-rw-r--r--cli/tools/info.rs17
-rw-r--r--cli/tools/registry/diagnostics.rs14
-rw-r--r--cli/tools/registry/mod.rs6
-rw-r--r--cli/tools/registry/pm/cache_deps.rs3
-rw-r--r--cli/tools/repl/mod.rs6
-rw-r--r--cli/tools/run/mod.rs12
-rw-r--r--cli/tools/serve.rs17
-rw-r--r--cli/worker.rs21
21 files changed, 466 insertions, 217 deletions
diff --git a/cli/Cargo.toml b/cli/Cargo.toml
index ec2243a81..ede5cc15c 100644
--- a/cli/Cargo.toml
+++ b/cli/Cargo.toml
@@ -68,7 +68,7 @@ deno_cache_dir = { workspace = true }
deno_config = { version = "=0.35.0", features = ["workspace", "sync"] }
deno_core = { workspace = true, features = ["include_js_files_for_snapshotting"] }
deno_doc = { version = "0.148.0", features = ["html", "syntect"] }
-deno_graph = { version = "=0.82.2" }
+deno_graph = { version = "=0.82.3" }
deno_lint = { version = "=0.67.0", features = ["docs"] }
deno_lockfile.workspace = true
deno_npm = "=0.25.2"
diff --git a/cli/args/flags.rs b/cli/args/flags.rs
index 10fa07bed..74ccb512f 100644
--- a/cli/args/flags.rs
+++ b/cli/args/flags.rs
@@ -1,5 +1,6 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
+use std::borrow::Cow;
use std::collections::HashSet;
use std::env;
use std::ffi::OsString;
@@ -44,6 +45,7 @@ use crate::args::resolve_no_prompt;
use crate::util::fs::canonicalize_path;
use super::flags_net;
+use super::jsr_url;
#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub enum ConfigFlag {
@@ -639,6 +641,7 @@ pub struct PermissionFlags {
pub allow_write: Option<Vec<String>>,
pub deny_write: Option<Vec<String>>,
pub no_prompt: bool,
+ pub allow_import: Option<Vec<String>>,
}
impl PermissionFlags {
@@ -658,9 +661,10 @@ impl PermissionFlags {
|| self.deny_sys.is_some()
|| self.allow_write.is_some()
|| self.deny_write.is_some()
+ || self.allow_import.is_some()
}
- pub fn to_options(&self) -> PermissionsOptions {
+ pub fn to_options(&self, cli_arg_urls: &[Cow<Url>]) -> PermissionsOptions {
fn handle_allow<T: Default>(
allow_all: bool,
value: Option<T>,
@@ -673,6 +677,41 @@ impl PermissionFlags {
}
}
+ fn handle_imports(
+ cli_arg_urls: &[Cow<Url>],
+ imports: Option<Vec<String>>,
+ ) -> Option<Vec<String>> {
+ if imports.is_some() {
+ return imports;
+ }
+
+ let builtin_allowed_import_hosts = [
+ "deno.land:443",
+ "esm.sh:443",
+ "jsr.io:443",
+ "raw.githubusercontent.com:443",
+ "gist.githubusercontent.com:443",
+ ];
+
+ let mut imports =
+ Vec::with_capacity(builtin_allowed_import_hosts.len() + 1);
+ imports
+ .extend(builtin_allowed_import_hosts.iter().map(|s| s.to_string()));
+
+ // also add the JSR_URL env var
+ if let Some(jsr_host) = allow_import_host_from_url(jsr_url()) {
+ imports.push(jsr_host);
+ }
+ // include the cli arg urls
+ for url in cli_arg_urls {
+ if let Some(host) = allow_import_host_from_url(url) {
+ imports.push(host);
+ }
+ }
+
+ Some(imports)
+ }
+
PermissionsOptions {
allow_all: self.allow_all,
allow_env: handle_allow(self.allow_all, self.allow_env.clone()),
@@ -689,11 +728,33 @@ impl PermissionFlags {
deny_sys: self.deny_sys.clone(),
allow_write: handle_allow(self.allow_all, self.allow_write.clone()),
deny_write: self.deny_write.clone(),
+ allow_import: handle_imports(
+ cli_arg_urls,
+ handle_allow(self.allow_all, self.allow_import.clone()),
+ ),
prompt: !resolve_no_prompt(self),
}
}
}
+/// Gets the --allow-import host from the provided url
+fn allow_import_host_from_url(url: &Url) -> Option<String> {
+ let host = url.host()?;
+ if let Some(port) = url.port() {
+ Some(format!("{}:{}", host, port))
+ } else {
+ use deno_core::url::Host::*;
+ match host {
+ Domain(domain) if domain == "jsr.io" && url.scheme() == "https" => None,
+ _ => match url.scheme() {
+ "https" => Some(format!("{}:443", host)),
+ "http" => Some(format!("{}:80", host)),
+ _ => None,
+ },
+ }
+ }
+}
+
fn join_paths(allowlist: &[String], d: &str) -> String {
allowlist
.iter()
@@ -881,6 +942,17 @@ impl Flags {
_ => {}
}
+ match &self.permissions.allow_import {
+ Some(allowlist) if allowlist.is_empty() => {
+ args.push("--allow-import".to_string());
+ }
+ Some(allowlist) => {
+ let s = format!("--allow-import={}", allowlist.join(","));
+ args.push(s);
+ }
+ _ => {}
+ }
+
args
}
@@ -991,6 +1063,7 @@ impl Flags {
self.permissions.allow_write = None;
self.permissions.allow_sys = None;
self.permissions.allow_ffi = None;
+ self.permissions.allow_import = None;
}
pub fn resolve_watch_exclude_set(
@@ -1707,6 +1780,7 @@ Future runs of this module will trigger no downloads or compilation unless --rel
)
.arg(frozen_lockfile_arg())
.arg(allow_scripts_arg())
+ .arg(allow_import_arg())
})
}
@@ -1766,6 +1840,7 @@ Unless --reload is specified, this command will not re-download already cached d
.required_unless_present("help")
.value_hint(ValueHint::FilePath),
)
+ .arg(allow_import_arg())
}
)
}
@@ -1994,6 +2069,7 @@ Show documentation for runtime built-ins:
.arg(no_lock_arg())
.arg(no_npm_arg())
.arg(no_remote_arg())
+ .arg(allow_import_arg())
.arg(
Arg::new("json")
.long("json")
@@ -2358,6 +2434,7 @@ The following information is shown:
.help("UNSTABLE: Outputs the information in JSON format")
.action(ArgAction::SetTrue),
))
+ .arg(allow_import_arg())
}
fn install_subcommand() -> Command {
@@ -3151,47 +3228,44 @@ fn permission_args(app: Command, requires: Option<&'static str>) -> Command {
.after_help(cstr!(r#"<y>Permission options:</>
<y>Docs</>: <c>https://docs.deno.com/go/permissions</>
- <g>-A, --allow-all</> Allow all permissions.
- <g>--no-prompt</> Always throw if required permission wasn't passed.
- <p(245)>Can also be set via the DENO_NO_PROMPT environment variable.</>
- <g>-R, --allow-read[=<<PATH>...]</> Allow file system read access. Optionally specify allowed paths.
- <p(245)>--allow-read | --allow-read="/etc,/var/log.txt"</>
- <g>-W, --allow-write[=<<PATH>...]</> Allow file system write access. Optionally specify allowed paths.
- <p(245)>--allow-write | --allow-write="/etc,/var/log.txt"</>
- <g>-N, --allow-net[=<<IP_OR_HOSTNAME>...]</> Allow network access. Optionally specify allowed IP addresses and host names, with ports as necessary.
- <p(245)>--allow-net | --allow-net="localhost:8080,deno.land"</>
- <g>-E, --allow-env[=<<VARIABLE_NAME>...]</> Allow access to environment variables. Optionally specify accessible environment variables.
- <p(245)>--allow-env | --allow-env="PORT,HOME,PATH"</>
- <g>-S, --allow-sys[=<<API_NAME>...]</> Allow access to OS information. Optionally allow specific APIs by function name.
- <p(245)>--allow-sys | --allow-sys="systemMemoryInfo,osRelease"</>
- <g>--allow-run[=<<PROGRAM_NAME>...]</> Allow running subprocesses. Optionally specify allowed runnable program names.
- <p(245)>--allow-run | --allow-run="whoami,ps"</>
- <g>--allow-ffi[=<<PATH>...]</> (Unstable) Allow loading dynamic libraries. Optionally specify allowed directories or files.
- <p(245)>--allow-ffi | --allow-ffi="./libfoo.so"</>
- <g> --deny-read[=<<PATH>...]</> Deny file system read access. Optionally specify denied paths.
- <p(245)>--deny-read | --deny-read="/etc,/var/log.txt"</>
- <g> --deny-write[=<<PATH>...]</> Deny file system write access. Optionally specify denied paths.
- <p(245)>--deny-write | --deny-write="/etc,/var/log.txt"</>
- <g> --deny-net[=<<IP_OR_HOSTNAME>...]</> Deny network access. Optionally specify defined IP addresses and host names, with ports as necessary.
- <p(245)>--deny-net | --deny-net="localhost:8080,deno.land"</>
- <g> --deny-env[=<<VARIABLE_NAME>...]</> Deny access to environment variables. Optionally specify inacessible environment variables.
- <p(245)>--deny-env | --deny-env="PORT,HOME,PATH"</>
- <g>-S, --deny-sys[=<<API_NAME>...]</> Deny access to OS information. Optionally deny specific APIs by function name.
- <p(245)>--deny-sys | --deny-sys="systemMemoryInfo,osRelease"</>
- <g>--deny-run[=<<PROGRAM_NAME>...]</> Deny running subprocesses. Optionally specify denied runnable program names.
- <p(245)>--deny-run | --deny-run="whoami,ps"</>
- <g>--deny-ffi[=<<PATH>...]</> (Unstable) Deny loading dynamic libraries. Optionally specify denied directories or files.
- <p(245)>--deny-ffi | --deny-ffi="./libfoo.so"</>
+ <g>-A, --allow-all</> Allow all permissions.
+ <g>--no-prompt</> Always throw if required permission wasn't passed.
+ <p(245)>Can also be set via the DENO_NO_PROMPT environment variable.</>
+ <g>-R, --allow-read[=<<PATH>...]</> Allow file system read access. Optionally specify allowed paths.
+ <p(245)>--allow-read | --allow-read="/etc,/var/log.txt"</>
+ <g>-W, --allow-write[=<<PATH>...]</> Allow file system write access. Optionally specify allowed paths.
+ <p(245)>--allow-write | --allow-write="/etc,/var/log.txt"</>
+ <g>-I, --allow-import[=<<IP_OR_HOSTNAME>...]</> Allow importing from remote hosts. Optionally specify allowed IP addresses and host names, with ports as necessary.
+ Default value: <p(245)>deno.land:443,jsr.io:443,esm.sh:443,raw.githubusercontent.com:443,user.githubusercontent.com:443</>
+ <p(245)>--allow-import | --allow-import="example.com,github.com"</>
+ <g>-N, --allow-net[=<<IP_OR_HOSTNAME>...]</> Allow network access. Optionally specify allowed IP addresses and host names, with ports as necessary.
+ <p(245)>--allow-net | --allow-net="localhost:8080,deno.land"</>
+ <g>-E, --allow-env[=<<VARIABLE_NAME>...]</> Allow access to environment variables. Optionally specify accessible environment variables.
+ <p(245)>--allow-env | --allow-env="PORT,HOME,PATH"</>
+ <g>-S, --allow-sys[=<<API_NAME>...]</> Allow access to OS information. Optionally allow specific APIs by function name.
+ <p(245)>--allow-sys | --allow-sys="systemMemoryInfo,osRelease"</>
+ <g>--allow-run[=<<PROGRAM_NAME>...]</> Allow running subprocesses. Optionally specify allowed runnable program names.
+ <p(245)>--allow-run | --allow-run="whoami,ps"</>
+ <g>--allow-ffi[=<<PATH>...]</> (Unstable) Allow loading dynamic libraries. Optionally specify allowed directories or files.
+ <p(245)>--allow-ffi | --allow-ffi="./libfoo.so"</>
+ <g> --deny-read[=<<PATH>...]</> Deny file system read access. Optionally specify denied paths.
+ <p(245)>--deny-read | --deny-read="/etc,/var/log.txt"</>
+ <g> --deny-write[=<<PATH>...]</> Deny file system write access. Optionally specify denied paths.
+ <p(245)>--deny-write | --deny-write="/etc,/var/log.txt"</>
+ <g> --deny-net[=<<IP_OR_HOSTNAME>...]</> Deny network access. Optionally specify defined IP addresses and host names, with ports as necessary.
+ <p(245)>--deny-net | --deny-net="localhost:8080,deno.land"</>
+ <g> --deny-env[=<<VARIABLE_NAME>...]</> Deny access to environment variables. Optionally specify inacessible environment variables.
+ <p(245)>--deny-env | --deny-env="PORT,HOME,PATH"</>
+ <g>-S, --deny-sys[=<<API_NAME>...]</> Deny access to OS information. Optionally deny specific APIs by function name.
+ <p(245)>--deny-sys | --deny-sys="systemMemoryInfo,osRelease"</>
+ <g>--deny-run[=<<PROGRAM_NAME>...]</> Deny running subprocesses. Optionally specify denied runnable program names.
+ <p(245)>--deny-run | --deny-run="whoami,ps"</>
+ <g>--deny-ffi[=<<PATH>...]</> (Unstable) Deny loading dynamic libraries. Optionally specify denied directories or files.
+ <p(245)>--deny-ffi | --deny-ffi="./libfoo.so"</>
"#))
.arg(
{
- let mut arg = Arg::new("allow-all")
- .short('A')
- .long("allow-all")
- .action(ArgAction::SetTrue)
- .help("Allow all permissions")
- .hide(true)
- ;
+ let mut arg = allow_all_arg().hide(true);
if let Some(requires) = requires {
arg = arg.requires(requires)
}
@@ -3200,7 +3274,7 @@ fn permission_args(app: Command, requires: Option<&'static str>) -> Command {
)
.arg(
{
- let mut arg = Arg::new("allow-read")
+ let mut arg = Arg::new("allow-read")
.long("allow-read")
.short('R')
.num_args(0..)
@@ -3218,7 +3292,7 @@ fn permission_args(app: Command, requires: Option<&'static str>) -> Command {
)
.arg(
{
- let mut arg = Arg::new("deny-read")
+ let mut arg = Arg::new("deny-read")
.long("deny-read")
.num_args(0..)
.action(ArgAction::Append)
@@ -3235,7 +3309,7 @@ fn permission_args(app: Command, requires: Option<&'static str>) -> Command {
)
.arg(
{
- let mut arg = Arg::new("allow-write")
+ let mut arg = Arg::new("allow-write")
.long("allow-write")
.short('W')
.num_args(0..)
@@ -3253,7 +3327,7 @@ fn permission_args(app: Command, requires: Option<&'static str>) -> Command {
)
.arg(
{
- let mut arg = Arg::new("deny-write")
+ let mut arg = Arg::new("deny-write")
.long("deny-write")
.num_args(0..)
.action(ArgAction::Append)
@@ -3270,7 +3344,7 @@ fn permission_args(app: Command, requires: Option<&'static str>) -> Command {
)
.arg(
{
- let mut arg = Arg::new("allow-net")
+ let mut arg = Arg::new("allow-net")
.long("allow-net")
.short('N')
.num_args(0..)
@@ -3289,7 +3363,7 @@ fn permission_args(app: Command, requires: Option<&'static str>) -> Command {
)
.arg(
{
- let mut arg = Arg::new("deny-net")
+ let mut arg = Arg::new("deny-net")
.long("deny-net")
.num_args(0..)
.use_value_delimiter(true)
@@ -3383,7 +3457,7 @@ fn permission_args(app: Command, requires: Option<&'static str>) -> Command {
)
.arg(
{
- let mut arg = Arg::new("deny-sys")
+ let mut arg = Arg::new("deny-sys")
.long("deny-sys")
.num_args(0..)
.use_value_delimiter(true)
@@ -3418,7 +3492,7 @@ fn permission_args(app: Command, requires: Option<&'static str>) -> Command {
)
.arg(
{
- let mut arg = Arg::new("deny-run")
+ let mut arg = Arg::new("deny-run")
.long("deny-run")
.num_args(0..)
.use_value_delimiter(true)
@@ -3509,6 +3583,26 @@ fn permission_args(app: Command, requires: Option<&'static str>) -> Command {
arg
}
)
+ .arg(
+ {
+ let mut arg = allow_import_arg().hide(true);
+ if let Some(requires) = requires {
+ // allow this for install --global
+ if requires != "global" {
+ arg = arg.requires(requires)
+ }
+ }
+ arg
+ }
+ )
+}
+
+fn allow_all_arg() -> Arg {
+ Arg::new("allow-all")
+ .short('A')
+ .long("allow-all")
+ .action(ArgAction::SetTrue)
+ .help("Allow all permissions")
}
fn runtime_args(
@@ -3537,6 +3631,20 @@ fn runtime_args(
.arg(strace_ops_arg())
}
+fn allow_import_arg() -> Arg {
+ Arg::new("allow-import")
+ .long("allow-import")
+ .short('I')
+ .num_args(0..)
+ .use_value_delimiter(true)
+ .require_equals(true)
+ .value_name("IP_OR_HOSTNAME")
+ .help(cstr!(
+ "Allow importing from remote hosts. Optionally specify allowed IP addresses and host names, with ports as necessary. Default value: <p(245)>deno.land:443,jsr.io:443,esm.sh:443,raw.githubusercontent.com:443,user.githubusercontent.com:443</>"
+ ))
+ .value_parser(flags_net::validator)
+}
+
fn inspect_args(app: Command) -> Command {
app
.arg(
@@ -4174,6 +4282,7 @@ fn cache_parse(
unstable_args_parse(flags, matches, UnstableArgsConfig::ResolutionOnly);
frozen_lockfile_arg_parse(flags, matches);
allow_scripts_arg_parse(flags, matches)?;
+ allow_import_parse(flags, matches);
let files = matches.remove_many::<String>("file").unwrap().collect();
flags.subcommand = DenoSubcommand::Cache(CacheFlags { files });
Ok(())
@@ -4195,6 +4304,7 @@ fn check_parse(
doc: matches.get_flag("doc"),
doc_only: matches.get_flag("doc-only"),
});
+ allow_import_parse(flags, matches);
Ok(())
}
@@ -4320,6 +4430,7 @@ fn doc_parse(
no_lock_arg_parse(flags, matches);
no_npm_arg_parse(flags, matches);
no_remote_arg_parse(flags, matches);
+ allow_import_parse(flags, matches);
let source_files_val = matches.remove_many::<String>("source_file");
let source_files = if let Some(val) = source_files_val {
@@ -4460,6 +4571,7 @@ fn info_parse(
lock_args_parse(flags, matches);
no_remote_arg_parse(flags, matches);
no_npm_arg_parse(flags, matches);
+ allow_import_parse(flags, matches);
let json = matches.get_flag("json");
flags.subcommand = DenoSubcommand::Info(InfoFlags {
file: matches.remove_one::<String>("file"),
@@ -4495,6 +4607,7 @@ fn install_parse(
force,
}),
});
+
return Ok(());
}
@@ -5175,13 +5288,22 @@ fn permission_args_parse(
}
if matches.get_flag("allow-hrtime") || matches.get_flag("deny-hrtime") {
- log::warn!("⚠️ Warning: `allow-hrtime` and `deny-hrtime` have been removed in Deno 2, as high resolution time is now always allowed.");
+ // use eprintln instead of log::warn because logging hasn't been initialized yet
+ #[allow(clippy::print_stderr)]
+ {
+ eprintln!(
+ "{} `allow-hrtime` and `deny-hrtime` have been removed in Deno 2, as high resolution time is now always allowed",
+ deno_runtime::colors::yellow("Warning")
+ );
+ }
}
if matches.get_flag("allow-all") {
flags.allow_all();
}
+ allow_import_parse(flags, matches);
+
if matches.get_flag("no-prompt") {
flags.permissions.no_prompt = true;
}
@@ -5189,6 +5311,13 @@ fn permission_args_parse(
Ok(())
}
+fn allow_import_parse(flags: &mut Flags, matches: &mut ArgMatches) {
+ if let Some(imports_wl) = matches.remove_many::<String>("allow-import") {
+ let imports_allowlist = flags_net::parse(imports_wl.collect()).unwrap();
+ flags.permissions.allow_import = Some(imports_allowlist);
+ }
+}
+
fn unsafely_ignore_certificate_errors_parse(
flags: &mut Flags,
matches: &mut ArgMatches,
@@ -6215,7 +6344,7 @@ mod tests {
#[test]
fn short_permission_flags() {
- let r = flags_from_vec(svec!["deno", "run", "-RNESW", "gist.ts"]);
+ let r = flags_from_vec(svec!["deno", "run", "-RNESWI", "gist.ts"]);
assert_eq!(
r.unwrap(),
Flags {
@@ -6226,6 +6355,7 @@ mod tests {
allow_read: Some(vec![]),
allow_write: Some(vec![]),
allow_env: Some(vec![]),
+ allow_import: Some(vec![]),
allow_net: Some(vec![]),
allow_sys: Some(vec![]),
..Default::default()
@@ -10777,7 +10907,7 @@ mod tests {
}
);
// just make sure this doesn't panic
- let _ = flags.permissions.to_options();
+ let _ = flags.permissions.to_options(&[]);
}
#[test]
@@ -10852,4 +10982,27 @@ mod tests {
Usage: deno repl [OPTIONS] [-- [ARGS]...]\n"
)
}
+
+ #[test]
+ fn test_allow_import_host_from_url() {
+ fn parse(text: &str) -> Option<String> {
+ allow_import_host_from_url(&Url::parse(text).unwrap())
+ }
+
+ assert_eq!(parse("https://jsr.io"), None);
+ assert_eq!(
+ parse("http://127.0.0.1:4250"),
+ Some("127.0.0.1:4250".to_string())
+ );
+ assert_eq!(parse("http://jsr.io"), Some("jsr.io:80".to_string()));
+ assert_eq!(
+ parse("https://example.com"),
+ Some("example.com:443".to_string())
+ );
+ assert_eq!(
+ parse("http://example.com"),
+ Some("example.com:80".to_string())
+ );
+ assert_eq!(parse("file:///example.com"), None);
+ }
}
diff --git a/cli/args/mod.rs b/cli/args/mod.rs
index 1c92777ae..80b9afb24 100644
--- a/cli/args/mod.rs
+++ b/cli/args/mod.rs
@@ -769,6 +769,7 @@ pub struct CliOptions {
// application need not concern itself with, so keep these private
flags: Arc<Flags>,
initial_cwd: PathBuf,
+ main_module_cell: std::sync::OnceLock<Result<ModuleSpecifier, AnyError>>,
maybe_node_modules_folder: Option<PathBuf>,
npmrc: Arc<ResolvedNpmRc>,
maybe_lockfile: Option<Arc<CliLockfile>>,
@@ -825,6 +826,7 @@ impl CliOptions {
npmrc,
maybe_node_modules_folder,
overrides: Default::default(),
+ main_module_cell: std::sync::OnceLock::new(),
start_dir,
deno_dir_provider,
})
@@ -1105,40 +1107,43 @@ impl CliOptions {
self.flags.env_file.as_ref()
}
- pub fn resolve_main_module(&self) -> Result<ModuleSpecifier, AnyError> {
- let main_module = match &self.flags.subcommand {
- DenoSubcommand::Compile(compile_flags) => {
- resolve_url_or_path(&compile_flags.source_file, self.initial_cwd())?
- }
- DenoSubcommand::Eval(_) => {
- resolve_url_or_path("./$deno$eval.ts", self.initial_cwd())?
- }
- DenoSubcommand::Repl(_) => {
- resolve_url_or_path("./$deno$repl.ts", self.initial_cwd())?
- }
- DenoSubcommand::Run(run_flags) => {
- if run_flags.is_stdin() {
- std::env::current_dir()
- .context("Unable to get CWD")
- .and_then(|cwd| {
- resolve_url_or_path("./$deno$stdin.ts", &cwd)
- .map_err(AnyError::from)
- })?
- } else if NpmPackageReqReference::from_str(&run_flags.script).is_ok() {
- ModuleSpecifier::parse(&run_flags.script)?
- } else {
- resolve_url_or_path(&run_flags.script, self.initial_cwd())?
- }
- }
- DenoSubcommand::Serve(run_flags) => {
- resolve_url_or_path(&run_flags.script, self.initial_cwd())?
- }
- _ => {
- bail!("No main module.")
- }
- };
+ pub fn resolve_main_module(&self) -> Result<&ModuleSpecifier, AnyError> {
+ self
+ .main_module_cell
+ .get_or_init(|| {
+ let main_module = match &self.flags.subcommand {
+ DenoSubcommand::Compile(compile_flags) => {
+ resolve_url_or_path(&compile_flags.source_file, self.initial_cwd())?
+ }
+ DenoSubcommand::Eval(_) => {
+ resolve_url_or_path("./$deno$eval.ts", self.initial_cwd())?
+ }
+ DenoSubcommand::Repl(_) => {
+ resolve_url_or_path("./$deno$repl.ts", self.initial_cwd())?
+ }
+ DenoSubcommand::Run(run_flags) => {
+ if run_flags.is_stdin() {
+ resolve_url_or_path("./$deno$stdin.ts", self.initial_cwd())?
+ } else if NpmPackageReqReference::from_str(&run_flags.script)
+ .is_ok()
+ {
+ ModuleSpecifier::parse(&run_flags.script)?
+ } else {
+ resolve_url_or_path(&run_flags.script, self.initial_cwd())?
+ }
+ }
+ DenoSubcommand::Serve(run_flags) => {
+ resolve_url_or_path(&run_flags.script, self.initial_cwd())?
+ }
+ _ => {
+ bail!("No main module.")
+ }
+ };
- Ok(main_module)
+ Ok(main_module)
+ })
+ .as_ref()
+ .map_err(|err| deno_core::anyhow::anyhow!("{}", err))
}
pub fn resolve_file_header_overrides(
@@ -1159,7 +1164,7 @@ impl CliOptions {
(maybe_main_specifier, maybe_content_type)
{
HashMap::from([(
- main_specifier,
+ main_specifier.clone(),
HashMap::from([("content-type".to_string(), content_type.to_string())]),
)])
} else {
@@ -1480,7 +1485,34 @@ impl CliOptions {
}
pub fn permissions_options(&self) -> PermissionsOptions {
- self.flags.permissions.to_options()
+ fn files_to_urls(files: &[String]) -> Vec<Cow<'_, Url>> {
+ files
+ .iter()
+ .filter_map(|f| Url::parse(f).ok().map(Cow::Owned))
+ .collect()
+ }
+
+ // get a list of urls to imply for --allow-import
+ let cli_arg_urls = self
+ .resolve_main_module()
+ .ok()
+ .map(|url| vec![Cow::Borrowed(url)])
+ .or_else(|| match &self.flags.subcommand {
+ DenoSubcommand::Cache(cache_flags) => {
+ Some(files_to_urls(&cache_flags.files))
+ }
+ DenoSubcommand::Check(check_flags) => {
+ Some(files_to_urls(&check_flags.files))
+ }
+ DenoSubcommand::Install(InstallFlags {
+ kind: InstallKind::Global(flags),
+ }) => Url::parse(&flags.module_url)
+ .ok()
+ .map(|url| vec![Cow::Owned(url)]),
+ _ => None,
+ })
+ .unwrap_or_default();
+ self.flags.permissions.to_options(&cli_arg_urls)
}
pub fn reload_flag(&self) -> bool {
diff --git a/cli/cache/mod.rs b/cli/cache/mod.rs
index a95c35086..2d8813716 100644
--- a/cli/cache/mod.rs
+++ b/cli/cache/mod.rs
@@ -1,10 +1,11 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
+use crate::args::jsr_url;
use crate::args::CacheSetting;
use crate::errors::get_error_class_name;
use crate::file_fetcher::FetchNoFollowOptions;
use crate::file_fetcher::FetchOptions;
-use crate::file_fetcher::FetchPermissionsOption;
+use crate::file_fetcher::FetchPermissionsOptionRef;
use crate::file_fetcher::FileFetcher;
use crate::file_fetcher::FileOrRedirect;
use crate::npm::CliNpmResolver;
@@ -19,6 +20,7 @@ use deno_graph::source::CacheInfo;
use deno_graph::source::LoadFuture;
use deno_graph::source::LoadResponse;
use deno_graph::source::Loader;
+use deno_runtime::deno_permissions::PermissionsContainer;
use std::collections::HashMap;
use std::path::Path;
use std::path::PathBuf;
@@ -104,6 +106,13 @@ pub type LocalLspHttpCache =
deno_cache_dir::LocalLspHttpCache<RealDenoCacheEnv>;
pub use deno_cache_dir::HttpCache;
+pub struct FetchCacherOptions {
+ pub file_header_overrides: HashMap<ModuleSpecifier, HashMap<String, String>>,
+ pub permissions: PermissionsContainer,
+ /// If we're publishing for `deno publish`.
+ pub is_deno_publish: bool,
+}
+
/// A "wrapper" for the FileFetcher and DiskCache for the Deno CLI that provides
/// a concise interface to the DENO_DIR when building module graphs.
pub struct FetchCacher {
@@ -112,26 +121,27 @@ pub struct FetchCacher {
global_http_cache: Arc<GlobalHttpCache>,
npm_resolver: Arc<dyn CliNpmResolver>,
module_info_cache: Arc<ModuleInfoCache>,
- permissions: FetchPermissionsOption,
+ permissions: PermissionsContainer,
cache_info_enabled: bool,
+ is_deno_publish: bool,
}
impl FetchCacher {
pub fn new(
file_fetcher: Arc<FileFetcher>,
- file_header_overrides: HashMap<ModuleSpecifier, HashMap<String, String>>,
global_http_cache: Arc<GlobalHttpCache>,
npm_resolver: Arc<dyn CliNpmResolver>,
module_info_cache: Arc<ModuleInfoCache>,
- permissions: FetchPermissionsOption,
+ options: FetchCacherOptions,
) -> Self {
Self {
file_fetcher,
- file_header_overrides,
global_http_cache,
npm_resolver,
module_info_cache,
- permissions,
+ file_header_overrides: options.file_header_overrides,
+ permissions: options.permissions,
+ is_deno_publish: options.is_deno_publish,
cache_info_enabled: false,
}
}
@@ -208,10 +218,24 @@ impl Loader for FetchCacher {
}
}
+ if self.is_deno_publish
+ && matches!(specifier.scheme(), "http" | "https")
+ && !specifier.as_str().starts_with(jsr_url().as_str())
+ {
+ // mark non-JSR remote modules as external so we don't need --allow-import
+ // permissions as these will error out later when publishing
+ return Box::pin(futures::future::ready(Ok(Some(
+ LoadResponse::External {
+ specifier: specifier.clone(),
+ },
+ ))));
+ }
+
let file_fetcher = self.file_fetcher.clone();
let file_header_overrides = self.file_header_overrides.clone();
let permissions = self.permissions.clone();
let specifier = specifier.clone();
+ let is_statically_analyzable = !options.was_dynamic_root;
async move {
let maybe_cache_setting = match options.cache_setting {
@@ -230,7 +254,11 @@ impl Loader for FetchCacher {
.fetch_no_follow_with_options(FetchNoFollowOptions {
fetch_options: FetchOptions {
specifier: &specifier,
- permissions: permissions.as_ref(),
+ permissions: if is_statically_analyzable {
+ FetchPermissionsOptionRef::StaticContainer(&permissions)
+ } else {
+ FetchPermissionsOptionRef::DynamicContainer(&permissions)
+ },
maybe_accept: None,
maybe_cache_setting: maybe_cache_setting.as_ref(),
},
diff --git a/cli/factory.rs b/cli/factory.rs
index 0f49546d0..8aea635d2 100644
--- a/cli/factory.rs
+++ b/cli/factory.rs
@@ -185,6 +185,7 @@ struct CliFactoryServices {
node_resolver: Deferred<Arc<NodeResolver>>,
npm_resolver: Deferred<Arc<dyn CliNpmResolver>>,
permission_desc_parser: Deferred<Arc<RuntimePermissionDescriptorParser>>,
+ root_permissions_container: Deferred<PermissionsContainer>,
sloppy_imports_resolver: Deferred<Option<Arc<SloppyImportsResolver>>>,
text_only_progress_bar: Deferred<ProgressBar>,
type_checker: Deferred<Arc<TypeChecker>>,
@@ -626,6 +627,7 @@ impl CliFactory {
self.maybe_file_watcher_reporter().clone(),
self.file_fetcher()?.clone(),
self.global_http_cache()?.clone(),
+ self.root_permissions_container()?.clone(),
)))
})
.await
@@ -659,6 +661,7 @@ impl CliFactory {
Ok(Arc::new(MainModuleGraphContainer::new(
self.cli_options()?.clone(),
self.module_load_preparer().await?.clone(),
+ self.root_permissions_container()?.clone(),
)))
})
.await
@@ -755,15 +758,20 @@ impl CliFactory {
))
}
- pub fn create_permissions_container(
+ pub fn root_permissions_container(
&self,
- ) -> Result<PermissionsContainer, AnyError> {
- let desc_parser = self.permission_desc_parser()?.clone();
- let permissions = Permissions::from_options(
- desc_parser.as_ref(),
- &self.cli_options()?.permissions_options(),
- )?;
- Ok(PermissionsContainer::new(desc_parser, permissions))
+ ) -> Result<&PermissionsContainer, AnyError> {
+ self
+ .services
+ .root_permissions_container
+ .get_or_try_init(|| {
+ let desc_parser = self.permission_desc_parser()?.clone();
+ let permissions = Permissions::from_options(
+ desc_parser.as_ref(),
+ &self.cli_options()?.permissions_options(),
+ )?;
+ Ok(PermissionsContainer::new(desc_parser, permissions))
+ })
}
pub async fn create_cli_main_worker_factory(
@@ -816,6 +824,7 @@ impl CliFactory {
npm_resolver.clone(),
self.permission_desc_parser()?.clone(),
self.root_cert_store_provider().clone(),
+ self.root_permissions_container()?.clone(),
StorageKeyResolver::from_options(cli_options),
cli_options.sub_command().clone(),
self.create_cli_main_worker_options()?,
diff --git a/cli/file_fetcher.rs b/cli/file_fetcher.rs
index 2f4b0b3dc..ca1144939 100644
--- a/cli/file_fetcher.rs
+++ b/cli/file_fetcher.rs
@@ -23,6 +23,7 @@ use deno_graph::source::LoaderChecksum;
use deno_runtime::deno_permissions::PermissionsContainer;
use deno_runtime::deno_web::BlobStore;
+use deno_runtime::fs_util::specifier_to_file_path;
use log::debug;
use std::borrow::Cow;
use std::collections::HashMap;
@@ -135,7 +136,7 @@ impl MemoryFiles {
/// Fetch a source file from the local file system.
fn fetch_local(specifier: &ModuleSpecifier) -> Result<File, AnyError> {
- let local = specifier.to_file_path().map_err(|_| {
+ let local = specifier_to_file_path(specifier).map_err(|_| {
uri_error(format!("Invalid file path.\n Specifier: {specifier}"))
})?;
// If it doesnt have a extension, we want to treat it as typescript by default
@@ -173,30 +174,8 @@ fn get_validated_scheme(
#[derive(Debug, Copy, Clone)]
pub enum FetchPermissionsOptionRef<'a> {
AllowAll,
- Container(&'a PermissionsContainer),
-}
-
-#[derive(Debug, Clone)]
-pub enum FetchPermissionsOption {
- AllowAll,
- Container(PermissionsContainer),
-}
-
-impl FetchPermissionsOption {
- pub fn as_ref(&self) -> FetchPermissionsOptionRef {
- match self {
- FetchPermissionsOption::AllowAll => FetchPermissionsOptionRef::AllowAll,
- FetchPermissionsOption::Container(container) => {
- FetchPermissionsOptionRef::Container(container)
- }
- }
- }
-}
-
-impl From<PermissionsContainer> for FetchPermissionsOption {
- fn from(value: PermissionsContainer) -> Self {
- Self::Container(value)
- }
+ DynamicContainer(&'a PermissionsContainer),
+ StaticContainer(&'a PermissionsContainer),
}
pub struct FetchOptions<'a> {
@@ -564,7 +543,6 @@ impl FileFetcher {
}
/// Fetch a source file and asynchronously return it.
- #[allow(dead_code)] // todo(25469): undo when merging
#[inline(always)]
pub async fn fetch(
&self,
@@ -572,7 +550,10 @@ impl FileFetcher {
permissions: &PermissionsContainer,
) -> Result<File, AnyError> {
self
- .fetch_inner(specifier, FetchPermissionsOptionRef::Container(permissions))
+ .fetch_inner(
+ specifier,
+ FetchPermissionsOptionRef::StaticContainer(permissions),
+ )
.await
}
@@ -647,8 +628,17 @@ impl FileFetcher {
FetchPermissionsOptionRef::AllowAll => {
// allow
}
- FetchPermissionsOptionRef::Container(permissions) => {
- permissions.check_specifier(specifier)?;
+ FetchPermissionsOptionRef::StaticContainer(permissions) => {
+ permissions.check_specifier(
+ specifier,
+ deno_runtime::deno_permissions::CheckSpecifierKind::Static,
+ )?;
+ }
+ FetchPermissionsOptionRef::DynamicContainer(permissions) => {
+ permissions.check_specifier(
+ specifier,
+ deno_runtime::deno_permissions::CheckSpecifierKind::Dynamic,
+ )?;
}
}
if let Some(file) = self.memory_files.get(specifier) {
diff --git a/cli/graph_container.rs b/cli/graph_container.rs
index 211b278e0..c463d71a6 100644
--- a/cli/graph_container.rs
+++ b/cli/graph_container.rs
@@ -9,9 +9,9 @@ use deno_core::error::AnyError;
use deno_core::parking_lot::RwLock;
use deno_graph::ModuleGraph;
use deno_runtime::colors;
+use deno_runtime::deno_permissions::PermissionsContainer;
use crate::args::CliOptions;
-use crate::file_fetcher::FetchPermissionsOption;
use crate::module_loader::ModuleLoadPreparer;
use crate::util::fs::collect_specifiers;
use crate::util::path::is_script_ext;
@@ -45,12 +45,14 @@ pub struct MainModuleGraphContainer {
inner: Arc<RwLock<Arc<ModuleGraph>>>,
cli_options: Arc<CliOptions>,
module_load_preparer: Arc<ModuleLoadPreparer>,
+ root_permissions: PermissionsContainer,
}
impl MainModuleGraphContainer {
pub fn new(
cli_options: Arc<CliOptions>,
module_load_preparer: Arc<ModuleLoadPreparer>,
+ root_permissions: PermissionsContainer,
) -> Self {
Self {
update_queue: Default::default(),
@@ -59,6 +61,7 @@ impl MainModuleGraphContainer {
)))),
cli_options,
module_load_preparer,
+ root_permissions,
}
}
@@ -76,7 +79,7 @@ impl MainModuleGraphContainer {
specifiers,
false,
self.cli_options.ts_type_lib_window(),
- FetchPermissionsOption::AllowAll,
+ self.root_permissions.clone(),
ext_overwrite,
)
.await?;
diff --git a/cli/graph_util.rs b/cli/graph_util.rs
index 1add83eb9..d8f7ae749 100644
--- a/cli/graph_util.rs
+++ b/cli/graph_util.rs
@@ -11,7 +11,6 @@ use crate::cache::ModuleInfoCache;
use crate::cache::ParsedSourceCache;
use crate::colors;
use crate::errors::get_error_class_name;
-use crate::file_fetcher::FetchPermissionsOption;
use crate::file_fetcher::FileFetcher;
use crate::npm::CliNpmResolver;
use crate::resolver::CliGraphResolver;
@@ -41,6 +40,7 @@ use deno_graph::ResolutionError;
use deno_graph::SpecifierError;
use deno_runtime::deno_fs::FileSystem;
use deno_runtime::deno_node;
+use deno_runtime::deno_permissions::PermissionsContainer;
use deno_runtime::fs_util::specifier_to_file_path;
use deno_semver::jsr::JsrDepPackageReq;
use deno_semver::package::PackageNv;
@@ -249,6 +249,19 @@ impl ModuleGraphCreator {
package_configs: &[JsrPackageConfig],
build_fast_check_graph: bool,
) -> Result<ModuleGraph, AnyError> {
+ fn graph_has_external_remote(graph: &ModuleGraph) -> bool {
+ // Earlier on, we marked external non-JSR modules as external.
+ // If the graph contains any of those, it would cause type checking
+ // to crash, so since publishing is going to fail anyway, skip type
+ // checking.
+ graph.modules().any(|module| match module {
+ deno_graph::Module::External(external_module) => {
+ matches!(external_module.specifier.scheme(), "http" | "https")
+ }
+ _ => false,
+ })
+ }
+
let mut roots = Vec::new();
for package_config in package_configs {
roots.extend(package_config.config_file.resolve_export_value_urls()?);
@@ -262,9 +275,12 @@ impl ModuleGraphCreator {
})
.await?;
self.graph_valid(&graph)?;
- if self.options.type_check_mode().is_true() {
+ if self.options.type_check_mode().is_true()
+ && !graph_has_external_remote(&graph)
+ {
self.type_check_graph(graph.clone()).await?;
}
+
if build_fast_check_graph {
let fast_check_workspace_members = package_configs
.iter()
@@ -279,6 +295,7 @@ impl ModuleGraphCreator {
},
)?;
}
+
Ok(graph)
}
@@ -370,6 +387,7 @@ pub struct ModuleGraphBuilder {
maybe_file_watcher_reporter: Option<FileWatcherReporter>,
file_fetcher: Arc<FileFetcher>,
global_http_cache: Arc<GlobalHttpCache>,
+ root_permissions_container: PermissionsContainer,
}
impl ModuleGraphBuilder {
@@ -386,6 +404,7 @@ impl ModuleGraphBuilder {
maybe_file_watcher_reporter: Option<FileWatcherReporter>,
file_fetcher: Arc<FileFetcher>,
global_http_cache: Arc<GlobalHttpCache>,
+ root_permissions_container: PermissionsContainer,
) -> Self {
Self {
options,
@@ -399,6 +418,7 @@ impl ModuleGraphBuilder {
maybe_file_watcher_reporter,
file_fetcher,
global_http_cache,
+ root_permissions_container,
}
}
@@ -670,20 +690,26 @@ impl ModuleGraphBuilder {
/// Creates the default loader used for creating a graph.
pub fn create_graph_loader(&self) -> cache::FetchCacher {
- self.create_fetch_cacher(FetchPermissionsOption::AllowAll)
+ self.create_fetch_cacher(self.root_permissions_container.clone())
}
pub fn create_fetch_cacher(
&self,
- permissions: FetchPermissionsOption,
+ permissions: PermissionsContainer,
) -> cache::FetchCacher {
cache::FetchCacher::new(
self.file_fetcher.clone(),
- self.options.resolve_file_header_overrides(),
self.global_http_cache.clone(),
self.npm_resolver.clone(),
self.module_info_cache.clone(),
- permissions,
+ cache::FetchCacherOptions {
+ file_header_overrides: self.options.resolve_file_header_overrides(),
+ permissions,
+ is_deno_publish: matches!(
+ self.options.sub_command(),
+ crate::args::DenoSubcommand::Publish { .. }
+ ),
+ },
)
}
diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs
index 85daa4e28..aed4b47ef 100644
--- a/cli/lsp/language_server.rs
+++ b/cli/lsp/language_server.rs
@@ -3614,6 +3614,11 @@ impl Inner {
}),
// bit of a hack to force the lsp to cache the @types/node package
type_check_mode: crate::args::TypeCheckMode::Local,
+ permissions: crate::args::PermissionFlags {
+ // allow remote import permissions in the lsp for now
+ allow_import: Some(vec![]),
+ ..Default::default()
+ },
..Default::default()
}),
initial_cwd,
diff --git a/cli/module_loader.rs b/cli/module_loader.rs
index a81c5a0aa..f4e219bea 100644
--- a/cli/module_loader.rs
+++ b/cli/module_loader.rs
@@ -104,7 +104,7 @@ impl ModuleLoadPreparer {
roots: &[ModuleSpecifier],
is_dynamic: bool,
lib: TsTypeLib,
- permissions: crate::file_fetcher::FetchPermissionsOption,
+ permissions: PermissionsContainer,
ext_overwrite: Option<&String>,
) -> Result<(), AnyError> {
log::debug!("Preparing module load.");
@@ -252,13 +252,15 @@ impl CliModuleLoaderFactory {
&self,
graph_container: TGraphContainer,
lib: TsTypeLib,
- root_permissions: PermissionsContainer,
- dynamic_permissions: PermissionsContainer,
+ is_worker: bool,
+ parent_permissions: PermissionsContainer,
+ permissions: PermissionsContainer,
) -> ModuleLoaderAndSourceMapGetter {
let loader = Rc::new(CliModuleLoader(Rc::new(CliModuleLoaderInner {
lib,
- root_permissions,
- dynamic_permissions,
+ is_worker,
+ parent_permissions,
+ permissions,
graph_container,
emitter: self.shared.emitter.clone(),
parsed_source_cache: self.shared.parsed_source_cache.clone(),
@@ -274,20 +276,20 @@ impl ModuleLoaderFactory for CliModuleLoaderFactory {
fn create_for_main(
&self,
root_permissions: PermissionsContainer,
- dynamic_permissions: PermissionsContainer,
) -> ModuleLoaderAndSourceMapGetter {
self.create_with_lib(
(*self.shared.main_module_graph_container).clone(),
self.shared.lib_window,
+ /* is worker */ false,
+ root_permissions.clone(),
root_permissions,
- dynamic_permissions,
)
}
fn create_for_worker(
&self,
- root_permissions: PermissionsContainer,
- dynamic_permissions: PermissionsContainer,
+ parent_permissions: PermissionsContainer,
+ permissions: PermissionsContainer,
) -> ModuleLoaderAndSourceMapGetter {
self.create_with_lib(
// create a fresh module graph for the worker
@@ -295,21 +297,21 @@ impl ModuleLoaderFactory for CliModuleLoaderFactory {
self.shared.graph_kind,
))),
self.shared.lib_worker,
- root_permissions,
- dynamic_permissions,
+ /* is worker */ true,
+ parent_permissions,
+ permissions,
)
}
}
struct CliModuleLoaderInner<TGraphContainer: ModuleGraphContainer> {
lib: TsTypeLib,
+ is_worker: bool,
/// The initial set of permissions used to resolve the static imports in the
/// worker. These are "allow all" for main worker, and parent thread
/// permissions for Web Worker.
- root_permissions: PermissionsContainer,
- /// Permissions used to resolve dynamic imports, these get passed as
- /// "root permissions" for Web Worker.
- dynamic_permissions: PermissionsContainer,
+ parent_permissions: PermissionsContainer,
+ permissions: PermissionsContainer,
shared: Arc<SharedCliModuleLoaderState>,
emitter: Arc<Emitter>,
parsed_source_cache: Arc<ParsedSourceCache>,
@@ -769,11 +771,12 @@ impl<TGraphContainer: ModuleGraphContainer> ModuleLoader
}
}
- let root_permissions = if is_dynamic {
- inner.dynamic_permissions.clone()
+ let permissions = if is_dynamic {
+ inner.permissions.clone()
} else {
- inner.root_permissions.clone()
+ inner.parent_permissions.clone()
};
+ let is_dynamic = is_dynamic || inner.is_worker; // consider workers as dynamic for permissions
let lib = inner.lib;
let mut update_permit = graph_container.acquire_update_permit().await;
let graph = update_permit.graph_mut();
@@ -783,7 +786,7 @@ impl<TGraphContainer: ModuleGraphContainer> ModuleLoader
&[specifier],
is_dynamic,
lib,
- root_permissions.into(),
+ permissions,
None,
)
.await?;
diff --git a/cli/standalone/mod.rs b/cli/standalone/mod.rs
index bab266734..c501d2d6b 100644
--- a/cli/standalone/mod.rs
+++ b/cli/standalone/mod.rs
@@ -130,8 +130,6 @@ struct SharedModuleLoaderState {
#[derive(Clone)]
struct EmbeddedModuleLoader {
shared: Arc<SharedModuleLoaderState>,
- root_permissions: PermissionsContainer,
- dynamic_permissions: PermissionsContainer,
}
pub const MODULE_NOT_FOUND: &str = "Module not found";
@@ -402,28 +400,23 @@ struct StandaloneModuleLoaderFactory {
impl ModuleLoaderFactory for StandaloneModuleLoaderFactory {
fn create_for_main(
&self,
- root_permissions: PermissionsContainer,
- dynamic_permissions: PermissionsContainer,
+ _root_permissions: PermissionsContainer,
) -> ModuleLoaderAndSourceMapGetter {
ModuleLoaderAndSourceMapGetter {
module_loader: Rc::new(EmbeddedModuleLoader {
shared: self.shared.clone(),
- root_permissions,
- dynamic_permissions,
}),
}
}
fn create_for_worker(
&self,
- root_permissions: PermissionsContainer,
- dynamic_permissions: PermissionsContainer,
+ _parent_permissions: PermissionsContainer,
+ _permissions: PermissionsContainer,
) -> ModuleLoaderAndSourceMapGetter {
ModuleLoaderAndSourceMapGetter {
module_loader: Rc::new(EmbeddedModuleLoader {
shared: self.shared.clone(),
- root_permissions,
- dynamic_permissions,
}),
}
}
@@ -664,7 +657,8 @@ pub async fn run(
};
let permissions = {
- let mut permissions = metadata.permissions.to_options();
+ let mut permissions =
+ metadata.permissions.to_options(/* cli_arg_urls */ &[]);
// if running with an npm vfs, grant read access to it
if let Some(vfs_root) = maybe_vfs_root {
match &mut permissions.allow_read {
@@ -713,6 +707,7 @@ pub async fn run(
npm_resolver,
permission_desc_parser,
root_cert_store_provider,
+ permissions,
StorageKeyResolver::empty(),
crate::args::DenoSubcommand::Run(Default::default()),
CliMainWorkerOptions {
@@ -752,7 +747,7 @@ pub async fn run(
deno_core::JsRuntime::init_platform(None, true);
let mut worker = worker_factory
- .create_main_worker(WorkerExecutionMode::Run, main_module, permissions)
+ .create_main_worker(WorkerExecutionMode::Run, main_module)
.await?;
let exit_code = worker.run().await?;
diff --git a/cli/tools/check.rs b/cli/tools/check.rs
index c22afbb9a..7edb392d4 100644
--- a/cli/tools/check.rs
+++ b/cli/tools/check.rs
@@ -51,6 +51,7 @@ pub async fn check(
let specifiers_for_typecheck = if check_flags.doc || check_flags.doc_only {
let file_fetcher = factory.file_fetcher()?;
+ let root_permissions = factory.root_permissions_container()?;
let mut specifiers_for_typecheck = if check_flags.doc {
specifiers.clone()
@@ -59,7 +60,7 @@ pub async fn check(
};
for s in specifiers {
- let file = file_fetcher.fetch_bypass_permissions(&s).await?;
+ let file = file_fetcher.fetch(&s, root_permissions).await?;
let snippet_files = extract::extract_snippet_files(file)?;
for snippet_file in snippet_files {
specifiers_for_typecheck.push(snippet_file.specifier.clone());
diff --git a/cli/tools/compile.rs b/cli/tools/compile.rs
index b9620cfde..c1f98bc08 100644
--- a/cli/tools/compile.rs
+++ b/cli/tools/compile.rs
@@ -135,7 +135,7 @@ pub async fn compile(
file,
eszip,
root_dir_url,
- &module_specifier,
+ module_specifier,
&compile_flags,
cli_options,
)
diff --git a/cli/tools/info.rs b/cli/tools/info.rs
index d78b83cbe..174785631 100644
--- a/cli/tools/info.rs
+++ b/cli/tools/info.rs
@@ -644,8 +644,21 @@ impl<'a> GraphDisplayContext<'a> {
ModuleError::InvalidTypeAssertion { .. } => {
self.build_error_msg(specifier, "(invalid import attribute)")
}
- ModuleError::LoadingErr(_, _, _) => {
- self.build_error_msg(specifier, "(loading error)")
+ ModuleError::LoadingErr(_, _, err) => {
+ use deno_graph::ModuleLoadError::*;
+ let message = match err {
+ HttpsChecksumIntegrity(_) => "(checksum integrity error)",
+ Decode(_) => "(loading decode error)",
+ Loader(err) => match deno_core::error::get_custom_error_class(err) {
+ Some("NotCapable") => "(not capable, requires --allow-import)",
+ _ => "(loading error)",
+ },
+ Jsr(_) => "(loading error)",
+ NodeUnknownBuiltinModule(_) => "(unknown node built-in error)",
+ Npm(_) => "(npm loading error)",
+ TooManyRedirects => "(too many redirects error)",
+ };
+ self.build_error_msg(specifier, message.as_ref())
}
ModuleError::ParseErr(_, _) => {
self.build_error_msg(specifier, "(parsing error)")
diff --git a/cli/tools/registry/diagnostics.rs b/cli/tools/registry/diagnostics.rs
index c53a39683..733a78dda 100644
--- a/cli/tools/registry/diagnostics.rs
+++ b/cli/tools/registry/diagnostics.rs
@@ -3,7 +3,6 @@
use std::borrow::Cow;
use std::path::PathBuf;
use std::sync::Arc;
-use std::sync::Mutex;
use deno_ast::diagnostics::Diagnostic;
use deno_ast::diagnostics::DiagnosticLevel;
@@ -21,6 +20,7 @@ use deno_ast::SourceRanged;
use deno_ast::SourceTextInfo;
use deno_core::anyhow::anyhow;
use deno_core::error::AnyError;
+use deno_core::parking_lot::Mutex;
use deno_core::url::Url;
use deno_graph::FastCheckDiagnostic;
use deno_semver::Version;
@@ -36,7 +36,7 @@ impl PublishDiagnosticsCollector {
pub fn print_and_error(&self) -> Result<(), AnyError> {
let mut errors = 0;
let mut has_slow_types_errors = false;
- let mut diagnostics = self.diagnostics.lock().unwrap().take();
+ let mut diagnostics = self.diagnostics.lock().take();
diagnostics.sort_by_cached_key(|d| d.sorting_key());
@@ -75,8 +75,16 @@ impl PublishDiagnosticsCollector {
}
}
+ pub fn has_error(&self) -> bool {
+ self
+ .diagnostics
+ .lock()
+ .iter()
+ .any(|d| matches!(d.level(), DiagnosticLevel::Error))
+ }
+
pub fn push(&self, diagnostic: PublishDiagnostic) {
- self.diagnostics.lock().unwrap().push(diagnostic);
+ self.diagnostics.lock().push(diagnostic);
}
}
diff --git a/cli/tools/registry/mod.rs b/cli/tools/registry/mod.rs
index fbdcd9e77..941514b04 100644
--- a/cli/tools/registry/mod.rs
+++ b/cli/tools/registry/mod.rs
@@ -341,13 +341,11 @@ impl PublishPreparer {
bail!("Exiting due to DENO_INTERNAL_FAST_CHECK_OVERWRITE")
} else {
log::info!("Checking for slow types in the public API...");
- let mut any_pkg_had_diagnostics = false;
for package in package_configs {
let export_urls = package.config_file.resolve_export_value_urls()?;
let diagnostics =
collect_no_slow_type_diagnostics(&graph, &export_urls);
if !diagnostics.is_empty() {
- any_pkg_had_diagnostics = true;
for diagnostic in diagnostics {
diagnostics_collector
.push(PublishDiagnostic::FastCheck(diagnostic));
@@ -355,7 +353,9 @@ impl PublishPreparer {
}
}
- if any_pkg_had_diagnostics {
+ // skip type checking the slow type graph if there are any errors because
+ // errors like remote modules existing will cause type checking to crash
+ if diagnostics_collector.has_error() {
Ok(Arc::new(graph))
} else {
// fast check passed, type check the output as a temporary measure
diff --git a/cli/tools/registry/pm/cache_deps.rs b/cli/tools/registry/pm/cache_deps.rs
index a59817055..7d1773b34 100644
--- a/cli/tools/registry/pm/cache_deps.rs
+++ b/cli/tools/registry/pm/cache_deps.rs
@@ -16,6 +16,7 @@ pub async fn cache_top_level_deps(
) -> Result<(), AnyError> {
let npm_resolver = factory.npm_resolver().await?;
let cli_options = factory.cli_options()?;
+ let root_permissions = factory.root_permissions_container()?;
if let Some(npm_resolver) = npm_resolver.as_managed() {
if !npm_resolver.ensure_top_level_package_json_install().await? {
if let Some(lockfile) = cli_options.maybe_lockfile() {
@@ -106,7 +107,7 @@ pub async fn cache_top_level_deps(
&roots,
false,
deno_config::deno_json::TsTypeLib::DenoWorker,
- crate::file_fetcher::FetchPermissionsOption::AllowAll,
+ root_permissions.clone(),
None,
)
.await?;
diff --git a/cli/tools/repl/mod.rs b/cli/tools/repl/mod.rs
index 24bc8e30a..a30304687 100644
--- a/cli/tools/repl/mod.rs
+++ b/cli/tools/repl/mod.rs
@@ -162,7 +162,7 @@ pub async fn run(
let factory = CliFactory::from_flags(flags);
let cli_options = factory.cli_options()?;
let main_module = cli_options.resolve_main_module()?;
- let permissions = factory.create_permissions_container()?;
+ let permissions = factory.root_permissions_container()?;
let npm_resolver = factory.npm_resolver().await?.clone();
let resolver = factory.resolver().await?.clone();
let file_fetcher = factory.file_fetcher()?;
@@ -177,7 +177,7 @@ pub async fn run(
.create_custom_worker(
WorkerExecutionMode::Repl,
main_module.clone(),
- permissions,
+ permissions.clone(),
vec![crate::ops::testing::deno_test::init_ops(test_event_sender)],
Default::default(),
)
@@ -189,7 +189,7 @@ pub async fn run(
npm_resolver,
resolver,
worker,
- main_module,
+ main_module.clone(),
test_event_receiver,
)
.await?;
diff --git a/cli/tools/run/mod.rs b/cli/tools/run/mod.rs
index 200644490..152e2650b 100644
--- a/cli/tools/run/mod.rs
+++ b/cli/tools/run/mod.rs
@@ -60,10 +60,9 @@ pub async fn run_script(
maybe_npm_install(&factory).await?;
- let permissions = factory.create_permissions_container()?;
let worker_factory = factory.create_cli_main_worker_factory().await?;
let mut worker = worker_factory
- .create_main_worker(mode, main_module, permissions)
+ .create_main_worker(mode, main_module.clone())
.await?;
let exit_code = worker.run().await?;
@@ -79,7 +78,6 @@ pub async fn run_from_stdin(flags: Arc<Flags>) -> Result<i32, AnyError> {
let file_fetcher = factory.file_fetcher()?;
let worker_factory = factory.create_cli_main_worker_factory().await?;
- let permissions = factory.create_permissions_container()?;
let mut source = Vec::new();
std::io::stdin().read_to_end(&mut source)?;
// Save a fake file into file fetcher cache
@@ -91,7 +89,7 @@ pub async fn run_from_stdin(flags: Arc<Flags>) -> Result<i32, AnyError> {
});
let mut worker = worker_factory
- .create_main_worker(WorkerExecutionMode::Run, main_module, permissions)
+ .create_main_worker(WorkerExecutionMode::Run, main_module.clone())
.await?;
let exit_code = worker.run().await?;
Ok(exit_code)
@@ -125,11 +123,10 @@ async fn run_with_watch(
let _ = watcher_communicator.watch_paths(cli_options.watch_paths());
- let permissions = factory.create_permissions_container()?;
let mut worker = factory
.create_cli_main_worker_factory()
.await?
- .create_main_worker(mode, main_module, permissions)
+ .create_main_worker(mode, main_module.clone())
.await?;
if watch_flags.hmr {
@@ -173,10 +170,9 @@ pub async fn eval_command(
source: source_code.into_bytes().into(),
});
- let permissions = factory.create_permissions_container()?;
let worker_factory = factory.create_cli_main_worker_factory().await?;
let mut worker = worker_factory
- .create_main_worker(WorkerExecutionMode::Eval, main_module, permissions)
+ .create_main_worker(WorkerExecutionMode::Eval, main_module.clone())
.await?;
let exit_code = worker.run().await?;
Ok(exit_code)
diff --git a/cli/tools/serve.rs b/cli/tools/serve.rs
index 2f553cf1e..4ce1cad6f 100644
--- a/cli/tools/serve.rs
+++ b/cli/tools/serve.rs
@@ -5,7 +5,6 @@ use std::sync::Arc;
use deno_core::error::AnyError;
use deno_core::futures::TryFutureExt;
use deno_core::ModuleSpecifier;
-use deno_runtime::deno_permissions::PermissionsContainer;
use super::run::check_permission_before_script;
use super::run::maybe_npm_install;
@@ -44,13 +43,11 @@ pub async fn serve(
maybe_npm_install(&factory).await?;
- let permissions = factory.create_permissions_container()?;
let worker_factory = factory.create_cli_main_worker_factory().await?;
do_serve(
worker_factory,
- main_module,
- permissions,
+ main_module.clone(),
serve_flags.worker_count,
false,
)
@@ -60,7 +57,6 @@ pub async fn serve(
async fn do_serve(
worker_factory: CliMainWorkerFactory,
main_module: ModuleSpecifier,
- permissions: PermissionsContainer,
worker_count: Option<usize>,
hmr: bool,
) -> Result<i32, AnyError> {
@@ -71,7 +67,6 @@ async fn do_serve(
worker_count,
},
main_module.clone(),
- permissions.clone(),
)
.await?;
let worker_count = match worker_count {
@@ -87,15 +82,13 @@ async fn do_serve(
for i in 0..extra_workers {
let worker_factory = worker_factory.clone();
let main_module = main_module.clone();
- let permissions = permissions.clone();
let (tx, rx) = tokio::sync::oneshot::channel();
channels.push(rx);
std::thread::Builder::new()
.name(format!("serve-worker-{i}"))
.spawn(move || {
deno_runtime::tokio_util::create_and_run_current_thread(async move {
- let result =
- run_worker(i, worker_factory, main_module, permissions, hmr).await;
+ let result = run_worker(i, worker_factory, main_module, hmr).await;
let _ = tx.send(result);
});
})?;
@@ -124,7 +117,6 @@ async fn run_worker(
worker_count: usize,
worker_factory: CliMainWorkerFactory,
main_module: ModuleSpecifier,
- permissions: PermissionsContainer,
hmr: bool,
) -> Result<i32, AnyError> {
let mut worker = worker_factory
@@ -134,7 +126,6 @@ async fn run_worker(
worker_count: Some(worker_count),
},
main_module,
- permissions,
)
.await?;
if hmr {
@@ -171,11 +162,9 @@ async fn serve_with_watch(
maybe_npm_install(&factory).await?;
let _ = watcher_communicator.watch_paths(cli_options.watch_paths());
-
- let permissions = factory.create_permissions_container()?;
let worker_factory = factory.create_cli_main_worker_factory().await?;
- do_serve(worker_factory, main_module, permissions, worker_count, hmr)
+ do_serve(worker_factory, main_module.clone(), worker_count, hmr)
.await?;
Ok(())
diff --git a/cli/worker.rs b/cli/worker.rs
index 6176398d5..861419f1e 100644
--- a/cli/worker.rs
+++ b/cli/worker.rs
@@ -62,13 +62,12 @@ pub trait ModuleLoaderFactory: Send + Sync {
fn create_for_main(
&self,
root_permissions: PermissionsContainer,
- dynamic_permissions: PermissionsContainer,
) -> ModuleLoaderAndSourceMapGetter;
fn create_for_worker(
&self,
- root_permissions: PermissionsContainer,
- dynamic_permissions: PermissionsContainer,
+ parent_permissions: PermissionsContainer,
+ permissions: PermissionsContainer,
) -> ModuleLoaderAndSourceMapGetter;
}
@@ -136,6 +135,7 @@ struct SharedWorkerState {
npm_resolver: Arc<dyn CliNpmResolver>,
permission_desc_parser: Arc<RuntimePermissionDescriptorParser>,
root_cert_store_provider: Arc<dyn RootCertStoreProvider>,
+ root_permissions: PermissionsContainer,
shared_array_buffer_store: SharedArrayBufferStore,
storage_key_resolver: StorageKeyResolver,
options: CliMainWorkerOptions,
@@ -432,6 +432,7 @@ impl CliMainWorkerFactory {
npm_resolver: Arc<dyn CliNpmResolver>,
permission_parser: Arc<RuntimePermissionDescriptorParser>,
root_cert_store_provider: Arc<dyn RootCertStoreProvider>,
+ root_permissions: PermissionsContainer,
storage_key_resolver: StorageKeyResolver,
subcommand: DenoSubcommand,
options: CliMainWorkerOptions,
@@ -452,6 +453,7 @@ impl CliMainWorkerFactory {
npm_resolver,
permission_desc_parser: permission_parser,
root_cert_store_provider,
+ root_permissions,
shared_array_buffer_store: Default::default(),
storage_key_resolver,
options,
@@ -464,13 +466,12 @@ impl CliMainWorkerFactory {
&self,
mode: WorkerExecutionMode,
main_module: ModuleSpecifier,
- permissions: PermissionsContainer,
) -> Result<CliMainWorker, AnyError> {
self
.create_custom_worker(
mode,
main_module,
- permissions,
+ self.shared.root_permissions.clone(),
vec![],
Default::default(),
)
@@ -530,13 +531,9 @@ impl CliMainWorkerFactory {
(main_module, is_cjs)
};
- let ModuleLoaderAndSourceMapGetter { module_loader } =
- shared.module_loader_factory.create_for_main(
- PermissionsContainer::allow_all(
- self.shared.permission_desc_parser.clone(),
- ),
- permissions.clone(),
- );
+ let ModuleLoaderAndSourceMapGetter { module_loader } = shared
+ .module_loader_factory
+ .create_for_main(permissions.clone());
let maybe_inspector_server = shared.maybe_inspector_server.clone();
let create_web_worker_cb =