Skip to content

[security][high] src/visibility.rs:61 — team/unit/org scope treated as world-readable — is_visible_to_caller returns true for #1921

Description

@alphaonedev

Severity: high · CWE-863 Incorrect Authorization (scope isolation not enforced) · Location: src/visibility.rs:61 · dim: tenant-isolation

Attack

is_visible_to_caller() short-circuits if scope != "private" { return true }, so scope in {team,unit,org,collective,shared,legacy} passes for ANY caller with no namespace-subtree check. This predicate is the ONLY scope gate on the paths that never invoke the SQL visibility_clause: HTTP GET /api/v1/memories (list_memories, src/handlers/memories_query.rs:148 postgres and :195 sqlite — the code comment itself notes 'db::list does NOT apply the visibility-prefix filter'), the SAL adapters get()/list() (src/store/sqlite.rs:173 and :274; postgres twins), the entity-alias fallback walk (src/handlers/kg.rs:357/383), and the kg_query per-node filter (src/handlers/kg.rs:1384). A co-tenant NHI agent (any authenticated X-Agent-Id, or anonymous when no api_key) sends GET /api/v1/memories?namespace=<victim-org>/<unit>/<team>&limit=1000 and receives every team/unit/org-scoped Memory in the victim's subtree — full title + content + metadata — even though src/models/namespace.rs:42-49 documents Team/Unit/Org as subtree-restricted isolation levels.

Impact

Full-content cross-tenant disclosure of every non-private (team/unit/org/collective) memory across all tenants via a standard list/get endpoint. The private-scope owner fix (#1720) is intact, but the equally-documented team/unit/org boundary provides zero confidentiality on these read paths.

Fix

Route list()/get() (and any is_visible_to_caller call used as the sole scope gate) through the same subtree enforcement the recall/search SQL visibility_clause uses, keyed on the AUTHENTICATED principal's namespace prefixes (never a request-supplied as_agent). Concretely, change is_visible_to_caller to take the caller's (private/team/unit/org) prefixes and apply matches_subtree for Team/Unit/Org instead of returning true, mirroring storage::is_visible().


Found by the v0.9.0 multi-agent security review, adversarially verified. Part of #1918.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions