1
0
Fork 0
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:
Bartek Iwańczuk 2023-02-08 18:13:33 +01:00 committed by GitHub
parent 19543ffec3
commit bef50416b9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 190 additions and 32 deletions

View file

@ -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);

View file

@ -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())
);
} }
} }

View file

@ -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"
);
}
} }