Last Saturday, 17 October 2026, I gave a talk at the DevNot Developer Summit in Istanbul called “From Prompt Injection to Permission Boundaries: Security for AI Agents.” This post is the written version, with a few extra notes I didn’t have time to say on stage.

Why agent security is different now

A chatbot that gives a bad answer is embarrassing. An agent that takes a bad action is an incident.

Agents don’t just generate text anymore. They call tools, hit APIs, write to databases, send emails and open pull requests. That changes the threat model completely. With tools attached, a prompt injection is no longer a text exploit; it can become an unauthorized state change in your systems.

The framing I want you to keep for the rest of this post is simple:

LLMs are untrusted planners. The model proposes actions; deterministic policy decides whether they run. Prompt defense alone is not enough.

Everything below follows from that one idea.

Prompt injection: the gateway attack

Prompt injection is the attack vector everyone talks about, and for good reason. It comes in four main flavours:

  • Direct jailbreaks: the user types input designed to override the system prompt.
  • Indirect injection: hidden instructions sit inside emails, web pages or documents the agent reads.
  • Retrieval (RAG) poisoning: an attacker plants content in your knowledge base so it gets retrieved later.
  • Multi-turn manipulation: gradual steering across a conversation that slowly erodes guardrails.

The key point: prompt injection is the vector, but the permissions and data access you give the agent decide the impact.

How indirect injection works

The dangerous part is that the attack doesn’t have to come through the chat box. It rides in on data the agent reads:

How prompt injection works: attacker, untrusted content, agent reads it, model follows it, tool call runs
  1. Attacker plants hidden text in content: a web page, an email, a PDF.
  2. Agent reads it. The untrusted text enters the model’s context.
  3. Model follows it. The hidden text is treated as a goal, sometimes as if it were the primary system directive.
  4. Tool call runs. For example, send_email with your data attached.

There are two places to break this chain: scan inputs for injection before they reach the model, and run a policy check before any action executes. You want both.

Tools worth evaluating for input scanning: Lakera Guard, Azure Prompt Shields, AWS Bedrock Guardrails, NVIDIA NeMo Guardrails and Guardrails AI.

Tool abuse: where the real damage happens

Agents gain their power through tools, and that is exactly why tool abuse is the bigger threat. Common failures look like this:

  • Over-broad APIs (one tool that can do far more than the task needs)
  • Chained actions that add up to something nobody intended
  • Unintended deletes or sends

A compromised LLM means compromised tools. Misleading tool outputs and sensitive context leaking into the model add risk on top.

The tool abuse lifecycle

Here is the scenario I walked through on stage:

How tool abuse happens: a hidden instruction leads to delete_records(scope="all") and the customers table drops from 4,812 rows to 0
  1. Goal. The agent reads untrusted input. A hidden instruction in a GitHub ticket or document gets interpreted as a legitimate task.
  2. Action. The model plans around that goal and generates a high-risk MCP tool call: delete_records(scope=”all”).
  3. Impact. The tool runs with the agent’s full permissions. Without boundaries, DELETE * FROM CUSTOMERS simply succeeds.
  4. Database. Rows go from 4,812 to 0. And then someone says the worst sentence in operations: “We don’t have any backups.”

The fixes map directly onto the steps: allowlist tools and validate their arguments, require human approval for high-risk actions, and make sure recovery doesn’t depend on luck. Limit what each tool can do, and check every call before it runs.

Policy engines and controls to look at: Cedar, OPA (Rego), OpenFGA, and MCP gateways with tool allowlists.

Permission boundaries: guardrails, not trust

If you take one thing from this post, take this: enforce security policy outside the model. Never rely on the prompt for safety.

  • Define what each agent may read, write and execute. Deny by default.
  • Give tools scoped permissions, not god-mode API keys.
  • Give agents role-based access.
  • Require human approval for high-risk actions.
  • Validate every tool call.

Why outside the prompt? Because policy that lives outside the prompt doesn’t move when the model or the instructions change. Swap the model, rewrite the system prompt, get injected: what the agent is allowed to do stays the same.

An access map for a coding agent

Here’s what default-deny looks like for an agent doing agentic development:

ResourceAccess
Feature branch✓ Read and write
Unit test runner✓ Run
Docs and issue tracker✓ Read
Production database✗ Denied
Customer data (PII)✗ Denied
Payment API✗ Denied

Grant only what the task needs. Everything else stays closed.

Data boundaries matter too

Permissions on tools are half the story. The other half is what data flows in and out.

Enforcement (before the model sees anything):

  • Apply user, tenant and document-level permissions before retrieval, not after.
  • Minimize how much sensitive data enters the model context. Less in context means a smaller blast radius if things go wrong.

Inspection (on the way out):

  • Treat model-generated SQL, HTML and shell commands as untrusted input, exactly like user input.
  • Inspect tool responses and outbound content, and redact where appropriate.
  • Check both the destination and the data being sent.

My favourite line from this part of the talk: permission to send email is not permission to send every document.

Agents need their own identities

Too many agents today run on a shared service account or, worse, a developer’s personal token. That makes every action untraceable.

  • Give each agent its own unique, non-shared identity.
  • Scope, rotate and audit tokens per agent and per task.
  • Every action should trace back to an accountable identity.

Dynamic keys in practice

Here’s the pattern: an identity store (a vault or identity provider) issues a short-lived key to the agent for one task.

How an agent uses an identity key: the identity store issues agent-42 a scoped 15-minute key for Ticket API and docs search, Billing API stays out of scope
  • Agent: Support Agent, agent-42
  • Key: scoped to the task, expires in 15 minutes (TTL: 15 MIN | SCOPE: TICKET_API_READ)
  • Allowed: Ticket API (read), docs search (read)
  • Not in scope: Billing API
  • Audit log: every use is recorded

No static credentials in agent code. Short-lived keys, scoped to the task. If the agent is compromised, the attacker gets 15 minutes of read access to tickets, not your billing system.

Tools to evaluate: Astrix, Oasis Security, Entro, Linx Security, Microsoft Entra Agent ID and Microsoft Agent 365.

Zero Trust for agents

Zero Trust isn’t new, but it fits agents perfectly. Never trust by network location or by prior approval alone.

  • Verify every tool call, data source and agent-to-agent request at the point of execution.
  • Minimize privilege for every task, and assume the agent’s context is already compromised.
  • Check at runtime: validate every request against identity, policy and context gates.

On the Zero Trust slide I added a line for the Istanbul audience: “Türk insanının güven sorunu değil, tecrübe fazlalığı var.” Roughly: Turkish people don’t have a trust problem, they have too much experience. That’s the right attitude for agents too.

Runtime gates: check every call

Here’s what that looks like when an agent tries to send an email to an external address:

Runtime gates: identity, context and policy pass, the arguments check fails because the destination is not on the allowlist, so the call is denied and logged to SIEM

Three gates pass, but the destination isn’t on the allowlist, so the call is denied and the failure is logged to your SIEM. Past approval does not carry forward: every request is verified again at runtime.

Agent gateways: one choke point

If every team bolts its own checks onto every agent, you end up with inconsistent, half-enforced policy. An agent gateway centralizes it: a single choke point for tool access, policy and logging.

A gateway handles authentication, rate limits, allowlists and human approval for risky actions. Its core jobs are policy enforcement, observability, audit trails, safe orchestration and boundaries. The payoff is that every agent is governed the same way.

A gateway in action

Support agent agent-42 asks the gateway to send_file to an external address. The gateway checks identity, evaluates policy, applies guardrails, writes an audit log and emits observability data. Then it routes the request one of three ways:

Agent gateway in action: external email blocked, internal file store allowed, high-risk action escalated to a human reviewer
DestinationDecisionWhy
Email API (external)BlockedDomain not on the allowlist
Internal file storeAllowedInternal recipient
Human reviewerApproval neededHigh-risk action escalated

Every call is checked, logged and tied to an agent identity before it runs.

Options to look at: MCP gateways (Gravitee, TrueFoundry, Bifrost), Amazon Bedrock AgentCore Gateway with Policy, LangGraph human-in-the-loop and OpenAI Agents SDK guardrails.

Contain, detect, recover

Assume a boundary will be challenged, and plan how you’ll contain the consequences before it happens.

  • Detect: alert on unusual destinations, odd access patterns and repeated denied calls. A string of denials is often the first sign of an injection in progress.
  • Limit: enforce caps on actions, spending, execution time and sub-agents.
  • Stop: keep an operator-controlled kill switch that revokes credentials and halts active, queued and delegated work.
  • Preserve evidence: keep tamper-resistant logs, with secrets and sensitive data redacted.
  • Own it: assign an incident owner and actually rehearse recovery. Remember the customer table with no backups.

Test the boundary, not just the answer

Most teams test whether the model says the right thing. That’s not enough. A refusal in the chat is not proof that no unauthorized action occurred.

  • Test direct prompts and malicious content hidden in documents and tool responses.
  • Assert that unauthorized tool calls and cross-user data access are actually blocked at the gateway.
  • Test the failure modes: expired credentials, gateway bypass attempts, and an unavailable policy service. Does it fail closed?
  • Repeat the tests after every model, prompt, tool or permission change.
  • Measure both sides: blocked attacks and legitimate task completion. A policy that blocks everything is secure and useless.

The tool landscape: commercial and open source

Here’s the map I shared at the end of the talk, grouped by the layer each tool protects:

LayerCommercialOpen source
Prompt protectionLakera Guard, Microsoft Prompt Shields, AWS Bedrock GuardrailsNVIDIA NeMo Guardrails, Guardrails AI
Data protectionMicrosoft Purview, Google Cloud DLPMicrosoft Presidio
IdentityAstrix, Oasis Security, Entro, Linx Security, Microsoft Entra Agent IDSPIFFE / SPIRE, OpenFGA
Agent gateway and policyMicrosoft Agent 365, AWS Bedrock AgentCore Gateway, Gravitee, TrueFoundryBifrost, Open Policy Agent (OPA), Cedar

My advice is to mix both: open source for control you can inspect, commercial for support and scale.

Key takeaways

Prompt injection is only the beginning.

  • Tool abuse is the real threat.
  • Permission boundaries are mandatory.
  • Agents must be governed as identities.
  • Zero Trust applies to every tool call.
  • Agent gateways are the future of AI security.

I closed the talk with one slide: a giant “LGTM” Don’t LGTM your agents. Put the boundaries in place, then verify every action.

Thanks to everyone who came to the session at DevNot, and to the organizers for having me.

Connect