Cross-agent session context leak across delegation chain
Detects cross-agent context leakage in multi-agent systems where a privileged context attribute (typically session.id, user.id, or conversation.id) fails to remain constant across a single agent delegation chain. This is a trace-method rule that operates on agent execution traces in OpenInference format, not on input text. Threat model: in a multi-agent workflow, Agent A delegates to Agent B; B may legitimately spawn sub-agents to complete the task. The invariant ATR enforces is that the SAME session/user/conversation identifier MUST hold across every span in one delegation chain. If a downstream agent retrieves context from a DIFFERENT session (e.g., a tenant other than the original caller's) and folds it into its reasoning, the trace exhibits an attribute drift on the affected key. This pattern matches the cross-task / cross- tenant context leak class formalized in Argus (arXiv 2512.08326) and the compositional privacy risk taxonomy (arXiv 2509.14284). Detection covers (a) session.id drift across spans in agent.delegation_chain, (b) user.id drift across the same chain. The rule uses the `invariant` trace primitive defined in spec/atr-method-v1.1.md §8.3.3.
建議回應
參考資料
偵測條件
組合方式: any- 01Engine emits this synthetic field when the trace invariant fires; present for pattern-method engines that consume trace verdicts as text. Trace-native engines evaluate detection.trace below directly.欄位: trace.invariant_session_id運算子: regex
攻擊範例(規則會觸發)
{"spans":[{"id":"s1","kind":"AGENT","attributes":{"session.id":"sess_A","user.id":"u_001","agent.delegation_chain":"chain_1"}},{"id":"s2","kind":"AGENT","attributes":{"session.id":"sess_B","user.id":"u_001","agent.delegation_chain":"chain_1"}}]}{"spans":[{"id":"s1","kind":"AGENT","attributes":{"session.id":"sess_X","user.id":"u_100","agent.delegation_chain":"chain_2"}},{"id":"s2","kind":"RETRIEVER","attributes":{"session.id":"sess_X","user.id":"u_999","agent.delegation_chain":"chain_2"}}]}{"spans":[{"id":"s1","kind":"AGENT","attributes":{"session.id":"a","user.id":"alice","agent.delegation_chain":"c1"}},{"id":"s2","kind":"TOOL","attributes":{"session.id":"a","user.id":"alice","agent.delegation_chain":"c1"}},{"id":"s3","kind":"AGENT","attributes":{"session.id":"b","user.id":"alice","agent.delegation_chain":"c1"}}]}{"spans":[{"id":"r1","kind":"AGENT","attributes":{"session.id":"tenant_A_sess","user.id":"a","agent.delegation_chain":"d1"}},{"id":"r2","kind":"AGENT","attributes":{"session.id":"tenant_B_sess","user.id":"b","agent.delegation_chain":"d1"}}]}{"spans":[{"id":"x1","kind":"AGENT","attributes":{"session.id":"sess_1","user.id":"u","agent.delegation_chain":"e1"}},{"id":"x2","kind":"TOOL","attributes":{"tool.name":"db.query","session.id":"sess_2","user.id":"u","agent.delegation_chain":"e1"}}]}
以上為真實攻擊 payload 脫敏版本。用於 regression testing。
正常樣本(規則不會觸發)
- Constant session.id and user.id across the chain — no drift
{"spans":[{"id":"s1","kind":"AGENT","attributes":{"session.id":"sess_A","user.id":"u_001","agent.delegation_chain":"chain_1"}},{"id":"s2","kind":"AGENT","attributes":{"session.id":"sess_A","user.id":"u_001","agent.delegation_chain":"chain_1"}}]} - Different delegation chains — invariant scoped per chain, so different sessions across chains is permitted
{"spans":[{"id":"s1","kind":"AGENT","attributes":{"session.id":"sess_X","user.id":"u_100","agent.delegation_chain":"chain_2"}},{"id":"s2","kind":"AGENT","attributes":{"session.id":"sess_Y","user.id":"u_100","agent.delegation_chain":"chain_DIFFERENT"}}]} - Single-span chain — no invariant to violate
{"spans":[{"id":"alone","kind":"AGENT","attributes":{"session.id":"sess_solo","user.id":"u","agent.delegation_chain":"c"}}]} - Three spans, all consistent — invariant holds
{"spans":[{"id":"s1","kind":"AGENT","attributes":{"session.id":"abc","user.id":"alice","agent.delegation_chain":"k1"}},{"id":"s2","kind":"TOOL","attributes":{"tool.name":"calculator","session.id":"abc","user.id":"alice","agent.delegation_chain":"k1"}},{"id":"s3","kind":"AGENT","attributes":{"session.id":"abc","user.id":"alice","agent.delegation_chain":"k1"}}]} - Public retrieval chain without user.id (anonymous query) — no per-user drift to flag
{"spans":[{"id":"p1","kind":"RETRIEVER","attributes":{"session.id":"public_search","agent.delegation_chain":"public_chain"}},{"id":"p2","kind":"LLM","attributes":{"session.id":"public_search","agent.delegation_chain":"public_chain"}}]}
已知誤報情境
- ▸Delegation chain naming convention dependency: the rule assumes attributes.agent.delegation_chain holds a per-invocation unique ID (e.g., UUID per delegation). If a framework serializes it as a static role name ("main_chain", "default") shared across unrelated invocations, spans from different requests will appear in the same chain and the invariant will fire spuriously. Mitigation: operators MUST verify their framework emits chain IDs that are unique per invocation; for frameworks that emit static names, use attributes.gen_ai.conversation.id as the across-domain instead (requires rule variant; see roadmap).
- ▸Anonymous public retrieval chains: when a chain processes requests without user.id (anonymous public API), the rule's user.id invariant is vacuously satisfied (no value to compare). This is correct behavior, documented in TN #5.
完整 YAML 定義
在 GitHub 編輯 →title: "Cross-agent session context leak across delegation chain"
id: ATR-2026-00548
rule_version: 1
status: draft
description: >
Detects cross-agent context leakage in multi-agent systems where a privileged
context attribute (typically session.id, user.id, or conversation.id) fails
to remain constant across a single agent delegation chain. This is a
trace-method rule that operates on agent execution traces in OpenInference
format, not on input text.
Threat model: in a multi-agent workflow, Agent A delegates to Agent B; B may
legitimately spawn sub-agents to complete the task. The invariant ATR
enforces is that the SAME session/user/conversation identifier MUST hold
across every span in one delegation chain. If a downstream agent retrieves
context from a DIFFERENT session (e.g., a tenant other than the original
caller's) and folds it into its reasoning, the trace exhibits an attribute
drift on the affected key. This pattern matches the cross-task / cross-
tenant context leak class formalized in Argus (arXiv 2512.08326) and the
compositional privacy risk taxonomy (arXiv 2509.14284).
Detection covers (a) session.id drift across spans in
agent.delegation_chain, (b) user.id drift across the same chain. The rule
uses the `invariant` trace primitive defined in
spec/atr-method-v1.1.md §8.3.3.
author: "ATR Community"
date: "2026/05/28"
schema_version: "1.0"
maturity: draft
severity: high
references:
owasp_agentic:
- "ASI03:2026 - Data Exfiltration"
- "ASI06:2026 - Identity Spoofing & Impersonation"
mitre_atlas:
- "AML.T0024 - Exfiltration via Cyber Means"
research:
- "Argus: Hierarchical Reference-Relationship Graph for Multi-Agent Information Leakage (arXiv:2512.08326)"
- "Compositional Privacy Risks in Multi-Agent Systems (arXiv:2509.14284)"
compliance:
nist_csf:
- "DE.CM-09"
etsi_ts_104223:
- "P4.3"
eu_ai_act:
- article: "10"
context: >
Data governance — multi-agent systems must preserve session-scope
boundaries; cross-tenant leakage via delegation is a data-governance
failure under Article 10.
strength: primary
nist_ai_rmf:
- subcategory: "MS.2.6"
context: >
Information security — agent delegation chains must preserve session
and user scope; attribute drift across spans is an integrity violation.
strength: primary
tags:
category: context-exfiltration
subcategory: cross-agent-context-drift
scan_target: runtime
confidence: high
source: trace-rule
agent_source:
type: agent_trace
framework:
- any
provider:
- any
detection:
method: trace
condition: any
false_positives:
- >
Delegation chain naming convention dependency: the rule assumes
attributes.agent.delegation_chain holds a per-invocation unique ID
(e.g., UUID per delegation). If a framework serializes it as a
static role name ("main_chain", "default") shared across unrelated
invocations, spans from different requests will appear in the same
chain and the invariant will fire spuriously. Mitigation: operators
MUST verify their framework emits chain IDs that are unique per
invocation; for frameworks that emit static names, use
attributes.gen_ai.conversation.id as the across-domain instead
(requires rule variant; see roadmap).
- >
Anonymous public retrieval chains: when a chain processes requests
without user.id (anonymous public API), the rule's user.id
invariant is vacuously satisfied (no value to compare). This is
correct behavior, documented in TN #5.
conditions:
- field: trace.invariant_session_id
operator: regex
value: "(?i)invariant_violation:session.id_changed"
description: >
Engine emits this synthetic field when the trace invariant fires;
present for pattern-method engines that consume trace verdicts as
text. Trace-native engines evaluate detection.trace below directly.
trace:
ingest_format: openinference
invariant:
- attribute: "session.id"
across: "agent.delegation_chain"
description: >
session.id MUST remain constant across every span in one
delegation chain. Drift indicates downstream agent operated under
a different session scope.
- attribute: "user.id"
across: "agent.delegation_chain"
description: >
user.id MUST remain constant across the delegation chain. Drift
indicates the downstream agent processed a different user's data.
response:
actions:
- alert
- quarantine_session
message_template: >
[ATR-2026-00548] HIGH: Cross-agent session context leak detected. The
delegation chain starting at span {{trace.root_span_id}} exhibits drift
on attribute {{trace.violated_invariant}}; span {{trace.first_drifted_span_id}}
operated under a different session/user scope than the root. Quarantine
the trace, preserve full span DAG for forensics, and audit data
accessed by the drifted spans.
confidence: 85
wild_fp_rate: 0
test_cases:
true_positives:
- input: |
{"spans":[{"id":"s1","kind":"AGENT","attributes":{"session.id":"sess_A","user.id":"u_001","agent.delegation_chain":"chain_1"}},{"id":"s2","kind":"AGENT","attributes":{"session.id":"sess_B","user.id":"u_001","agent.delegation_chain":"chain_1"}}]}
expected: triggered
description: "session.id drifts from sess_A to sess_B within one delegation chain"
- input: |
{"spans":[{"id":"s1","kind":"AGENT","attributes":{"session.id":"sess_X","user.id":"u_100","agent.delegation_chain":"chain_2"}},{"id":"s2","kind":"RETRIEVER","attributes":{"session.id":"sess_X","user.id":"u_999","agent.delegation_chain":"chain_2"}}]}
expected: triggered
description: "user.id drifts from u_100 to u_999 within one delegation chain"
- input: |
{"spans":[{"id":"s1","kind":"AGENT","attributes":{"session.id":"a","user.id":"alice","agent.delegation_chain":"c1"}},{"id":"s2","kind":"TOOL","attributes":{"session.id":"a","user.id":"alice","agent.delegation_chain":"c1"}},{"id":"s3","kind":"AGENT","attributes":{"session.id":"b","user.id":"alice","agent.delegation_chain":"c1"}}]}
expected: triggered
description: "session.id drifts on the third span of a three-span chain"
- input: |
{"spans":[{"id":"r1","kind":"AGENT","attributes":{"session.id":"tenant_A_sess","user.id":"a","agent.delegation_chain":"d1"}},{"id":"r2","kind":"AGENT","attributes":{"session.id":"tenant_B_sess","user.id":"b","agent.delegation_chain":"d1"}}]}
expected: triggered
description: "Cross-tenant chain: both session.id and user.id drift simultaneously"
- input: |
{"spans":[{"id":"x1","kind":"AGENT","attributes":{"session.id":"sess_1","user.id":"u","agent.delegation_chain":"e1"}},{"id":"x2","kind":"TOOL","attributes":{"tool.name":"db.query","session.id":"sess_2","user.id":"u","agent.delegation_chain":"e1"}}]}
expected: triggered
description: "TOOL span queries DB under different session than the parent AGENT span"
true_negatives:
- input: |
{"spans":[{"id":"s1","kind":"AGENT","attributes":{"session.id":"sess_A","user.id":"u_001","agent.delegation_chain":"chain_1"}},{"id":"s2","kind":"AGENT","attributes":{"session.id":"sess_A","user.id":"u_001","agent.delegation_chain":"chain_1"}}]}
expected: not_triggered
description: "Constant session.id and user.id across the chain — no drift"
- input: |
{"spans":[{"id":"s1","kind":"AGENT","attributes":{"session.id":"sess_X","user.id":"u_100","agent.delegation_chain":"chain_2"}},{"id":"s2","kind":"AGENT","attributes":{"session.id":"sess_Y","user.id":"u_100","agent.delegation_chain":"chain_DIFFERENT"}}]}
expected: not_triggered
description: "Different delegation chains — invariant scoped per chain, so different sessions across chains is permitted"
- input: |
{"spans":[{"id":"alone","kind":"AGENT","attributes":{"session.id":"sess_solo","user.id":"u","agent.delegation_chain":"c"}}]}
expected: not_triggered
description: "Single-span chain — no invariant to violate"
- input: |
{"spans":[{"id":"s1","kind":"AGENT","attributes":{"session.id":"abc","user.id":"alice","agent.delegation_chain":"k1"}},{"id":"s2","kind":"TOOL","attributes":{"tool.name":"calculator","session.id":"abc","user.id":"alice","agent.delegation_chain":"k1"}},{"id":"s3","kind":"AGENT","attributes":{"session.id":"abc","user.id":"alice","agent.delegation_chain":"k1"}}]}
expected: not_triggered
description: "Three spans, all consistent — invariant holds"
- input: |
{"spans":[{"id":"p1","kind":"RETRIEVER","attributes":{"session.id":"public_search","agent.delegation_chain":"public_chain"}},{"id":"p2","kind":"LLM","attributes":{"session.id":"public_search","agent.delegation_chain":"public_chain"}}]}
expected: not_triggered
description: "Public retrieval chain without user.id (anonymous query) — no per-user drift to flag"