BXUP Track progress & test yourself — free
Security & AI Security · Playbook · Advanced · ~4 min read
SECURITY & AI SECURITY · PLAYBOOK · ADVANCED

Design Against the Lethal Trifecta with the Rule of Two and Taint-Gating

This playbook is the architectural capstone of agent security. Simon Willison's lethal trifecta (June 2025) names the exact combination that turns prompt injection into data theft, and Meta's Agents Rule of Two turns it into a design constraint you can enforce.

Tier: Advanced · Workflow area: RAG & agent security

Taint-tracking is the mechanism that lets an agent keep all three capabilities most of the time while blocking the dangerous moments. This playbook teaches all three accurately and applies them to redesign an unsafe agent of your own into a safe one.

When to reach for this

  • An agent design review needs a crisp yes/no on whether the architecture is exploitable.
  • Your agent has private data access, sees untrusted content, and can communicate externally — all at once.
  • You want a policy the whole team can apply, not a per-feature judgment call.

The workflow

Threat, briefly — the lethal trifecta. An agent is dangerous when it combines all three of: (1) access to private data, (2) exposure to untrusted content, and (3) the ability to communicate externally (exfiltrate). With all three, an injection hidden in untrusted content can instruct the agent to read private data and send it out. Remove any one leg and the attack has no path.

  1. Classify your agent against the three legs. For the session, mark each: does it touch private/sensitive data? does it ingest untrusted content? can it externally communicate or change state? All three present = exploitable by design.

  2. Apply the Agents Rule of Two. Meta's formulation: within a session an agent should satisfy at most two of these properties — [A] processes untrustworthy input, [B] has access to sensitive systems or private data, [C] can change state or communicate externally. If a workflow needs all three, it must not run autonomously — require human-in-the-loop or split it into separate sessions that each hold only two.

    Design rule (enforce in review):
      count(A_untrusted_input, B_sensitive_access, C_external_action) <= 2
    If all three are required -> human approval gate OR session split.
    
  3. Break a leg by design where you can. Prefer the cheapest leg to remove per workflow: strip the exfil leg (egress allow-list, no external comms — see pb-33), or the private-data leg (don't give the untrusted-input path access to secrets), or isolate untrusted content into a session that holds nothing sensitive.

  4. Where you must keep all three, add taint-tracking. Mark any data derived from untrusted content as tainted. Propagate the taint through the agent's state. Gate privileged actions (external send, sensitive read, state change) on whether their inputs are tainted; if tainted, block or force human confirmation.

    class Value:
        def __init__(self, data, tainted=False):
            self.data, self.tainted = data, tainted
    def external_send(v: Value):
        if v.tainted:                       # data touched untrusted content
            return require_human_approval(v)  # gate, don't auto-send
        return send(v.data)
    
  5. Make the taint gate un-bypassable from model text. Like the human gate in pb-37, the confirmation must come from the UI/session, never from a token the model can emit.

  6. Test the trifecta on your local agent. Build the unsafe all-three version locally, land an indirect injection that reads private data and tries to exfiltrate, then apply Rule-of-Two / taint-gating and confirm the exfil is blocked.

    # local agent you own
    python run_agent.py --task "process this ticket"   # ticket = poisoned doc
    # undefended: private data beacons out. defended: tainted send BLOCKED.
    
Key Takeaway:

The lethal trifecta names the danger; the Rule of Two makes it a countable design rule (hold at most two of untrusted-input / sensitive-access / external-action); taint-gating lets you keep all three safely only by blocking privileged actions on tainted data.

Verify before you trust it

The defense holds when the redesigned agent cannot complete the read-private-then-exfiltrate chain. Success criteria:

  • Architecture shows the change: your before/after architecture diagram shows the undefended agent holding all three legs and the redesigned one holding at most two per session (or gated).
  • Injection blocked after redesign: the indirect injection that exfiltrates on the undefended build is blocked on the defended build — the tainted external-send hits the gate and no beacon fires at your local listener.
  • Legitimate work survives: legitimate, untainted workflows still complete, proving you constrained the dangerous path without breaking the feature.

The proof is the exfil channel closing while normal use survives — a working Rule-of-Two or taint boundary, not a blanket shutdown.

Pitfalls

  • Counting capabilities across sessions instead of within one. The Rule of Two applies per session; a workflow that holds all three at once is unsafe even if each could be separated. Split sessions to reduce the count.
  • Calling an egress allow-list "no external comms." DNS, logs, and error channels are comms too. Removing the leg means removing all of them (pb-33).
  • Taint that doesn't propagate. If tainted data laundered through a summary loses its taint, the gate never fires. Propagate taint through every transformation.
  • A gate the model can self-approve. Same failure as any human gate — approval must be out-of-band.
  • Treating this as prompt engineering. No system prompt makes an all-three agent safe. This is an architecture control, enforced in code and review.

Tools

  • A threat-modeling checklist / design-review gateencode the three-leg classification and the Rule-of-Two count into the review template.
  • A guardrail/policy framework with data-flow tagging (NeMo Guardrails-class + custom taint wrapper) — propagate taint and gate privileged actions.
  • PyRIT / DeepTeammulti-turn indirect-injection tests to prove the trifecta is broken on your local agent.

Client-safe variant

This is a design and policy engagement, ideal for client work because it produces a reusable rule rather than touching data. Run the three-leg classification and Rule-of-Two count on the client's agent architectures in a whiteboard/review setting, and encode the check into their design-review template so every future agent is scored the same way. The taint-gating and session-splitting are implemented in the client's own code; the proof-of-concept exfil test runs against a client-approved staging agent with a synthetic poisoned document and a local canary listener. The output — a before/after architecture and a standing design rule — is exactly the kind of durable, framework-aligned control (mapping to OWASP LLM01/LLM06 and the Agentic Top 10) that a client's security architecture function can own long after the engagement ends.

This is the free reading view. Open BXUP to track your progress, run the hands-on labs, and prove it with original, exam-pattern assessments.
© Anvi Innovate · BXUP — Be Exponentially Upskilled · BXUP is an independent learning platform, not affiliated with or endorsed by AWS, Microsoft, Google, NVIDIA, IBM, Salesforce, Databricks, Oracle or Anthropic. All trademarks belong to their respective owners. · bxup.in