#![cfg(any(feature = "tier-2-hook-host-v2", feature = "tier-2-observer-host-v2"))]
pub(crate) mod sealed_traits;
pub(crate) use sealed_traits::sealed_impl;
pub use sealed_traits::{HookCapTokenSealed, ObserverCapTokenSealed, SealedHostImport};
pub(crate) mod wasm_memory;
pub(crate) use wasm_memory::{read_caller_memory, write_caller_memory};
pub(crate) mod import_scan;
pub use import_scan::WASI_DENY_PREFIXES;
pub(crate) use import_scan::{scan_module_imports, ScanImportsError};
pub(crate) mod engine_profile;
pub(crate) use engine_profile::{build_engine, config_for_profile, EngineProfile};
pub(crate) mod register_module;
pub(crate) use register_module::{register_module_common, RegistrationError};
#[cfg(test)]
#[allow(clippy::expect_used, clippy::unwrap_used, clippy::panic)]
mod tests {
use super::*;
use bytes::Bytes;
use wasmtime::{Caller, Config, Engine, Module, Store};
fn engine() -> Engine {
let mut c = Config::new();
c.consume_fuel(true);
Engine::new(&c).expect("test engine builds")
}
fn wat_to_bytes(wat: &str) -> Bytes {
Bytes::from(wat::parse_str(wat).expect("valid wat"))
}
#[test]
fn scan_accepts_arbitrary_allowed_prefix() {
let bytes = wat_to_bytes(
r#"(module
(import "test:custom/effect" "fire"
(func (param i32 i32))))"#,
);
let _ = scan_module_imports(
&engine(),
&bytes,
&["test:custom/"],
&["wasi:random"],
"only `test:custom/*` permitted",
)
.expect("custom allow-list accepts matching import");
}
#[test]
fn scan_rejection_carries_audit_message() {
let bytes = wat_to_bytes(
r#"(module
(import "wasi:io/streams" "write"
(func (param i32))))"#,
);
let err = scan_module_imports(
&engine(),
&bytes,
&["arkhe:observer/"],
&["wasi:io"],
"only `arkhe:observer/*` permitted",
)
.expect_err("denied import must reject");
let msg = format!("{err}");
assert!(
msg.contains("denied namespace `wasi:io`"),
"audit message lost: {msg}"
);
}
#[test]
fn read_caller_memory_works_for_arbitrary_t() {
let eng = engine();
let bytes = wat_to_bytes(
r#"(module
(memory (export "memory") 1)
(data (i32.const 0) "HELLO")
(func (export "hook") (result i32)
i32.const 0))"#,
);
let module = Module::from_binary(&eng, bytes.as_ref()).expect("module parses");
let mut store = Store::new(&eng, 42_u32);
store.set_fuel(1_000_000).expect("seed fuel");
let mut linker = wasmtime::Linker::<u32>::new(&eng);
linker
.func_wrap(
"test",
"probe",
|mut caller: Caller<'_, u32>, ptr: i32, len: i32| -> Result<i32, wasmtime::Error> {
let buf = read_caller_memory(&mut caller, ptr, len)?;
Ok(buf.len() as i32)
},
)
.expect("bind probe");
let inst = linker
.instantiate(&mut store, &module)
.expect("instantiate");
let entry = inst
.get_typed_func::<(), i32>(&mut store, "hook")
.expect("entry");
let _ = entry.call(&mut store, ()).expect("call");
}
#[test]
fn engine_profile_replay_deterministic_builds_engine() {
let profile = EngineProfile::ReplayDeterministic {
fuel_budget: 10_000_000,
};
let (engine, fuel) = build_engine(&profile).expect("engine builds");
assert_eq!(fuel, 10_000_000);
let wat = r#"(module
(memory (export "memory") 1)
(func (export "noop")))"#;
let bytes = wat_to_bytes(wat);
let module = Module::from_binary(&engine, bytes.as_ref()).expect("noop module parses");
let mut store = Store::new(&engine, ());
store.set_fuel(fuel).expect("seed fuel");
let inst = wasmtime::Linker::<()>::new(&engine)
.instantiate(&mut store, &module)
.expect("instantiate");
let _ = inst
.get_typed_func::<(), ()>(&mut store, "noop")
.expect("noop export");
}
#[test]
fn engine_profile_replay_deterministic_rejects_simd() {
let profile = EngineProfile::ReplayDeterministic {
fuel_budget: 10_000_000,
};
let (engine, _) = build_engine(&profile).expect("engine builds");
let wat = r#"(module (func (drop (v128.const i32x4 0 0 0 0))))"#;
let bytes = wat_to_bytes(wat);
let res = Module::from_binary(&engine, bytes.as_ref());
assert!(
res.is_err(),
"ReplayDeterministic profile must reject SIMD modules"
);
}
#[test]
fn engine_profile_chain_non_affecting_builds_engine() {
let profile = EngineProfile::ChainNonAffecting {
fuel_budget: 100_000_000,
};
let (engine, fuel) = build_engine(&profile).expect("engine builds");
assert_eq!(fuel, 100_000_000);
let wat = r#"(module (func (export "noop")))"#;
let bytes = wat_to_bytes(wat);
let module = Module::from_binary(&engine, bytes.as_ref()).expect("noop module parses");
let mut store = Store::new(&engine, ());
store.set_fuel(fuel).expect("seed fuel");
let _ = wasmtime::Linker::<()>::new(&engine)
.instantiate(&mut store, &module)
.expect("instantiate");
}
#[test]
fn engine_profile_fuel_budget_accessor() {
assert_eq!(
EngineProfile::ReplayDeterministic {
fuel_budget: 42_000
}
.fuel_budget(),
42_000
);
assert_eq!(
EngineProfile::ChainNonAffecting {
fuel_budget: 7_000_000
}
.fuel_budget(),
7_000_000
);
}
fn assert_hook_cap_token_sealed<C: HookCapTokenSealed>() {}
fn assert_observer_cap_token_sealed<C: ObserverCapTokenSealed>() {}
#[test]
fn hook_cap_token_satisfies_sealed_bound() {
assert_hook_cap_token_sealed::<crate::hook_host::CapToken>();
}
#[test]
fn observer_cap_token_satisfies_sealed_bound() {
assert_observer_cap_token_sealed::<crate::observer_host::ObserverCapToken>();
}
fn assert_sealed_host_import<L: SealedHostImport>() {}
#[cfg(feature = "tier-2-hook-host-v2")]
#[test]
fn hook_capability_linker_satisfies_sealed_host_import() {
assert_sealed_host_import::<crate::hook_host::capability_linker::CapabilityLinker>();
}
#[cfg(feature = "tier-2-observer-host-v2")]
#[test]
fn observer_capability_linker_satisfies_sealed_host_import() {
assert_sealed_host_import::<
crate::observer_host::capability_linker::ObserverCapabilityLinker,
>();
}
}