Skip to main content

camel_component_wasm/
lib.rs

1pub mod bean;
2pub mod bean_bindings;
3pub mod bindings;
4pub mod bundle;
5pub mod config;
6pub mod endpoint;
7pub mod epoch;
8pub mod error;
9pub mod host_functions;
10pub mod producer;
11pub mod runtime;
12pub mod serde_bridge;
13pub mod state_store;
14
15pub use bundle::WasmBundle;
16pub use config::WasmConfig;
17pub use endpoint::WasmEndpoint;
18pub use epoch::EpochTicker;
19pub use error::{TrapReason, WasmError};
20pub use state_store::StateStore;
21
22use std::path::PathBuf;
23use std::sync::Arc;
24
25use camel_api::CamelError;
26use camel_component_api::{Component, ComponentContext, Endpoint};
27use camel_core::Registry;
28
29pub struct WasmComponent {
30    registry: Arc<std::sync::Mutex<Registry>>,
31    base_dir: PathBuf,
32}
33
34impl WasmComponent {
35    pub fn new(registry: Arc<std::sync::Mutex<Registry>>, base_dir: PathBuf) -> Self {
36        Self { registry, base_dir }
37    }
38
39    fn validate_and_resolve_path(&self, uri_path: &str) -> Result<PathBuf, CamelError> {
40        if PathBuf::from(uri_path).is_absolute() {
41            return Err(CamelError::InvalidUri(
42                "WASM path must be relative (not absolute)".to_string(),
43            ));
44        }
45
46        if PathBuf::from(uri_path)
47            .components()
48            .any(|c| matches!(c, std::path::Component::ParentDir))
49        {
50            return Err(CamelError::InvalidUri(
51                "WASM path must not contain '..'".to_string(),
52            ));
53        }
54
55        let resolved = self.base_dir.join(uri_path);
56        let canonical = resolved.canonicalize().map_err(|_| {
57            CamelError::ComponentNotFound(format!("WASM module not found: {}", resolved.display()))
58        })?;
59
60        let canonical_base = self.base_dir.canonicalize().map_err(|_| {
61            CamelError::EndpointCreationFailed(format!(
62                "failed to resolve base directory: {}",
63                self.base_dir.display()
64            ))
65        })?;
66
67        if !canonical.starts_with(&canonical_base) {
68            return Err(CamelError::InvalidUri(
69                "WASM path escapes project root".to_string(),
70            ));
71        }
72
73        Ok(canonical)
74    }
75}
76
77impl Component for WasmComponent {
78    fn scheme(&self) -> &str {
79        "wasm"
80    }
81
82    fn create_endpoint(
83        &self,
84        uri: &str,
85        _ctx: &dyn ComponentContext,
86    ) -> Result<Box<dyn Endpoint>, CamelError> {
87        let uri_without_scheme = uri.strip_prefix("wasm:").ok_or_else(|| {
88            CamelError::InvalidUri(format!("WASM URI must start with 'wasm:': {uri}"))
89        })?;
90
91        let (path_part, wasm_config) = crate::config::WasmConfig::from_uri(uri_without_scheme);
92        if path_part.is_empty() {
93            return Err(CamelError::InvalidUri(
94                "WASM URI must include a module path".to_string(),
95            ));
96        }
97
98        let module_path = self.validate_and_resolve_path(&path_part)?;
99        Ok(Box::new(WasmEndpoint::new(
100            uri.to_string(),
101            module_path,
102            self.registry.clone(),
103            wasm_config,
104        )))
105    }
106}