mirror of
https://github.com/denoland/deno.git
synced 2025-03-03 17:34:47 -05:00
Ergonomics: Prompt TTY for permission escalation (#1081)
This commit is contained in:
parent
7f204b9803
commit
6adc87e3eb
12 changed files with 353 additions and 60 deletions
15
BUILD.gn
15
BUILD.gn
|
@ -44,24 +44,25 @@ config("deno_config") {
|
|||
}
|
||||
|
||||
main_extern = [
|
||||
"$rust_build:atty",
|
||||
"$rust_build:dirs",
|
||||
"$rust_build:futures",
|
||||
"$rust_build:getopts",
|
||||
"$rust_build:hyper",
|
||||
"$rust_build:hyper_rustls",
|
||||
"$rust_build:futures",
|
||||
"$rust_build:lazy_static",
|
||||
"$rust_build:libc",
|
||||
"$rust_build:log",
|
||||
"$rust_build:rand",
|
||||
"$rust_build:remove_dir_all",
|
||||
"$rust_build:ring",
|
||||
"$rust_build:tempfile",
|
||||
"$rust_build:rand",
|
||||
"$rust_build:tokio",
|
||||
"$rust_build:tokio_io",
|
||||
"$rust_build:tokio_fs",
|
||||
"$rust_build:tokio_executor",
|
||||
"$rust_build:tokio_fs",
|
||||
"$rust_build:tokio_io",
|
||||
"$rust_build:tokio_threadpool",
|
||||
"$rust_build:url",
|
||||
"$rust_build:remove_dir_all",
|
||||
"$rust_build:dirs",
|
||||
"$rust_build:getopts",
|
||||
"//build_extra/flatbuffers/rust:flatbuffers",
|
||||
":msg_rs",
|
||||
]
|
||||
|
|
|
@ -9,6 +9,7 @@ name = "deno"
|
|||
version = "0.0.0"
|
||||
|
||||
[dependencies]
|
||||
atty = "0.2.11"
|
||||
dirs = "1.0.4"
|
||||
flatbuffers = { path = "third_party/flatbuffers/rust/flatbuffers/" }
|
||||
futures = "0.1.25"
|
||||
|
|
|
@ -113,14 +113,15 @@ rust_crate("winapi") {
|
|||
"basetsd",
|
||||
"cfg",
|
||||
"cfgmgr32",
|
||||
"consoleapi",
|
||||
"combaseapi",
|
||||
"errhandlingapi",
|
||||
"excpt",
|
||||
"fileapi",
|
||||
"guiddef",
|
||||
"handleapi",
|
||||
"inaddr",
|
||||
"in6addr",
|
||||
"inaddr",
|
||||
"knownfolders",
|
||||
"ktmtypes",
|
||||
"libloaderapi",
|
||||
|
@ -134,6 +135,7 @@ rust_crate("winapi") {
|
|||
"objbase",
|
||||
"objidl",
|
||||
"objidlbase",
|
||||
"processenv",
|
||||
"processthreadsapi",
|
||||
"profileapi",
|
||||
"propidl",
|
||||
|
@ -152,8 +154,10 @@ rust_crate("winapi") {
|
|||
"vadefs",
|
||||
"vcruntime",
|
||||
"winbase",
|
||||
"wincon",
|
||||
"wincred",
|
||||
"windef",
|
||||
"wingdi",
|
||||
"winerror",
|
||||
"winnt",
|
||||
"winreg",
|
||||
|
@ -865,6 +869,14 @@ rust_crate("sct") {
|
|||
]
|
||||
}
|
||||
|
||||
rust_crate("atty") {
|
||||
source_root = "$registry_github/atty-0.2.11/src/lib.rs"
|
||||
extern = [
|
||||
":libc",
|
||||
":winapi",
|
||||
]
|
||||
}
|
||||
|
||||
rust_crate("base64") {
|
||||
source_root = "$registry_github/base64-0.9.2/src/lib.rs"
|
||||
extern = [
|
||||
|
|
|
@ -6,8 +6,10 @@
|
|||
|
||||
use deno_dir;
|
||||
use errors::DenoError;
|
||||
use errors::DenoResult;
|
||||
use flags;
|
||||
use libdeno;
|
||||
use permissions::DenoPermissions;
|
||||
use snapshot;
|
||||
|
||||
use futures::Future;
|
||||
|
@ -56,6 +58,7 @@ pub struct Isolate {
|
|||
pub struct IsolateState {
|
||||
pub dir: deno_dir::DenoDir,
|
||||
pub argv: Vec<String>,
|
||||
pub permissions: Mutex<DenoPermissions>,
|
||||
pub flags: flags::DenoFlags,
|
||||
tx: Mutex<Option<mpsc::Sender<(i32, Buf)>>>,
|
||||
pub metrics: Mutex<Metrics>,
|
||||
|
@ -71,6 +74,21 @@ impl IsolateState {
|
|||
tx.send((req_id, buf)).expect("tx.send error");
|
||||
}
|
||||
|
||||
pub fn check_write(&self, filename: &str) -> DenoResult<()> {
|
||||
let mut perm = self.permissions.lock().unwrap();
|
||||
perm.check_write(filename)
|
||||
}
|
||||
|
||||
pub fn check_env(&self) -> DenoResult<()> {
|
||||
let mut perm = self.permissions.lock().unwrap();
|
||||
perm.check_env()
|
||||
}
|
||||
|
||||
pub fn check_net(&self, filename: &str) -> DenoResult<()> {
|
||||
let mut perm = self.permissions.lock().unwrap();
|
||||
perm.check_net(filename)
|
||||
}
|
||||
|
||||
fn metrics_op_dispatched(
|
||||
&self,
|
||||
bytes_sent_control: u64,
|
||||
|
@ -143,6 +161,7 @@ impl Isolate {
|
|||
state: Arc::new(IsolateState {
|
||||
dir: deno_dir::DenoDir::new(flags.reload, custom_root).unwrap(),
|
||||
argv: argv_rest,
|
||||
permissions: Mutex::new(DenoPermissions::new(&flags)),
|
||||
flags,
|
||||
tx: Mutex::new(Some(tx)),
|
||||
metrics: Mutex::new(Metrics::default()),
|
||||
|
|
|
@ -31,6 +31,7 @@ mod http_util;
|
|||
mod isolate;
|
||||
mod libdeno;
|
||||
pub mod ops;
|
||||
mod permissions;
|
||||
mod resources;
|
||||
mod snapshot;
|
||||
mod tokio_util;
|
||||
|
|
100
src/ops.rs
100
src/ops.rs
|
@ -1,6 +1,5 @@
|
|||
// Copyright 2018 the Deno authors. All rights reserved. MIT license.
|
||||
use errors;
|
||||
use errors::permission_denied;
|
||||
use errors::{DenoError, DenoResult, ErrorKind};
|
||||
use fs as deno_fs;
|
||||
use http_util;
|
||||
|
@ -333,11 +332,9 @@ fn op_set_env(
|
|||
let inner = base.inner_as_set_env().unwrap();
|
||||
let key = inner.key().unwrap();
|
||||
let value = inner.value().unwrap();
|
||||
|
||||
if !state.flags.allow_env {
|
||||
return odd_future(permission_denied());
|
||||
if let Err(e) = state.check_env() {
|
||||
return odd_future(e);
|
||||
}
|
||||
|
||||
std::env::set_var(key, value);
|
||||
ok_future(empty_buf())
|
||||
}
|
||||
|
@ -350,8 +347,8 @@ fn op_env(
|
|||
assert_eq!(data.len(), 0);
|
||||
let cmd_id = base.cmd_id();
|
||||
|
||||
if !state.flags.allow_env {
|
||||
return odd_future(permission_denied());
|
||||
if let Err(e) = state.check_env() {
|
||||
return odd_future(e);
|
||||
}
|
||||
|
||||
let builder = &mut FlatBufferBuilder::new();
|
||||
|
@ -399,8 +396,9 @@ fn op_fetch_req(
|
|||
let id = inner.id();
|
||||
let url = inner.url().unwrap();
|
||||
|
||||
if !state.flags.allow_net {
|
||||
return odd_future(permission_denied());
|
||||
// FIXME use domain (or use this inside check_net)
|
||||
if let Err(e) = state.check_net(url) {
|
||||
return odd_future(e);
|
||||
}
|
||||
|
||||
let url = url.parse::<hyper::Uri>().unwrap();
|
||||
|
@ -513,8 +511,9 @@ fn op_make_temp_dir(
|
|||
let inner = base.inner_as_make_temp_dir().unwrap();
|
||||
let cmd_id = base.cmd_id();
|
||||
|
||||
if !state.flags.allow_write {
|
||||
return odd_future(permission_denied());
|
||||
// FIXME
|
||||
if let Err(e) = state.check_write("make_temp") {
|
||||
return odd_future(e);
|
||||
}
|
||||
|
||||
let dir = inner.dir().map(PathBuf::from);
|
||||
|
@ -562,10 +561,9 @@ fn op_mkdir(
|
|||
let mode = inner.mode();
|
||||
let path = String::from(inner.path().unwrap());
|
||||
|
||||
if !state.flags.allow_write {
|
||||
return odd_future(permission_denied());
|
||||
if let Err(e) = state.check_write(&path) {
|
||||
return odd_future(e);
|
||||
}
|
||||
|
||||
blocking!(base.sync(), || {
|
||||
debug!("op_mkdir {}", path);
|
||||
deno_fs::mkdir(Path::new(&path), mode)?;
|
||||
|
@ -583,8 +581,8 @@ fn op_chmod(
|
|||
let _mode = inner.mode();
|
||||
let path = String::from(inner.path().unwrap());
|
||||
|
||||
if !state.flags.allow_write {
|
||||
return odd_future(permission_denied());
|
||||
if let Err(e) = state.check_write(&path) {
|
||||
return odd_future(e);
|
||||
}
|
||||
|
||||
blocking!(base.sync(), || {
|
||||
|
@ -766,11 +764,14 @@ fn op_remove(
|
|||
) -> Box<Op> {
|
||||
assert_eq!(data.len(), 0);
|
||||
let inner = base.inner_as_remove().unwrap();
|
||||
let path = PathBuf::from(inner.path().unwrap());
|
||||
let path_ = inner.path().unwrap();
|
||||
let path = PathBuf::from(path_);
|
||||
let recursive = inner.recursive();
|
||||
if !state.flags.allow_write {
|
||||
return odd_future(permission_denied());
|
||||
|
||||
if let Err(e) = state.check_write(path.to_str().unwrap()) {
|
||||
return odd_future(e);
|
||||
}
|
||||
|
||||
blocking!(base.sync(), || {
|
||||
debug!("op_remove {}", path.display());
|
||||
let metadata = fs::metadata(&path)?;
|
||||
|
@ -831,10 +832,11 @@ fn op_copy_file(
|
|||
assert_eq!(data.len(), 0);
|
||||
let inner = base.inner_as_copy_file().unwrap();
|
||||
let from = PathBuf::from(inner.from().unwrap());
|
||||
let to = PathBuf::from(inner.to().unwrap());
|
||||
let to_ = inner.to().unwrap();
|
||||
let to = PathBuf::from(to_);
|
||||
|
||||
if !state.flags.allow_write {
|
||||
return odd_future(permission_denied());
|
||||
if let Err(e) = state.check_write(&to_) {
|
||||
return odd_future(e);
|
||||
}
|
||||
|
||||
debug!("op_copy_file {} {}", from.display(), to.display());
|
||||
|
@ -1015,14 +1017,13 @@ fn op_write_file(
|
|||
data: &'static mut [u8],
|
||||
) -> Box<Op> {
|
||||
let inner = base.inner_as_write_file().unwrap();
|
||||
|
||||
if !state.flags.allow_write {
|
||||
return odd_future(permission_denied());
|
||||
}
|
||||
|
||||
let filename = String::from(inner.filename().unwrap());
|
||||
let perm = inner.perm();
|
||||
|
||||
if let Err(e) = state.check_write(&filename) {
|
||||
return odd_future(e);
|
||||
}
|
||||
|
||||
blocking!(base.sync(), || -> OpResult {
|
||||
debug!("op_write_file {} {}", filename, data.len());
|
||||
deno_fs::write_file(Path::new(&filename), data, perm)?;
|
||||
|
@ -1036,12 +1037,13 @@ fn op_rename(
|
|||
data: &'static mut [u8],
|
||||
) -> Box<Op> {
|
||||
assert_eq!(data.len(), 0);
|
||||
if !state.flags.allow_write {
|
||||
return odd_future(permission_denied());
|
||||
}
|
||||
let inner = base.inner_as_rename().unwrap();
|
||||
let oldpath = PathBuf::from(inner.oldpath().unwrap());
|
||||
let newpath = PathBuf::from(inner.newpath().unwrap());
|
||||
let newpath_ = inner.newpath().unwrap();
|
||||
let newpath = PathBuf::from(newpath_);
|
||||
if let Err(e) = state.check_write(&newpath_) {
|
||||
return odd_future(e);
|
||||
}
|
||||
blocking!(base.sync(), || -> OpResult {
|
||||
debug!("op_rename {} {}", oldpath.display(), newpath.display());
|
||||
fs::rename(&oldpath, &newpath)?;
|
||||
|
@ -1055,8 +1057,13 @@ fn op_symlink(
|
|||
data: &'static mut [u8],
|
||||
) -> Box<Op> {
|
||||
assert_eq!(data.len(), 0);
|
||||
if !state.flags.allow_write {
|
||||
return odd_future(permission_denied());
|
||||
let inner = base.inner_as_symlink().unwrap();
|
||||
let oldname = PathBuf::from(inner.oldname().unwrap());
|
||||
let newname_ = inner.newname().unwrap();
|
||||
let newname = PathBuf::from(newname_);
|
||||
|
||||
if let Err(e) = state.check_write(&newname_) {
|
||||
return odd_future(e);
|
||||
}
|
||||
// TODO Use type for Windows.
|
||||
if cfg!(windows) {
|
||||
|
@ -1065,10 +1072,6 @@ fn op_symlink(
|
|||
"Not implemented".to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let inner = base.inner_as_symlink().unwrap();
|
||||
let oldname = PathBuf::from(inner.oldname().unwrap());
|
||||
let newname = PathBuf::from(inner.newname().unwrap());
|
||||
blocking!(base.sync(), || -> OpResult {
|
||||
debug!("op_symlink {} {}", oldname.display(), newname.display());
|
||||
#[cfg(any(unix))]
|
||||
|
@ -1118,13 +1121,14 @@ fn op_truncate(
|
|||
) -> Box<Op> {
|
||||
assert_eq!(data.len(), 0);
|
||||
|
||||
if !state.flags.allow_write {
|
||||
return odd_future(permission_denied());
|
||||
}
|
||||
|
||||
let inner = base.inner_as_truncate().unwrap();
|
||||
let filename = String::from(inner.name().unwrap());
|
||||
let len = inner.len();
|
||||
|
||||
if let Err(e) = state.check_write(&filename) {
|
||||
return odd_future(e);
|
||||
}
|
||||
|
||||
blocking!(base.sync(), || {
|
||||
debug!("op_truncate {} {}", filename, len);
|
||||
let f = fs::OpenOptions::new().write(true).open(&filename)?;
|
||||
|
@ -1139,8 +1143,8 @@ fn op_listen(
|
|||
data: &'static mut [u8],
|
||||
) -> Box<Op> {
|
||||
assert_eq!(data.len(), 0);
|
||||
if !state.flags.allow_net {
|
||||
return odd_future(permission_denied());
|
||||
if let Err(e) = state.check_net("listen") {
|
||||
return odd_future(e);
|
||||
}
|
||||
|
||||
let cmd_id = base.cmd_id();
|
||||
|
@ -1205,10 +1209,9 @@ fn op_accept(
|
|||
data: &'static mut [u8],
|
||||
) -> Box<Op> {
|
||||
assert_eq!(data.len(), 0);
|
||||
if !state.flags.allow_net {
|
||||
return odd_future(permission_denied());
|
||||
if let Err(e) = state.check_net("accept") {
|
||||
return odd_future(e);
|
||||
}
|
||||
|
||||
let cmd_id = base.cmd_id();
|
||||
let inner = base.inner_as_accept().unwrap();
|
||||
let server_rid = inner.rid();
|
||||
|
@ -1232,10 +1235,9 @@ fn op_dial(
|
|||
data: &'static mut [u8],
|
||||
) -> Box<Op> {
|
||||
assert_eq!(data.len(), 0);
|
||||
if !state.flags.allow_net {
|
||||
return odd_future(permission_denied());
|
||||
if let Err(e) = state.check_net("dial") {
|
||||
return odd_future(e);
|
||||
}
|
||||
|
||||
let cmd_id = base.cmd_id();
|
||||
let inner = base.inner_as_dial().unwrap();
|
||||
let network = inner.network().unwrap();
|
||||
|
|
86
src/permissions.rs
Normal file
86
src/permissions.rs
Normal file
|
@ -0,0 +1,86 @@
|
|||
extern crate atty;
|
||||
|
||||
use flags::DenoFlags;
|
||||
|
||||
use errors::permission_denied;
|
||||
use errors::DenoResult;
|
||||
use std::io;
|
||||
|
||||
#[derive(Debug, Default, PartialEq)]
|
||||
pub struct DenoPermissions {
|
||||
pub allow_write: bool,
|
||||
pub allow_net: bool,
|
||||
pub allow_env: bool,
|
||||
}
|
||||
|
||||
impl DenoPermissions {
|
||||
pub fn new(flags: &DenoFlags) -> DenoPermissions {
|
||||
DenoPermissions {
|
||||
allow_write: flags.allow_write,
|
||||
allow_env: flags.allow_env,
|
||||
allow_net: flags.allow_net,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_write(&mut self, filename: &str) -> DenoResult<()> {
|
||||
if self.allow_write {
|
||||
return Ok(());
|
||||
};
|
||||
// TODO get location (where access occurred)
|
||||
let r = permission_prompt(format!(
|
||||
"Deno requests write access to \"{}\".",
|
||||
filename
|
||||
));;
|
||||
if r.is_ok() {
|
||||
self.allow_write = true;
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
pub fn check_net(&mut self, domain_name: &str) -> DenoResult<()> {
|
||||
if self.allow_net {
|
||||
return Ok(());
|
||||
};
|
||||
// TODO get location (where access occurred)
|
||||
let r = permission_prompt(format!(
|
||||
"Deno requests network access to \"{}\".",
|
||||
domain_name
|
||||
));
|
||||
if r.is_ok() {
|
||||
self.allow_net = true;
|
||||
}
|
||||
r
|
||||
}
|
||||
|
||||
pub fn check_env(&mut self) -> DenoResult<()> {
|
||||
if self.allow_env {
|
||||
return Ok(());
|
||||
};
|
||||
// TODO get location (where access occurred)
|
||||
let r = permission_prompt(
|
||||
"Deno requests access to environment variables.".to_string(),
|
||||
);
|
||||
if r.is_ok() {
|
||||
self.allow_env = true;
|
||||
}
|
||||
r
|
||||
}
|
||||
}
|
||||
|
||||
fn permission_prompt(message: String) -> DenoResult<()> {
|
||||
if !atty::is(atty::Stream::Stdin) || !atty::is(atty::Stream::Stderr) {
|
||||
return Err(permission_denied());
|
||||
};
|
||||
// print to stderr so that if deno is > to a file this is still displayed.
|
||||
eprint!("{} Grant? [yN] ", message);
|
||||
let mut input = String::new();
|
||||
let stdin = io::stdin();
|
||||
let _nread = stdin.read_line(&mut input)?;
|
||||
let ch = input.chars().next().unwrap();
|
||||
let is_yes = ch == 'y' || ch == 'Y';
|
||||
if is_yes {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(permission_denied())
|
||||
}
|
||||
}
|
|
@ -1 +1 @@
|
|||
Subproject commit b93f9c8bd39a2548d60167043da6b947c023a830
|
||||
Subproject commit 56c4acce2e8ffe979b2e7d52d2b3e6f613ed492e
|
143
tools/permission_prompt_test.py
Executable file
143
tools/permission_prompt_test.py
Executable file
|
@ -0,0 +1,143 @@
|
|||
#!/usr/bin/env python
|
||||
import os
|
||||
import pty
|
||||
import select
|
||||
import subprocess
|
||||
|
||||
from util import build_path, executable_suffix
|
||||
|
||||
PERMISSIONS_PROMPT_TEST_TS = "tools/permission_prompt_test.ts"
|
||||
|
||||
|
||||
# This function is copied from:
|
||||
# https://gist.github.com/hayd/4f46a68fc697ba8888a7b517a414583e
|
||||
# https://stackoverflow.com/q/52954248/1240268
|
||||
def tty_capture(cmd, bytes_input):
|
||||
"""Capture the output of cmd with bytes_input to stdin,
|
||||
with stdin, stdout and stderr as TTYs."""
|
||||
mo, so = pty.openpty() # provide tty to enable line-buffering
|
||||
me, se = pty.openpty()
|
||||
mi, si = pty.openpty()
|
||||
fdmap = {mo: 'stdout', me: 'stderr', mi: 'stdin'}
|
||||
|
||||
p = subprocess.Popen(
|
||||
cmd, bufsize=1, stdin=si, stdout=so, stderr=se, close_fds=True)
|
||||
os.write(mi, bytes_input)
|
||||
|
||||
timeout = .04 # seconds
|
||||
res = {'stdout': b'', 'stderr': b''}
|
||||
while True:
|
||||
ready, _, _ = select.select([mo, me], [], [], timeout)
|
||||
if ready:
|
||||
for fd in ready:
|
||||
data = os.read(fd, 512)
|
||||
if not data:
|
||||
break
|
||||
res[fdmap[fd]] += data
|
||||
elif p.poll() is not None: # select timed-out
|
||||
break # p exited
|
||||
for fd in [si, so, se, mi, mo, me]:
|
||||
os.close(fd) # can't do it sooner: it leads to errno.EIO error
|
||||
p.wait()
|
||||
return p.returncode, res['stdout'], res['stderr']
|
||||
|
||||
|
||||
class Prompt(object):
|
||||
def __init__(self, deno_exe):
|
||||
self.deno_exe = deno_exe
|
||||
|
||||
def run(self,
|
||||
arg,
|
||||
bytes_input,
|
||||
allow_write=False,
|
||||
allow_net=False,
|
||||
allow_env=False):
|
||||
"Returns (return_code, stdout, stderr)."
|
||||
cmd = [self.deno_exe, PERMISSIONS_PROMPT_TEST_TS, arg]
|
||||
if allow_write:
|
||||
cmd.append("--allow-write")
|
||||
if allow_net:
|
||||
cmd.append("--allow-net")
|
||||
if allow_env:
|
||||
cmd.append("--allow-env")
|
||||
return tty_capture(cmd, bytes_input)
|
||||
|
||||
def warm_up(self):
|
||||
# ignore the ts compiling message
|
||||
self.run('needsWrite', b'', allow_write=True)
|
||||
|
||||
def test_write_yes(self):
|
||||
code, stdout, stderr = self.run('needsWrite', b'y\n')
|
||||
assert code == 0
|
||||
assert stdout == b''
|
||||
assert b'Deno requests write access' in stderr
|
||||
|
||||
def test_write_arg(self):
|
||||
code, stdout, stderr = self.run('needsWrite', b'', allow_write=True)
|
||||
assert code == 0
|
||||
assert stdout == b''
|
||||
assert stderr == b''
|
||||
|
||||
def test_write_no(self):
|
||||
code, stdout, stderr = self.run('needsWrite', b'N\n')
|
||||
assert code == 1
|
||||
# FIXME this error message should be in stderr
|
||||
assert b'PermissionDenied: permission denied' in stdout
|
||||
assert b'Deno requests write access' in stderr
|
||||
|
||||
def test_env_yes(self):
|
||||
code, stdout, stderr = self.run('needsEnv', b'y\n')
|
||||
assert code == 0
|
||||
assert stdout == b''
|
||||
assert b'Deno requests access to environment' in stderr
|
||||
|
||||
def test_env_arg(self):
|
||||
code, stdout, stderr = self.run('needsEnv', b'', allow_env=True)
|
||||
assert code == 0
|
||||
assert stdout == b''
|
||||
assert stderr == b''
|
||||
|
||||
def test_env_no(self):
|
||||
code, stdout, stderr = self.run('needsEnv', b'N\n')
|
||||
assert code == 1
|
||||
# FIXME this error message should be in stderr
|
||||
assert b'PermissionDenied: permission denied' in stdout
|
||||
assert b'Deno requests access to environment' in stderr
|
||||
|
||||
def test_net_yes(self):
|
||||
code, stdout, stderr = self.run('needsEnv', b'y\n')
|
||||
assert code == 0
|
||||
assert stdout == b''
|
||||
assert b'Deno requests access to environment' in stderr
|
||||
|
||||
def test_net_arg(self):
|
||||
code, stdout, stderr = self.run('needsNet', b'', allow_net=True)
|
||||
assert code == 0
|
||||
assert stdout == b''
|
||||
assert stderr == b''
|
||||
|
||||
def test_net_no(self):
|
||||
code, stdout, stderr = self.run('needsNet', b'N\n')
|
||||
assert code == 1
|
||||
# FIXME this error message should be in stderr
|
||||
assert b'PermissionDenied: permission denied' in stdout
|
||||
assert b'Deno requests network access' in stderr
|
||||
|
||||
|
||||
def permission_prompt_test(deno_exe):
|
||||
p = Prompt(deno_exe)
|
||||
p.warm_up()
|
||||
p.test_write_yes()
|
||||
p.test_write_arg()
|
||||
p.test_write_no()
|
||||
p.test_env_yes()
|
||||
p.test_env_arg()
|
||||
p.test_env_no()
|
||||
p.test_net_yes()
|
||||
p.test_net_arg()
|
||||
p.test_net_no()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
deno_exe = os.path.join(build_path(), "deno" + executable_suffix)
|
||||
permission_prompt_test(deno_exe)
|
21
tools/permission_prompt_test.ts
Normal file
21
tools/permission_prompt_test.ts
Normal file
|
@ -0,0 +1,21 @@
|
|||
import { args, listen, env, exit, makeTempDirSync } from "deno";
|
||||
|
||||
const name = args[1];
|
||||
const test = {
|
||||
needsWrite: () => {
|
||||
makeTempDirSync();
|
||||
},
|
||||
needsEnv: () => {
|
||||
env().home;
|
||||
},
|
||||
needsNet: () => {
|
||||
listen("tcp", "127.0.0.1:4540");
|
||||
}
|
||||
}[name];
|
||||
|
||||
if (!test) {
|
||||
console.log("Unknown test:", name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
test();
|
|
@ -60,6 +60,13 @@ def main(argv):
|
|||
|
||||
check_output_test(deno_exe)
|
||||
|
||||
# TODO We currently skip testing the prompt in Windows completely.
|
||||
# Windows does not support the pty module used for testing the permission
|
||||
# prompt.
|
||||
if os.name != 'nt':
|
||||
from permission_prompt_test import permission_prompt_test
|
||||
permission_prompt_test(deno_exe)
|
||||
|
||||
rmtree(deno_dir)
|
||||
|
||||
deno_dir_test(deno_exe, deno_dir)
|
||||
|
|
|
@ -125,8 +125,8 @@ def run_cargo():
|
|||
# If the lockfile ends up in the git repo, it'll make cargo hang for everyone
|
||||
# else who tries to run sync_third_party.
|
||||
def delete_lockfile():
|
||||
lockfiles = find_exts(
|
||||
path.join(rust_crates_path, "registry/index"), '.cargo-index-lock')
|
||||
lockfiles = find_exts([path.join(rust_crates_path, "registry/index")],
|
||||
['.cargo-index-lock'])
|
||||
for lockfile in lockfiles:
|
||||
os.remove(lockfile)
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue