AI Agent Security: Permissions, Prompt Injection, and Safe Actions

Today

An AI agent reads a help article containing "ignore previous instructions," or receives a result handle copied from another user. Neither input should grant new authority. The difficult part is making that rule hold in code, even when the model chooses an unexpected tool call.

This tutorial follows the trust boundaries of an in-product AI assistant: authenticated identity, tool arguments, business-data queries, stored artifacts, rendered output, and traces. It ends with a proposed design for adding narrow, explicitly confirmed business actions.

Separate Instructions from Authority#

The scope prompt directs the assistant toward product help, navigation, and business data. It declines unrelated coding questions, drafting outside that scope, arbitrary actions, and even general arithmetic such as "2+2".

That is behavior guidance. It does not prove a model will always refuse, and it does not eliminate prompt injection. Help articles, user text, and model output remain untrusted inputs.

Authority comes from the tool and database boundaries: schemas validate arguments, server context fixes ownership, semantic definitions inject tenant predicates, permissions gate entities and fields, and business-record reads use a read-only transaction.

Rendering has its own boundaries. CSV cells need formula-injection protection. Text entering PDF HTML must be escaped. Source and download URLs need allowlists. Browser context and conversation references need ownership checks. These protections are necessary even when the model's prose looks sensible.

"Read-only" here means business operations do not create or change donor records or send messages on the user's behalf. Chats, feedback, usage, results, and PDFs still write supporting application data.

It also does not mean business information never leaves the application. The model provider receives assembled questions, context, and bounded tool outputs. Tracing is another data path: tracing and content capture default to enabled in the reviewed snapshot. Disabling content capture redacts strings while retaining numbers, keys, and structure, so it is not total anonymization. Deployment privacy decisions need to account for those actual flows.

Recheck Access Where Data Is Read#

Build turn context from authenticated server identity: tenant, user, permissions, enabled modules, and ownership. Let the model supply a query specification, not the identity under which it executes.

Expose only eligible tools, then enforce permissions again inside them. Entity access, filter access, dimensions, and detail fields can differ. Someone allowed to see donation totals is not necessarily allowed to see donor names.

The reference accepts organization-wide analytics grants whose resource ID is null. It does not implement general analytics scope resolution for record-scoped grants. Rejecting those broad operations is safer than assuming a restricted grant applies to the entire organization.

Typed entity search also establishes the kind and provenance of a named-record ID. Navigation keeps a per-turn set of discovered screen IDs. Support article reading checks a known article ID but does not maintain the same per-turn search set. These are distinct checks; do not infer one tool's guarantees from another's behavior.

Read-Only Queries Still Need Resource Limits#

The general business query fetch uses a read-only transaction and a ten-second statement timeout. The executor requests one more than the 50,000-record ceiling to detect an oversized match, then refuses it. Aggregation in application memory has its own CPU and memory cost beyond the database statement.

The agent loop permits six tool-enabled steps plus an answer-only step. Multiple calls can occur within a step. These bounds constrain different resources; none alone is a universal limit on the request's total cost or duration.

A narrower tool surface prevents unsupported mutations. Tenant predicates prevent cross-tenant reads. Read ceilings and timeouts protect availability. Keep all three concerns visible when reviewing an agent tool.

An Opaque Handle Is Not Authorization#

Stored result handles let charts, CSV, and PDF consume server-calculated data without putting full tables into model context. They do not establish ownership.

Every result read must enforce the authenticated tenant and user and reject expired data. The reference stores up to 10,000 result rows, returns a 50-row preview, and expires results after 24 hours. Document reads enforce ownership and expire after seven days. Result lookup is not additionally bound to the originating session.

That means a handle available to the same user in another conversation can still refer to an authorized result; it does not mean another user can use it. If session binding is a product requirement, add it explicitly and test follow-up behavior.

Expiry checks must happen during reads. Cleanup jobs remove expired records later; deletion timing is not the access boundary. Saved conversations likewise require user and organization scoping rather than trust in a client-supplied conversation ID.

Treat Every Renderer as Another Boundary#

Model output can become links, CSV cells, HTML, or UI controls. Each destination has its own interpretation rules.

For CSV, escape values correctly and neutralize text that spreadsheet software could interpret as a formula. For PDF templates, escape text before inserting it into HTML. Use declared templates and calculated data instead of accepting arbitrary model-generated HTML.

Allowlist source and download URLs. Resolve navigation through the application's registry, then recheck frontend access and router mappings when the user clicks. Validate streamed artifact schemas before rendering them.

A schema establishes shape. Escaping establishes how text is interpreted by a renderer. Authorization establishes whether the user may obtain the underlying data. Passing one check cannot substitute for the others.

Trace Data Flows, Not Just Tool Permissions#

A read-only business tool can still disclose its bounded output to a model provider. Tracing can create a second external data path. The reference enables tracing and content capture by default; disabling content capture redacts strings but preserves numeric values, keys, and structure.

Amounts and counts can be sensitive even when names disappear. Choose capture settings and retention based on the data actually retained, not on a label such as "redacted." Operational steps are retained for 90 days in the reference, separate from user-owned conversation history.

Versioned prompts and semantic descriptions help investigate behavior changes. They do not prove privacy, prevent prompt injection, or version every computation. A meaningful audit needs to inspect tool execution and the relevant source revision as well.

Add Business Actions Only Through a Confirmed Proposal (Proposed)#

The tools discussed in this series read business records and propose navigation. They do not already mutate business records or run scheduled user analysis.

For a future action, start with one small operation, such as updating a single explicitly selected field. Separate proposal from execution:

Interpret request
    -> resolve target under current permissions
    -> validate allowed fields and values
    -> store an immutable proposal
    -> show the exact change for explicit user confirmation
    -> recheck authorization and record state
    -> execute once and record the outcome

The confirmation must identify the target and the change. Bind execution to the stored proposal so the model cannot substitute another record or value afterward. Recheck permissions at execution time, because a user may lose access while reviewing the proposal.

Use an expected record version or equivalent precondition to detect stale state. If the record changed after the preview, return a fresh proposal instead of silently applying an outdated confirmation.

Scope an idempotency key to the tenant, user, and action. Store its outcome so a retry returns the original result rather than repeating the mutation. Define what happens when execution partially fails; an idempotency key alone does not reverse side effects.

Audit the proposal, user confirmation, execution outcome, and relevant before/after values under a defined retention policy. External messages, payments, and bulk edits deserve their own narrower contracts. A generic action tool would remove the boundaries this architecture relies on.

Build a Security Test Set Around the Boundaries#

Use synthetic users from different tenants and permission scopes. Try another user's conversation and result IDs, expired artifacts, mismatched entity-ID kinds, invented screen IDs, CSV formula text, HTML markup, and disallowed URLs.

Then evaluate model behavior with malicious help content and unsupported requests. Deterministic tests should prove that tool boundaries hold even if the model cooperates with the attack. Model evaluations measure whether the assistant also explains the failure clearly and stays within scope.

The implementation order across this series is deliberate: useful help and navigation, declared business queries, accountable usage, then stronger scale and security guarantees before narrowly authorized actions. Each phase should leave a smaller set of decisions for the model and a clearer contract for the backend.

About This Series#

These tutorials use a reviewed implementation snapshot (ae7c6e6fca71) as a concrete reference, with synthetic examples and generalized identifiers. They do not describe a verified live deployment. Code marked illustrative explains a pattern rather than providing a complete application. Proposed improvements are labeled explicitly.

  1. Building an AI Assistant: Product Help and Navigation
  2. AI Agents for Business Data: A Semantic Query Engine
  3. AI Assistant Credits: Token Accounting and Usage Limits
  4. AI Agent Security: Permissions, Prompt Injection, and Safe Actions