diff --git a/.dprint.json b/.dprint.json index ea99f2aeda..489a61e571 100644 --- a/.dprint.json +++ b/.dprint.json @@ -68,7 +68,7 @@ "third_party" ], "plugins": [ - "https://plugins.dprint.dev/typescript-0.93.1.wasm", + "https://plugins.dprint.dev/typescript-0.93.2.wasm", "https://plugins.dprint.dev/json-0.19.4.wasm", "https://plugins.dprint.dev/markdown-0.17.8.wasm", "https://plugins.dprint.dev/toml-0.6.3.wasm", diff --git a/.github/workflows/ci.generate.ts b/.github/workflows/ci.generate.ts index 49d11107d5..0faf08a1a8 100755 --- a/.github/workflows/ci.generate.ts +++ b/.github/workflows/ci.generate.ts @@ -14,6 +14,7 @@ const windowsX86Runner = "windows-2022"; const windowsX86XlRunner = "windows-2022-xl"; const macosX86Runner = "macos-13"; const macosArmRunner = "macos-14"; +const selfHostedMacosArmRunner = "self-hosted"; const Runners = { linuxX86: { @@ -40,7 +41,8 @@ const Runners = { macosArm: { os: "macos", arch: "aarch64", - runner: macosArmRunner, + runner: + `\${{ github.repository == 'denoland/deno' && startsWith(github.ref, 'refs/tags/') && '${selfHostedMacosArmRunner}' || '${macosArmRunner}' }}`, }, windowsX86: { os: "windows", diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a9c06de17a..5aafe89d27 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -68,12 +68,12 @@ jobs: skip: '${{ !contains(github.event.pull_request.labels.*.name, ''ci-full'') && (github.event_name == ''pull_request'') }}' - os: macos arch: aarch64 - runner: macos-14 + runner: '${{ github.repository == ''denoland/deno'' && startsWith(github.ref, ''refs/tags/'') && ''self-hosted'' || ''macos-14'' }}' job: test profile: debug - os: macos arch: aarch64 - runner: '${{ (!contains(github.event.pull_request.labels.*.name, ''ci-full'') && (github.event_name == ''pull_request'')) && ''ubuntu-24.04'' || ''macos-14'' }}' + runner: '${{ (!contains(github.event.pull_request.labels.*.name, ''ci-full'') && (github.event_name == ''pull_request'')) && ''ubuntu-24.04'' || github.repository == ''denoland/deno'' && startsWith(github.ref, ''refs/tags/'') && ''self-hosted'' || ''macos-14'' }}' job: test profile: release skip: '${{ !contains(github.event.pull_request.labels.*.name, ''ci-full'') && (github.event_name == ''pull_request'') }}' diff --git a/Cargo.lock b/Cargo.lock index 8c7b35ca9f..73d417baae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1387,9 +1387,9 @@ dependencies = [ [[package]] name = "deno_config" -version = "0.37.2" +version = "0.38.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5900bfb37538d83b19ba0b157cdc785770e38422ee4632411e3bd3d90ac0f537" +checksum = "966825073480a6ac7e01977a3879d13edc8d6ea2d65ea164b37156a5fb206e9a" dependencies = [ "anyhow", "deno_package_json", @@ -1418,9 +1418,9 @@ dependencies = [ [[package]] name = "deno_core" -version = "0.316.0" +version = "0.318.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94f68061c88ced959c6b0417f0f0d0b3dbeaeb18013b55f86c505e9fba705cf8" +checksum = "10cae2393219ff9278123f7b24799cdfab37c7d6561b69ca06ced115cac92111" dependencies = [ "anyhow", "bincode", @@ -1921,9 +1921,9 @@ dependencies = [ [[package]] name = "deno_ops" -version = "0.192.0" +version = "0.194.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdb7096887508456349d7e7e09e326d157d4dba46ef1f5849bc544592ea3042a" +checksum = "f760b492bd638c1dc3e992d11672c259fbe9a233162099a8347591c9e22d0391" dependencies = [ "proc-macro-rules", "proc-macro2", @@ -1972,6 +1972,7 @@ dependencies = [ "once_cell", "percent-encoding", "serde", + "thiserror", "which 4.4.2", "winapi", ] @@ -2608,9 +2609,9 @@ dependencies = [ [[package]] name = "dprint-plugin-typescript" -version = "0.93.1" +version = "0.93.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5abfd78fe3cde4f5a6699d65f760c8d44da130cf446b6f80a7a9bc6580e156ab" +checksum = "3ff29fd136541e59d51946f0d2d353fefc886776f61a799ebfb5838b06cef13b" dependencies = [ "anyhow", "deno_ast", @@ -6169,9 +6170,9 @@ dependencies = [ [[package]] name = "serde_v8" -version = "0.225.0" +version = "0.227.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce4b71200ef49a9e629edaea3d13fc98c25ede07e1496558df7f09354e37976f" +checksum = "0a8294c2223c53bed343be8b80564ece4dc0d03b643b06fa86c4ccc0e064eda0" dependencies = [ "num-bigint", "serde", diff --git a/Cargo.toml b/Cargo.toml index f57563e0b8..28ef2829e0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -46,7 +46,7 @@ repository = "https://github.com/denoland/deno" [workspace.dependencies] deno_ast = { version = "=0.43.3", features = ["transpiling"] } -deno_core = { version = "0.316.0" } +deno_core = { version = "0.318.0" } deno_bench_util = { version = "0.169.0", path = "./bench_util" } deno_lockfile = "=0.23.1" diff --git a/cli/Cargo.toml b/cli/Cargo.toml index a3e2b71f00..1fa9692a4d 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -70,7 +70,7 @@ winres.workspace = true [dependencies] deno_ast = { workspace = true, features = ["bundler", "cjs", "codegen", "proposal", "react", "sourcemap", "transforms", "typescript", "view", "visit"] } deno_cache_dir = { workspace = true } -deno_config = { version = "=0.37.2", features = ["workspace", "sync"] } +deno_config = { version = "=0.38.2", features = ["workspace", "sync"] } deno_core = { workspace = true, features = ["include_js_files_for_snapshotting"] } deno_doc = { version = "0.156.0", default-features = false, features = ["rust", "html", "syntect"] } deno_graph = { version = "=0.84.1" } @@ -107,7 +107,7 @@ dotenvy = "0.15.7" dprint-plugin-json = "=0.19.4" dprint-plugin-jupyter = "=0.1.5" dprint-plugin-markdown = "=0.17.8" -dprint-plugin-typescript = "=0.93.1" +dprint-plugin-typescript = "=0.93.2" env_logger = "=0.10.0" fancy-regex = "=0.10.0" faster-hex.workspace = true diff --git a/cli/args/flags.rs b/cli/args/flags.rs index 1a1213aac2..eb77971748 100644 --- a/cli/args/flags.rs +++ b/cli/args/flags.rs @@ -3388,8 +3388,7 @@ fn permission_args(app: Command, requires: Option<&'static str>) -> Command { .value_name("IP_OR_HOSTNAME") .help("Allow network access. Optionally specify allowed IP addresses and host names, with ports as necessary") .value_parser(flags_net::validator) - .hide(true) - ; + .hide(true); if let Some(requires) = requires { arg = arg.requires(requires) } diff --git a/cli/args/flags_net.rs b/cli/args/flags_net.rs index 88ffcf0e46..abfcf28382 100644 --- a/cli/args/flags_net.rs +++ b/cli/args/flags_net.rs @@ -51,7 +51,7 @@ pub fn parse(paths: Vec) -> clap::error::Result> { } } else { NetDescriptor::parse(&host_and_port).map_err(|e| { - clap::Error::raw(clap::error::ErrorKind::InvalidValue, format!("{e:?}")) + clap::Error::raw(clap::error::ErrorKind::InvalidValue, e.to_string()) })?; out.push(host_and_port) } diff --git a/cli/bench/encode_into.js b/cli/bench/encode_into.js index 11f5a56d90..ab5e11b04d 100644 --- a/cli/bench/encode_into.js +++ b/cli/bench/encode_into.js @@ -1,6 +1,5 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -// deno-lint-ignore-file no-console +// deno-lint-ignore-file no-console no-process-globals let [total, count] = typeof Deno !== "undefined" ? Deno.args diff --git a/cli/bench/getrandom.js b/cli/bench/getrandom.js index 3c3ec4aa19..fe99bbcbdf 100644 --- a/cli/bench/getrandom.js +++ b/cli/bench/getrandom.js @@ -1,6 +1,5 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -// deno-lint-ignore-file no-console +// deno-lint-ignore-file no-console no-process-globals let [total, count] = typeof Deno !== "undefined" ? Deno.args diff --git a/cli/bench/op_now.js b/cli/bench/op_now.js index bcc3ea3c56..7c1427c809 100644 --- a/cli/bench/op_now.js +++ b/cli/bench/op_now.js @@ -1,6 +1,5 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -// deno-lint-ignore-file no-console +// deno-lint-ignore-file no-console no-process-globals const queueMicrotask = globalThis.queueMicrotask || process.nextTick; let [total, count] = typeof Deno !== "undefined" diff --git a/cli/bench/secure_curves.js b/cli/bench/secure_curves.js index 02d248b23f..912b75cccd 100644 --- a/cli/bench/secure_curves.js +++ b/cli/bench/secure_curves.js @@ -1,6 +1,5 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -// deno-lint-ignore-file no-console +// deno-lint-ignore-file no-console no-process-globals let [total, count] = typeof Deno !== "undefined" ? Deno.args diff --git a/cli/bench/tty.js b/cli/bench/tty.js index 248a901137..e494e76af7 100644 --- a/cli/bench/tty.js +++ b/cli/bench/tty.js @@ -1,6 +1,5 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -// deno-lint-ignore-file no-console +// deno-lint-ignore-file no-console no-process-globals const queueMicrotask = globalThis.queueMicrotask || process.nextTick; let [total, count] = typeof Deno !== "undefined" diff --git a/cli/bench/url_parse.js b/cli/bench/url_parse.js index 367cf73f46..9cb0045f64 100644 --- a/cli/bench/url_parse.js +++ b/cli/bench/url_parse.js @@ -1,6 +1,5 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -// deno-lint-ignore-file no-console +// deno-lint-ignore-file no-console no-process-globals const queueMicrotask = globalThis.queueMicrotask || process.nextTick; let [total, count] = typeof Deno !== "undefined" diff --git a/cli/bench/write_file.js b/cli/bench/write_file.js index 104a23a8db..747503ce2a 100644 --- a/cli/bench/write_file.js +++ b/cli/bench/write_file.js @@ -1,6 +1,5 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - -// deno-lint-ignore-file no-console +// deno-lint-ignore-file no-console no-process-globals const queueMicrotask = globalThis.queueMicrotask || process.nextTick; let [total, count] = typeof Deno !== "undefined" diff --git a/cli/lsp/language_server.rs b/cli/lsp/language_server.rs index 4fa0e3afbc..61a02f036e 100644 --- a/cli/lsp/language_server.rs +++ b/cli/lsp/language_server.rs @@ -863,7 +863,10 @@ impl Inner { // We ignore these directories by default because there is a // high likelihood they aren't relevant. Someone can opt-into // them by specifying one of them as an enabled path. - if matches!(dir_name.as_str(), "vendor" | "node_modules" | ".git") { + if matches!( + dir_name.as_str(), + "vendor" | "coverage" | "node_modules" | ".git" + ) { continue; } // ignore cargo target directories for anyone using Deno with Rust @@ -3944,7 +3947,9 @@ mod tests { fn test_walk_workspace() { let temp_dir = TempDir::new(); temp_dir.create_dir_all("root1/vendor/"); + temp_dir.create_dir_all("root1/coverage/"); temp_dir.write("root1/vendor/mod.ts", ""); // no, vendor + temp_dir.write("root1/coverage/mod.ts", ""); // no, coverage temp_dir.create_dir_all("root1/node_modules/"); temp_dir.write("root1/node_modules/mod.ts", ""); // no, node_modules diff --git a/cli/npm/byonm.rs b/cli/npm/byonm.rs index 4535b07fc5..45fa4cfd1f 100644 --- a/cli/npm/byonm.rs +++ b/cli/npm/byonm.rs @@ -89,7 +89,7 @@ impl CliNpmResolver for CliByonmNpmResolver { .components() .any(|c| c.as_os_str().to_ascii_lowercase() == "node_modules") { - permissions.check_read_path(path) + permissions.check_read_path(path).map_err(Into::into) } else { Ok(Cow::Borrowed(path)) } diff --git a/cli/npm/managed/resolvers/common.rs b/cli/npm/managed/resolvers/common.rs index 590f8fb25c..eee11c7604 100644 --- a/cli/npm/managed/resolvers/common.rs +++ b/cli/npm/managed/resolvers/common.rs @@ -133,7 +133,7 @@ impl RegistryReadPermissionChecker { } } - permissions.check_read_path(path) + permissions.check_read_path(path).map_err(Into::into) } } diff --git a/cli/npm/managed/resolvers/local.rs b/cli/npm/managed/resolvers/local.rs index 0968be8a7e..eddb0dc9b6 100644 --- a/cli/npm/managed/resolvers/local.rs +++ b/cli/npm/managed/resolvers/local.rs @@ -1035,12 +1035,18 @@ fn junction_or_symlink_dir( if symlink_err.kind() == std::io::ErrorKind::PermissionDenied => { USE_JUNCTIONS.store(true, std::sync::atomic::Ordering::Relaxed); - junction::create(old_path, new_path).map_err(Into::into) + junction::create(old_path, new_path) + .context("Failed creating junction in node_modules folder") + } + Err(symlink_err) => { + log::warn!( + "{} Unexpected error symlinking node_modules: {symlink_err}", + colors::yellow("Warning") + ); + USE_JUNCTIONS.store(true, std::sync::atomic::Ordering::Relaxed); + junction::create(old_path, new_path) + .context("Failed creating junction in node_modules folder") } - Err(symlink_err) => Err( - AnyError::from(symlink_err) - .context("Failed creating symlink in node_modules folder"), - ), } } diff --git a/cli/tools/fmt.rs b/cli/tools/fmt.rs index 81af25c34b..8378835612 100644 --- a/cli/tools/fmt.rs +++ b/cli/tools/fmt.rs @@ -353,6 +353,21 @@ fn format_yaml( file_text: &str, fmt_options: &FmtOptionsConfig, ) -> Result, AnyError> { + let ignore_file = file_text + .lines() + .take_while(|line| line.starts_with('#')) + .any(|line| { + line + .strip_prefix('#') + .unwrap() + .trim() + .starts_with("deno-fmt-ignore-file") + }); + + if ignore_file { + return Ok(None); + } + let formatted_str = pretty_yaml::format_text(file_text, &get_resolved_yaml_config(fmt_options)) .map_err(AnyError::from)?; diff --git a/cli/tools/info.rs b/cli/tools/info.rs index b53485bd6b..c2f5a8cb8d 100644 --- a/cli/tools/info.rs +++ b/cli/tools/info.rs @@ -645,10 +645,12 @@ impl<'a> GraphDisplayContext<'a> { 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)", - }, + Loader(err) => { + match deno_runtime::errors::get_error_class_name(err) { + Some("NotCapable") => "(not capable, requires --allow-import)", + _ => "(loading error)", + } + } Jsr(_) => "(loading error)", NodeUnknownBuiltinModule(_) => "(unknown node built-in error)", Npm(_) => "(npm loading error)", diff --git a/cli/tools/registry/pm.rs b/cli/tools/registry/pm.rs index 4e03879983..68913e2591 100644 --- a/cli/tools/registry/pm.rs +++ b/cli/tools/registry/pm.rs @@ -12,7 +12,9 @@ use deno_core::futures::StreamExt; use deno_path_util::url_to_file_path; use deno_semver::jsr::JsrPackageReqReference; use deno_semver::npm::NpmPackageReqReference; +use deno_semver::package::PackageNv; use deno_semver::package::PackageReq; +use deno_semver::Version; use deno_semver::VersionReq; use jsonc_parser::cst::CstObject; use jsonc_parser::cst::CstObjectProp; @@ -455,15 +457,32 @@ pub async fn add( match package_and_version { PackageAndVersion::NotFound { package: package_name, - found_npm_package, + help, package_req, - } => { - if found_npm_package { - bail!("{} was not found, but a matching npm package exists. Did you mean `{}`?", crate::colors::red(package_name), crate::colors::yellow(format!("deno {cmd_name} npm:{package_req}"))); - } else { - bail!("{} was not found.", crate::colors::red(package_name)); + } => match help { + Some(NotFoundHelp::NpmPackage) => { + bail!( + "{} was not found, but a matching npm package exists. Did you mean `{}`?", + crate::colors::red(package_name), + crate::colors::yellow(format!("deno {cmd_name} npm:{package_req}")) + ); } - } + Some(NotFoundHelp::JsrPackage) => { + bail!( + "{} was not found, but a matching jsr package exists. Did you mean `{}`?", + crate::colors::red(package_name), + crate::colors::yellow(format!("deno {cmd_name} jsr:{package_req}")) + ) + } + Some(NotFoundHelp::PreReleaseVersion(version)) => { + bail!( + "{} has only pre-release versions available. Try specifying a version: `{}`", + crate::colors::red(&package_name), + crate::colors::yellow(format!("deno {cmd_name} {package_name}@^{version}")) + ) + } + None => bail!("{} was not found.", crate::colors::red(package_name)), + }, PackageAndVersion::Selected(selected) => { selected_packages.push(selected); } @@ -511,76 +530,144 @@ struct SelectedPackage { selected_version: String, } +enum NotFoundHelp { + NpmPackage, + JsrPackage, + PreReleaseVersion(Version), +} + enum PackageAndVersion { NotFound { package: String, - found_npm_package: bool, package_req: PackageReq, + help: Option, }, Selected(SelectedPackage), } +fn best_version<'a>( + versions: impl Iterator, +) -> Option<&'a Version> { + let mut maybe_best_version: Option<&Version> = None; + for version in versions { + let is_best_version = maybe_best_version + .as_ref() + .map(|best_version| (*best_version).cmp(version).is_lt()) + .unwrap_or(true); + if is_best_version { + maybe_best_version = Some(version); + } + } + maybe_best_version +} + +trait PackageInfoProvider { + const SPECIFIER_PREFIX: &str; + /// The help to return if a package is found by this provider + const HELP: NotFoundHelp; + async fn req_to_nv(&self, req: &PackageReq) -> Option; + async fn latest_version<'a>(&self, req: &PackageReq) -> Option; +} + +impl PackageInfoProvider for Arc { + const HELP: NotFoundHelp = NotFoundHelp::JsrPackage; + const SPECIFIER_PREFIX: &str = "jsr"; + async fn req_to_nv(&self, req: &PackageReq) -> Option { + (**self).req_to_nv(req).await + } + + async fn latest_version<'a>(&self, req: &PackageReq) -> Option { + let info = self.package_info(&req.name).await?; + best_version( + info + .versions + .iter() + .filter(|(_, version_info)| !version_info.yanked) + .map(|(version, _)| version), + ) + .cloned() + } +} + +impl PackageInfoProvider for Arc { + const HELP: NotFoundHelp = NotFoundHelp::NpmPackage; + const SPECIFIER_PREFIX: &str = "npm"; + async fn req_to_nv(&self, req: &PackageReq) -> Option { + (**self).req_to_nv(req).await + } + + async fn latest_version<'a>(&self, req: &PackageReq) -> Option { + let info = self.package_info(&req.name).await?; + best_version(info.versions.keys()).cloned() + } +} + async fn find_package_and_select_version_for_req( jsr_resolver: Arc, npm_resolver: Arc, add_package_req: AddRmPackageReq, ) -> Result { - match add_package_req.value { - AddRmPackageReqValue::Jsr(req) => { - let jsr_prefixed_name = format!("jsr:{}", &req.name); - let Some(nv) = jsr_resolver.req_to_nv(&req).await else { - if npm_resolver.req_to_nv(&req).await.is_some() { + async fn select( + main_resolver: T, + fallback_resolver: S, + add_package_req: AddRmPackageReq, + ) -> Result { + let req = match &add_package_req.value { + AddRmPackageReqValue::Jsr(req) => req, + AddRmPackageReqValue::Npm(req) => req, + }; + let prefixed_name = format!("{}:{}", T::SPECIFIER_PREFIX, req.name); + let help_if_found_in_fallback = S::HELP; + let Some(nv) = main_resolver.req_to_nv(req).await else { + if fallback_resolver.req_to_nv(req).await.is_some() { + // it's in the other registry + return Ok(PackageAndVersion::NotFound { + package: prefixed_name, + help: Some(help_if_found_in_fallback), + package_req: req.clone(), + }); + } + if req.version_req.version_text() == "*" { + if let Some(pre_release_version) = + main_resolver.latest_version(req).await + { return Ok(PackageAndVersion::NotFound { - package: jsr_prefixed_name, - found_npm_package: true, - package_req: req, + package: prefixed_name, + package_req: req.clone(), + help: Some(NotFoundHelp::PreReleaseVersion( + pre_release_version.clone(), + )), }); } + } - return Ok(PackageAndVersion::NotFound { - package: jsr_prefixed_name, - found_npm_package: false, - package_req: req, - }); - }; - let range_symbol = if req.version_req.version_text().starts_with('~') { - "~" - } else if req.version_req.version_text() == nv.version.to_string() { - "" - } else { - "^" - }; - Ok(PackageAndVersion::Selected(SelectedPackage { - import_name: add_package_req.alias, - package_name: jsr_prefixed_name, - version_req: format!("{}{}", range_symbol, &nv.version), - selected_version: nv.version.to_string(), - })) + return Ok(PackageAndVersion::NotFound { + package: prefixed_name, + help: None, + package_req: req.clone(), + }); + }; + let range_symbol = if req.version_req.version_text().starts_with('~') { + "~" + } else if req.version_req.version_text() == nv.version.to_string() { + "" + } else { + "^" + }; + Ok(PackageAndVersion::Selected(SelectedPackage { + import_name: add_package_req.alias, + package_name: prefixed_name, + version_req: format!("{}{}", range_symbol, &nv.version), + selected_version: nv.version.to_string(), + })) + } + + match &add_package_req.value { + AddRmPackageReqValue::Jsr(_) => { + select(jsr_resolver, npm_resolver, add_package_req).await } - AddRmPackageReqValue::Npm(req) => { - let npm_prefixed_name = format!("npm:{}", &req.name); - let Some(nv) = npm_resolver.req_to_nv(&req).await else { - return Ok(PackageAndVersion::NotFound { - package: npm_prefixed_name, - found_npm_package: false, - package_req: req, - }); - }; - - let range_symbol = if req.version_req.version_text().starts_with('~') { - "~" - } else if req.version_req.version_text() == nv.version.to_string() { - "" - } else { - "^" - }; - - Ok(PackageAndVersion::Selected(SelectedPackage { - import_name: add_package_req.alias, - package_name: npm_prefixed_name, - version_req: format!("{}{}", range_symbol, &nv.version), - selected_version: nv.version.to_string(), - })) + AddRmPackageReqValue::Npm(_) => { + select(npm_resolver, jsr_resolver, add_package_req).await } } } diff --git a/cli/tsc/99_main_compiler.js b/cli/tsc/99_main_compiler.js index bdc4340e33..52c9134dad 100644 --- a/cli/tsc/99_main_compiler.js +++ b/cli/tsc/99_main_compiler.js @@ -801,13 +801,18 @@ delete Object.prototype.__proto__; if (logDebug) { debug(`host.getScriptSnapshot("${specifier}")`); } - const sourceFile = sourceFileCache.get(specifier); - if (sourceFile) { - if (!assetScopes.has(specifier)) { - assetScopes.set(specifier, lastRequestScope); + if (specifier.startsWith(ASSETS_URL_PREFIX)) { + const sourceFile = this.getSourceFile( + specifier, + ts.ScriptTarget.ESNext, + ); + if (sourceFile) { + if (!assetScopes.has(specifier)) { + assetScopes.set(specifier, lastRequestScope); + } + // This case only occurs for assets. + return ts.ScriptSnapshot.fromString(sourceFile.text); } - // This case only occurs for assets. - return ts.ScriptSnapshot.fromString(sourceFile.text); } let sourceText = sourceTextCache.get(specifier); if (sourceText == undefined) { diff --git a/cli/tsc/dts/lib.deno.ns.d.ts b/cli/tsc/dts/lib.deno.ns.d.ts index 36592e10dc..6e0e84b687 100644 --- a/cli/tsc/dts/lib.deno.ns.d.ts +++ b/cli/tsc/dts/lib.deno.ns.d.ts @@ -556,14 +556,23 @@ declare namespace Deno { */ env?: "inherit" | boolean | string[]; - /** Specifies if the `sys` permission should be requested or revoked. - * If set to `"inherit"`, the current `sys` permission will be inherited. - * If set to `true`, the global `sys` permission will be requested. - * If set to `false`, the global `sys` permission will be revoked. + /** Specifies if the `ffi` permission should be requested or revoked. + * If set to `"inherit"`, the current `ffi` permission will be inherited. + * If set to `true`, the global `ffi` permission will be requested. + * If set to `false`, the global `ffi` permission will be revoked. * * @default {false} */ - sys?: "inherit" | boolean | string[]; + ffi?: "inherit" | boolean | Array; + + /** Specifies if the `import` permission should be requested or revoked. + * If set to `"inherit"` the current `import` permission will be inherited. + * If set to `true`, the global `import` permission will be requested. + * If set to `false`, the global `import` permission will be revoked. + * If set to `Array`, the `import` permissions will be requested with the + * specified domains. + */ + import?: "inherit" | boolean | Array; /** Specifies if the `net` permission should be requested or revoked. * if set to `"inherit"`, the current `net` permission will be inherited. @@ -638,15 +647,6 @@ declare namespace Deno { */ net?: "inherit" | boolean | string[]; - /** Specifies if the `ffi` permission should be requested or revoked. - * If set to `"inherit"`, the current `ffi` permission will be inherited. - * If set to `true`, the global `ffi` permission will be requested. - * If set to `false`, the global `ffi` permission will be revoked. - * - * @default {false} - */ - ffi?: "inherit" | boolean | Array; - /** Specifies if the `read` permission should be requested or revoked. * If set to `"inherit"`, the current `read` permission will be inherited. * If set to `true`, the global `read` permission will be requested. @@ -667,6 +667,15 @@ declare namespace Deno { */ run?: "inherit" | boolean | Array; + /** Specifies if the `sys` permission should be requested or revoked. + * If set to `"inherit"`, the current `sys` permission will be inherited. + * If set to `true`, the global `sys` permission will be requested. + * If set to `false`, the global `sys` permission will be revoked. + * + * @default {false} + */ + sys?: "inherit" | boolean | string[]; + /** Specifies if the `write` permission should be requested or revoked. * If set to `"inherit"`, the current `write` permission will be inherited. * If set to `true`, the global `write` permission will be requested. diff --git a/cli/util/fs.rs b/cli/util/fs.rs index 2c34f486ad..d36c02242c 100644 --- a/cli/util/fs.rs +++ b/cli/util/fs.rs @@ -565,7 +565,9 @@ pub fn symlink_dir(oldpath: &Path, newpath: &Path) -> Result<(), Error> { use std::os::windows::fs::symlink_dir; symlink_dir(oldpath, newpath).map_err(|err| { if let Some(code) = err.raw_os_error() { - if code as u32 == winapi::shared::winerror::ERROR_PRIVILEGE_NOT_HELD { + if code as u32 == winapi::shared::winerror::ERROR_PRIVILEGE_NOT_HELD + || code as u32 == winapi::shared::winerror::ERROR_INVALID_FUNCTION + { return err_mapper(err, Some(ErrorKind::PermissionDenied)); } } diff --git a/ext/fetch/lib.rs b/ext/fetch/lib.rs index 4df8dc3d72..7ef26431c2 100644 --- a/ext/fetch/lib.rs +++ b/ext/fetch/lib.rs @@ -39,6 +39,7 @@ use deno_core::OpState; use deno_core::RcRef; use deno_core::Resource; use deno_core::ResourceId; +use deno_permissions::PermissionCheckError; use deno_tls::rustls::RootCertStore; use deno_tls::Proxy; use deno_tls::RootCertStoreProvider; @@ -149,7 +150,7 @@ pub enum FetchError { #[error(transparent)] Resource(deno_core::error::AnyError), #[error(transparent)] - Permission(deno_core::error::AnyError), + Permission(#[from] PermissionCheckError), #[error("NetworkError when attempting to fetch resource")] NetworkError, #[error("Fetching files only supports the GET method: received {0}")] @@ -346,13 +347,13 @@ pub trait FetchPermissions { &mut self, url: &Url, api_name: &str, - ) -> Result<(), deno_core::error::AnyError>; + ) -> Result<(), PermissionCheckError>; #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"] fn check_read<'a>( &mut self, p: &'a Path, api_name: &str, - ) -> Result, deno_core::error::AnyError>; + ) -> Result, PermissionCheckError>; } impl FetchPermissions for deno_permissions::PermissionsContainer { @@ -361,7 +362,7 @@ impl FetchPermissions for deno_permissions::PermissionsContainer { &mut self, url: &Url, api_name: &str, - ) -> Result<(), deno_core::error::AnyError> { + ) -> Result<(), PermissionCheckError> { deno_permissions::PermissionsContainer::check_net_url(self, url, api_name) } @@ -370,7 +371,7 @@ impl FetchPermissions for deno_permissions::PermissionsContainer { &mut self, path: &'a Path, api_name: &str, - ) -> Result, deno_core::error::AnyError> { + ) -> Result, PermissionCheckError> { deno_permissions::PermissionsContainer::check_read_path( self, path, @@ -414,9 +415,7 @@ where "file" => { let path = url.to_file_path().map_err(|_| FetchError::NetworkError)?; let permissions = state.borrow_mut::(); - let path = permissions - .check_read(&path, "fetch()") - .map_err(FetchError::Permission)?; + let path = permissions.check_read(&path, "fetch()")?; let url = match path { Cow::Owned(path) => Url::from_file_path(path).unwrap(), Cow::Borrowed(_) => url, @@ -442,9 +441,7 @@ where } "http" | "https" => { let permissions = state.borrow_mut::(); - permissions - .check_net_url(&url, "fetch()") - .map_err(FetchError::Resource)?; + permissions.check_net_url(&url, "fetch()")?; let maybe_authority = extract_authority(&mut url); let uri = url @@ -863,9 +860,7 @@ where if let Some(proxy) = args.proxy.clone() { let permissions = state.borrow_mut::(); let url = Url::parse(&proxy.url)?; - permissions - .check_net_url(&url, "Deno.createHttpClient()") - .map_err(FetchError::Permission)?; + permissions.check_net_url(&url, "Deno.createHttpClient()")?; } let options = state.borrow::(); diff --git a/ext/ffi/call.rs b/ext/ffi/call.rs index ef61dc383e..bbff0ee48f 100644 --- a/ext/ffi/call.rs +++ b/ext/ffi/call.rs @@ -32,7 +32,9 @@ pub enum CallError { #[error("Invalid FFI symbol name: '{0}'")] InvalidSymbol(String), #[error(transparent)] - Permission(deno_core::error::AnyError), + Permission(#[from] deno_permissions::PermissionCheckError), + #[error(transparent)] + Resource(deno_core::error::AnyError), #[error(transparent)] Callback(#[from] super::CallbackError), } @@ -301,9 +303,7 @@ where { let mut state = state.borrow_mut(); let permissions = state.borrow_mut::(); - permissions - .check_partial_no_path() - .map_err(CallError::Permission)?; + permissions.check_partial_no_path()?; }; let symbol = PtrSymbol::new(pointer, &def)?; @@ -347,7 +347,7 @@ pub fn op_ffi_call_nonblocking( let resource = state .resource_table .get::(rid) - .map_err(CallError::Permission)?; + .map_err(CallError::Resource)?; let symbols = &resource.symbols; *symbols .get(&symbol) @@ -401,9 +401,7 @@ where { let mut state = state.borrow_mut(); let permissions = state.borrow_mut::(); - permissions - .check_partial_no_path() - .map_err(CallError::Permission)?; + permissions.check_partial_no_path()?; }; let symbol = PtrSymbol::new(pointer, &def)?; diff --git a/ext/ffi/callback.rs b/ext/ffi/callback.rs index f33e0413a3..29583c800c 100644 --- a/ext/ffi/callback.rs +++ b/ext/ffi/callback.rs @@ -38,7 +38,7 @@ pub enum CallbackError { #[error(transparent)] Resource(deno_core::error::AnyError), #[error(transparent)] - Permission(deno_core::error::AnyError), + Permission(#[from] deno_permissions::PermissionCheckError), #[error(transparent)] Other(deno_core::error::AnyError), } @@ -572,9 +572,7 @@ where FP: FfiPermissions + 'static, { let permissions = state.borrow_mut::(); - permissions - .check_partial_no_path() - .map_err(CallbackError::Permission)?; + permissions.check_partial_no_path()?; let thread_id: u32 = LOCAL_THREAD_ID.with(|s| { let value = *s.borrow(); diff --git a/ext/ffi/dlfcn.rs b/ext/ffi/dlfcn.rs index 53bdcbc5cc..55909468f8 100644 --- a/ext/ffi/dlfcn.rs +++ b/ext/ffi/dlfcn.rs @@ -30,7 +30,7 @@ pub enum DlfcnError { #[error(transparent)] Dlopen(#[from] dlopen2::Error), #[error(transparent)] - Permission(deno_core::error::AnyError), + Permission(#[from] deno_permissions::PermissionCheckError), #[error(transparent)] Other(deno_core::error::AnyError), } @@ -133,9 +133,7 @@ where FP: FfiPermissions + 'static, { let permissions = state.borrow_mut::(); - let path = permissions - .check_partial_with_path(&args.path) - .map_err(DlfcnError::Permission)?; + let path = permissions.check_partial_with_path(&args.path)?; let lib = Library::open(&path).map_err(|e| { dlopen2::Error::OpeningLibraryError(std::io::Error::new( diff --git a/ext/ffi/lib.rs b/ext/ffi/lib.rs index 237f8c3b05..73ec7757ab 100644 --- a/ext/ffi/lib.rs +++ b/ext/ffi/lib.rs @@ -1,7 +1,5 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use deno_core::error::AnyError; - use std::mem::size_of; use std::os::raw::c_char; use std::os::raw::c_short; @@ -31,6 +29,7 @@ use symbol::Symbol; pub use call::CallError; pub use callback::CallbackError; +use deno_permissions::PermissionCheckError; pub use dlfcn::DlfcnError; pub use ir::IRError; pub use r#static::StaticError; @@ -48,17 +47,17 @@ const _: () = { pub const UNSTABLE_FEATURE_NAME: &str = "ffi"; pub trait FfiPermissions { - fn check_partial_no_path(&mut self) -> Result<(), AnyError>; + fn check_partial_no_path(&mut self) -> Result<(), PermissionCheckError>; #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"] fn check_partial_with_path( &mut self, path: &str, - ) -> Result; + ) -> Result; } impl FfiPermissions for deno_permissions::PermissionsContainer { #[inline(always)] - fn check_partial_no_path(&mut self) -> Result<(), AnyError> { + fn check_partial_no_path(&mut self) -> Result<(), PermissionCheckError> { deno_permissions::PermissionsContainer::check_ffi_partial_no_path(self) } @@ -66,7 +65,7 @@ impl FfiPermissions for deno_permissions::PermissionsContainer { fn check_partial_with_path( &mut self, path: &str, - ) -> Result { + ) -> Result { deno_permissions::PermissionsContainer::check_ffi_partial_with_path( self, path, ) diff --git a/ext/ffi/repr.rs b/ext/ffi/repr.rs index 2f04f4feb4..fd8a2c8e70 100644 --- a/ext/ffi/repr.rs +++ b/ext/ffi/repr.rs @@ -46,7 +46,7 @@ pub enum ReprError { #[error("Invalid pointer pointer, pointer is null")] InvalidPointer, #[error(transparent)] - Permission(deno_core::error::AnyError), + Permission(#[from] deno_permissions::PermissionCheckError), } #[op2(fast)] @@ -58,9 +58,7 @@ where FP: FfiPermissions + 'static, { let permissions = state.borrow_mut::(); - permissions - .check_partial_no_path() - .map_err(ReprError::Permission)?; + permissions.check_partial_no_path()?; Ok(ptr_number as *mut c_void) } @@ -75,9 +73,7 @@ where FP: FfiPermissions + 'static, { let permissions = state.borrow_mut::(); - permissions - .check_partial_no_path() - .map_err(ReprError::Permission)?; + permissions.check_partial_no_path()?; Ok(a == b) } @@ -91,9 +87,7 @@ where FP: FfiPermissions + 'static, { let permissions = state.borrow_mut::(); - permissions - .check_partial_no_path() - .map_err(ReprError::Permission)?; + permissions.check_partial_no_path()?; Ok(buf as *mut c_void) } @@ -107,9 +101,7 @@ where FP: FfiPermissions + 'static, { let permissions = state.borrow_mut::(); - permissions - .check_partial_no_path() - .map_err(ReprError::Permission)?; + permissions.check_partial_no_path()?; let Some(buf) = buf.get_backing_store() else { return Ok(0 as _); @@ -130,9 +122,7 @@ where FP: FfiPermissions + 'static, { let permissions = state.borrow_mut::(); - permissions - .check_partial_no_path() - .map_err(ReprError::Permission)?; + permissions.check_partial_no_path()?; if ptr.is_null() { return Err(ReprError::InvalidOffset); @@ -162,9 +152,7 @@ where FP: FfiPermissions + 'static, { let permissions = state.borrow_mut::(); - permissions - .check_partial_no_path() - .map_err(ReprError::Permission)?; + permissions.check_partial_no_path()?; Ok(ptr as usize) } @@ -181,9 +169,7 @@ where FP: FfiPermissions + 'static, { let permissions = state.borrow_mut::(); - permissions - .check_partial_no_path() - .map_err(ReprError::Permission)?; + permissions.check_partial_no_path()?; if ptr.is_null() { return Err(ReprError::InvalidArrayBuffer); @@ -215,9 +201,7 @@ where FP: FfiPermissions + 'static, { let permissions = state.borrow_mut::(); - permissions - .check_partial_no_path() - .map_err(ReprError::Permission)?; + permissions.check_partial_no_path()?; if src.is_null() { Err(ReprError::InvalidArrayBuffer) @@ -246,9 +230,7 @@ where FP: FfiPermissions + 'static, { let permissions = state.borrow_mut::(); - permissions - .check_partial_no_path() - .map_err(ReprError::Permission)?; + permissions.check_partial_no_path()?; if ptr.is_null() { return Err(ReprError::InvalidCString); @@ -272,9 +254,7 @@ where FP: FfiPermissions + 'static, { let permissions = state.borrow_mut::(); - permissions - .check_partial_no_path() - .map_err(ReprError::Permission)?; + permissions.check_partial_no_path()?; if ptr.is_null() { return Err(ReprError::InvalidBool); @@ -294,9 +274,7 @@ where FP: FfiPermissions + 'static, { let permissions = state.borrow_mut::(); - permissions - .check_partial_no_path() - .map_err(ReprError::Permission)?; + permissions.check_partial_no_path()?; if ptr.is_null() { return Err(ReprError::InvalidU8); @@ -318,9 +296,7 @@ where FP: FfiPermissions + 'static, { let permissions = state.borrow_mut::(); - permissions - .check_partial_no_path() - .map_err(ReprError::Permission)?; + permissions.check_partial_no_path()?; if ptr.is_null() { return Err(ReprError::InvalidI8); @@ -342,9 +318,7 @@ where FP: FfiPermissions + 'static, { let permissions = state.borrow_mut::(); - permissions - .check_partial_no_path() - .map_err(ReprError::Permission)?; + permissions.check_partial_no_path()?; if ptr.is_null() { return Err(ReprError::InvalidU16); @@ -366,9 +340,7 @@ where FP: FfiPermissions + 'static, { let permissions = state.borrow_mut::(); - permissions - .check_partial_no_path() - .map_err(ReprError::Permission)?; + permissions.check_partial_no_path()?; if ptr.is_null() { return Err(ReprError::InvalidI16); @@ -390,9 +362,7 @@ where FP: FfiPermissions + 'static, { let permissions = state.borrow_mut::(); - permissions - .check_partial_no_path() - .map_err(ReprError::Permission)?; + permissions.check_partial_no_path()?; if ptr.is_null() { return Err(ReprError::InvalidU32); @@ -412,9 +382,7 @@ where FP: FfiPermissions + 'static, { let permissions = state.borrow_mut::(); - permissions - .check_partial_no_path() - .map_err(ReprError::Permission)?; + permissions.check_partial_no_path()?; if ptr.is_null() { return Err(ReprError::InvalidI32); @@ -437,9 +405,7 @@ where FP: FfiPermissions + 'static, { let permissions = state.borrow_mut::(); - permissions - .check_partial_no_path() - .map_err(ReprError::Permission)?; + permissions.check_partial_no_path()?; if ptr.is_null() { return Err(ReprError::InvalidU64); @@ -465,9 +431,7 @@ where FP: FfiPermissions + 'static, { let permissions = state.borrow_mut::(); - permissions - .check_partial_no_path() - .map_err(ReprError::Permission)?; + permissions.check_partial_no_path()?; if ptr.is_null() { return Err(ReprError::InvalidI64); @@ -490,9 +454,7 @@ where FP: FfiPermissions + 'static, { let permissions = state.borrow_mut::(); - permissions - .check_partial_no_path() - .map_err(ReprError::Permission)?; + permissions.check_partial_no_path()?; if ptr.is_null() { return Err(ReprError::InvalidF32); @@ -512,9 +474,7 @@ where FP: FfiPermissions + 'static, { let permissions = state.borrow_mut::(); - permissions - .check_partial_no_path() - .map_err(ReprError::Permission)?; + permissions.check_partial_no_path()?; if ptr.is_null() { return Err(ReprError::InvalidF64); @@ -534,9 +494,7 @@ where FP: FfiPermissions + 'static, { let permissions = state.borrow_mut::(); - permissions - .check_partial_no_path() - .map_err(ReprError::Permission)?; + permissions.check_partial_no_path()?; if ptr.is_null() { return Err(ReprError::InvalidPointer); diff --git a/ext/fs/lib.rs b/ext/fs/lib.rs index cd2baf22a9..dd852e6be8 100644 --- a/ext/fs/lib.rs +++ b/ext/fs/lib.rs @@ -22,8 +22,8 @@ pub use crate::sync::MaybeSync; use crate::ops::*; -use deno_core::error::AnyError; use deno_io::fs::FsError; +use deno_permissions::PermissionCheckError; use std::borrow::Cow; use std::path::Path; use std::path::PathBuf; @@ -42,45 +42,51 @@ pub trait FsPermissions { &mut self, path: &str, api_name: &str, - ) -> Result; + ) -> Result; #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"] fn check_read_path<'a>( &mut self, path: &'a Path, api_name: &str, - ) -> Result, AnyError>; - fn check_read_all(&mut self, api_name: &str) -> Result<(), AnyError>; + ) -> Result, PermissionCheckError>; + fn check_read_all( + &mut self, + api_name: &str, + ) -> Result<(), PermissionCheckError>; fn check_read_blind( &mut self, p: &Path, display: &str, api_name: &str, - ) -> Result<(), AnyError>; + ) -> Result<(), PermissionCheckError>; #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"] fn check_write( &mut self, path: &str, api_name: &str, - ) -> Result; + ) -> Result; #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"] fn check_write_path<'a>( &mut self, path: &'a Path, api_name: &str, - ) -> Result, AnyError>; + ) -> Result, PermissionCheckError>; #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"] fn check_write_partial( &mut self, path: &str, api_name: &str, - ) -> Result; - fn check_write_all(&mut self, api_name: &str) -> Result<(), AnyError>; + ) -> Result; + fn check_write_all( + &mut self, + api_name: &str, + ) -> Result<(), PermissionCheckError>; fn check_write_blind( &mut self, p: &Path, display: &str, api_name: &str, - ) -> Result<(), AnyError>; + ) -> Result<(), PermissionCheckError>; fn check<'a>( &mut self, @@ -140,7 +146,7 @@ impl FsPermissions for deno_permissions::PermissionsContainer { &mut self, path: &str, api_name: &str, - ) -> Result { + ) -> Result { deno_permissions::PermissionsContainer::check_read(self, path, api_name) } @@ -148,7 +154,7 @@ impl FsPermissions for deno_permissions::PermissionsContainer { &mut self, path: &'a Path, api_name: &str, - ) -> Result, AnyError> { + ) -> Result, PermissionCheckError> { deno_permissions::PermissionsContainer::check_read_path( self, path, @@ -160,7 +166,7 @@ impl FsPermissions for deno_permissions::PermissionsContainer { path: &Path, display: &str, api_name: &str, - ) -> Result<(), AnyError> { + ) -> Result<(), PermissionCheckError> { deno_permissions::PermissionsContainer::check_read_blind( self, path, display, api_name, ) @@ -170,7 +176,7 @@ impl FsPermissions for deno_permissions::PermissionsContainer { &mut self, path: &str, api_name: &str, - ) -> Result { + ) -> Result { deno_permissions::PermissionsContainer::check_write(self, path, api_name) } @@ -178,7 +184,7 @@ impl FsPermissions for deno_permissions::PermissionsContainer { &mut self, path: &'a Path, api_name: &str, - ) -> Result, AnyError> { + ) -> Result, PermissionCheckError> { deno_permissions::PermissionsContainer::check_write_path( self, path, api_name, ) @@ -188,7 +194,7 @@ impl FsPermissions for deno_permissions::PermissionsContainer { &mut self, path: &str, api_name: &str, - ) -> Result { + ) -> Result { deno_permissions::PermissionsContainer::check_write_partial( self, path, api_name, ) @@ -199,17 +205,23 @@ impl FsPermissions for deno_permissions::PermissionsContainer { p: &Path, display: &str, api_name: &str, - ) -> Result<(), AnyError> { + ) -> Result<(), PermissionCheckError> { deno_permissions::PermissionsContainer::check_write_blind( self, p, display, api_name, ) } - fn check_read_all(&mut self, api_name: &str) -> Result<(), AnyError> { + fn check_read_all( + &mut self, + api_name: &str, + ) -> Result<(), PermissionCheckError> { deno_permissions::PermissionsContainer::check_read_all(self, api_name) } - fn check_write_all(&mut self, api_name: &str) -> Result<(), AnyError> { + fn check_write_all( + &mut self, + api_name: &str, + ) -> Result<(), PermissionCheckError> { deno_permissions::PermissionsContainer::check_write_all(self, api_name) } } diff --git a/ext/fs/ops.rs b/ext/fs/ops.rs index a3f59da4ea..9b76b49e61 100644 --- a/ext/fs/ops.rs +++ b/ext/fs/ops.rs @@ -10,6 +10,12 @@ use std::path::PathBuf; use std::path::StripPrefixError; use std::rc::Rc; +use crate::interface::AccessCheckFn; +use crate::interface::FileSystemRc; +use crate::interface::FsDirEntry; +use crate::interface::FsFileType; +use crate::FsPermissions; +use crate::OpenOptions; use deno_core::op2; use deno_core::CancelFuture; use deno_core::CancelHandle; @@ -20,18 +26,12 @@ use deno_core::ToJsBuffer; use deno_io::fs::FileResource; use deno_io::fs::FsError; use deno_io::fs::FsStat; +use deno_permissions::PermissionCheckError; use rand::rngs::ThreadRng; use rand::thread_rng; use rand::Rng; use serde::Serialize; -use crate::interface::AccessCheckFn; -use crate::interface::FileSystemRc; -use crate::interface::FsDirEntry; -use crate::interface::FsFileType; -use crate::FsPermissions; -use crate::OpenOptions; - #[derive(Debug, thiserror::Error)] pub enum FsOpsError { #[error("{0}")] @@ -39,7 +39,7 @@ pub enum FsOpsError { #[error("{0}")] OperationError(#[source] OperationError), #[error(transparent)] - Permission(deno_core::error::AnyError), + Permission(#[from] PermissionCheckError), #[error(transparent)] Resource(deno_core::error::AnyError), #[error("File name or path {0:?} is not valid UTF-8")] @@ -150,8 +150,7 @@ where let path = fs.cwd()?; state .borrow_mut::

() - .check_read_blind(&path, "CWD", "Deno.cwd()") - .map_err(FsOpsError::Permission)?; + .check_read_blind(&path, "CWD", "Deno.cwd()")?; let path_str = path_into_string(path.into_os_string())?; Ok(path_str) } @@ -166,8 +165,7 @@ where { let d = state .borrow_mut::

() - .check_read(directory, "Deno.chdir()") - .map_err(FsOpsError::Permission)?; + .check_read(directory, "Deno.chdir()")?; state .borrow::() .chdir(&d) @@ -253,8 +251,7 @@ where let path = state .borrow_mut::

() - .check_write(&path, "Deno.mkdirSync()") - .map_err(FsOpsError::Permission)?; + .check_write(&path, "Deno.mkdirSync()")?; let fs = state.borrow::(); fs.mkdir_sync(&path, recursive, Some(mode)) @@ -277,10 +274,7 @@ where let (fs, path) = { let mut state = state.borrow_mut(); - let path = state - .borrow_mut::

() - .check_write(&path, "Deno.mkdir()") - .map_err(FsOpsError::Permission)?; + let path = state.borrow_mut::

().check_write(&path, "Deno.mkdir()")?; (state.borrow::().clone(), path) }; @@ -302,8 +296,7 @@ where { let path = state .borrow_mut::

() - .check_write(&path, "Deno.chmodSync()") - .map_err(FsOpsError::Permission)?; + .check_write(&path, "Deno.chmodSync()")?; let fs = state.borrow::(); fs.chmod_sync(&path, mode).context_path("chmod", &path)?; Ok(()) @@ -320,10 +313,7 @@ where { let (fs, path) = { let mut state = state.borrow_mut(); - let path = state - .borrow_mut::

() - .check_write(&path, "Deno.chmod()") - .map_err(FsOpsError::Permission)?; + let path = state.borrow_mut::

().check_write(&path, "Deno.chmod()")?; (state.borrow::().clone(), path) }; fs.chmod_async(path.clone(), mode) @@ -344,8 +334,7 @@ where { let path = state .borrow_mut::

() - .check_write(&path, "Deno.chownSync()") - .map_err(FsOpsError::Permission)?; + .check_write(&path, "Deno.chownSync()")?; let fs = state.borrow::(); fs.chown_sync(&path, uid, gid) .context_path("chown", &path)?; @@ -364,10 +353,7 @@ where { let (fs, path) = { let mut state = state.borrow_mut(); - let path = state - .borrow_mut::

() - .check_write(&path, "Deno.chown()") - .map_err(FsOpsError::Permission)?; + let path = state.borrow_mut::

().check_write(&path, "Deno.chown()")?; (state.borrow::().clone(), path) }; fs.chown_async(path.clone(), uid, gid) @@ -387,8 +373,7 @@ where { let path = state .borrow_mut::

() - .check_write(path, "Deno.removeSync()") - .map_err(FsOpsError::Permission)?; + .check_write(path, "Deno.removeSync()")?; let fs = state.borrow::(); fs.remove_sync(&path, recursive) @@ -411,13 +396,11 @@ where let path = if recursive { state .borrow_mut::

() - .check_write(&path, "Deno.remove()") - .map_err(FsOpsError::Permission)? + .check_write(&path, "Deno.remove()")? } else { state .borrow_mut::

() - .check_write_partial(&path, "Deno.remove()") - .map_err(FsOpsError::Permission)? + .check_write_partial(&path, "Deno.remove()")? }; (state.borrow::().clone(), path) @@ -440,12 +423,8 @@ where P: FsPermissions + 'static, { let permissions = state.borrow_mut::

(); - let from = permissions - .check_read(from, "Deno.copyFileSync()") - .map_err(FsOpsError::Permission)?; - let to = permissions - .check_write(to, "Deno.copyFileSync()") - .map_err(FsOpsError::Permission)?; + let from = permissions.check_read(from, "Deno.copyFileSync()")?; + let to = permissions.check_write(to, "Deno.copyFileSync()")?; let fs = state.borrow::(); fs.copy_file_sync(&from, &to) @@ -466,12 +445,8 @@ where let (fs, from, to) = { let mut state = state.borrow_mut(); let permissions = state.borrow_mut::

(); - let from = permissions - .check_read(&from, "Deno.copyFile()") - .map_err(FsOpsError::Permission)?; - let to = permissions - .check_write(&to, "Deno.copyFile()") - .map_err(FsOpsError::Permission)?; + let from = permissions.check_read(&from, "Deno.copyFile()")?; + let to = permissions.check_write(&to, "Deno.copyFile()")?; (state.borrow::().clone(), from, to) }; @@ -493,8 +468,7 @@ where { let path = state .borrow_mut::

() - .check_read(&path, "Deno.statSync()") - .map_err(FsOpsError::Permission)?; + .check_read(&path, "Deno.statSync()")?; let fs = state.borrow::(); let stat = fs.stat_sync(&path).context_path("stat", &path)?; let serializable_stat = SerializableStat::from(stat); @@ -514,9 +488,7 @@ where let (fs, path) = { let mut state = state.borrow_mut(); let permissions = state.borrow_mut::

(); - let path = permissions - .check_read(&path, "Deno.stat()") - .map_err(FsOpsError::Permission)?; + let path = permissions.check_read(&path, "Deno.stat()")?; (state.borrow::().clone(), path) }; let stat = fs @@ -537,8 +509,7 @@ where { let path = state .borrow_mut::

() - .check_read(&path, "Deno.lstatSync()") - .map_err(FsOpsError::Permission)?; + .check_read(&path, "Deno.lstatSync()")?; let fs = state.borrow::(); let stat = fs.lstat_sync(&path).context_path("lstat", &path)?; let serializable_stat = SerializableStat::from(stat); @@ -558,9 +529,7 @@ where let (fs, path) = { let mut state = state.borrow_mut(); let permissions = state.borrow_mut::

(); - let path = permissions - .check_read(&path, "Deno.lstat()") - .map_err(FsOpsError::Permission)?; + let path = permissions.check_read(&path, "Deno.lstat()")?; (state.borrow::().clone(), path) }; let stat = fs @@ -581,13 +550,9 @@ where { let fs = state.borrow::().clone(); let permissions = state.borrow_mut::

(); - let path = permissions - .check_read(&path, "Deno.realPathSync()") - .map_err(FsOpsError::Permission)?; + let path = permissions.check_read(&path, "Deno.realPathSync()")?; if path.is_relative() { - permissions - .check_read_blind(&fs.cwd()?, "CWD", "Deno.realPathSync()") - .map_err(FsOpsError::Permission)?; + permissions.check_read_blind(&fs.cwd()?, "CWD", "Deno.realPathSync()")?; } let resolved_path = @@ -610,13 +575,9 @@ where let mut state = state.borrow_mut(); let fs = state.borrow::().clone(); let permissions = state.borrow_mut::

(); - let path = permissions - .check_read(&path, "Deno.realPath()") - .map_err(FsOpsError::Permission)?; + let path = permissions.check_read(&path, "Deno.realPath()")?; if path.is_relative() { - permissions - .check_read_blind(&fs.cwd()?, "CWD", "Deno.realPath()") - .map_err(FsOpsError::Permission)?; + permissions.check_read_blind(&fs.cwd()?, "CWD", "Deno.realPath()")?; } (fs, path) }; @@ -640,8 +601,7 @@ where { let path = state .borrow_mut::

() - .check_read(&path, "Deno.readDirSync()") - .map_err(FsOpsError::Permission)?; + .check_read(&path, "Deno.readDirSync()")?; let fs = state.borrow::(); let entries = fs.read_dir_sync(&path).context_path("readdir", &path)?; @@ -662,8 +622,7 @@ where let mut state = state.borrow_mut(); let path = state .borrow_mut::

() - .check_read(&path, "Deno.readDir()") - .map_err(FsOpsError::Permission)?; + .check_read(&path, "Deno.readDir()")?; (state.borrow::().clone(), path) }; @@ -685,15 +644,9 @@ where P: FsPermissions + 'static, { let permissions = state.borrow_mut::

(); - let _ = permissions - .check_read(&oldpath, "Deno.renameSync()") - .map_err(FsOpsError::Permission)?; - let oldpath = permissions - .check_write(&oldpath, "Deno.renameSync()") - .map_err(FsOpsError::Permission)?; - let newpath = permissions - .check_write(&newpath, "Deno.renameSync()") - .map_err(FsOpsError::Permission)?; + let _ = permissions.check_read(&oldpath, "Deno.renameSync()")?; + let oldpath = permissions.check_write(&oldpath, "Deno.renameSync()")?; + let newpath = permissions.check_write(&newpath, "Deno.renameSync()")?; let fs = state.borrow::(); fs.rename_sync(&oldpath, &newpath) @@ -714,15 +667,9 @@ where let (fs, oldpath, newpath) = { let mut state = state.borrow_mut(); let permissions = state.borrow_mut::

(); - _ = permissions - .check_read(&oldpath, "Deno.rename()") - .map_err(FsOpsError::Permission)?; - let oldpath = permissions - .check_write(&oldpath, "Deno.rename()") - .map_err(FsOpsError::Permission)?; - let newpath = permissions - .check_write(&newpath, "Deno.rename()") - .map_err(FsOpsError::Permission)?; + _ = permissions.check_read(&oldpath, "Deno.rename()")?; + let oldpath = permissions.check_write(&oldpath, "Deno.rename()")?; + let newpath = permissions.check_write(&newpath, "Deno.rename()")?; (state.borrow::().clone(), oldpath, newpath) }; @@ -743,18 +690,10 @@ where P: FsPermissions + 'static, { let permissions = state.borrow_mut::

(); - _ = permissions - .check_read(oldpath, "Deno.linkSync()") - .map_err(FsOpsError::Permission)?; - let oldpath = permissions - .check_write(oldpath, "Deno.linkSync()") - .map_err(FsOpsError::Permission)?; - _ = permissions - .check_read(newpath, "Deno.linkSync()") - .map_err(FsOpsError::Permission)?; - let newpath = permissions - .check_write(newpath, "Deno.linkSync()") - .map_err(FsOpsError::Permission)?; + _ = permissions.check_read(oldpath, "Deno.linkSync()")?; + let oldpath = permissions.check_write(oldpath, "Deno.linkSync()")?; + _ = permissions.check_read(newpath, "Deno.linkSync()")?; + let newpath = permissions.check_write(newpath, "Deno.linkSync()")?; let fs = state.borrow::(); fs.link_sync(&oldpath, &newpath) @@ -775,18 +714,10 @@ where let (fs, oldpath, newpath) = { let mut state = state.borrow_mut(); let permissions = state.borrow_mut::

(); - _ = permissions - .check_read(&oldpath, "Deno.link()") - .map_err(FsOpsError::Permission)?; - let oldpath = permissions - .check_write(&oldpath, "Deno.link()") - .map_err(FsOpsError::Permission)?; - _ = permissions - .check_read(&newpath, "Deno.link()") - .map_err(FsOpsError::Permission)?; - let newpath = permissions - .check_write(&newpath, "Deno.link()") - .map_err(FsOpsError::Permission)?; + _ = permissions.check_read(&oldpath, "Deno.link()")?; + let oldpath = permissions.check_write(&oldpath, "Deno.link()")?; + _ = permissions.check_read(&newpath, "Deno.link()")?; + let newpath = permissions.check_write(&newpath, "Deno.link()")?; (state.borrow::().clone(), oldpath, newpath) }; @@ -811,12 +742,8 @@ where let newpath = PathBuf::from(newpath); let permissions = state.borrow_mut::

(); - permissions - .check_write_all("Deno.symlinkSync()") - .map_err(FsOpsError::Permission)?; - permissions - .check_read_all("Deno.symlinkSync()") - .map_err(FsOpsError::Permission)?; + permissions.check_write_all("Deno.symlinkSync()")?; + permissions.check_read_all("Deno.symlinkSync()")?; let fs = state.borrow::(); fs.symlink_sync(&oldpath, &newpath, file_type) @@ -841,12 +768,8 @@ where let fs = { let mut state = state.borrow_mut(); let permissions = state.borrow_mut::

(); - permissions - .check_write_all("Deno.symlink()") - .map_err(FsOpsError::Permission)?; - permissions - .check_read_all("Deno.symlink()") - .map_err(FsOpsError::Permission)?; + permissions.check_write_all("Deno.symlink()")?; + permissions.check_read_all("Deno.symlink()")?; state.borrow::().clone() }; @@ -868,8 +791,7 @@ where { let path = state .borrow_mut::

() - .check_read(&path, "Deno.readLink()") - .map_err(FsOpsError::Permission)?; + .check_read(&path, "Deno.readLink()")?; let fs = state.borrow::(); @@ -891,8 +813,7 @@ where let mut state = state.borrow_mut(); let path = state .borrow_mut::

() - .check_read(&path, "Deno.readLink()") - .map_err(FsOpsError::Permission)?; + .check_read(&path, "Deno.readLink()")?; (state.borrow::().clone(), path) }; @@ -915,8 +836,7 @@ where { let path = state .borrow_mut::

() - .check_write(path, "Deno.truncateSync()") - .map_err(FsOpsError::Permission)?; + .check_write(path, "Deno.truncateSync()")?; let fs = state.borrow::(); fs.truncate_sync(&path, len) @@ -938,8 +858,7 @@ where let mut state = state.borrow_mut(); let path = state .borrow_mut::

() - .check_write(&path, "Deno.truncate()") - .map_err(FsOpsError::Permission)?; + .check_write(&path, "Deno.truncate()")?; (state.borrow::().clone(), path) }; @@ -962,10 +881,7 @@ pub fn op_fs_utime_sync

( where P: FsPermissions + 'static, { - let path = state - .borrow_mut::

() - .check_write(path, "Deno.utime()") - .map_err(FsOpsError::Permission)?; + let path = state.borrow_mut::

().check_write(path, "Deno.utime()")?; let fs = state.borrow::(); fs.utime_sync(&path, atime_secs, atime_nanos, mtime_secs, mtime_nanos) @@ -988,10 +904,7 @@ where { let (fs, path) = { let mut state = state.borrow_mut(); - let path = state - .borrow_mut::

() - .check_write(&path, "Deno.utime()") - .map_err(FsOpsError::Permission)?; + let path = state.borrow_mut::

().check_write(&path, "Deno.utime()")?; (state.borrow::().clone(), path) }; @@ -1219,16 +1132,12 @@ where { let fs = state.borrow::().clone(); let dir = match dir { - Some(dir) => state - .borrow_mut::

() - .check_write(dir, api_name) - .map_err(FsOpsError::Permission)?, + Some(dir) => state.borrow_mut::

().check_write(dir, api_name)?, None => { let dir = fs.tmp_dir().context("tmpdir")?; state .borrow_mut::

() - .check_write_blind(&dir, "TMP", api_name) - .map_err(FsOpsError::Permission)?; + .check_write_blind(&dir, "TMP", api_name)?; dir } }; @@ -1246,16 +1155,12 @@ where let mut state = state.borrow_mut(); let fs = state.borrow::().clone(); let dir = match dir { - Some(dir) => state - .borrow_mut::

() - .check_write(dir, api_name) - .map_err(FsOpsError::Permission)?, + Some(dir) => state.borrow_mut::

().check_write(dir, api_name)?, None => { let dir = fs.tmp_dir().context("tmpdir")?; state .borrow_mut::

() - .check_write_blind(&dir, "TMP", api_name) - .map_err(FsOpsError::Permission)?; + .check_write_blind(&dir, "TMP", api_name)?; dir } }; diff --git a/ext/kv/remote.rs b/ext/kv/remote.rs index 922853588a..4930aacfe3 100644 --- a/ext/kv/remote.rs +++ b/ext/kv/remote.rs @@ -15,6 +15,7 @@ use deno_core::futures::Stream; use deno_core::OpState; use deno_fetch::create_http_client; use deno_fetch::CreateHttpClientOptions; +use deno_permissions::PermissionCheckError; use deno_tls::rustls::RootCertStore; use deno_tls::Proxy; use deno_tls::RootCertStoreProvider; @@ -45,17 +46,17 @@ impl HttpOptions { } pub trait RemoteDbHandlerPermissions { - fn check_env(&mut self, var: &str) -> Result<(), AnyError>; + fn check_env(&mut self, var: &str) -> Result<(), PermissionCheckError>; fn check_net_url( &mut self, url: &Url, api_name: &str, - ) -> Result<(), AnyError>; + ) -> Result<(), PermissionCheckError>; } impl RemoteDbHandlerPermissions for deno_permissions::PermissionsContainer { #[inline(always)] - fn check_env(&mut self, var: &str) -> Result<(), AnyError> { + fn check_env(&mut self, var: &str) -> Result<(), PermissionCheckError> { deno_permissions::PermissionsContainer::check_env(self, var) } @@ -64,7 +65,7 @@ impl RemoteDbHandlerPermissions for deno_permissions::PermissionsContainer { &mut self, url: &Url, api_name: &str, - ) -> Result<(), AnyError> { + ) -> Result<(), PermissionCheckError> { deno_permissions::PermissionsContainer::check_net_url(self, url, api_name) } } @@ -103,7 +104,9 @@ impl denokv_remote::RemotePermissions fn check_net_url(&self, url: &Url) -> Result<(), anyhow::Error> { let mut state = self.state.borrow_mut(); let permissions = state.borrow_mut::

(); - permissions.check_net_url(url, "Deno.openKv") + permissions + .check_net_url(url, "Deno.openKv") + .map_err(Into::into) } } diff --git a/ext/kv/sqlite.rs b/ext/kv/sqlite.rs index 0b4a3693c4..9de5209275 100644 --- a/ext/kv/sqlite.rs +++ b/ext/kv/sqlite.rs @@ -13,20 +13,20 @@ use std::sync::Arc; use std::sync::Mutex; use std::sync::OnceLock; +use crate::DatabaseHandler; use async_trait::async_trait; use deno_core::error::type_error; use deno_core::error::AnyError; use deno_core::unsync::spawn_blocking; use deno_core::OpState; use deno_path_util::normalize_path; +use deno_permissions::PermissionCheckError; pub use denokv_sqlite::SqliteBackendError; use denokv_sqlite::SqliteConfig; use denokv_sqlite::SqliteNotifier; use rand::SeedableRng; use rusqlite::OpenFlags; -use crate::DatabaseHandler; - static SQLITE_NOTIFIERS_MAP: OnceLock>> = OnceLock::new(); @@ -42,13 +42,13 @@ pub trait SqliteDbHandlerPermissions { &mut self, p: &str, api_name: &str, - ) -> Result; + ) -> Result; #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"] fn check_write<'a>( &mut self, p: &'a Path, api_name: &str, - ) -> Result, AnyError>; + ) -> Result, PermissionCheckError>; } impl SqliteDbHandlerPermissions for deno_permissions::PermissionsContainer { @@ -57,7 +57,7 @@ impl SqliteDbHandlerPermissions for deno_permissions::PermissionsContainer { &mut self, p: &str, api_name: &str, - ) -> Result { + ) -> Result { deno_permissions::PermissionsContainer::check_read(self, p, api_name) } @@ -66,7 +66,7 @@ impl SqliteDbHandlerPermissions for deno_permissions::PermissionsContainer { &mut self, p: &'a Path, api_name: &str, - ) -> Result, AnyError> { + ) -> Result, PermissionCheckError> { deno_permissions::PermissionsContainer::check_write_path(self, p, api_name) } } diff --git a/ext/napi/lib.rs b/ext/napi/lib.rs index 20f924bdbf..88b8c238df 100644 --- a/ext/napi/lib.rs +++ b/ext/napi/lib.rs @@ -43,7 +43,7 @@ pub enum NApiError { #[error("Unable to find register Node-API module at {}", .0.display())] ModuleNotFound(PathBuf), #[error(transparent)] - Permission(deno_core::error::AnyError), + Permission(#[from] PermissionCheckError), } #[cfg(unix)] @@ -55,6 +55,7 @@ use libloading::os::windows::*; // Expose common stuff for ease of use. // `use deno_napi::*` pub use deno_core::v8; +use deno_permissions::PermissionCheckError; pub use std::ffi::CStr; pub use std::os::raw::c_char; pub use std::os::raw::c_void; @@ -508,20 +509,14 @@ deno_core::extension!(deno_napi, pub trait NapiPermissions { #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"] - fn check( - &mut self, - path: &str, - ) -> Result; + fn check(&mut self, path: &str) -> Result; } // NOTE(bartlomieju): for now, NAPI uses `--allow-ffi` flag, but that might // change in the future. impl NapiPermissions for deno_permissions::PermissionsContainer { #[inline(always)] - fn check( - &mut self, - path: &str, - ) -> Result { + fn check(&mut self, path: &str) -> Result { deno_permissions::PermissionsContainer::check_ffi(self, path) } } @@ -553,7 +548,7 @@ where let (async_work_sender, cleanup_hooks, external_ops_tracker, path) = { let mut op_state = op_state.borrow_mut(); let permissions = op_state.borrow_mut::(); - let path = permissions.check(&path).map_err(NApiError::Permission)?; + let path = permissions.check(&path)?; let napi_state = op_state.borrow::(); ( op_state.borrow::().clone(), diff --git a/ext/net/lib.rs b/ext/net/lib.rs index b039965d4c..bf8f58aa27 100644 --- a/ext/net/lib.rs +++ b/ext/net/lib.rs @@ -11,6 +11,7 @@ mod tcp; use deno_core::error::AnyError; use deno_core::OpState; +use deno_permissions::PermissionCheckError; use deno_tls::rustls::RootCertStore; use deno_tls::RootCertStoreProvider; use std::borrow::Cow; @@ -25,25 +26,25 @@ pub trait NetPermissions { &mut self, host: &(T, Option), api_name: &str, - ) -> Result<(), AnyError>; + ) -> Result<(), PermissionCheckError>; #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"] fn check_read( &mut self, p: &str, api_name: &str, - ) -> Result; + ) -> Result; #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"] fn check_write( &mut self, p: &str, api_name: &str, - ) -> Result; + ) -> Result; #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"] fn check_write_path<'a>( &mut self, p: &'a Path, api_name: &str, - ) -> Result, AnyError>; + ) -> Result, PermissionCheckError>; } impl NetPermissions for deno_permissions::PermissionsContainer { @@ -52,7 +53,7 @@ impl NetPermissions for deno_permissions::PermissionsContainer { &mut self, host: &(T, Option), api_name: &str, - ) -> Result<(), AnyError> { + ) -> Result<(), PermissionCheckError> { deno_permissions::PermissionsContainer::check_net(self, host, api_name) } @@ -61,7 +62,7 @@ impl NetPermissions for deno_permissions::PermissionsContainer { &mut self, path: &str, api_name: &str, - ) -> Result { + ) -> Result { deno_permissions::PermissionsContainer::check_read(self, path, api_name) } @@ -70,7 +71,7 @@ impl NetPermissions for deno_permissions::PermissionsContainer { &mut self, path: &str, api_name: &str, - ) -> Result { + ) -> Result { deno_permissions::PermissionsContainer::check_write(self, path, api_name) } @@ -79,7 +80,7 @@ impl NetPermissions for deno_permissions::PermissionsContainer { &mut self, path: &'a Path, api_name: &str, - ) -> Result, AnyError> { + ) -> Result, PermissionCheckError> { deno_permissions::PermissionsContainer::check_write_path( self, path, api_name, ) diff --git a/ext/net/ops.rs b/ext/net/ops.rs index 0f92dead0c..35bcff8dcf 100644 --- a/ext/net/ops.rs +++ b/ext/net/ops.rs @@ -81,8 +81,8 @@ pub enum NetError { Io(#[from] std::io::Error), #[error("Another accept task is ongoing")] AcceptTaskOngoing, - #[error("{0}")] - Permission(deno_core::error::AnyError), + #[error(transparent)] + Permission(#[from] deno_permissions::PermissionCheckError), #[error("{0}")] Resource(deno_core::error::AnyError), #[error("No resolved address found")] @@ -195,12 +195,10 @@ where { { let mut s = state.borrow_mut(); - s.borrow_mut::() - .check_net( - &(&addr.hostname, Some(addr.port)), - "Deno.DatagramConn.send()", - ) - .map_err(NetError::Permission)?; + s.borrow_mut::().check_net( + &(&addr.hostname, Some(addr.port)), + "Deno.DatagramConn.send()", + )?; } let addr = resolve_addr(&addr.hostname, addr.port) .await? @@ -369,8 +367,7 @@ where let mut state_ = state.borrow_mut(); state_ .borrow_mut::() - .check_net(&(&addr.hostname, Some(addr.port)), "Deno.connect()") - .map_err(NetError::Permission)?; + .check_net(&(&addr.hostname, Some(addr.port)), "Deno.connect()")?; } let addr = resolve_addr(&addr.hostname, addr.port) @@ -420,8 +417,7 @@ where } state .borrow_mut::() - .check_net(&(&addr.hostname, Some(addr.port)), "Deno.listen()") - .map_err(NetError::Permission)?; + .check_net(&(&addr.hostname, Some(addr.port)), "Deno.listen()")?; let addr = resolve_addr_sync(&addr.hostname, addr.port)? .next() .ok_or_else(|| NetError::NoResolvedAddress)?; @@ -449,8 +445,7 @@ where { state .borrow_mut::() - .check_net(&(&addr.hostname, Some(addr.port)), "Deno.listenDatagram()") - .map_err(NetError::Permission)?; + .check_net(&(&addr.hostname, Some(addr.port)), "Deno.listenDatagram()")?; let addr = resolve_addr_sync(&addr.hostname, addr.port)? .next() .ok_or_else(|| NetError::NoResolvedAddress)?; @@ -647,9 +642,7 @@ where let socker_addr = &ns.socket_addr; let ip = socker_addr.ip().to_string(); let port = socker_addr.port(); - perm - .check_net(&(ip, Some(port)), "Deno.resolveDns()") - .map_err(NetError::Permission)?; + perm.check_net(&(ip, Some(port)), "Deno.resolveDns()")?; } } @@ -834,6 +827,7 @@ mod tests { use deno_core::futures::FutureExt; use deno_core::JsRuntime; use deno_core::RuntimeOptions; + use deno_permissions::PermissionCheckError; use socket2::SockRef; use std::net::Ipv4Addr; use std::net::Ipv6Addr; @@ -1041,7 +1035,7 @@ mod tests { &mut self, _host: &(T, Option), _api_name: &str, - ) -> Result<(), deno_core::error::AnyError> { + ) -> Result<(), PermissionCheckError> { Ok(()) } @@ -1049,7 +1043,7 @@ mod tests { &mut self, p: &str, _api_name: &str, - ) -> Result { + ) -> Result { Ok(PathBuf::from(p)) } @@ -1057,7 +1051,7 @@ mod tests { &mut self, p: &str, _api_name: &str, - ) -> Result { + ) -> Result { Ok(PathBuf::from(p)) } @@ -1065,7 +1059,7 @@ mod tests { &mut self, p: &'a Path, _api_name: &str, - ) -> Result, deno_core::error::AnyError> { + ) -> Result, PermissionCheckError> { Ok(Cow::Borrowed(p)) } } diff --git a/ext/node/lib.rs b/ext/node/lib.rs index db6d08e11e..b08b0493b0 100644 --- a/ext/node/lib.rs +++ b/ext/node/lib.rs @@ -24,6 +24,7 @@ pub mod ops; mod polyfill; pub use deno_package_json::PackageJson; +use deno_permissions::PermissionCheckError; pub use node_resolver::PathClean; pub use ops::ipc::ChildPipeFd; pub use ops::ipc::IpcJsonStreamResource; @@ -45,10 +46,13 @@ pub trait NodePermissions { &mut self, url: &Url, api_name: &str, - ) -> Result<(), AnyError>; + ) -> Result<(), PermissionCheckError>; #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"] #[inline(always)] - fn check_read(&mut self, path: &str) -> Result { + fn check_read( + &mut self, + path: &str, + ) -> Result { self.check_read_with_api_name(path, None) } #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"] @@ -56,20 +60,24 @@ pub trait NodePermissions { &mut self, path: &str, api_name: Option<&str>, - ) -> Result; + ) -> Result; #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"] fn check_read_path<'a>( &mut self, path: &'a Path, - ) -> Result, AnyError>; + ) -> Result, PermissionCheckError>; fn query_read_all(&mut self) -> bool; - fn check_sys(&mut self, kind: &str, api_name: &str) -> Result<(), AnyError>; + fn check_sys( + &mut self, + kind: &str, + api_name: &str, + ) -> Result<(), PermissionCheckError>; #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"] fn check_write_with_api_name( &mut self, path: &str, api_name: Option<&str>, - ) -> Result; + ) -> Result; } impl NodePermissions for deno_permissions::PermissionsContainer { @@ -78,7 +86,7 @@ impl NodePermissions for deno_permissions::PermissionsContainer { &mut self, url: &Url, api_name: &str, - ) -> Result<(), AnyError> { + ) -> Result<(), PermissionCheckError> { deno_permissions::PermissionsContainer::check_net_url(self, url, api_name) } @@ -87,7 +95,7 @@ impl NodePermissions for deno_permissions::PermissionsContainer { &mut self, path: &str, api_name: Option<&str>, - ) -> Result { + ) -> Result { deno_permissions::PermissionsContainer::check_read_with_api_name( self, path, api_name, ) @@ -96,7 +104,7 @@ impl NodePermissions for deno_permissions::PermissionsContainer { fn check_read_path<'a>( &mut self, path: &'a Path, - ) -> Result, AnyError> { + ) -> Result, PermissionCheckError> { deno_permissions::PermissionsContainer::check_read_path(self, path, None) } @@ -109,13 +117,17 @@ impl NodePermissions for deno_permissions::PermissionsContainer { &mut self, path: &str, api_name: Option<&str>, - ) -> Result { + ) -> Result { deno_permissions::PermissionsContainer::check_write_with_api_name( self, path, api_name, ) } - fn check_sys(&mut self, kind: &str, api_name: &str) -> Result<(), AnyError> { + fn check_sys( + &mut self, + kind: &str, + api_name: &str, + ) -> Result<(), PermissionCheckError> { deno_permissions::PermissionsContainer::check_sys(self, kind, api_name) } } diff --git a/ext/node/ops/fs.rs b/ext/node/ops/fs.rs index 98b3c46a14..9c0e4e1ccf 100644 --- a/ext/node/ops/fs.rs +++ b/ext/node/ops/fs.rs @@ -13,7 +13,7 @@ use crate::NodePermissions; #[derive(Debug, thiserror::Error)] pub enum FsError { #[error(transparent)] - Permission(deno_core::error::AnyError), + Permission(#[from] deno_permissions::PermissionCheckError), #[error("{0}")] Io(#[from] std::io::Error), #[cfg(windows)] @@ -53,8 +53,7 @@ where let mut state = state.borrow_mut(); let path = state .borrow_mut::

() - .check_read_with_api_name(&path, Some("node:fs.exists()")) - .map_err(FsError::Permission)?; + .check_read_with_api_name(&path, Some("node:fs.exists()"))?; (state.borrow::().clone(), path) }; @@ -72,12 +71,10 @@ where { let path = state .borrow_mut::

() - .check_read_with_api_name(path, Some("node:fs.cpSync")) - .map_err(FsError::Permission)?; + .check_read_with_api_name(path, Some("node:fs.cpSync"))?; let new_path = state .borrow_mut::

() - .check_write_with_api_name(new_path, Some("node:fs.cpSync")) - .map_err(FsError::Permission)?; + .check_write_with_api_name(new_path, Some("node:fs.cpSync"))?; let fs = state.borrow::(); fs.cp_sync(&path, &new_path)?; @@ -97,12 +94,10 @@ where let mut state = state.borrow_mut(); let path = state .borrow_mut::

() - .check_read_with_api_name(&path, Some("node:fs.cpSync")) - .map_err(FsError::Permission)?; + .check_read_with_api_name(&path, Some("node:fs.cpSync"))?; let new_path = state .borrow_mut::

() - .check_write_with_api_name(&new_path, Some("node:fs.cpSync")) - .map_err(FsError::Permission)?; + .check_write_with_api_name(&new_path, Some("node:fs.cpSync"))?; (state.borrow::().clone(), path, new_path) }; @@ -136,12 +131,10 @@ where let mut state = state.borrow_mut(); let path = state .borrow_mut::

() - .check_read_with_api_name(&path, Some("node:fs.statfs")) - .map_err(FsError::Permission)?; + .check_read_with_api_name(&path, Some("node:fs.statfs"))?; state .borrow_mut::

() - .check_sys("statfs", "node:fs.statfs") - .map_err(FsError::Permission)?; + .check_sys("statfs", "node:fs.statfs")?; path }; #[cfg(unix)] @@ -279,8 +272,7 @@ where { let path = state .borrow_mut::

() - .check_write_with_api_name(path, Some("node:fs.lutimes")) - .map_err(FsError::Permission)?; + .check_write_with_api_name(path, Some("node:fs.lutimes"))?; let fs = state.borrow::(); fs.lutime_sync(&path, atime_secs, atime_nanos, mtime_secs, mtime_nanos)?; @@ -303,8 +295,7 @@ where let mut state = state.borrow_mut(); let path = state .borrow_mut::

() - .check_write_with_api_name(&path, Some("node:fs.lutimesSync")) - .map_err(FsError::Permission)?; + .check_write_with_api_name(&path, Some("node:fs.lutimesSync"))?; (state.borrow::().clone(), path) }; @@ -326,8 +317,7 @@ where { let path = state .borrow_mut::

() - .check_write_with_api_name(&path, Some("node:fs.lchownSync")) - .map_err(FsError::Permission)?; + .check_write_with_api_name(&path, Some("node:fs.lchownSync"))?; let fs = state.borrow::(); fs.lchown_sync(&path, uid, gid)?; Ok(()) @@ -347,8 +337,7 @@ where let mut state = state.borrow_mut(); let path = state .borrow_mut::

() - .check_write_with_api_name(&path, Some("node:fs.lchown")) - .map_err(FsError::Permission)?; + .check_write_with_api_name(&path, Some("node:fs.lchown"))?; (state.borrow::().clone(), path) }; fs.lchown_async(path, uid, gid).await?; diff --git a/ext/node/ops/http.rs b/ext/node/ops/http.rs index 730e1e482b..69571078fe 100644 --- a/ext/node/ops/http.rs +++ b/ext/node/ops/http.rs @@ -78,9 +78,7 @@ where { let permissions = state.borrow_mut::

(); - permissions - .check_net_url(&url, "ClientRequest") - .map_err(FetchError::Permission)?; + permissions.check_net_url(&url, "ClientRequest")?; } let mut header_map = HeaderMap::new(); diff --git a/ext/node/ops/os/mod.rs b/ext/node/ops/os/mod.rs index ea7e6b99fd..d291277ad4 100644 --- a/ext/node/ops/os/mod.rs +++ b/ext/node/ops/os/mod.rs @@ -14,7 +14,7 @@ pub enum OsError { #[error(transparent)] Priority(priority::PriorityError), #[error(transparent)] - Permission(deno_core::error::AnyError), + Permission(#[from] deno_permissions::PermissionCheckError), #[error("Failed to get cpu info")] FailedToGetCpuInfo, #[error("Failed to get user info")] @@ -31,9 +31,7 @@ where { { let permissions = state.borrow_mut::

(); - permissions - .check_sys("getPriority", "node:os.getPriority()") - .map_err(OsError::Permission)?; + permissions.check_sys("getPriority", "node:os.getPriority()")?; } priority::get_priority(pid).map_err(OsError::Priority) @@ -50,9 +48,7 @@ where { { let permissions = state.borrow_mut::

(); - permissions - .check_sys("setPriority", "node:os.setPriority()") - .map_err(OsError::Permission)?; + permissions.check_sys("setPriority", "node:os.setPriority()")?; } priority::set_priority(pid, priority).map_err(OsError::Priority) @@ -266,9 +262,7 @@ where { { let permissions = state.borrow_mut::

(); - permissions - .check_sys("cpus", "node:os.cpus()") - .map_err(OsError::Permission)?; + permissions.check_sys("cpus", "node:os.cpus()")?; } cpus::cpu_info().ok_or(OsError::FailedToGetCpuInfo) diff --git a/ext/node/polyfills/01_require.js b/ext/node/polyfills/01_require.js index 296d819aa5..d818bb5727 100644 --- a/ext/node/polyfills/01_require.js +++ b/ext/node/polyfills/01_require.js @@ -1312,6 +1312,8 @@ export function findSourceMap(_path) { return undefined; } +Module.findSourceMap = findSourceMap; + /** * @param {string | URL} _specifier * @param {string | URL} _parentUrl diff --git a/ext/node/polyfills/_fs/_fs_common.ts b/ext/node/polyfills/_fs/_fs_common.ts index ac0bf5a551..a29548bb36 100644 --- a/ext/node/polyfills/_fs/_fs_common.ts +++ b/ext/node/polyfills/_fs/_fs_common.ts @@ -20,6 +20,7 @@ import { notImplemented, TextEncodings, } from "ext:deno_node/_utils.ts"; +import { type Buffer } from "node:buffer"; export type CallbackWithError = (err: ErrnoException | null) => void; diff --git a/ext/node/polyfills/_fs/_fs_open.ts b/ext/node/polyfills/_fs/_fs_open.ts index 8bd989790b..31ca4bb619 100644 --- a/ext/node/polyfills/_fs/_fs_open.ts +++ b/ext/node/polyfills/_fs/_fs_open.ts @@ -147,8 +147,8 @@ export function open( export function openPromise( path: string | Buffer | URL, - flags?: openFlags = "r", - mode? = 0o666, + flags: openFlags = "r", + mode = 0o666, ): Promise { return new Promise((resolve, reject) => { open(path, flags, mode, (err, fd) => { diff --git a/ext/node/polyfills/_fs/_fs_readv.ts b/ext/node/polyfills/_fs/_fs_readv.ts index 384f5e319a..2259f029ae 100644 --- a/ext/node/polyfills/_fs/_fs_readv.ts +++ b/ext/node/polyfills/_fs/_fs_readv.ts @@ -15,6 +15,7 @@ import { maybeCallback } from "ext:deno_node/_fs/_fs_common.ts"; import { validateInteger } from "ext:deno_node/internal/validators.mjs"; import * as io from "ext:deno_io/12_io.js"; import { op_fs_seek_async, op_fs_seek_sync } from "ext:core/ops"; +import process from "node:process"; type Callback = ( err: ErrnoException | null, diff --git a/ext/node/polyfills/internal/crypto/keygen.ts b/ext/node/polyfills/internal/crypto/keygen.ts index a40c76c0d7..44bfd83277 100644 --- a/ext/node/polyfills/internal/crypto/keygen.ts +++ b/ext/node/polyfills/internal/crypto/keygen.ts @@ -29,6 +29,7 @@ import { } from "ext:deno_node/internal/validators.mjs"; import { Buffer } from "node:buffer"; import { KeyFormat, KeyType } from "ext:deno_node/internal/crypto/types.ts"; +import process from "node:process"; import { op_node_generate_dh_group_key, diff --git a/ext/node/polyfills/internal/crypto/random.ts b/ext/node/polyfills/internal/crypto/random.ts index 4219414dc1..a41b868190 100644 --- a/ext/node/polyfills/internal/crypto/random.ts +++ b/ext/node/polyfills/internal/crypto/random.ts @@ -38,6 +38,7 @@ import { ERR_INVALID_ARG_TYPE, ERR_OUT_OF_RANGE, } from "ext:deno_node/internal/errors.ts"; +import { Buffer } from "node:buffer"; export { default as randomBytes } from "ext:deno_node/internal/crypto/_randomBytes.ts"; export { diff --git a/ext/node/polyfills/internal_binding/http_parser.ts b/ext/node/polyfills/internal_binding/http_parser.ts index ca4f896e20..bad10d9851 100644 --- a/ext/node/polyfills/internal_binding/http_parser.ts +++ b/ext/node/polyfills/internal_binding/http_parser.ts @@ -126,6 +126,7 @@ ObjectSetPrototypeOf(HTTPParser.prototype, AsyncWrap.prototype); function defineProps(obj: object, props: Record) { for (const entry of new SafeArrayIterator(ObjectEntries(props))) { ObjectDefineProperty(obj, entry[0], { + __proto__: null, value: entry[1], enumerable: true, writable: true, diff --git a/ext/node/polyfills/vm.js b/ext/node/polyfills/vm.js index 183ddad2f4..b64c847c58 100644 --- a/ext/node/polyfills/vm.js +++ b/ext/node/polyfills/vm.js @@ -182,6 +182,7 @@ function getContextOptions(options) { let defaultContextNameIndex = 1; export function createContext( + // deno-lint-ignore prefer-primordials contextObject = {}, options = { __proto__: null }, ) { diff --git a/ext/websocket/lib.rs b/ext/websocket/lib.rs index 2a67ac5a17..a5734271cf 100644 --- a/ext/websocket/lib.rs +++ b/ext/websocket/lib.rs @@ -50,6 +50,7 @@ use tokio::io::ReadHalf; use tokio::io::WriteHalf; use tokio::net::TcpStream; +use deno_permissions::PermissionCheckError; use fastwebsockets::CloseCode; use fastwebsockets::FragmentCollectorRead; use fastwebsockets::Frame; @@ -75,7 +76,7 @@ pub enum WebsocketError { #[error(transparent)] Url(url::ParseError), #[error(transparent)] - Permission(deno_core::error::AnyError), + Permission(#[from] PermissionCheckError), #[error(transparent)] Resource(deno_core::error::AnyError), #[error(transparent)] @@ -112,7 +113,7 @@ pub trait WebSocketPermissions { &mut self, _url: &url::Url, _api_name: &str, - ) -> Result<(), deno_core::error::AnyError>; + ) -> Result<(), PermissionCheckError>; } impl WebSocketPermissions for deno_permissions::PermissionsContainer { @@ -121,7 +122,7 @@ impl WebSocketPermissions for deno_permissions::PermissionsContainer { &mut self, url: &url::Url, api_name: &str, - ) -> Result<(), deno_core::error::AnyError> { + ) -> Result<(), PermissionCheckError> { deno_permissions::PermissionsContainer::check_net_url(self, url, api_name) } } @@ -158,13 +159,10 @@ pub fn op_ws_check_permission_and_cancel_handle( where WP: WebSocketPermissions + 'static, { - state - .borrow_mut::() - .check_net_url( - &url::Url::parse(&url).map_err(WebsocketError::Url)?, - &api_name, - ) - .map_err(WebsocketError::Permission)?; + state.borrow_mut::().check_net_url( + &url::Url::parse(&url).map_err(WebsocketError::Url)?, + &api_name, + )?; if cancel_handle { let rid = state diff --git a/runtime/errors.rs b/runtime/errors.rs index ada26ec35f..5051150f27 100644 --- a/runtime/errors.rs +++ b/runtime/errors.rs @@ -12,6 +12,8 @@ use crate::ops::fs_events::FsEventsError; use crate::ops::http::HttpStartError; use crate::ops::os::OsError; +use crate::ops::permissions::PermissionError; +use crate::ops::process::CheckRunPermissionError; use crate::ops::process::ProcessError; use crate::ops::signal::SignalError; use crate::ops::tty::TtyError; @@ -48,6 +50,12 @@ use deno_kv::KvError; use deno_kv::KvMutationError; use deno_napi::NApiError; use deno_net::ops::NetError; +use deno_permissions::ChildPermissionError; +use deno_permissions::NetDescriptorFromUrlParseError; +use deno_permissions::PathResolveError; +use deno_permissions::PermissionCheckError; +use deno_permissions::RunDescriptorParseError; +use deno_permissions::SysDescriptorParseError; use deno_tls::TlsError; use deno_web::BlobError; use deno_web::CompressionError; @@ -63,6 +71,54 @@ use std::error::Error; use std::io; use std::sync::Arc; +fn get_run_descriptor_parse_error(e: &RunDescriptorParseError) -> &'static str { + match e { + RunDescriptorParseError::Which(_) => "Error", + RunDescriptorParseError::PathResolve(e) => get_path_resolve_error(e), + RunDescriptorParseError::EmptyRunQuery => "Error", + } +} + +fn get_sys_descriptor_parse_error(e: &SysDescriptorParseError) -> &'static str { + match e { + SysDescriptorParseError::InvalidKind(_) => "TypeError", + SysDescriptorParseError::Empty => "Error", + } +} + +fn get_path_resolve_error(e: &PathResolveError) -> &'static str { + match e { + PathResolveError::CwdResolve(e) => get_io_error_class(e), + PathResolveError::EmptyPath => "Error", + } +} + +fn get_permission_error_class(e: &PermissionError) -> &'static str { + match e { + PermissionError::InvalidPermissionName(_) => "ReferenceError", + PermissionError::PathResolve(e) => get_path_resolve_error(e), + PermissionError::NetDescriptorParse(_) => "URIError", + PermissionError::SysDescriptorParse(e) => get_sys_descriptor_parse_error(e), + PermissionError::RunDescriptorParse(e) => get_run_descriptor_parse_error(e), + } +} + +fn get_permission_check_error_class(e: &PermissionCheckError) -> &'static str { + match e { + PermissionCheckError::PermissionDenied(_) => "NotCapable", + PermissionCheckError::InvalidFilePath(_) => "URIError", + PermissionCheckError::NetDescriptorForUrlParse(e) => match e { + NetDescriptorFromUrlParseError::MissingHost(_) => "TypeError", + NetDescriptorFromUrlParseError::Host(_) => "URIError", + }, + PermissionCheckError::SysDescriptorParse(e) => { + get_sys_descriptor_parse_error(e) + } + PermissionCheckError::PathResolve(e) => get_path_resolve_error(e), + PermissionCheckError::HostParse(_) => "URIError", + } +} + fn get_dlopen_error_class(error: &dlopen2::Error) -> &'static str { use dlopen2::Error::*; match error { @@ -445,7 +501,7 @@ fn get_napi_error_class(e: &NApiError) -> &'static str { NApiError::InvalidPath | NApiError::LibLoading(_) | NApiError::ModuleNotFound(_) => "TypeError", - NApiError::Permission(e) => get_error_class_name(e).unwrap_or("Error"), + NApiError::Permission(e) => get_permission_check_error_class(e), } } @@ -523,7 +579,7 @@ fn get_ffi_repr_error_class(e: &ReprError) -> &'static str { ReprError::InvalidF32 => "TypeError", ReprError::InvalidF64 => "TypeError", ReprError::InvalidPointer => "TypeError", - ReprError::Permission(e) => get_error_class_name(e).unwrap_or("Error"), + ReprError::Permission(e) => get_permission_check_error_class(e), } } @@ -531,7 +587,7 @@ fn get_ffi_dlfcn_error_class(e: &DlfcnError) -> &'static str { match e { DlfcnError::RegisterSymbol { .. } => "Error", DlfcnError::Dlopen(_) => "Error", - DlfcnError::Permission(e) => get_error_class_name(e).unwrap_or("Error"), + DlfcnError::Permission(e) => get_permission_check_error_class(e), DlfcnError::Other(e) => get_error_class_name(e).unwrap_or("Error"), } } @@ -549,7 +605,7 @@ fn get_ffi_callback_error_class(e: &CallbackError) -> &'static str { match e { CallbackError::Resource(e) => get_error_class_name(e).unwrap_or("Error"), CallbackError::Other(e) => get_error_class_name(e).unwrap_or("Error"), - CallbackError::Permission(e) => get_error_class_name(e).unwrap_or("Error"), + CallbackError::Permission(e) => get_permission_check_error_class(e), } } @@ -558,8 +614,9 @@ fn get_ffi_call_error_class(e: &CallError) -> &'static str { CallError::IR(_) => "TypeError", CallError::NonblockingCallFailure(_) => "Error", CallError::InvalidSymbol(_) => "TypeError", - CallError::Permission(e) => get_error_class_name(e).unwrap_or("Error"), + CallError::Permission(e) => get_permission_check_error_class(e), CallError::Callback(e) => get_ffi_callback_error_class(e), + CallError::Resource(e) => get_error_class_name(e).unwrap_or("Error"), } } @@ -633,9 +690,8 @@ fn get_broadcast_channel_error(error: &BroadcastChannelError) -> &'static str { fn get_fetch_error(error: &FetchError) -> &'static str { match error { - FetchError::Resource(e) | FetchError::Permission(e) => { - get_error_class_name(e).unwrap_or("Error") - } + FetchError::Resource(e) => get_error_class_name(e).unwrap_or("Error"), + FetchError::Permission(e) => get_permission_check_error_class(e), FetchError::NetworkError => "TypeError", FetchError::FsNotGet(_) => "TypeError", FetchError::InvalidUrl(_) => "TypeError", @@ -669,9 +725,8 @@ fn get_http_client_create_error(error: &HttpClientCreateError) -> &'static str { fn get_websocket_error(error: &WebsocketError) -> &'static str { match error { - WebsocketError::Permission(e) | WebsocketError::Resource(e) => { - get_error_class_name(e).unwrap_or("Error") - } + WebsocketError::Resource(e) => get_error_class_name(e).unwrap_or("Error"), + WebsocketError::Permission(e) => get_permission_check_error_class(e), WebsocketError::Url(e) => get_url_parse_error_class(e), WebsocketError::Io(e) => get_io_error_class(e), WebsocketError::WebSocket(_) => "TypeError", @@ -708,9 +763,10 @@ fn get_fs_ops_error(error: &FsOpsError) -> &'static str { match error { FsOpsError::Io(e) => get_io_error_class(e), FsOpsError::OperationError(e) => get_fs_error(&e.err), - FsOpsError::Permission(e) - | FsOpsError::Resource(e) - | FsOpsError::Other(e) => get_error_class_name(e).unwrap_or("Error"), + FsOpsError::Permission(e) => get_permission_check_error_class(e), + FsOpsError::Resource(e) | FsOpsError::Other(e) => { + get_error_class_name(e).unwrap_or("Error") + } FsOpsError::InvalidUtf8(_) => "InvalidData", FsOpsError::StripPrefix(_) => "Error", FsOpsError::Canceled(e) => { @@ -777,9 +833,10 @@ fn get_net_error(error: &NetError) -> &'static str { NetError::SocketBusy => "Busy", NetError::Io(e) => get_io_error_class(e), NetError::AcceptTaskOngoing => "Busy", - NetError::RootCertStore(e) - | NetError::Permission(e) - | NetError::Resource(e) => get_error_class_name(e).unwrap_or("Error"), + NetError::RootCertStore(e) | NetError::Resource(e) => { + get_error_class_name(e).unwrap_or("Error") + } + NetError::Permission(e) => get_permission_check_error_class(e), NetError::NoResolvedAddress => "Error", NetError::AddrParse(_) => "Error", NetError::Map(e) => get_net_map_error(e), @@ -810,12 +867,25 @@ fn get_net_map_error(error: &deno_net::io::MapError) -> &'static str { } } +fn get_child_permission_error(e: &ChildPermissionError) -> &'static str { + match e { + ChildPermissionError::Escalation => "NotCapable", + ChildPermissionError::PathResolve(e) => get_path_resolve_error(e), + ChildPermissionError::NetDescriptorParse(_) => "URIError", + ChildPermissionError::EnvDescriptorParse(_) => "Error", + ChildPermissionError::SysDescriptorParse(e) => { + get_sys_descriptor_parse_error(e) + } + ChildPermissionError::RunDescriptorParse(e) => { + get_run_descriptor_parse_error(e) + } + } +} + fn get_create_worker_error(error: &CreateWorkerError) -> &'static str { match error { CreateWorkerError::ClassicWorkers => "DOMExceptionNotSupportedError", - CreateWorkerError::Permission(e) => { - get_error_class_name(e).unwrap_or("Error") - } + CreateWorkerError::Permission(e) => get_child_permission_error(e), CreateWorkerError::ModuleResolution(e) => { get_module_resolution_error_class(e) } @@ -862,9 +932,8 @@ fn get_signal_error(error: &SignalError) -> &'static str { fn get_fs_events_error(error: &FsEventsError) -> &'static str { match error { - FsEventsError::Resource(e) | FsEventsError::Permission(e) => { - get_error_class_name(e).unwrap_or("Error") - } + FsEventsError::Resource(e) => get_error_class_name(e).unwrap_or("Error"), + FsEventsError::Permission(e) => get_permission_check_error_class(e), FsEventsError::Notify(e) => get_notify_error_class(e), FsEventsError::Canceled(e) => { let io_err: io::Error = e.to_owned().into(); @@ -892,9 +961,8 @@ fn get_process_error(error: &ProcessError) -> &'static str { ProcessError::FailedResolvingCwd(e) | ProcessError::Io(e) => { get_io_error_class(e) } - ProcessError::Permission(e) | ProcessError::Resource(e) => { - get_error_class_name(e).unwrap_or("Error") - } + ProcessError::Permission(e) => get_permission_check_error_class(e), + ProcessError::Resource(e) => get_error_class_name(e).unwrap_or("Error"), ProcessError::BorrowMut(_) => "Error", ProcessError::Which(_) => "Error", ProcessError::ChildProcessAlreadyTerminated => "TypeError", @@ -903,6 +971,14 @@ fn get_process_error(error: &ProcessError) -> &'static str { ProcessError::InvalidPid => "TypeError", #[cfg(unix)] ProcessError::Nix(e) => get_nix_error_class(e), + ProcessError::RunPermission(e) => match e { + CheckRunPermissionError::Permission(e) => { + get_permission_check_error_class(e) + } + CheckRunPermissionError::Other(e) => { + get_error_class_name(e).unwrap_or("Error") + } + }, } } @@ -971,6 +1047,7 @@ fn get_fs_error(e: &FsError) -> &'static str { mod node { use super::get_error_class_name; use super::get_io_error_class; + use super::get_permission_check_error_class; use super::get_serde_json_error_class; use super::get_url_parse_error_class; pub use deno_node::ops::blocklist::BlocklistError; @@ -998,7 +1075,7 @@ mod node { pub fn get_fs_error(error: &FsError) -> &'static str { match error { - FsError::Permission(e) => get_error_class_name(e).unwrap_or("Error"), + FsError::Permission(e) => get_permission_check_error_class(e), FsError::Io(e) => get_io_error_class(e), #[cfg(windows)] FsError::PathHasNoRoot => "Error", @@ -1084,7 +1161,7 @@ mod node { #[cfg(windows)] PriorityError::InvalidPriority => "TypeError", }, - OsError::Permission(e) => get_error_class_name(e).unwrap_or("Error"), + OsError::Permission(e) => get_permission_check_error_class(e), OsError::FailedToGetCpuInfo => "TypeError", OsError::FailedToGetUserInfo(e) => get_io_error_class(e), } @@ -1116,7 +1193,7 @@ mod node { fn get_os_error(error: &OsError) -> &'static str { match error { - OsError::Permission(e) => get_error_class_name(e).unwrap_or("Error"), + OsError::Permission(e) => get_permission_check_error_class(e), OsError::InvalidUtf8(_) => "InvalidData", OsError::EnvEmptyKey => "TypeError", OsError::EnvInvalidKey(_) => "TypeError", @@ -1144,6 +1221,18 @@ fn get_sync_fetch_error(error: &SyncFetchError) -> &'static str { pub fn get_error_class_name(e: &AnyError) -> Option<&'static str> { deno_core::error::get_custom_error_class(e) + .or_else(|| { + e.downcast_ref::() + .map(get_child_permission_error) + }) + .or_else(|| { + e.downcast_ref::() + .map(get_permission_check_error_class) + }) + .or_else(|| { + e.downcast_ref::() + .map(get_permission_error_class) + }) .or_else(|| e.downcast_ref::().map(get_fs_error)) .or_else(|| { e.downcast_ref::() diff --git a/runtime/js/99_main.js b/runtime/js/99_main.js index 56a5b411bb..4d391dcc3a 100644 --- a/runtime/js/99_main.js +++ b/runtime/js/99_main.js @@ -696,6 +696,7 @@ function bootstrapMainRuntime(runtimeOptions, warmup = false) { // are lost. let jupyterNs = undefined; ObjectDefineProperty(finalDenoNs, "jupyter", { + __proto__: null, get() { if (jupyterNs) { return jupyterNs; diff --git a/runtime/ops/fs_events.rs b/runtime/ops/fs_events.rs index 648553376a..c8e0228bc0 100644 --- a/runtime/ops/fs_events.rs +++ b/runtime/ops/fs_events.rs @@ -114,7 +114,7 @@ pub enum FsEventsError { #[error(transparent)] Resource(deno_core::error::AnyError), #[error(transparent)] - Permission(deno_core::error::AnyError), + Permission(#[from] deno_permissions::PermissionCheckError), #[error(transparent)] Notify(#[from] NotifyError), #[error(transparent)] @@ -181,8 +181,7 @@ fn op_fs_events_open( for path in &paths { let path = state .borrow_mut::() - .check_read(path, "Deno.watchFs()") - .map_err(FsEventsError::Permission)?; + .check_read(path, "Deno.watchFs()")?; let watcher = state.borrow_mut::(); watcher.watcher.watch(&path, recursive_mode)?; diff --git a/runtime/ops/os/mod.rs b/runtime/ops/os/mod.rs index 24b0389e16..9bee9d8234 100644 --- a/runtime/ops/os/mod.rs +++ b/runtime/ops/os/mod.rs @@ -73,7 +73,7 @@ deno_core::extension!( #[derive(Debug, thiserror::Error)] pub enum OsError { #[error(transparent)] - Permission(deno_core::error::AnyError), + Permission(#[from] deno_permissions::PermissionCheckError), #[error("File name or path {0:?} is not valid UTF-8")] InvalidUtf8(std::ffi::OsString), #[error("Key is an empty string.")] @@ -94,8 +94,7 @@ fn op_exec_path(state: &mut OpState) -> Result { let current_exe = env::current_exe().unwrap(); state .borrow_mut::() - .check_read_blind(¤t_exe, "exec_path", "Deno.execPath()") - .map_err(OsError::Permission)?; + .check_read_blind(¤t_exe, "exec_path", "Deno.execPath()")?; // normalize path so it doesn't include '.' or '..' components let path = normalize_path(current_exe); @@ -111,10 +110,7 @@ fn op_set_env( #[string] key: &str, #[string] value: &str, ) -> Result<(), OsError> { - state - .borrow_mut::() - .check_env(key) - .map_err(OsError::Permission)?; + state.borrow_mut::().check_env(key)?; if key.is_empty() { return Err(OsError::EnvEmptyKey); } @@ -146,10 +142,7 @@ fn op_get_env( let skip_permission_check = NODE_ENV_VAR_ALLOWLIST.contains(&key); if !skip_permission_check { - state - .borrow_mut::() - .check_env(&key) - .map_err(OsError::Permission)?; + state.borrow_mut::().check_env(&key)?; } if key.is_empty() { @@ -172,10 +165,7 @@ fn op_delete_env( state: &mut OpState, #[string] key: String, ) -> Result<(), OsError> { - state - .borrow_mut::() - .check_env(&key) - .map_err(OsError::Permission)?; + state.borrow_mut::().check_env(&key)?; if key.is_empty() || key.contains(&['=', '\0'] as &[char]) { return Err(OsError::EnvInvalidKey(key.to_string())); } @@ -240,8 +230,7 @@ fn op_network_interfaces( ) -> Result, OsError> { state .borrow_mut::() - .check_sys("networkInterfaces", "Deno.networkInterfaces()") - .map_err(OsError::Permission)?; + .check_sys("networkInterfaces", "Deno.networkInterfaces()")?; Ok(netif::up()?.map(NetworkInterface::from).collect()) } diff --git a/runtime/ops/permissions.rs b/runtime/ops/permissions.rs index 1dbc852596..9ad963f3bc 100644 --- a/runtime/ops/permissions.rs +++ b/runtime/ops/permissions.rs @@ -2,8 +2,6 @@ use ::deno_permissions::PermissionState; use ::deno_permissions::PermissionsContainer; -use deno_core::error::custom_error; -use deno_core::error::AnyError; use deno_core::op2; use deno_core::OpState; use serde::Deserialize; @@ -47,12 +45,26 @@ impl From for PermissionStatus { } } +#[derive(Debug, thiserror::Error)] +pub enum PermissionError { + #[error("No such permission name: {0}")] + InvalidPermissionName(String), + #[error("{0}")] + PathResolve(#[from] ::deno_permissions::PathResolveError), + #[error("{0}")] + NetDescriptorParse(#[from] ::deno_permissions::NetDescriptorParseError), + #[error("{0}")] + SysDescriptorParse(#[from] ::deno_permissions::SysDescriptorParseError), + #[error("{0}")] + RunDescriptorParse(#[from] ::deno_permissions::RunDescriptorParseError), +} + #[op2] #[serde] pub fn op_query_permission( state: &mut OpState, #[serde] args: PermissionArgs, -) -> Result { +) -> Result { let permissions = state.borrow::(); let perm = match args.name.as_ref() { "read" => permissions.query_read(args.path.as_deref())?, @@ -62,12 +74,7 @@ pub fn op_query_permission( "sys" => permissions.query_sys(args.kind.as_deref())?, "run" => permissions.query_run(args.command.as_deref())?, "ffi" => permissions.query_ffi(args.path.as_deref())?, - n => { - return Err(custom_error( - "ReferenceError", - format!("No such permission name: {n}"), - )) - } + _ => return Err(PermissionError::InvalidPermissionName(args.name)), }; Ok(PermissionStatus::from(perm)) } @@ -77,7 +84,7 @@ pub fn op_query_permission( pub fn op_revoke_permission( state: &mut OpState, #[serde] args: PermissionArgs, -) -> Result { +) -> Result { let permissions = state.borrow::(); let perm = match args.name.as_ref() { "read" => permissions.revoke_read(args.path.as_deref())?, @@ -87,12 +94,7 @@ pub fn op_revoke_permission( "sys" => permissions.revoke_sys(args.kind.as_deref())?, "run" => permissions.revoke_run(args.command.as_deref())?, "ffi" => permissions.revoke_ffi(args.path.as_deref())?, - n => { - return Err(custom_error( - "ReferenceError", - format!("No such permission name: {n}"), - )) - } + _ => return Err(PermissionError::InvalidPermissionName(args.name)), }; Ok(PermissionStatus::from(perm)) } @@ -102,7 +104,7 @@ pub fn op_revoke_permission( pub fn op_request_permission( state: &mut OpState, #[serde] args: PermissionArgs, -) -> Result { +) -> Result { let permissions = state.borrow::(); let perm = match args.name.as_ref() { "read" => permissions.request_read(args.path.as_deref())?, @@ -112,12 +114,7 @@ pub fn op_request_permission( "sys" => permissions.request_sys(args.kind.as_deref())?, "run" => permissions.request_run(args.command.as_deref())?, "ffi" => permissions.request_ffi(args.path.as_deref())?, - n => { - return Err(custom_error( - "ReferenceError", - format!("No such permission name: {n}"), - )) - } + _ => return Err(PermissionError::InvalidPermissionName(args.name)), }; Ok(PermissionStatus::from(perm)) } diff --git a/runtime/ops/process.rs b/runtime/ops/process.rs index 28913f7c16..de3141f1f9 100644 --- a/runtime/ops/process.rs +++ b/runtime/ops/process.rs @@ -206,7 +206,9 @@ pub enum ProcessError { #[error("failed resolving cwd: {0}")] FailedResolvingCwd(#[source] std::io::Error), #[error(transparent)] - Permission(deno_core::error::AnyError), + Permission(#[from] deno_permissions::PermissionCheckError), + #[error(transparent)] + RunPermission(#[from] CheckRunPermissionError), #[error(transparent)] Resource(deno_core::error::AnyError), #[error(transparent)] @@ -653,8 +655,7 @@ fn compute_run_cmd_and_check_permissions( }, &run_env, api_name, - ) - .map_err(ProcessError::Permission)?; + )?; Ok((cmd, run_env)) } @@ -734,12 +735,20 @@ fn resolve_path(path: &str, cwd: &Path) -> PathBuf { deno_path_util::normalize_path(cwd.join(path)) } +#[derive(Debug, thiserror::Error)] +pub enum CheckRunPermissionError { + #[error(transparent)] + Permission(#[from] deno_permissions::PermissionCheckError), + #[error("{0}")] + Other(deno_core::error::AnyError), +} + fn check_run_permission( state: &mut OpState, cmd: &RunQueryDescriptor, run_env: &RunEnv, api_name: &str, -) -> Result<(), deno_core::error::AnyError> { +) -> Result<(), CheckRunPermissionError> { let permissions = state.borrow_mut::(); if !permissions.query_run_all(api_name) { // error the same on all platforms @@ -747,14 +756,14 @@ fn check_run_permission( if !env_var_names.is_empty() { // we don't allow users to launch subprocesses with any LD_ or DYLD_* // env vars set because this allows executing code (ex. LD_PRELOAD) - return Err(deno_core::error::custom_error( + return Err(CheckRunPermissionError::Other(deno_core::error::custom_error( "NotCapable", format!( "Requires --allow-all permissions to spawn subprocess with {} environment variable{}.", env_var_names.join(", "), if env_var_names.len() != 1 { "s" } else { "" } ) - )); + ))); } permissions.check_run(cmd, api_name)?; } @@ -1126,8 +1135,7 @@ mod deprecated { ) -> Result<(), ProcessError> { state .borrow_mut::() - .check_run_all(&api_name) - .map_err(ProcessError::Permission)?; + .check_run_all(&api_name)?; kill(pid, &signal) } } diff --git a/runtime/ops/worker_host.rs b/runtime/ops/worker_host.rs index 82cc949242..521284a6a0 100644 --- a/runtime/ops/worker_host.rs +++ b/runtime/ops/worker_host.rs @@ -123,7 +123,7 @@ pub enum CreateWorkerError { #[error("Classic workers are not supported.")] ClassicWorkers, #[error(transparent)] - Permission(deno_core::error::AnyError), + Permission(deno_permissions::ChildPermissionError), #[error(transparent)] ModuleResolution(#[from] deno_core::ModuleResolutionError), #[error(transparent)] diff --git a/runtime/permissions.rs b/runtime/permissions.rs index fa62227e0d..e8460e03f8 100644 --- a/runtime/permissions.rs +++ b/runtime/permissions.rs @@ -3,9 +3,6 @@ use std::path::Path; use std::path::PathBuf; -use deno_core::anyhow::bail; -use deno_core::anyhow::Context; -use deno_core::error::AnyError; use deno_path_util::normalize_path; use deno_permissions::AllowRunDescriptor; use deno_permissions::AllowRunDescriptorParseResult; @@ -15,9 +12,12 @@ use deno_permissions::FfiDescriptor; use deno_permissions::ImportDescriptor; use deno_permissions::NetDescriptor; use deno_permissions::PathQueryDescriptor; +use deno_permissions::PathResolveError; use deno_permissions::ReadDescriptor; +use deno_permissions::RunDescriptorParseError; use deno_permissions::RunQueryDescriptor; use deno_permissions::SysDescriptor; +use deno_permissions::SysDescriptorParseError; use deno_permissions::WriteDescriptor; #[derive(Debug)] @@ -30,9 +30,9 @@ impl RuntimePermissionDescriptorParser { Self { fs } } - fn resolve_from_cwd(&self, path: &str) -> Result { + fn resolve_from_cwd(&self, path: &str) -> Result { if path.is_empty() { - bail!("Empty path is not allowed"); + return Err(PathResolveError::EmptyPath); } let path = Path::new(path); if path.is_absolute() { @@ -43,12 +43,11 @@ impl RuntimePermissionDescriptorParser { } } - fn resolve_cwd(&self) -> Result { + fn resolve_cwd(&self) -> Result { self .fs .cwd() - .map_err(|e| e.into_io_error()) - .context("failed resolving cwd") + .map_err(|e| PathResolveError::CwdResolve(e.into_io_error())) } } @@ -58,37 +57,37 @@ impl deno_permissions::PermissionDescriptorParser fn parse_read_descriptor( &self, text: &str, - ) -> Result { + ) -> Result { Ok(ReadDescriptor(self.resolve_from_cwd(text)?)) } fn parse_write_descriptor( &self, text: &str, - ) -> Result { + ) -> Result { Ok(WriteDescriptor(self.resolve_from_cwd(text)?)) } fn parse_net_descriptor( &self, text: &str, - ) -> Result { + ) -> Result { NetDescriptor::parse(text) } fn parse_import_descriptor( &self, text: &str, - ) -> Result { + ) -> Result { ImportDescriptor::parse(text) } fn parse_env_descriptor( &self, text: &str, - ) -> Result { + ) -> Result { if text.is_empty() { - Err(AnyError::msg("Empty env not allowed")) + Err(deno_permissions::EnvDescriptorParseError) } else { Ok(EnvDescriptor::new(text)) } @@ -97,9 +96,9 @@ impl deno_permissions::PermissionDescriptorParser fn parse_sys_descriptor( &self, text: &str, - ) -> Result { + ) -> Result { if text.is_empty() { - Err(AnyError::msg("Empty sys not allowed")) + Err(SysDescriptorParseError::Empty) } else { Ok(SysDescriptor::parse(text.to_string())?) } @@ -108,21 +107,21 @@ impl deno_permissions::PermissionDescriptorParser fn parse_allow_run_descriptor( &self, text: &str, - ) -> Result { + ) -> Result { Ok(AllowRunDescriptor::parse(text, &self.resolve_cwd()?)?) } fn parse_deny_run_descriptor( &self, text: &str, - ) -> Result { + ) -> Result { Ok(DenyRunDescriptor::parse(text, &self.resolve_cwd()?)) } fn parse_ffi_descriptor( &self, text: &str, - ) -> Result { + ) -> Result { Ok(FfiDescriptor(self.resolve_from_cwd(text)?)) } @@ -131,7 +130,7 @@ impl deno_permissions::PermissionDescriptorParser fn parse_path_query( &self, path: &str, - ) -> Result { + ) -> Result { Ok(PathQueryDescriptor { resolved: self.resolve_from_cwd(path)?, requested: path.to_string(), @@ -141,11 +140,12 @@ impl deno_permissions::PermissionDescriptorParser fn parse_run_query( &self, requested: &str, - ) -> Result { + ) -> Result { if requested.is_empty() { - bail!("Empty run query is not allowed"); + return Err(RunDescriptorParseError::EmptyRunQuery); } RunQueryDescriptor::parse(requested) + .map_err(RunDescriptorParseError::PathResolve) } } diff --git a/runtime/permissions/Cargo.toml b/runtime/permissions/Cargo.toml index bfa37b4898..f0b2e71b55 100644 --- a/runtime/permissions/Cargo.toml +++ b/runtime/permissions/Cargo.toml @@ -23,6 +23,7 @@ log.workspace = true once_cell.workspace = true percent-encoding = { version = "2.3.1", features = [] } serde.workspace = true +thiserror.workspace = true which.workspace = true [target.'cfg(windows)'.dependencies] diff --git a/runtime/permissions/lib.rs b/runtime/permissions/lib.rs index 84503f025b..6480f4bf58 100644 --- a/runtime/permissions/lib.rs +++ b/runtime/permissions/lib.rs @@ -1,11 +1,5 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use deno_core::anyhow::bail; -use deno_core::anyhow::Context; -use deno_core::error::custom_error; -use deno_core::error::type_error; -use deno_core::error::uri_error; -use deno_core::error::AnyError; use deno_core::parking_lot::Mutex; use deno_core::serde::de; use deno_core::serde::Deserialize; @@ -43,6 +37,21 @@ pub use prompter::PermissionPrompter; pub use prompter::PromptCallback; pub use prompter::PromptResponse; +#[derive(Debug, thiserror::Error)] +#[error("Requires {access}, {}", format_permission_error(.name))] +pub struct PermissionDeniedError { + access: String, + name: &'static str, +} + +fn format_permission_error(name: &'static str) -> String { + if is_standalone() { + format!("specify the required permissions during compilation using `deno compile --allow-{name}`") + } else { + format!("run again with the --allow-{name} flag") + } +} + /// Fast exit from permission check routines if this permission /// is in the "fully-granted" state. macro_rules! skip_check_if_is_permission_fully_granted { @@ -104,7 +113,10 @@ impl From for AllowPartial { impl PermissionState { #[inline(always)] - fn log_perm_access(name: &str, info: impl FnOnce() -> Option) { + fn log_perm_access( + name: &'static str, + info: impl FnOnce() -> Option, + ) { // Eliminates log overhead (when logging is disabled), // log_enabled!(Debug) check in a hot path still has overhead // TODO(AaronO): generalize or upstream this optimization @@ -120,53 +132,47 @@ impl PermissionState { } } - fn fmt_access(name: &str, info: impl FnOnce() -> Option) -> String { + fn fmt_access( + name: &'static str, + info: impl FnOnce() -> Option, + ) -> String { format!( "{} access{}", name, - info() - .map(|info| { format!(" to {info}") }) - .unwrap_or_default(), + info().map(|info| format!(" to {info}")).unwrap_or_default(), ) } - fn error(name: &str, info: impl FnOnce() -> Option) -> AnyError { - let msg = if is_standalone() { - format!( - "Requires {}, specify the required permissions during compilation using `deno compile --allow-{}`", - Self::fmt_access(name, info), - name - ) - } else { - format!( - "Requires {}, run again with the --allow-{} flag", - Self::fmt_access(name, info), - name - ) - }; - custom_error("NotCapable", msg) + fn error( + name: &'static str, + info: impl FnOnce() -> Option, + ) -> PermissionDeniedError { + PermissionDeniedError { + access: Self::fmt_access(name, info), + name, + } } /// Check the permission state. bool is whether a prompt was issued. #[inline] fn check( self, - name: &str, + name: &'static str, api_name: Option<&str>, info: Option<&str>, prompt: bool, - ) -> (Result<(), AnyError>, bool, bool) { + ) -> (Result<(), PermissionDeniedError>, bool, bool) { self.check2(name, api_name, || info.map(|s| s.to_string()), prompt) } #[inline] fn check2( self, - name: &str, + name: &'static str, api_name: Option<&str>, info: impl Fn() -> Option, prompt: bool, - ) -> (Result<(), AnyError>, bool, bool) { + ) -> (Result<(), PermissionDeniedError>, bool, bool) { match self { PermissionState::Granted => { Self::log_perm_access(name, info); @@ -246,7 +252,7 @@ impl UnitPermission { self.state } - pub fn check(&mut self) -> Result<(), AnyError> { + pub fn check(&mut self) -> Result<(), PermissionDeniedError> { let (result, prompted, _is_allow_all) = self.state.check(self.name, None, None, self.prompt); if prompted { @@ -262,7 +268,7 @@ impl UnitPermission { fn create_child_permissions( &mut self, flag: ChildUnitPermissionArg, - ) -> Result { + ) -> Result { let mut perm = self.clone(); match flag { ChildUnitPermissionArg::Inherit => { @@ -270,7 +276,7 @@ impl UnitPermission { } ChildUnitPermissionArg::Granted => { if self.check().is_err() { - return Err(escalation_error()); + return Err(ChildPermissionError::Escalation); } perm.state = PermissionState::Granted; } @@ -327,7 +333,7 @@ pub trait QueryDescriptor: Debug { &self, perm: &mut UnaryPermission, api_name: Option<&str>, - ) -> Result<(), AnyError>; + ) -> Result<(), PermissionDeniedError>; fn matches_allow(&self, other: &Self::AllowDesc) -> bool; fn matches_deny(&self, other: &Self::DenyDesc) -> bool; @@ -402,7 +408,7 @@ impl UnaryPermission { pub fn check_all_api( &mut self, api_name: Option<&str>, - ) -> Result<(), AnyError> { + ) -> Result<(), PermissionDeniedError> { skip_check_if_is_permission_fully_granted!(self); self.check_desc(None, false, api_name) } @@ -412,7 +418,7 @@ impl UnaryPermission { desc: Option<&TQuery>, assert_non_partial: bool, api_name: Option<&str>, - ) -> Result<(), AnyError> { + ) -> Result<(), PermissionDeniedError> { let (result, prompted, is_allow_all) = self .query_desc(desc, AllowPartial::from(!assert_non_partial)) .check2( @@ -599,11 +605,14 @@ impl UnaryPermission { } } - fn create_child_permissions( + fn create_child_permissions( &mut self, flag: ChildUnaryPermissionArg, - parse: impl Fn(&str) -> Result, AnyError>, - ) -> Result, AnyError> { + parse: impl Fn(&str) -> Result, E>, + ) -> Result, ChildPermissionError> + where + ChildPermissionError: From, + { let mut perms = Self::default(); match flag { @@ -612,7 +621,7 @@ impl UnaryPermission { } ChildUnaryPermissionArg::Granted => { if self.check_all_api(None).is_err() { - return Err(escalation_error()); + return Err(ChildPermissionError::Escalation); } perms.granted_global = true; } @@ -621,13 +630,13 @@ impl UnaryPermission { perms.granted_list = granted_list .iter() .filter_map(|i| parse(i).transpose()) - .collect::>()?; + .collect::>()?; if !perms.granted_list.iter().all(|desc| { TQuery::from_allow(desc) .check_in_permission(self, None) .is_ok() }) { - return Err(escalation_error()); + return Err(ChildPermissionError::Escalation); } } } @@ -698,7 +707,7 @@ impl QueryDescriptor for ReadQueryDescriptor { &self, perm: &mut UnaryPermission, api_name: Option<&str>, - ) -> Result<(), AnyError> { + ) -> Result<(), PermissionDeniedError> { skip_check_if_is_permission_fully_granted!(perm); perm.check_desc(Some(self), true, api_name) } @@ -761,7 +770,7 @@ impl QueryDescriptor for WriteQueryDescriptor { &self, perm: &mut UnaryPermission, api_name: Option<&str>, - ) -> Result<(), AnyError> { + ) -> Result<(), PermissionDeniedError> { skip_check_if_is_permission_fully_granted!(perm); perm.check_desc(Some(self), true, api_name) } @@ -796,22 +805,37 @@ pub enum Host { Ip(IpAddr), } +#[derive(Debug, thiserror::Error)] +pub enum HostParseError { + #[error("invalid IPv6 address: '{0}'")] + InvalidIpv6(String), + #[error("invalid host: '{0}'")] + InvalidHost(String), + #[error("invalid empty host: '{0}'")] + InvalidEmptyHost(String), + #[error("invalid host '{host}': {error}")] + Fqdn { + #[source] + error: fqdn::Error, + host: String, + }, +} + impl Host { - // TODO(bartlomieju): rewrite to not use `AnyError` but a specific error implementations - fn parse(s: &str) -> Result { + fn parse(s: &str) -> Result { if s.starts_with('[') && s.ends_with(']') { let ip = s[1..s.len() - 1] .parse::() - .map_err(|_| uri_error(format!("invalid IPv6 address: '{s}'")))?; + .map_err(|_| HostParseError::InvalidIpv6(s.to_string()))?; return Ok(Host::Ip(IpAddr::V6(ip))); } let (without_trailing_dot, has_trailing_dot) = s.strip_suffix('.').map_or((s, false), |s| (s, true)); if let Ok(ip) = without_trailing_dot.parse::() { if has_trailing_dot { - return Err(uri_error(format!( - "invalid host: '{without_trailing_dot}'" - ))); + return Err(HostParseError::InvalidHost( + without_trailing_dot.to_string(), + )); } Ok(Host::Ip(ip)) } else { @@ -822,11 +846,13 @@ impl Host { }; let fqdn = { use std::str::FromStr; - FQDN::from_str(&lower) - .with_context(|| format!("invalid host: '{s}'"))? + FQDN::from_str(&lower).map_err(|e| HostParseError::Fqdn { + error: e, + host: s.to_string(), + })? }; if fqdn.is_root() { - return Err(uri_error(format!("invalid empty host: '{s}'"))); + return Err(HostParseError::InvalidEmptyHost(s.to_string())); } Ok(Host::Fqdn(fqdn)) } @@ -870,7 +896,7 @@ impl QueryDescriptor for NetDescriptor { &self, perm: &mut UnaryPermission, api_name: Option<&str>, - ) -> Result<(), AnyError> { + ) -> Result<(), PermissionDeniedError> { skip_check_if_is_permission_fully_granted!(perm); perm.check_desc(Some(self), false, api_name) } @@ -896,39 +922,72 @@ impl QueryDescriptor for NetDescriptor { } } -// TODO(bartlomieju): rewrite to not use `AnyError` but a specific error implementations +#[derive(Debug, thiserror::Error)] +pub enum NetDescriptorParseError { + #[error("invalid value '{0}': URLs are not supported, only domains and ips")] + Url(String), + #[error("invalid IPv6 address in '{hostname}': '{ip}'")] + InvalidIpv6 { hostname: String, ip: String }, + #[error("invalid port in '{hostname}': '{port}'")] + InvalidPort { hostname: String, port: String }, + #[error("invalid host: '{0}'")] + InvalidHost(String), + #[error("invalid empty port in '{0}'")] + EmptyPort(String), + #[error("ipv6 addresses must be enclosed in square brackets: '{0}'")] + Ipv6MissingSquareBrackets(String), + #[error("{0}")] + Host(#[from] HostParseError), +} + +#[derive(Debug, thiserror::Error)] +pub enum NetDescriptorFromUrlParseError { + #[error("Missing host in url: '{0}'")] + MissingHost(Url), + #[error("{0}")] + Host(#[from] HostParseError), +} + impl NetDescriptor { - pub fn parse(hostname: &str) -> Result { + pub fn parse(hostname: &str) -> Result { if hostname.starts_with("http://") || hostname.starts_with("https://") { - return Err(uri_error(format!("invalid value '{hostname}': URLs are not supported, only domains and ips"))); + return Err(NetDescriptorParseError::Url(hostname.to_string())); } // If this is a IPv6 address enclosed in square brackets, parse it as such. if hostname.starts_with('[') { if let Some((ip, after)) = hostname.split_once(']') { let ip = ip[1..].parse::().map_err(|_| { - uri_error(format!("invalid IPv6 address in '{hostname}': '{ip}'")) + NetDescriptorParseError::InvalidIpv6 { + hostname: hostname.to_string(), + ip: ip.to_string(), + } })?; let port = if let Some(port) = after.strip_prefix(':') { let port = port.parse::().map_err(|_| { - uri_error(format!("invalid port in '{hostname}': '{port}'")) + NetDescriptorParseError::InvalidPort { + hostname: hostname.to_string(), + port: port.to_string(), + } })?; Some(port) } else if after.is_empty() { None } else { - return Err(uri_error(format!("invalid host: '{hostname}'"))); + return Err(NetDescriptorParseError::InvalidHost( + hostname.to_string(), + )); }; return Ok(NetDescriptor(Host::Ip(IpAddr::V6(ip)), port)); } else { - return Err(uri_error(format!("invalid host: '{hostname}'"))); + return Err(NetDescriptorParseError::InvalidHost(hostname.to_string())); } } // Otherwise it is an IPv4 address or a FQDN with an optional port. let (host, port) = match hostname.split_once(':') { Some((_, "")) => { - return Err(uri_error(format!("invalid empty port in '{hostname}'"))); + return Err(NetDescriptorParseError::EmptyPort(hostname.to_string())); } Some((host, port)) => (host, port), None => (hostname, ""), @@ -943,11 +1002,14 @@ impl NetDescriptor { // should give them a hint. There are always at least two colons in an // IPv6 address, so this heuristic finds likely a bare IPv6 address. if port.contains(':') { - uri_error(format!( - "ipv6 addresses must be enclosed in square brackets: '{hostname}'" - )) + NetDescriptorParseError::Ipv6MissingSquareBrackets( + hostname.to_string(), + ) } else { - uri_error(format!("invalid port in '{hostname}': '{port}'")) + NetDescriptorParseError::InvalidPort { + hostname: hostname.to_string(), + port: port.to_string(), + } } })?; Some(port) @@ -956,10 +1018,10 @@ impl NetDescriptor { Ok(NetDescriptor(host, port)) } - pub fn from_url(url: &Url) -> Result { - let host = url - .host_str() - .ok_or_else(|| type_error(format!("Missing host in url: '{}'", url)))?; + pub fn from_url(url: &Url) -> Result { + let host = url.host_str().ok_or_else(|| { + NetDescriptorFromUrlParseError::MissingHost(url.clone()) + })?; let host = Host::parse(host)?; let port = url.port_or_known_default(); Ok(NetDescriptor(host, port)) @@ -1011,7 +1073,7 @@ impl QueryDescriptor for ImportDescriptor { &self, perm: &mut UnaryPermission, api_name: Option<&str>, - ) -> Result<(), AnyError> { + ) -> Result<(), PermissionDeniedError> { skip_check_if_is_permission_fully_granted!(perm); perm.check_desc(Some(self), false, api_name) } @@ -1038,15 +1100,19 @@ impl QueryDescriptor for ImportDescriptor { } impl ImportDescriptor { - pub fn parse(specifier: &str) -> Result { + pub fn parse(specifier: &str) -> Result { Ok(ImportDescriptor(NetDescriptor::parse(specifier)?)) } - pub fn from_url(url: &Url) -> Result { + pub fn from_url(url: &Url) -> Result { Ok(ImportDescriptor(NetDescriptor::from_url(url)?)) } } +#[derive(Debug, thiserror::Error)] +#[error("Empty env not allowed")] +pub struct EnvDescriptorParseError; + #[derive(Clone, Eq, PartialEq, Hash, Debug)] pub struct EnvDescriptor(EnvVarName); @@ -1084,7 +1150,7 @@ impl QueryDescriptor for EnvDescriptor { &self, perm: &mut UnaryPermission, api_name: Option<&str>, - ) -> Result<(), AnyError> { + ) -> Result<(), PermissionDeniedError> { skip_check_if_is_permission_fully_granted!(perm); perm.check_desc(Some(self), false, api_name) } @@ -1131,14 +1197,25 @@ pub enum RunQueryDescriptor { Name(String), } +#[derive(Debug, thiserror::Error)] +pub enum PathResolveError { + #[error("failed resolving cwd: {0}")] + CwdResolve(#[source] std::io::Error), + #[error("Empty path is not allowed")] + EmptyPath, +} + impl RunQueryDescriptor { - pub fn parse(requested: &str) -> Result { + pub fn parse( + requested: &str, + ) -> Result { if is_path(requested) { let path = PathBuf::from(requested); let resolved = if path.is_absolute() { normalize_path(path) } else { - let cwd = std::env::current_dir().context("failed resolving cwd")?; + let cwd = + std::env::current_dir().map_err(PathResolveError::CwdResolve)?; normalize_path(cwd.join(path)) }; Ok(RunQueryDescriptor::Path { @@ -1210,7 +1287,7 @@ impl QueryDescriptor for RunQueryDescriptor { &self, perm: &mut UnaryPermission, api_name: Option<&str>, - ) -> Result<(), AnyError> { + ) -> Result<(), PermissionDeniedError> { skip_check_if_is_permission_fully_granted!(perm); perm.check_desc(Some(self), false, api_name) } @@ -1280,6 +1357,16 @@ pub enum AllowRunDescriptorParseResult { Descriptor(AllowRunDescriptor), } +#[derive(Debug, thiserror::Error)] +pub enum RunDescriptorParseError { + #[error("{0}")] + Which(#[from] which::Error), + #[error("{0}")] + PathResolve(#[from] PathResolveError), + #[error("Empty run query is not allowed")] + EmptyRunQuery, +} + #[derive(Debug, Clone, Hash, Eq, PartialEq)] pub struct AllowRunDescriptor(pub PathBuf); @@ -1360,11 +1447,19 @@ fn denies_run_name(name: &str, cmd_path: &Path) -> bool { suffix.is_empty() || suffix.starts_with('.') } +#[derive(Debug, thiserror::Error)] +pub enum SysDescriptorParseError { + #[error("unknown system info kind \"{0}\"")] + InvalidKind(String), // TypeError + #[error("Empty sys not allowed")] + Empty, // Error +} + #[derive(Clone, Eq, PartialEq, Hash, Debug)] pub struct SysDescriptor(String); impl SysDescriptor { - pub fn parse(kind: String) -> Result { + pub fn parse(kind: String) -> Result { match kind.as_str() { "hostname" | "osRelease" | "osUptime" | "loadavg" | "networkInterfaces" | "systemMemoryInfo" | "uid" | "gid" | "cpus" @@ -1374,7 +1469,7 @@ impl SysDescriptor { // the underlying permission check changed to `userInfo` to better match the API, // alias this to avoid breaking existing projects with `--allow-sys=username` "username" => Ok(Self("userInfo".into())), - _ => Err(type_error(format!("unknown system info kind \"{kind}\""))), + _ => Err(SysDescriptorParseError::InvalidKind(kind)), } } @@ -1411,7 +1506,7 @@ impl QueryDescriptor for SysDescriptor { &self, perm: &mut UnaryPermission, api_name: Option<&str>, - ) -> Result<(), AnyError> { + ) -> Result<(), PermissionDeniedError> { skip_check_if_is_permission_fully_granted!(perm); perm.check_desc(Some(self), false, api_name) } @@ -1472,7 +1567,7 @@ impl QueryDescriptor for FfiQueryDescriptor { &self, perm: &mut UnaryPermission, api_name: Option<&str>, - ) -> Result<(), AnyError> { + ) -> Result<(), PermissionDeniedError> { skip_check_if_is_permission_fully_granted!(perm); perm.check_desc(Some(self), true, api_name) } @@ -1524,7 +1619,7 @@ impl UnaryPermission { &mut self, desc: &ReadQueryDescriptor, api_name: Option<&str>, - ) -> Result<(), AnyError> { + ) -> Result<(), PermissionDeniedError> { skip_check_if_is_permission_fully_granted!(self); self.check_desc(Some(desc), true, api_name) } @@ -1534,12 +1629,15 @@ impl UnaryPermission { &mut self, desc: &ReadQueryDescriptor, api_name: Option<&str>, - ) -> Result<(), AnyError> { + ) -> Result<(), PermissionDeniedError> { skip_check_if_is_permission_fully_granted!(self); self.check_desc(Some(desc), false, api_name) } - pub fn check_all(&mut self, api_name: Option<&str>) -> Result<(), AnyError> { + pub fn check_all( + &mut self, + api_name: Option<&str>, + ) -> Result<(), PermissionDeniedError> { skip_check_if_is_permission_fully_granted!(self); self.check_desc(None, false, api_name) } @@ -1568,7 +1666,7 @@ impl UnaryPermission { &mut self, path: &WriteQueryDescriptor, api_name: Option<&str>, - ) -> Result<(), AnyError> { + ) -> Result<(), PermissionDeniedError> { skip_check_if_is_permission_fully_granted!(self); self.check_desc(Some(path), true, api_name) } @@ -1578,12 +1676,15 @@ impl UnaryPermission { &mut self, path: &WriteQueryDescriptor, api_name: Option<&str>, - ) -> Result<(), AnyError> { + ) -> Result<(), PermissionDeniedError> { skip_check_if_is_permission_fully_granted!(self); self.check_desc(Some(path), false, api_name) } - pub fn check_all(&mut self, api_name: Option<&str>) -> Result<(), AnyError> { + pub fn check_all( + &mut self, + api_name: Option<&str>, + ) -> Result<(), PermissionDeniedError> { skip_check_if_is_permission_fully_granted!(self); self.check_desc(None, false, api_name) } @@ -1606,12 +1707,12 @@ impl UnaryPermission { &mut self, host: &NetDescriptor, api_name: Option<&str>, - ) -> Result<(), AnyError> { + ) -> Result<(), PermissionDeniedError> { skip_check_if_is_permission_fully_granted!(self); self.check_desc(Some(host), false, api_name) } - pub fn check_all(&mut self) -> Result<(), AnyError> { + pub fn check_all(&mut self) -> Result<(), PermissionDeniedError> { skip_check_if_is_permission_fully_granted!(self); self.check_desc(None, false, None) } @@ -1637,12 +1738,12 @@ impl UnaryPermission { &mut self, host: &ImportDescriptor, api_name: Option<&str>, - ) -> Result<(), AnyError> { + ) -> Result<(), PermissionDeniedError> { skip_check_if_is_permission_fully_granted!(self); self.check_desc(Some(host), false, api_name) } - pub fn check_all(&mut self) -> Result<(), AnyError> { + pub fn check_all(&mut self) -> Result<(), PermissionDeniedError> { skip_check_if_is_permission_fully_granted!(self); self.check_desc(None, false, None) } @@ -1668,12 +1769,12 @@ impl UnaryPermission { &mut self, env: &str, api_name: Option<&str>, - ) -> Result<(), AnyError> { + ) -> Result<(), PermissionDeniedError> { skip_check_if_is_permission_fully_granted!(self); self.check_desc(Some(&EnvDescriptor::new(env)), false, api_name) } - pub fn check_all(&mut self) -> Result<(), AnyError> { + pub fn check_all(&mut self) -> Result<(), PermissionDeniedError> { skip_check_if_is_permission_fully_granted!(self); self.check_desc(None, false, None) } @@ -1696,12 +1797,12 @@ impl UnaryPermission { &mut self, kind: &SysDescriptor, api_name: Option<&str>, - ) -> Result<(), AnyError> { + ) -> Result<(), PermissionDeniedError> { skip_check_if_is_permission_fully_granted!(self); self.check_desc(Some(kind), false, api_name) } - pub fn check_all(&mut self) -> Result<(), AnyError> { + pub fn check_all(&mut self) -> Result<(), PermissionDeniedError> { skip_check_if_is_permission_fully_granted!(self); self.check_desc(None, false, None) } @@ -1730,11 +1831,14 @@ impl UnaryPermission { &mut self, cmd: &RunQueryDescriptor, api_name: Option<&str>, - ) -> Result<(), AnyError> { + ) -> Result<(), PermissionDeniedError> { self.check_desc(Some(cmd), false, api_name) } - pub fn check_all(&mut self, api_name: Option<&str>) -> Result<(), AnyError> { + pub fn check_all( + &mut self, + api_name: Option<&str>, + ) -> Result<(), PermissionDeniedError> { self.check_desc(None, false, api_name) } @@ -1777,7 +1881,7 @@ impl UnaryPermission { &mut self, path: &FfiQueryDescriptor, api_name: Option<&str>, - ) -> Result<(), AnyError> { + ) -> Result<(), PermissionDeniedError> { skip_check_if_is_permission_fully_granted!(self); self.check_desc(Some(path), true, api_name) } @@ -1785,12 +1889,12 @@ impl UnaryPermission { pub fn check_partial( &mut self, path: Option<&FfiQueryDescriptor>, - ) -> Result<(), AnyError> { + ) -> Result<(), PermissionDeniedError> { skip_check_if_is_permission_fully_granted!(self); self.check_desc(path, false, None) } - pub fn check_all(&mut self) -> Result<(), AnyError> { + pub fn check_all(&mut self) -> Result<(), PermissionDeniedError> { skip_check_if_is_permission_fully_granted!(self); self.check_desc(None, false, Some("all")) } @@ -1830,23 +1934,39 @@ pub struct PermissionsOptions { pub prompt: bool, } +#[derive(Debug, thiserror::Error)] +pub enum PermissionsFromOptionsError { + #[error("{0}")] + PathResolve(#[from] PathResolveError), + #[error("{0}")] + SysDescriptorParse(#[from] SysDescriptorParseError), + #[error("{0}")] + NetDescriptorParse(#[from] NetDescriptorParseError), + #[error("{0}")] + EnvDescriptorParse(#[from] EnvDescriptorParseError), + #[error("{0}")] + RunDescriptorParse(#[from] RunDescriptorParseError), + #[error("Empty command name not allowed in --allow-run=...")] + RunEmptyCommandName, +} + impl Permissions { pub fn new_unary( allow_list: Option>, deny_list: Option>, prompt: bool, - ) -> Result, AnyError> + ) -> UnaryPermission where TQuery: QueryDescriptor, { - Ok(UnaryPermission:: { + UnaryPermission:: { granted_global: global_from_option(allow_list.as_ref()), granted_list: allow_list.unwrap_or_default(), flag_denied_global: global_from_option(deny_list.as_ref()), flag_denied_list: deny_list.unwrap_or_default(), prompt, ..Default::default() - }) + } } pub const fn new_all(allow_state: bool) -> UnitPermission { @@ -1862,15 +1982,15 @@ impl Permissions { pub fn from_options( parser: &dyn PermissionDescriptorParser, opts: &PermissionsOptions, - ) -> Result { + ) -> Result { fn resolve_allow_run( parser: &dyn PermissionDescriptorParser, allow_run: &[String], - ) -> Result, AnyError> { + ) -> Result, PermissionsFromOptionsError> { let mut new_allow_run = HashSet::with_capacity(allow_run.len()); for unresolved in allow_run { if unresolved.is_empty() { - bail!("Empty command name not allowed in --allow-run=...") + return Err(PermissionsFromOptionsError::RunEmptyCommandName); } match parser.parse_allow_run_descriptor(unresolved)? { AllowRunDescriptorParseResult::Descriptor(descriptor) => { @@ -1889,10 +2009,13 @@ impl Permissions { Ok(new_allow_run) } - fn parse_maybe_vec( + fn parse_maybe_vec( items: Option<&[String]>, - parse: impl Fn(&str) -> Result, - ) -> Result>, AnyError> { + parse: impl Fn(&str) -> Result, + ) -> Result>, PermissionsFromOptionsError> + where + PermissionsFromOptionsError: From, + { match items { Some(items) => Ok(Some( items @@ -1944,14 +2067,14 @@ impl Permissions { parser.parse_read_descriptor(item) })?, opts.prompt, - )?, + ), write: Permissions::new_unary( parse_maybe_vec(opts.allow_write.as_deref(), |item| { parser.parse_write_descriptor(item) })?, deny_write, opts.prompt, - )?, + ), net: Permissions::new_unary( parse_maybe_vec(opts.allow_net.as_deref(), |item| { parser.parse_net_descriptor(item) @@ -1960,7 +2083,7 @@ impl Permissions { parser.parse_net_descriptor(item) })?, opts.prompt, - )?, + ), env: Permissions::new_unary( parse_maybe_vec(opts.allow_env.as_deref(), |item| { parser.parse_env_descriptor(item) @@ -1969,7 +2092,7 @@ impl Permissions { parser.parse_env_descriptor(text) })?, opts.prompt, - )?, + ), sys: Permissions::new_unary( parse_maybe_vec(opts.allow_sys.as_deref(), |text| { parser.parse_sys_descriptor(text) @@ -1978,14 +2101,14 @@ impl Permissions { parser.parse_sys_descriptor(text) })?, opts.prompt, - )?, + ), run: Permissions::new_unary( allow_run, parse_maybe_vec(opts.deny_run.as_deref(), |text| { parser.parse_deny_run_descriptor(text) })?, opts.prompt, - )?, + ), ffi: Permissions::new_unary( parse_maybe_vec(opts.allow_ffi.as_deref(), |text| { parser.parse_ffi_descriptor(text) @@ -1994,14 +2117,14 @@ impl Permissions { parser.parse_ffi_descriptor(text) })?, opts.prompt, - )?, + ), import: Permissions::new_unary( parse_maybe_vec(opts.allow_import.as_deref(), |item| { parser.parse_import_descriptor(item) })?, None, opts.prompt, - )?, + ), all: Permissions::new_all(opts.allow_all), }) } @@ -2033,14 +2156,14 @@ impl Permissions { fn none(prompt: bool) -> Self { Self { - read: Permissions::new_unary(None, None, prompt).unwrap(), - write: Permissions::new_unary(None, None, prompt).unwrap(), - net: Permissions::new_unary(None, None, prompt).unwrap(), - env: Permissions::new_unary(None, None, prompt).unwrap(), - sys: Permissions::new_unary(None, None, prompt).unwrap(), - run: Permissions::new_unary(None, None, prompt).unwrap(), - ffi: Permissions::new_unary(None, None, prompt).unwrap(), - import: Permissions::new_unary(None, None, prompt).unwrap(), + read: Permissions::new_unary(None, None, prompt), + write: Permissions::new_unary(None, None, prompt), + net: Permissions::new_unary(None, None, prompt), + env: Permissions::new_unary(None, None, prompt), + sys: Permissions::new_unary(None, None, prompt), + run: Permissions::new_unary(None, None, prompt), + ffi: Permissions::new_unary(None, None, prompt), + import: Permissions::new_unary(None, None, prompt), all: Permissions::new_all(false), } } @@ -2052,6 +2175,38 @@ pub enum CheckSpecifierKind { Dynamic, } +#[derive(Debug, thiserror::Error)] +pub enum ChildPermissionError { + #[error("Can't escalate parent thread permissions")] + Escalation, + #[error("{0}")] + PathResolve(#[from] PathResolveError), + #[error("{0}")] + NetDescriptorParse(#[from] NetDescriptorParseError), + #[error("{0}")] + EnvDescriptorParse(#[from] EnvDescriptorParseError), + #[error("{0}")] + SysDescriptorParse(#[from] SysDescriptorParseError), + #[error("{0}")] + RunDescriptorParse(#[from] RunDescriptorParseError), +} + +#[derive(Debug, thiserror::Error)] +pub enum PermissionCheckError { + #[error(transparent)] + PermissionDenied(#[from] PermissionDeniedError), + #[error("Invalid file path.\n Specifier: {0}")] + InvalidFilePath(Url), + #[error(transparent)] + NetDescriptorForUrlParse(#[from] NetDescriptorFromUrlParseError), + #[error(transparent)] + SysDescriptorParse(#[from] SysDescriptorParseError), + #[error(transparent)] + PathResolve(#[from] PathResolveError), + #[error(transparent)] + HostParse(#[from] HostParseError), +} + /// Wrapper struct for `Permissions` that can be shared across threads. /// /// We need a way to have internal mutability for permissions as they might get @@ -2084,7 +2239,7 @@ impl PermissionsContainer { pub fn create_child_permissions( &self, child_permissions_arg: ChildPermissionsArg, - ) -> Result { + ) -> Result { fn is_granted_unary(arg: &ChildUnaryPermissionArg) -> bool { match arg { ChildUnaryPermissionArg::Inherit | ChildUnaryPermissionArg::Granted => { @@ -2122,48 +2277,71 @@ impl PermissionsContainer { // WARNING: When adding a permission here, ensure it is handled // in the worker_perms.all block above - worker_perms.read = inner - .read - .create_child_permissions(child_permissions_arg.read, |text| { - Ok(Some(self.descriptor_parser.parse_read_descriptor(text)?)) - })?; - worker_perms.write = inner - .write - .create_child_permissions(child_permissions_arg.write, |text| { - Ok(Some(self.descriptor_parser.parse_write_descriptor(text)?)) - })?; - worker_perms.import = inner - .import - .create_child_permissions(child_permissions_arg.import, |text| { - Ok(Some(self.descriptor_parser.parse_import_descriptor(text)?)) - })?; - worker_perms.net = inner - .net - .create_child_permissions(child_permissions_arg.net, |text| { - Ok(Some(self.descriptor_parser.parse_net_descriptor(text)?)) - })?; - worker_perms.env = inner - .env - .create_child_permissions(child_permissions_arg.env, |text| { - Ok(Some(self.descriptor_parser.parse_env_descriptor(text)?)) - })?; - worker_perms.sys = inner - .sys - .create_child_permissions(child_permissions_arg.sys, |text| { - Ok(Some(self.descriptor_parser.parse_sys_descriptor(text)?)) - })?; + worker_perms.read = inner.read.create_child_permissions( + child_permissions_arg.read, + |text| { + Ok::<_, PathResolveError>(Some( + self.descriptor_parser.parse_read_descriptor(text)?, + )) + }, + )?; + worker_perms.write = inner.write.create_child_permissions( + child_permissions_arg.write, + |text| { + Ok::<_, PathResolveError>(Some( + self.descriptor_parser.parse_write_descriptor(text)?, + )) + }, + )?; + worker_perms.import = inner.import.create_child_permissions( + child_permissions_arg.import, + |text| { + Ok::<_, NetDescriptorParseError>(Some( + self.descriptor_parser.parse_import_descriptor(text)?, + )) + }, + )?; + worker_perms.net = inner.net.create_child_permissions( + child_permissions_arg.net, + |text| { + Ok::<_, NetDescriptorParseError>(Some( + self.descriptor_parser.parse_net_descriptor(text)?, + )) + }, + )?; + worker_perms.env = inner.env.create_child_permissions( + child_permissions_arg.env, + |text| { + Ok::<_, EnvDescriptorParseError>(Some( + self.descriptor_parser.parse_env_descriptor(text)?, + )) + }, + )?; + worker_perms.sys = inner.sys.create_child_permissions( + child_permissions_arg.sys, + |text| { + Ok::<_, SysDescriptorParseError>(Some( + self.descriptor_parser.parse_sys_descriptor(text)?, + )) + }, + )?; worker_perms.run = inner.run.create_child_permissions( child_permissions_arg.run, |text| match self.descriptor_parser.parse_allow_run_descriptor(text)? { - AllowRunDescriptorParseResult::Unresolved(_) => Ok(None), + AllowRunDescriptorParseResult::Unresolved(_) => { + Ok::<_, RunDescriptorParseError>(None) + } AllowRunDescriptorParseResult::Descriptor(desc) => Ok(Some(desc)), }, )?; - worker_perms.ffi = inner - .ffi - .create_child_permissions(child_permissions_arg.ffi, |text| { - Ok(Some(self.descriptor_parser.parse_ffi_descriptor(text)?)) - })?; + worker_perms.ffi = inner.ffi.create_child_permissions( + child_permissions_arg.ffi, + |text| { + Ok::<_, PathResolveError>(Some( + self.descriptor_parser.parse_ffi_descriptor(text)?, + )) + }, + )?; Ok(PermissionsContainer::new( self.descriptor_parser.clone(), @@ -2176,7 +2354,7 @@ impl PermissionsContainer { &self, specifier: &ModuleSpecifier, kind: CheckSpecifierKind, - ) -> Result<(), AnyError> { + ) -> Result<(), PermissionCheckError> { let mut inner = self.inner.lock(); match specifier.scheme() { "file" => { @@ -2185,17 +2363,20 @@ impl PermissionsContainer { } match url_to_file_path(specifier) { - Ok(path) => inner.read.check( - &PathQueryDescriptor { - requested: path.to_string_lossy().into_owned(), - resolved: path, - } - .into_read(), - Some("import()"), - ), - Err(_) => Err(uri_error(format!( - "Invalid file path.\n Specifier: {specifier}" - ))), + Ok(path) => inner + .read + .check( + &PathQueryDescriptor { + requested: path.to_string_lossy().into_owned(), + resolved: path, + } + .into_read(), + Some("import()"), + ) + .map_err(PermissionCheckError::PermissionDenied), + Err(_) => { + Err(PermissionCheckError::InvalidFilePath(specifier.clone())) + } } } "data" => Ok(()), @@ -2220,7 +2401,7 @@ impl PermissionsContainer { &self, path: &str, api_name: &str, - ) -> Result { + ) -> Result { self.check_read_with_api_name(path, Some(api_name)) } @@ -2230,7 +2411,7 @@ impl PermissionsContainer { &self, path: &str, api_name: Option<&str>, - ) -> Result { + ) -> Result { let mut inner = self.inner.lock(); let inner = &mut inner.read; if inner.is_allow_all() { @@ -2248,7 +2429,7 @@ impl PermissionsContainer { &self, path: &'a Path, api_name: Option<&str>, - ) -> Result, AnyError> { + ) -> Result, PermissionCheckError> { let mut inner = self.inner.lock(); let inner = &mut inner.read; if inner.is_allow_all() { @@ -2272,7 +2453,7 @@ impl PermissionsContainer { path: &Path, display: &str, api_name: &str, - ) -> Result<(), AnyError> { + ) -> Result<(), PermissionCheckError> { let mut inner = self.inner.lock(); let inner = &mut inner.read; skip_check_if_is_permission_fully_granted!(inner); @@ -2283,12 +2464,17 @@ impl PermissionsContainer { } .into_read(), Some(api_name), - ) + )?; + Ok(()) } #[inline(always)] - pub fn check_read_all(&self, api_name: &str) -> Result<(), AnyError> { - self.inner.lock().read.check_all(Some(api_name)) + pub fn check_read_all( + &self, + api_name: &str, + ) -> Result<(), PermissionCheckError> { + self.inner.lock().read.check_all(Some(api_name))?; + Ok(()) } #[inline(always)] @@ -2302,7 +2488,7 @@ impl PermissionsContainer { &self, path: &str, api_name: &str, - ) -> Result { + ) -> Result { self.check_write_with_api_name(path, Some(api_name)) } @@ -2312,7 +2498,7 @@ impl PermissionsContainer { &self, path: &str, api_name: Option<&str>, - ) -> Result { + ) -> Result { let mut inner = self.inner.lock(); let inner = &mut inner.write; if inner.is_allow_all() { @@ -2330,7 +2516,7 @@ impl PermissionsContainer { &self, path: &'a Path, api_name: &str, - ) -> Result, AnyError> { + ) -> Result, PermissionCheckError> { let mut inner = self.inner.lock(); let inner = &mut inner.write; if inner.is_allow_all() { @@ -2347,8 +2533,12 @@ impl PermissionsContainer { } #[inline(always)] - pub fn check_write_all(&self, api_name: &str) -> Result<(), AnyError> { - self.inner.lock().write.check_all(Some(api_name)) + pub fn check_write_all( + &self, + api_name: &str, + ) -> Result<(), PermissionCheckError> { + self.inner.lock().write.check_all(Some(api_name))?; + Ok(()) } /// As `check_write()`, but permission error messages will anonymize the path @@ -2359,7 +2549,7 @@ impl PermissionsContainer { path: &Path, display: &str, api_name: &str, - ) -> Result<(), AnyError> { + ) -> Result<(), PermissionCheckError> { let mut inner = self.inner.lock(); let inner = &mut inner.write; skip_check_if_is_permission_fully_granted!(inner); @@ -2370,7 +2560,8 @@ impl PermissionsContainer { } .into_write(), Some(api_name), - ) + )?; + Ok(()) } #[inline(always)] @@ -2378,7 +2569,7 @@ impl PermissionsContainer { &mut self, path: &str, api_name: &str, - ) -> Result { + ) -> Result { let mut inner = self.inner.lock(); let inner = &mut inner.write; if inner.is_allow_all() { @@ -2395,13 +2586,18 @@ impl PermissionsContainer { &mut self, cmd: &RunQueryDescriptor, api_name: &str, - ) -> Result<(), AnyError> { - self.inner.lock().run.check(cmd, Some(api_name)) + ) -> Result<(), PermissionCheckError> { + self.inner.lock().run.check(cmd, Some(api_name))?; + Ok(()) } #[inline(always)] - pub fn check_run_all(&mut self, api_name: &str) -> Result<(), AnyError> { - self.inner.lock().run.check_all(Some(api_name)) + pub fn check_run_all( + &mut self, + api_name: &str, + ) -> Result<(), PermissionCheckError> { + self.inner.lock().run.check_all(Some(api_name))?; + Ok(()) } #[inline(always)] @@ -2410,38 +2606,50 @@ impl PermissionsContainer { } #[inline(always)] - pub fn check_sys(&self, kind: &str, api_name: &str) -> Result<(), AnyError> { + pub fn check_sys( + &self, + kind: &str, + api_name: &str, + ) -> Result<(), PermissionCheckError> { self.inner.lock().sys.check( &self.descriptor_parser.parse_sys_descriptor(kind)?, Some(api_name), - ) + )?; + Ok(()) } #[inline(always)] - pub fn check_env(&mut self, var: &str) -> Result<(), AnyError> { - self.inner.lock().env.check(var, None) + pub fn check_env(&mut self, var: &str) -> Result<(), PermissionCheckError> { + self.inner.lock().env.check(var, None)?; + Ok(()) } #[inline(always)] - pub fn check_env_all(&mut self) -> Result<(), AnyError> { - self.inner.lock().env.check_all() + pub fn check_env_all(&mut self) -> Result<(), PermissionCheckError> { + self.inner.lock().env.check_all()?; + Ok(()) } #[inline(always)] - pub fn check_sys_all(&mut self) -> Result<(), AnyError> { - self.inner.lock().sys.check_all() + pub fn check_sys_all(&mut self) -> Result<(), PermissionCheckError> { + self.inner.lock().sys.check_all()?; + Ok(()) } #[inline(always)] - pub fn check_ffi_all(&mut self) -> Result<(), AnyError> { - self.inner.lock().ffi.check_all() + pub fn check_ffi_all(&mut self) -> Result<(), PermissionCheckError> { + self.inner.lock().ffi.check_all()?; + Ok(()) } /// This checks to see if the allow-all flag was passed, not whether all /// permissions are enabled! #[inline(always)] - pub fn check_was_allow_all_flag_passed(&mut self) -> Result<(), AnyError> { - self.inner.lock().all.check() + pub fn check_was_allow_all_flag_passed( + &mut self, + ) -> Result<(), PermissionCheckError> { + self.inner.lock().all.check()?; + Ok(()) } /// Checks special file access, returning the failed permission type if @@ -2553,13 +2761,14 @@ impl PermissionsContainer { &mut self, url: &Url, api_name: &str, - ) -> Result<(), AnyError> { + ) -> Result<(), PermissionCheckError> { let mut inner = self.inner.lock(); if inner.net.is_allow_all() { return Ok(()); } let desc = self.descriptor_parser.parse_net_descriptor_from_url(url)?; - inner.net.check(&desc, Some(api_name)) + inner.net.check(&desc, Some(api_name))?; + Ok(()) } #[inline(always)] @@ -2567,17 +2776,21 @@ impl PermissionsContainer { &mut self, host: &(T, Option), api_name: &str, - ) -> Result<(), AnyError> { + ) -> Result<(), PermissionCheckError> { let mut inner = self.inner.lock(); let inner = &mut inner.net; skip_check_if_is_permission_fully_granted!(inner); let hostname = Host::parse(host.0.as_ref())?; let descriptor = NetDescriptor(hostname, host.1); - inner.check(&descriptor, Some(api_name)) + inner.check(&descriptor, Some(api_name))?; + Ok(()) } #[inline(always)] - pub fn check_ffi(&mut self, path: &str) -> Result { + pub fn check_ffi( + &mut self, + path: &str, + ) -> Result { let mut inner = self.inner.lock(); let inner = &mut inner.ffi; if inner.is_allow_all() { @@ -2591,14 +2804,15 @@ impl PermissionsContainer { #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"] #[inline(always)] - pub fn check_ffi_partial_no_path(&mut self) -> Result<(), AnyError> { + pub fn check_ffi_partial_no_path( + &mut self, + ) -> Result<(), PermissionCheckError> { let mut inner = self.inner.lock(); let inner = &mut inner.ffi; - if inner.is_allow_all() { - Ok(()) - } else { - inner.check_partial(None) + if !inner.is_allow_all() { + inner.check_partial(None)?; } + Ok(()) } #[must_use = "the resolved return value to mitigate time-of-check to time-of-use issues"] @@ -2606,7 +2820,7 @@ impl PermissionsContainer { pub fn check_ffi_partial_with_path( &mut self, path: &str, - ) -> Result { + ) -> Result { let mut inner = self.inner.lock(); let inner = &mut inner.ffi; if inner.is_allow_all() { @@ -2624,7 +2838,7 @@ impl PermissionsContainer { pub fn query_read( &self, path: Option<&str>, - ) -> Result { + ) -> Result { let inner = self.inner.lock(); let permission = &inner.read; if permission.is_allow_all() { @@ -2634,7 +2848,7 @@ impl PermissionsContainer { permission.query( path .map(|path| { - Result::<_, AnyError>::Ok( + Ok::<_, PathResolveError>( self.descriptor_parser.parse_path_query(path)?.into_read(), ) }) @@ -2648,7 +2862,7 @@ impl PermissionsContainer { pub fn query_write( &self, path: Option<&str>, - ) -> Result { + ) -> Result { let inner = self.inner.lock(); let permission = &inner.write; if permission.is_allow_all() { @@ -2658,7 +2872,7 @@ impl PermissionsContainer { permission.query( path .map(|path| { - Result::<_, AnyError>::Ok( + Ok::<_, PathResolveError>( self.descriptor_parser.parse_path_query(path)?.into_write(), ) }) @@ -2672,7 +2886,7 @@ impl PermissionsContainer { pub fn query_net( &self, host: Option<&str>, - ) -> Result { + ) -> Result { let inner = self.inner.lock(); let permission = &inner.net; if permission.is_allow_all() { @@ -2703,7 +2917,7 @@ impl PermissionsContainer { pub fn query_sys( &self, kind: Option<&str>, - ) -> Result { + ) -> Result { let inner = self.inner.lock(); let permission = &inner.sys; if permission.is_allow_all() { @@ -2723,7 +2937,7 @@ impl PermissionsContainer { pub fn query_run( &self, cmd: Option<&str>, - ) -> Result { + ) -> Result { let inner = self.inner.lock(); let permission = &inner.run; if permission.is_allow_all() { @@ -2743,7 +2957,7 @@ impl PermissionsContainer { pub fn query_ffi( &self, path: Option<&str>, - ) -> Result { + ) -> Result { let inner = self.inner.lock(); let permission = &inner.ffi; if permission.is_allow_all() { @@ -2753,7 +2967,7 @@ impl PermissionsContainer { permission.query( path .map(|path| { - Result::<_, AnyError>::Ok( + Ok::<_, PathResolveError>( self.descriptor_parser.parse_path_query(path)?.into_ffi(), ) }) @@ -2769,12 +2983,12 @@ impl PermissionsContainer { pub fn revoke_read( &self, path: Option<&str>, - ) -> Result { + ) -> Result { Ok( self.inner.lock().read.revoke( path .map(|path| { - Result::<_, AnyError>::Ok( + Ok::<_, PathResolveError>( self.descriptor_parser.parse_path_query(path)?.into_read(), ) }) @@ -2788,12 +3002,12 @@ impl PermissionsContainer { pub fn revoke_write( &self, path: Option<&str>, - ) -> Result { + ) -> Result { Ok( self.inner.lock().write.revoke( path .map(|path| { - Result::<_, AnyError>::Ok( + Ok::<_, PathResolveError>( self.descriptor_parser.parse_path_query(path)?.into_write(), ) }) @@ -2807,7 +3021,7 @@ impl PermissionsContainer { pub fn revoke_net( &self, host: Option<&str>, - ) -> Result { + ) -> Result { Ok( self.inner.lock().net.revoke( match host { @@ -2828,7 +3042,7 @@ impl PermissionsContainer { pub fn revoke_sys( &self, kind: Option<&str>, - ) -> Result { + ) -> Result { Ok( self.inner.lock().sys.revoke( kind @@ -2843,7 +3057,7 @@ impl PermissionsContainer { pub fn revoke_run( &self, cmd: Option<&str>, - ) -> Result { + ) -> Result { Ok( self.inner.lock().run.revoke( cmd @@ -2858,12 +3072,12 @@ impl PermissionsContainer { pub fn revoke_ffi( &self, path: Option<&str>, - ) -> Result { + ) -> Result { Ok( self.inner.lock().ffi.revoke( path .map(|path| { - Result::<_, AnyError>::Ok( + Ok::<_, PathResolveError>( self.descriptor_parser.parse_path_query(path)?.into_ffi(), ) }) @@ -2879,12 +3093,12 @@ impl PermissionsContainer { pub fn request_read( &self, path: Option<&str>, - ) -> Result { + ) -> Result { Ok( self.inner.lock().read.request( path .map(|path| { - Result::<_, AnyError>::Ok( + Ok::<_, PathResolveError>( self.descriptor_parser.parse_path_query(path)?.into_read(), ) }) @@ -2898,12 +3112,12 @@ impl PermissionsContainer { pub fn request_write( &self, path: Option<&str>, - ) -> Result { + ) -> Result { Ok( self.inner.lock().write.request( path .map(|path| { - Result::<_, AnyError>::Ok( + Ok::<_, PathResolveError>( self.descriptor_parser.parse_path_query(path)?.into_write(), ) }) @@ -2917,7 +3131,7 @@ impl PermissionsContainer { pub fn request_net( &self, host: Option<&str>, - ) -> Result { + ) -> Result { Ok( self.inner.lock().net.request( match host { @@ -2938,7 +3152,7 @@ impl PermissionsContainer { pub fn request_sys( &self, kind: Option<&str>, - ) -> Result { + ) -> Result { Ok( self.inner.lock().sys.request( kind @@ -2953,7 +3167,7 @@ impl PermissionsContainer { pub fn request_run( &self, cmd: Option<&str>, - ) -> Result { + ) -> Result { Ok( self.inner.lock().run.request( cmd @@ -2968,12 +3182,12 @@ impl PermissionsContainer { pub fn request_ffi( &self, path: Option<&str>, - ) -> Result { + ) -> Result { Ok( self.inner.lock().ffi.request( path .map(|path| { - Result::<_, AnyError>::Ok( + Ok::<_, PathResolveError>( self.descriptor_parser.parse_path_query(path)?.into_ffi(), ) }) @@ -3009,10 +3223,6 @@ fn global_from_option(flag: Option<&HashSet>) -> bool { matches!(flag, Some(v) if v.is_empty()) } -fn escalation_error() -> AnyError { - custom_error("NotCapable", "Can't escalate parent thread permissions") -} - #[derive(Debug, Eq, PartialEq)] pub enum ChildUnitPermissionArg { Inherit, @@ -3273,65 +3483,73 @@ pub trait PermissionDescriptorParser: Debug + Send + Sync { fn parse_read_descriptor( &self, text: &str, - ) -> Result; + ) -> Result; fn parse_write_descriptor( &self, text: &str, - ) -> Result; + ) -> Result; - fn parse_net_descriptor(&self, text: &str) - -> Result; + fn parse_net_descriptor( + &self, + text: &str, + ) -> Result; fn parse_net_descriptor_from_url( &self, url: &Url, - ) -> Result { + ) -> Result { NetDescriptor::from_url(url) } fn parse_import_descriptor( &self, text: &str, - ) -> Result; + ) -> Result; fn parse_import_descriptor_from_url( &self, url: &Url, - ) -> Result { + ) -> Result { ImportDescriptor::from_url(url) } - fn parse_env_descriptor(&self, text: &str) - -> Result; + fn parse_env_descriptor( + &self, + text: &str, + ) -> Result; - fn parse_sys_descriptor(&self, text: &str) - -> Result; + fn parse_sys_descriptor( + &self, + text: &str, + ) -> Result; fn parse_allow_run_descriptor( &self, text: &str, - ) -> Result; + ) -> Result; fn parse_deny_run_descriptor( &self, text: &str, - ) -> Result; + ) -> Result; - fn parse_ffi_descriptor(&self, text: &str) - -> Result; + fn parse_ffi_descriptor( + &self, + text: &str, + ) -> Result; // queries fn parse_path_query( &self, path: &str, - ) -> Result; + ) -> Result; fn parse_run_query( &self, requested: &str, - ) -> Result; + ) -> Result; } static IS_STANDALONE: AtomicFlag = AtomicFlag::lowered(); @@ -3374,49 +3592,49 @@ mod tests { fn parse_read_descriptor( &self, text: &str, - ) -> Result { + ) -> Result { Ok(ReadDescriptor(self.join_path_with_root(text))) } fn parse_write_descriptor( &self, text: &str, - ) -> Result { + ) -> Result { Ok(WriteDescriptor(self.join_path_with_root(text))) } fn parse_net_descriptor( &self, text: &str, - ) -> Result { + ) -> Result { NetDescriptor::parse(text) } fn parse_import_descriptor( &self, text: &str, - ) -> Result { + ) -> Result { ImportDescriptor::parse(text) } fn parse_env_descriptor( &self, text: &str, - ) -> Result { + ) -> Result { Ok(EnvDescriptor::new(text)) } fn parse_sys_descriptor( &self, text: &str, - ) -> Result { + ) -> Result { SysDescriptor::parse(text.to_string()) } fn parse_allow_run_descriptor( &self, text: &str, - ) -> Result { + ) -> Result { Ok(AllowRunDescriptorParseResult::Descriptor( AllowRunDescriptor(self.join_path_with_root(text)), )) @@ -3425,7 +3643,7 @@ mod tests { fn parse_deny_run_descriptor( &self, text: &str, - ) -> Result { + ) -> Result { if text.contains("/") { Ok(DenyRunDescriptor::Path(self.join_path_with_root(text))) } else { @@ -3436,14 +3654,14 @@ mod tests { fn parse_ffi_descriptor( &self, text: &str, - ) -> Result { + ) -> Result { Ok(FfiDescriptor(self.join_path_with_root(text))) } fn parse_path_query( &self, path: &str, - ) -> Result { + ) -> Result { Ok(PathQueryDescriptor { resolved: self.join_path_with_root(path), requested: path.to_string(), @@ -3453,8 +3671,8 @@ mod tests { fn parse_run_query( &self, requested: &str, - ) -> Result { - RunQueryDescriptor::parse(requested) + ) -> Result { + RunQueryDescriptor::parse(requested).map_err(Into::into) } } @@ -4335,7 +4553,6 @@ mod tests { None, false, ) - .unwrap() }; prompt_value.set(true); @@ -4562,13 +4779,12 @@ mod tests { .lock() .clone(), Permissions { - env: Permissions::new_unary(Some(HashSet::new()), None, false).unwrap(), + env: Permissions::new_unary(Some(HashSet::new()), None, false), net: Permissions::new_unary( Some(HashSet::from([NetDescriptor::parse("foo").unwrap()])), None, false - ) - .unwrap(), + ), ..Permissions::none_without_prompt() } ); diff --git a/runtime/permissions/prompter.rs b/runtime/permissions/prompter.rs index b582b4f53e..168a845a29 100644 --- a/runtime/permissions/prompter.rs +++ b/runtime/permissions/prompter.rs @@ -1,6 +1,5 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. -use deno_core::error::AnyError; use deno_core::parking_lot::Mutex; use deno_terminal::colors; use once_cell::sync::Lazy; @@ -101,8 +100,7 @@ pub struct TtyPrompter; fn clear_stdin( _stdin_lock: &mut StdinLock, _stderr_lock: &mut StderrLock, -) -> Result<(), AnyError> { - use deno_core::anyhow::bail; +) -> Result<(), std::io::Error> { use std::mem::MaybeUninit; const STDIN_FD: i32 = 0; @@ -117,7 +115,10 @@ fn clear_stdin( loop { let r = libc::tcflush(STDIN_FD, libc::TCIFLUSH); if r != 0 { - bail!("clear_stdin failed (tcflush)"); + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + "clear_stdin failed (tcflush)", + )); } // Initialize timeout for select to be 100ms @@ -137,7 +138,10 @@ fn clear_stdin( // Check if select returned an error if r < 0 { - bail!("clear_stdin failed (select)"); + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + "clear_stdin failed (select)", + )); } // Check if select returned due to timeout (stdin is quiescent) @@ -156,8 +160,7 @@ fn clear_stdin( fn clear_stdin( stdin_lock: &mut StdinLock, stderr_lock: &mut StderrLock, -) -> Result<(), AnyError> { - use deno_core::anyhow::bail; +) -> Result<(), std::io::Error> { use winapi::shared::minwindef::TRUE; use winapi::shared::minwindef::UINT; use winapi::shared::minwindef::WORD; @@ -194,18 +197,23 @@ fn clear_stdin( return Ok(()); - unsafe fn flush_input_buffer(stdin: HANDLE) -> Result<(), AnyError> { + unsafe fn flush_input_buffer(stdin: HANDLE) -> Result<(), std::io::Error> { let success = FlushConsoleInputBuffer(stdin); if success != TRUE { - bail!( - "Could not flush the console input buffer: {}", - std::io::Error::last_os_error() - ) + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + format!( + "Could not flush the console input buffer: {}", + std::io::Error::last_os_error() + ), + )); } Ok(()) } - unsafe fn emulate_enter_key_press(stdin: HANDLE) -> Result<(), AnyError> { + unsafe fn emulate_enter_key_press( + stdin: HANDLE, + ) -> Result<(), std::io::Error> { // https://github.com/libuv/libuv/blob/a39009a5a9252a566ca0704d02df8dabc4ce328f/src/win/tty.c#L1121-L1131 let mut input_record: INPUT_RECORD = std::mem::zeroed(); input_record.EventType = KEY_EVENT; @@ -220,34 +228,43 @@ fn clear_stdin( let success = WriteConsoleInputW(stdin, &input_record, 1, &mut record_written); if success != TRUE { - bail!( - "Could not emulate enter key press: {}", - std::io::Error::last_os_error() - ); + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + format!( + "Could not emulate enter key press: {}", + std::io::Error::last_os_error() + ), + )); } Ok(()) } - unsafe fn is_input_buffer_empty(stdin: HANDLE) -> Result { + unsafe fn is_input_buffer_empty( + stdin: HANDLE, + ) -> Result { let mut buffer = Vec::with_capacity(1); let mut events_read = 0; let success = PeekConsoleInputW(stdin, buffer.as_mut_ptr(), 1, &mut events_read); if success != TRUE { - bail!( - "Could not peek the console input buffer: {}", - std::io::Error::last_os_error() - ) + return Err(std::io::Error::new( + std::io::ErrorKind::Other, + format!( + "Could not peek the console input buffer: {}", + std::io::Error::last_os_error() + ), + )); } Ok(events_read == 0) } - fn move_cursor_up(stderr_lock: &mut StderrLock) -> Result<(), AnyError> { - write!(stderr_lock, "\x1B[1A")?; - Ok(()) + fn move_cursor_up( + stderr_lock: &mut StderrLock, + ) -> Result<(), std::io::Error> { + write!(stderr_lock, "\x1B[1A") } - fn read_stdin_line(stdin_lock: &mut StdinLock) -> Result<(), AnyError> { + fn read_stdin_line(stdin_lock: &mut StdinLock) -> Result<(), std::io::Error> { let mut input = String::new(); stdin_lock.read_line(&mut input)?; Ok(()) diff --git a/runtime/snapshot.rs b/runtime/snapshot.rs index 041132f971..0d81af6e74 100644 --- a/runtime/snapshot.rs +++ b/runtime/snapshot.rs @@ -5,12 +5,12 @@ use crate::ops::bootstrap::SnapshotOptions; use crate::shared::maybe_transpile_source; use crate::shared::runtime; use deno_cache::SqliteBackedCache; -use deno_core::error::AnyError; use deno_core::snapshot::*; use deno_core::v8; use deno_core::Extension; use deno_http::DefaultHttpPropertyExtractor; use deno_io::fs::FsError; +use deno_permissions::PermissionCheckError; use std::borrow::Cow; use std::io::Write; use std::path::Path; @@ -26,7 +26,7 @@ impl deno_websocket::WebSocketPermissions for Permissions { &mut self, _url: &deno_core::url::Url, _api_name: &str, - ) -> Result<(), deno_core::error::AnyError> { + ) -> Result<(), PermissionCheckError> { unreachable!("snapshotting!") } } @@ -42,7 +42,7 @@ impl deno_fetch::FetchPermissions for Permissions { &mut self, _url: &deno_core::url::Url, _api_name: &str, - ) -> Result<(), deno_core::error::AnyError> { + ) -> Result<(), PermissionCheckError> { unreachable!("snapshotting!") } @@ -50,28 +50,26 @@ impl deno_fetch::FetchPermissions for Permissions { &mut self, _p: &'a Path, _api_name: &str, - ) -> Result, AnyError> { + ) -> Result, PermissionCheckError> { unreachable!("snapshotting!") } } impl deno_ffi::FfiPermissions for Permissions { - fn check_partial_no_path( - &mut self, - ) -> Result<(), deno_core::error::AnyError> { + fn check_partial_no_path(&mut self) -> Result<(), PermissionCheckError> { unreachable!("snapshotting!") } fn check_partial_with_path( &mut self, _path: &str, - ) -> Result { + ) -> Result { unreachable!("snapshotting!") } } impl deno_napi::NapiPermissions for Permissions { - fn check(&mut self, _path: &str) -> std::result::Result { + fn check(&mut self, _path: &str) -> Result { unreachable!("snapshotting!") } } @@ -81,20 +79,20 @@ impl deno_node::NodePermissions for Permissions { &mut self, _url: &deno_core::url::Url, _api_name: &str, - ) -> Result<(), deno_core::error::AnyError> { + ) -> Result<(), PermissionCheckError> { unreachable!("snapshotting!") } fn check_read_path<'a>( &mut self, _path: &'a Path, - ) -> Result, AnyError> { + ) -> Result, PermissionCheckError> { unreachable!("snapshotting!") } fn check_read_with_api_name( &mut self, _p: &str, _api_name: Option<&str>, - ) -> Result { + ) -> Result { unreachable!("snapshotting!") } fn query_read_all(&mut self) -> bool { @@ -104,14 +102,14 @@ impl deno_node::NodePermissions for Permissions { &mut self, _p: &str, _api_name: Option<&str>, - ) -> Result { + ) -> Result { unreachable!("snapshotting!") } fn check_sys( &mut self, _kind: &str, _api_name: &str, - ) -> Result<(), deno_core::error::AnyError> { + ) -> Result<(), PermissionCheckError> { unreachable!("snapshotting!") } } @@ -121,7 +119,7 @@ impl deno_net::NetPermissions for Permissions { &mut self, _host: &(T, Option), _api_name: &str, - ) -> Result<(), deno_core::error::AnyError> { + ) -> Result<(), PermissionCheckError> { unreachable!("snapshotting!") } @@ -129,7 +127,7 @@ impl deno_net::NetPermissions for Permissions { &mut self, _p: &str, _api_name: &str, - ) -> Result { + ) -> Result { unreachable!("snapshotting!") } @@ -137,7 +135,7 @@ impl deno_net::NetPermissions for Permissions { &mut self, _p: &str, _api_name: &str, - ) -> Result { + ) -> Result { unreachable!("snapshotting!") } @@ -145,7 +143,7 @@ impl deno_net::NetPermissions for Permissions { &mut self, _p: &'a Path, _api_name: &str, - ) -> Result, AnyError> { + ) -> Result, PermissionCheckError> { unreachable!("snapshotting!") } } @@ -158,7 +156,7 @@ impl deno_fs::FsPermissions for Permissions { _write: bool, _path: &'a Path, _api_name: &str, - ) -> Result, FsError> { + ) -> Result, FsError> { unreachable!("snapshotting!") } @@ -166,11 +164,14 @@ impl deno_fs::FsPermissions for Permissions { &mut self, _path: &str, _api_name: &str, - ) -> Result { + ) -> Result { unreachable!("snapshotting!") } - fn check_read_all(&mut self, _api_name: &str) -> Result<(), AnyError> { + fn check_read_all( + &mut self, + _api_name: &str, + ) -> Result<(), PermissionCheckError> { unreachable!("snapshotting!") } @@ -179,7 +180,7 @@ impl deno_fs::FsPermissions for Permissions { _path: &Path, _display: &str, _api_name: &str, - ) -> Result<(), AnyError> { + ) -> Result<(), PermissionCheckError> { unreachable!("snapshotting!") } @@ -187,7 +188,7 @@ impl deno_fs::FsPermissions for Permissions { &mut self, _path: &str, _api_name: &str, - ) -> Result { + ) -> Result { unreachable!("snapshotting!") } @@ -195,11 +196,14 @@ impl deno_fs::FsPermissions for Permissions { &mut self, _path: &str, _api_name: &str, - ) -> Result { + ) -> Result { unreachable!("snapshotting!") } - fn check_write_all(&mut self, _api_name: &str) -> Result<(), AnyError> { + fn check_write_all( + &mut self, + _api_name: &str, + ) -> Result<(), PermissionCheckError> { unreachable!("snapshotting!") } @@ -208,7 +212,7 @@ impl deno_fs::FsPermissions for Permissions { _path: &Path, _display: &str, _api_name: &str, - ) -> Result<(), AnyError> { + ) -> Result<(), PermissionCheckError> { unreachable!("snapshotting!") } @@ -216,7 +220,7 @@ impl deno_fs::FsPermissions for Permissions { &mut self, _path: &'a Path, _api_name: &str, - ) -> Result, AnyError> { + ) -> Result, PermissionCheckError> { unreachable!("snapshotting!") } @@ -224,7 +228,7 @@ impl deno_fs::FsPermissions for Permissions { &mut self, _path: &'a Path, _api_name: &str, - ) -> Result, AnyError> { + ) -> Result, PermissionCheckError> { unreachable!("snapshotting!") } } @@ -234,7 +238,7 @@ impl deno_kv::sqlite::SqliteDbHandlerPermissions for Permissions { &mut self, _path: &str, _api_name: &str, - ) -> Result { + ) -> Result { unreachable!("snapshotting!") } @@ -242,7 +246,7 @@ impl deno_kv::sqlite::SqliteDbHandlerPermissions for Permissions { &mut self, _path: &'a Path, _api_name: &str, - ) -> Result, AnyError> { + ) -> Result, PermissionCheckError> { unreachable!("snapshotting!") } } diff --git a/tests/integration/lsp_tests.rs b/tests/integration/lsp_tests.rs index 2376aebd90..56221a0269 100644 --- a/tests/integration/lsp_tests.rs +++ b/tests/integration/lsp_tests.rs @@ -6396,6 +6396,45 @@ fn lsp_cache_on_save() { client.shutdown(); } +// Regression test for https://github.com/denoland/deno/issues/25999. +#[test] +fn lsp_asset_document_dom_code_action() { + let context = TestContextBuilder::new().use_temp_cwd().build(); + let temp_dir = context.temp_dir(); + temp_dir.write( + "deno.json", + json!({ + "compilerOptions": { + "lib": ["deno.window", "dom"], + }, + }) + .to_string(), + ); + let mut client = context.new_lsp_command().build(); + client.initialize_default(); + client.did_open(json!({ + "textDocument": { + "uri": temp_dir.url().join("file.ts").unwrap(), + "languageId": "typescript", + "version": 1, + "text": r#""#, + }, + })); + let res = client.write_request( + "textDocument/codeAction", + json!({ + "textDocument": { "uri": "asset:///lib.dom.d.ts" }, + "range": { + "start": { "line": 0, "character": 0 }, + "end": { "line": 0, "character": 0 }, + }, + "context": { "diagnostics": [], "only": ["quickfix"] }, + }), + ); + assert_eq!(res, json!(null)); + client.shutdown(); +} + // Regression test for https://github.com/denoland/deno/issues/22122. #[test] fn lsp_cache_then_definition() { diff --git a/tests/integration/watcher_tests.rs b/tests/integration/watcher_tests.rs index d4705b8d53..e8f264632d 100644 --- a/tests/integration/watcher_tests.rs +++ b/tests/integration/watcher_tests.rs @@ -572,16 +572,19 @@ async fn serve_watch_all() { let main_file_to_watch = t.path().join("main_file_to_watch.js"); main_file_to_watch.write( "export default { - fetch(_request: Request) { + fetch(_request) { return new Response(\"aaaaaaqqq!\"); }, };", ); + let another_file = t.path().join("another_file.js"); + another_file.write(""); + let mut child = util::deno_cmd() .current_dir(t.path()) .arg("serve") - .arg("--watch=another_file.js") + .arg(format!("--watch={another_file}")) .arg("-L") .arg("debug") .arg(&main_file_to_watch) @@ -596,7 +599,7 @@ async fn serve_watch_all() { // Change content of the file main_file_to_watch.write( "export default { - fetch(_request: Request) { + fetch(_request) { return new Response(\"aaaaaaqqq123!\"); }, };", @@ -604,18 +607,20 @@ async fn serve_watch_all() { wait_contains("Restarting", &mut stderr_lines).await; wait_for_watcher("main_file_to_watch.js", &mut stderr_lines).await; - let another_file = t.path().join("another_file.js"); another_file.write("export const foo = 0;"); // Confirm that the added file is watched as well wait_contains("Restarting", &mut stderr_lines).await; + wait_for_watcher("main_file_to_watch.js", &mut stderr_lines).await; main_file_to_watch .write("import { foo } from './another_file.js'; console.log(foo);"); wait_contains("Restarting", &mut stderr_lines).await; + wait_for_watcher("main_file_to_watch.js", &mut stderr_lines).await; wait_contains("0", &mut stdout_lines).await; another_file.write("export const foo = 42;"); wait_contains("Restarting", &mut stderr_lines).await; + wait_for_watcher("main_file_to_watch.js", &mut stderr_lines).await; wait_contains("42", &mut stdout_lines).await; // Confirm that watch continues even with wrong syntax error @@ -623,10 +628,11 @@ async fn serve_watch_all() { wait_contains("Restarting", &mut stderr_lines).await; wait_contains("error:", &mut stderr_lines).await; + wait_for_watcher("main_file_to_watch.js", &mut stderr_lines).await; main_file_to_watch.write( "export default { - fetch(_request: Request) { + fetch(_request) { return new Response(\"aaaaaaqqq!\"); }, };", diff --git a/tests/node_compat/test/fixtures/child-process-spawn-node.js b/tests/node_compat/test/fixtures/child-process-spawn-node.js index d403aabf91..7112567e01 100644 --- a/tests/node_compat/test/fixtures/child-process-spawn-node.js +++ b/tests/node_compat/test/fixtures/child-process-spawn-node.js @@ -1,5 +1,6 @@ const assert = require("assert"); const debug = require('util').debuglog('test'); +const process = require("process"); function onmessage(m) { debug("CHILD got message:", m); diff --git a/tests/registry/jsr/@denotest/unstable/1.0.0-beta.1/mod.ts b/tests/registry/jsr/@denotest/unstable/1.0.0-beta.1/mod.ts new file mode 100644 index 0000000000..de63686189 --- /dev/null +++ b/tests/registry/jsr/@denotest/unstable/1.0.0-beta.1/mod.ts @@ -0,0 +1,3 @@ +export function doThing() { + return "thing"; +} \ No newline at end of file diff --git a/tests/registry/jsr/@denotest/unstable/1.0.0-beta.1_meta.json b/tests/registry/jsr/@denotest/unstable/1.0.0-beta.1_meta.json new file mode 100644 index 0000000000..631a18d0e5 --- /dev/null +++ b/tests/registry/jsr/@denotest/unstable/1.0.0-beta.1_meta.json @@ -0,0 +1,5 @@ +{ + "exports": { + ".": "./mod.ts" + } +} diff --git a/tests/registry/jsr/@denotest/unstable/1.0.0-beta.2/mod.ts b/tests/registry/jsr/@denotest/unstable/1.0.0-beta.2/mod.ts new file mode 100644 index 0000000000..4e599641ef --- /dev/null +++ b/tests/registry/jsr/@denotest/unstable/1.0.0-beta.2/mod.ts @@ -0,0 +1,3 @@ +export function doThing() { + return "thing2"; +} \ No newline at end of file diff --git a/tests/registry/jsr/@denotest/unstable/1.0.0-beta.2_meta.json b/tests/registry/jsr/@denotest/unstable/1.0.0-beta.2_meta.json new file mode 100644 index 0000000000..631a18d0e5 --- /dev/null +++ b/tests/registry/jsr/@denotest/unstable/1.0.0-beta.2_meta.json @@ -0,0 +1,5 @@ +{ + "exports": { + ".": "./mod.ts" + } +} diff --git a/tests/registry/jsr/@denotest/unstable/meta.json b/tests/registry/jsr/@denotest/unstable/meta.json new file mode 100644 index 0000000000..7c5c9971ef --- /dev/null +++ b/tests/registry/jsr/@denotest/unstable/meta.json @@ -0,0 +1,6 @@ +{ + "versions": { + "1.0.0-beta.1": {}, + "1.0.0-beta.2": {} + } +} diff --git a/tests/specs/add/only_unstable_versions/__test__.jsonc b/tests/specs/add/only_unstable_versions/__test__.jsonc index d05628b6f5..ea5a270b9c 100644 --- a/tests/specs/add/only_unstable_versions/__test__.jsonc +++ b/tests/specs/add/only_unstable_versions/__test__.jsonc @@ -1,9 +1,22 @@ { "tempDir": true, - "steps": [ - { - "args": "add npm:@denotest/unstable", - "output": "add.out" + "tests": { + "npm_package": { + "steps": [ + { + "args": "add npm:@denotest/unstable", + "output": "add.out" + } + ] + }, + "jsr_package": { + "steps": [ + { + "args": "add jsr:@denotest/unstable", + "output": "add_jsr.out", + "exitCode": 1 + } + ] } - ] + } } diff --git a/tests/specs/add/only_unstable_versions/add_jsr.out b/tests/specs/add/only_unstable_versions/add_jsr.out new file mode 100644 index 0000000000..95f0630bf3 --- /dev/null +++ b/tests/specs/add/only_unstable_versions/add_jsr.out @@ -0,0 +1 @@ +error: jsr:@denotest/unstable has only pre-release versions available. Try specifying a version: `deno add jsr:@denotest/unstable@^1.0.0-beta.2` diff --git a/tests/specs/fmt/yaml/__test__.jsonc b/tests/specs/fmt/yaml/__test__.jsonc index 499b4144b7..71a2e06827 100644 --- a/tests/specs/fmt/yaml/__test__.jsonc +++ b/tests/specs/fmt/yaml/__test__.jsonc @@ -8,6 +8,30 @@ "well_formatted": { "args": "fmt --check well_formatted.yml", "output": "Checked 1 file\n" + }, + "ignore_line": { + "args": "fmt --check ignore_line.yml", + "output": "Checked 1 file\n" + }, + "ignore_file": { + "args": "fmt ignore_file.yaml", + "output": "Checked 1 file\n" + }, + "ignore_file2": { + "args": "fmt ignore_file2.yaml", + "output": "Checked 1 file\n" + }, + "ignore_file3": { + "args": "fmt ignore_file3.yaml", + "output": "Checked 1 file\n" + }, + "ignore_file4": { + "args": "fmt ignore_file4.yaml", + "output": "Checked 1 file\n" + }, + "wrong_file_ignore": { + "args": "fmt wrong_file_ignore.yaml", + "output": "wrong_file_ignore.out" } } } diff --git a/tests/specs/fmt/yaml/ignore_file.yaml b/tests/specs/fmt/yaml/ignore_file.yaml new file mode 100644 index 0000000000..c724c7bde5 --- /dev/null +++ b/tests/specs/fmt/yaml/ignore_file.yaml @@ -0,0 +1,2 @@ +# deno-fmt-ignore-file +{{something crazy \ No newline at end of file diff --git a/tests/specs/fmt/yaml/ignore_file2.yaml b/tests/specs/fmt/yaml/ignore_file2.yaml new file mode 100644 index 0000000000..a2053533ef --- /dev/null +++ b/tests/specs/fmt/yaml/ignore_file2.yaml @@ -0,0 +1,2 @@ +#deno-fmt-ignore-file +{{something crazy \ No newline at end of file diff --git a/tests/specs/fmt/yaml/ignore_file3.yaml b/tests/specs/fmt/yaml/ignore_file3.yaml new file mode 100644 index 0000000000..a3927a7b19 --- /dev/null +++ b/tests/specs/fmt/yaml/ignore_file3.yaml @@ -0,0 +1,5 @@ +# Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor +# incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, +# quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +# deno-fmt-ignore-file +{{something crazy \ No newline at end of file diff --git a/tests/specs/fmt/yaml/ignore_file4.yaml b/tests/specs/fmt/yaml/ignore_file4.yaml new file mode 100644 index 0000000000..c32fe9f2be --- /dev/null +++ b/tests/specs/fmt/yaml/ignore_file4.yaml @@ -0,0 +1,2 @@ +# deno-fmt-ignore-file Because this is templated yaml file +{{something crazy \ No newline at end of file diff --git a/tests/specs/fmt/yaml/ignore_line.yml b/tests/specs/fmt/yaml/ignore_line.yml new file mode 100644 index 0000000000..490adcb5f7 --- /dev/null +++ b/tests/specs/fmt/yaml/ignore_line.yml @@ -0,0 +1,2 @@ +# deno-fmt-ignore +- Test diff --git a/tests/specs/fmt/yaml/wrong_file_ignore.out b/tests/specs/fmt/yaml/wrong_file_ignore.out new file mode 100644 index 0000000000..065dc98db0 --- /dev/null +++ b/tests/specs/fmt/yaml/wrong_file_ignore.out @@ -0,0 +1,7 @@ +Error formatting: [WILDCARD]wrong_file_ignore.yaml + parse error at line 5, column 1 + | +5 | {{something crazy + | ^ + +Checked 1 file diff --git a/tests/specs/fmt/yaml/wrong_file_ignore.yaml b/tests/specs/fmt/yaml/wrong_file_ignore.yaml new file mode 100644 index 0000000000..8019d989d3 --- /dev/null +++ b/tests/specs/fmt/yaml/wrong_file_ignore.yaml @@ -0,0 +1,5 @@ +# File ignore directive only works if it's in the first cluster +# of comment, ie. there are no empty lines after the first n-leading lines. + +# deno-fmt-ignore-file +{{something crazy \ No newline at end of file diff --git a/tests/specs/run/workspaces/wildcard/__test__.jsonc b/tests/specs/run/workspaces/wildcard/__test__.jsonc new file mode 100644 index 0000000000..ea467e51b4 --- /dev/null +++ b/tests/specs/run/workspaces/wildcard/__test__.jsonc @@ -0,0 +1,5 @@ +{ + "args": "run -L debug -A main.ts", + "output": "main.out", + "tempDir": true +} diff --git a/tests/specs/run/workspaces/wildcard/deno.json b/tests/specs/run/workspaces/wildcard/deno.json new file mode 100644 index 0000000000..8c39ec6564 --- /dev/null +++ b/tests/specs/run/workspaces/wildcard/deno.json @@ -0,0 +1,8 @@ +{ + "workspace": [ + "./packages/*" + ], + "imports": { + "chalk": "npm:chalk" + } +} diff --git a/tests/specs/run/workspaces/wildcard/main.out b/tests/specs/run/workspaces/wildcard/main.out new file mode 100644 index 0000000000..72165dbdea --- /dev/null +++ b/tests/specs/run/workspaces/wildcard/main.out @@ -0,0 +1,22 @@ +[WILDCARD]Workspace config generated this import map { + "imports": { + "chalk": "npm:chalk", + "chalk/": "npm:/chalk/" + }, + "scopes": { + "./packages/bar/": { + "@/": "./packages/bar/", + "secret_mod/": "./packages/bar/some_mod/" + }, + "./packages/foo/": { + "~/": "./packages/foo/", + "foo/": "./packages/foo/bar/" + } + } +} +[WILDCARD] +hello from foo +buzz from foo +hello from bar +buzz from bar +[Function: chalk][WILDCARD] \ No newline at end of file diff --git a/tests/specs/run/workspaces/wildcard/main.ts b/tests/specs/run/workspaces/wildcard/main.ts new file mode 100644 index 0000000000..e69143370d --- /dev/null +++ b/tests/specs/run/workspaces/wildcard/main.ts @@ -0,0 +1,5 @@ +import chalk from "chalk"; +import "./packages/foo/mod.ts"; +import "./packages/bar/mod.ts"; + +console.log(chalk); diff --git a/tests/specs/run/workspaces/wildcard/packages/bar/deno.json b/tests/specs/run/workspaces/wildcard/packages/bar/deno.json new file mode 100644 index 0000000000..ef3bfc37af --- /dev/null +++ b/tests/specs/run/workspaces/wildcard/packages/bar/deno.json @@ -0,0 +1,8 @@ +{ + "name": "asdfasdfasdf", + "version": "0.0.0", + "imports": { + "@/": "./", + "secret_mod/": "./some_mod/" + } +} diff --git a/tests/specs/run/workspaces/wildcard/packages/bar/fizz/buzz.ts b/tests/specs/run/workspaces/wildcard/packages/bar/fizz/buzz.ts new file mode 100644 index 0000000000..f88d62fcc8 --- /dev/null +++ b/tests/specs/run/workspaces/wildcard/packages/bar/fizz/buzz.ts @@ -0,0 +1 @@ +export const buzz = "buzz from bar"; diff --git a/tests/specs/run/workspaces/wildcard/packages/bar/mod.ts b/tests/specs/run/workspaces/wildcard/packages/bar/mod.ts new file mode 100644 index 0000000000..6f898e3898 --- /dev/null +++ b/tests/specs/run/workspaces/wildcard/packages/bar/mod.ts @@ -0,0 +1,5 @@ +import { hello } from "secret_mod/hello.ts"; +import { buzz } from "@/fizz/buzz.ts"; + +console.log(hello); +console.log(buzz); diff --git a/tests/specs/run/workspaces/wildcard/packages/bar/some_mod/hello.ts b/tests/specs/run/workspaces/wildcard/packages/bar/some_mod/hello.ts new file mode 100644 index 0000000000..1013de8d2e --- /dev/null +++ b/tests/specs/run/workspaces/wildcard/packages/bar/some_mod/hello.ts @@ -0,0 +1 @@ +export const hello = "hello from bar"; diff --git a/tests/specs/run/workspaces/wildcard/packages/foo/bar/hello.ts b/tests/specs/run/workspaces/wildcard/packages/foo/bar/hello.ts new file mode 100644 index 0000000000..c8a7e57c4d --- /dev/null +++ b/tests/specs/run/workspaces/wildcard/packages/foo/bar/hello.ts @@ -0,0 +1 @@ +export const hello = "hello from foo"; diff --git a/tests/specs/run/workspaces/wildcard/packages/foo/deno.json b/tests/specs/run/workspaces/wildcard/packages/foo/deno.json new file mode 100644 index 0000000000..46d84f06fa --- /dev/null +++ b/tests/specs/run/workspaces/wildcard/packages/foo/deno.json @@ -0,0 +1,8 @@ +{ + "name": "qwerqwer", + "version": "0.0.0", + "imports": { + "~/": "./", + "foo/": "./bar/" + } +} diff --git a/tests/specs/run/workspaces/wildcard/packages/foo/fizz/buzz.ts b/tests/specs/run/workspaces/wildcard/packages/foo/fizz/buzz.ts new file mode 100644 index 0000000000..4e03777d1b --- /dev/null +++ b/tests/specs/run/workspaces/wildcard/packages/foo/fizz/buzz.ts @@ -0,0 +1 @@ +export const buzz = "buzz from foo"; diff --git a/tests/specs/run/workspaces/wildcard/packages/foo/mod.ts b/tests/specs/run/workspaces/wildcard/packages/foo/mod.ts new file mode 100644 index 0000000000..d7b16dcc05 --- /dev/null +++ b/tests/specs/run/workspaces/wildcard/packages/foo/mod.ts @@ -0,0 +1,5 @@ +import { hello } from "foo/hello.ts"; +import { buzz } from "~/fizz/buzz.ts"; + +console.log(hello); +console.log(buzz); diff --git a/tests/unit/globals_test.ts b/tests/unit/globals_test.ts index 45a0458357..6de228e1c9 100644 --- a/tests/unit/globals_test.ts +++ b/tests/unit/globals_test.ts @@ -1,4 +1,5 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. +// deno-lint-ignore-file no-node-globals import { assert, diff --git a/tools/lint.js b/tools/lint.js index 1f3f56498b..b591cae0ba 100755 --- a/tools/lint.js +++ b/tools/lint.js @@ -56,12 +56,13 @@ async function dlint() { ":!:cli/tsc/compiler.d.ts", ":!:runtime/examples/", ":!:target/", + ":!:tests/ffi/tests/test.js", ":!:tests/registry/**", ":!:tests/specs/**", ":!:tests/testdata/**", ":!:tests/unit_node/testdata/**", - ":!:tests/wpt/suite/**", ":!:tests/wpt/runner/**", + ":!:tests/wpt/suite/**", ]); if (!sourceFiles.length) { diff --git a/tools/util.js b/tools/util.js index 99133628ce..8c7cea15d8 100644 --- a/tools/util.js +++ b/tools/util.js @@ -11,7 +11,7 @@ export { delay } from "@std/async/delay"; // [toolName] --version output const versions = { - "dlint": "dlint 0.60.0", + "dlint": "dlint 0.68.0", }; const compressed = new Set(["ld64.lld", "rcodesign"]); @@ -178,7 +178,7 @@ export function getPrebuiltToolPath(toolName) { return join(PREBUILT_TOOL_DIR, toolName + executableSuffix); } -const commitId = "b8aac22e0cd7c1c6557a56a813fe0c25486fafee"; +const commitId = "7a3a6fee951b3381c59aa4c907274957f324ce8c"; const downloadUrl = `https://raw.githubusercontent.com/denoland/deno_third_party/${commitId}/prebuilt/${platformDirName}`;