Skip to content

security(a2a): a peer's research task writes peer-chosen content into the knowledge base, and no capability covers writes #16967

Description

@mrveiss

Problem

An A2A peer's task can write to the knowledge base, and no capability covers that.

  • The orchestrator routes research requests to librarian_assistant.research_query(request): agents/agent_orchestration/agent_execution.py, AgentType.RESEARCH.
  • research_query auto-stores "quality" web content into the KB by default: auto_store_quality defaults to True (agents/librarian_assistant.py:61) and is applied when the caller passes nothing (:501). The write happens through store_in_knowledge_base (:311, :402).
  • The orchestrator passes nothing. So a peer whose query steers the research also chooses content that is written into the knowledge base other users read.

#16957's routing gate requires QUERY_MEMORY for the research agent, a read capability, so a STANDARD peer still reaches this write. The A2A capability matrix has no write capability at all.

Why it matters

The knowledge base is shared and trusted input for retrieval. A peer-chosen write is a poisoning path, and the operator cannot see that a peer caused it.

Acceptance criteria

  • A peer's task cannot cause a knowledge-base write unless a capability that names writes permits it. Either the research route runs with auto-store off for an external originator, or a write capability is defined and enforced at the write. Decide which, and record why.
  • A test drives a peer task through the orchestrator to the research agent and asserts no KB write happens without that permission. Stub the web search; use the real write path.
  • Any KB write that a peer's task is permitted to cause is attributed to the peer, not to the executor.

Activity

  1. MoltyCel commented on Oct 2, 2026

    @MoltyCel

    I read the path at 1b596fc and it holds. research_query() takes store_quality_content=None and falls back to the instance attribute at librarian_assistant.py:500-501, no YAML sets librarian_assistant.auto_store_quality, so the effective default is True, and since the orchestrator's single call site at agent_execution.py:340 passes no flag, the fallback is what actually runs. From there the chain goes through _process_single_search_result:401-402 into store_in_knowledge_base(), and add_document(...) at :311-342 writes the fetched page body into the shared store, while the gate in front of the whole path asks only for QUERY_MEMORY.
    Worth adding to what the issue already says: the file that defines the gate records the write in the same breath as it grants a read. The comment at capability_requirements.py:36-37 — "librarian_assistant holds a KnowledgeBase, and can also add documents to it" — sits directly above "research": Capability.QUERY_MEMORY, and four lines further down the same shape appears for "sentiment_analysis", whose comment names reads and writes of working memory and the agent diary.
    The part that would decide an incident review is attribution. Line :338 hardwires "stored_by": "librarian_assistant", which names the executor and leaves the peer that chose the content out of the record entirely, so a reader of the knowledge base months later can establish that something was stored and cannot establish who caused it or under which authorization. Refusing the write at the capability check is one way out, and recording the requesting peer together with the authorization that admitted it is the other; the second keeps its value even where a deployment decides to allow such writes on purpose, because the question after the fact is rarely whether a write was permitted and almost always which party it came from.
    A test would pin the first half cheaply: a peer at TrustLevel.TRUSTED submits a research task, and the assertion is that no new document appears in the knowledge base afterwards. That test fails today, and trust_score.py:93-114 shows why, since the matrix carries SUBMIT_TASKS and QUERY_MEMORY and nothing that denotes a write at all, which leaves no level able to grant or withhold one.
    Lars

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

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions