mirror of
https://github.com/kristoferssolo/captra.git
synced 2025-12-20 11:04:39 +00:00
feat: add wasmtime
This commit is contained in:
parent
51f07beb4d
commit
d28b182422
1371
Cargo.lock
generated
1371
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@ -10,17 +10,18 @@ keywords = ["wasm", "sandbox", "capabilities", "plugins", "traces"]
|
||||
categories = ["cryptography::randomness", "encoding"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
base64 = "0.22"
|
||||
color-eyre = "0.6"
|
||||
ed25519-dalek = { version = "2.2", features = ["rand_core"] }
|
||||
glob = "0.3"
|
||||
rand = "0.8" # ed25519-dalek depends on rand_core v0.6
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
sha2 = "0.10.9"
|
||||
sha2 = "0.10"
|
||||
thiserror = "2.0"
|
||||
tracing = "0.1"
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter", "json"] }
|
||||
wasmtime = "37.0"
|
||||
|
||||
[dev-dependencies]
|
||||
claims = "0.8"
|
||||
|
||||
82
src/host.rs
82
src/host.rs
@ -9,9 +9,10 @@ use ed25519_dalek::{PUBLIC_KEY_LENGTH, SigningKey, ed25519::signature::SignerMut
|
||||
use glob::Pattern;
|
||||
use rand::{Rng, SeedableRng, rngs::StdRng};
|
||||
use sha2::{Digest, Sha256};
|
||||
use std::path::Path;
|
||||
use std::{path::Path, str::from_utf8};
|
||||
use thiserror::Error;
|
||||
use tracing::Level;
|
||||
use wasmtime::{Caller, Extern, Linker, Trap};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct HostState {
|
||||
@ -40,6 +41,15 @@ pub enum CapError {
|
||||
InvalidPath,
|
||||
}
|
||||
|
||||
/// Host-visible status codes returned from host functions.
|
||||
#[repr(i32)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum HostStatus {
|
||||
Allowed = 0,
|
||||
Denied = 1,
|
||||
Error = -1,
|
||||
}
|
||||
|
||||
impl HostState {
|
||||
#[inline]
|
||||
#[must_use]
|
||||
@ -234,3 +244,73 @@ impl HostState {
|
||||
pub fn init_tracing() {
|
||||
tracing_subscriber::fmt().with_max_level(Level::INFO).init();
|
||||
}
|
||||
|
||||
/// Register host functions for Wasmtime on the provided linker.
|
||||
///
|
||||
/// Exposes:
|
||||
/// - `host::read_file(ptr: i32, len: i32) -> Result<i32, Trap>`
|
||||
/// - `host::status_allowed() -> i32`
|
||||
/// - `host::status_denied() -> i32`
|
||||
/// - `host::status_error() -> i32`
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// `read_file` returns `Ok(HostStatus::Allowed/Denied)` for normal outcomes,
|
||||
/// and Err(Trap) for exceptional errors (OOB, invalid pointer, invalid UTF-8).
|
||||
pub fn add_wasm_linker_funcs(linker: &mut Linker<HostState>) -> anyhow::Result<()> {
|
||||
linker.func_wrap(
|
||||
"host",
|
||||
"read_file",
|
||||
|mut caller: Caller<'_, HostState>, ptr: i32, len: i32| -> anyhow::Result<i32> {
|
||||
let memory = caller
|
||||
.get_export("memory")
|
||||
.and_then(Extern::into_memory)
|
||||
.ok_or(Trap::MemoryOutOfBounds)?;
|
||||
|
||||
let start = usize::try_from(ptr).map_err(|_| Trap::BadConversionToInteger)?;
|
||||
let read_len = usize::try_from(len).map_err(|_| Trap::BadConversionToInteger)?;
|
||||
|
||||
let data = memory.data(&caller);
|
||||
|
||||
if start
|
||||
.checked_add(read_len)
|
||||
.is_none_or(|end| end > data.len())
|
||||
{
|
||||
return Err(Trap::MemoryOutOfBounds.into());
|
||||
}
|
||||
|
||||
let bytes = &data[start..start + read_len];
|
||||
let path_str = from_utf8(bytes)
|
||||
.map_err(|_| Trap::BadConversionToInteger)?
|
||||
.to_string();
|
||||
|
||||
match caller.data_mut().execute_plugin(path_str) {
|
||||
Ok(true) => Ok(HostStatus::Allowed.into()),
|
||||
Ok(false) => Ok(HostStatus::Denied.into()),
|
||||
Err(err) => match err {
|
||||
CapError::GlobMismatch => Ok(HostStatus::Denied.into()),
|
||||
CapError::NoFsCapability | CapError::NoReadPatterns => {
|
||||
Ok(HostStatus::Denied.into())
|
||||
}
|
||||
CapError::InvalidPath => Err(Trap::MemoryOutOfBounds.into()),
|
||||
},
|
||||
}
|
||||
},
|
||||
)?;
|
||||
linker.func_wrap("host", "status_allowed", || -> i32 {
|
||||
HostStatus::Allowed.into()
|
||||
})?;
|
||||
linker.func_wrap("host", "status_denied", || -> i32 {
|
||||
HostStatus::Denied.into()
|
||||
})?;
|
||||
linker.func_wrap("host", "status_error", || -> i32 {
|
||||
HostStatus::Error.into()
|
||||
})?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
impl From<HostStatus> for i32 {
|
||||
fn from(value: HostStatus) -> Self {
|
||||
value as Self
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user