Repository navigation
fix(security): load WASM tool description and schema from capabilities.json #520
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -123,34 +123,73 @@ impl WasmToolLoader { | |
| } | ||
| let wasm_bytes = fs::read(wasm_path).await?; | ||
|
|
||
| // Read capabilities (optional) and extract OAuth refresh config | ||
| let (capabilities, oauth_refresh) = if let Some(cap_path) = capabilities_path { | ||
| if cap_path.exists() { | ||
| let cap_bytes = fs::read(cap_path).await?; | ||
| let cap_file = CapabilitiesFile::from_bytes(&cap_bytes) | ||
| .map_err(|e| WasmLoadError::InvalidCapabilities(e.to_string()))?; | ||
| cap_file.validate(name); | ||
|
|
||
| // Check WIT version compatibility | ||
| check_wit_version_compat( | ||
| name, | ||
| cap_file.wit_version.as_deref(), | ||
| crate::tools::wasm::WIT_TOOL_VERSION, | ||
| )?; | ||
|
|
||
| let caps = cap_file.to_capabilities(); | ||
| let oauth = resolve_oauth_refresh_config(&cap_file); | ||
| (caps, oauth) | ||
| // Read capabilities (optional) and extract OAuth refresh config, | ||
| // tool description, and parameter schema. | ||
| let (capabilities, oauth_refresh, description, schema) = | ||
| if let Some(cap_path) = capabilities_path { | ||
| if cap_path.exists() { | ||
| let cap_bytes = fs::read(cap_path).await?; | ||
| let cap_file = CapabilitiesFile::from_bytes(&cap_bytes) | ||
| .map_err(|e| WasmLoadError::InvalidCapabilities(e.to_string()))?; | ||
| cap_file.validate(name); | ||
|
|
||
| // Check WIT version compatibility | ||
| check_wit_version_compat( | ||
| name, | ||
| cap_file.wit_version.as_deref(), | ||
| crate::tools::wasm::WIT_TOOL_VERSION, | ||
| )?; | ||
|
|
||
| let caps = cap_file.to_capabilities(); | ||
| let oauth = resolve_oauth_refresh_config(&cap_file); | ||
| let desc = cap_file.description.clone(); | ||
| // Validate parameters schema before accepting it. | ||
| let params = cap_file.parameters.clone().and_then(|p| { | ||
| let errors = crate::tools::validate_tool_schema(&p, name); | ||
| if errors.is_empty() { | ||
| Some(p) | ||
| } else { | ||
| tracing::warn!( | ||
| tool = name, | ||
| ?errors, | ||
| "Invalid parameters schema in capabilities.json, \ | ||
| using permissive fallback" | ||
| ); | ||
| None | ||
| } | ||
| }); | ||
| if desc.is_none() { | ||
| tracing::warn!( | ||
| tool = name, | ||
| path = %cap_path.display(), | ||
| "Capabilities file missing \"description\" field; \ | ||
| tool will use generic fallback description" | ||
| ); | ||
| } | ||
| if params.is_none() && cap_file.parameters.is_none() { | ||
| tracing::warn!( | ||
| tool = name, | ||
| path = %cap_path.display(), | ||
| "Capabilities file missing \"parameters\" field; \ | ||
| tool will accept any JSON object (permissive fallback)" | ||
| ); | ||
| } | ||
| (caps, oauth, desc, params) | ||
| } else { | ||
| tracing::warn!( | ||
| path = %cap_path.display(), | ||
| "Capabilities file not found, using default (no permissions)" | ||
| ); | ||
| (Capabilities::default(), None, None, None) | ||
| } | ||
| } else { | ||
| tracing::warn!( | ||
| path = %cap_path.display(), | ||
| "Capabilities file not found, using default (no permissions)" | ||
| tool = name, | ||
| "No capabilities file for WASM tool; \ | ||
| tool will use generic fallback description and accept any JSON object" | ||
| ); | ||
| (Capabilities::default(), None) | ||
| } | ||
| } else { | ||
| (Capabilities::default(), None) | ||
| }; | ||
| (Capabilities::default(), None, None, None) | ||
| }; | ||
|
|
||
| // Register the tool | ||
| self.registry | ||
|
|
@@ -160,8 +199,8 @@ impl WasmToolLoader { | |
| runtime: &self.runtime, | ||
| capabilities, | ||
| limits: None, | ||
| description: None, | ||
| schema: None, | ||
| description: description.as_deref(), | ||
| schema, | ||
|
Comment on lines
199
to
+203
|
||
| secrets_store: self.secrets_store.clone(), | ||
| oauth_refresh, | ||
| }) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The warning message says the tool will accept "any JSON input", but the runtime fallback schema is an object schema (type "object"); non-object JSON values would still be rejected by schema-based callers. Update the warning text to accurately reflect the fallback behavior (e.g., "any JSON object"), so operators understand the real risk/behavior.