feat: permission stack traces in ops (#26938)

This commit improves permission prompts by adding an option
to print a full trace of where the permissions is being requested.

Due to big performance hint of stack trace collection, this is only
enabled when `DENO_TRACE_PERMISSIONS` env var is present.

Closes https://github.com/denoland/deno/issues/20756

---------

Co-authored-by: Bartek Iwańczuk <biwanczuk@gmail.com>
This commit is contained in:
Leo Kettmeir 2024-11-20 13:24:04 -08:00 committed by GitHub
parent 8f72798622
commit cf49599359
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
34 changed files with 294 additions and 181 deletions

View File

@ -1160,25 +1160,26 @@ static ENV_VARIABLES_HELP: &str = cstr!(
<y>Docs:</> <c>https://docs.deno.com/go/env-vars</>
<g>DENO_AUTH_TOKENS</> A semi-colon separated list of bearer tokens and hostnames
to use when fetching remote modules from private repositories
<p(245)>(e.g. "abcde12345@deno.land;54321edcba@github.com")</>
<g>DENO_CERT</> Load certificate authorities from PEM encoded file
<g>DENO_DIR</> Set the cache directory
<g>DENO_INSTALL_ROOT</> Set deno install's output directory
<p(245)>(defaults to $HOME/.deno/bin)</>
<g>DENO_NO_PACKAGE_JSON</> Disables auto-resolution of package.json
<g>DENO_NO_UPDATE_CHECK</> Set to disable checking if a newer Deno version is available
<g>DENO_TLS_CA_STORE</> Comma-separated list of order dependent certificate stores.
Possible values: "system", "mozilla".
<p(245)>(defaults to "mozilla")</>
<g>HTTP_PROXY</> Proxy address for HTTP requests
<p(245)>(module downloads, fetch)</>
<g>HTTPS_PROXY</> Proxy address for HTTPS requests
<p(245)>(module downloads, fetch)</>
<g>NO_COLOR</> Set to disable color
<g>NO_PROXY</> Comma-separated list of hosts which do not use a proxy
<p(245)>(module downloads, fetch)</>
<g>NPM_CONFIG_REGISTRY</> URL to use for the npm registry."#
to use when fetching remote modules from private repositories
<p(245)>(e.g. "abcde12345@deno.land;54321edcba@github.com")</>
<g>DENO_CERT</> Load certificate authorities from PEM encoded file
<g>DENO_DIR</> Set the cache directory
<g>DENO_INSTALL_ROOT</> Set deno install's output directory
<p(245)>(defaults to $HOME/.deno/bin)</>
<g>DENO_NO_PACKAGE_JSON</> Disables auto-resolution of package.json
<g>DENO_NO_UPDATE_CHECK</> Set to disable checking if a newer Deno version is available
<g>DENO_TLS_CA_STORE</> Comma-separated list of order dependent certificate stores.
<g>DENO_TRACE_PERMISSIONS</> Environmental variable to enable stack traces in permission prompts.
Possible values: "system", "mozilla".
<p(245)>(defaults to "mozilla")</>
<g>HTTP_PROXY</> Proxy address for HTTP requests
<p(245)>(module downloads, fetch)</>
<g>HTTPS_PROXY</> Proxy address for HTTPS requests
<p(245)>(module downloads, fetch)</>
<g>NO_COLOR</> Set to disable color
<g>NO_PROXY</> Comma-separated list of hosts which do not use a proxy
<p(245)>(module downloads, fetch)</>
<g>NPM_CONFIG_REGISTRY</> URL to use for the npm registry."#
);
static DENO_HELP: &str = cstr!(
@ -3346,6 +3347,8 @@ fn permission_args(app: Command, requires: Option<&'static str>) -> Command {
<p(245)>--deny-run | --deny-run="whoami,ps"</>
<g>--deny-ffi[=<<PATH>...]</> (Unstable) Deny loading dynamic libraries. Optionally specify denied directories or files.
<p(245)>--deny-ffi | --deny-ffi="./libfoo.so"</>
<g>DENO_TRACE_PERMISSIONS</> Environmental variable to enable stack traces in permission prompts.
<p(245)>DENO_TRACE_PERMISSIONS=1 deno run main.ts</>
"#))
.arg(
{

View File

@ -1912,6 +1912,10 @@ pub fn resolve_no_prompt(flags: &PermissionFlags) -> bool {
flags.no_prompt || has_flag_env_var("DENO_NO_PROMPT")
}
pub fn has_trace_permissions_enabled() -> bool {
has_flag_env_var("DENO_TRACE_PERMISSIONS")
}
pub fn has_flag_env_var(name: &str) -> bool {
let value = env::var(name);
matches!(value.as_ref().map(|s| s.as_str()), Ok("1"))

View File

@ -51,7 +51,7 @@ fn op_bench_get_origin(state: &mut OpState) -> String {
#[derive(Clone)]
struct PermissionsHolder(Uuid, PermissionsContainer);
#[op2]
#[op2(stack_trace)]
#[serde]
pub fn op_pledge_test_permissions(
state: &mut OpState,

View File

@ -46,7 +46,7 @@ deno_core::extension!(deno_test,
#[derive(Clone)]
struct PermissionsHolder(Uuid, PermissionsContainer);
#[op2]
#[op2(stack_trace)]
#[serde]
pub fn op_pledge_test_permissions(
state: &mut OpState,

View File

@ -617,6 +617,8 @@ impl CliMainWorkerFactory {
origin_storage_dir,
stdio,
skip_op_registration: shared.options.skip_op_registration,
enable_stack_trace_arg_in_ops: crate::args::has_trace_permissions_enabled(
),
};
let mut worker = MainWorker::bootstrap_from_options(
@ -813,6 +815,8 @@ fn create_web_worker_callback(
strace_ops: shared.options.strace_ops.clone(),
close_on_idle: args.close_on_idle,
maybe_worker_metadata: args.maybe_worker_metadata,
enable_stack_trace_arg_in_ops: crate::args::has_trace_permissions_enabled(
),
};
WebWorker::bootstrap_from_options(services, options)

View File

@ -397,7 +397,7 @@ impl FetchPermissions for deno_permissions::PermissionsContainer {
}
}
#[op2]
#[op2(stack_trace)]
#[serde]
#[allow(clippy::too_many_arguments)]
pub fn op_fetch<FP>(
@ -866,7 +866,7 @@ fn default_true() -> bool {
true
}
#[op2]
#[op2(stack_trace)]
#[smi]
pub fn op_fetch_custom_client<FP>(
state: &mut OpState,

View File

@ -287,7 +287,7 @@ fn ffi_call(
}
}
#[op2(async)]
#[op2(async, stack_trace)]
#[serde]
pub fn op_ffi_call_ptr_nonblocking<FP>(
scope: &mut v8::HandleScope,
@ -385,7 +385,7 @@ pub fn op_ffi_call_nonblocking(
})
}
#[op2(reentrant)]
#[op2(reentrant, stack_trace)]
#[serde]
pub fn op_ffi_call_ptr<FP>(
scope: &mut v8::HandleScope,

View File

@ -561,7 +561,7 @@ pub struct RegisterCallbackArgs {
result: NativeType,
}
#[op2]
#[op2(stack_trace)]
pub fn op_ffi_unsafe_callback_create<FP, 'scope>(
state: &mut OpState,
scope: &mut v8::HandleScope<'scope>,

View File

@ -124,7 +124,7 @@ pub struct FfiLoadArgs {
symbols: HashMap<String, ForeignSymbol>,
}
#[op2]
#[op2(stack_trace)]
pub fn op_ffi_load<'scope, FP>(
scope: &mut v8::HandleScope<'scope>,
state: Rc<RefCell<OpState>>,

View File

@ -49,7 +49,7 @@ pub enum ReprError {
Permission(#[from] deno_permissions::PermissionCheckError),
}
#[op2(fast)]
#[op2(fast, stack_trace)]
pub fn op_ffi_ptr_create<FP>(
state: &mut OpState,
#[bigint] ptr_number: usize,
@ -63,7 +63,7 @@ where
Ok(ptr_number as *mut c_void)
}
#[op2(fast)]
#[op2(fast, stack_trace)]
pub fn op_ffi_ptr_equals<FP>(
state: &mut OpState,
a: *const c_void,
@ -78,7 +78,7 @@ where
Ok(a == b)
}
#[op2]
#[op2(stack_trace)]
pub fn op_ffi_ptr_of<FP>(
state: &mut OpState,
#[anybuffer] buf: *const u8,
@ -92,7 +92,7 @@ where
Ok(buf as *mut c_void)
}
#[op2(fast)]
#[op2(fast, stack_trace)]
pub fn op_ffi_ptr_of_exact<FP>(
state: &mut OpState,
buf: v8::Local<v8::ArrayBufferView>,
@ -112,7 +112,7 @@ where
Ok(buf.as_ptr() as _)
}
#[op2(fast)]
#[op2(fast, stack_trace)]
pub fn op_ffi_ptr_offset<FP>(
state: &mut OpState,
ptr: *mut c_void,
@ -142,7 +142,7 @@ unsafe extern "C" fn noop_deleter_callback(
) {
}
#[op2(fast)]
#[op2(fast, stack_trace)]
#[bigint]
pub fn op_ffi_ptr_value<FP>(
state: &mut OpState,
@ -157,7 +157,7 @@ where
Ok(ptr as usize)
}
#[op2]
#[op2(stack_trace)]
pub fn op_ffi_get_buf<FP, 'scope>(
scope: &mut v8::HandleScope<'scope>,
state: &mut OpState,
@ -189,7 +189,7 @@ where
Ok(array_buffer)
}
#[op2]
#[op2(stack_trace)]
pub fn op_ffi_buf_copy_into<FP>(
state: &mut OpState,
src: *mut c_void,
@ -219,7 +219,7 @@ where
}
}
#[op2]
#[op2(stack_trace)]
pub fn op_ffi_cstr_read<FP, 'scope>(
scope: &mut v8::HandleScope<'scope>,
state: &mut OpState,
@ -244,7 +244,7 @@ where
Ok(value)
}
#[op2(fast)]
#[op2(fast, stack_trace)]
pub fn op_ffi_read_bool<FP>(
state: &mut OpState,
ptr: *mut c_void,
@ -264,7 +264,7 @@ where
Ok(unsafe { ptr::read_unaligned::<bool>(ptr.offset(offset) as *const bool) })
}
#[op2(fast)]
#[op2(fast, stack_trace)]
pub fn op_ffi_read_u8<FP>(
state: &mut OpState,
ptr: *mut c_void,
@ -286,7 +286,7 @@ where
})
}
#[op2(fast)]
#[op2(fast, stack_trace)]
pub fn op_ffi_read_i8<FP>(
state: &mut OpState,
ptr: *mut c_void,
@ -308,7 +308,7 @@ where
})
}
#[op2(fast)]
#[op2(fast, stack_trace)]
pub fn op_ffi_read_u16<FP>(
state: &mut OpState,
ptr: *mut c_void,
@ -330,7 +330,7 @@ where
})
}
#[op2(fast)]
#[op2(fast, stack_trace)]
pub fn op_ffi_read_i16<FP>(
state: &mut OpState,
ptr: *mut c_void,
@ -352,7 +352,7 @@ where
})
}
#[op2(fast)]
#[op2(fast, stack_trace)]
pub fn op_ffi_read_u32<FP>(
state: &mut OpState,
ptr: *mut c_void,
@ -372,7 +372,7 @@ where
Ok(unsafe { ptr::read_unaligned::<u32>(ptr.offset(offset) as *const u32) })
}
#[op2(fast)]
#[op2(fast, stack_trace)]
pub fn op_ffi_read_i32<FP>(
state: &mut OpState,
ptr: *mut c_void,
@ -392,7 +392,7 @@ where
Ok(unsafe { ptr::read_unaligned::<i32>(ptr.offset(offset) as *const i32) })
}
#[op2(fast)]
#[op2(fast, stack_trace)]
#[bigint]
pub fn op_ffi_read_u64<FP>(
state: &mut OpState,
@ -418,7 +418,7 @@ where
Ok(value)
}
#[op2(fast)]
#[op2(fast, stack_trace)]
#[bigint]
pub fn op_ffi_read_i64<FP>(
state: &mut OpState,
@ -444,7 +444,7 @@ where
Ok(value)
}
#[op2(fast)]
#[op2(fast, stack_trace)]
pub fn op_ffi_read_f32<FP>(
state: &mut OpState,
ptr: *mut c_void,
@ -464,7 +464,7 @@ where
Ok(unsafe { ptr::read_unaligned::<f32>(ptr.offset(offset) as *const f32) })
}
#[op2(fast)]
#[op2(fast, stack_trace)]
pub fn op_ffi_read_f64<FP>(
state: &mut OpState,
ptr: *mut c_void,
@ -484,7 +484,7 @@ where
Ok(unsafe { ptr::read_unaligned::<f64>(ptr.offset(offset) as *const f64) })
}
#[op2(fast)]
#[op2(fast, stack_trace)]
pub fn op_ffi_read_ptr<FP>(
state: &mut OpState,
ptr: *mut c_void,

View File

@ -146,7 +146,7 @@ fn map_permission_error(
}
}
#[op2]
#[op2(stack_trace)]
#[string]
pub fn op_fs_cwd<P>(state: &mut OpState) -> Result<String, FsOpsError>
where
@ -161,7 +161,7 @@ where
Ok(path_str)
}
#[op2(fast)]
#[op2(fast, stack_trace)]
pub fn op_fs_chdir<P>(
state: &mut OpState,
#[string] directory: &str,
@ -188,7 +188,7 @@ where
state.borrow::<FileSystemRc>().umask(mask).context("umask")
}
#[op2]
#[op2(stack_trace)]
#[smi]
pub fn op_fs_open_sync<P>(
state: &mut OpState,
@ -215,7 +215,7 @@ where
Ok(rid)
}
#[op2(async)]
#[op2(async, stack_trace)]
#[smi]
pub async fn op_fs_open_async<P>(
state: Rc<RefCell<OpState>>,
@ -243,7 +243,7 @@ where
Ok(rid)
}
#[op2]
#[op2(stack_trace)]
pub fn op_fs_mkdir_sync<P>(
state: &mut OpState,
#[string] path: String,
@ -266,7 +266,7 @@ where
Ok(())
}
#[op2(async)]
#[op2(async, stack_trace)]
pub async fn op_fs_mkdir_async<P>(
state: Rc<RefCell<OpState>>,
#[string] path: String,
@ -291,7 +291,7 @@ where
Ok(())
}
#[op2(fast)]
#[op2(fast, stack_trace)]
pub fn op_fs_chmod_sync<P>(
state: &mut OpState,
#[string] path: String,
@ -308,7 +308,7 @@ where
Ok(())
}
#[op2(async)]
#[op2(async, stack_trace)]
pub async fn op_fs_chmod_async<P>(
state: Rc<RefCell<OpState>>,
#[string] path: String,
@ -328,7 +328,7 @@ where
Ok(())
}
#[op2]
#[op2(stack_trace)]
pub fn op_fs_chown_sync<P>(
state: &mut OpState,
#[string] path: String,
@ -347,7 +347,7 @@ where
Ok(())
}
#[op2(async)]
#[op2(async, stack_trace)]
pub async fn op_fs_chown_async<P>(
state: Rc<RefCell<OpState>>,
#[string] path: String,
@ -368,7 +368,7 @@ where
Ok(())
}
#[op2(fast)]
#[op2(fast, stack_trace)]
pub fn op_fs_remove_sync<P>(
state: &mut OpState,
#[string] path: &str,
@ -388,7 +388,7 @@ where
Ok(())
}
#[op2(async)]
#[op2(async, stack_trace)]
pub async fn op_fs_remove_async<P>(
state: Rc<RefCell<OpState>>,
#[string] path: String,
@ -419,7 +419,7 @@ where
Ok(())
}
#[op2(fast)]
#[op2(fast, stack_trace)]
pub fn op_fs_copy_file_sync<P>(
state: &mut OpState,
#[string] from: &str,
@ -439,7 +439,7 @@ where
Ok(())
}
#[op2(async)]
#[op2(async, stack_trace)]
pub async fn op_fs_copy_file_async<P>(
state: Rc<RefCell<OpState>>,
#[string] from: String,
@ -463,7 +463,7 @@ where
Ok(())
}
#[op2(fast)]
#[op2(fast, stack_trace)]
pub fn op_fs_stat_sync<P>(
state: &mut OpState,
#[string] path: String,
@ -482,7 +482,7 @@ where
Ok(())
}
#[op2(async)]
#[op2(async, stack_trace)]
#[serde]
pub async fn op_fs_stat_async<P>(
state: Rc<RefCell<OpState>>,
@ -504,7 +504,7 @@ where
Ok(SerializableStat::from(stat))
}
#[op2(fast)]
#[op2(fast, stack_trace)]
pub fn op_fs_lstat_sync<P>(
state: &mut OpState,
#[string] path: String,
@ -523,7 +523,7 @@ where
Ok(())
}
#[op2(async)]
#[op2(async, stack_trace)]
#[serde]
pub async fn op_fs_lstat_async<P>(
state: Rc<RefCell<OpState>>,
@ -545,7 +545,7 @@ where
Ok(SerializableStat::from(stat))
}
#[op2]
#[op2(stack_trace)]
#[string]
pub fn op_fs_realpath_sync<P>(
state: &mut OpState,
@ -568,7 +568,7 @@ where
Ok(path_string)
}
#[op2(async)]
#[op2(async, stack_trace)]
#[string]
pub async fn op_fs_realpath_async<P>(
state: Rc<RefCell<OpState>>,
@ -596,7 +596,7 @@ where
Ok(path_string)
}
#[op2]
#[op2(stack_trace)]
#[serde]
pub fn op_fs_read_dir_sync<P>(
state: &mut OpState,
@ -615,7 +615,7 @@ where
Ok(entries)
}
#[op2(async)]
#[op2(async, stack_trace)]
#[serde]
pub async fn op_fs_read_dir_async<P>(
state: Rc<RefCell<OpState>>,
@ -640,7 +640,7 @@ where
Ok(entries)
}
#[op2(fast)]
#[op2(fast, stack_trace)]
pub fn op_fs_rename_sync<P>(
state: &mut OpState,
#[string] oldpath: String,
@ -661,7 +661,7 @@ where
Ok(())
}
#[op2(async)]
#[op2(async, stack_trace)]
pub async fn op_fs_rename_async<P>(
state: Rc<RefCell<OpState>>,
#[string] oldpath: String,
@ -686,7 +686,7 @@ where
Ok(())
}
#[op2(fast)]
#[op2(fast, stack_trace)]
pub fn op_fs_link_sync<P>(
state: &mut OpState,
#[string] oldpath: &str,
@ -708,7 +708,7 @@ where
Ok(())
}
#[op2(async)]
#[op2(async, stack_trace)]
pub async fn op_fs_link_async<P>(
state: Rc<RefCell<OpState>>,
#[string] oldpath: String,
@ -734,7 +734,7 @@ where
Ok(())
}
#[op2]
#[op2(stack_trace)]
pub fn op_fs_symlink_sync<P>(
state: &mut OpState,
#[string] oldpath: &str,
@ -758,7 +758,7 @@ where
Ok(())
}
#[op2(async)]
#[op2(async, stack_trace)]
pub async fn op_fs_symlink_async<P>(
state: Rc<RefCell<OpState>>,
#[string] oldpath: String,
@ -786,7 +786,7 @@ where
Ok(())
}
#[op2]
#[op2(stack_trace)]
#[string]
pub fn op_fs_read_link_sync<P>(
state: &mut OpState,
@ -806,7 +806,7 @@ where
Ok(target_string)
}
#[op2(async)]
#[op2(async, stack_trace)]
#[string]
pub async fn op_fs_read_link_async<P>(
state: Rc<RefCell<OpState>>,
@ -831,7 +831,7 @@ where
Ok(target_string)
}
#[op2(fast)]
#[op2(fast, stack_trace)]
pub fn op_fs_truncate_sync<P>(
state: &mut OpState,
#[string] path: &str,
@ -851,7 +851,7 @@ where
Ok(())
}
#[op2(async)]
#[op2(async, stack_trace)]
pub async fn op_fs_truncate_async<P>(
state: Rc<RefCell<OpState>>,
#[string] path: String,
@ -875,7 +875,7 @@ where
Ok(())
}
#[op2(fast)]
#[op2(fast, stack_trace)]
pub fn op_fs_utime_sync<P>(
state: &mut OpState,
#[string] path: &str,
@ -896,7 +896,7 @@ where
Ok(())
}
#[op2(async)]
#[op2(async, stack_trace)]
pub async fn op_fs_utime_async<P>(
state: Rc<RefCell<OpState>>,
#[string] path: String,
@ -927,7 +927,7 @@ where
Ok(())
}
#[op2]
#[op2(stack_trace)]
#[string]
pub fn op_fs_make_temp_dir_sync<P>(
state: &mut OpState,
@ -969,7 +969,7 @@ where
.context("tmpdir")
}
#[op2(async)]
#[op2(async, stack_trace)]
#[string]
pub async fn op_fs_make_temp_dir_async<P>(
state: Rc<RefCell<OpState>>,
@ -1015,7 +1015,7 @@ where
.context("tmpdir")
}
#[op2]
#[op2(stack_trace)]
#[string]
pub fn op_fs_make_temp_file_sync<P>(
state: &mut OpState,
@ -1063,7 +1063,7 @@ where
.context("tmpfile")
}
#[op2(async)]
#[op2(async, stack_trace)]
#[string]
pub async fn op_fs_make_temp_file_async<P>(
state: Rc<RefCell<OpState>>,
@ -1235,7 +1235,7 @@ fn tmp_name(
Ok(path)
}
#[op2]
#[op2(stack_trace)]
pub fn op_fs_write_file_sync<P>(
state: &mut OpState,
#[string] path: String,
@ -1261,7 +1261,7 @@ where
Ok(())
}
#[op2(async)]
#[op2(async, stack_trace)]
#[allow(clippy::too_many_arguments)]
pub async fn op_fs_write_file_async<P>(
state: Rc<RefCell<OpState>>,
@ -1315,7 +1315,7 @@ where
Ok(())
}
#[op2]
#[op2(stack_trace)]
#[serde]
pub fn op_fs_read_file_sync<P>(
state: &mut OpState,
@ -1336,7 +1336,7 @@ where
Ok(buf.into())
}
#[op2(async)]
#[op2(async, stack_trace)]
#[serde]
pub async fn op_fs_read_file_async<P>(
state: Rc<RefCell<OpState>>,
@ -1378,7 +1378,7 @@ where
Ok(buf.into())
}
#[op2]
#[op2(stack_trace)]
#[string]
pub fn op_fs_read_file_text_sync<P>(
state: &mut OpState,
@ -1399,7 +1399,7 @@ where
Ok(str)
}
#[op2(async)]
#[op2(async, stack_trace)]
#[string]
pub async fn op_fs_read_file_text_async<P>(
state: Rc<RefCell<OpState>>,

View File

@ -178,7 +178,7 @@ pub enum KvErrorKind {
InvalidRange,
}
#[op2(async)]
#[op2(async, stack_trace)]
#[smi]
async fn op_kv_database_open<DBH>(
state: Rc<RefCell<OpState>>,

View File

@ -530,7 +530,7 @@ static NAPI_LOADED_MODULES: std::sync::LazyLock<
RwLock<HashMap<PathBuf, NapiModuleHandle>>,
> = std::sync::LazyLock::new(|| RwLock::new(HashMap::new()));
#[op2(reentrant)]
#[op2(reentrant, stack_trace)]
fn op_napi_open<NP, 'scope>(
scope: &mut v8::HandleScope<'scope>,
isolate: *mut v8::Isolate,

View File

@ -182,7 +182,7 @@ pub async fn op_net_recv_udp(
Ok((nread, IpAddr::from(remote_addr)))
}
#[op2(async)]
#[op2(async, stack_trace)]
#[number]
pub async fn op_net_send_udp<NP>(
state: Rc<RefCell<OpState>>,
@ -343,7 +343,7 @@ pub async fn op_net_set_multi_ttl_udp(
Ok(())
}
#[op2(async)]
#[op2(async, stack_trace)]
#[serde]
pub async fn op_net_connect_tcp<NP>(
state: Rc<RefCell<OpState>>,
@ -401,7 +401,7 @@ impl Resource for UdpSocketResource {
}
}
#[op2]
#[op2(stack_trace)]
#[serde]
pub fn op_net_listen_tcp<NP>(
state: &mut OpState,
@ -501,7 +501,7 @@ where
Ok((rid, IpAddr::from(local_addr)))
}
#[op2]
#[op2(stack_trace)]
#[serde]
pub fn op_net_listen_udp<NP>(
state: &mut OpState,
@ -516,7 +516,7 @@ where
net_listen_udp::<NP>(state, addr, reuse_address, loopback)
}
#[op2]
#[op2(stack_trace)]
#[serde]
pub fn op_node_unstable_net_listen_udp<NP>(
state: &mut OpState,
@ -601,7 +601,7 @@ pub struct NameServer {
port: u16,
}
#[op2(async)]
#[op2(async, stack_trace)]
#[serde]
pub async fn op_dns_resolve<NP>(
state: Rc<RefCell<OpState>>,

View File

@ -251,7 +251,7 @@ pub fn op_tls_cert_resolver_resolve_error(
lookup.resolve(sni, Err(error))
}
#[op2]
#[op2(stack_trace)]
#[serde]
pub fn op_tls_start<NP>(
state: Rc<RefCell<OpState>>,
@ -340,7 +340,7 @@ where
Ok((rid, IpAddr::from(local_addr), IpAddr::from(remote_addr)))
}
#[op2(async)]
#[op2(async, stack_trace)]
#[serde]
pub async fn op_net_connect_tls<NP>(
state: Rc<RefCell<OpState>>,
@ -445,7 +445,7 @@ pub struct ListenTlsArgs {
load_balanced: bool,
}
#[op2]
#[op2(stack_trace)]
#[serde]
pub fn op_net_listen_tls<NP>(
state: &mut OpState,

View File

@ -85,7 +85,7 @@ pub async fn op_net_accept_unix(
Ok((rid, local_addr_path, remote_addr_path))
}
#[op2(async)]
#[op2(async, stack_trace)]
#[serde]
pub async fn op_net_connect_unix<NP>(
state: Rc<RefCell<OpState>>,
@ -118,7 +118,7 @@ where
Ok((rid, local_addr_path, remote_addr_path))
}
#[op2(async)]
#[op2(async, stack_trace)]
#[serde]
pub async fn op_net_recv_unixpacket(
state: Rc<RefCell<OpState>>,
@ -140,7 +140,7 @@ pub async fn op_net_recv_unixpacket(
Ok((nread, path))
}
#[op2(async)]
#[op2(async, stack_trace)]
#[number]
pub async fn op_net_send_unixpacket<NP>(
state: Rc<RefCell<OpState>>,
@ -171,7 +171,7 @@ where
Ok(nwritten)
}
#[op2]
#[op2(stack_trace)]
#[serde]
pub fn op_net_listen_unix<NP>(
state: &mut OpState,
@ -222,7 +222,7 @@ where
Ok((rid, pathname))
}
#[op2]
#[op2(stack_trace)]
#[serde]
pub fn op_net_listen_unixpacket<NP>(
state: &mut OpState,
@ -235,7 +235,7 @@ where
net_listen_unixpacket::<NP>(state, path)
}
#[op2]
#[op2(stack_trace)]
#[serde]
pub fn op_node_unstable_net_listen_unixpacket<NP>(
state: &mut OpState,

View File

@ -26,7 +26,7 @@ pub enum FsError {
Fs(#[from] deno_io::fs::FsError),
}
#[op2(fast)]
#[op2(fast, stack_trace)]
pub fn op_node_fs_exists_sync<P>(
state: &mut OpState,
#[string] path: String,
@ -41,7 +41,7 @@ where
Ok(fs.exists_sync(&path))
}
#[op2(async)]
#[op2(async, stack_trace)]
pub async fn op_node_fs_exists<P>(
state: Rc<RefCell<OpState>>,
#[string] path: String,
@ -60,7 +60,7 @@ where
Ok(fs.exists_async(path).await?)
}
#[op2(fast)]
#[op2(fast, stack_trace)]
pub fn op_node_cp_sync<P>(
state: &mut OpState,
#[string] path: &str,
@ -81,7 +81,7 @@ where
Ok(())
}
#[op2(async)]
#[op2(async, stack_trace)]
pub async fn op_node_cp<P>(
state: Rc<RefCell<OpState>>,
#[string] path: String,
@ -117,7 +117,7 @@ pub struct StatFs {
pub ffree: u64,
}
#[op2]
#[op2(stack_trace)]
#[serde]
pub fn op_node_statfs<P>(
state: Rc<RefCell<OpState>>,
@ -258,7 +258,7 @@ where
}
}
#[op2(fast)]
#[op2(fast, stack_trace)]
pub fn op_node_lutimes_sync<P>(
state: &mut OpState,
#[string] path: &str,
@ -279,7 +279,7 @@ where
Ok(())
}
#[op2(async)]
#[op2(async, stack_trace)]
pub async fn op_node_lutimes<P>(
state: Rc<RefCell<OpState>>,
#[string] path: String,
@ -305,7 +305,7 @@ where
Ok(())
}
#[op2]
#[op2(stack_trace)]
pub fn op_node_lchown_sync<P>(
state: &mut OpState,
#[string] path: String,
@ -323,7 +323,7 @@ where
Ok(())
}
#[op2(async)]
#[op2(async, stack_trace)]
pub async fn op_node_lchown<P>(
state: Rc<RefCell<OpState>>,
#[string] path: String,

View File

@ -49,7 +49,7 @@ use std::cmp::min;
use tokio::io::AsyncReadExt;
use tokio::io::AsyncWriteExt;
#[op2]
#[op2(stack_trace)]
#[serde]
pub fn op_node_http_request<P>(
state: &mut OpState,

View File

@ -20,7 +20,7 @@ pub fn op_inspector_enabled() -> bool {
false
}
#[op2]
#[op2(stack_trace)]
pub fn op_inspector_open<P>(
_state: &mut OpState,
_port: Option<u16>,
@ -85,7 +85,7 @@ struct JSInspectorSession {
impl GarbageCollected for JSInspectorSession {}
#[op2]
#[op2(stack_trace)]
#[cppgc]
pub fn op_inspector_connect<'s, P>(
isolate: *mut v8::Isolate,

View File

@ -21,7 +21,7 @@ pub enum OsError {
FailedToGetUserInfo(#[source] std::io::Error),
}
#[op2(fast)]
#[op2(fast, stack_trace)]
pub fn op_node_os_get_priority<P>(
state: &mut OpState,
pid: u32,
@ -37,7 +37,7 @@ where
priority::get_priority(pid).map_err(OsError::Priority)
}
#[op2(fast)]
#[op2(fast, stack_trace)]
pub fn op_node_os_set_priority<P>(
state: &mut OpState,
pid: u32,
@ -193,7 +193,7 @@ fn get_user_info(_uid: u32) -> Result<UserInfo, OsError> {
})
}
#[op2]
#[op2(stack_trace)]
#[serde]
pub fn op_node_os_user_info<P>(
state: &mut OpState,
@ -212,7 +212,7 @@ where
get_user_info(uid)
}
#[op2(fast)]
#[op2(fast, stack_trace)]
pub fn op_geteuid<P>(
state: &mut OpState,
) -> Result<u32, deno_core::error::AnyError>
@ -233,7 +233,7 @@ where
Ok(euid)
}
#[op2(fast)]
#[op2(fast, stack_trace)]
pub fn op_getegid<P>(
state: &mut OpState,
) -> Result<u32, deno_core::error::AnyError>
@ -254,7 +254,7 @@ where
Ok(egid)
}
#[op2]
#[op2(stack_trace)]
#[serde]
pub fn op_cpus<P>(state: &mut OpState) -> Result<Vec<cpus::CpuInfo>, OsError>
where
@ -268,7 +268,7 @@ where
cpus::cpu_info().ok_or(OsError::FailedToGetCpuInfo)
}
#[op2]
#[op2(stack_trace)]
#[string]
pub fn op_homedir<P>(
state: &mut OpState,

View File

@ -45,7 +45,7 @@ fn kill(pid: i32, _sig: i32) -> i32 {
}
}
#[op2(fast)]
#[op2(fast, stack_trace)]
pub fn op_node_process_kill(
state: &mut OpState,
#[smi] pid: i32,

View File

@ -125,7 +125,7 @@ pub fn op_require_init_paths() -> Vec<String> {
vec![]
}
#[op2]
#[op2(stack_trace)]
#[serde]
pub fn op_require_node_module_paths<P>(
state: &mut OpState,
@ -295,7 +295,7 @@ pub fn op_require_path_is_absolute(#[string] p: String) -> bool {
PathBuf::from(p).is_absolute()
}
#[op2(fast)]
#[op2(fast, stack_trace)]
pub fn op_require_stat<P>(
state: &mut OpState,
#[string] path: String,
@ -317,7 +317,7 @@ where
Ok(-1)
}
#[op2]
#[op2(stack_trace)]
#[string]
pub fn op_require_real_path<P>(
state: &mut OpState,
@ -381,7 +381,7 @@ pub fn op_require_path_basename(
}
}
#[op2]
#[op2(stack_trace)]
#[string]
pub fn op_require_try_self_parent_path<P>(
state: &mut OpState,
@ -412,7 +412,7 @@ where
Ok(None)
}
#[op2]
#[op2(stack_trace)]
#[string]
pub fn op_require_try_self<P>(
state: &mut OpState,
@ -476,7 +476,7 @@ where
}
}
#[op2]
#[op2(stack_trace)]
#[string]
pub fn op_require_read_file<P>(
state: &mut OpState,
@ -507,7 +507,7 @@ pub fn op_require_as_file_path(#[string] file_or_url: String) -> String {
file_or_url
}
#[op2]
#[op2(stack_trace)]
#[string]
pub fn op_require_resolve_exports<P>(
state: &mut OpState,
@ -583,7 +583,7 @@ pub fn op_require_is_maybe_cjs(
loader.is_maybe_cjs(&url)
}
#[op2]
#[op2(stack_trace)]
#[serde]
pub fn op_require_read_package_scope<P>(
state: &mut OpState,
@ -604,7 +604,7 @@ where
.flatten()
}
#[op2]
#[op2(stack_trace)]
#[string]
pub fn op_require_package_imports_resolve<P>(
state: &mut OpState,

View File

@ -45,7 +45,7 @@ pub enum WorkerThreadsFilenameError {
}
// todo(dsherret): we should remove this and do all this work inside op_create_worker
#[op2]
#[op2(stack_trace)]
#[string]
pub fn op_worker_threads_filename<P>(
state: &mut OpState,

View File

@ -148,7 +148,7 @@ impl Resource for WsCancelResource {
// This op is needed because creating a WS instance in JavaScript is a sync
// operation and should throw error when permissions are not fulfilled,
// but actual op that connects WS is async.
#[op2]
#[op2(stack_trace)]
#[smi]
pub fn op_ws_check_permission_and_cancel_handle<WP>(
state: &mut OpState,
@ -443,7 +443,7 @@ fn populate_common_request_headers(
Ok(request)
}
#[op2(async)]
#[op2(async, stack_trace)]
#[serde]
pub async fn op_ws_create<WP>(
state: Rc<RefCell<OpState>>,

View File

@ -162,7 +162,7 @@ fn start_watcher(
Ok(())
}
#[op2]
#[op2(stack_trace)]
#[smi]
fn op_fs_events_open(
state: &mut OpState,

View File

@ -87,7 +87,7 @@ pub enum OsError {
Io(#[from] std::io::Error),
}
#[op2]
#[op2(stack_trace)]
#[string]
fn op_exec_path(state: &mut OpState) -> Result<String, OsError> {
let current_exe = env::current_exe().unwrap();
@ -103,7 +103,7 @@ fn op_exec_path(state: &mut OpState) -> Result<String, OsError> {
.map_err(OsError::InvalidUtf8)
}
#[op2(fast)]
#[op2(fast, stack_trace)]
fn op_set_env(
state: &mut OpState,
#[string] key: &str,
@ -123,7 +123,7 @@ fn op_set_env(
Ok(())
}
#[op2]
#[op2(stack_trace)]
#[serde]
fn op_env(
state: &mut OpState,
@ -132,7 +132,7 @@ fn op_env(
Ok(env::vars().collect())
}
#[op2]
#[op2(stack_trace)]
#[string]
fn op_get_env(
state: &mut OpState,
@ -159,7 +159,7 @@ fn op_get_env(
Ok(r)
}
#[op2(fast)]
#[op2(fast, stack_trace)]
fn op_delete_env(
state: &mut OpState,
#[string] key: String,
@ -189,7 +189,7 @@ fn op_exit(state: &mut OpState) {
crate::exit(code)
}
#[op2]
#[op2(stack_trace)]
#[serde]
fn op_loadavg(
state: &mut OpState,
@ -200,7 +200,7 @@ fn op_loadavg(
Ok(sys_info::loadavg())
}
#[op2]
#[op2(stack_trace, stack_trace)]
#[string]
fn op_hostname(
state: &mut OpState,
@ -211,7 +211,7 @@ fn op_hostname(
Ok(sys_info::hostname())
}
#[op2]
#[op2(stack_trace)]
#[string]
fn op_os_release(
state: &mut OpState,
@ -222,7 +222,7 @@ fn op_os_release(
Ok(sys_info::os_release())
}
#[op2]
#[op2(stack_trace)]
#[serde]
fn op_network_interfaces(
state: &mut OpState,
@ -274,7 +274,7 @@ impl From<netif::Interface> for NetworkInterface {
}
}
#[op2]
#[op2(stack_trace)]
#[serde]
fn op_system_memory_info(
state: &mut OpState,
@ -286,7 +286,7 @@ fn op_system_memory_info(
}
#[cfg(not(windows))]
#[op2]
#[op2(stack_trace)]
#[smi]
fn op_gid(
state: &mut OpState,
@ -302,7 +302,7 @@ fn op_gid(
}
#[cfg(windows)]
#[op2]
#[op2(stack_trace)]
#[smi]
fn op_gid(
state: &mut OpState,
@ -314,7 +314,7 @@ fn op_gid(
}
#[cfg(not(windows))]
#[op2]
#[op2(stack_trace)]
#[smi]
fn op_uid(
state: &mut OpState,
@ -330,7 +330,7 @@ fn op_uid(
}
#[cfg(windows)]
#[op2]
#[op2(stack_trace)]
#[smi]
fn op_uid(
state: &mut OpState,
@ -519,7 +519,7 @@ fn os_uptime(state: &mut OpState) -> Result<u64, deno_core::error::AnyError> {
Ok(sys_info::os_uptime())
}
#[op2(fast)]
#[op2(fast, stack_trace)]
#[number]
fn op_os_uptime(
state: &mut OpState,

View File

@ -99,7 +99,7 @@ pub fn op_revoke_permission(
Ok(PermissionStatus::from(perm))
}
#[op2]
#[op2(stack_trace)]
#[serde]
pub fn op_request_permission(
state: &mut OpState,

View File

@ -802,7 +802,7 @@ fn get_requires_allow_all_env_vars(env: &RunEnv) -> Vec<&str> {
found_envs
}
#[op2]
#[op2(stack_trace)]
#[serde]
fn op_spawn_child(
state: &mut OpState,
@ -844,7 +844,7 @@ async fn op_spawn_wait(
Ok(result)
}
#[op2]
#[op2(stack_trace)]
#[serde]
fn op_spawn_sync(
state: &mut OpState,
@ -928,7 +928,7 @@ mod deprecated {
stderr_rid: Option<ResourceId>,
}
#[op2]
#[op2(stack_trace)]
#[serde]
pub fn op_run(
state: &mut OpState,
@ -1129,7 +1129,7 @@ mod deprecated {
}
}
#[op2(fast)]
#[op2(fast, stack_trace)]
pub fn op_kill(
state: &mut OpState,
#[smi] pid: i32,

View File

@ -133,7 +133,7 @@ pub enum CreateWorkerError {
}
/// Create worker as the host
#[op2]
#[op2(stack_trace)]
#[serde]
fn op_create_worker(
state: &mut OpState,

View File

@ -1,5 +1,7 @@
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
use crate::is_standalone;
use deno_core::error::JsStackFrame;
use deno_core::parking_lot::Mutex;
use deno_terminal::colors;
use once_cell::sync::Lazy;
@ -10,8 +12,6 @@ use std::io::StderrLock;
use std::io::StdinLock;
use std::io::Write as IoWrite;
use crate::is_standalone;
/// Helper function to make control characters visible so users can see the underlying filename.
fn escape_control_characters(s: &str) -> std::borrow::Cow<str> {
if !s.contains(|c: char| c.is_ascii_control() || c.is_control()) {
@ -53,6 +53,13 @@ static MAYBE_BEFORE_PROMPT_CALLBACK: Lazy<Mutex<Option<PromptCallback>>> =
static MAYBE_AFTER_PROMPT_CALLBACK: Lazy<Mutex<Option<PromptCallback>>> =
Lazy::new(|| Mutex::new(None));
static MAYBE_CURRENT_STACKTRACE: Lazy<Mutex<Option<Vec<JsStackFrame>>>> =
Lazy::new(|| Mutex::new(None));
pub fn set_current_stacktrace(trace: Vec<JsStackFrame>) {
*MAYBE_CURRENT_STACKTRACE.lock() = Some(trace);
}
pub fn permission_prompt(
message: &str,
flag: &str,
@ -62,9 +69,10 @@ pub fn permission_prompt(
if let Some(before_callback) = MAYBE_BEFORE_PROMPT_CALLBACK.lock().as_mut() {
before_callback();
}
let stack = MAYBE_CURRENT_STACKTRACE.lock().take();
let r = PERMISSION_PROMPTER
.lock()
.prompt(message, flag, api_name, is_unary);
.prompt(message, flag, api_name, is_unary, stack);
if let Some(after_callback) = MAYBE_AFTER_PROMPT_CALLBACK.lock().as_mut() {
after_callback();
}
@ -92,6 +100,7 @@ pub trait PermissionPrompter: Send + Sync {
name: &str,
api_name: Option<&str>,
is_unary: bool,
stack: Option<Vec<JsStackFrame>>,
) -> PromptResponse;
}
@ -298,6 +307,7 @@ impl PermissionPrompter for TtyPrompter {
name: &str,
api_name: Option<&str>,
is_unary: bool,
stack: Option<Vec<JsStackFrame>>,
) -> PromptResponse {
if !std::io::stdin().is_terminal() || !std::io::stderr().is_terminal() {
return PromptResponse::Deny;
@ -340,7 +350,7 @@ impl PermissionPrompter for TtyPrompter {
};
// output everything in one shot to make the tests more reliable
{
let stack_lines_count = {
let mut output = String::new();
write!(&mut output, "┏ {PERMISSION_EMOJI} ").unwrap();
write!(&mut output, "{}", colors::bold("Deno requests ")).unwrap();
@ -354,6 +364,27 @@ impl PermissionPrompter for TtyPrompter {
)
.unwrap();
}
let stack_lines_count = if let Some(stack) = stack {
let len = stack.len();
for (idx, frame) in stack.into_iter().enumerate() {
writeln!(
&mut output,
"┃ {} {}",
colors::gray(if idx != len - 1 { "├─" } else { "└─" }),
colors::gray(deno_core::error::format_frame::<
deno_core::error::NoAnsiColors,
>(&frame))
)
.unwrap();
}
len
} else {
writeln!(
&mut output,
"┠─ To see a stack trace for this prompt, set the DENO_TRACE_PERMISSIONS environmental variable.",
).unwrap();
1
};
let msg = format!(
"Learn more at: {}",
colors::cyan_with_underline(&format!(
@ -372,7 +403,9 @@ impl PermissionPrompter for TtyPrompter {
write!(&mut output, " {opts} > ").unwrap();
stderr_lock.write_all(output.as_bytes()).unwrap();
}
stack_lines_count
};
let value = loop {
// Clear stdin each time we loop around in case the user accidentally pasted
@ -391,30 +424,24 @@ impl PermissionPrompter for TtyPrompter {
if result.is_err() || input.len() != 1 {
break PromptResponse::Deny;
};
let clear_n = if api_name.is_some() { 5 } else { 4 } + stack_lines_count;
match input.as_bytes()[0] as char {
'y' | 'Y' => {
clear_n_lines(
&mut stderr_lock,
if api_name.is_some() { 5 } else { 4 },
);
clear_n_lines(&mut stderr_lock, clear_n);
let msg = format!("Granted {message}.");
writeln!(stderr_lock, "✅ {}", colors::bold(&msg)).unwrap();
break PromptResponse::Allow;
}
'n' | 'N' | '\x1b' => {
clear_n_lines(
&mut stderr_lock,
if api_name.is_some() { 5 } else { 4 },
);
clear_n_lines(&mut stderr_lock, clear_n);
let msg = format!("Denied {message}.");
writeln!(stderr_lock, "❌ {}", colors::bold(&msg)).unwrap();
break PromptResponse::Deny;
}
'A' if is_unary => {
clear_n_lines(
&mut stderr_lock,
if api_name.is_some() { 5 } else { 4 },
);
clear_n_lines(&mut stderr_lock, clear_n);
let msg = format!("Granted all {name} access.");
writeln!(stderr_lock, "✅ {}", colors::bold(&msg)).unwrap();
break PromptResponse::AllowAll;
@ -475,6 +502,7 @@ pub mod tests {
_name: &str,
_api_name: Option<&str>,
_is_unary: bool,
_stack: Option<Vec<JsStackFrame>>,
) -> PromptResponse {
if STUB_PROMPT_VALUE.load(Ordering::SeqCst) {
PromptResponse::Allow

View File

@ -373,6 +373,7 @@ pub struct WebWorkerOptions {
pub strace_ops: Option<Vec<String>>,
pub close_on_idle: bool,
pub maybe_worker_metadata: Option<WorkerMetadata>,
pub enable_stack_trace_arg_in_ops: bool,
}
/// This struct is an implementation of `Worker` Web API
@ -585,6 +586,13 @@ impl WebWorker {
validate_import_attributes_callback,
)),
import_assertions_support: deno_core::ImportAssertionsSupport::Error,
maybe_op_stack_trace_callback: if options.enable_stack_trace_arg_in_ops {
Some(Box::new(|stack| {
deno_permissions::prompter::set_current_stacktrace(stack)
}))
} else {
None
},
..Default::default()
});

View File

@ -207,6 +207,7 @@ pub struct WorkerOptions {
pub cache_storage_dir: Option<std::path::PathBuf>,
pub origin_storage_dir: Option<std::path::PathBuf>,
pub stdio: Stdio,
pub enable_stack_trace_arg_in_ops: bool,
}
impl Default for WorkerOptions {
@ -231,6 +232,7 @@ impl Default for WorkerOptions {
create_params: Default::default(),
bootstrap: Default::default(),
stdio: Default::default(),
enable_stack_trace_arg_in_ops: false,
}
}
}
@ -544,6 +546,11 @@ impl MainWorker {
) as Box<dyn Fn(_, _, &_)>,
)
}),
maybe_op_stack_trace_callback: if options.enable_stack_trace_arg_in_ops {
Some(Box::new(|stack| {
deno_permissions::prompter::set_current_stacktrace(stack)
}))
} else { None },
..Default::default()
});

View File

@ -179,6 +179,7 @@ fn _090_run_permissions_request() {
console.expect(concat!(
"┏ ⚠️ Deno requests run access to \"ls\".\r\n",
"┠─ Requested by `Deno.permissions.request()` API.\r\n",
"┠─ To see a stack trace for this prompt, set the DENO_TRACE_PERMISSIONS environmental variable.\r\n",
"┠─ Learn more at: https://docs.deno.com/go/--allow-run\r\n",
"┠─ Run again with --allow-run to bypass this prompt.\r\n",
"┗ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all run permissions)",
@ -189,6 +190,7 @@ fn _090_run_permissions_request() {
console.expect(concat!(
"┏ ⚠️ Deno requests run access to \"cat\".\r\n",
"┠─ Requested by `Deno.permissions.request()` API.\r\n",
"┠─ To see a stack trace for this prompt, set the DENO_TRACE_PERMISSIONS environmental variable.\r\n",
"┠─ Learn more at: https://docs.deno.com/go/--allow-run\r\n",
"┠─ Run again with --allow-run to bypass this prompt.\r\n",
"┗ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all run permissions)",
@ -210,6 +212,7 @@ fn _090_run_permissions_request_sync() {
console.expect(concat!(
"┏ ⚠️ Deno requests run access to \"ls\".\r\n",
"┠─ Requested by `Deno.permissions.request()` API.\r\n",
"┠─ To see a stack trace for this prompt, set the DENO_TRACE_PERMISSIONS environmental variable.\r\n",
"┠─ Learn more at: https://docs.deno.com/go/--allow-run\r\n",
"┠─ Run again with --allow-run to bypass this prompt.\r\n",
"┗ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all run permissions)",
@ -220,6 +223,7 @@ fn _090_run_permissions_request_sync() {
console.expect(concat!(
"┏ ⚠️ Deno requests run access to \"cat\".\r\n",
"┠─ Requested by `Deno.permissions.request()` API.\r\n",
"┠─ To see a stack trace for this prompt, set the DENO_TRACE_PERMISSIONS environmental variable.\r\n",
"┠─ Learn more at: https://docs.deno.com/go/--allow-run\r\n",
"┠─ Run again with --allow-run to bypass this prompt.\r\n",
"┗ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all run permissions)",
@ -242,6 +246,7 @@ fn permissions_prompt_allow_all() {
console.expect(concat!(
"┏ ⚠️ Deno requests run access to \"FOO\".\r\n",
"┠─ Requested by `Deno.permissions.request()` API.\r\n",
"┠─ To see a stack trace for this prompt, set the DENO_TRACE_PERMISSIONS environmental variable.\r\n",
"┠─ Learn more at: https://docs.deno.com/go/--allow-run\r\n",
"┠─ Run again with --allow-run to bypass this prompt.\r\n",
"┗ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all run permissions)",
@ -253,6 +258,7 @@ fn permissions_prompt_allow_all() {
console.expect(concat!(
"┏ ⚠️ Deno requests read access to \"FOO\".\r\n",
"┠─ Requested by `Deno.permissions.request()` API.\r\n",
"┠─ To see a stack trace for this prompt, set the DENO_TRACE_PERMISSIONS environmental variable.\r\n",
"┠─ Learn more at: https://docs.deno.com/go/--allow-read\r\n",
"┠─ Run again with --allow-read to bypass this prompt.\r\n",
"┗ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all read permissions)",
@ -264,6 +270,7 @@ fn permissions_prompt_allow_all() {
console.expect(concat!(
"┏ ⚠️ Deno requests write access to \"FOO\".\r\n",
"┠─ Requested by `Deno.permissions.request()` API.\r\n",
"┠─ To see a stack trace for this prompt, set the DENO_TRACE_PERMISSIONS environmental variable.\r\n",
"┠─ Learn more at: https://docs.deno.com/go/--allow-write\r\n",
"┠─ Run again with --allow-write to bypass this prompt.\r\n",
"┗ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all write permissions)",
@ -275,6 +282,7 @@ fn permissions_prompt_allow_all() {
console.expect(concat!(
"┏ ⚠️ Deno requests net access to \"foo\".\r\n",
"┠─ Requested by `Deno.permissions.request()` API.\r\n",
"┠─ To see a stack trace for this prompt, set the DENO_TRACE_PERMISSIONS environmental variable.\r\n",
"┠─ Learn more at: https://docs.deno.com/go/--allow-net\r\n",
"┠─ Run again with --allow-net to bypass this prompt.\r\n",
"┗ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all net permissions)",
@ -286,6 +294,7 @@ fn permissions_prompt_allow_all() {
console.expect(concat!(
"┏ ⚠️ Deno requests env access to \"FOO\".\r\n",
"┠─ Requested by `Deno.permissions.request()` API.\r\n",
"┠─ To see a stack trace for this prompt, set the DENO_TRACE_PERMISSIONS environmental variable.\r\n",
"┠─ Learn more at: https://docs.deno.com/go/--allow-env\r\n",
"┠─ Run again with --allow-env to bypass this prompt.\r\n",
"┗ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all env permissions)",
@ -297,6 +306,7 @@ fn permissions_prompt_allow_all() {
console.expect(concat!(
"┏ ⚠️ Deno requests sys access to \"loadavg\".\r\n",
"┠─ Requested by `Deno.permissions.request()` API.\r\n",
"┠─ To see a stack trace for this prompt, set the DENO_TRACE_PERMISSIONS environmental variable.\r\n",
"┠─ Learn more at: https://docs.deno.com/go/--allow-sys\r\n",
"┠─ Run again with --allow-sys to bypass this prompt.\r\n",
"┗ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all sys permissions)",
@ -308,6 +318,7 @@ fn permissions_prompt_allow_all() {
console.expect(concat!(
"┏ ⚠️ Deno requests ffi access to \"FOO\".\r\n",
"┠─ Requested by `Deno.permissions.request()` API.\r\n",
"┠─ To see a stack trace for this prompt, set the DENO_TRACE_PERMISSIONS environmental variable.\r\n",
"┠─ Learn more at: https://docs.deno.com/go/--allow-ffi\r\n",
"┠─ Run again with --allow-ffi to bypass this prompt.\r\n",
"┗ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all ffi permissions)",
@ -328,6 +339,7 @@ fn permissions_prompt_allow_all_2() {
// "env" permissions
console.expect(concat!(
"┏ ⚠️ Deno requests env access to \"FOO\".\r\n",
"┠─ To see a stack trace for this prompt, set the DENO_TRACE_PERMISSIONS environmental variable.\r\n",
"┠─ Learn more at: https://docs.deno.com/go/--allow-env\r\n",
"┠─ Run again with --allow-env to bypass this prompt.\r\n",
"┗ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all env permissions)",
@ -340,6 +352,7 @@ fn permissions_prompt_allow_all_2() {
console.expect(concat!(
"┏ ⚠️ Deno requests sys access to \"loadavg\".\r\n",
"┠─ Requested by `Deno.loadavg()` API.\r\n",
"┠─ To see a stack trace for this prompt, set the DENO_TRACE_PERMISSIONS environmental variable.\r\n",
"┠─ Learn more at: https://docs.deno.com/go/--allow-sys\r\n",
"┠─ Run again with --allow-sys to bypass this prompt.\r\n",
"┗ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all sys permissions)",
@ -352,6 +365,7 @@ fn permissions_prompt_allow_all_2() {
console.expect(concat!(
"┏ ⚠️ Deno requests read access to <CWD>.\r\n",
"┠─ Requested by `Deno.cwd()` API.\r\n",
"┠─ To see a stack trace for this prompt, set the DENO_TRACE_PERMISSIONS environmental variable.\r\n",
"┠─ Learn more at: https://docs.deno.com/go/--allow-read\r\n",
"┠─ Run again with --allow-read to bypass this prompt.\r\n",
"┗ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all read permissions)",
@ -372,6 +386,7 @@ fn permissions_prompt_allow_all_lowercase_a() {
console.expect(concat!(
"┏ ⚠️ Deno requests run access to \"FOO\".\r\n",
"┠─ Requested by `Deno.permissions.request()` API.\r\n",
"┠─ To see a stack trace for this prompt, set the DENO_TRACE_PERMISSIONS environmental variable.\r\n",
"┠─ Learn more at: https://docs.deno.com/go/--allow-run\r\n",
"┠─ Run again with --allow-run to bypass this prompt.\r\n",
"┗ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all run permissions)",
@ -406,6 +421,7 @@ fn permissions_cache() {
"prompt\r\n",
"┏ ⚠️ Deno requests read access to \"foo\".\r\n",
"┠─ Requested by `Deno.permissions.request()` API.\r\n",
"┠─ To see a stack trace for this prompt, set the DENO_TRACE_PERMISSIONS environmental variable.\r\n",
"┠─ Learn more at: https://docs.deno.com/go/--allow-read\r\n",
"┠─ Run again with --allow-read to bypass this prompt.\r\n",
"┗ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all read permissions)",
@ -418,6 +434,32 @@ fn permissions_cache() {
});
}
#[test]
fn permissions_trace() {
TestContext::default()
.new_command()
.env("DENO_TRACE_PERMISSIONS", "1")
.args_vec(["run", "--quiet", "run/permissions_trace.ts"])
.with_pty(|mut console| {
let text = console.read_until("Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all sys permissions)");
test_util::assertions::assert_wildcard_match(&text, concat!(
"┏ ⚠️ Deno requests sys access to \"hostname\".\r\n",
"┠─ Requested by `Deno.hostname()` API.\r\n",
"┃ ├─ Object.hostname (ext:runtime/30_os.js:43:10)\r\n",
"┃ ├─ foo (file://[WILDCARD]/run/permissions_trace.ts:2:8)\r\n",
"┃ ├─ bar (file://[WILDCARD]/run/permissions_trace.ts:6:3)\r\n",
"┃ └─ file://[WILDCARD]/run/permissions_trace.ts:9:1\r\n",
"┠─ Learn more at: https://docs.deno.com/go/--allow-sys\r\n",
"┠─ Run again with --allow-sys to bypass this prompt.\r\n",
"┗ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all sys permissions)",
));
console.human_delay();
console.write_line_raw("y");
console.expect("✅ Granted sys access to \"hostname\".");
});
}
itest!(lock_write_fetch {
args:
"run --quiet --allow-import --allow-read --allow-write --allow-env --allow-run run/lock_write_fetch/main.ts",
@ -1512,6 +1554,7 @@ mod permissions {
console.expect(concat!(
"┏ ⚠️ Deno requests read access to \"foo\".\r\n",
"┠─ Requested by `Deno.permissions.request()` API.\r\n",
"┠─ To see a stack trace for this prompt, set the DENO_TRACE_PERMISSIONS environmental variable.\r\n",
"┠─ Learn more at: https://docs.deno.com/go/--allow-read\r\n",
"┠─ Run again with --allow-read to bypass this prompt.\r\n",
"┗ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all read permissions)",
@ -1521,6 +1564,7 @@ mod permissions {
console.expect(concat!(
"┏ ⚠️ Deno requests read access to \"bar\".\r\n",
"┠─ Requested by `Deno.permissions.request()` API.\r\n",
"┠─ To see a stack trace for this prompt, set the DENO_TRACE_PERMISSIONS environmental variable.\r\n",
"┠─ Learn more at: https://docs.deno.com/go/--allow-read\r\n",
"┠─ Run again with --allow-read to bypass this prompt.\r\n",
"┗ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all read permissions)",
@ -1542,6 +1586,7 @@ mod permissions {
console.expect(concat!(
"┏ ⚠️ Deno requests read access to \"foo\".\r\n",
"┠─ Requested by `Deno.permissions.request()` API.\r\n",
"┠─ To see a stack trace for this prompt, set the DENO_TRACE_PERMISSIONS environmental variable.\r\n",
"┠─ Learn more at: https://docs.deno.com/go/--allow-read\r\n",
"┠─ Run again with --allow-read to bypass this prompt.\r\n",
"┗ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all read permissions)",
@ -1551,6 +1596,7 @@ mod permissions {
console.expect(concat!(
"┏ ⚠️ Deno requests read access to \"bar\".\r\n",
"┠─ Requested by `Deno.permissions.request()` API.\r\n",
"┠─ To see a stack trace for this prompt, set the DENO_TRACE_PERMISSIONS environmental variable.\r\n",
"┠─ Learn more at: https://docs.deno.com/go/--allow-read\r\n",
"┠─ Run again with --allow-read to bypass this prompt.\r\n",
"┗ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all read permissions)",
@ -1572,6 +1618,7 @@ mod permissions {
console.expect(concat!(
"┏ ⚠️ Deno requests read access.\r\n",
"┠─ Requested by `Deno.permissions.request()` API.\r\n",
"┠─ To see a stack trace for this prompt, set the DENO_TRACE_PERMISSIONS environmental variable.\r\n",
"┠─ Learn more at: https://docs.deno.com/go/--allow-read\r\n",
"┠─ Run again with --allow-read to bypass this prompt.\r\n",
"┗ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all read permissions)",
@ -1596,6 +1643,7 @@ mod permissions {
console.expect(concat!(
"┏ ⚠️ Deno requests read access.\r\n",
"┠─ Requested by `Deno.permissions.request()` API.\r\n",
"┠─ To see a stack trace for this prompt, set the DENO_TRACE_PERMISSIONS environmental variable.\r\n",
"┠─ Learn more at: https://docs.deno.com/go/--allow-read\r\n",
"┠─ Run again with --allow-read to bypass this prompt.\r\n",
"┗ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all read permissions)",
@ -1673,6 +1721,7 @@ fn issue9750() {
console.expect(concat!(
"┏ ⚠️ Deno requests env access.\r\n",
"┠─ Requested by `Deno.permissions.request()` API.\r\n",
"┠─ To see a stack trace for this prompt, set the DENO_TRACE_PERMISSIONS environmental variable.\r\n",
"┠─ Learn more at: https://docs.deno.com/go/--allow-env\r\n",
"┠─ Run again with --allow-env to bypass this prompt.\r\n",
"┗ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all env permissions)",
@ -1682,6 +1731,7 @@ fn issue9750() {
console.expect("Denied env access.");
console.expect(concat!(
"┏ ⚠️ Deno requests env access to \"SECRET\".\r\n",
"┠─ To see a stack trace for this prompt, set the DENO_TRACE_PERMISSIONS environmental variable.\r\n",
"┠─ Learn more at: https://docs.deno.com/go/--allow-env\r\n",
"┠─ Run again with --allow-env to bypass this prompt.\r\n",
"┗ Allow? [y/n/A] (y = yes, allow; n = no, deny; A = allow all env permissions)",
@ -2723,7 +2773,7 @@ fn stdio_streams_are_locked_in_permission_prompt() {
console.human_delay();
console.write_line_raw("y");
// We ensure that nothing gets written here between the permission prompt and this text, despire the delay
console.expect_raw_next(format!("y{newline}\x1b[5A\x1b[0J✅ Granted read access to \""));
console.expect_raw_next(format!("y{newline}\x1b[6A\x1b[0J✅ Granted read access to \""));
// Back to spamming!
console.expect(malicious_output);

View File

@ -0,0 +1,9 @@
function foo() {
Deno.hostname();
}
function bar() {
foo();
}
bar();