mirror of
https://github.com/denoland/deno.git
synced 2024-12-22 07:14:47 -05:00
fix(core): don't allow to import internal code is snapshot is loaded (#17694)
Addressing Luca concerns from https://github.com/denoland/deno/pull/17648#discussion_r1099352286
This commit is contained in:
parent
19543ffec3
commit
bef50416b9
3 changed files with 190 additions and 32 deletions
|
@ -10,6 +10,7 @@ use v8::MapFnTo;
|
||||||
use crate::error::is_instance_of_error;
|
use crate::error::is_instance_of_error;
|
||||||
use crate::modules::get_asserted_module_type_from_assertions;
|
use crate::modules::get_asserted_module_type_from_assertions;
|
||||||
use crate::modules::parse_import_assertions;
|
use crate::modules::parse_import_assertions;
|
||||||
|
use crate::modules::resolve_helper;
|
||||||
use crate::modules::validate_import_assertions;
|
use crate::modules::validate_import_assertions;
|
||||||
use crate::modules::ImportAssertionsKind;
|
use crate::modules::ImportAssertionsKind;
|
||||||
use crate::modules::ModuleMap;
|
use crate::modules::ModuleMap;
|
||||||
|
@ -379,9 +380,12 @@ fn import_meta_resolve(
|
||||||
url_prop.to_rust_string_lossy(scope)
|
url_prop.to_rust_string_lossy(scope)
|
||||||
};
|
};
|
||||||
let module_map_rc = JsRuntime::module_map(scope);
|
let module_map_rc = JsRuntime::module_map(scope);
|
||||||
let loader = {
|
let (loader, snapshot_loaded_and_not_snapshotting) = {
|
||||||
let module_map = module_map_rc.borrow();
|
let module_map = module_map_rc.borrow();
|
||||||
module_map.loader.clone()
|
(
|
||||||
|
module_map.loader.clone(),
|
||||||
|
module_map.snapshot_loaded_and_not_snapshotting,
|
||||||
|
)
|
||||||
};
|
};
|
||||||
let specifier_str = specifier.to_rust_string_lossy(scope);
|
let specifier_str = specifier.to_rust_string_lossy(scope);
|
||||||
|
|
||||||
|
@ -390,8 +394,13 @@ fn import_meta_resolve(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
match loader.resolve(&specifier_str, &referrer, ResolutionKind::DynamicImport)
|
match resolve_helper(
|
||||||
{
|
snapshot_loaded_and_not_snapshotting,
|
||||||
|
loader,
|
||||||
|
&specifier_str,
|
||||||
|
&referrer,
|
||||||
|
ResolutionKind::DynamicImport,
|
||||||
|
) {
|
||||||
Ok(resolved) => {
|
Ok(resolved) => {
|
||||||
let resolved_val = serde_v8::to_v8(scope, resolved.as_str()).unwrap();
|
let resolved_val = serde_v8::to_v8(scope, resolved.as_str()).unwrap();
|
||||||
rv.set(resolved_val);
|
rv.set(resolved_val);
|
||||||
|
|
131
core/modules.rs
131
core/modules.rs
|
@ -292,6 +292,26 @@ impl ModuleLoader for NoopModuleLoader {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Helper function, that calls into `loader.resolve()`, but denies resolution
|
||||||
|
/// of `internal` scheme if we are running with a snapshot loaded and not
|
||||||
|
/// creating a snapshot
|
||||||
|
pub(crate) fn resolve_helper(
|
||||||
|
snapshot_loaded_and_not_snapshotting: bool,
|
||||||
|
loader: Rc<dyn ModuleLoader>,
|
||||||
|
specifier: &str,
|
||||||
|
referrer: &str,
|
||||||
|
kind: ResolutionKind,
|
||||||
|
) -> Result<ModuleSpecifier, Error> {
|
||||||
|
if snapshot_loaded_and_not_snapshotting && specifier.starts_with("internal:")
|
||||||
|
{
|
||||||
|
return Err(generic_error(
|
||||||
|
"Cannot load internal module from external code",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
loader.resolve(specifier, referrer, kind)
|
||||||
|
}
|
||||||
|
|
||||||
pub struct InternalModuleLoader(Rc<dyn ModuleLoader>);
|
pub struct InternalModuleLoader(Rc<dyn ModuleLoader>);
|
||||||
|
|
||||||
impl InternalModuleLoader {
|
impl InternalModuleLoader {
|
||||||
|
@ -440,10 +460,11 @@ pub(crate) struct RecursiveModuleLoad {
|
||||||
module_map_rc: Rc<RefCell<ModuleMap>>,
|
module_map_rc: Rc<RefCell<ModuleMap>>,
|
||||||
pending: FuturesUnordered<Pin<Box<ModuleLoadFuture>>>,
|
pending: FuturesUnordered<Pin<Box<ModuleLoadFuture>>>,
|
||||||
visited: HashSet<ModuleRequest>,
|
visited: HashSet<ModuleRequest>,
|
||||||
// These two fields are copied from `module_map_rc`, but they are cloned ahead
|
// These three fields are copied from `module_map_rc`, but they are cloned
|
||||||
// of time to avoid already-borrowed errors.
|
// ahead of time to avoid already-borrowed errors.
|
||||||
op_state: Rc<RefCell<OpState>>,
|
op_state: Rc<RefCell<OpState>>,
|
||||||
loader: Rc<dyn ModuleLoader>,
|
loader: Rc<dyn ModuleLoader>,
|
||||||
|
snapshot_loaded_and_not_snapshotting: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RecursiveModuleLoad {
|
impl RecursiveModuleLoad {
|
||||||
|
@ -499,6 +520,9 @@ impl RecursiveModuleLoad {
|
||||||
init,
|
init,
|
||||||
state: LoadState::Init,
|
state: LoadState::Init,
|
||||||
module_map_rc: module_map_rc.clone(),
|
module_map_rc: module_map_rc.clone(),
|
||||||
|
snapshot_loaded_and_not_snapshotting: module_map_rc
|
||||||
|
.borrow()
|
||||||
|
.snapshot_loaded_and_not_snapshotting,
|
||||||
op_state,
|
op_state,
|
||||||
loader,
|
loader,
|
||||||
pending: FuturesUnordered::new(),
|
pending: FuturesUnordered::new(),
|
||||||
|
@ -527,39 +551,60 @@ impl RecursiveModuleLoad {
|
||||||
|
|
||||||
fn resolve_root(&self) -> Result<ModuleSpecifier, Error> {
|
fn resolve_root(&self) -> Result<ModuleSpecifier, Error> {
|
||||||
match self.init {
|
match self.init {
|
||||||
LoadInit::Main(ref specifier) => {
|
LoadInit::Main(ref specifier) => resolve_helper(
|
||||||
self
|
self.snapshot_loaded_and_not_snapshotting,
|
||||||
.loader
|
self.loader.clone(),
|
||||||
.resolve(specifier, ".", ResolutionKind::MainModule)
|
specifier,
|
||||||
|
".",
|
||||||
|
ResolutionKind::MainModule,
|
||||||
|
),
|
||||||
|
LoadInit::Side(ref specifier) => resolve_helper(
|
||||||
|
self.snapshot_loaded_and_not_snapshotting,
|
||||||
|
self.loader.clone(),
|
||||||
|
specifier,
|
||||||
|
".",
|
||||||
|
ResolutionKind::Import,
|
||||||
|
),
|
||||||
|
LoadInit::DynamicImport(ref specifier, ref referrer, _) => {
|
||||||
|
resolve_helper(
|
||||||
|
self.snapshot_loaded_and_not_snapshotting,
|
||||||
|
self.loader.clone(),
|
||||||
|
specifier,
|
||||||
|
referrer,
|
||||||
|
ResolutionKind::DynamicImport,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
LoadInit::Side(ref specifier) => {
|
|
||||||
self.loader.resolve(specifier, ".", ResolutionKind::Import)
|
|
||||||
}
|
|
||||||
LoadInit::DynamicImport(ref specifier, ref referrer, _) => self
|
|
||||||
.loader
|
|
||||||
.resolve(specifier, referrer, ResolutionKind::DynamicImport),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn prepare(&self) -> Result<(), Error> {
|
async fn prepare(&self) -> Result<(), Error> {
|
||||||
let op_state = self.op_state.clone();
|
let op_state = self.op_state.clone();
|
||||||
|
|
||||||
let (module_specifier, maybe_referrer) = match self.init {
|
let (module_specifier, maybe_referrer) = match self.init {
|
||||||
LoadInit::Main(ref specifier) => {
|
LoadInit::Main(ref specifier) => {
|
||||||
let spec =
|
let spec = resolve_helper(
|
||||||
self
|
self.snapshot_loaded_and_not_snapshotting,
|
||||||
.loader
|
self.loader.clone(),
|
||||||
.resolve(specifier, ".", ResolutionKind::MainModule)?;
|
specifier,
|
||||||
|
".",
|
||||||
|
ResolutionKind::MainModule,
|
||||||
|
)?;
|
||||||
(spec, None)
|
(spec, None)
|
||||||
}
|
}
|
||||||
LoadInit::Side(ref specifier) => {
|
LoadInit::Side(ref specifier) => {
|
||||||
let spec =
|
let spec = resolve_helper(
|
||||||
self
|
self.snapshot_loaded_and_not_snapshotting,
|
||||||
.loader
|
self.loader.clone(),
|
||||||
.resolve(specifier, ".", ResolutionKind::Import)?;
|
specifier,
|
||||||
|
".",
|
||||||
|
ResolutionKind::Import,
|
||||||
|
)?;
|
||||||
(spec, None)
|
(spec, None)
|
||||||
}
|
}
|
||||||
LoadInit::DynamicImport(ref specifier, ref referrer, _) => {
|
LoadInit::DynamicImport(ref specifier, ref referrer, _) => {
|
||||||
let spec = self.loader.resolve(
|
let spec = resolve_helper(
|
||||||
|
self.snapshot_loaded_and_not_snapshotting,
|
||||||
|
self.loader.clone(),
|
||||||
specifier,
|
specifier,
|
||||||
referrer,
|
referrer,
|
||||||
ResolutionKind::DynamicImport,
|
ResolutionKind::DynamicImport,
|
||||||
|
@ -868,6 +913,8 @@ pub(crate) struct ModuleMap {
|
||||||
// This store is used temporarly, to forward parsed JSON
|
// This store is used temporarly, to forward parsed JSON
|
||||||
// value from `new_json_module` to `json_module_evaluation_steps`
|
// value from `new_json_module` to `json_module_evaluation_steps`
|
||||||
json_value_store: HashMap<v8::Global<v8::Module>, v8::Global<v8::Value>>,
|
json_value_store: HashMap<v8::Global<v8::Module>, v8::Global<v8::Value>>,
|
||||||
|
|
||||||
|
pub(crate) snapshot_loaded_and_not_snapshotting: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ModuleMap {
|
impl ModuleMap {
|
||||||
|
@ -945,6 +992,7 @@ impl ModuleMap {
|
||||||
pub(crate) fn new(
|
pub(crate) fn new(
|
||||||
loader: Rc<dyn ModuleLoader>,
|
loader: Rc<dyn ModuleLoader>,
|
||||||
op_state: Rc<RefCell<OpState>>,
|
op_state: Rc<RefCell<OpState>>,
|
||||||
|
snapshot_loaded_and_not_snapshotting: bool,
|
||||||
) -> ModuleMap {
|
) -> ModuleMap {
|
||||||
Self {
|
Self {
|
||||||
handles: vec![],
|
handles: vec![],
|
||||||
|
@ -957,6 +1005,7 @@ impl ModuleMap {
|
||||||
preparing_dynamic_imports: FuturesUnordered::new(),
|
preparing_dynamic_imports: FuturesUnordered::new(),
|
||||||
pending_dynamic_imports: FuturesUnordered::new(),
|
pending_dynamic_imports: FuturesUnordered::new(),
|
||||||
json_value_store: HashMap::new(),
|
json_value_store: HashMap::new(),
|
||||||
|
snapshot_loaded_and_not_snapshotting,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1083,7 +1132,9 @@ impl ModuleMap {
|
||||||
return Err(ModuleError::Exception(exception));
|
return Err(ModuleError::Exception(exception));
|
||||||
}
|
}
|
||||||
|
|
||||||
let module_specifier = match self.loader.resolve(
|
let module_specifier = match resolve_helper(
|
||||||
|
self.snapshot_loaded_and_not_snapshotting,
|
||||||
|
self.loader.clone(),
|
||||||
&import_specifier,
|
&import_specifier,
|
||||||
name,
|
name,
|
||||||
if is_dynamic_import {
|
if is_dynamic_import {
|
||||||
|
@ -1249,7 +1300,17 @@ impl ModuleMap {
|
||||||
.borrow_mut()
|
.borrow_mut()
|
||||||
.dynamic_import_map
|
.dynamic_import_map
|
||||||
.insert(load.id, resolver_handle);
|
.insert(load.id, resolver_handle);
|
||||||
let resolve_result = module_map_rc.borrow().loader.resolve(
|
|
||||||
|
let (loader, snapshot_loaded_and_not_snapshotting) = {
|
||||||
|
let module_map = module_map_rc.borrow();
|
||||||
|
(
|
||||||
|
module_map.loader.clone(),
|
||||||
|
module_map.snapshot_loaded_and_not_snapshotting,
|
||||||
|
)
|
||||||
|
};
|
||||||
|
let resolve_result = resolve_helper(
|
||||||
|
snapshot_loaded_and_not_snapshotting,
|
||||||
|
loader,
|
||||||
specifier,
|
specifier,
|
||||||
referrer,
|
referrer,
|
||||||
ResolutionKind::DynamicImport,
|
ResolutionKind::DynamicImport,
|
||||||
|
@ -1287,10 +1348,14 @@ impl ModuleMap {
|
||||||
referrer: &str,
|
referrer: &str,
|
||||||
import_assertions: HashMap<String, String>,
|
import_assertions: HashMap<String, String>,
|
||||||
) -> Option<v8::Local<'s, v8::Module>> {
|
) -> Option<v8::Local<'s, v8::Module>> {
|
||||||
let resolved_specifier = self
|
let resolved_specifier = resolve_helper(
|
||||||
.loader
|
self.snapshot_loaded_and_not_snapshotting,
|
||||||
.resolve(specifier, referrer, ResolutionKind::Import)
|
self.loader.clone(),
|
||||||
.expect("Module should have been already resolved");
|
specifier,
|
||||||
|
referrer,
|
||||||
|
ResolutionKind::Import,
|
||||||
|
)
|
||||||
|
.expect("Module should have been already resolved");
|
||||||
|
|
||||||
let module_type =
|
let module_type =
|
||||||
get_asserted_module_type_from_assertions(&import_assertions);
|
get_asserted_module_type_from_assertions(&import_assertions);
|
||||||
|
@ -2599,5 +2664,17 @@ if (import.meta.url != 'file:///main_with_code.js') throw Error();
|
||||||
.map(|e| e.to_string()),
|
.map(|e| e.to_string()),
|
||||||
Some("Module loading is not supported".to_string())
|
Some("Module loading is not supported".to_string())
|
||||||
);
|
);
|
||||||
|
assert_eq!(
|
||||||
|
resolve_helper(
|
||||||
|
true,
|
||||||
|
Rc::new(loader),
|
||||||
|
"internal:core.js",
|
||||||
|
"file://bar",
|
||||||
|
ResolutionKind::Import,
|
||||||
|
)
|
||||||
|
.err()
|
||||||
|
.map(|e| e.to_string()),
|
||||||
|
Some("Cannot load internal module from external code".to_string())
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -629,7 +629,11 @@ impl JsRuntime {
|
||||||
Rc::into_raw(state_rc.clone()) as *mut c_void,
|
Rc::into_raw(state_rc.clone()) as *mut c_void,
|
||||||
);
|
);
|
||||||
|
|
||||||
let module_map_rc = Rc::new(RefCell::new(ModuleMap::new(loader, op_state)));
|
let module_map_rc = Rc::new(RefCell::new(ModuleMap::new(
|
||||||
|
loader,
|
||||||
|
op_state,
|
||||||
|
snapshot_options == SnapshotOptions::Load,
|
||||||
|
)));
|
||||||
if let Some(module_map_data) = module_map_data {
|
if let Some(module_map_data) = module_map_data {
|
||||||
let scope =
|
let scope =
|
||||||
&mut v8::HandleScope::with_context(&mut isolate, global_context);
|
&mut v8::HandleScope::with_context(&mut isolate, global_context);
|
||||||
|
@ -4928,4 +4932,72 @@ Deno.core.ops.op_async_serialize_object_with_numbers_as_keys({
|
||||||
)
|
)
|
||||||
.is_ok());
|
.is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn cant_load_internal_module_when_snapshot_is_loaded_and_not_snapshotting(
|
||||||
|
) {
|
||||||
|
#[derive(Default)]
|
||||||
|
struct ModsLoader;
|
||||||
|
|
||||||
|
impl ModuleLoader for ModsLoader {
|
||||||
|
fn resolve(
|
||||||
|
&self,
|
||||||
|
specifier: &str,
|
||||||
|
referrer: &str,
|
||||||
|
_kind: ResolutionKind,
|
||||||
|
) -> Result<ModuleSpecifier, Error> {
|
||||||
|
assert_eq!(specifier, "file:///main.js");
|
||||||
|
assert_eq!(referrer, ".");
|
||||||
|
let s = crate::resolve_import(specifier, referrer).unwrap();
|
||||||
|
Ok(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load(
|
||||||
|
&self,
|
||||||
|
_module_specifier: &ModuleSpecifier,
|
||||||
|
_maybe_referrer: Option<ModuleSpecifier>,
|
||||||
|
_is_dyn_import: bool,
|
||||||
|
) -> Pin<Box<ModuleSourceFuture>> {
|
||||||
|
let source = r#"
|
||||||
|
// This module doesn't really exist, just verifying that we'll get
|
||||||
|
// an error when specifier starts with "internal:".
|
||||||
|
import { core } from "internal:core.js";
|
||||||
|
"#;
|
||||||
|
|
||||||
|
async move {
|
||||||
|
Ok(ModuleSource {
|
||||||
|
code: source.as_bytes().to_vec().into_boxed_slice(),
|
||||||
|
module_url_specified: "file:///main.js".to_string(),
|
||||||
|
module_url_found: "file:///main.js".to_string(),
|
||||||
|
module_type: ModuleType::JavaScript,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
.boxed_local()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let snapshot = {
|
||||||
|
let runtime = JsRuntime::new(RuntimeOptions {
|
||||||
|
will_snapshot: true,
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
let snap: &[u8] = &runtime.snapshot();
|
||||||
|
Vec::from(snap).into_boxed_slice()
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut runtime2 = JsRuntime::new(RuntimeOptions {
|
||||||
|
module_loader: Some(Rc::new(ModsLoader)),
|
||||||
|
startup_snapshot: Some(Snapshot::Boxed(snapshot)),
|
||||||
|
..Default::default()
|
||||||
|
});
|
||||||
|
|
||||||
|
let err = runtime2
|
||||||
|
.load_main_module(&crate::resolve_url("file:///main.js").unwrap(), None)
|
||||||
|
.await
|
||||||
|
.unwrap_err();
|
||||||
|
assert_eq!(
|
||||||
|
err.to_string(),
|
||||||
|
"Cannot load internal module from external code"
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue