mirror of
https://codeberg.org/likwid/likwid.git
synced 2026-02-09 21:13:09 +00:00
wasm: harden host api input validation
This commit is contained in:
parent
2fa1ac5fdf
commit
f29543f429
1 changed files with 59 additions and 2 deletions
|
|
@ -25,6 +25,13 @@ pub const CAP_SETTINGS: &str = "settings";
|
||||||
/// Capability identifier for outbound HTTP requests.
|
/// Capability identifier for outbound HTTP requests.
|
||||||
pub const CAP_OUTBOUND_HTTP: &str = "outbound_http";
|
pub const CAP_OUTBOUND_HTTP: &str = "outbound_http";
|
||||||
|
|
||||||
|
const MAX_WASM_STRING_BYTES: u32 = 64 * 1024;
|
||||||
|
const MAX_WASM_HTTP_BODY_BYTES: u32 = 256 * 1024;
|
||||||
|
const ERR_TOO_LARGE: u32 = 9;
|
||||||
|
const ERR_INVALID_URL: u32 = 10;
|
||||||
|
const ERR_INVALID_METHOD: u32 = 11;
|
||||||
|
const ERR_INVALID_BODY_UTF8: u32 = 12;
|
||||||
|
|
||||||
/// Plugin capability configuration.
|
/// Plugin capability configuration.
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct Capability {
|
pub struct Capability {
|
||||||
|
|
@ -92,6 +99,9 @@ impl HostState {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if let Ok(parsed) = reqwest::Url::parse(url) {
|
if let Ok(parsed) = reqwest::Url::parse(url) {
|
||||||
|
if parsed.scheme() != "http" && parsed.scheme() != "https" {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if let Some(host) = parsed.host_str() {
|
if let Some(host) = parsed.host_str() {
|
||||||
return self.egress_allowlist.iter().any(|pattern| {
|
return self.egress_allowlist.iter().any(|pattern| {
|
||||||
if pattern.starts_with("*.") {
|
if pattern.starts_with("*.") {
|
||||||
|
|
@ -107,6 +117,13 @@ impl HostState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_valid_method(method: &str) -> bool {
|
||||||
|
matches!(
|
||||||
|
method,
|
||||||
|
"GET" | "POST" | "PUT" | "DELETE" | "PATCH" | "HEAD" | "OPTIONS"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/// Combined state with resource limits for WASM execution.
|
/// Combined state with resource limits for WASM execution.
|
||||||
pub struct HostStateWithLimits {
|
pub struct HostStateWithLimits {
|
||||||
pub inner: HostState,
|
pub inner: HostState,
|
||||||
|
|
@ -147,6 +164,9 @@ pub fn register_host_functions(linker: &mut Linker<HostStateWithLimits>) -> Resu
|
||||||
.func_wrap("env", "host_log", |mut caller: wasmtime::Caller<'_, HostStateWithLimits>, ptr: u32, len: u32, level: u32| {
|
.func_wrap("env", "host_log", |mut caller: wasmtime::Caller<'_, HostStateWithLimits>, ptr: u32, len: u32, level: u32| {
|
||||||
let memory = caller.get_export("memory").and_then(|e| e.into_memory());
|
let memory = caller.get_export("memory").and_then(|e| e.into_memory());
|
||||||
if let Some(mem) = memory {
|
if let Some(mem) = memory {
|
||||||
|
if len > MAX_WASM_STRING_BYTES {
|
||||||
|
return;
|
||||||
|
}
|
||||||
let mut buf = vec![0u8; len as usize];
|
let mut buf = vec![0u8; len as usize];
|
||||||
if mem.read(&caller, ptr as usize, &mut buf).is_ok() {
|
if mem.read(&caller, ptr as usize, &mut buf).is_ok() {
|
||||||
if let Ok(msg) = String::from_utf8(buf) {
|
if let Ok(msg) = String::from_utf8(buf) {
|
||||||
|
|
@ -175,6 +195,10 @@ pub fn register_host_functions(linker: &mut Linker<HostStateWithLimits>) -> Resu
|
||||||
None => return pack_result(1, 0),
|
None => return pack_result(1, 0),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if key_len > MAX_WASM_STRING_BYTES {
|
||||||
|
return pack_result(ERR_TOO_LARGE, 0);
|
||||||
|
}
|
||||||
|
|
||||||
let mut key_buf = vec![0u8; key_len as usize];
|
let mut key_buf = vec![0u8; key_len as usize];
|
||||||
if memory.read(&caller, key_ptr as usize, &mut key_buf).is_err() {
|
if memory.read(&caller, key_ptr as usize, &mut key_buf).is_err() {
|
||||||
return pack_result(2, 0);
|
return pack_result(2, 0);
|
||||||
|
|
@ -249,6 +273,10 @@ pub fn register_host_functions(linker: &mut Linker<HostStateWithLimits>) -> Resu
|
||||||
None => return pack_result(1, 0),
|
None => return pack_result(1, 0),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if key_len > MAX_WASM_STRING_BYTES {
|
||||||
|
return pack_result(ERR_TOO_LARGE, 0);
|
||||||
|
}
|
||||||
|
|
||||||
let mut key_buf = vec![0u8; key_len as usize];
|
let mut key_buf = vec![0u8; key_len as usize];
|
||||||
if memory.read(&caller, key_ptr as usize, &mut key_buf).is_err() {
|
if memory.read(&caller, key_ptr as usize, &mut key_buf).is_err() {
|
||||||
return pack_result(2, 0);
|
return pack_result(2, 0);
|
||||||
|
|
@ -299,6 +327,10 @@ pub fn register_host_functions(linker: &mut Linker<HostStateWithLimits>) -> Resu
|
||||||
None => return 1,
|
None => return 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if key_len > MAX_WASM_STRING_BYTES || val_len > MAX_WASM_STRING_BYTES {
|
||||||
|
return ERR_TOO_LARGE;
|
||||||
|
}
|
||||||
|
|
||||||
let mut key_buf = vec![0u8; key_len as usize];
|
let mut key_buf = vec![0u8; key_len as usize];
|
||||||
let mut val_buf = vec![0u8; val_len as usize];
|
let mut val_buf = vec![0u8; val_len as usize];
|
||||||
|
|
||||||
|
|
@ -361,6 +393,10 @@ pub fn register_host_functions(linker: &mut Linker<HostStateWithLimits>) -> Resu
|
||||||
None => return 1,
|
None => return 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if event_len > MAX_WASM_STRING_BYTES || payload_len > MAX_WASM_STRING_BYTES {
|
||||||
|
return ERR_TOO_LARGE;
|
||||||
|
}
|
||||||
|
|
||||||
let mut event_buf = vec![0u8; event_len as usize];
|
let mut event_buf = vec![0u8; event_len as usize];
|
||||||
let mut payload_buf = vec![0u8; payload_len as usize];
|
let mut payload_buf = vec![0u8; payload_len as usize];
|
||||||
|
|
||||||
|
|
@ -461,6 +497,10 @@ pub fn register_host_functions(linker: &mut Linker<HostStateWithLimits>) -> Resu
|
||||||
None => return pack_result(1, 0),
|
None => return pack_result(1, 0),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if url_len > MAX_WASM_STRING_BYTES || method_len > MAX_WASM_STRING_BYTES || body_len > MAX_WASM_HTTP_BODY_BYTES {
|
||||||
|
return pack_result(ERR_TOO_LARGE, 0);
|
||||||
|
}
|
||||||
|
|
||||||
let mut url_buf = vec![0u8; url_len as usize];
|
let mut url_buf = vec![0u8; url_len as usize];
|
||||||
let mut method_buf = vec![0u8; method_len as usize];
|
let mut method_buf = vec![0u8; method_len as usize];
|
||||||
|
|
||||||
|
|
@ -486,7 +526,22 @@ pub fn register_host_functions(linker: &mut Linker<HostStateWithLimits>) -> Resu
|
||||||
Err(_) => return pack_result(5, 0),
|
Err(_) => return pack_result(5, 0),
|
||||||
};
|
};
|
||||||
|
|
||||||
let body = String::from_utf8(body_buf).unwrap_or_default();
|
let Ok(parsed_url) = reqwest::Url::parse(&url) else {
|
||||||
|
return pack_result(ERR_INVALID_URL, 0);
|
||||||
|
};
|
||||||
|
if parsed_url.scheme() != "http" && parsed_url.scheme() != "https" {
|
||||||
|
return pack_result(ERR_INVALID_URL, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
let method_upper = method.to_ascii_uppercase();
|
||||||
|
if !is_valid_method(method_upper.as_str()) {
|
||||||
|
return pack_result(ERR_INVALID_METHOD, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
let body = match String::from_utf8(body_buf) {
|
||||||
|
Ok(b) => b,
|
||||||
|
Err(_) => return pack_result(ERR_INVALID_BODY_UTF8, 0),
|
||||||
|
};
|
||||||
|
|
||||||
let state = caller.data();
|
let state = caller.data();
|
||||||
|
|
||||||
|
|
@ -511,11 +566,13 @@ pub fn register_host_functions(linker: &mut Linker<HostStateWithLimits>) -> Resu
|
||||||
return serde_json::json!({"error": "client_init_failed"});
|
return serde_json::json!({"error": "client_init_failed"});
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut req = match method.to_ascii_uppercase().as_str() {
|
let mut req = match method_upper.as_str() {
|
||||||
"POST" => client.post(&url),
|
"POST" => client.post(&url),
|
||||||
"PUT" => client.put(&url),
|
"PUT" => client.put(&url),
|
||||||
"DELETE" => client.delete(&url),
|
"DELETE" => client.delete(&url),
|
||||||
"PATCH" => client.patch(&url),
|
"PATCH" => client.patch(&url),
|
||||||
|
"HEAD" => client.head(&url),
|
||||||
|
"OPTIONS" => client.request(reqwest::Method::OPTIONS, &url),
|
||||||
_ => client.get(&url),
|
_ => client.get(&url),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue