Repository navigation
fix(security): load WASM tool description and schema from capabilities.json - #520
Conversation
…s.json
The extract_tool_description and extract_tool_schema stubs in runtime.rs
returned permissive fallbacks ("WASM sandboxed tool" and
additionalProperties:true) for every WASM tool, defeating parameter
validation and preventing the LLM from using tools correctly.
Add optional `description` and `parameters` fields to CapabilitiesFile so
tool authors can declare proper metadata in their sidecar JSON. The
WasmToolLoader now extracts these fields and passes them through to the
tool registry as overrides. Tools without a capabilities.json or without
these fields get a tracing::warn and fall back to the old stubs.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Warning You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again! |
There was a problem hiding this comment.
Pull request overview
This PR tightens WASM tool metadata handling by loading tool descriptions and parameter schemas from the *.capabilities.json sidecar file at load time, instead of relying on permissive runtime stubs.
Changes:
- Extends
CapabilitiesFileto support optionaldescriptionandparameters(JSON Schema) fields, including nested promotion logic. - Updates
WasmToolLoader::load_from_files()to extract and pass these fields intoWasmToolRegistration, with warnings when metadata is missing. - Clarifies
runtime.rsdocs that runtime extraction is a fallback and real metadata should come from capabilities sidecars.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
src/tools/wasm/runtime.rs |
Updates doc/comments to clarify description/schema extraction is fallback behavior. |
src/tools/wasm/loader.rs |
Loads description + parameter schema from capabilities files and forwards them into tool registration with warnings. |
src/tools/wasm/capabilities_schema.rs |
Adds description/parameters fields to the capabilities schema, promotes nested fields, and adds tests. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| "Capabilities file missing \"parameters\" field; \ | ||
| tool will accept any JSON input (permissive fallback)" | ||
| ); |
There was a problem hiding this comment.
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.
| 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 input" |
There was a problem hiding this comment.
This warning also says the tool will accept "any JSON input", but the fallback schema only permits JSON objects. Adjust the log message to match the actual fallback schema behavior ("any JSON object"), to avoid misleading diagnostics.
| runtime: &self.runtime, | ||
| capabilities, | ||
| limits: None, | ||
| description: None, | ||
| schema: None, | ||
| description: description.as_deref(), | ||
| schema, |
There was a problem hiding this comment.
parameters is accepted as an arbitrary serde_json::Value and forwarded directly as the tool's parameter schema override. If the capabilities file contains a malformed/unsupported schema (or a non-object value), this can break downstream function-calling/tool schema consumers. Consider validating params with the existing schema validators (e.g., crate::tools::validate_tool_schema for a lenient check) and, on errors, log the issues and fall back to the runtime/default schema instead of registering an invalid one.
zmanian
left a comment
There was a problem hiding this comment.
Review
The security goal is correct — trusting WASM-provided metadata for tool schema is a supply chain risk (malicious module could self-report a misleading description or permissive schema). Moving metadata to the host-controlled capabilities.json sidecar is the right fix. The implementation is clean: Option<String>/Option<Value> with #[serde(default)], resolve_nested() with outer-wins semantics, and structured tracing::warn for missing fields.
Required before merge
1. Validate parameters schema before accepting (Medium)
The serde_json::Value from capabilities.json is forwarded as-is to WasmToolRegistration::schema. A malformed value (e.g., "parameters": "not an object") could break downstream LLM function-call serialization. validate_tool_schema() already exists at src/tools/tool.rs:314 — call it and warn+fallback if invalid:
if let Some(ref params) = capabilities.parameters {
let errors = crate::tools::validate_tool_schema(params, name);
if !errors.is_empty() {
tracing::warn!(tool = name, ?errors, "Invalid parameters schema in capabilities.json, using permissive fallback");
// set to None so it falls through to the stub
}
}2. Fix warning text accuracy (Low)
Both warnings say the tool will accept "any JSON input" but the fallback schema (additionalProperties: true with "type": "object") only accepts JSON objects. Should say "any JSON object".
Should fix
3. Add integration test for loader path — the 5 new tests cover CapabilitiesFile parsing in isolation but nothing verifies the full load_from_files() → WasmToolRegistration wiring with description/parameters populated.
4. Document storage path gap — register_wasm_from_storage still uses schema from the database, not covered by this fix. Worth tracking as a separate issue so this security fix isn't considered complete when it isn't.
…ing text
- Resolve merge conflict with upstream main, incorporating cap_file.validate()
- Validate parameters schema from capabilities.json using validate_tool_schema()
before accepting it; invalid schemas fall back to permissive default
- Fix warning text: "any JSON input" -> "any JSON object" to accurately describe
the fallback schema {"type": "object", "additionalProperties": true}
[skip-regression-check]
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
gabehamilton
left a comment
There was a problem hiding this comment.
Fixed both review items:
1. Schema validation (zmanian Required): Parameters loaded from capabilities.json are now validated using validate_tool_schema() before being accepted. Invalid schemas log a warning with the specific errors and fall back to the permissive default, preventing malformed schemas from reaching the LLM.
2. Warning text accuracy (zmanian Low): Changed "any JSON input" to "any JSON object" in both warning messages to accurately reflect the fallback schema {"type": "object", "additionalProperties": true}.
Also resolved merge conflict with upstream main, incorporating the cap_file.validate(name) call added in the interim.
All 12 loader tests pass. Zero clippy warnings.
Merge both sides: our branch's description/parameters fields and schema validation in the loader, plus upstream's version/wit_version fields and WIT version compatibility checking. All four new CapabilitiesFile fields are preserved and the loader performs both WIT compat checks and schema validation. [skip-regression-check] Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 3 out of 3 changed files in this pull request and generated no new comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
zmanian
left a comment
There was a problem hiding this comment.
Re-review: fix(security): load WASM tool description and schema from capabilities.json
Verdict: APPROVE
Both required items from the original review are correctly addressed:
- Schema validation --
validate_tool_schema()is called on capabilities.json parameters, with invalid schemas logged and gracefully falling back to permissive schema - Warning text -- Corrected from "any JSON input" to "any JSON object" to match the actual fallback behavior
The "should fix" items (integration test for loader path, storage path gap documentation) were not fully addressed but were explicitly not required. The schema parsing has 5 good unit tests covering the new fields. The security goal -- preventing malicious WASM modules from self-reporting misleading metadata -- is achieved.
…s.json (nearai#520) The extract_tool_description and extract_tool_schema stubs in runtime.rs returned permissive fallbacks ("WASM sandboxed tool" and additionalProperties:true) for every WASM tool, defeating parameter validation and preventing the LLM from using tools correctly. Add optional `description` and `parameters` fields to CapabilitiesFile so tool authors can declare proper metadata in their sidecar JSON. The WasmToolLoader now extracts these fields and passes them through to the tool registry as overrides. Tools without a capabilities.json or without these fields get a tracing::warn and fall back to the old stubs. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…s.json (nearai#520) The extract_tool_description and extract_tool_schema stubs in runtime.rs returned permissive fallbacks ("WASM sandboxed tool" and additionalProperties:true) for every WASM tool, defeating parameter validation and preventing the LLM from using tools correctly. Add optional `description` and `parameters` fields to CapabilitiesFile so tool authors can declare proper metadata in their sidecar JSON. The WasmToolLoader now extracts these fields and passes them through to the tool registry as overrides. Tools without a capabilities.json or without these fields get a tracing::warn and fall back to the old stubs. Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Summary
extract_tool_descriptionandextract_tool_schemastubs inruntime.rsreturned permissive fallbacks for every WASM tool, defeating parameter validation and making the agent unable to use tools correctly.descriptionandparametersfields toCapabilitiesFileso tool authors can declare proper metadata in their*.capabilities.jsonsidecar files.WasmToolLoadernow extracts these fields at load time and passes them as overrides to the tool registry. Tools without these fields log atracing::warnand fall back to the previous stubs.Changes
src/tools/wasm/capabilities_schema.rsdescription: Option<String>andparameters: Option<serde_json::Value>toCapabilitiesFileresolve_nested()to promote these fields from innercapabilitieswrappersrc/tools/wasm/loader.rsload_from_files()now extractsdescriptionandparametersfrom the parsedCapabilitiesFileand passes them through toWasmToolRegistrationtracing::warnwhen either field is missing, making it easy to identify tools that need capabilities updatessrc/tools/wasm/runtime.rsextract_tool_descriptionandextract_tool_schemato clarify they are fallbacks and that real metadata should come from capabilities.jsonTest plan
capabilities_schematests pass (including 5 new ones)loadertests passruntimetests passcargo clippy --all --all-featuresreports zero warnings.unwrap()or.expect()in production code pathsGenerated with Claude Code