Skip to content

security: Phase-2 hardening — close #73/#80/#71, advance #76 - #83

Merged
xerj-org merged 2 commits into
mainfrom
security/phase2-hardening
Jul 31, 2026
Merged

security: Phase-2 hardening — close #73/#80/#71, advance #76#83
xerj-org merged 2 commits into
mainfrom
security/phase2-hardening

Conversation

@xerj-org

Copy link
Copy Markdown
Owner

Security & perf hardening (Phase 2, wave 1)

Fixes the tractable, self-contained items from the six open post-audit issues, each with a regression test. The three remaining items need invasive plumbing or a design decision and are called out below rather than rushed (correctness over breadth for security code).

Fixed

  • #73 (High) — snapshot settings.location residual .. escape (F-PATH-02). A location whose fully-resolved target did not yet exist made canonicalize() fail, so the lexical fallback kept the raw .. components and a component-based Path::starts_with treated <data_dir>/../../escape as inside data_dir; create_dir_all then wrote outside it. Now any .. path component in a repo location is rejected up front. (engine.rs; +regression test covering the direct escape and the allowlist-laundering variant.)
  • #80create_index path-traversal defense-in-depth. The es_compat create_index handler now calls IndexName::validate at the boundary (made pub) instead of relying solely on the deeper Engine::create_index guard. Same accepted set; the rejection is now local and obvious.
  • #71 — ONNX windowing memory regression. embed_semantic_jobs no longer pre-materializes (and clones) every window's passage texts up front. Windows carry (start, end, passages) only; texts are built lazily and moved into embed_batch — sequential path moves per window, dual path materializes at most the two in-flight windows (collect_ordinal_buffered_two launches lazily).
  • #76 S5-5 — field-limit bypass on the explicit mapping path. Index::add_fields (PUT /:index/_mapping, schema evolve) now enforces max_fields_per_index, matching the dynamic-ingest guard. (+regression test.)
  • #76 S5-3 — magic-link redeem TOCTOU. The single-use check and the mark_magic_link_used commit are now serialized by a redeem gate held across the whole check→consume, so two concurrent redeems of one token can no longer both mint a session.
  • #76 AUTHZ-2 — pre-auth info disclosure. The unauthenticated cluster/info endpoint no longer returns node_id or the exact build version (fingerprinting vectors); it keeps only mode (needed for the SPA's boot decision) and benign uptime. (+regression test.)

Deferred (tracked on their issues — each needs a dedicated, tested change)

All touched crates pass fmt/clippy (-D warnings) and the new + existing tests.

xerj-org added 2 commits July 31, 2026 19:40
…k TOCTOU, info disclosure, index-name boundary; perf: ONNX windowing

Fixes the self-contained items from the post-audit backlog, each with a
regression test where testable:

- #73 (High) F-PATH-02 residual: a snapshot repository `settings.location`
  containing `..` that resolves to a nonexistent target escaped `data_dir`
  (canonicalize lexical fallback + component-based starts_with). Reject `..`
  path components outright. (+regression test: direct + allowlist-laundering.)
- #80: wire IndexName::validate (now pub) at the es_compat create_index
  boundary — defense-in-depth, same accepted set.
- #71: ONNX embed_semantic_jobs no longer pre-materializes/clones every
  window's passage texts; windows carry (start,end,passages) and texts are
  built lazily and moved into embed_batch (dual: <=2 in-flight).
- #76 S5-5: enforce max_fields_per_index on the explicit add_fields path
  (PUT _mapping / schema evolve), not only dynamic ingest. (+regression test.)
- #76 S5-3: serialize magic-link redeem check->consume behind a redeem gate
  so two concurrent redeems of one token cannot both mint a session.
- #76 AUTHZ-2: drop node_id and exact build version from the unauthenticated
  cluster/info body; keep only mode + uptime. (+regression test.)

Deferred (tracked): #76 S5-4 (ConnectInfo through shared serve/TLS path),
#75 (cluster frame auth wire-format + fail-closed config), #79 (per-brain
access model).
…egression guard)

The #76 AUTHZ-2 fix removed node_id and the exact build version from the
unauthenticated cluster/info response. Update the phase-1 integration tests
to assert those fields are now absent (and keep asserting mode + uptime), so
they double as a regression guard instead of asserting the old leaky shape.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant