1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
//! Per-world capability allowlist for WASM host functions.
//!
//! Controls which host operations a WASM guest can invoke based on the
//! plugin kind. Policy worlds (AuthorizationPolicy, SecurityPolicy) get a
//! fully denied set — they cannot call `camel_call`, `camel_poll`,
//! `host_store`, or `host_load`. Processor and Bean worlds get an
//! explicitly-configured scheme allowlist (fail-closed: empty by default).
use std::collections::HashSet;
/// Per-world capability allowlist.
#[derive(Clone, Debug, Default)]
pub struct WasmCapabilities {
/// URI schemes the guest may call via `camel_call` / `camel_poll`.
/// Empty set = deny all (fail-closed).
pub call_schemes: HashSet<String>,
/// Whether `host_store` / `host_load` are available.
pub host_kv: bool,
}
impl WasmCapabilities {
/// Check whether a URI scheme may be called.
pub fn can_call(&self, scheme: &str) -> bool {
self.call_schemes.contains(scheme)
}
/// Policy world capabilities — everything denied.
pub fn denied() -> Self {
Self::default()
}
/// Build capabilities from a comma-separated scheme list (e.g. "log,direct").
/// host_kv defaults to true for Processor/Bean (trusted plugin kinds).
pub fn from_scheme_list(schemes: &str) -> Self {
let call_schemes = schemes
.split(',')
.map(|s| s.trim().to_string())
.filter(|s| !s.is_empty())
.collect();
Self {
call_schemes,
host_kv: true,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn denied_capabilities_block_all_calls() {
let caps = WasmCapabilities::denied();
assert!(!caps.can_call("log"));
assert!(!caps.can_call("direct"));
assert!(!caps.host_kv);
}
#[test]
fn from_scheme_list_grants_listed_schemes() {
let caps = WasmCapabilities::from_scheme_list("log,direct,file");
assert!(caps.can_call("log"));
assert!(caps.can_call("direct"));
assert!(caps.can_call("file"));
assert!(!caps.can_call("kafka"));
assert!(caps.host_kv);
}
#[test]
fn from_scheme_list_empty_is_denied() {
let caps = WasmCapabilities::from_scheme_list("");
assert!(caps.call_schemes.is_empty());
assert!(!caps.can_call("anything"));
}
#[test]
fn from_scheme_list_trims_whitespace() {
let caps = WasmCapabilities::from_scheme_list(" log , direct ");
assert!(caps.can_call("log"));
assert!(caps.can_call("direct"));
}
#[test]
fn default_is_fail_closed() {
let caps = WasmCapabilities::default();
assert!(caps.call_schemes.is_empty());
assert!(!caps.host_kv);
}
}