{
  "module": "B6 — Inter-Agent Trust and Communication Security",
  "course": "2B — Securing & Attacking Harnesses and LLMs",
  "version": "1.0.0",
  "duration_minutes": 35,
  "total_questions": 15,
  "bloom_distribution": {
    "target": "20% recall / 40% application / 40% analysis-design",
    "actual": { "recall": 3, "application": 6, "analysis": 6 }
  },
  "passing_score_percent": 70,
  "questions": [
    {
      "id": "Q01", "bloom": "recall", "type": "multiple_choice",
      "prompt": "What is Microsoft's Failure Mode Taxonomy v2.0 failure mode #3, and why is it the canonical framing for inter-agent trust attacks?",
      "options": [
        "A model jailbreak that propagates across agents; it is canonical because jailbreaks are the most common AI failure.",
        "Inter-Agent Trust Escalation — forging a message between agents (e.g., a low-trust agent forging an orchestrator message) to escalate privileges beyond the forger's authority. It is canonical because it is empirical (drawn from a year of red-teaming real agentic systems) and names the structural cause: inter-agent messages are trusted on content, not authenticated sender identity.",
        "A memory-poisoning attack that crosses session boundaries; it is canonical because it is the OWASP top risk.",
        "A tool-calling vulnerability where an agent abuses a tool's authority; it is canonical because tools are the highest-risk surface."
      ],
      "answer_index": 1,
      "rationale": "Microsoft Failure Mode Taxonomy v2.0 #3 (Inter-Agent Trust Escalation) is the canonical framing because it is empirical — drawn from a year of red-teaming — and it names the structural root cause explicitly: the recipient trusts the message's content (a 'from' field) rather than an authenticated sender identity. The multi-agent confused-deputy pattern (a low-trust agent forging an orchestrator message to make a high-trust agent act) is the canonical instance."
    },
    {
      "id": "Q02", "bloom": "recall", "type": "multiple_choice",
      "prompt": "What are the four properties of the signed inter-agent message contract?",
      "options": [
        "Encryption, compression, batching, retry.",
        "Authenticity (signed by a key only the sender holds, recipient verifies), integrity (signature covers header + payload), freshness (timestamp + nonce), and binding (task_id in the signed payload).",
        "Confidentiality, availability, non-repudiation, scalability.",
        "Hashing, salting, peppering, and rotation."
      ],
      "answer_index": 1,
      "rationale": "The four properties are authenticity (cryptographic sender verification), integrity (signature covers the whole message so tampering invalidates it), freshness (timestamp window + single-use nonces defeat replay), and binding (task_id ties the message to a specific task so an approval for task 42 cannot replay against task 43). This is the same contract TLS, JWT, and mTLS enforce; the novelty is applying it to the inter-agent channel."
    },
    {
      "id": "Q03", "bloom": "recall", "type": "multiple_choice",
      "prompt": "What open gap in DD-16 ZeroClaw's HMAC tool receipts does B6 close, and how?",
      "options": [
        "ZeroClaw used AES instead of HMAC; B6 fixes the algorithm.",
        "ZeroClaw's signing keys are EPHEMERAL (in-memory only, not persisted) — so messages signed in session 1 cannot be verified in session 2, and a restart invalidates every signature. B6 closes this with DURABLE, ROTATED keys in the B5 vault (persistence + rotation + key IDs + a verify-retention window), so signatures survive session boundaries where the B3 sleeper and ASI06 cascade operate.",
        "ZeroClaw had no replay protection; B6 adds nonces.",
        "ZeroClaw used symmetric keys where asymmetric were needed; B6 switches to RSA."
      ],
      "answer_index": 1,
      "rationale": "Course 1 flagged that ZeroClaw's HMAC keys are ephemeral — the gap is durability, not algorithm. Ephemeral keys authenticate within a session but not across sessions, which is exactly the boundary the B3 sleeper attack (a poisoned message retrieved later) and the cascade exploit. B6 closes it with durable, rotated keys in the secrets vault (the harness accesses it, not the agent), key IDs (the JWT kid pattern), a verification-retention window for rotated-out keys, and an asymmetric option for cross-domain trust."
    },
    {
      "id": "Q04", "bloom": "application", "type": "multiple_choice",
      "prompt": "Your multi-agent mesh has an orchestrator and three executors all run by the same team in the same process. Which signature scheme should inter-agent messages use, and why?",
      "options": [
        "Asymmetric (Ed25519), because it is always more secure.",
        "HMAC (symmetric), because all agents are in one trust domain; a shared secret is fast, simple, and sufficient. Asymmetric's scaling advantage (N recipients without N secrets) and non-repudiation are not needed within a single team/process.",
        "No signatures — same-team agents can be trusted implicitly.",
        "RSA with 4096-bit keys, for maximum key length."
      ],
      "answer_index": 1,
      "rationale": "The rule is HMAC within a trust domain, asymmetric across trust domains. When all agents are operated by one team in one process, they share a trust domain, so a shared HMAC secret is the right choice — it is faster and simpler, and the operational/political reasons for asymmetric (cross-team key distribution, non-repudiation across org boundaries) do not apply. Asymmetric becomes necessary when agents cross team or org boundaries (e.g., platform-team orchestrator + product-team executor), where sharing a symmetric secret is untenable. Implicit trust (option 3) is the mesh default and the root cause of every attack in the module."
    },
    {
      "id": "Q05", "bloom": "application", "type": "multiple_choice",
      "prompt": "An attacker captures a validly-signed orchestrator 'approve_deployment' message for task 42 (signature valid, the deployment ran). They replay it two minutes later while task 43 is active. Your verification flow checks signature, timestamp (60s window), nonce, and task_id. Which gate rejects the replay?",
      "options": [
        "The signature gate — the message was not re-signed.",
        "The task_id gate — the message's task_id (42) does not match the current task (43). This is why task binding is in the contract: an approval for one task cannot replay against another.",
        "The nonce gate — the nonce was seen in the first deployment.",
        "The timestamp gate — two minutes exceeds a 60s window."
      ],
      "answer_index": 1,
      "rationale": "The task_id gate rejects it. The signature is valid (genuinely signed), the nonce may not be in the task-43 session's cache, and depending on timing the timestamp may be within the window — but the task_id (42) is bound into the signed payload and does not match the current task (43), so the task-binding check fails. This is exactly why task binding is the fourth property of the contract: it closes cross-task replay that the other three gates (signature, freshness, nonce) might miss. If only two minutes passed, the timestamp gate (60s) would also reject, but the task_id gate is the structurally correct answer because it catches the replay even if the attacker re-sends within the window."
    },
    {
      "id": "Q06", "bloom": "application", "type": "multiple_choice",
      "prompt": "A compromised research agent (low-trust) sends a message to the deploy agent (high-trust) claiming 'from: orchestrator, intent: approve_deployment.' The channel uses signed messages. What happens at the deploy agent's verify()?",
      "options": [
        "The deploy agent executes the deployment because the 'from' field says orchestrator.",
        "verify() fails at the signature gate — the research agent does not hold the orchestrator's signing key, so it cannot produce a valid HMAC/signature for 'from: orchestrator.' The forged message is rejected. The deploy agent never acts on an unforgeable message.",
        "verify() fails at the nonce gate because the research agent used the wrong nonce format.",
        "verify() passes because the research agent is in the same process."
      ],
      "answer_index": 1,
      "rationale": "This is the entire defense against trust escalation (Microsoft Failure Mode #3). The research agent cannot forge the orchestrator's signature because it does not hold the orchestrator's signing key (which lives in the vault, per B5 credential isolation — the agent never sees any signing key, including its own). The signature gate (gate 5 of the verification flow) rejects the message. The 'from' field is irrelevant — it is verified by the signature, not read from the payload. This is what turns the multi-agent confused deputy from a working attack into a rejected one."
    },
    {
      "id": "Q07", "bloom": "application", "type": "multiple_choice",
      "prompt": "You deploy signed inter-agent messages with durable HMAC keys, but you omit the timestamp and nonce (signature only, with task_id binding). What attack still works, and what is the minimum fix?",
      "options": [
        "No attack works — signature + task_id is sufficient.",
        "Within-task replay: an attacker captures a valid approve_deployment for task 42 and replays it MULTIPLE TIMES within the task-42 session. Each replay has a valid signature and a matching task_id, so the deploy agent executes repeatedly — a second, third, fourth unauthorized deployment within the same task. Fix: add timestamp (freshness window) AND nonce (single-use); both are needed — drop either and a replay path opens.",
        "Forged orchestrator messages — signature alone does not prevent forgery.",
        "Cross-domain key theft — timestamps do not affect key security."
      ],
      "answer_index": 1,
      "rationale": "Signature + task_id defeats forgery and cross-task replay, but NOT within-task replay. A captured message for task 42 can be replayed many times within the task-42 session (the task_id matches every time). The fix is BOTH timestamp (freshness window — reject old messages) AND nonce (single-use — reject seen nonces). Drop either and a path opens: drop timestamps and the nonce cache (which must be bounded) eventually evicts the nonce, allowing replay; drop nonces and an attacker replays within the freshness window. All three replay controls (timestamp + nonce + task binding) are required."
    },
    {
      "id": "Q08", "bloom": "application", "type": "multiple_choice",
      "prompt": "Your orchestrator delegates to 10 executor agents. One executor's output is poisoned (hallucinated), and the other 9 consume it as fact. What OWASP risk is this, and what is the structural defense?",
      "options": [
        "ASI04 (Memory Poisoning); defense is harness-managed writes (B3).",
        "ASI06 (Cascading Hallucination) — the poisoned output propagates through the mesh as downstream agents consume it as fact; fan-out (1→10) amplifies it. The structural defense is output verification at every edge (never consume a peer's output as fact without checking), signed messages (so a forged peer output is rejected), and blast-radius caps (fan-out limit) so the cascade hits a hard limit.",
        "ASI07 (Insecure Output Handling); defense is input sanitization.",
        "ASI03 (Excessive Agency); defense is per-task scoped credentials."
      ],
      "answer_index": 1,
      "rationale": "ASI06 (Cascading Hallucination) is the propagation risk: one poisoned/hallucinated output consumed as fact by downstream agents corrupts all their reasoning. Fan-out (10 executors) amplifies it — one bad output poisons all 10. The cascade is worse than a single hallucination because each hop adds confidence ('two sources agree') and the original error is buried. The defense is structural: output verification at every edge (peer output = untrusted data, not fact), signed messages, and blast-radius caps (a fan-out limit is the circuit breaker). B8 observability detects the cascade; the cap stops it."
    },
    {
      "id": "Q09", "bloom": "application", "type": "multiple_choice",
      "prompt": "You implement compartmentalization but put the trust-level check inside each agent (the agent checks its own trust level before acting on a peer's request). Why does this fail, and where must the check live?",
      "options": [
        "It works fine — agents are good at checking their own permissions.",
        "It fails because a COMPROMISED agent will claim whatever trust level gets its message through — an injected agent cannot be trusted to enforce its own trust boundary. The check must live in the ORCHESTRATOR — the smaller, deterministic, auditable policy enforcement point that mediates every cross-compartment request. This is the same principle as B4's tool gate and B5's scope check: the deterministic component holds the policy because the model-in-the-loop component can be coerced.",
        "It fails because agents run too slowly to check permissions.",
        "It works if the agent logs the check, even if it does not enforce it."
      ],
      "answer_index": 1,
      "rationale": "The load-bearing principle of compartmentalization: the boundary is enforced in the orchestrator, not the agents. An agent cannot be trusted to enforce its own trust level because a compromised agent is coerced into bypassing it. The orchestrator — deterministic, compiled, auditable — is the policy enforcement point. This mirrors B4 (the harness enforces tool capability, not the model) and B5 (the harness enforces scope, not the agent). The model-in-the-loop component can always be coerced; the deterministic component cannot."
    },
    {
      "id": "Q10", "bloom": "analysis", "type": "multiple_choice",
      "prompt": "Why is the inter-agent edge rated the worst blast radius on the B1 surface map, and what two structural properties of the mesh cause it?",
      "options": [
        "Because agents are the most complex component; complexity causes blast radius.",
        "Because the mesh is CONNECTED and trust is IMPLICIT. Connected: a compromise at any node can reach every other node through the message graph. Implicit trust: peer outputs are consumed as fact with no authentication or verification, so a forged or poisoned message propagates without resistance. Together these make one compromised node → mesh-wide damage. The fix is signed messages (authenticate every edge) + output verification + blast-radius caps (break the connectedness assumption under attack).",
        "Because inter-agent messages are larger than user messages.",
        "Because orchestrators have the highest privilege in the system."
      ],
      "answer_index": 1,
      "rationale": "Two structural properties: the mesh is connected (graph reachability means any node can reach any other) and trust is implicit (peer outputs consumed as fact, no verification). Connected + implicit trust = one compromise propagates everywhere. This is why B1 rated it mesh-wide, the worst case. The defense breaks both properties: signed messages add authentication (so a forged message is rejected even on a connected graph), output verification adds skepticism (so a poisoned output does not propagate as fact), and blast-radius caps break the connectedness assumption by hard-limiting cascade depth and fan-out."
    },
    {
      "id": "Q11", "bloom": "analysis", "type": "multiple_choice",
      "prompt": "Your signed-message implementation uses a bounded LRU nonce cache with a TTL of 30 seconds, but the freshness window (timestamp tolerance) is 60 seconds. Describe the replay attack this enables.",
      "options": [
        "No attack — the LRU cache is sufficient.",
        "An attacker captures a valid message at time T (timestamp T, nonce N). They wait 35 seconds. The nonce N has expired from the cache (TTL 30s), but the message is still 'fresh' by timestamp (within the 60s window). They replay it at T+35s: the freshness gate passes (35 < 60), the nonce gate INCORRECTLY reports N as unseen (it was evicted), and the signature verifies. The replay succeeds. Fix: the nonce cache TTL must be ≥ the freshness window, so any message the freshness gate accepts has a nonce still in the cache.",
        "The attack is forgery — the attacker forges a new nonce.",
        "The attack requires stealing the signing key."
      ],
      "answer_index": 1,
      "rationale": "The nonce cache TTL must be at least as long as the freshness window. If it is shorter, a nonce expires while the message is still timestamp-fresh, creating a window (between TTL and freshness-window) where a replay passes both the freshness and nonce gates. The fix is TTL ≥ freshness window: any message the freshness gate would accept has a nonce still tracked, so replay is correctly rejected. The bounded LRU must also be large enough that nonces within the window are not evicted by volume — size it to peak message rate × window."
    },
    {
      "id": "Q12", "bloom": "analysis", "type": "multiple_choice",
      "prompt": "A signed-message defense is deployed with durable keys, but the signing key is stored in an environment variable the agent process can read. What attack does this enable, and what is the fix?",
      "options": [
        "No attack — environment variables are secure.",
        "The agent can read its own signing key, so an INJECTED agent can be coerced into signing ARBITRARY messages (a confused deputy): the attacker directs the compromised agent to sign 'from: orchestrator, approve_deployment,' producing a VALIDLY-SIGNED forged message that passes verify(). This defeats the entire defense. Fix: B5 credential isolation applied to signing keys — the harness signs on the agent's behalf; the key lives in the vault; the agent NEVER sees it. The agent cannot forge what it cannot sign.",
        "The attack is replay — env vars do not affect replay.",
        "The attack is cascade — env vars cause cascades."
      ],
      "answer_index": 1,
      "rationale": "This is the confused-deputy attack restated for signing keys. If the agent can read its signing key, an injected agent is coerced into signing whatever the attacker directs — producing validly-signed malicious messages that pass the signature gate. The entire signed-message defense collapses because the signature verifies. The fix is B5's credential isolation: the key lives in the vault the harness accesses, not the agent process; the harness signs on the agent's behalf; the agent never sees the raw key. This is the same principle as B5's rule that the agent never holds a long-lived secret — applied here to the signing key specifically."
    },
    {
      "id": "Q13", "bloom": "analysis", "type": "multiple_choice",
      "prompt": "Construct the session-level intent tracking control. What does it check, why is it deterministic, and which attack does it stop that signature verification alone does not?",
      "options": [
        "It checks the model's confidence score; it is deterministic because confidence is a number; it stops hallucination.",
        "The orchestrator records the AUTHORIZED INTENT at session start (e.g., 'research; do NOT deploy'). Every agent message is checked: does its intent match? A research agent emitting 'deploy' is out of policy and rejected — REGARDLESS of whether the message is validly signed. It is deterministic because the policy is a COMPILED BOOLEAN the agent cannot talk past (the B2.3 resolution applied to the inter-agent channel). It stops a COMPROMISED ORCHESTRATOR (or a legitimately-signed message from a compromised high-trust agent) directing an out-of-policy action — which signature verification alone cannot detect, because the signature is valid.",
        "It checks the user's OAuth token; it is deterministic because tokens are signed; it stops unauthorized access.",
        "It checks the agent's memory; it is deterministic because memory is persistent; it stops the sleeper attack."
      ],
      "answer_index": 1,
      "rationale": "Session-level intent tracking is the control that catches what signatures cannot: a legitimately-signed message (from a compromised orchestrator or high-trust agent) directing an action outside the session's authorized intent. The orchestrator pins the intent at session start ('research; no deploy'), and every message's intent is checked against it as a compiled boolean. A research agent that starts emitting deploy intents is rejected even if the message is perfectly signed — because the policy is about INTENT, not just authenticity. This is deterministic (the B2.3 resolution: the model cannot coerce a compiled gate) and it is the layer that bounds a compromised high-trust agent, which signature verification alone cannot."
    },
    {
      "id": "Q14", "bloom": "analysis", "type": "multiple_choice",
      "prompt": "Your mesh enforces signed messages, replay protection, and session intent tracking. An attacker compromises the orchestrator itself (via injection) and sends validly-signed, in-policy deploy intents to the deploy agent. Which control bounds the damage, and why can it not be removed?",
      "options": [
        "Signature verification — it rejects the compromised orchestrator's messages.",
        "The BLAST-RADIUS CAPS (cascade depth, fan-out, session action budget). The compromised orchestrator's messages are validly signed and in-policy, so signature and intent checks pass — but the caps are STRUCTURAL CIRCUIT BREAKERS that limit how many state-changing actions can occur per session regardless of authorization. They cannot be removed because they are the last line of defense when an interior node (the orchestrator) is compromised: they ensure a cascade of authorized-looking actions hits a hard limit (e.g., max K deploys/session) rather than consuming the mesh. B8 detects the anomaly; the cap stops it.",
        "Compartmentalization — it isolates the orchestrator from the deploy agent.",
        "The nonce cache — it limits the orchestrator to one message."
      ],
      "answer_index": 1,
      "rationale": "When the orchestrator itself is compromised, its messages are validly signed (it holds the key) and can be crafted to match the session intent. Signature verification and intent tracking both pass. The only remaining control is the blast-radius cap: a hard limit on cascade depth, fan-out, and session actions that does not depend on the message being malicious. This is why the caps are 'blunt on purpose' — they bound damage from unanticipated compromise that the semantic controls (signature, intent) cannot classify as an attack. They are the structural floor; B8 observability detects the anomaly and alerts, but the cap is what stops the damage in real time."
    },
    {
      "id": "Q15", "bloom": "analysis", "type": "multiple_choice",
      "prompt": "You are designing a multi-agent system with a platform-team orchestrator and a product-team executor that must exchange signed messages. The teams refuse to share a symmetric secret. Design the signature scheme and key distribution.",
      "options": [
        "Use HMAC with a shared secret stored in a shared vault both teams can read.",
        "Use asymmetric signatures (Ed25519): each trust compartment (platform team, product team) has its own keypair. The orchestrator signs with the platform team's PRIVATE key; the executor verifies with the platform team's PUBLIC key. Public keys are distributed out-of-band via a key directory, JWKS endpoint, or SPIFFE/SPIRE trust bundle (B5 workload identity). No shared secret crosses the team boundary. The signature also provides non-repudiation (audit can prove which compartment signed). This mirrors the mTLS/SPIFFE precedent for cross-cluster/cross-team trust.",
        "Use no signatures — the teams will handle trust via a contract.",
        "Use HMAC but have each team generate the same key independently from a shared seed."
      ],
      "answer_index": 1,
      "rationale": "Cross-domain trust requires asymmetric signatures. Each compartment holds its own keypair; the signer uses its private key, the verifier uses the corresponding public key distributed out-of-band (JWKS, SPIFFE bundle, key directory). No shared secret crosses the org boundary, which is the operational and political requirement. The scheme scales to N compartments without N×(N-1)/2 shared secrets, and provides non-repudiation (useful for cross-team audit). This is exactly the SPIFFE/SPIRE workload-identity model from B5 (the platform attests continuously via SVIDs) applied to inter-agent message signing. Option 4 (deriving the same key from a shared seed) is just a shared secret with extra steps and fails for the same reason."
    }
  ]
}
