Skip to content

Security: Knowledge/RAG retrieval fall-opens to unfiltered corpus when ExecutionContext is missing or system — agent data ceiling escape #2981

Description

@os-zhuang

Surfaced by the ADR-0096 execution-surface audit. The knowledge/RAG retrieval permission filter fails open when no caller identity is supplied — returning semantic-search hits across the entire corpus, unscoped by RLS.

Detail

packages/services/service-knowledge/src/knowledge-service.ts applyPermissionFilter(hits, ctx) (:308):

if (!ctx || ctx.isSystem) return hits;   // :312 — no ctx OR system ⇒ every hit passes through

When ctx is present and non-system, the function correctly re-queries each source object under the caller's context and drops hits the caller can't see (:332-342). But with ctx omitted (or system), the RLS re-scope is skipped and all hits — including sourceRecordId-backed object rows the caller may not be permitted to read — are returned.

Why it's reachable (agent angle)

The AI tool loop that drives retrieval passes identity via ToolExecutionContext.actor, which the contract declares optional with a system-level fallback:

packages/spec/src/contracts/ai-service.ts (:378,393) — "Optional… when omitted, tools fall back to system-level behaviour" / "Omit for internal/system invocations."

So an agent/chat path whose (out-of-framework, cloud-side) route does not populate actor from req.user gets unfiltered semantic search over the whole knowledge corpus — another instance of the D10 agent data ceiling being escaped (cf. #2849/#2845), this time on the read/retrieval side.

Suggested fix

  • Make applyPermissionFilter fail closed on missing identity: no ctx ⇒ drop object-source hits (the same conservative branch already used when dataEngine is unbound, :313-318), rather than returning everything. Reserve the pass-through strictly for an explicit systemContext(reason) (ADR-0096 D2), not for "context happened to be absent."
  • Harden the ToolExecutionContext contract (ai-service.ts): retrieval/data tools should require an actor, or the framework should refuse to run them system-level unless an explicit trusted-run flag is set. Coordinate with the cloud chatWithTools implementation.

Relationship to ADR-0096

Direct instance of the class — a data path that treats "no context" as "full access." Exactly the seam ADR-0096 D1/D5 (fail-closed on principal-less non-system calls) closes. Tracking under the ADR-0096 evidence base.

Refs: ADR-0096 (PR #2975), #2849, #2845 (agent data ceiling framing).

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

    bugSomething isn't workingpriority:p1High: required for production / M2

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions