Skip to content

Harden validation of untrusted tool schemas: well-formedness checks, depth and size limits, validator caching #484

Description

@dgenio

Summary

Validate tool input schemas themselves — not just arguments against them — when catalogs are ingested from external sources, and bound schema complexity (depth, size) before running jsonschema validation. Cache compiled validators per tool.

Why this matters

The gateway validates LLM-supplied arguments against schemas that arrive from upstream servers, but a malformed or pathologically nested schema currently flows through unchecked until validation time. Checking schema well-formedness at ingest gives catalog authors early, actionable errors; complexity bounds keep validation costs predictable on untrusted inputs; validator caching removes repeated compilation from the hot execute path.

Current evidence

  • src/contextweaver/adapters/mcp.py (~lines 131–136): inputSchema / outputSchema from upstream tools/list are stored verbatim on the SelectableItem with no checks.
  • src/contextweaver/adapters/proxy_runtime.py lines 572–583: _validate_args calls jsonschema.validate(instance=args, schema=schema) per execution — schema is assumed well-formed, no check_schema, no depth/size guard, and a fresh validation each call.
  • docs/gateway_spec.md §4.4 mandates argument validation but does not address schema trust or complexity.

Proposed implementation

  1. At catalog ingest (and hydrate_with_schema in routing/hydration.py), run jsonschema.validators.validator_for(schema).check_schema(schema); surface failures as a structured normalization/catalog error rather than at execution time.
  2. Add conservative complexity bounds (configurable): max serialized size (e.g. 64 KB), max nesting depth (e.g. 32), max property count. Reject or flag schemas beyond bounds with a clear error.
  3. Cache compiled validators keyed by canonical tool_id + hash8 (the existing schema-shape hash in routing/tool_id.py is a natural cache key); invalidate on catalog refresh.
  4. Map ingest-time schema failures to a distinct GatewayError code (e.g. SCHEMA_INVALID) and document in the spec.
  5. Extend CatalogNormalizer / NormalizationReport (routing/normalizer.py) to report schema-health findings so operators can audit catalogs.

Example prompt, schema, or interface

report = normalizer.normalize(catalog)
# report.schema_findings -> [
#   SchemaFinding(tool_id="acme:deploy", kind="not_well_formed", detail="'type': 'strnig'"),
#   SchemaFinding(tool_id="acme:query", kind="depth_exceeded", detail="depth 41 > 32"),
# ]

Acceptance criteria

  • Malformed schemas are detected at ingest with tool-level diagnostics, not at first execution.
  • Schemas exceeding configured complexity bounds are rejected or flagged per configuration.
  • Repeated tool_execute calls for the same tool reuse a cached validator (verifiable via a counter in tests).
  • make ci passes; spec documents the new behavior and error code.

Test and evaluation plan

  • Unit tests: well-formed, malformed, oversized, and deeply nested schema fixtures.
  • Performance check: validator caching measurably reduces repeated-validation time in the gateway benchmark (benchmarks/gateway_benchmark.py).
  • Regression: existing real-catalog snapshots (tests/test_architectures_mcp_context_gateway_real.py) still load cleanly.

Migration notes

Catalogs containing malformed schemas that previously loaded silently will now produce diagnostics; default to flag-and-continue with strict mode opt-in to avoid breaking existing deployments.

Risks and tradeoffs

Bounds that are too tight could reject legitimate large schemas (some enterprise APIs are huge) — limits must be configurable and defaults generous. Caching adds invalidation complexity tied to catalog refresh (coordinate with #424).

Suggested labels

security, reliability, area/gateway

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions