mirror of
https://github.com/denoland/deno.git
synced 2025-01-12 00:54:02 -05:00
Dynamic import should respect permissions (#2764)
This commit is contained in:
parent
1947f572d7
commit
1f8b1a587c
16 changed files with 95 additions and 19 deletions
|
@ -497,7 +497,12 @@ fn op_fetch_source_file(
|
||||||
let specifier = inner.specifier().unwrap();
|
let specifier = inner.specifier().unwrap();
|
||||||
let referrer = inner.referrer().unwrap();
|
let referrer = inner.referrer().unwrap();
|
||||||
|
|
||||||
let resolved_specifier = state.resolve(specifier, referrer, false)?;
|
// TODO(ry) Maybe a security hole. Only the compiler worker should have access
|
||||||
|
// to this. Need a test to demonstrate the hole.
|
||||||
|
let is_dyn_import = false;
|
||||||
|
|
||||||
|
let resolved_specifier =
|
||||||
|
state.resolve(specifier, referrer, false, is_dyn_import)?;
|
||||||
|
|
||||||
let fut = state
|
let fut = state
|
||||||
.file_fetcher
|
.file_fetcher
|
||||||
|
@ -750,7 +755,7 @@ fn op_fetch(
|
||||||
let req = msg_util::deserialize_request(header, body)?;
|
let req = msg_util::deserialize_request(header, body)?;
|
||||||
|
|
||||||
let url_ = url::Url::parse(url).map_err(ErrBox::from)?;
|
let url_ = url::Url::parse(url).map_err(ErrBox::from)?;
|
||||||
state.check_net_url(url_)?;
|
state.check_net_url(&url_)?;
|
||||||
|
|
||||||
let client = http_util::get_client();
|
let client = http_util::get_client();
|
||||||
|
|
||||||
|
|
|
@ -274,7 +274,7 @@ impl DenoPermissions {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn check_net_url(&self, url: url::Url) -> Result<(), ErrBox> {
|
pub fn check_net_url(&self, url: &url::Url) -> Result<(), ErrBox> {
|
||||||
let msg = &format!("network access to \"{}\"", url);
|
let msg = &format!("network access to \"{}\"", url);
|
||||||
match self.allow_net.get_state() {
|
match self.allow_net.get_state() {
|
||||||
PermissionAccessorState::Allow => {
|
PermissionAccessorState::Allow => {
|
||||||
|
@ -627,7 +627,7 @@ mod tests {
|
||||||
|
|
||||||
for (url_str, is_ok) in url_tests.iter() {
|
for (url_str, is_ok) in url_tests.iter() {
|
||||||
let u = url::Url::parse(url_str).unwrap();
|
let u = url::Url::parse(url_str).unwrap();
|
||||||
assert_eq!(*is_ok, perms.check_net_url(u).is_ok());
|
assert_eq!(*is_ok, perms.check_net_url(&u).is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
for (domain, is_ok) in domain_tests.iter() {
|
for (domain, is_ok) in domain_tests.iter() {
|
||||||
|
|
36
cli/state.rs
36
cli/state.rs
|
@ -4,6 +4,7 @@ use crate::compilers::JsCompiler;
|
||||||
use crate::compilers::JsonCompiler;
|
use crate::compilers::JsonCompiler;
|
||||||
use crate::compilers::TsCompiler;
|
use crate::compilers::TsCompiler;
|
||||||
use crate::deno_dir;
|
use crate::deno_dir;
|
||||||
|
use crate::deno_error::permission_denied;
|
||||||
use crate::file_fetcher::SourceFileFetcher;
|
use crate::file_fetcher::SourceFileFetcher;
|
||||||
use crate::flags;
|
use crate::flags;
|
||||||
use crate::global_timer::GlobalTimer;
|
use crate::global_timer::GlobalTimer;
|
||||||
|
@ -119,6 +120,7 @@ impl Loader for ThreadSafeState {
|
||||||
specifier: &str,
|
specifier: &str,
|
||||||
referrer: &str,
|
referrer: &str,
|
||||||
is_main: bool,
|
is_main: bool,
|
||||||
|
is_dyn_import: bool,
|
||||||
) -> Result<ModuleSpecifier, ErrBox> {
|
) -> Result<ModuleSpecifier, ErrBox> {
|
||||||
if !is_main {
|
if !is_main {
|
||||||
if let Some(import_map) = &self.import_map {
|
if let Some(import_map) = &self.import_map {
|
||||||
|
@ -128,8 +130,14 @@ impl Loader for ThreadSafeState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let module_specifier =
|
||||||
|
ModuleSpecifier::resolve_import(specifier, referrer)?;
|
||||||
|
|
||||||
ModuleSpecifier::resolve_import(specifier, referrer).map_err(ErrBox::from)
|
if is_dyn_import {
|
||||||
|
self.check_dyn_import(&module_specifier)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(module_specifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Given an absolute url, load its source code.
|
/// Given an absolute url, load its source code.
|
||||||
|
@ -294,7 +302,7 @@ impl ThreadSafeState {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn check_net_url(&self, url: url::Url) -> Result<(), ErrBox> {
|
pub fn check_net_url(&self, url: &url::Url) -> Result<(), ErrBox> {
|
||||||
self.permissions.check_net_url(url)
|
self.permissions.check_net_url(url)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -303,6 +311,30 @@ impl ThreadSafeState {
|
||||||
self.permissions.check_run()
|
self.permissions.check_run()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn check_dyn_import(
|
||||||
|
self: &Self,
|
||||||
|
module_specifier: &ModuleSpecifier,
|
||||||
|
) -> Result<(), ErrBox> {
|
||||||
|
let u = module_specifier.as_url();
|
||||||
|
match u.scheme() {
|
||||||
|
"http" | "https" => {
|
||||||
|
self.check_net_url(u)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
"file" => {
|
||||||
|
let filename = u
|
||||||
|
.to_file_path()
|
||||||
|
.unwrap()
|
||||||
|
.into_os_string()
|
||||||
|
.into_string()
|
||||||
|
.unwrap();
|
||||||
|
self.check_read(&filename)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
_ => Err(permission_denied()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub fn mock(argv: Vec<String>) -> ThreadSafeState {
|
pub fn mock(argv: Vec<String>) -> ThreadSafeState {
|
||||||
ThreadSafeState::new(
|
ThreadSafeState::new(
|
||||||
|
|
|
@ -40,6 +40,7 @@ pub trait Loader: Send + Sync {
|
||||||
specifier: &str,
|
specifier: &str,
|
||||||
referrer: &str,
|
referrer: &str,
|
||||||
is_main: bool,
|
is_main: bool,
|
||||||
|
is_dyn_import: bool,
|
||||||
) -> Result<ModuleSpecifier, ErrBox>;
|
) -> Result<ModuleSpecifier, ErrBox>;
|
||||||
|
|
||||||
/// Given ModuleSpecifier, load its source code.
|
/// Given ModuleSpecifier, load its source code.
|
||||||
|
@ -125,12 +126,15 @@ impl<L: Loader> RecursiveLoad<L> {
|
||||||
|
|
||||||
fn add_root(&mut self) -> Result<(), ErrBox> {
|
fn add_root(&mut self) -> Result<(), ErrBox> {
|
||||||
let module_specifier = match self.state {
|
let module_specifier = match self.state {
|
||||||
State::ResolveMain(ref specifier) => {
|
State::ResolveMain(ref specifier) => self.loader.resolve(
|
||||||
self.loader.resolve(specifier, ".", true)?
|
specifier,
|
||||||
}
|
".",
|
||||||
State::ResolveImport(ref specifier, ref referrer) => {
|
true,
|
||||||
self.loader.resolve(specifier, referrer, false)?
|
self.dyn_import_id().is_some(),
|
||||||
}
|
)?,
|
||||||
|
State::ResolveImport(ref specifier, ref referrer) => self
|
||||||
|
.loader
|
||||||
|
.resolve(specifier, referrer, false, self.dyn_import_id().is_some())?,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -160,7 +164,12 @@ impl<L: Loader> RecursiveLoad<L> {
|
||||||
referrer: &str,
|
referrer: &str,
|
||||||
parent_id: deno_mod,
|
parent_id: deno_mod,
|
||||||
) -> Result<(), ErrBox> {
|
) -> Result<(), ErrBox> {
|
||||||
let module_specifier = self.loader.resolve(specifier, referrer, false)?;
|
let module_specifier = self.loader.resolve(
|
||||||
|
specifier,
|
||||||
|
referrer,
|
||||||
|
false,
|
||||||
|
self.dyn_import_id().is_some(),
|
||||||
|
)?;
|
||||||
let module_name = module_specifier.as_str();
|
let module_name = module_specifier.as_str();
|
||||||
|
|
||||||
let mut modules = self.modules.lock().unwrap();
|
let mut modules = self.modules.lock().unwrap();
|
||||||
|
@ -277,7 +286,12 @@ impl<L: Loader> ImportStream for RecursiveLoad<L> {
|
||||||
|specifier: &str, referrer_id: deno_mod| -> deno_mod {
|
|specifier: &str, referrer_id: deno_mod| -> deno_mod {
|
||||||
let modules = self.modules.lock().unwrap();
|
let modules = self.modules.lock().unwrap();
|
||||||
let referrer = modules.get_name(referrer_id).unwrap();
|
let referrer = modules.get_name(referrer_id).unwrap();
|
||||||
match self.loader.resolve(specifier, &referrer, is_main) {
|
match self.loader.resolve(
|
||||||
|
specifier,
|
||||||
|
&referrer,
|
||||||
|
is_main,
|
||||||
|
self.dyn_import_id().is_some(),
|
||||||
|
) {
|
||||||
Ok(specifier) => modules.get_id(specifier.as_str()).unwrap_or(0),
|
Ok(specifier) => modules.get_id(specifier.as_str()).unwrap_or(0),
|
||||||
// We should have already resolved and Ready this module, so
|
// We should have already resolved and Ready this module, so
|
||||||
// resolve() will not fail this time.
|
// resolve() will not fail this time.
|
||||||
|
@ -675,6 +689,7 @@ mod tests {
|
||||||
specifier: &str,
|
specifier: &str,
|
||||||
referrer: &str,
|
referrer: &str,
|
||||||
_is_root: bool,
|
_is_root: bool,
|
||||||
|
_is_dyn_import: bool,
|
||||||
) -> Result<ModuleSpecifier, ErrBox> {
|
) -> Result<ModuleSpecifier, ErrBox> {
|
||||||
let referrer = if referrer == "." {
|
let referrer = if referrer == "." {
|
||||||
"file:///"
|
"file:///"
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
args: tests/013_dynamic_import.ts --reload
|
args: tests/013_dynamic_import.ts --reload --allow-read
|
||||||
output: tests/013_dynamic_import.ts.out
|
output: tests/013_dynamic_import.ts.out
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
args: tests/014_duplicate_import.ts --reload
|
args: tests/014_duplicate_import.ts --reload --allow-read
|
||||||
output: tests/014_duplicate_import.ts.out
|
output: tests/014_duplicate_import.ts.out
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
args: tests/015_duplicate_parallel_import.js --reload
|
args: tests/015_duplicate_parallel_import.js --reload --allow-read
|
||||||
output: tests/015_duplicate_parallel_import.js.out
|
output: tests/015_duplicate_parallel_import.js.out
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
args: run --reload tests/042_dyn_import_evalcontext.ts
|
args: run --allow-read --reload tests/042_dyn_import_evalcontext.ts
|
||||||
output: tests/042_dyn_import_evalcontext.ts.out
|
output: tests/042_dyn_import_evalcontext.ts.out
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
args: tests/error_014_catch_dynamic_import_error.js --reload
|
args: tests/error_014_catch_dynamic_import_error.js --reload --allow-read
|
||||||
output: tests/error_014_catch_dynamic_import_error.js.out
|
output: tests/error_014_catch_dynamic_import_error.js.out
|
||||||
|
|
3
tests/error_015_dynamic_import_permissions.js
Normal file
3
tests/error_015_dynamic_import_permissions.js
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
(async () => {
|
||||||
|
await import("http://localhost:4545/tests/subdir/mod4.js");
|
||||||
|
})();
|
1
tests/error_015_dynamic_import_permissions.out
Normal file
1
tests/error_015_dynamic_import_permissions.out
Normal file
|
@ -0,0 +1 @@
|
||||||
|
error: Uncaught TypeError: permission denied
|
4
tests/error_015_dynamic_import_permissions.test
Normal file
4
tests/error_015_dynamic_import_permissions.test
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
args: --reload --no-prompt tests/error_015_dynamic_import_permissions.js
|
||||||
|
output: tests/error_015_dynamic_import_permissions.out
|
||||||
|
check_stderr: true
|
||||||
|
exit_code: 1
|
5
tests/error_016_dynamic_import_permissions2.js
Normal file
5
tests/error_016_dynamic_import_permissions2.js
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
// If this is executed with --allow-net but not --allow-read the following
|
||||||
|
// import should cause a permission denied error.
|
||||||
|
(async () => {
|
||||||
|
await import("http://localhost:4545/tests/subdir/evil_remote_import.js");
|
||||||
|
})();
|
2
tests/error_016_dynamic_import_permissions2.out
Normal file
2
tests/error_016_dynamic_import_permissions2.out
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
[WILDCARD]
|
||||||
|
error: Uncaught TypeError: permission denied
|
5
tests/error_016_dynamic_import_permissions2.test
Normal file
5
tests/error_016_dynamic_import_permissions2.test
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
# We have an allow-net flag but not allow-read, it should still result in error.
|
||||||
|
args: --no-prompt --reload --allow-net tests/error_016_dynamic_import_permissions2.js
|
||||||
|
output: tests/error_016_dynamic_import_permissions2.out
|
||||||
|
check_stderr: true
|
||||||
|
exit_code: 1
|
4
tests/subdir/evil_remote_import.js
Normal file
4
tests/subdir/evil_remote_import.js
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
// We expect to get a permission denied error if we dynamically
|
||||||
|
// import this module without --allow-read.
|
||||||
|
export * from "file:///c:/etc/passwd";
|
||||||
|
console.log("Hello from evil_remote_import.js");
|
Loading…
Reference in a new issue