Skip to content

    AI Architect Case Files

    Case: Tool Abuse and Data Exfiltration, Privilege Split

    A composite case on a clinic assistant with a send-email tool and a broad service account: how a confused deputy leaks data and how least privilege stops it.

    By Published 6 min read

    • ai-architect
    • case-file
    • industry-healthcare
    • pattern-privilege-separation
    • failure-data-leak
    • kind-composite
    Share

    Composite scenario, not a real company. Assembled from common industry patterns; every number is illustrative and its assumptions are listed at the end.

    This case belongs to Security and Threat Modeling for AI Systems. Defensive framing only: the injected text below is a placeholder, not a working payload.

    Context

    • A regional clinic network gives 300 staff an assistant that answers questions over referral letters and care plans.
    • It has three tools: search documents, read a record, and send an email.
    • It runs under one service account (a shared, non-human identity) that can read all 200,000 patient records. A staff member normally needs about 150.
    • Some documents arrive from outside organisations, so their text is not written by the clinic.

    Problem statement

    Defect found in review Consequence
    Over-broad authority: the tools act as the service account, not as the staff member Text in a letter, such as [sentence addressed to the assistant], can cause reads the user could not make
    Unrestricted outbound channel: the send tool takes any recipient What the assistant reads can leave the network

    Goals (illustrative targets): a session can reach only the records its user may see, and no email goes outside the organisation without a person approving it.

    Constraints

    • Staff do need to email summaries to colleagues, and occasionally to an outside GP.
    • Answers must stay interactive.
    • Every read and send must be attributable to a named person.
    • The model provider holds no credentials.

    Why the traditional approach fell short

    Role-based access control (RBAC) checks the user, but the tools called with the service account, so the check never saw the user. Data-loss rules on outbound email match known formats, such as an identifier pattern, and miss a paraphrased summary. Doing it by hand is safe and slow, which is why the assistant exists.

    Options considered

    Option Verdict
    A. Remove the send tool: the assistant drafts, and staff send from their own mail client (the simpler option) Accepted as the default wherever sending is occasional
    B. Tell the model in the prompt never to send data outside Rejected: it is a request, and the same context that carries the request carries the letter
    C. Least privilege and egress control: user-scoped credentials, a recipient allow-list, approval for external sends, a network allow-list Accepted where sending is needed

    Solution architecture

    Figure 1. Where authority and egress are controlled

    flowchart LR accTitle: Clinic assistant with user-scoped authority and controlled egress accDescr: The Client app sends the user's session to the Orchestrator. The Orchestrator asks the Policy engine for a credential scoped to that user and passes it to the Tool server, which reads the Document store inside that scope. Sends go through the Approval service when the recipient is external, and leave only through an egress proxy to the internal mail relay. The Orchestrator reaches the external LLM provider through the Model gateway and writes audit events to the Event bus. subgraph ZA["Zone A: staff"] CA["Client app"] end subgraph ZB["Zone B: application"] OR["Orchestrator"] PE["Policy engine"] AP["Approval service"] MG["Model gateway"] end subgraph ZC["Zone C: tools and data"] TS["Tool server"] DS[("Document store")] end XP["Egress proxy: allow-list"] MR["Internal mail relay"] LLM["LLM provider (external)"] EB["Event bus: audit"] CA ==>|"session with user identity"| OR OR -->|"may this user do this?"| PE PE -->|"credential scoped to the user"| OR OR ==>|"call with scoped credential"| TS TS ==>|"read inside the user's scope"| DS OR -->|"external recipient"| AP TS ==>|"send"| XP ==> MR OR ==>|"prompt text only"| MG ==> LLM OR -.->|"audit event"| EB

    How to read this: thick arrows cross a trust boundary; the egress proxy is the last control, and it works even if every control above it fails.

    Figure 2. Runtime path with a steered request

    sequenceDiagram accTitle: Steered send request stopped by policy and approval accDescr: A staff member asks about a letter. The Orchestrator reads it through the Tool server with a user-scoped credential. The model, steered by text in the letter, requests a send to an outside address. The Policy engine marks it external, the Approval service asks the staff member, who rejects it. If a send were wrongly approved, the egress proxy still permits only the internal mail relay. autonumber participant U as Staff member participant O as Orchestrator participant L as LLM provider participant P as Policy engine participant T as Tool server participant A as Approval service participant X as Egress proxy U->>O: summarise this referral letter O->>L: prompt and tool list L-->>O: request to read the letter O->>P: may this user read it? P-->>O: allow, credential scoped to the user O->>T: read with the scoped credential T-->>O: letter text, marked untrusted O->>L: text as data L-->>O: request to send email to an outside address O->>P: may this send run? P-->>O: external recipient, approval needed O->>A: show recipient and body to the user A-->>O: rejected Note over O,X: If approved wrongly, the proxy still allows only the internal relay

    How to read this: the model's request is evaluated by code that knows the user, the recipient and the destination.

    How AI solved it, step by step

    The stepper follows one letter that contains steering text.

    One referral letter with steering text (interactive when JavaScript is on)

    As designed

    1. Ordinary request. A staff member asks the assistant to summarise a referral letter.
    2. Service account reads. The letter includes a placeholder sentence addressed to the assistant. The tools run as the service account.
    3. Model asks for more reads. It requests records outside the staff member's care list. The service account can read all 200,000, so nothing refuses.
    4. Model asks to send. It requests an email to an outside address. The send tool accepts any recipient.
    5. Data leaves. The mail goes out through unrestricted outbound access. It is found later, if an audit looks.

    With the fix

    1. Identity travels with the request. The session carries the staff member's identity; the Policy engine issues a credential scoped to that person's records.
    2. Letter marked untrusted. The tool returns the text as data; it cannot widen the credential.
    3. Out-of-scope read denied. A request for another patient's record fails at the Policy engine and again at the Document store, and an alert is logged.
    4. External send needs a person. The Policy engine flags the outside recipient; the staff member sees the recipient and the body, and rejects it.
    5. Network allow-list. If a send were approved by mistake, the egress proxy permits only the internal mail relay.
    6. Audit and pause. Every call writes an audit event, and three denied reads in one session pause it for review.

    Under the hood

    A confused deputy is a program that holds authority from more than one source and cannot keep them apart. Norm Hardy's 1988 account of a compiler at Tymshare says so: "The fundamental problem is that the compiler runs with authority stemming from two sources." It adds that the compiler "serves two masters and carries some authority from each" and "has no way to keep them apart" (Hardy, The Confused Deputy).

    This page's mapping, not Hardy's: the Orchestrator holds the service account's authority, acts on a user's request, and its model output is shaped by third-party text. Hardy's remedy is to carry authority with the request, so that a credential both identifies a resource and authorises use of it. Here that is a user-scoped credential per call.

    What went wrong / failure modes

    Failure What the system does Why
    Staff cover for a colleague and need a record outside their scope Break-glass: a second person approves, and the access is audited Denying everything pushes staff back to shared logins
    An "internal" mailbox auto-forwards outside The allow-list names individual groups, and the relay never auto-forwards A domain check alone is too coarse
    A new tool gets its own internet route Tool servers have no direct outbound route, and a network test checks it Egress control fails if one path skips the proxy
    A tool result tries to widen privileges Results are data; only the Policy engine grants credentials Authority never comes from model-visible text
    Approval fatigue on external sends Show recipient and body, and sample approved sends Approvals work only if they are read

    Results

    These are illustrative targets, not measurements.

    Target Value
    Records reachable per session from 200,000 to about 150, roughly 1,333 times fewer
    Cross-user reads in a 150-prompt adversarial set 0
    External sends without approval 0
    External-send approvals 4,000 sessions x 12% send x 5% external = 24 a day
    Outbound destinations from the tool tier 1 (the internal mail relay)

    Tradeoffs and when NOT to use this approach

    • User-scoped credentials need integration work in every data source.
    • Approval adds delay to external sends; 24 a day is manageable, 2,400 would not be.
    • This limits what a steered model can reach and send. It does not stop it writing a misleading summary.
    • If sending is rare, take option A and remove the tool.

    Design rules extracted

    1. Run the agent with the requesting user's authority, not a service account's.
    2. If a person can do a step as easily as the tool, remove the tool.
    3. Require approval for any action that crosses the organisation boundary.
    4. Enforce egress below the application, with a network allow-list.
    5. Treat denied calls as signals: alert, and pause the session.

    Assumptions

    • 300 staff, 200,000 records, about 150 records per staff member: 200,000 / 150 = 1,333.
    • 4,000 sessions a day; 12% use the send tool (480 sends); 5% of those are external: 480 x 0.05 = 24 approvals.
    • The 150-prompt test set and the threshold of three denied reads are chosen for illustration.
    • The clinic network and all names are invented.

    Last verified: 2026-10-22

    Ask about this article

    Get answers grounded in this post. AI-generated — based on this article, and may be imperfect.

    Scaled AI Weekly

    Enjoyed this? Get more like it every Monday.

    Real architecture decisions, LLMOps patterns that survive production, and engineering leadership advice — from 12+ years of building at enterprise scale. Free. No spam. Unsubscribe anytime.

    Get new articles by email

    Free. We email a confirmation link first, and you can unsubscribe from any email.

    Join engineers building production AI systems

    Comments