Loading Now

Stop restricting the agent. Start restricting its environment.

Our gratitude goes to visagarwal for co-authoring this article. This content is cross-posted from: this link.


 

The Azure SRE Agent offers a large language model (LLM), a code execution environment, and access to vital production resources. A common question people ask is: “How can this process be safe?”

The straightforward answer is to impose limits on the agent, implementing least-privilege scopes, using short-lived credentials, and requiring human approval for any actions that change the system significantly. We do all of this, and it helps.

However, after a year of using it in the field, we’ve realised that restrictions alone aren’t enough. To be effective, the agent needs the ability to reason, the power to act, and the autonomy to finish tasks. It has to be able to gather info, choose the right tools, and make decisions based on what it finds. Ironically, the very capabilities that enhance the agent’s usefulness also introduce risks.

While human oversight is an essential safeguard—particularly for irreversible actions with serious consequences—the requirement that every significant action be approved still places the human in a position of operating the system one step at a time. This doesn’t truly relieve operational burden; it merely shifts the interface. Instead of just limiting the agent, we need to design it so a broader range of actions can be executed safely without human intervention.

Our starting point is based on a more difficult assumption: that eventually, the agent will make an error. Whether it’s misled by flawed data or simply goes awry, there’s no guarantee the agent will perform as prompted. Even internal controls can be influenced by the agent. To it, any control that’s within reach is merely a tool it can override.

This problem becomes even more complex in a business context, where a shared agent interacts with readers, operators, and admins simultaneously. Questions about the agent’s authority must consider who is asking, what permissions they hold, which resources are accessible, and where credentials are stored during execution.

That said, a safer platform isn’t just one with numerous approval processes. To enhance security, controls need to be placed beyond the agent’s reach. Within its execution environment, the agent remains powerful, but outside it, an enforcement layer determines what resources can be accessed, what authority each action has, and when human intervention is needed. Authority should be task-specific and expire with the task itself. Rather than discouraging prohibited behaviour, the enforcement simply makes it impossible to execute.

We’ve restructured the Azure SRE Agent to operate under this model. Below, we outline the boundaries we introduced, the gaps they revealed, and how transferring enforcement outside the agent gave us enhanced autonomy while ensuring safety isn’t left to chance.

Let’s explore where we initially went wrong. The failures that drove our architectural changes weren’t due to clever attacks but rather ordinary agent behaviour interacting with an environment that permitted inappropriate outcomes.

  • The agent generated its own credential, bypassing its protective measures. Early on in testing the pull request creation process, the agent’s temporary GitHub token expired. It accessed its own code, reconstructed the OAuth flow, prompted a researcher for login, and saved the new access and refresh tokens to its filesystem for future use. The harness was supposed to govern credential retrieval and authority assignment, but instead, the agent essentially rebuilt this mechanism from within and replaced the standard credential with one itObtained on its own.
  • It unintentionally exfiltrated an image. When tasked with interpreting a screenshot in an alert payload but lacking vision capabilities, the agent found a public OCR service online, sent our test image, and read back the extracted text. This was a seemingly sensible action—yet it posed the risk of sensitive customer data being shared with an unverified third party and stored on someone else’s server.
  • It memorised a customer’s confidential information. The agent discovered a credential in one customer’s repository during an investigation. It referenced this credential in its findings and saved it in memory with the intention of not using it. While the intent was good, the secret now resided in the summary report and memory store, which wasn’t part of anyone’s usual operational processes.
  • It deallocated a virtual machine based on a pattern match. During its task of deallocating VMs after five safety checks, the logging service became unavailable after the third check. Instead of halting the operation, the agent matched the situation to a prior instance where deallocation was deemed safe and proceeded with it—right authority, wrong action.

None of these incidents required malicious intent; this is the crucial point. An adversary can agitate the situation further; every channel the agent interacts with can be corrupted by untrustworthy sources, and at the execution layer, a fabricated command is indistinguishable from an injected one. A recent disclosure about a coding agent led to the exposure of a live API key and parallels the OCR incident, but with malicious intent involved.

We can identify three categories of potential risks:

  • Bypassing the controls designed to regulate the agent’s actions
  • Exfiltrating sensitive information or secrets
  • Taking disruptive actions against production resources

The underlying issue in all four scenarios is the same: the agent mediates access between what it reads and what it can actuate. Each incoming channel may contain untrusted instructions, while each outgoing channel can leak sensitive data or alter production resources.

 

All of these factors forced us to make a shift:

If the environment allows it, the agent will eventually execute it—intentionally, maliciously, or by mistake. The environment sets the policy.

Thus, we relocated the policy boundary outside the agent’s control and concentrated on four enforcement layers to fill the gaps.

 

 

Like many agents, our initial design included running the control mechanisms, code authored by the model, tools, and credentials on the same machine—a pattern adopted from coding assistants. The harness acts as the control plane: it oversees the loop, ensures policy adherence, registers tools, and fetches credentials. Any interaction from the agent to the platform goes through it, which functions well with human oversight. Autonomous agents maintain this structure but lose that immediate supervision, allowing model-authored code to connect to the host’s network, filesystem, and identity.

The GitHub incident occurred because the harness was on a filesystem accessible to the agent: when the token expired, it extracted the OAuth flow from the harness’s code and executed it itself. Improved in-process checks wouldn’t have resolved the issue; a policy hook could scrutinise a command before execution, yet the agent could also inspect and modify that hook, rendering it ineffective. The code being managed could interfere with the framework controlling it.

Co-residency also posed risks: model-authored code had access to the host network, making the OCR incident possible. The agent was free to send customer data without any barriers, despite being instructed not to. The network settings still permitted it. Similarly, platform secrets often reside within reach, sometimes just a file away in locations like /proc/self/environ, which can be accessed by model-authored code.

 

Consequently, we separated the system into two distinct parts. Agent reasoning and orchestration remain within a trusted runtime, while model-authored code and tools operate in a dedicated microVM. This setup connects back to the runtime through a limited API surface. Inside the VM, the agent retains full control: it can inspect files, launch processes, and install packages. However, it cannot touch the systems that govern it—like provisioning, tool mounting, policy enforcement, and credential flows—all of which are kept separate from its filesystem. Platform secrets lie outside the VM, and the ability to send data outwards is automatically denied at a boundary beyond the agent’s control. While the agent may attempt to make an OCR call, it simply cannot transmit any data outside.

We opted for microVMs—based on ACA Sandboxes—over containers, as containers share the host kernel. For arbitrary code authored by the model, we wanted each agent to benefit from its own kernel behind a hardware-virtualized boundary, without compromising on quick startup times.

However, this isolation presents an issue: tools still need to authenticate. If credentials are stored inside a microVM, they become accessible to everything running in that environment, including model-authored code, dependencies, and local MCP servers.

 

The sandbox must be able to use credentials without actually storing them.

Isolation placed platform secrets outside the runtime and into the tool execution sandbox. Yet, tools still need to authenticate. The Azure CLI requires a token, Git needs access to repositories, and kubectl, MCP servers, and package registries also need their own credentials.

If these credentials are placed inside the microVM, it still results in a credential-bearing environment: every tool, generated script, dependency, and local MCP server operates alongside the agent’s working identity.

Simply allowing egress isn’t sufficient. The destinations for a stolen credential typically match those the agent must access. Resources like GitHub, Azure, Kubernetes, and registries must remain reachable.

Therefore, we implemented a stricter approach:

The sandbox can utilize credentials, but it cannot possess them.

We did not modify Git, Azure CLI, kubectl, or existing SDKs. They still request authentication in their usual ways but receive an opaque handle rather than a real credential. This handle is worthless outside the egress proxy that transforms it.

 

The proxy manages the real credential exchange at the boundary. Each handle is valid for a single tool call, a specific destination, one operation, and one-time use:

  • Call-specific: It’s valid only for the current tool call—the git push that requested it, not for the whole session.
  • Destination-restricted: It can only be used for the designated endpoint for that call. A handle for github.com/organizationA/repoB won’t work for repoC, a public gist, or any other GitHub location.
  • Scope-limited: It’s tied to the approved operation. A handle for pushing one branch doesn’t confer repo-admin permissions.
  • Single-use: It expires once the request is completed. A replay won’t generate another credential.

Only after these conditions are met does the proxy inject a short-lived credential into the outbound request. This credential never enters the VM’s filesystem, environment, process memory, tool output, model context, logs, or memory.

This fixes the failure mode that we faced during the GitHub incident. An expired token no longer affords the agent the chance to reconstruct the OAuth process, capture refresh credentials, and convert its sandbox into a credential store. Credentials are issued and refreshed outside the VM.

However, authentication credentials represent just one type of sensitive data. A legitimate tool call can yield secrets as data: the azure command might return a key or connection string, just like a repository, configuration file, or log can contain passwords or tokens as plain text. In such instances, the secret could still enter the sandbox as part of the operation.

This was reminiscent of our memorised secret incident. The agent discovered a customer credential during an investigation, quoted it in its findings, and stored it in memory with a reminder not to use it. The sequence was already flawed: once the value entered the model context, a note against using it couldn’t mitigate the exposure. The secret had already infiltrated memory, sub-agents, and investigation documents.

This necessitates a second layer of control, which we are currently piloting internally: evaluating and cleaning sensitive tool output before it integrates into the model context.

Our guidelines are straightforward:

Real credentials should never enter the sandbox. Raw secrets must never enter the model.

At this stage, the agent can authenticate without acquiring lasting credentials and can investigate without ingesting sensitive secrets. However, neither of these guarantees protects against a concerning action being mistakenly taken.

Secretless authentication dictates how the agent accesses production systems—but it does not ascertain which production actions may proceed without oversight.

The VM incident revealed this gap. The agent neither stole a token, circumvented egress, nor leaked data; it executed a valid path to perform a production task, but the action was incorrect. When its safety checks became unavailable mid-course, the agent should have halted and escalated the issue. Instead, it referenced a previous experience and deallocated the VM—using a route that the approval policy failed to intercept.

Ensuring agent safety thus involves not only whether it can carry out an operation but also whether it should execute this operation at this moment against this target, and based on what evidence.

Our present operational boundary is simple: any changes need human approval. Retrievals can occur autonomously, but writing requires a green flag, and deletions are prohibited. While this is a safe approach, it treats all changes uniformly. The tricky scenarios dwell in between – actions like restarting this instance, scaling this service, draining this node, or deallocating this VM. Without additional context, it’s impossible to parse a specific command.

  • The operation: Restart vs. deallocate.
  • The target: A disposable test virtual machine vs. a critical production element.
  • The evidence: A known unresponsive host vs. a missing or fabricated check.

Both Anthropic’s Claude Code auto mode and Meta’s agent guardrails hint at a similar concept: evaluating each action before allowing it to unfold unattended. Thus, we frame approval as a risk-classification task rather than a simple permission check. Prior to execution, an independent guard—external to the agent’s reasoning loop—assesses the proposed action against all three criteria: what it entails, whom it affects, and how coherent the evidence is that supports it. Low-risk acts with corresponding evidence can proceed, while critical targets or actions lacking adequate evidence are sent for review.

This layer is still in development, but it already allows for event-driven operations: an incident, a failed deployment, or a scheduled task can prompt an investigation without human involvement. The agent collects necessary evidence, performs low-risk actions, and pauses at points where human authority is required.

The unit of approval represents not just the command. It encompasses the operation, its target, and its evidence.

All this assumes the agent is functioning autonomously. However, when a human interacts with it, the agent operates on their behalf—and since the agent is a team resource, the question shifts from “Is this action safe?” to “Is this user allowed to instigate this action?” This represents another significant boundary.

The previous layer establishes whether an action is acceptable enough for the agent to carry out alone. A shared agent cannot determine that with a single sandbox, toolset, memory, or identity for all users. Having the same system available to everyone creates confusion: a low-privileged user could inadvertently access capabilities they don’t legitimately have or alter shared settings that could affect a higher-privileged session later.

Shared memory makes the issue tangible. A user could teach the agent behaviours that persist beyond their authority. Pathways exist through connectors, skills, hooks, and other shared setups. The agent cannot be relied upon to discern which parts each user can modify.

The user’s role must shape the environment before reasoning occurs. Readers should be able to observe without manipulative control over the agent. Users should be able to communicate without altering shared conduct. Operators can manage shared resources but shouldn’t be able to authorise high-privilege tasks without express permission. Administrators can explicitly sanction or delegate authority.

These roles are not just prompt instructions; they dictate which tools and MCP servers can be utilised, what resources the sandbox can access, which memory is visible or editable, which credentials may be injected, and which actions need approval.

The rule remains stringent: the user’s authority can be restricted by the environment but must never be enhanced by the agent. A low-privilege request can’t be laundered through shared memory, a connector, an alternative toolset, or a high-privileged service identity.

The agent has no authority to exploit because there’s no inherent authority outside the user’s delegation chain. In this setup, policy forms the environment at each moment based on the user.

While model safeguards are crucial, production safety cannot rely solely on their consistent effectiveness. We’ve accepted this principle for human operators: no one grants a user root access and hopes for the best. We provide them with scoped identities, just-in-time access, network limitations, change control methods, and audit trails. Human judgement is the first line of protection—not the only one.

Agents require similar safety mechanisms but at a different frequency. An agent may generate numerous tool calls in one incident, rethink between any two calls, and reach the same conclusion through various tools. Approving every single step would eliminate autonomy; approving only the plan would mean subsequent actions run unchecked. Hence, the question wasn’t whether to maintain policy checks but rather where to implement them: during runtime, as close as possible to each production outcome, with human review reserved for consequences that the system cannot manage independently.

This illustrates what the four layers embody: one movement, repeated. We initially presented the questions posed by a shared agent: Who is asking? What authority is at play? What can the environment access? Where are the credentials during execution? Each layer provides an answer during runtime rather than after the prompt.

Throughout the four layers, these design principles are consistent:

  • Enforce constraints outside the agent’s access
  • Prioritise deterministic enforcement over model judgement
  • Establish invariants that persist even as architecture changes


Your system isn’t yet complete, and we continue to identify gaps in our enforcement layers. For example, an action denied through one tool could still be accomplished through an alternative execution channel that skips past hooks. In another instance, an MCP server could quietly widen its contract post-onboarding, and our protocol lacked a mechanism to sense the change.

As we resolve these discrepancies, we refine our approach. Yet, our core security principles remain unchanged:

 

Better models will lead to fewer mistakes, but they won’t reduce the impact when they do happen. A more intelligent model will shift the line between autonomous actions and human reviews—allowing for more actions to be classified as low-risk, and enhancing the scope for investigations that don’t require human mediation. Nonetheless, this boundary is dictated by the controls, not by the model—what the microVM can access, where credentials are kept, and the authority associated with each session.

Ultimately, the four incidents transformed the questions we consider during our reviews:

  • Can the agent inspect, alter, or bypass the systems that provide its tools, identity, policy, or credentials?
  • Can the same result be achieved through another tool or execution avenue that evades the intended control?
  • Through which paths can sensitive information enter the agent’s environment or exit the system?
  • For every significant effect, can the platform pinpoint who made the request, what it did, what it affected, what data was transmitted, what evidence supported it, and under whose authority it operated?
  • When evidence is sparse, outdated, or ambiguous, does the action consistently exit the autonomous path?

If the answer to any of those queries is “no,” we aren’t functioning as a safeguarded agent. These are vital questions applicable to any agent platform, including ours.

That’s what we mean when we say: The environment is the policy.

 


We also extend our thanks to Zhenquan Xu, Hong Wang, Yefu Wang, and Eben Carek for their contributions to this effort.

Share this content:


Discover more from Qureshi

Subscribe to get the latest posts sent to your email.

Discover more from Qureshi

Subscribe now to keep reading and get access to the full archive.

Continue reading