Skip to content

fix(security): load WASM tool description and schema from capabilities.json - #520

Merged
zmanian merged 3 commits into
nearai:stagingfrom
difflabai:fix/m5-wasm-schema-from-capabilities
Mar 11, 2026
Merged

zmanian merged 3 commits into
nearai:stagingfrom
difflabai:fix/m5-wasm-schema-from-capabilities

Conversation

@gabehamilton

Copy link
Copy Markdown
Contributor

Summary

  • Security fix (M5): WASM tools no longer silently accept any JSON input. The extract_tool_description and extract_tool_schema stubs in runtime.rs returned permissive fallbacks for every WASM tool, defeating parameter validation and making the agent unable to use tools correctly.
  • Adds optional description and parameters fields to CapabilitiesFile so tool authors can declare proper metadata in their *.capabilities.json sidecar files.
  • The WasmToolLoader now extracts these fields at load time and passes them as overrides to the tool registry. Tools without these fields log a tracing::warn and fall back to the previous stubs.

Changes

src/tools/wasm/capabilities_schema.rs

  • Added description: Option<String> and parameters: Option<serde_json::Value> to CapabilitiesFile
  • Updated resolve_nested() to promote these fields from inner capabilities wrapper
  • Added 5 new tests covering parsing, fallback, and nested resolution

src/tools/wasm/loader.rs

  • load_from_files() now extracts description and parameters from the parsed CapabilitiesFile and passes them through to WasmToolRegistration
  • Logs tracing::warn when either field is missing, making it easy to identify tools that need capabilities updates
  • Also warns when no capabilities file exists at all

src/tools/wasm/runtime.rs

  • Updated doc comments on extract_tool_description and extract_tool_schema to clarify they are fallbacks and that real metadata should come from capabilities.json

Test plan

  • All 27 capabilities_schema tests pass (including 5 new ones)
  • All 12 loader tests pass
  • All 6 runtime tests pass
  • cargo clippy --all --all-features reports zero warnings
  • No .unwrap() or .expect() in production code paths

Generated with Claude Code

…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>
Copilot AI review requested due to automatic review settings March 4, 2026 03:36
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Warning

You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again!

@github-actions github-actions Bot added scope: tool/wasm WASM tool sandbox size: L 200-499 changed lines risk: medium Business logic, config, or moderate-risk modules contributor: new First-time contributor labels Mar 4, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 CapabilitiesFile to support optional description and parameters (JSON Schema) fields, including nested promotion logic.
  • Updates WasmToolLoader::load_from_files() to extract and pass these fields into WasmToolRegistration, with warnings when metadata is missing.
  • Clarifies runtime.rs docs 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.

Comment thread src/tools/wasm/loader.rs
Comment on lines +147 to +149
"Capabilities file missing \"parameters\" field; \
tool will accept any JSON input (permissive fallback)"
);

Copilot AI Mar 4, 2026

Copy link

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.

Copilot uses AI. Check for mistakes.
Comment thread src/tools/wasm/loader.rs Outdated
Comment on lines +160 to +163
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"

Copilot AI Mar 4, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment thread src/tools/wasm/loader.rs
Comment on lines 173 to +177
runtime: &self.runtime,
capabilities,
limits: None,
description: None,
schema: None,
description: description.as_deref(),
schema,

Copilot AI Mar 4, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.

@zmanian zmanian left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 gabehamilton left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@github-actions github-actions Bot added contributor: regular 2-5 merged PRs and removed contributor: new First-time contributor labels Mar 6, 2026
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>
Copilot AI review requested due to automatic review settings March 9, 2026 17:31

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@henrypark133
henrypark133 changed the base branch from main to staging March 10, 2026 02:31

@zmanian zmanian left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

  1. Schema validation -- validate_tool_schema() is called on capabilities.json parameters, with invalid schemas logged and gracefully falling back to permissive schema
  2. 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.

@zmanian
zmanian merged commit 94b448f into nearai:staging Mar 11, 2026
25 of 26 checks passed
@gabehamilton
gabehamilton deleted the fix/m5-wasm-schema-from-capabilities branch March 12, 2026 18:11
@ironclaw-ci ironclaw-ci Bot mentioned this pull request Mar 12, 2026
bkutasi pushed a commit to bkutasi/ironclaw that referenced this pull request Mar 28, 2026
…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>
drchirag1991 pushed a commit to drchirag1991/ironclaw that referenced this pull request Apr 8, 2026
…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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

contributor: regular 2-5 merged PRs risk: medium Business logic, config, or moderate-risk modules scope: tool/wasm WASM tool sandbox size: L 200-499 changed lines

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants