Skip to content

feat(validation): JSON-Schema compile timeout + depth pre-scan#255

Closed
zeevdr wants to merge 1 commit into
feat/schema-limitsfrom
feat/schema-compile-timeout
Closed

feat(validation): JSON-Schema compile timeout + depth pre-scan#255
zeevdr wants to merge 1 commit into
feat/schema-limitsfrom
feat/schema-compile-timeout

Conversation

@zeevdr
Copy link
Copy Markdown
Member

@zeevdr zeevdr commented Apr 29, 2026

Summary

Test plan

  • make pre-commit (build, vet, format, lint, test, coverage)
  • New unit tests in internal/validation/json_schema_test.go cover: DefaultLimits, depth-exceeded rejection, depth-disabled (MaxDepth: 0), malformed-JSON falls through to compiler, timeout-zero-is-unbounded, and direct scanJSONDepth cases (objects, arrays, non-JSON)
  • Existing factory_concurrent_test.go / validator_bench_test.go / cache_race_test.go callers continue to compile (zero-opt form preserved)
  • Env vars SCHEMA_COMPILE_TIMEOUT and SCHEMA_MAX_REF_DEPTH documented in docs/server/configuration.md
  • .agents/context/security-review.md updated — finding 6 now marked complete across both halves

Closes #217. Merge after #254.

🤖 Generated with Claude Code

Lands the second half of #217 (security review finding 6): bound the
JSON-Schema compiler so a malicious per-field constraint cannot hang
the server during the first compile. Sits on top of #254, which already
caps schema doc bytes and field count at the ingest layer.

- internal/validation/limits.go — new Limits struct (CompileTimeout,
  MaxDepth) + DefaultLimits (5 s, depth 64) + shared Option/WithLimits
  pattern usable by both the factory and individual field validators.
- internal/validation/json_schema.go — newJSONSchemaValidator now takes
  Limits. A pre-compile depth scan rejects pathological nesting before
  invoking the compiler; the Compile call itself runs in a goroutine
  and is bounded by CompileTimeout. jsonschema/v6 has no CompileContext,
  so the timeout is a wall-clock backstop — the goroutine may continue
  past the deadline, but the depth scan + upstream MaxDocBytes cap
  bound the worst-case work.
- NewValidatorFactory(store, opts...) and NewFieldValidator(..., opts...)
  accept the shared Option type; existing zero-opt call sites continue
  to compile unchanged.
- cmd/server reads SCHEMA_COMPILE_TIMEOUT (Go duration) and
  SCHEMA_MAX_REF_DEPTH env vars and threads them through WithLimits on
  the shared validator factory.

Closes #217.
@zeevdr zeevdr added this to the Security Review milestone Apr 29, 2026
@zeevdr zeevdr added enhancement New feature or request server Server changes size: M Moderate — a day or two, clear scope priority: P0 Blocks alpha or release labels Apr 29, 2026
zeevdr added a commit that referenced this pull request Apr 29, 2026
…Service (#254)

Lands the first half of #217: bound schema document size and field count at
CreateSchema / ImportSchema, so a single malicious import cannot exhaust the
server before validator caching helps. Compile-timeout for the JSON-Schema
compiler ships in the stacked follow-up #255.

- internal/schema/limits.go — new Limits struct (MaxFields, MaxDocBytes) +
  DefaultLimits (10 000 fields, 5 MiB).
- schema.NewService(store, opts...) — store stays positional; logger,
  metrics, validators, and limits move to With...() options. Mirrors the
  config / audit / auth / server functional-options sweep from #235, #236, #249.
- ImportSchema rejects YAML payloads above MaxDocBytes before parsing, and
  CreateSchema / ImportSchema reject schemas above MaxFields after parsing.
  Zero on either dimension means unlimited.
- cmd/server reads SCHEMA_MAX_FIELDS and SCHEMA_MAX_DOC_BYTES env vars and
  threads them through WithLimits. Both documented in
  docs/server/configuration.md.

Refs #217.
@zeevdr zeevdr deleted the branch feat/schema-limits April 29, 2026 11:23
@zeevdr zeevdr closed this Apr 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request priority: P0 Blocks alpha or release server Server changes size: M Moderate — a day or two, clear scope

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant