cred: isolate scripts in WASI and move RSA signing to AWS-LC
This commit is contained in:
@@ -0,0 +1,196 @@
|
||||
use super::*;
|
||||
fn run(s: &str) -> Result<String, Error> {
|
||||
evaluate(s, "null", None, Limits::default())
|
||||
}
|
||||
#[test]
|
||||
fn module_default_and_named() {
|
||||
assert_eq!(
|
||||
evaluate(
|
||||
"export default x => ({value:x}); export const other=x=>x+1",
|
||||
"42",
|
||||
None,
|
||||
Limits::default()
|
||||
)
|
||||
.unwrap(),
|
||||
"{\"value\":42}"
|
||||
);
|
||||
assert_eq!(
|
||||
evaluate(
|
||||
"export const other=x=>x+1",
|
||||
"42",
|
||||
Some("other"),
|
||||
Limits::default()
|
||||
)
|
||||
.unwrap(),
|
||||
"43"
|
||||
);
|
||||
assert_eq!(run("export default x=>null").unwrap(), "null");
|
||||
}
|
||||
#[test]
|
||||
fn promise_settlement() {
|
||||
assert_eq!(
|
||||
run("export default async x => { await Promise.resolve(); return 42; }").unwrap(),
|
||||
"42"
|
||||
);
|
||||
assert_eq!(
|
||||
run("await Promise.resolve(); export default x=>true").unwrap(),
|
||||
"true"
|
||||
);
|
||||
for s in [
|
||||
"export default x=>Promise.reject(new Error('rejected'))",
|
||||
"export default async x=>{ throw 'rejected'; }",
|
||||
"await Promise.reject('rejected'); export default x=>true",
|
||||
] {
|
||||
let e = run(s).unwrap_err();
|
||||
assert_eq!(e.kind, ErrorKind::Throw);
|
||||
assert_eq!(e.message, "rejected");
|
||||
}
|
||||
assert_eq!(
|
||||
run("export default x=>new Promise(()=>{})")
|
||||
.unwrap_err()
|
||||
.kind,
|
||||
ErrorKind::Engine
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn typed_errors() {
|
||||
assert_eq!(run("export default (").unwrap_err().kind, ErrorKind::Syntax);
|
||||
assert_eq!(
|
||||
run("export default x=>{throw new Error('oops')}")
|
||||
.unwrap_err()
|
||||
.kind,
|
||||
ErrorKind::Throw
|
||||
);
|
||||
for s in [
|
||||
"export default x=>undefined",
|
||||
"export default x=>(()=>1)",
|
||||
"export default x=>{let a={};a.a=a;return a}",
|
||||
"export default x=>Symbol()",
|
||||
] {
|
||||
assert_eq!(run(s).unwrap_err().kind, ErrorKind::NonJson);
|
||||
}
|
||||
assert_eq!(
|
||||
evaluate("export default x=>x", "not json", None, Limits::default())
|
||||
.unwrap_err()
|
||||
.kind,
|
||||
ErrorKind::Syntax
|
||||
);
|
||||
assert_eq!(run("export const x=1").unwrap_err().kind, ErrorKind::Engine);
|
||||
assert_eq!(run("export default 1").unwrap_err().kind, ErrorKind::Engine);
|
||||
}
|
||||
#[test]
|
||||
fn recursion_is_bounded() {
|
||||
for stack in [4096, 16384, usize::MAX] {
|
||||
let e = evaluate(
|
||||
"export default x=>{function f(){return f()+1}return f()}",
|
||||
"null",
|
||||
None,
|
||||
Limits {
|
||||
max_stack_bytes: Some(stack),
|
||||
..Limits::default()
|
||||
},
|
||||
)
|
||||
.unwrap_err();
|
||||
eprintln!("STACK {stack}: {e:?}");
|
||||
assert_eq!(e.kind, ErrorKind::Limit);
|
||||
assert!(e.message.contains("stack"));
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
fn runaway_loop_and_jobs() {
|
||||
for s in [
|
||||
"export default x=>{while(true){}}",
|
||||
"while(true){};export default x=>x",
|
||||
"export default x=>({toJSON(){while(true){}}})",
|
||||
"export default x=>{throw {get message(){while(true){}}}}",
|
||||
"export default x=>new Promise(()=>{function f(){Promise.resolve().then(f)};f()})",
|
||||
] {
|
||||
let start = Instant::now();
|
||||
assert_eq!(run(s).unwrap_err().kind, ErrorKind::Limit);
|
||||
assert!(start.elapsed() < Duration::from_secs(2));
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
fn runaway_allocation() {
|
||||
let e = evaluate(
|
||||
"export default x=>{let a=[];while(1)a.push('abcdefghijklmnopqrstuvwxyz'+a.length)}",
|
||||
"null",
|
||||
None,
|
||||
Limits {
|
||||
max_time_ms: Some(1000),
|
||||
max_memory_bytes: Some(1024 * 1024),
|
||||
..Limits::default()
|
||||
},
|
||||
)
|
||||
.unwrap_err();
|
||||
eprintln!("HEAP {e:?}");
|
||||
assert_eq!(e.kind, ErrorKind::Limit);
|
||||
assert!(e.message.contains("memory"));
|
||||
}
|
||||
#[test]
|
||||
fn forbidden_globals_and_imports() {
|
||||
for name in [
|
||||
"Date",
|
||||
"performance",
|
||||
"Math.random",
|
||||
"fetch",
|
||||
"XMLHttpRequest",
|
||||
"require",
|
||||
"process",
|
||||
"std",
|
||||
"os",
|
||||
"fs",
|
||||
"crypto",
|
||||
"console",
|
||||
"setTimeout",
|
||||
] {
|
||||
assert_eq!(
|
||||
run(&format!("export default x=>typeof {name}")).unwrap(),
|
||||
"\"undefined\"",
|
||||
"{name}"
|
||||
);
|
||||
}
|
||||
assert!(run("import x from 'fs';export default x=>x").is_err());
|
||||
assert_eq!(
|
||||
run("export default x=>import('fs')").unwrap_err().kind,
|
||||
ErrorKind::Throw
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn isolation_and_limits() {
|
||||
run("export default x=>{globalThis.leak=42;return true}").unwrap();
|
||||
assert_eq!(
|
||||
run("export default x=>typeof leak").unwrap(),
|
||||
"\"undefined\""
|
||||
);
|
||||
assert_eq!(
|
||||
Limits {
|
||||
max_time_ms: Some(u64::MAX),
|
||||
max_memory_bytes: Some(usize::MAX),
|
||||
max_stack_bytes: Some(usize::MAX)
|
||||
}
|
||||
.clamped()
|
||||
.max_time_ms,
|
||||
Some(1000)
|
||||
);
|
||||
assert_eq!(
|
||||
Limits {
|
||||
max_time_ms: Some(0),
|
||||
..Limits::default()
|
||||
}
|
||||
.clamped()
|
||||
.max_time_ms,
|
||||
Some(1)
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn timings() {
|
||||
let start = Instant::now();
|
||||
assert_eq!(run("export default x=>true").unwrap(), "true");
|
||||
eprintln!("TIMING first eval {}us", start.elapsed().as_micros());
|
||||
let start = Instant::now();
|
||||
for _ in 0..100 {
|
||||
assert_eq!(run("export default x=>true").unwrap(), "true");
|
||||
}
|
||||
eprintln!("TIMING 100 evals {}us", start.elapsed().as_micros());
|
||||
}
|
||||
Reference in New Issue
Block a user