#[test]
fn benchmark_persistent_sandbox_100_probes() {
use jsdet_chrome_ext::state::ExtensionState;
use jsdet_chrome_ext::{AnalysisProfile, ChromeExtBridge, Manifest};
use jsdet_core::bridge::Bridge;
use jsdet_core::{CompiledModule, Observation, PersistentSandbox, SandboxConfig};
use std::sync::Arc;
use std::time::Instant;
let module = CompiledModule::new().unwrap();
let manifest = Manifest::parse(
r#"{"name":"Bench","manifest_version":3,"version":"1.0",
"background":{"service_worker":"sw.js"},
"permissions":["tabs","cookies","storage"]}"#,
)
.unwrap();
let profile = AnalysisProfile::default();
let state = ExtensionState::default_with_id("bench");
let bridge: Arc<dyn Bridge> = Arc::new(ChromeExtBridge::new(manifest.clone(), profile, state));
let config = SandboxConfig {
max_fuel: 50_000_000,
max_memory_bytes: 32 * 1024 * 1024,
timeout_ms: 5000,
..SandboxConfig::default()
};
let mut sb = PersistentSandbox::new(&module, bridge, &config).unwrap();
let bootstrap = jsdet_chrome_ext::bootstrap::generate_bootstrap(&manifest);
let handler = r#"
chrome.runtime.onMessage.addListener(function(msg) {
if (msg.action === 'eval') eval(msg.code);
if (msg.action === 'fetch') fetch(msg.url);
if (msg.action === 'redirect') chrome.tabs.update(1, {url: msg.url});
});
"#
.to_string();
sb.load(&[bootstrap, handler]).unwrap();
let probes = [
r#"chrome.runtime._fireOnMessage({action:"eval",code:"SLN_1"},{tab:{id:1},id:"t"},function(){});"#,
r#"chrome.runtime._fireOnMessage({action:"fetch",url:"https://evil.com/SLN_2"},{tab:{id:1},id:"t"},function(){});"#,
r#"chrome.runtime._fireOnMessage({action:"redirect",url:"javascript:SLN_3"},{tab:{id:1},id:"t"},function(){});"#,
r#"chrome.runtime._fireOnMessage({data:"benign"},{tab:{id:1},id:"t"},function(){});"#,
];
let n = 100;
let start = Instant::now();
let mut total_sinks = 0;
for i in 0..n {
let obs = sb.eval_only(probes[i % probes.len()]);
total_sinks += obs.iter().filter(|o| matches!(o,
Observation::ApiCall { api, .. } if api == "eval" || api == "fetch" || api.contains("tabs.update")
)).count();
}
let elapsed = start.elapsed();
let per_probe_us = elapsed.as_micros() as f64 / n as f64;
let probes_per_sec = n as f64 / elapsed.as_secs_f64();
eprintln!("--- BENCHMARK: PersistentSandbox ---");
eprintln!(
"Probes: {n} | Time: {:.1}ms | Per probe: {:.0}µs | Rate: {:.0}/s | Sinks: {total_sinks}",
elapsed.as_secs_f64() * 1000.0,
per_probe_us,
probes_per_sec
);
assert!(
per_probe_us < 5000.0,
"probe should take <5ms, took {per_probe_us:.0}µs"
);
assert!(
probes_per_sec > 100.0,
"should do >100 probes/sec, got {probes_per_sec:.0}"
);
assert!(
total_sinks >= 50,
"should detect ≥50 sinks from 75 payload probes, got {total_sinks}"
);
}