feat: execute v2 Plugin tools in isolated Wasmtime invocations

Reuse canonical artifact and ABI verification; inject live Host admission and operation adapters without product authority or ambient WASI. Bound fresh Stores, resources, CPU, common deadlines and diagnostics, and regress actual PDK/adversarial Components.
This commit is contained in:
2026-10-28 16:54:53 +09:00
parent a1fbe1e754
commit d9e9c0f13d
13 changed files with 3120 additions and 7 deletions
@@ -0,0 +1,138 @@
use yoi_plugin_pdk::wit_bindgen;
wit_bindgen::generate!({
path: "../../../../resources/plugin/wit",
world: "https-logged-tool-plugin",
generate_all,
runtime_path: "yoi_plugin_pdk::wit_bindgen::rt",
});
use exports::yoi::plugin::tool::{Guest, Invocation, Output, ToolError};
use std::sync::atomic::{AtomicU32, Ordering};
use yoi::host::{connections, http, logging};
static CALLS: AtomicU32 = AtomicU32::new(0);
struct Plugin;
impl Guest for Plugin {
fn invoke(input: Invocation) -> Result<Output, ToolError> {
let count = CALLS.fetch_add(1, Ordering::SeqCst) + 1;
let mode = input.input_json.trim_matches('"');
match mode {
"trap" => panic!("guest trap"),
"loop" => loop {
std::hint::black_box(CALLS.load(Ordering::SeqCst));
},
"wall-loop" => {
logging::log(&logging::Entry {
level: logging::Level::Info,
event_code: "count".into(),
message: "loop entered".into(),
fields: vec![],
})
.map_err(failure)?;
loop {
std::hint::black_box(CALLS.load(Ordering::SeqCst));
}
}
"memory" => {
core::arch::wasm32::memory_grow::<0>(8192);
}
"error-output" => {
return Err(ToolError {
code: "typed".into(),
message: "\"".repeat(40_000),
});
}
"typed" | "unknown" => {
return Err(ToolError {
code: mode.into(),
message: "bounded business failure".into(),
});
}
"logging" => {
for _ in 0..3 {
logging::log(&logging::Entry {
level: logging::Level::Info,
event_code: "count".into(),
message: "safe diagnostic".into(),
fields: vec![logging::Field {
name: "calls".into(),
value: logging::Scalar::Unsigned(count as u64),
}],
})
.map_err(failure)?;
}
}
"http" | "stale" | "foreign" | "resources" => {
if mode == "resources" {
let _resources: Vec<_> =
(0..64).map(|_| connections::open("api").unwrap()).collect();
}
let target = if mode == "foreign" {
// An attacker can fabricate a representation, not a capability.
unsafe { connections::Connection::from_handle(0) }
} else {
connections::open("api").map_err(failure)?
};
let target = if mode == "stale" {
let handle = target.handle();
drop(target);
unsafe { connections::Connection::from_handle(handle) }
} else {
target
};
let response = http::send(
&target,
&http::Request {
method: http::Method::Get,
path: "/status".into(),
query: vec![],
headers: vec![],
body: vec![],
timeout_ms: 15_000,
effect_key: "one".into(),
},
)
.map_err(failure)?;
return Ok(Output {
summary: String::from_utf8(response.body).unwrap(),
content: None,
structured_json: Some(format!("{{\"calls\":{count}}}")),
});
}
_ => (),
}
Ok(Output {
summary: if mode == "output" {
"x".repeat(70_000)
} else {
input.invocation_id
},
content: None,
structured_json: Some(if mode == "bad-json" {
"not-json".into()
} else if mode == "bad-schema" {
"{}".into()
} else {
format!("{{\"calls\":{count}}}")
}),
})
}
}
fn failure(error: yoi::host::errors::HostError) -> ToolError {
ToolError {
code: "host".into(),
message: format!(
"{}:{}",
match error.code {
yoi::host::errors::Code::DeadlineExceeded => "deadline-exceeded",
yoi::host::errors::Code::QuotaExceeded => "quota-exceeded",
_ => "host-rejected",
},
match error.effect {
yoi::host::errors::EffectObservation::NotDispatched => "not-dispatched",
yoi::host::errors::EffectObservation::ResponseObserved => "response-observed",
yoi::host::errors::EffectObservation::Indeterminate => "indeterminate",
}
),
}
}
export!(Plugin);