From d95f06f20b97839d9a2aaeb590f4ed30b6383b0f Mon Sep 17 00:00:00 2001 From: Nayeem Rahman Date: Mon, 4 Nov 2024 16:36:21 +0000 Subject: [PATCH 01/14] perf(lsp): don't walk coverage directory (#26715) --- cli/lsp/language_server.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) 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 From fb1d33a7111e45e9b414cfe922a5db5ee4daf3ea Mon Sep 17 00:00:00 2001 From: Kenta Moriuchi Date: Tue, 5 Nov 2024 02:17:11 +0900 Subject: [PATCH 02/14] chore: update dlint to v0.68.0 for internal (#26711) --- cli/bench/encode_into.js | 3 +-- cli/bench/getrandom.js | 3 +-- cli/bench/op_now.js | 3 +-- cli/bench/secure_curves.js | 3 +-- cli/bench/tty.js | 3 +-- cli/bench/url_parse.js | 3 +-- cli/bench/write_file.js | 3 +-- ext/node/polyfills/_fs/_fs_common.ts | 1 + ext/node/polyfills/_fs/_fs_open.ts | 4 ++-- ext/node/polyfills/_fs/_fs_readv.ts | 1 + ext/node/polyfills/internal/crypto/keygen.ts | 1 + ext/node/polyfills/internal/crypto/random.ts | 1 + ext/node/polyfills/internal_binding/http_parser.ts | 1 + ext/node/polyfills/vm.js | 1 + runtime/js/99_main.js | 1 + tests/node_compat/test/fixtures/child-process-spawn-node.js | 1 + tests/unit/globals_test.ts | 1 + tools/lint.js | 3 ++- tools/util.js | 4 ++-- 19 files changed, 22 insertions(+), 19 deletions(-) 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/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/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/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/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}`; From fe9f0ee5934871175758857899fe64e56c397fd5 Mon Sep 17 00:00:00 2001 From: Leo Kettmeir Date: Mon, 4 Nov 2024 09:17:21 -0800 Subject: [PATCH 03/14] refactor(runtime/permissions): use concrete error types (#26464) --- Cargo.lock | 1 + cli/args/flags.rs | 3 +- cli/args/flags_net.rs | 2 +- cli/npm/byonm.rs | 2 +- cli/npm/managed/resolvers/common.rs | 2 +- cli/tools/info.rs | 10 +- ext/fetch/lib.rs | 23 +- ext/ffi/call.rs | 14 +- ext/ffi/callback.rs | 6 +- ext/ffi/dlfcn.rs | 6 +- ext/ffi/lib.rs | 11 +- ext/ffi/repr.rs | 86 +-- ext/fs/lib.rs | 50 +- ext/fs/ops.rs | 217 +++----- ext/kv/remote.rs | 13 +- ext/kv/sqlite.rs | 12 +- ext/napi/lib.rs | 15 +- ext/net/lib.rs | 17 +- ext/net/ops.rs | 36 +- ext/node/lib.rs | 34 +- ext/node/ops/fs.rs | 35 +- ext/node/ops/http.rs | 4 +- ext/node/ops/os/mod.rs | 14 +- ext/websocket/lib.rs | 18 +- runtime/errors.rs | 147 ++++-- runtime/ops/fs_events.rs | 5 +- runtime/ops/os/mod.rs | 23 +- runtime/ops/permissions.rs | 43 +- runtime/ops/process.rs | 24 +- runtime/ops/worker_host.rs | 2 +- runtime/permissions.rs | 44 +- runtime/permissions/Cargo.toml | 1 + runtime/permissions/lib.rs | 776 ++++++++++++++++++---------- runtime/permissions/prompter.rs | 69 ++- runtime/snapshot.rs | 64 +-- 35 files changed, 999 insertions(+), 830 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 8c7b35ca9f..a8a501716c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1972,6 +1972,7 @@ dependencies = [ "once_cell", "percent-encoding", "serde", + "thiserror", "which 4.4.2", "winapi", ] 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/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/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/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/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/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!") } } From 9a39a98b57de4183e054ab170592c85c97fac183 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Mon, 4 Nov 2024 17:57:29 +0000 Subject: [PATCH 04/14] fix(fmt): ignore file directive for YAML files (#26717) Closes https://github.com/denoland/deno/issues/26712 Support `# deno-fmt-ignore-file` directive for YAML files. Also added tests for single line ignores. --- cli/tools/fmt.rs | 15 +++++++++++++ tests/specs/fmt/yaml/__test__.jsonc | 24 +++++++++++++++++++++ tests/specs/fmt/yaml/ignore_file.yaml | 2 ++ tests/specs/fmt/yaml/ignore_file2.yaml | 2 ++ tests/specs/fmt/yaml/ignore_file3.yaml | 5 +++++ tests/specs/fmt/yaml/ignore_file4.yaml | 2 ++ tests/specs/fmt/yaml/ignore_line.yml | 2 ++ tests/specs/fmt/yaml/wrong_file_ignore.out | 7 ++++++ tests/specs/fmt/yaml/wrong_file_ignore.yaml | 5 +++++ 9 files changed, 64 insertions(+) create mode 100644 tests/specs/fmt/yaml/ignore_file.yaml create mode 100644 tests/specs/fmt/yaml/ignore_file2.yaml create mode 100644 tests/specs/fmt/yaml/ignore_file3.yaml create mode 100644 tests/specs/fmt/yaml/ignore_file4.yaml create mode 100644 tests/specs/fmt/yaml/ignore_line.yml create mode 100644 tests/specs/fmt/yaml/wrong_file_ignore.out create mode 100644 tests/specs/fmt/yaml/wrong_file_ignore.yaml 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/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 From d55e30f41855d40b36019f347a15b6c8984eb3e9 Mon Sep 17 00:00:00 2001 From: Scott Twiname Date: Tue, 5 Nov 2024 07:20:34 +1300 Subject: [PATCH 05/14] fix(types): missing `import` permission on `PermissionOptionsObject` (#26627) --- cli/tsc/dts/lib.deno.ns.d.ts | 37 ++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) 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. From d67765b0b48d711cd0878e168ccb1e28178c4006 Mon Sep 17 00:00:00 2001 From: Nayeem Rahman Date: Mon, 4 Nov 2024 20:01:31 +0000 Subject: [PATCH 06/14] fix(lsp): scope attribution for lazily loaded assets (#26699) --- cli/tsc/99_main_compiler.js | 17 +++++++++------ tests/integration/lsp_tests.rs | 39 ++++++++++++++++++++++++++++++++++ 2 files changed, 50 insertions(+), 6 deletions(-) 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/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() { From bb3ca84e6d8f4f9f2a30de93bd87e243048a8d74 Mon Sep 17 00:00:00 2001 From: David Sherret Date: Mon, 4 Nov 2024 15:40:05 -0500 Subject: [PATCH 07/14] fix(fmt): do not panic for jsx ignore container followed by jsx text (#26723) --- .dprint.json | 2 +- Cargo.lock | 4 ++-- cli/Cargo.toml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) 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/Cargo.lock b/Cargo.lock index a8a501716c..40b0bc171b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2609,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", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index a3e2b71f00..b4f2e614f0 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -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 From 84aee0be9aac53499a757973a8a2054d2a44e479 Mon Sep 17 00:00:00 2001 From: Nathan Whitaker <17734409+nathanwhit@users.noreply.github.com> Date: Mon, 4 Nov 2024 13:08:29 -0800 Subject: [PATCH 08/14] fix(ext/node): add `findSourceMap` to the default export of `node:module` (#26720) Next.js 15.0.2 tries to use this and errors out --- ext/node/polyfills/01_require.js | 2 ++ 1 file changed, 2 insertions(+) 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 From 051552172c07e42bf666d04863bc511667471509 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Mon, 4 Nov 2024 23:42:18 +0000 Subject: [PATCH 09/14] fix(workspace): support wildcard packages (#26568) This commit adds support for wildcard packages in `workspace` configuration option in `deno.json`. This is now supported: ``` { "workspace": [ "./packages/*" ] } ``` Closes https://github.com/denoland/deno/issues/25783 --- Cargo.lock | 4 ++-- cli/Cargo.toml | 2 +- .../run/workspaces/wildcard/__test__.jsonc | 5 +++++ tests/specs/run/workspaces/wildcard/deno.json | 8 +++++++ tests/specs/run/workspaces/wildcard/main.out | 22 +++++++++++++++++++ tests/specs/run/workspaces/wildcard/main.ts | 5 +++++ .../wildcard/packages/bar/deno.json | 8 +++++++ .../wildcard/packages/bar/fizz/buzz.ts | 1 + .../workspaces/wildcard/packages/bar/mod.ts | 5 +++++ .../wildcard/packages/bar/some_mod/hello.ts | 1 + .../wildcard/packages/foo/bar/hello.ts | 1 + .../wildcard/packages/foo/deno.json | 8 +++++++ .../wildcard/packages/foo/fizz/buzz.ts | 1 + .../workspaces/wildcard/packages/foo/mod.ts | 5 +++++ 14 files changed, 73 insertions(+), 3 deletions(-) create mode 100644 tests/specs/run/workspaces/wildcard/__test__.jsonc create mode 100644 tests/specs/run/workspaces/wildcard/deno.json create mode 100644 tests/specs/run/workspaces/wildcard/main.out create mode 100644 tests/specs/run/workspaces/wildcard/main.ts create mode 100644 tests/specs/run/workspaces/wildcard/packages/bar/deno.json create mode 100644 tests/specs/run/workspaces/wildcard/packages/bar/fizz/buzz.ts create mode 100644 tests/specs/run/workspaces/wildcard/packages/bar/mod.ts create mode 100644 tests/specs/run/workspaces/wildcard/packages/bar/some_mod/hello.ts create mode 100644 tests/specs/run/workspaces/wildcard/packages/foo/bar/hello.ts create mode 100644 tests/specs/run/workspaces/wildcard/packages/foo/deno.json create mode 100644 tests/specs/run/workspaces/wildcard/packages/foo/fizz/buzz.ts create mode 100644 tests/specs/run/workspaces/wildcard/packages/foo/mod.ts diff --git a/Cargo.lock b/Cargo.lock index 40b0bc171b..c22d49c41c 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", diff --git a/cli/Cargo.toml b/cli/Cargo.toml index b4f2e614f0..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" } 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); From 25ed90baaec420e6496593406494d3747d9a6db7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bartek=20Iwa=C5=84czuk?= Date: Mon, 4 Nov 2024 23:56:09 +0000 Subject: [PATCH 10/14] ci: use self-hosted mac arm runner for building on tags (#26727) --- .github/workflows/ci.generate.ts | 4 +++- .github/workflows/ci.yml | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) 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'') }}' From 44eca0505c35201c6b67ba073834402b7681914f Mon Sep 17 00:00:00 2001 From: Nathan Whitaker <17734409+nathanwhit@users.noreply.github.com> Date: Mon, 4 Nov 2024 17:09:17 -0800 Subject: [PATCH 11/14] chore: fix serve_watch_all test (#26725) It's been failing a ton lately, it looks like the test is just incorrectly using TS syntax in a JS file https://github.com/denoland/deno/actions/runs/11672972415/job/32502710624?pr=26724#step:43:2791 I'm not really sure how this ever passes --- tests/integration/watcher_tests.rs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) 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!\"); }, };", From 706b1dfcea8ab6bf7d155893ab795669107516a8 Mon Sep 17 00:00:00 2001 From: Nathan Whitaker <17734409+nathanwhit@users.noreply.github.com> Date: Mon, 4 Nov 2024 18:45:00 -0800 Subject: [PATCH 12/14] fix(add): better error message when adding package that only has pre-release versions (#26724) Fixes https://github.com/denoland/deno/issues/26597 A small refactor as well to reduce some code duplication --- cli/tools/registry/pm.rs | 205 +++++++++++++----- .../@denotest/unstable/1.0.0-beta.1/mod.ts | 3 + .../@denotest/unstable/1.0.0-beta.1_meta.json | 5 + .../@denotest/unstable/1.0.0-beta.2/mod.ts | 3 + .../@denotest/unstable/1.0.0-beta.2_meta.json | 5 + .../registry/jsr/@denotest/unstable/meta.json | 6 + .../add/only_unstable_versions/__test__.jsonc | 23 +- .../add/only_unstable_versions/add_jsr.out | 1 + 8 files changed, 187 insertions(+), 64 deletions(-) create mode 100644 tests/registry/jsr/@denotest/unstable/1.0.0-beta.1/mod.ts create mode 100644 tests/registry/jsr/@denotest/unstable/1.0.0-beta.1_meta.json create mode 100644 tests/registry/jsr/@denotest/unstable/1.0.0-beta.2/mod.ts create mode 100644 tests/registry/jsr/@denotest/unstable/1.0.0-beta.2_meta.json create mode 100644 tests/registry/jsr/@denotest/unstable/meta.json create mode 100644 tests/specs/add/only_unstable_versions/add_jsr.out 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/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` From 383cb85a730e42a2951ead84233ccef0ed3a23e8 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Tue, 5 Nov 2024 09:13:54 +0530 Subject: [PATCH 13/14] fix: op_run_microtasks crash (#26718) Upgrade deno_core to 0.318.0 Fixes https://github.com/denoland/deno_core/issues/951 Fixes https://github.com/denoland/deno/issues/26468 --- Cargo.lock | 12 ++++++------ Cargo.toml | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index c22d49c41c..73d417baae 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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", @@ -6170,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" From f9a05068d6de247574fb764044a446d1d7ed2e9b Mon Sep 17 00:00:00 2001 From: Nathan Whitaker <17734409+nathanwhit@users.noreply.github.com> Date: Mon, 4 Nov 2024 20:16:53 -0800 Subject: [PATCH 14/14] fix(install): handle invalid function error, and fallback to junctions regardless of the error (#26730) Fixes #26116. Handle the new error and treat is as lacking permission to make symlinks, but also to make this more robust, just always fall back to junctions no matter what the actual error is. Instead, warn if the error isn't one we've handled, but go on to attempt creating the junction --- cli/npm/managed/resolvers/local.rs | 16 +++++++++++----- cli/util/fs.rs | 4 +++- 2 files changed, 14 insertions(+), 6 deletions(-) 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/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)); } }