[bug] hero_embedder parses but discards X-Hero-Context — cross-context vector leak risk #31

Open
opened 2026-05-01 04:09:21 +00:00 by mik-tf · 0 comments
Owner

Summary

hero_embedder parses the X-Hero-Context header but immediately discards it. Per-context isolation depends entirely on every caller passing the correct namespace parameter — a soft contract, not enforcement. A misbehaving or compromised caller can read or pollute another context's vector index.

Source

  • crates/hero_embedder_server/src/main.rs:38-53X-Hero-Context is parsed.
  • Same file :112 and :220 — the parsed value is bound to _hero_context and never read.
  • search(query, top_k, namespace, ...), index.add, vectors.add, namespace.create — all key off the namespace request parameter, which is fully caller-controlled.

Why this matters

Per-context vector indices live at ~/hero/var/embedder/data/<namespace>/q<n>/rag.redb (default root from EMBEDDER_DATA). The embedder cannot tell whether a caller is asking on behalf of the right context — it just trusts whatever namespace arrives.

Concretely:

  • A bug in a service's namespace-resolution code can read another context's vectors.
  • An attacker that gains the embedder's rpc.sock (Unix socket on the same host — boundary is filesystem permissions only) can enumerate all namespaces via namespace.list and read any of them.

Proposed fix

Two layers, defense in depth:

  1. Bind context to namespace at the entry handler. Either reject calls whose namespace doesn't start with <context>/ prefix, or rewrite the namespace server-side as <context>/<original> and treat the request as that namespace.
  2. Enforce on namespace.list / namespace.stats that callers can only see namespaces under their own context prefix.

Severity

Medium-high. Not exploitable from outside the host, but invalidates the per-context isolation claim in the sovereignty pitch.

Cross-refs

  • Same shape on hero_indexer (separate issue, this session)
  • Same shape on hero_aibroker (no per-context awareness at all — separate issue)
  • hero_demo#52 — vision

Spotted during docs_hero Phase 1 source-grounded read (session 52). Reconciliation memo: memory/investigation_roadmap_reconciliation.md.

## Summary `hero_embedder` parses the `X-Hero-Context` header but immediately discards it. Per-context isolation depends entirely on every caller passing the correct `namespace` parameter — a soft contract, not enforcement. A misbehaving or compromised caller can read or pollute another context's vector index. ## Source - [`crates/hero_embedder_server/src/main.rs:38-53`](https://forge.ourworld.tf/lhumina_code/hero_embedder/src/branch/development/crates/hero_embedder_server/src/main.rs) — `X-Hero-Context` is parsed. - Same file `:112` and `:220` — the parsed value is bound to `_hero_context` and never read. - `search(query, top_k, namespace, ...)`, `index.add`, `vectors.add`, `namespace.create` — all key off the `namespace` request parameter, which is fully caller-controlled. ## Why this matters Per-context vector indices live at `~/hero/var/embedder/data/<namespace>/q<n>/rag.redb` (default root from `EMBEDDER_DATA`). The embedder cannot tell whether a caller is asking on behalf of the right context — it just trusts whatever `namespace` arrives. Concretely: - A bug in a service's namespace-resolution code can read another context's vectors. - An attacker that gains the embedder's `rpc.sock` (Unix socket on the same host — boundary is filesystem permissions only) can enumerate all namespaces via `namespace.list` and read any of them. ## Proposed fix Two layers, defense in depth: 1. **Bind context to namespace at the entry handler.** Either reject calls whose `namespace` doesn't start with `<context>/` prefix, or rewrite the namespace server-side as `<context>/<original>` and treat the request as that namespace. 2. **Enforce on `namespace.list` / `namespace.stats`** that callers can only see namespaces under their own context prefix. ## Severity Medium-high. Not exploitable from outside the host, but invalidates the per-context isolation claim in the sovereignty pitch. ## Cross-refs - Same shape on `hero_indexer` (separate issue, this session) - Same shape on `hero_aibroker` (no per-context awareness at all — separate issue) - [hero_demo#52 — vision](https://forge.ourworld.tf/lhumina_code/hero_demo/issues/52) Spotted during docs_hero Phase 1 source-grounded read (session 52). Reconciliation memo: `memory/investigation_roadmap_reconciliation.md`.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
lhumina_code/hero_embedder#31
No description provided.