mirror of
https://github.com/denoland/deno.git
synced 2024-11-21 15:04:11 -05:00
fix: lock down allow-run permissions more (#25370)
`--allow-run` even with an allow list has essentially been `--allow-all`... this locks it down more. 1. Resolves allow list for `--allow-run=` on startup to an absolute path, then uses these paths when evaluating if a command can execute. Also, adds these paths to `--deny-write` 1. Resolves the environment (cwd and env vars) before evaluating permissions and before executing a command. Then uses this environment to evaluate the permissions and then evaluate the command.
This commit is contained in:
parent
334c842392
commit
74fc66da11
27 changed files with 684 additions and 364 deletions
|
@ -1,7 +1,16 @@
|
|||
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
|
||||
|
||||
use crate::args::resolve_no_prompt;
|
||||
use crate::util::fs::canonicalize_path;
|
||||
use std::collections::HashSet;
|
||||
use std::env;
|
||||
use std::ffi::OsString;
|
||||
use std::net::SocketAddr;
|
||||
use std::num::NonZeroU32;
|
||||
use std::num::NonZeroU8;
|
||||
use std::num::NonZeroUsize;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
|
||||
use clap::builder::styling::AnsiColor;
|
||||
use clap::builder::FalseyValueParser;
|
||||
use clap::error::ErrorKind;
|
||||
|
@ -23,22 +32,16 @@ use deno_core::normalize_path;
|
|||
use deno_core::resolve_url_or_path;
|
||||
use deno_core::url::Url;
|
||||
use deno_graph::GraphKind;
|
||||
use deno_runtime::colors;
|
||||
use deno_runtime::deno_permissions::parse_sys_kind;
|
||||
use deno_runtime::deno_permissions::PermissionsOptions;
|
||||
use log::debug;
|
||||
use log::Level;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::collections::HashSet;
|
||||
use std::env;
|
||||
use std::ffi::OsString;
|
||||
use std::net::SocketAddr;
|
||||
use std::num::NonZeroU32;
|
||||
use std::num::NonZeroU8;
|
||||
use std::num::NonZeroUsize;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::args::resolve_no_prompt;
|
||||
use crate::util::fs::canonicalize_path;
|
||||
|
||||
use super::flags_net;
|
||||
|
||||
|
@ -681,6 +684,54 @@ impl PermissionFlags {
|
|||
Ok(Some(new_paths))
|
||||
}
|
||||
|
||||
fn resolve_allow_run(
|
||||
allow_run: &[String],
|
||||
) -> Result<Vec<PathBuf>, AnyError> {
|
||||
let mut new_allow_run = Vec::with_capacity(allow_run.len());
|
||||
for command_name in allow_run {
|
||||
if command_name.is_empty() {
|
||||
bail!("Empty command name not allowed in --allow-run=...")
|
||||
}
|
||||
let command_path_result = which::which(command_name);
|
||||
match command_path_result {
|
||||
Ok(command_path) => new_allow_run.push(command_path),
|
||||
Err(err) => {
|
||||
log::info!(
|
||||
"{} Failed to resolve '{}' for allow-run: {}",
|
||||
colors::gray("Info"),
|
||||
command_name,
|
||||
err
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(new_allow_run)
|
||||
}
|
||||
|
||||
let mut deny_write =
|
||||
convert_option_str_to_path_buf(&self.deny_write, initial_cwd)?;
|
||||
let allow_run = self
|
||||
.allow_run
|
||||
.as_ref()
|
||||
.and_then(|raw_allow_run| match resolve_allow_run(raw_allow_run) {
|
||||
Ok(resolved_allow_run) => {
|
||||
if resolved_allow_run.is_empty() && !raw_allow_run.is_empty() {
|
||||
None // convert to no permissions if now empty
|
||||
} else {
|
||||
Some(Ok(resolved_allow_run))
|
||||
}
|
||||
}
|
||||
Err(err) => Some(Err(err)),
|
||||
})
|
||||
.transpose()?;
|
||||
// add the allow_run list to deno_write
|
||||
if let Some(allow_run_vec) = &allow_run {
|
||||
if !allow_run_vec.is_empty() {
|
||||
let deno_write = deny_write.get_or_insert_with(Vec::new);
|
||||
deno_write.extend(allow_run_vec.iter().cloned());
|
||||
}
|
||||
}
|
||||
|
||||
Ok(PermissionsOptions {
|
||||
allow_all: self.allow_all,
|
||||
allow_env: self.allow_env.clone(),
|
||||
|
@ -694,7 +745,7 @@ impl PermissionFlags {
|
|||
initial_cwd,
|
||||
)?,
|
||||
deny_read: convert_option_str_to_path_buf(&self.deny_read, initial_cwd)?,
|
||||
allow_run: self.allow_run.clone(),
|
||||
allow_run,
|
||||
deny_run: self.deny_run.clone(),
|
||||
allow_sys: self.allow_sys.clone(),
|
||||
deny_sys: self.deny_sys.clone(),
|
||||
|
@ -702,10 +753,7 @@ impl PermissionFlags {
|
|||
&self.allow_write,
|
||||
initial_cwd,
|
||||
)?,
|
||||
deny_write: convert_option_str_to_path_buf(
|
||||
&self.deny_write,
|
||||
initial_cwd,
|
||||
)?,
|
||||
deny_write,
|
||||
prompt: !resolve_no_prompt(self),
|
||||
})
|
||||
}
|
||||
|
|
|
@ -213,8 +213,8 @@ impl ShellCommand for NodeGypCommand {
|
|||
) -> LocalBoxFuture<'static, ExecuteResult> {
|
||||
// at the moment this shell command is just to give a warning if node-gyp is not found
|
||||
// in the future, we could try to run/install node-gyp for the user with deno
|
||||
if which::which("node-gyp").is_err() {
|
||||
log::warn!("{}: node-gyp was used in a script, but was not listed as a dependency. Either add it as a dependency or install it globally (e.g. `npm install -g node-gyp`)", crate::colors::yellow("warning"));
|
||||
if context.state.resolve_command_path("node-gyp").is_err() {
|
||||
log::warn!("{} node-gyp was used in a script, but was not listed as a dependency. Either add it as a dependency or install it globally (e.g. `npm install -g node-gyp`)", crate::colors::yellow("Warning"));
|
||||
}
|
||||
ExecutableCommand::new(
|
||||
"node-gyp".to_string(),
|
||||
|
|
|
@ -21,6 +21,9 @@ use serde::Deserialize;
|
|||
use serde::Serialize;
|
||||
use std::borrow::Cow;
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::path::Path;
|
||||
use std::path::PathBuf;
|
||||
use std::process::ExitStatus;
|
||||
use std::rc::Rc;
|
||||
use tokio::process::Command;
|
||||
|
@ -228,63 +231,15 @@ fn create_command(
|
|||
mut args: SpawnArgs,
|
||||
api_name: &str,
|
||||
) -> Result<CreateCommand, AnyError> {
|
||||
fn get_requires_allow_all_env_var(args: &SpawnArgs) -> Option<Cow<str>> {
|
||||
fn requires_allow_all(key: &str) -> bool {
|
||||
let key = key.trim();
|
||||
// we could be more targted here, but there are quite a lot of
|
||||
// LD_* and DYLD_* env variables
|
||||
key.starts_with("LD_") || key.starts_with("DYLD_")
|
||||
}
|
||||
|
||||
/// Checks if the user set this env var to an empty
|
||||
/// string in order to clear it.
|
||||
fn args_has_empty_env_value(args: &SpawnArgs, key_name: &str) -> bool {
|
||||
args
|
||||
.env
|
||||
.iter()
|
||||
.find(|(k, _)| k == key_name)
|
||||
.map(|(_, v)| v.trim().is_empty())
|
||||
.unwrap_or(false)
|
||||
}
|
||||
|
||||
if let Some((key, _)) = args
|
||||
.env
|
||||
.iter()
|
||||
.find(|(k, v)| requires_allow_all(k) && !v.trim().is_empty())
|
||||
{
|
||||
return Some(key.into());
|
||||
}
|
||||
|
||||
if !args.clear_env {
|
||||
if let Some((key, _)) = std::env::vars().find(|(k, v)| {
|
||||
requires_allow_all(k)
|
||||
&& !v.trim().is_empty()
|
||||
&& !args_has_empty_env_value(args, k)
|
||||
}) {
|
||||
return Some(key.into());
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
{
|
||||
let permissions = state.borrow_mut::<PermissionsContainer>();
|
||||
permissions.check_run(&args.cmd, api_name)?;
|
||||
if permissions.check_run_all(api_name).is_err() {
|
||||
// error the same on all platforms
|
||||
if let Some(name) = get_requires_allow_all_env_var(&args) {
|
||||
// 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(
|
||||
"PermissionDenied",
|
||||
format!("Requires --allow-all permissions to spawn subprocess with {} environment variable.", name)
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut command = std::process::Command::new(args.cmd);
|
||||
let (cmd, run_env) = compute_run_cmd_and_check_permissions(
|
||||
&args.cmd,
|
||||
args.cwd.as_deref(),
|
||||
&args.env,
|
||||
args.clear_env,
|
||||
state,
|
||||
api_name,
|
||||
)?;
|
||||
let mut command = std::process::Command::new(cmd);
|
||||
|
||||
#[cfg(windows)]
|
||||
if args.windows_raw_arguments {
|
||||
|
@ -298,14 +253,9 @@ fn create_command(
|
|||
#[cfg(not(windows))]
|
||||
command.args(args.args);
|
||||
|
||||
if let Some(cwd) = args.cwd {
|
||||
command.current_dir(cwd);
|
||||
}
|
||||
|
||||
if args.clear_env {
|
||||
command.env_clear();
|
||||
}
|
||||
command.envs(args.env);
|
||||
command.current_dir(run_env.cwd);
|
||||
command.env_clear();
|
||||
command.envs(run_env.envs);
|
||||
|
||||
#[cfg(unix)]
|
||||
if let Some(gid) = args.gid {
|
||||
|
@ -554,6 +504,133 @@ fn close_raw_handle(handle: deno_io::RawBiPipeHandle) {
|
|||
}
|
||||
}
|
||||
|
||||
fn compute_run_cmd_and_check_permissions(
|
||||
arg_cmd: &str,
|
||||
arg_cwd: Option<&str>,
|
||||
arg_envs: &[(String, String)],
|
||||
arg_clear_env: bool,
|
||||
state: &mut OpState,
|
||||
api_name: &str,
|
||||
) -> Result<(PathBuf, RunEnv), AnyError> {
|
||||
let run_env = compute_run_env(arg_cwd, arg_envs, arg_clear_env)
|
||||
.with_context(|| format!("Failed to spawn '{}'", arg_cmd))?;
|
||||
let cmd = resolve_cmd(arg_cmd, &run_env)
|
||||
.with_context(|| format!("Failed to spawn '{}'", arg_cmd))?;
|
||||
check_run_permission(state, &cmd, &run_env, api_name)?;
|
||||
Ok((cmd, run_env))
|
||||
}
|
||||
|
||||
struct RunEnv {
|
||||
envs: HashMap<String, String>,
|
||||
cwd: PathBuf,
|
||||
}
|
||||
|
||||
/// Computes the current environment, which will then be used to inform
|
||||
/// permissions and finally spawning. This is very important to compute
|
||||
/// ahead of time so that the environment used to verify permissions is
|
||||
/// the same environment used to spawn the sub command. This protects against
|
||||
/// someone doing timing attacks by changing the environment on a worker.
|
||||
fn compute_run_env(
|
||||
arg_cwd: Option<&str>,
|
||||
arg_envs: &[(String, String)],
|
||||
arg_clear_env: bool,
|
||||
) -> Result<RunEnv, AnyError> {
|
||||
#[allow(clippy::disallowed_methods)]
|
||||
let cwd = std::env::current_dir().context("failed resolving cwd")?;
|
||||
let cwd = arg_cwd
|
||||
.map(|cwd_arg| resolve_path(cwd_arg, &cwd))
|
||||
.unwrap_or(cwd);
|
||||
let envs = if arg_clear_env {
|
||||
arg_envs.iter().cloned().collect()
|
||||
} else {
|
||||
let mut envs = std::env::vars().collect::<HashMap<_, _>>();
|
||||
for (key, value) in arg_envs {
|
||||
envs.insert(key.clone(), value.clone());
|
||||
}
|
||||
envs
|
||||
};
|
||||
Ok(RunEnv { envs, cwd })
|
||||
}
|
||||
|
||||
fn resolve_cmd(cmd: &str, env: &RunEnv) -> Result<PathBuf, AnyError> {
|
||||
let is_path = cmd.contains('/');
|
||||
#[cfg(windows)]
|
||||
let is_path = is_path || cmd.contains('\\') || Path::new(&cmd).is_absolute();
|
||||
if is_path {
|
||||
Ok(resolve_path(cmd, &env.cwd))
|
||||
} else {
|
||||
let path = env.envs.get("PATH").or_else(|| {
|
||||
if cfg!(windows) {
|
||||
env.envs.iter().find_map(|(k, v)| {
|
||||
if k.to_uppercase() == "PATH" {
|
||||
Some(v)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
match which::which_in(cmd, path, &env.cwd) {
|
||||
Ok(cmd) => Ok(cmd),
|
||||
Err(which::Error::CannotFindBinaryPath) => {
|
||||
Err(std::io::Error::from(std::io::ErrorKind::NotFound).into())
|
||||
}
|
||||
Err(err) => Err(err.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_path(path: &str, cwd: &Path) -> PathBuf {
|
||||
deno_core::normalize_path(cwd.join(path))
|
||||
}
|
||||
|
||||
fn check_run_permission(
|
||||
state: &mut OpState,
|
||||
cmd: &Path,
|
||||
run_env: &RunEnv,
|
||||
api_name: &str,
|
||||
) -> Result<(), AnyError> {
|
||||
let permissions = state.borrow_mut::<PermissionsContainer>();
|
||||
if !permissions.query_run_all(api_name) {
|
||||
// error the same on all platforms
|
||||
let env_var_names = get_requires_allow_all_env_vars(run_env);
|
||||
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(
|
||||
"PermissionDenied",
|
||||
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)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_requires_allow_all_env_vars(env: &RunEnv) -> Vec<&str> {
|
||||
fn requires_allow_all(key: &str) -> bool {
|
||||
let key = key.trim();
|
||||
// we could be more targted here, but there are quite a lot of
|
||||
// LD_* and DYLD_* env variables
|
||||
key.starts_with("LD_") || key.starts_with("DYLD_")
|
||||
}
|
||||
|
||||
let mut found_envs = env
|
||||
.envs
|
||||
.iter()
|
||||
.filter(|(k, v)| requires_allow_all(k) && !v.trim().is_empty())
|
||||
.map(|(k, _)| k.as_str())
|
||||
.collect::<Vec<_>>();
|
||||
found_envs.sort();
|
||||
found_envs
|
||||
}
|
||||
|
||||
#[op2]
|
||||
#[serde]
|
||||
fn op_spawn_child(
|
||||
|
@ -634,6 +711,8 @@ fn op_spawn_kill(
|
|||
}
|
||||
|
||||
mod deprecated {
|
||||
use deno_core::anyhow;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
|
@ -681,20 +760,24 @@ mod deprecated {
|
|||
#[serde] run_args: RunArgs,
|
||||
) -> Result<RunInfo, AnyError> {
|
||||
let args = run_args.cmd;
|
||||
state
|
||||
.borrow_mut::<PermissionsContainer>()
|
||||
.check_run(&args[0], "Deno.run()")?;
|
||||
let env = run_args.env;
|
||||
let cwd = run_args.cwd;
|
||||
let cmd = args.first().ok_or_else(|| anyhow::anyhow!("Missing cmd"))?;
|
||||
let (cmd, run_env) = compute_run_cmd_and_check_permissions(
|
||||
cmd,
|
||||
run_args.cwd.as_deref(),
|
||||
&run_args.env,
|
||||
/* clear env */ false,
|
||||
state,
|
||||
"Deno.run()",
|
||||
)?;
|
||||
|
||||
let mut c = Command::new(args.first().unwrap());
|
||||
(1..args.len()).for_each(|i| {
|
||||
let arg = args.get(i).unwrap();
|
||||
let mut c = Command::new(cmd);
|
||||
for arg in args.iter().skip(1) {
|
||||
c.arg(arg);
|
||||
});
|
||||
cwd.map(|d| c.current_dir(d));
|
||||
}
|
||||
c.current_dir(run_env.cwd);
|
||||
|
||||
for (key, value) in &env {
|
||||
c.env_clear();
|
||||
for (key, value) in run_env.envs {
|
||||
c.env(key, value);
|
||||
}
|
||||
|
||||
|
|
|
@ -32,7 +32,6 @@ use std::path::PathBuf;
|
|||
use std::str::FromStr;
|
||||
use std::string::ToString;
|
||||
use std::sync::Arc;
|
||||
use which::which;
|
||||
|
||||
pub mod prompter;
|
||||
use prompter::permission_prompt;
|
||||
|
@ -317,7 +316,7 @@ pub trait Descriptor: Eq + Clone + Hash {
|
|||
|
||||
/// Parse this descriptor from a list of Self::Arg, which may have been converted from
|
||||
/// command-line strings.
|
||||
fn parse(list: &Option<Vec<Self::Arg>>) -> Result<HashSet<Self>, AnyError>;
|
||||
fn parse(list: Option<&[Self::Arg]>) -> Result<HashSet<Self>, AnyError>;
|
||||
|
||||
/// Generic check function to check this descriptor against a `UnaryPermission`.
|
||||
fn check_in_permission(
|
||||
|
@ -333,9 +332,6 @@ pub trait Descriptor: Eq + Clone + Hash {
|
|||
fn stronger_than(&self, other: &Self) -> bool {
|
||||
self == other
|
||||
}
|
||||
fn aliases(&self) -> Vec<Self> {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
|
@ -423,43 +419,33 @@ impl<T: Descriptor + Hash> UnaryPermission<T> {
|
|||
desc: Option<&T>,
|
||||
allow_partial: AllowPartial,
|
||||
) -> PermissionState {
|
||||
let aliases = desc.map_or(vec![], T::aliases);
|
||||
for desc in [desc]
|
||||
.into_iter()
|
||||
.chain(aliases.iter().map(Some).collect::<Vec<_>>())
|
||||
{
|
||||
let state = if self.is_flag_denied(desc) || self.is_prompt_denied(desc) {
|
||||
PermissionState::Denied
|
||||
} else if self.is_granted(desc) {
|
||||
match allow_partial {
|
||||
AllowPartial::TreatAsGranted => PermissionState::Granted,
|
||||
AllowPartial::TreatAsDenied => {
|
||||
if self.is_partial_flag_denied(desc) {
|
||||
PermissionState::Denied
|
||||
} else {
|
||||
PermissionState::Granted
|
||||
}
|
||||
}
|
||||
AllowPartial::TreatAsPartialGranted => {
|
||||
if self.is_partial_flag_denied(desc) {
|
||||
PermissionState::GrantedPartial
|
||||
} else {
|
||||
PermissionState::Granted
|
||||
}
|
||||
if self.is_flag_denied(desc) || self.is_prompt_denied(desc) {
|
||||
PermissionState::Denied
|
||||
} else if self.is_granted(desc) {
|
||||
match allow_partial {
|
||||
AllowPartial::TreatAsGranted => PermissionState::Granted,
|
||||
AllowPartial::TreatAsDenied => {
|
||||
if self.is_partial_flag_denied(desc) {
|
||||
PermissionState::Denied
|
||||
} else {
|
||||
PermissionState::Granted
|
||||
}
|
||||
}
|
||||
AllowPartial::TreatAsPartialGranted => {
|
||||
if self.is_partial_flag_denied(desc) {
|
||||
PermissionState::GrantedPartial
|
||||
} else {
|
||||
PermissionState::Granted
|
||||
}
|
||||
}
|
||||
} else if matches!(allow_partial, AllowPartial::TreatAsDenied)
|
||||
&& self.is_partial_flag_denied(desc)
|
||||
{
|
||||
PermissionState::Denied
|
||||
} else {
|
||||
PermissionState::Prompt
|
||||
};
|
||||
if state != PermissionState::Prompt {
|
||||
return state;
|
||||
}
|
||||
} else if matches!(allow_partial, AllowPartial::TreatAsDenied)
|
||||
&& self.is_partial_flag_denied(desc)
|
||||
{
|
||||
PermissionState::Denied
|
||||
} else {
|
||||
PermissionState::Prompt
|
||||
}
|
||||
PermissionState::Prompt
|
||||
}
|
||||
|
||||
fn request_desc(
|
||||
|
@ -512,9 +498,6 @@ impl<T: Descriptor + Hash> UnaryPermission<T> {
|
|||
match desc {
|
||||
Some(desc) => {
|
||||
self.granted_list.retain(|v| !v.stronger_than(desc));
|
||||
for alias in desc.aliases() {
|
||||
self.granted_list.retain(|v| !v.stronger_than(&alias));
|
||||
}
|
||||
}
|
||||
None => {
|
||||
self.granted_global = false;
|
||||
|
@ -582,11 +565,7 @@ impl<T: Descriptor + Hash> UnaryPermission<T> {
|
|||
) {
|
||||
match desc {
|
||||
Some(desc) => {
|
||||
let aliases = desc.aliases();
|
||||
list.insert(desc);
|
||||
for alias in aliases {
|
||||
list.insert(alias);
|
||||
}
|
||||
}
|
||||
None => *list_global = true,
|
||||
}
|
||||
|
@ -612,7 +591,7 @@ impl<T: Descriptor + Hash> UnaryPermission<T> {
|
|||
ChildUnaryPermissionArg::GrantedList(granted_list) => {
|
||||
let granted: Vec<T::Arg> =
|
||||
granted_list.into_iter().map(From::from).collect();
|
||||
perms.granted_list = T::parse(&Some(granted))?;
|
||||
perms.granted_list = T::parse(Some(&granted))?;
|
||||
if !perms
|
||||
.granted_list
|
||||
.iter()
|
||||
|
@ -649,7 +628,7 @@ impl Descriptor for ReadDescriptor {
|
|||
perm.check_desc(Some(self), true, api_name, || None)
|
||||
}
|
||||
|
||||
fn parse(args: &Option<Vec<Self::Arg>>) -> Result<HashSet<Self>, AnyError> {
|
||||
fn parse(args: Option<&[Self::Arg]>) -> Result<HashSet<Self>, AnyError> {
|
||||
parse_path_list(args, ReadDescriptor)
|
||||
}
|
||||
|
||||
|
@ -681,7 +660,7 @@ impl Descriptor for WriteDescriptor {
|
|||
perm.check_desc(Some(self), true, api_name, || None)
|
||||
}
|
||||
|
||||
fn parse(args: &Option<Vec<Self::Arg>>) -> Result<HashSet<Self>, AnyError> {
|
||||
fn parse(args: Option<&[Self::Arg]>) -> Result<HashSet<Self>, AnyError> {
|
||||
parse_path_list(args, WriteDescriptor)
|
||||
}
|
||||
|
||||
|
@ -754,7 +733,7 @@ impl Descriptor for NetDescriptor {
|
|||
perm.check_desc(Some(self), false, api_name, || None)
|
||||
}
|
||||
|
||||
fn parse(args: &Option<Vec<Self::Arg>>) -> Result<HashSet<Self>, AnyError> {
|
||||
fn parse(args: Option<&[Self::Arg]>) -> Result<HashSet<Self>, AnyError> {
|
||||
parse_net_list(args)
|
||||
}
|
||||
|
||||
|
@ -864,7 +843,7 @@ impl Descriptor for EnvDescriptor {
|
|||
perm.check_desc(Some(self), false, api_name, || None)
|
||||
}
|
||||
|
||||
fn parse(list: &Option<Vec<Self::Arg>>) -> Result<HashSet<Self>, AnyError> {
|
||||
fn parse(list: Option<&[Self::Arg]>) -> Result<HashSet<Self>, AnyError> {
|
||||
parse_env_list(list)
|
||||
}
|
||||
|
||||
|
@ -883,6 +862,11 @@ impl AsRef<str> for EnvDescriptor {
|
|||
}
|
||||
}
|
||||
|
||||
pub enum RunDescriptorArg {
|
||||
Name(String),
|
||||
Path(PathBuf),
|
||||
}
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
|
||||
pub enum RunDescriptor {
|
||||
/// Warning: You may want to construct with `RunDescriptor::from()` for case
|
||||
|
@ -893,8 +877,26 @@ pub enum RunDescriptor {
|
|||
Path(PathBuf),
|
||||
}
|
||||
|
||||
impl From<String> for RunDescriptorArg {
|
||||
fn from(s: String) -> Self {
|
||||
#[cfg(windows)]
|
||||
let s = s.to_lowercase();
|
||||
let is_path = s.contains('/');
|
||||
#[cfg(windows)]
|
||||
let is_path = is_path || s.contains('\\') || Path::new(&s).is_absolute();
|
||||
if is_path {
|
||||
Self::Path(resolve_from_cwd(Path::new(&s)).unwrap())
|
||||
} else {
|
||||
match which::which(&s) {
|
||||
Ok(path) => Self::Path(path),
|
||||
Err(_) => Self::Name(s),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Descriptor for RunDescriptor {
|
||||
type Arg = String;
|
||||
type Arg = RunDescriptorArg;
|
||||
|
||||
fn check_in_permission(
|
||||
&self,
|
||||
|
@ -905,7 +907,7 @@ impl Descriptor for RunDescriptor {
|
|||
perm.check_desc(Some(self), false, api_name, || None)
|
||||
}
|
||||
|
||||
fn parse(args: &Option<Vec<Self::Arg>>) -> Result<HashSet<Self>, AnyError> {
|
||||
fn parse(args: Option<&[Self::Arg]>) -> Result<HashSet<Self>, AnyError> {
|
||||
parse_run_list(args)
|
||||
}
|
||||
|
||||
|
@ -916,16 +918,6 @@ impl Descriptor for RunDescriptor {
|
|||
fn name(&self) -> Cow<str> {
|
||||
Cow::from(self.to_string())
|
||||
}
|
||||
|
||||
fn aliases(&self) -> Vec<Self> {
|
||||
match self {
|
||||
RunDescriptor::Name(name) => match which(name) {
|
||||
Ok(path) => vec![RunDescriptor::Path(path)],
|
||||
Err(_) => vec![],
|
||||
},
|
||||
RunDescriptor::Path(_) => vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<String> for RunDescriptor {
|
||||
|
@ -938,7 +930,10 @@ impl From<String> for RunDescriptor {
|
|||
if is_path {
|
||||
Self::Path(resolve_from_cwd(Path::new(&s)).unwrap())
|
||||
} else {
|
||||
Self::Name(s)
|
||||
match which::which(&s) {
|
||||
Ok(path) => Self::Path(path),
|
||||
Err(_) => Self::Name(s),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -947,11 +942,7 @@ impl From<PathBuf> for RunDescriptor {
|
|||
fn from(p: PathBuf) -> Self {
|
||||
#[cfg(windows)]
|
||||
let p = PathBuf::from(p.to_string_lossy().to_string().to_lowercase());
|
||||
if p.is_absolute() {
|
||||
Self::Path(p)
|
||||
} else {
|
||||
Self::Path(resolve_from_cwd(&p).unwrap())
|
||||
}
|
||||
Self::Path(resolve_from_cwd(&p).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -988,7 +979,7 @@ impl Descriptor for SysDescriptor {
|
|||
perm.check_desc(Some(self), false, api_name, || None)
|
||||
}
|
||||
|
||||
fn parse(list: &Option<Vec<Self::Arg>>) -> Result<HashSet<Self>, AnyError> {
|
||||
fn parse(list: Option<&[Self::Arg]>) -> Result<HashSet<Self>, AnyError> {
|
||||
parse_sys_list(list)
|
||||
}
|
||||
|
||||
|
@ -1025,7 +1016,7 @@ impl Descriptor for FfiDescriptor {
|
|||
perm.check_desc(Some(self), true, api_name, || None)
|
||||
}
|
||||
|
||||
fn parse(list: &Option<Vec<Self::Arg>>) -> Result<HashSet<Self>, AnyError> {
|
||||
fn parse(list: Option<&[Self::Arg]>) -> Result<HashSet<Self>, AnyError> {
|
||||
parse_path_list(list, FfiDescriptor)
|
||||
}
|
||||
|
||||
|
@ -1330,15 +1321,16 @@ impl UnaryPermission<RunDescriptor> {
|
|||
|
||||
pub fn check(
|
||||
&mut self,
|
||||
cmd: &str,
|
||||
cmd: &Path,
|
||||
api_name: Option<&str>,
|
||||
) -> Result<(), AnyError> {
|
||||
debug_assert!(cmd.is_absolute());
|
||||
skip_check_if_is_permission_fully_granted!(self);
|
||||
self.check_desc(
|
||||
Some(&RunDescriptor::from(cmd.to_string())),
|
||||
Some(&RunDescriptor::Path(cmd.to_path_buf())),
|
||||
false,
|
||||
api_name,
|
||||
|| Some(format!("\"{}\"", cmd)),
|
||||
|| Some(format!("\"{}\"", cmd.display())),
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -1346,6 +1338,21 @@ impl UnaryPermission<RunDescriptor> {
|
|||
skip_check_if_is_permission_fully_granted!(self);
|
||||
self.check_desc(None, false, api_name, || None)
|
||||
}
|
||||
|
||||
/// Queries without prompting
|
||||
pub fn query_all(&mut self, api_name: Option<&str>) -> bool {
|
||||
if self.is_allow_all() {
|
||||
return true;
|
||||
}
|
||||
let (result, _prompted, _is_allow_all) =
|
||||
self.query_desc(None, AllowPartial::TreatAsDenied).check2(
|
||||
RunDescriptor::flag_name(),
|
||||
api_name,
|
||||
|| None,
|
||||
/* prompt */ false,
|
||||
);
|
||||
result.is_ok()
|
||||
}
|
||||
}
|
||||
|
||||
impl UnaryPermission<FfiDescriptor> {
|
||||
|
@ -1429,7 +1436,7 @@ pub struct PermissionsOptions {
|
|||
pub deny_ffi: Option<Vec<PathBuf>>,
|
||||
pub allow_read: Option<Vec<PathBuf>>,
|
||||
pub deny_read: Option<Vec<PathBuf>>,
|
||||
pub allow_run: Option<Vec<String>>,
|
||||
pub allow_run: Option<Vec<PathBuf>>,
|
||||
pub deny_run: Option<Vec<String>>,
|
||||
pub allow_sys: Option<Vec<String>>,
|
||||
pub deny_sys: Option<Vec<String>>,
|
||||
|
@ -1440,8 +1447,8 @@ pub struct PermissionsOptions {
|
|||
|
||||
impl Permissions {
|
||||
pub fn new_unary<T>(
|
||||
allow_list: &Option<Vec<T::Arg>>,
|
||||
deny_list: &Option<Vec<T::Arg>>,
|
||||
allow_list: Option<&[T::Arg]>,
|
||||
deny_list: Option<&[T::Arg]>,
|
||||
prompt: bool,
|
||||
) -> Result<UnaryPermission<T>, AnyError>
|
||||
where
|
||||
|
@ -1470,38 +1477,54 @@ impl Permissions {
|
|||
pub fn from_options(opts: &PermissionsOptions) -> Result<Self, AnyError> {
|
||||
Ok(Self {
|
||||
read: Permissions::new_unary(
|
||||
&opts.allow_read,
|
||||
&opts.deny_read,
|
||||
opts.allow_read.as_deref(),
|
||||
opts.deny_read.as_deref(),
|
||||
opts.prompt,
|
||||
)?,
|
||||
write: Permissions::new_unary(
|
||||
&opts.allow_write,
|
||||
&opts.deny_write,
|
||||
opts.allow_write.as_deref(),
|
||||
opts.deny_write.as_deref(),
|
||||
opts.prompt,
|
||||
)?,
|
||||
net: Permissions::new_unary(
|
||||
&opts.allow_net,
|
||||
&opts.deny_net,
|
||||
opts.allow_net.as_deref(),
|
||||
opts.deny_net.as_deref(),
|
||||
opts.prompt,
|
||||
)?,
|
||||
env: Permissions::new_unary(
|
||||
&opts.allow_env,
|
||||
&opts.deny_env,
|
||||
opts.allow_env.as_deref(),
|
||||
opts.deny_env.as_deref(),
|
||||
opts.prompt,
|
||||
)?,
|
||||
sys: Permissions::new_unary(
|
||||
&opts.allow_sys,
|
||||
&opts.deny_sys,
|
||||
opts.allow_sys.as_deref(),
|
||||
opts.deny_sys.as_deref(),
|
||||
opts.prompt,
|
||||
)?,
|
||||
run: Permissions::new_unary(
|
||||
&opts.allow_run,
|
||||
&opts.deny_run,
|
||||
opts
|
||||
.allow_run
|
||||
.as_ref()
|
||||
.map(|d| {
|
||||
d.iter()
|
||||
.map(|s| RunDescriptorArg::Path(s.clone()))
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.as_deref(),
|
||||
opts
|
||||
.deny_run
|
||||
.as_ref()
|
||||
.map(|d| {
|
||||
d.iter()
|
||||
.map(|s| RunDescriptorArg::from(s.clone()))
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
.as_deref(),
|
||||
opts.prompt,
|
||||
)?,
|
||||
ffi: Permissions::new_unary(
|
||||
&opts.allow_ffi,
|
||||
&opts.deny_ffi,
|
||||
opts.allow_ffi.as_deref(),
|
||||
opts.deny_ffi.as_deref(),
|
||||
opts.prompt,
|
||||
)?,
|
||||
all: Permissions::new_all(opts.allow_all),
|
||||
|
@ -1534,13 +1557,13 @@ 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(),
|
||||
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(),
|
||||
all: Permissions::new_all(false),
|
||||
}
|
||||
}
|
||||
|
@ -1669,7 +1692,7 @@ impl PermissionsContainer {
|
|||
#[inline(always)]
|
||||
pub fn check_run(
|
||||
&mut self,
|
||||
cmd: &str,
|
||||
cmd: &Path,
|
||||
api_name: &str,
|
||||
) -> Result<(), AnyError> {
|
||||
self.0.lock().run.check(cmd, Some(api_name))
|
||||
|
@ -1680,6 +1703,11 @@ impl PermissionsContainer {
|
|||
self.0.lock().run.check_all(Some(api_name))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn query_run_all(&mut self, api_name: &str) -> bool {
|
||||
self.0.lock().run.query_all(Some(api_name))
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn check_sys(&self, kind: &str, api_name: &str) -> Result<(), AnyError> {
|
||||
self.0.lock().sys.check(kind, Some(api_name))
|
||||
|
@ -1871,12 +1899,12 @@ const fn unit_permission_from_flag_bools(
|
|||
}
|
||||
}
|
||||
|
||||
fn global_from_option<T>(flag: &Option<Vec<T>>) -> bool {
|
||||
fn global_from_option<T>(flag: Option<&[T]>) -> bool {
|
||||
matches!(flag, Some(v) if v.is_empty())
|
||||
}
|
||||
|
||||
fn parse_net_list(
|
||||
list: &Option<Vec<String>>,
|
||||
list: Option<&[String]>,
|
||||
) -> Result<HashSet<NetDescriptor>, AnyError> {
|
||||
if let Some(v) = list {
|
||||
v.iter()
|
||||
|
@ -1888,7 +1916,7 @@ fn parse_net_list(
|
|||
}
|
||||
|
||||
fn parse_env_list(
|
||||
list: &Option<Vec<String>>,
|
||||
list: Option<&[String]>,
|
||||
) -> Result<HashSet<EnvDescriptor>, AnyError> {
|
||||
if let Some(v) = list {
|
||||
v.iter()
|
||||
|
@ -1906,7 +1934,7 @@ fn parse_env_list(
|
|||
}
|
||||
|
||||
fn parse_path_list<T: Descriptor + Hash>(
|
||||
list: &Option<Vec<PathBuf>>,
|
||||
list: Option<&[PathBuf]>,
|
||||
f: fn(PathBuf) -> T,
|
||||
) -> Result<HashSet<T>, AnyError> {
|
||||
if let Some(v) = list {
|
||||
|
@ -1925,7 +1953,7 @@ fn parse_path_list<T: Descriptor + Hash>(
|
|||
}
|
||||
|
||||
fn parse_sys_list(
|
||||
list: &Option<Vec<String>>,
|
||||
list: Option<&[String]>,
|
||||
) -> Result<HashSet<SysDescriptor>, AnyError> {
|
||||
if let Some(v) = list {
|
||||
v.iter()
|
||||
|
@ -1943,22 +1971,19 @@ fn parse_sys_list(
|
|||
}
|
||||
|
||||
fn parse_run_list(
|
||||
list: &Option<Vec<String>>,
|
||||
list: Option<&[RunDescriptorArg]>,
|
||||
) -> Result<HashSet<RunDescriptor>, AnyError> {
|
||||
let mut result = HashSet::new();
|
||||
if let Some(v) = list {
|
||||
for s in v {
|
||||
if s.is_empty() {
|
||||
return Err(AnyError::msg("Empty path is not allowed"));
|
||||
} else {
|
||||
let desc = RunDescriptor::from(s.to_string());
|
||||
let aliases = desc.aliases();
|
||||
result.insert(desc);
|
||||
result.extend(aliases);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(result)
|
||||
let Some(v) = list else {
|
||||
return Ok(HashSet::new());
|
||||
};
|
||||
Ok(
|
||||
v.iter()
|
||||
.map(|arg| match arg {
|
||||
RunDescriptorArg::Name(s) => RunDescriptor::Name(s.clone()),
|
||||
RunDescriptorArg::Path(l) => RunDescriptor::Path(l.clone()),
|
||||
})
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
|
||||
fn escalation_error() -> AnyError {
|
||||
|
@ -2298,6 +2323,9 @@ mod tests {
|
|||
macro_rules! svec {
|
||||
($($x:expr),*) => (vec![$($x.to_string()),*]);
|
||||
}
|
||||
macro_rules! sarr {
|
||||
($($x:expr),*) => ([$($x.to_string()),*]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_paths() {
|
||||
|
@ -2678,94 +2706,88 @@ mod tests {
|
|||
set_prompter(Box::new(TestPrompter));
|
||||
let perms1 = Permissions::allow_all();
|
||||
let perms2 = Permissions {
|
||||
read: Permissions::new_unary(
|
||||
&Some(vec![PathBuf::from("/foo")]),
|
||||
&None,
|
||||
false,
|
||||
)
|
||||
.unwrap(),
|
||||
read: Permissions::new_unary(Some(&[PathBuf::from("/foo")]), None, false)
|
||||
.unwrap(),
|
||||
write: Permissions::new_unary(
|
||||
&Some(vec![PathBuf::from("/foo")]),
|
||||
&None,
|
||||
Some(&[PathBuf::from("/foo")]),
|
||||
None,
|
||||
false,
|
||||
)
|
||||
.unwrap(),
|
||||
ffi: Permissions::new_unary(
|
||||
&Some(vec![PathBuf::from("/foo")]),
|
||||
&None,
|
||||
ffi: Permissions::new_unary(Some(&[PathBuf::from("/foo")]), None, false)
|
||||
.unwrap(),
|
||||
net: Permissions::new_unary(Some(&sarr!["127.0.0.1:8000"]), None, false)
|
||||
.unwrap(),
|
||||
env: Permissions::new_unary(Some(&sarr!["HOME"]), None, false).unwrap(),
|
||||
sys: Permissions::new_unary(Some(&sarr!["hostname"]), None, false)
|
||||
.unwrap(),
|
||||
run: Permissions::new_unary(
|
||||
Some(&["deno".to_string().into()]),
|
||||
None,
|
||||
false,
|
||||
)
|
||||
.unwrap(),
|
||||
net: Permissions::new_unary(&Some(svec!["127.0.0.1:8000"]), &None, false)
|
||||
.unwrap(),
|
||||
env: Permissions::new_unary(&Some(svec!["HOME"]), &None, false).unwrap(),
|
||||
sys: Permissions::new_unary(&Some(svec!["hostname"]), &None, false)
|
||||
.unwrap(),
|
||||
run: Permissions::new_unary(&Some(svec!["deno"]), &None, false).unwrap(),
|
||||
all: Permissions::new_all(false),
|
||||
};
|
||||
let perms3 = Permissions {
|
||||
read: Permissions::new_unary(
|
||||
&None,
|
||||
&Some(vec![PathBuf::from("/foo")]),
|
||||
false,
|
||||
)
|
||||
.unwrap(),
|
||||
read: Permissions::new_unary(None, Some(&[PathBuf::from("/foo")]), false)
|
||||
.unwrap(),
|
||||
write: Permissions::new_unary(
|
||||
&None,
|
||||
&Some(vec![PathBuf::from("/foo")]),
|
||||
None,
|
||||
Some(&[PathBuf::from("/foo")]),
|
||||
false,
|
||||
)
|
||||
.unwrap(),
|
||||
ffi: Permissions::new_unary(
|
||||
&None,
|
||||
&Some(vec![PathBuf::from("/foo")]),
|
||||
ffi: Permissions::new_unary(None, Some(&[PathBuf::from("/foo")]), false)
|
||||
.unwrap(),
|
||||
net: Permissions::new_unary(None, Some(&sarr!["127.0.0.1:8000"]), false)
|
||||
.unwrap(),
|
||||
env: Permissions::new_unary(None, Some(&sarr!["HOME"]), false).unwrap(),
|
||||
sys: Permissions::new_unary(None, Some(&sarr!["hostname"]), false)
|
||||
.unwrap(),
|
||||
run: Permissions::new_unary(
|
||||
None,
|
||||
Some(&["deno".to_string().into()]),
|
||||
false,
|
||||
)
|
||||
.unwrap(),
|
||||
net: Permissions::new_unary(&None, &Some(svec!["127.0.0.1:8000"]), false)
|
||||
.unwrap(),
|
||||
env: Permissions::new_unary(&None, &Some(svec!["HOME"]), false).unwrap(),
|
||||
sys: Permissions::new_unary(&None, &Some(svec!["hostname"]), false)
|
||||
.unwrap(),
|
||||
run: Permissions::new_unary(&None, &Some(svec!["deno"]), false).unwrap(),
|
||||
all: Permissions::new_all(false),
|
||||
};
|
||||
let perms4 = Permissions {
|
||||
read: Permissions::new_unary(
|
||||
&Some(vec![]),
|
||||
&Some(vec![PathBuf::from("/foo")]),
|
||||
Some(&[]),
|
||||
Some(&[PathBuf::from("/foo")]),
|
||||
false,
|
||||
)
|
||||
.unwrap(),
|
||||
write: Permissions::new_unary(
|
||||
&Some(vec![]),
|
||||
&Some(vec![PathBuf::from("/foo")]),
|
||||
Some(&[]),
|
||||
Some(&[PathBuf::from("/foo")]),
|
||||
false,
|
||||
)
|
||||
.unwrap(),
|
||||
ffi: Permissions::new_unary(
|
||||
&Some(vec![]),
|
||||
&Some(vec![PathBuf::from("/foo")]),
|
||||
Some(&[]),
|
||||
Some(&[PathBuf::from("/foo")]),
|
||||
false,
|
||||
)
|
||||
.unwrap(),
|
||||
net: Permissions::new_unary(
|
||||
&Some(vec![]),
|
||||
&Some(svec!["127.0.0.1:8000"]),
|
||||
Some(&[]),
|
||||
Some(&sarr!["127.0.0.1:8000"]),
|
||||
false,
|
||||
)
|
||||
.unwrap(),
|
||||
env: Permissions::new_unary(&Some(vec![]), &Some(svec!["HOME"]), false)
|
||||
env: Permissions::new_unary(Some(&[]), Some(&sarr!["HOME"]), false)
|
||||
.unwrap(),
|
||||
sys: Permissions::new_unary(
|
||||
&Some(vec![]),
|
||||
&Some(svec!["hostname"]),
|
||||
sys: Permissions::new_unary(Some(&[]), Some(&sarr!["hostname"]), false)
|
||||
.unwrap(),
|
||||
run: Permissions::new_unary(
|
||||
Some(&[]),
|
||||
Some(&["deno".to_string().into()]),
|
||||
false,
|
||||
)
|
||||
.unwrap(),
|
||||
run: Permissions::new_unary(&Some(vec![]), &Some(svec!["deno"]), false)
|
||||
.unwrap(),
|
||||
all: Permissions::new_all(false),
|
||||
};
|
||||
#[rustfmt::skip]
|
||||
|
@ -2894,33 +2916,38 @@ mod tests {
|
|||
set_prompter(Box::new(TestPrompter));
|
||||
let mut perms = Permissions {
|
||||
read: Permissions::new_unary(
|
||||
&Some(vec![PathBuf::from("/foo"), PathBuf::from("/foo/baz")]),
|
||||
&None,
|
||||
Some(&[PathBuf::from("/foo"), PathBuf::from("/foo/baz")]),
|
||||
None,
|
||||
false,
|
||||
)
|
||||
.unwrap(),
|
||||
write: Permissions::new_unary(
|
||||
&Some(vec![PathBuf::from("/foo"), PathBuf::from("/foo/baz")]),
|
||||
&None,
|
||||
Some(&[PathBuf::from("/foo"), PathBuf::from("/foo/baz")]),
|
||||
None,
|
||||
false,
|
||||
)
|
||||
.unwrap(),
|
||||
ffi: Permissions::new_unary(
|
||||
&Some(vec![PathBuf::from("/foo"), PathBuf::from("/foo/baz")]),
|
||||
&None,
|
||||
Some(&[PathBuf::from("/foo"), PathBuf::from("/foo/baz")]),
|
||||
None,
|
||||
false,
|
||||
)
|
||||
.unwrap(),
|
||||
net: Permissions::new_unary(
|
||||
&Some(svec!["127.0.0.1", "127.0.0.1:8000"]),
|
||||
&None,
|
||||
Some(&sarr!["127.0.0.1", "127.0.0.1:8000"]),
|
||||
None,
|
||||
false,
|
||||
)
|
||||
.unwrap(),
|
||||
env: Permissions::new_unary(&Some(svec!["HOME"]), &None, false).unwrap(),
|
||||
sys: Permissions::new_unary(&Some(svec!["hostname"]), &None, false)
|
||||
env: Permissions::new_unary(Some(&sarr!["HOME"]), None, false).unwrap(),
|
||||
sys: Permissions::new_unary(Some(&sarr!["hostname"]), None, false)
|
||||
.unwrap(),
|
||||
run: Permissions::new_unary(&Some(svec!["deno"]), &None, false).unwrap(),
|
||||
run: Permissions::new_unary(
|
||||
Some(&["deno".to_string().into()]),
|
||||
None,
|
||||
false,
|
||||
)
|
||||
.unwrap(),
|
||||
all: Permissions::new_all(false),
|
||||
};
|
||||
#[rustfmt::skip]
|
||||
|
@ -3006,11 +3033,13 @@ mod tests {
|
|||
.check(&NetDescriptor("deno.land".parse().unwrap(), None), None)
|
||||
.is_err());
|
||||
|
||||
#[allow(clippy::disallowed_methods)]
|
||||
let cwd = std::env::current_dir().unwrap();
|
||||
prompt_value.set(true);
|
||||
assert!(perms.run.check("cat", None).is_ok());
|
||||
assert!(perms.run.check(&cwd.join("cat"), None).is_ok());
|
||||
prompt_value.set(false);
|
||||
assert!(perms.run.check("cat", None).is_ok());
|
||||
assert!(perms.run.check("ls", None).is_err());
|
||||
assert!(perms.run.check(&cwd.join("cat"), None).is_ok());
|
||||
assert!(perms.run.check(&cwd.join("ls"), None).is_err());
|
||||
|
||||
prompt_value.set(true);
|
||||
assert!(perms.env.check("HOME", None).is_ok());
|
||||
|
@ -3102,12 +3131,14 @@ mod tests {
|
|||
.is_ok());
|
||||
|
||||
prompt_value.set(false);
|
||||
assert!(perms.run.check("cat", None).is_err());
|
||||
#[allow(clippy::disallowed_methods)]
|
||||
let cwd = std::env::current_dir().unwrap();
|
||||
assert!(perms.run.check(&cwd.join("cat"), None).is_err());
|
||||
prompt_value.set(true);
|
||||
assert!(perms.run.check("cat", None).is_err());
|
||||
assert!(perms.run.check("ls", None).is_ok());
|
||||
assert!(perms.run.check(&cwd.join("cat"), None).is_err());
|
||||
assert!(perms.run.check(&cwd.join("ls"), None).is_ok());
|
||||
prompt_value.set(false);
|
||||
assert!(perms.run.check("ls", None).is_ok());
|
||||
assert!(perms.run.check(&cwd.join("ls"), None).is_ok());
|
||||
|
||||
prompt_value.set(false);
|
||||
assert!(perms.env.check("HOME", None).is_err());
|
||||
|
@ -3134,7 +3165,7 @@ mod tests {
|
|||
let mut perms = Permissions::allow_all();
|
||||
perms.env = UnaryPermission {
|
||||
granted_global: false,
|
||||
..Permissions::new_unary(&Some(svec!["HOME"]), &None, false).unwrap()
|
||||
..Permissions::new_unary(Some(&sarr!["HOME"]), None, false).unwrap()
|
||||
};
|
||||
|
||||
prompt_value.set(true);
|
||||
|
@ -3150,14 +3181,14 @@ mod tests {
|
|||
fn test_check_partial_denied() {
|
||||
let mut perms = Permissions {
|
||||
read: Permissions::new_unary(
|
||||
&Some(vec![]),
|
||||
&Some(vec![PathBuf::from("/foo/bar")]),
|
||||
Some(&[]),
|
||||
Some(&[PathBuf::from("/foo/bar")]),
|
||||
false,
|
||||
)
|
||||
.unwrap(),
|
||||
write: Permissions::new_unary(
|
||||
&Some(vec![]),
|
||||
&Some(vec![PathBuf::from("/foo/bar")]),
|
||||
Some(&[]),
|
||||
Some(&[PathBuf::from("/foo/bar")]),
|
||||
false,
|
||||
)
|
||||
.unwrap(),
|
||||
|
@ -3175,8 +3206,8 @@ mod tests {
|
|||
fn test_net_fully_qualified_domain_name() {
|
||||
let mut perms = Permissions {
|
||||
net: Permissions::new_unary(
|
||||
&Some(vec!["allowed.domain".to_string(), "1.1.1.1".to_string()]),
|
||||
&Some(vec!["denied.domain".to_string(), "2.2.2.2".to_string()]),
|
||||
Some(&["allowed.domain".to_string(), "1.1.1.1".to_string()]),
|
||||
Some(&["denied.domain".to_string(), "2.2.2.2".to_string()]),
|
||||
false,
|
||||
)
|
||||
.unwrap(),
|
||||
|
@ -3341,8 +3372,8 @@ mod tests {
|
|||
fn test_create_child_permissions() {
|
||||
set_prompter(Box::new(TestPrompter));
|
||||
let mut main_perms = Permissions {
|
||||
env: Permissions::new_unary(&Some(vec![]), &None, false).unwrap(),
|
||||
net: Permissions::new_unary(&Some(svec!["foo", "bar"]), &None, false)
|
||||
env: Permissions::new_unary(Some(&[]), None, false).unwrap(),
|
||||
net: Permissions::new_unary(Some(&sarr!["foo", "bar"]), None, false)
|
||||
.unwrap(),
|
||||
..Permissions::none_without_prompt()
|
||||
};
|
||||
|
@ -3358,8 +3389,8 @@ mod tests {
|
|||
)
|
||||
.unwrap(),
|
||||
Permissions {
|
||||
env: Permissions::new_unary(&Some(vec![]), &None, false).unwrap(),
|
||||
net: Permissions::new_unary(&Some(svec!["foo"]), &None, false).unwrap(),
|
||||
env: Permissions::new_unary(Some(&[]), None, false).unwrap(),
|
||||
net: Permissions::new_unary(Some(&sarr!["foo"]), None, false).unwrap(),
|
||||
..Permissions::none_without_prompt()
|
||||
}
|
||||
);
|
||||
|
@ -3445,20 +3476,20 @@ mod tests {
|
|||
set_prompter(Box::new(TestPrompter));
|
||||
|
||||
assert!(Permissions::new_unary::<ReadDescriptor>(
|
||||
&Some(vec![Default::default()]),
|
||||
&None,
|
||||
Some(&[Default::default()]),
|
||||
None,
|
||||
false
|
||||
)
|
||||
.is_err());
|
||||
assert!(Permissions::new_unary::<EnvDescriptor>(
|
||||
&Some(vec![Default::default()]),
|
||||
&None,
|
||||
Some(&[Default::default()]),
|
||||
None,
|
||||
false
|
||||
)
|
||||
.is_err());
|
||||
assert!(Permissions::new_unary::<NetDescriptor>(
|
||||
&Some(vec![Default::default()]),
|
||||
&None,
|
||||
Some(&[Default::default()]),
|
||||
None,
|
||||
false
|
||||
)
|
||||
.is_err());
|
||||
|
|
|
@ -3683,11 +3683,6 @@ itest!(followup_dyn_import_resolved {
|
|||
output: "run/followup_dyn_import_resolves/main.ts.out",
|
||||
});
|
||||
|
||||
itest!(allow_run_allowlist_resolution {
|
||||
args: "run --quiet -A allow_run_allowlist_resolution.ts",
|
||||
output: "allow_run_allowlist_resolution.ts.out",
|
||||
});
|
||||
|
||||
itest!(unhandled_rejection {
|
||||
args: "run --check run/unhandled_rejection.ts",
|
||||
output: "run/unhandled_rejection.ts.out",
|
||||
|
@ -4592,16 +4587,32 @@ fn permission_prompt_escapes_ansi_codes_and_control_chars() {
|
|||
))
|
||||
});
|
||||
|
||||
util::with_pty(&["repl"], |mut console| {
|
||||
console.write_line_raw(r#"const boldANSI = "\u001b[1m";"#);
|
||||
console.expect("undefined");
|
||||
console.write_line_raw(r#"const unboldANSI = "\u001b[22m";"#);
|
||||
console.expect("undefined");
|
||||
console.write_line_raw(
|
||||
r#"new Deno.Command(`${boldANSI}cat${unboldANSI}`).spawn();"#,
|
||||
);
|
||||
console.expect("\u{250f} \u{26a0}\u{fe0f} Deno requests run access to \"\\u{1b}[1mcat\\u{1b}[22m\".");
|
||||
});
|
||||
// windows doesn't support backslashes in paths, so just try this on unix
|
||||
if cfg!(unix) {
|
||||
let context = TestContextBuilder::default().use_temp_cwd().build();
|
||||
context
|
||||
.new_command()
|
||||
.env("PATH", context.temp_dir().path())
|
||||
.env("DYLD_FALLBACK_LIBRARY_PATH", "")
|
||||
.env("LD_LIBRARY_PATH", "")
|
||||
.args_vec(["repl", "--allow-write=."])
|
||||
.with_pty(|mut console| {
|
||||
console.write_line_raw(r#"const boldANSI = "\u001b[1m";"#);
|
||||
console.expect("undefined");
|
||||
console.write_line_raw(r#"const unboldANSI = "\u001b[22m";"#);
|
||||
console.expect("undefined");
|
||||
console.write_line_raw(
|
||||
r#"Deno.writeTextFileSync(`${boldANSI}cat${unboldANSI}`, "");"#,
|
||||
);
|
||||
console.expect("undefined");
|
||||
console.write_line_raw(
|
||||
r#"new Deno.Command(`./${boldANSI}cat${unboldANSI}`).spawn();"#,
|
||||
);
|
||||
console
|
||||
.expect("\u{250f} \u{26a0}\u{fe0f} Deno requests run access to \"");
|
||||
console.expect("\\u{1b}[1mcat\\u{1b}[22m\"."); // ensure escaped
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
itest!(node_builtin_modules_ts {
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
{
|
||||
"tempDir": true,
|
||||
"envs": {
|
||||
"DYLD_FALLBACK_LIBRARY_PATH": "",
|
||||
"LD_LIBRARY_PATH": ""
|
||||
},
|
||||
"steps": [{
|
||||
"if": "unix",
|
||||
"args": "compile --output main main.ts",
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
error: Uncaught (in promise) PermissionDenied: Requires run access to "deno", specify the required permissions during compilation using `deno compile --allow-run`
|
||||
error: Uncaught (in promise) PermissionDenied: Requires run access to "[WILDLINE]deno[WILDLINE]", specify the required permissions during compilation using `deno compile --allow-run`
|
||||
[WILDCARD]
|
|
@ -3,6 +3,6 @@ Download http://localhost:4260/@denotest/node-addon-implicit-node-gyp
|
|||
Download http://localhost:4260/@denotest/node-addon-implicit-node-gyp/1.0.0.tgz
|
||||
Initialize @denotest/node-addon-implicit-node-gyp@1.0.0
|
||||
[UNORDERED_END]
|
||||
warning: node-gyp was used in a script, but was not listed as a dependency. Either add it as a dependency or install it globally (e.g. `npm install -g node-gyp`)
|
||||
Warning node-gyp was used in a script, but was not listed as a dependency. Either add it as a dependency or install it globally (e.g. `npm install -g node-gyp`)
|
||||
[WILDCARD]
|
||||
error: script 'install' in '@denotest/node-addon-implicit-node-gyp@1.0.0' failed with exit code 1
|
||||
|
|
10
tests/specs/permission/path_not_permitted/__test__.jsonc
Normal file
10
tests/specs/permission/path_not_permitted/__test__.jsonc
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"tempDir": true,
|
||||
"envs": {
|
||||
"LD_LIBRARY_PATH": "",
|
||||
"LD_PRELOAD": "",
|
||||
"DYLD_FALLBACK_LIBRARY_PATH": ""
|
||||
},
|
||||
"args": "run -A main.ts",
|
||||
"output": "main.out"
|
||||
}
|
11
tests/specs/permission/path_not_permitted/main.out
Normal file
11
tests/specs/permission/path_not_permitted/main.out
Normal file
|
@ -0,0 +1,11 @@
|
|||
Running...
|
||||
PermissionDenied: Requires run access to "[WILDLINE]deno[WILDLINE]", run again with the --allow-run flag
|
||||
[WILDCARD]
|
||||
at file:///[WILDLINE]/sub.ts:15:5 {
|
||||
name: "PermissionDenied"
|
||||
}
|
||||
PermissionDenied: Requires run access to "[WILDLINE]deno[WILDLINE]", run again with the --allow-run flag
|
||||
[WILDCARD]
|
||||
at file:///[WILDLINE]/sub.ts:23:22 {
|
||||
name: "PermissionDenied"
|
||||
}
|
18
tests/specs/permission/path_not_permitted/main.ts
Normal file
18
tests/specs/permission/path_not_permitted/main.ts
Normal file
|
@ -0,0 +1,18 @@
|
|||
const binaryName = Deno.build.os === "windows" ? "deno.exe" : "deno";
|
||||
Deno.copyFileSync(Deno.execPath(), binaryName);
|
||||
|
||||
console.log("Running...");
|
||||
new Deno.Command(
|
||||
Deno.execPath(),
|
||||
{
|
||||
args: [
|
||||
"run",
|
||||
"--allow-write",
|
||||
"--allow-read",
|
||||
`--allow-run=${binaryName}`,
|
||||
"sub.ts",
|
||||
],
|
||||
stderr: "inherit",
|
||||
stdout: "inherit",
|
||||
},
|
||||
).outputSync();
|
34
tests/specs/permission/path_not_permitted/sub.ts
Normal file
34
tests/specs/permission/path_not_permitted/sub.ts
Normal file
|
@ -0,0 +1,34 @@
|
|||
const binaryName = Deno.build.os === "windows" ? "deno.exe" : "deno";
|
||||
const pathSep = Deno.build.os === "windows" ? "\\" : "/";
|
||||
|
||||
Deno.mkdirSync("subdir");
|
||||
Deno.copyFileSync(binaryName, "subdir/" + binaryName);
|
||||
|
||||
try {
|
||||
const commandResult = new Deno.Command(
|
||||
binaryName,
|
||||
{
|
||||
env: { "PATH": Deno.cwd() + pathSep + "subdir" },
|
||||
stdout: "inherit",
|
||||
stderr: "inherit",
|
||||
},
|
||||
).outputSync();
|
||||
|
||||
console.log(commandResult.code);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
|
||||
try {
|
||||
const child = Deno.run(
|
||||
{
|
||||
cmd: [binaryName],
|
||||
env: { "PATH": Deno.cwd() + pathSep + "subdir" },
|
||||
stdout: "inherit",
|
||||
stderr: "inherit",
|
||||
},
|
||||
);
|
||||
console.log((await child.status()).code);
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
5
tests/specs/permission/write_allow_binary/__test__.jsonc
Normal file
5
tests/specs/permission/write_allow_binary/__test__.jsonc
Normal file
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"tempDir": true,
|
||||
"args": "run -A main.ts",
|
||||
"output": "main.out"
|
||||
}
|
6
tests/specs/permission/write_allow_binary/main.out
Normal file
6
tests/specs/permission/write_allow_binary/main.out
Normal file
|
@ -0,0 +1,6 @@
|
|||
Running...
|
||||
error: Uncaught (in promise) PermissionDenied: Requires write access to "binary[WILDLINE]", run again with the --allow-write flag
|
||||
Deno.writeTextFileSync(binaryName, "");
|
||||
^
|
||||
at [WILDCARD]
|
||||
at file:///[WILDLINE]sub.ts:3:6
|
14
tests/specs/permission/write_allow_binary/main.ts
Normal file
14
tests/specs/permission/write_allow_binary/main.ts
Normal file
|
@ -0,0 +1,14 @@
|
|||
const binaryName = Deno.build.os === "windows" ? "binary.exe" : "binary";
|
||||
Deno.copyFileSync(Deno.execPath(), binaryName);
|
||||
|
||||
console.log("Running...");
|
||||
const result = new Deno.Command(
|
||||
Deno.execPath(),
|
||||
{
|
||||
args: ["run", "--allow-write", `--allow-run=./${binaryName}`, "sub.ts"],
|
||||
stderr: "inherit",
|
||||
stdout: "inherit",
|
||||
},
|
||||
).outputSync();
|
||||
|
||||
console.assert(result.code == 1, "Expected failure");
|
3
tests/specs/permission/write_allow_binary/sub.ts
Normal file
3
tests/specs/permission/write_allow_binary/sub.ts
Normal file
|
@ -0,0 +1,3 @@
|
|||
const binaryName = Deno.build.os === "windows" ? "binary.exe" : "binary";
|
||||
|
||||
Deno.writeTextFileSync(binaryName, "");
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"args": "run --quiet -A main.ts",
|
||||
"output": "main.out",
|
||||
"envs": {
|
||||
"DYLD_FALLBACK_LIBRARY_PATH": "",
|
||||
"LD_LIBRARY_PATH": ""
|
||||
}
|
||||
}
|
|
@ -1,15 +1,15 @@
|
|||
PermissionStatus { state: "granted", onchange: null }
|
||||
PermissionStatus { state: "granted", onchange: null }
|
||||
PermissionStatus { state: "granted", onchange: null }
|
||||
PermissionStatus { state: "granted", onchange: null }
|
||||
|
||||
PermissionStatus { state: "granted", onchange: null }
|
||||
PermissionStatus { state: "prompt", onchange: null }
|
||||
PermissionStatus { state: "granted", onchange: null }
|
||||
PermissionStatus { state: "prompt", onchange: null }
|
||||
|
||||
PermissionStatus { state: "granted", onchange: null }
|
||||
PermissionStatus { state: "granted", onchange: null }
|
||||
PermissionStatus { state: "prompt", onchange: null }
|
||||
PermissionStatus { state: "granted", onchange: null }
|
||||
|
||||
---
|
||||
Info Failed to resolve 'deno' for allow-run: cannot find binary path
|
||||
PermissionStatus { state: "prompt", onchange: null }
|
||||
PermissionStatus { state: "prompt", onchange: null }
|
||||
PermissionStatus { state: "prompt", onchange: null }
|
||||
PermissionStatus { state: "prompt", onchange: null }
|
||||
---
|
||||
PermissionStatus { state: "granted", onchange: null }
|
||||
PermissionStatus { state: "granted", onchange: null }
|
||||
PermissionStatus { state: "prompt", onchange: null }
|
||||
PermissionStatus { state: "granted", onchange: null }
|
|
@ -1,26 +1,26 @@
|
|||
// Testing the following (but with `deno` instead of `echo`):
|
||||
// | `deno run --allow-run=echo` | `which path == "/usr/bin/echo"` at startup | `which path != "/usr/bin/echo"` at startup |
|
||||
// |-------------------------------------|--------------------------------------------|--------------------------------------------|
|
||||
// | **`Deno.Command("echo")`** | ✅ | ✅ |
|
||||
// | **`Deno.Command("/usr/bin/echo")`** | ✅ | ❌ |
|
||||
// | **`Deno.Command("echo")`** | ✅ | ✅ |
|
||||
// | **`Deno.Command("/usr/bin/echo")`** | ✅ | ❌ |
|
||||
|
||||
// | `deno run --allow-run=/usr/bin/echo | `which path == "/usr/bin/echo"` at runtime | `which path != "/usr/bin/echo"` at runtime |
|
||||
// |-------------------------------------|--------------------------------------------|--------------------------------------------|
|
||||
// | **`Deno.Command("echo")`** | ✅ | ❌ |
|
||||
// | **`Deno.Command("/usr/bin/echo")`** | ✅ | ✅ |
|
||||
// | **`Deno.Command("echo")`** | ✅ | ❌ |
|
||||
// | **`Deno.Command("/usr/bin/echo")`** | ✅ | ✅ |
|
||||
|
||||
const execPath = Deno.execPath();
|
||||
const execPathParent = execPath.replace(/[/\\][^/\\]+$/, "");
|
||||
|
||||
const testUrl = `data:application/typescript;base64,${
|
||||
btoa(`
|
||||
console.log(await Deno.permissions.query({ name: "run", command: "deno" }));
|
||||
console.log(await Deno.permissions.query({ name: "run", command: "${
|
||||
console.error(await Deno.permissions.query({ name: "run", command: "deno" }));
|
||||
console.error(await Deno.permissions.query({ name: "run", command: "${
|
||||
execPath.replaceAll("\\", "\\\\")
|
||||
}" }));
|
||||
Deno.env.set("PATH", "");
|
||||
console.log(await Deno.permissions.query({ name: "run", command: "deno" }));
|
||||
console.log(await Deno.permissions.query({ name: "run", command: "${
|
||||
console.error(await Deno.permissions.query({ name: "run", command: "deno" }));
|
||||
console.error(await Deno.permissions.query({ name: "run", command: "${
|
||||
execPath.replaceAll("\\", "\\\\")
|
||||
}" }));
|
||||
`)
|
||||
|
@ -29,38 +29,39 @@ const testUrl = `data:application/typescript;base64,${
|
|||
const process1 = await new Deno.Command(Deno.execPath(), {
|
||||
args: [
|
||||
"run",
|
||||
"--quiet",
|
||||
"--allow-env",
|
||||
"--allow-run=deno",
|
||||
testUrl,
|
||||
],
|
||||
stderr: "null",
|
||||
stdout: "inherit",
|
||||
stderr: "inherit",
|
||||
env: { "PATH": execPathParent },
|
||||
}).output();
|
||||
console.log(new TextDecoder().decode(process1.stdout));
|
||||
|
||||
const process2 = await new Deno.Command(Deno.execPath(), {
|
||||
console.error("---");
|
||||
|
||||
await new Deno.Command(Deno.execPath(), {
|
||||
args: [
|
||||
"run",
|
||||
"--quiet",
|
||||
"--allow-env",
|
||||
"--allow-run=deno",
|
||||
testUrl,
|
||||
],
|
||||
stderr: "null",
|
||||
stderr: "inherit",
|
||||
stdout: "inherit",
|
||||
env: { "PATH": "" },
|
||||
}).output();
|
||||
console.log(new TextDecoder().decode(process2.stdout));
|
||||
|
||||
const process3 = await new Deno.Command(Deno.execPath(), {
|
||||
console.error("---");
|
||||
|
||||
await new Deno.Command(Deno.execPath(), {
|
||||
args: [
|
||||
"run",
|
||||
"--quiet",
|
||||
"--allow-env",
|
||||
`--allow-run=${execPath}`,
|
||||
testUrl,
|
||||
],
|
||||
stderr: "null",
|
||||
stderr: "inherit",
|
||||
stdout: "inherit",
|
||||
env: { "PATH": execPathParent },
|
||||
}).output();
|
||||
console.log(new TextDecoder().decode(process3.stdout));
|
|
@ -7,13 +7,11 @@
|
|||
"tests": {
|
||||
"env_arg": {
|
||||
"args": "run --allow-run=echo env_arg.ts",
|
||||
"output": "env_arg.out",
|
||||
"exitCode": 1
|
||||
"output": "env_arg.out"
|
||||
},
|
||||
"set_with_allow_env": {
|
||||
"args": "run --allow-run=echo --allow-env set_with_allow_env.ts",
|
||||
"output": "set_with_allow_env.out",
|
||||
"exitCode": 1
|
||||
"output": "set_with_allow_env.out"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
error: Uncaught (in promise) PermissionDenied: Requires --allow-all permissions to spawn subprocess with LD_PRELOAD environment variable.
|
||||
}).spawn();
|
||||
^
|
||||
at [WILDCARD]
|
||||
PermissionDenied: Requires --allow-all permissions to spawn subprocess with LD_PRELOAD environment variable.
|
||||
[WILDCARD]
|
||||
name: "PermissionDenied"
|
||||
}
|
||||
PermissionDenied: Requires --allow-all permissions to spawn subprocess with LD_PRELOAD environment variable.
|
||||
[WILDCARD]
|
||||
name: "PermissionDenied"
|
||||
}
|
||||
|
|
|
@ -1,5 +1,20 @@
|
|||
const output = new Deno.Command("echo", {
|
||||
env: {
|
||||
"LD_PRELOAD": "./libpreload.so",
|
||||
},
|
||||
}).spawn();
|
||||
try {
|
||||
new Deno.Command("echo", {
|
||||
env: {
|
||||
"LD_PRELOAD": "./libpreload.so",
|
||||
},
|
||||
}).spawn();
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
|
||||
try {
|
||||
Deno.run({
|
||||
cmd: ["echo"],
|
||||
env: {
|
||||
"LD_PRELOAD": "./libpreload.so",
|
||||
},
|
||||
});
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
error: Uncaught (in promise) PermissionDenied: Requires --allow-all permissions to spawn subprocess with LD_PRELOAD environment variable.
|
||||
const output = new Deno.Command("echo").spawn();
|
||||
^
|
||||
at [WILDCARD]
|
||||
PermissionDenied: Requires --allow-all permissions to spawn subprocess with LD_PRELOAD environment variable.
|
||||
[WILDCARD]
|
||||
name: "PermissionDenied"
|
||||
}
|
||||
PermissionDenied: Requires --allow-all permissions to spawn subprocess with DYLD_FALLBACK_LIBRARY_PATH, LD_PRELOAD environment variables.
|
||||
[WILDCARD]
|
||||
name: "PermissionDenied"
|
||||
}
|
||||
|
|
|
@ -1,3 +1,15 @@
|
|||
Deno.env.set("LD_PRELOAD", "./libpreload.so");
|
||||
|
||||
const output = new Deno.Command("echo").spawn();
|
||||
try {
|
||||
new Deno.Command("echo").spawn();
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
|
||||
Deno.env.set("DYLD_FALLBACK_LIBRARY_PATH", "./libpreload.so");
|
||||
|
||||
try {
|
||||
Deno.run({ cmd: ["echo"] }).spawnSync();
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
|
|
2
tests/testdata/run/089_run_allow_list.ts.out
vendored
2
tests/testdata/run/089_run_allow_list.ts.out
vendored
|
@ -1,3 +1,3 @@
|
|||
[WILDCARD]PermissionDenied: Requires run access to "ls", run again with the --allow-run flag
|
||||
[WILDCARD]PermissionDenied: Requires run access to "[WILDLINE]ls[WILDLINE]", run again with the --allow-run flag
|
||||
[WILDCARD]
|
||||
true
|
||||
|
|
|
@ -611,6 +611,6 @@ Deno.test(
|
|||
p.close();
|
||||
p.stdout.close();
|
||||
assertStrictEquals(code, 1);
|
||||
assertStringIncludes(stderr, "Failed getting cwd.");
|
||||
assertStringIncludes(stderr, "failed resolving cwd:");
|
||||
},
|
||||
);
|
||||
|
|
|
@ -221,7 +221,7 @@ async function ensureNoNewITests() {
|
|||
"pm_tests.rs": 0,
|
||||
"publish_tests.rs": 0,
|
||||
"repl_tests.rs": 0,
|
||||
"run_tests.rs": 351,
|
||||
"run_tests.rs": 350,
|
||||
"shared_library_tests.rs": 0,
|
||||
"task_tests.rs": 30,
|
||||
"test_tests.rs": 75,
|
||||
|
|
Loading…
Reference in a new issue