mirror of
https://github.com/denoland/deno.git
synced 2024-11-22 15:06:54 -05:00
remove --no-prompt flag, fail on missing permissions (#3183)
This commit is contained in:
parent
edd6a90da8
commit
2804ba8674
9 changed files with 68 additions and 199 deletions
|
@ -67,6 +67,10 @@ pub fn permission_denied() -> ErrBox {
|
|||
StaticError(ErrorKind::PermissionDenied, "permission denied").into()
|
||||
}
|
||||
|
||||
pub fn permission_denied_msg(msg: String) -> ErrBox {
|
||||
DenoError::new(ErrorKind::PermissionDenied, msg).into()
|
||||
}
|
||||
|
||||
pub fn op_not_implemented() -> ErrBox {
|
||||
StaticError(ErrorKind::OpNotAvailable, "op not implemented").into()
|
||||
}
|
||||
|
@ -483,6 +487,14 @@ mod tests {
|
|||
assert_eq!(err.to_string(), "permission denied");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_permission_denied_msg() {
|
||||
let err =
|
||||
permission_denied_msg("run again with the --allow-net flag".to_string());
|
||||
assert_eq!(err.kind(), ErrorKind::PermissionDenied);
|
||||
assert_eq!(err.to_string(), "run again with the --allow-net flag");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_op_not_implemented() {
|
||||
let err = op_not_implemented();
|
||||
|
|
|
@ -118,11 +118,6 @@ fn add_run_args<'a, 'b>(app: App<'a, 'b>) -> App<'a, 'b> {
|
|||
.long("allow-all")
|
||||
.help("Allow all permissions"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("no-prompt")
|
||||
.long("no-prompt")
|
||||
.help("Do not use prompts"),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("no-fetch")
|
||||
.long("no-fetch")
|
||||
|
@ -707,9 +702,6 @@ fn parse_run_args(mut flags: DenoFlags, matches: &ArgMatches) -> DenoFlags {
|
|||
flags.allow_write = true;
|
||||
flags.allow_hrtime = true;
|
||||
}
|
||||
if matches.is_present("no-prompt") {
|
||||
flags.no_prompts = true;
|
||||
}
|
||||
if matches.is_present("no-fetch") {
|
||||
flags.no_fetch = true;
|
||||
}
|
||||
|
|
|
@ -52,13 +52,7 @@ async function main(): Promise<void> {
|
|||
console.log(`Running tests for: ${permsFmt}`);
|
||||
const cliPerms = permsToCliFlags(perms);
|
||||
// run subsequent tests using same deno executable
|
||||
const args = [
|
||||
Deno.execPath(),
|
||||
"run",
|
||||
"--no-prompt",
|
||||
...cliPerms,
|
||||
"cli/js/unit_tests.ts"
|
||||
];
|
||||
const args = [Deno.execPath(), "run", ...cliPerms, "cli/js/unit_tests.ts"];
|
||||
|
||||
const p = Deno.run({
|
||||
args,
|
||||
|
|
|
@ -1,15 +1,13 @@
|
|||
// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
|
||||
use crate::deno_error::permission_denied;
|
||||
use crate::deno_error::permission_denied_msg;
|
||||
use crate::flags::DenoFlags;
|
||||
use ansi_term::Style;
|
||||
use atty;
|
||||
use deno::ErrBox;
|
||||
use log;
|
||||
use std::collections::HashSet;
|
||||
use std::fmt;
|
||||
use std::io;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||
use std::sync::Arc;
|
||||
|
||||
const PERMISSION_EMOJI: &str = "⚠️";
|
||||
|
@ -87,21 +85,6 @@ impl PermissionAccessor {
|
|||
self.set_state(PermissionAccessorState::Ask)
|
||||
}
|
||||
|
||||
pub fn deny(&self) {
|
||||
self.set_state(PermissionAccessorState::Deny)
|
||||
}
|
||||
|
||||
/// Update this accessors state based on a PromptResult value
|
||||
/// This will only update the state if the PromptResult value
|
||||
/// is one of the "Always" values
|
||||
pub fn update_with_prompt_result(&self, prompt_result: &PromptResult) {
|
||||
match prompt_result {
|
||||
PromptResult::AllowAlways => self.allow(),
|
||||
PromptResult::DenyAlways => self.deny(),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn get_state(&self) -> PermissionAccessorState {
|
||||
self.state.load(Ordering::SeqCst).into()
|
||||
|
@ -137,7 +120,6 @@ pub struct DenoPermissions {
|
|||
pub allow_env: PermissionAccessor,
|
||||
pub allow_run: PermissionAccessor,
|
||||
pub allow_hrtime: PermissionAccessor,
|
||||
pub no_prompts: AtomicBool,
|
||||
}
|
||||
|
||||
impl DenoPermissions {
|
||||
|
@ -154,7 +136,6 @@ impl DenoPermissions {
|
|||
allow_env: PermissionAccessor::from(flags.allow_env),
|
||||
allow_run: PermissionAccessor::from(flags.allow_run),
|
||||
allow_hrtime: PermissionAccessor::from(flags.allow_hrtime),
|
||||
no_prompts: AtomicBool::new(flags.no_prompts),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -166,16 +147,12 @@ impl DenoPermissions {
|
|||
self.log_perm_access(msg);
|
||||
Ok(())
|
||||
}
|
||||
PermissionAccessorState::Ask => match self.try_permissions_prompt(msg) {
|
||||
Err(e) => Err(e),
|
||||
Ok(v) => {
|
||||
self.allow_run.update_with_prompt_result(&v);
|
||||
v.check()?;
|
||||
self.log_perm_access(msg);
|
||||
Ok(())
|
||||
}
|
||||
},
|
||||
PermissionAccessorState::Deny => Err(permission_denied()),
|
||||
PermissionAccessorState::Ask => Err(permission_denied_msg(
|
||||
"run again with the --allow-run flag".to_string(),
|
||||
)),
|
||||
PermissionAccessorState::Deny => Err(permission_denied_msg(
|
||||
"run again with the --allow-run flag".to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -192,18 +169,12 @@ impl DenoPermissions {
|
|||
Ok(())
|
||||
} else {
|
||||
match state {
|
||||
PermissionAccessorState::Ask => {
|
||||
match self.try_permissions_prompt(msg) {
|
||||
Err(e) => Err(e),
|
||||
Ok(v) => {
|
||||
self.allow_read.update_with_prompt_result(&v);
|
||||
v.check()?;
|
||||
self.log_perm_access(msg);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
PermissionAccessorState::Deny => Err(permission_denied()),
|
||||
PermissionAccessorState::Ask => Err(permission_denied_msg(
|
||||
"run again with the --allow-read flag".to_string(),
|
||||
)),
|
||||
PermissionAccessorState::Deny => Err(permission_denied_msg(
|
||||
"run again with the --allow-read flag".to_string(),
|
||||
)),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
@ -224,18 +195,12 @@ impl DenoPermissions {
|
|||
Ok(())
|
||||
} else {
|
||||
match state {
|
||||
PermissionAccessorState::Ask => {
|
||||
match self.try_permissions_prompt(msg) {
|
||||
Err(e) => Err(e),
|
||||
Ok(v) => {
|
||||
self.allow_write.update_with_prompt_result(&v);
|
||||
v.check()?;
|
||||
self.log_perm_access(msg);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
PermissionAccessorState::Deny => Err(permission_denied()),
|
||||
PermissionAccessorState::Ask => Err(permission_denied_msg(
|
||||
"run again with the --allow-write flag".to_string(),
|
||||
)),
|
||||
PermissionAccessorState::Deny => Err(permission_denied_msg(
|
||||
"run again with the --allow-write flag".to_string(),
|
||||
)),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
@ -250,7 +215,7 @@ impl DenoPermissions {
|
|||
self.log_perm_access(msg);
|
||||
Ok(())
|
||||
}
|
||||
state => {
|
||||
_state => {
|
||||
let parts = host_and_port.split(':').collect::<Vec<&str>>();
|
||||
if match parts.len() {
|
||||
2 => {
|
||||
|
@ -268,7 +233,9 @@ impl DenoPermissions {
|
|||
self.log_perm_access(msg);
|
||||
Ok(())
|
||||
} else {
|
||||
self.check_net_inner(state, msg)
|
||||
Err(permission_denied_msg(
|
||||
"run again with the --allow-net flag".to_string(),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -281,7 +248,7 @@ impl DenoPermissions {
|
|||
self.log_perm_access(msg);
|
||||
Ok(())
|
||||
}
|
||||
state => {
|
||||
_state => {
|
||||
let host = url.host().unwrap();
|
||||
let whitelist_result = {
|
||||
if self.net_whitelist.contains(&format!("{}", host)) {
|
||||
|
@ -299,34 +266,14 @@ impl DenoPermissions {
|
|||
self.log_perm_access(msg);
|
||||
Ok(())
|
||||
} else {
|
||||
self.check_net_inner(state, msg)
|
||||
Err(permission_denied_msg(
|
||||
"run again with the --allow-net flag".to_string(),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn check_net_inner(
|
||||
&self,
|
||||
state: PermissionAccessorState,
|
||||
prompt_str: &str,
|
||||
) -> Result<(), ErrBox> {
|
||||
match state {
|
||||
PermissionAccessorState::Ask => {
|
||||
match self.try_permissions_prompt(prompt_str) {
|
||||
Err(e) => Err(e),
|
||||
Ok(v) => {
|
||||
self.allow_net.update_with_prompt_result(&v);
|
||||
v.check()?;
|
||||
self.log_perm_access(prompt_str);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
PermissionAccessorState::Deny => Err(permission_denied()),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_env(&self) -> Result<(), ErrBox> {
|
||||
let msg = "access to environment variables";
|
||||
match self.allow_env.get_state() {
|
||||
|
@ -334,32 +281,13 @@ impl DenoPermissions {
|
|||
self.log_perm_access(msg);
|
||||
Ok(())
|
||||
}
|
||||
PermissionAccessorState::Ask => match self.try_permissions_prompt(msg) {
|
||||
Err(e) => Err(e),
|
||||
Ok(v) => {
|
||||
self.allow_env.update_with_prompt_result(&v);
|
||||
v.check()?;
|
||||
self.log_perm_access(msg);
|
||||
Ok(())
|
||||
PermissionAccessorState::Ask => Err(permission_denied_msg(
|
||||
"run again with the --allow-env flag".to_string(),
|
||||
)),
|
||||
PermissionAccessorState::Deny => Err(permission_denied_msg(
|
||||
"run again with the --allow-env flag".to_string(),
|
||||
)),
|
||||
}
|
||||
},
|
||||
PermissionAccessorState::Deny => Err(permission_denied()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Try to present the user with a permission prompt
|
||||
/// will error with permission_denied if no_prompts is enabled
|
||||
fn try_permissions_prompt(
|
||||
&self,
|
||||
message: &str,
|
||||
) -> Result<PromptResult, ErrBox> {
|
||||
if self.no_prompts.load(Ordering::SeqCst) {
|
||||
return Err(permission_denied());
|
||||
}
|
||||
if !atty::is(atty::Stream::Stdin) || !atty::is(atty::Stream::Stderr) {
|
||||
return Err(permission_denied());
|
||||
};
|
||||
permission_prompt(message)
|
||||
}
|
||||
|
||||
fn log_perm_access(&self, message: &str) {
|
||||
|
@ -427,60 +355,6 @@ impl DenoPermissions {
|
|||
}
|
||||
}
|
||||
|
||||
/// Quad-state value for representing user input on permission prompt
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum PromptResult {
|
||||
AllowAlways = 0,
|
||||
AllowOnce = 1,
|
||||
DenyOnce = 2,
|
||||
DenyAlways = 3,
|
||||
}
|
||||
|
||||
impl PromptResult {
|
||||
/// If value is any form of deny this will error with permission_denied
|
||||
pub fn check(&self) -> Result<(), ErrBox> {
|
||||
match self {
|
||||
PromptResult::DenyOnce => Err(permission_denied()),
|
||||
PromptResult::DenyAlways => Err(permission_denied()),
|
||||
_ => Ok(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for PromptResult {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
PromptResult::AllowAlways => f.pad("AllowAlways"),
|
||||
PromptResult::AllowOnce => f.pad("AllowOnce"),
|
||||
PromptResult::DenyOnce => f.pad("DenyOnce"),
|
||||
PromptResult::DenyAlways => f.pad("DenyAlways"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn permission_prompt(message: &str) -> Result<PromptResult, ErrBox> {
|
||||
let msg = format!("️{} Deno requests {}. Grant? [a/y/n/d (a = allow always, y = allow once, n = deny once, d = deny always)] ", PERMISSION_EMOJI, message);
|
||||
// print to stderr so that if deno is > to a file this is still displayed.
|
||||
eprint!("{}", Style::new().bold().paint(msg));
|
||||
loop {
|
||||
let mut input = String::new();
|
||||
let stdin = io::stdin();
|
||||
let _nread = stdin.read_line(&mut input)?;
|
||||
let ch = input.chars().next().unwrap();
|
||||
match ch.to_ascii_lowercase() {
|
||||
'a' => return Ok(PromptResult::AllowAlways),
|
||||
'y' => return Ok(PromptResult::AllowOnce),
|
||||
'n' => return Ok(PromptResult::DenyOnce),
|
||||
'd' => return Ok(PromptResult::DenyAlways),
|
||||
_ => {
|
||||
// If we don't get a recognized option try again.
|
||||
let msg_again = format!("Unrecognized option '{}' [a/y/n/d (a = allow always, y = allow once, n = deny once, d = deny always)] ", ch);
|
||||
eprint!("{}", Style::new().bold().paint(msg_again));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
fn check_path_white_list(
|
||||
filename: &str,
|
||||
white_list: &Arc<HashSet<String>>,
|
||||
|
@ -514,7 +388,6 @@ mod tests {
|
|||
let perms = DenoPermissions::from_flags(&DenoFlags {
|
||||
read_whitelist: whitelist.clone(),
|
||||
write_whitelist: whitelist.clone(),
|
||||
no_prompts: true,
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
|
@ -561,7 +434,6 @@ mod tests {
|
|||
"127.0.0.1",
|
||||
"172.16.0.2:8000"
|
||||
],
|
||||
no_prompts: true,
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
|
|
|
@ -24,13 +24,7 @@ async function proxyRequest(req: ServerRequest): Promise<void> {
|
|||
|
||||
async function testFetch(): Promise<void> {
|
||||
const c = Deno.run({
|
||||
args: [
|
||||
Deno.execPath(),
|
||||
"--no-prompt",
|
||||
"--reload",
|
||||
"--allow-net",
|
||||
"045_proxy_client.ts"
|
||||
],
|
||||
args: [Deno.execPath(), "--reload", "--allow-net", "045_proxy_client.ts"],
|
||||
stdout: "piped",
|
||||
env: {
|
||||
HTTP_PROXY: `http://${addr}`
|
||||
|
@ -46,7 +40,6 @@ async function testModuleDownload(): Promise<void> {
|
|||
const http = Deno.run({
|
||||
args: [
|
||||
Deno.execPath(),
|
||||
"--no-prompt",
|
||||
"--reload",
|
||||
"fetch",
|
||||
"http://localhost:4545/std/examples/colors.ts"
|
||||
|
|
|
@ -1 +1 @@
|
|||
error: Uncaught TypeError: permission denied
|
||||
error: Uncaught TypeError: run again with the --allow-net flag
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
[WILDCARD]
|
||||
error: Uncaught TypeError: permission denied
|
||||
error: Uncaught TypeError: run again with the --allow-read flag
|
||||
|
|
|
@ -311,11 +311,15 @@ itest!(_044_bad_resource {
|
|||
exit_code: 1,
|
||||
});
|
||||
|
||||
// TODO(kt3k): Temporarily skip this test because welcome.ts doesn't seem
|
||||
// working.
|
||||
/*
|
||||
itest!(_045_proxy {
|
||||
args: "run --allow-net --allow-env --allow-run --reload 045_proxy_test.ts",
|
||||
output: "045_proxy_test.ts.out",
|
||||
http_server: true,
|
||||
});
|
||||
*/
|
||||
|
||||
itest!(_046_tsx {
|
||||
args: "run --reload 046_jsx_test.tsx",
|
||||
|
@ -453,7 +457,7 @@ itest!(error_014_catch_dynamic_import_error {
|
|||
});
|
||||
|
||||
itest!(error_015_dynamic_import_permissions {
|
||||
args: "--reload --no-prompt error_015_dynamic_import_permissions.js",
|
||||
args: "--reload error_015_dynamic_import_permissions.js",
|
||||
output: "error_015_dynamic_import_permissions.out",
|
||||
check_stderr: true,
|
||||
exit_code: 1,
|
||||
|
@ -462,8 +466,7 @@ itest!(error_015_dynamic_import_permissions {
|
|||
|
||||
// We have an allow-net flag but not allow-read, it should still result in error.
|
||||
itest!(error_016_dynamic_import_permissions2 {
|
||||
args:
|
||||
"--no-prompt --reload --allow-net error_016_dynamic_import_permissions2.js",
|
||||
args: "--reload --allow-net error_016_dynamic_import_permissions2.js",
|
||||
output: "error_016_dynamic_import_permissions2.out",
|
||||
check_stderr: true,
|
||||
exit_code: 1,
|
||||
|
|
|
@ -318,16 +318,18 @@ while (true) {
|
|||
}
|
||||
```
|
||||
|
||||
When this program is started, the user is prompted for permission to listen on
|
||||
the network:
|
||||
When this program is started, it throws PermissionDenied error.
|
||||
|
||||
```shell
|
||||
$ deno https://deno.land/std/examples/echo_server.ts
|
||||
⚠️ Deno requests network access to "listen". Grant? [a/y/n/d (a = allow always, y = allow once, n = deny once, d = deny always)]
|
||||
error: Uncaught PermissionDenied: run again with the --allow-net flag
|
||||
► $deno$/dispatch_json.ts:40:11
|
||||
at DenoError ($deno$/errors.ts:20:5)
|
||||
...
|
||||
```
|
||||
|
||||
For security reasons, Deno does not allow programs to access the network without
|
||||
explicit permission. To avoid the console prompt, use a command-line flag:
|
||||
explicit permission. To allow accessing the network, use a command-line flag:
|
||||
|
||||
```shell
|
||||
$ deno --allow-net https://deno.land/std/examples/echo_server.ts
|
||||
|
@ -348,8 +350,7 @@ without further complexity.
|
|||
### Inspecting and revoking permissions
|
||||
|
||||
Sometimes a program may want to revoke previously granted permissions. When a
|
||||
program, at a later stage, needs those permissions, a new prompt will be
|
||||
presented to the user.
|
||||
program, at a later stage, needs those permissions, it will fail.
|
||||
|
||||
```ts
|
||||
const { permissions, revokePermission, open, remove } = Deno;
|
||||
|
@ -369,7 +370,7 @@ revokePermission("write");
|
|||
const encoder = new TextEncoder();
|
||||
await log.write(encoder.encode("hello\n"));
|
||||
|
||||
// this will prompt for the write permission or fail.
|
||||
// this will fail.
|
||||
await remove("request.log");
|
||||
```
|
||||
|
||||
|
@ -422,7 +423,10 @@ This is an example to restrict File system access by whitelist.
|
|||
|
||||
```shell
|
||||
$ deno --allow-read=/usr https://deno.land/std/examples/cat.ts /etc/passwd
|
||||
⚠️ Deno requests read access to "/etc/passwd". Grant? [a/y/n/d (a = allow always, y = allow once, n = deny once, d = deny always)]
|
||||
error: Uncaught PermissionDenied: run again with the --allow-read flag
|
||||
► $deno$/dispatch_json.ts:40:11
|
||||
at DenoError ($deno$/errors.ts:20:5)
|
||||
...
|
||||
```
|
||||
|
||||
You can grant read permission under `/etc` dir
|
||||
|
@ -702,7 +706,6 @@ OPTIONS:
|
|||
--importmap <FILE> Load import map file
|
||||
-L, --log-level <log-level> Set log level [possible values: debug, info]
|
||||
--no-fetch Do not download remote modules
|
||||
--no-prompt Do not use prompts
|
||||
-r, --reload=<CACHE_BLACKLIST> Reload source code cache (recompile TypeScript)
|
||||
--seed <NUMBER> Seed Math.random()
|
||||
--v8-flags=<v8-flags> Set V8 command line options
|
||||
|
|
Loading…
Reference in a new issue