Cyber Security  

Sandboxing AI Agents Against Unauthorized Code Execution

AI coding agents can inspect repositories, generate source code, execute tests, run build commands, install dependencies, and interact with development environments.

That ability makes them useful, but it also creates a security boundary that traditional applications do not always have.

A conventional application generally follows a predefined execution path. An AI agent can decide which tool to call based on instructions, repository contents, tool results, and other information it encounters during execution.

If that agent can directly execute arbitrary operating-system commands, the consequences of an unintended action can extend far beyond the source repository.

A safer architecture is to place the agent inside a sandbox and expose only the resources and operations required for its task.

The objective is not simply to prevent malicious code from running. The objective is to ensure that even if an agent requests an unsafe operation, the surrounding environment limits what that operation can affect.

What Is an AI Agent Sandbox?

An AI agent sandbox is an isolated execution environment where an agent's tools and generated code operate under predefined restrictions.

A simplified architecture looks like this:

User
  |
  v
AI Agent
  |
  v
Tool Authorization
  |
  v
Sandbox
  |
  +---- Filesystem
  +---- Process Execution
  +---- Network
  +---- Temporary Storage
  |
  v
Result

The sandbox should restrict at least four major resources:

  1. Filesystem access

  2. Process execution

  3. Network access

  4. Credentials and secrets

A sandbox does not replace authorization.

Instead, it provides another security boundary around the execution environment.

Why Running Generated Code Is Different

Consider a coding agent asked to fix a failing test.

The agent might generate:

public int Add(int a, int b)
{
    return a + b;
}

That code is harmless in isolation.

However, the same agent may also have the ability to execute:

dotnet test

or:

bash script.sh

The risk is not limited to the generated C# code.

A shell command can potentially access files, launch processes, communicate over the network, modify the environment, or invoke another executable.

This means the real security question is:

What can the generated workload do when it executes?

A strong sandbox answers that question independently of the model's instructions.

Prompt Restrictions Are Not a Sandbox

A system prompt might tell an agent:

Never access files outside the repository.
Never execute destructive commands.
Never access credentials.

These instructions can influence model behavior, but they should not be treated as a security boundary.

The model is still making decisions based on inputs it receives.

Repository content can contain instructions. Issues, documentation, source files, generated files, package metadata, or tool responses can also contain content that attempts to influence the agent.

A sandbox works differently.

If the agent attempts:

read /home/developer/.ssh/id_rsa

the environment should deny the operation regardless of what the model believes it is allowed to do.

The architecture should therefore be:

Model instruction
      |
      v
Agent decision
      |
      v
Tool request
      |
      v
Security boundary
      |
      +---- Allow
      |
      +---- Deny

The model decides what it wants to do.

The sandbox decides what it is actually allowed to do.

Start With a Dedicated Workspace

The simplest useful isolation boundary is a dedicated workspace.

Instead of giving an agent access to a developer's home directory:

/home/developer/

create an isolated workspace:

/workspace/
    src/
    tests/
    artifacts/

The agent receives access only to that directory.

For example:

Agent
 |
 +-- /workspace/project/src
 +-- /workspace/project/tests
 +-- /workspace/project/artifacts

Sensitive host directories should not be mounted into the environment.

Avoid exposing:

~/.ssh
~/.aws
~/.azure
~/.config
environment secrets
browser profiles
credential stores

unless there is an explicit and carefully controlled requirement.

Container Isolation for Coding Workloads

Containers are a practical way to establish a repeatable execution environment.

A basic Dockerfile might look like this:

FROM mcr.microsoft.com/dotnet/sdk:11.0

WORKDIR /workspace

COPY . .

RUN dotnet restore

CMD ["dotnet", "test", "--no-restore"]

This provides a separate filesystem namespace and a predictable .NET environment.

However, a container should not automatically be treated as a complete security sandbox.

Container security depends on how the container is configured.

For example, mounting the host filesystem into a container can largely defeat filesystem isolation:

docker run -v /:/host ...

A sandbox should expose only the resources the workload actually requires.

Run the Container as a Non-Root User

One important hardening measure is avoiding unnecessary root privileges.

A Dockerfile can create a dedicated user:

FROM mcr.microsoft.com/dotnet/sdk:11.0

RUN useradd --create-home agent

WORKDIR /workspace

COPY . /workspace

RUN chown -R agent:agent /workspace

USER agent

CMD ["dotnet", "test"]

The exact user-management commands can vary by base image.

The principle is more important:

The agent should not receive administrative privileges merely because it is running inside a container.

If a workload can operate as an unprivileged user, use that model.

Restrict the Filesystem

The agent may need to modify source code but should not receive unrestricted write access.

A useful layout is:

Read-only:
  /tools
  /runtime

Read/write:
  /workspace

Temporary:
  /tmp

Container execution can use read-only filesystem options while providing specific writable locations.

For example:

docker run \
  --read-only \
  --tmpfs /tmp \
  my-agent-sandbox

A read-only root filesystem reduces the number of locations an unexpected process can modify.

The application can still receive writable directories explicitly where required.

Do Not Mount Secrets Into the Sandbox

One of the most important rules is:

Do not place credentials in the sandbox unless the task genuinely requires them.

Avoid patterns such as:

docker run \
  -v ~/.aws:/root/.aws \
  my-agent

or:

docker run \
  -e PRODUCTION_DATABASE_PASSWORD=...
  my-agent

A coding agent fixing a unit test generally has no reason to access production credentials.

Instead, separate development capabilities from privileged infrastructure operations.

For example:

Coding Sandbox
   |
   +-- Source repository
   +-- Test database
   +-- Package registry
   |
   X-- Production database
   X-- Production credentials
   X-- Cloud administrator credentials

Control Network Access

Filesystem isolation is only one part of the problem.

An agent may also execute code that attempts to make outbound network requests.

A useful sandbox therefore defines network policy explicitly.

For example:

Allowed:
  package registry
  internal development API

Denied:
  production systems
  arbitrary public endpoints
  internal administration interfaces

A particularly restrictive test environment may disable network access completely:

docker run \
  --network none \
  my-agent-sandbox

This is useful when tests do not require network connectivity.

If dependency restoration is required, network access can instead be provided during a controlled preparation phase:

Dependency Restore
       |
       v
Build Sandbox
       |
       v
Test Execution

This is often safer than giving arbitrary network access throughout the entire agent session.

Separate Build From Execution

A useful security pattern is to separate operations into stages.

Agent
  |
  v
Generate Code
  |
  v
Build Sandbox
  |
  v
Test Sandbox
  |
  v
Review

The agent does not necessarily need unrestricted access to every environment.

For example:

Stage 1
Read + Write Source

Stage 2
Compile + Test

Stage 3
Human Review

Stage 4
Deployment

Deployment should generally occur outside the coding sandbox and under a separate authorization process.

This reduces the chance that an agent that can modify code can also immediately deploy that code.

Restrict Process Execution

Arbitrary shell execution is one of the broadest capabilities a coding agent can receive.

Compare:

ExecuteShellCommand

with:

RunDotnetTest
BuildProject
FormatCode

The second model is easier to secure because each tool has a clearly defined purpose.

A tool broker might define:

public enum AgentOperation
{
    ReadFile,
    WriteFile,
    RunTests,
    BuildProject
}

Then authorize only the operations required by the workflow:

public sealed class AgentPolicy
{
    private readonly HashSet<AgentOperation> _allowed;

    public AgentPolicy(IEnumerable<AgentOperation> allowed)
    {
        _allowed = allowed.ToHashSet();
    }

    public void Demand(AgentOperation operation)
    {
        if (!_allowed.Contains(operation))
        {
            throw new UnauthorizedAccessException(
                $"Operation '{operation}' is not allowed.");
        }
    }
}

A test tool can enforce its capability before execution:

public async Task RunTestsAsync(
    AgentPolicy policy)
{
    policy.Demand(AgentOperation.RunTests);

    // Invoke the test runner here.
}

This does not replace OS-level sandboxing, but it provides an application-level authorization layer before process execution.

Command Allowlisting

If arbitrary process execution is unavoidable, consider an allowlist.

For example:

Allowed:
  dotnet test
  dotnet build
  dotnet format

Denied:
  powershell
  cmd
  bash
  ssh
  curl

However, command allowlisting is difficult to implement correctly if it relies only on string matching.

A command such as:

dotnet test && malicious-command

illustrates why a naive prefix check is insufficient.

Where possible, expose structured tools instead of accepting arbitrary command strings.

For example:

RunTests(projectPath, testFilter)

is safer to reason about than:

Execute(command)

The tool implementation can validate the project path, arguments, working directory, and process environment before launching the process.

Protect Against Path Traversal

Agent tools that manipulate files should validate paths.

A request such as:

../../../../etc/passwd

should never escape the intended workspace.

A basic C# helper can normalize and validate the requested path:

public static string ValidateWorkspacePath(
    string workspace,
    string requestedPath)
{
    var root = Path.GetFullPath(workspace);
    var target = Path.GetFullPath(
        Path.Combine(root, requestedPath));

    var relative = Path.GetRelativePath(root, target);

    if (relative.StartsWith("..") ||
        Path.IsPathRooted(relative))
    {
        throw new UnauthorizedAccessException(
            "Path is outside the workspace.");
    }

    return target;
}

This is useful as an application-level control.

For stronger isolation, combine it with filesystem and container boundaries.

Also account for symbolic links, junctions, race conditions, and platform-specific filesystem behavior in a production implementation.

Protect Against Resource Exhaustion

Unauthorized code execution is not the only problem.

An agent could execute a legitimate operation that consumes excessive resources.

Examples include:

Infinite loop
Huge memory allocation
Recursive process creation
Large file generation
Long-running compilation
CPU-intensive computation

A sandbox should therefore define resource limits.

Potential controls include:

  • CPU limits

  • Memory limits

  • Process limits

  • Disk quotas

  • Execution timeouts

  • Maximum output size

  • Maximum concurrent operations

For example, a container runtime can enforce memory and CPU limits:

docker run \
  --memory=2g \
  --cpus=2 \
  my-agent-sandbox

The appropriate values depend on the workload.

Do not treat these example values as universal production recommendations.

Timeouts Are Security Controls

Every agent-controlled operation should have a bounded execution time where practical.

In C#:

using var timeout =
    new CancellationTokenSource(
        TimeSpan.FromMinutes(2));

await RunTestsAsync(timeout.Token);

The underlying process should also be terminated when the cancellation policy is triggered.

A timeout prevents a failed or unexpected operation from consuming resources indefinitely.

Keep the Sandbox Disposable

One of the strongest patterns for untrusted execution is ephemeral infrastructure.

Instead of:

One permanent agent container
        |
        +-- hundreds of tasks

prefer:

Task
 |
 v
Create Sandbox
 |
 v
Execute
 |
 v
Collect Results
 |
 v
Destroy Sandbox

This limits persistence.

Temporary files, modified packages, generated binaries, and unexpected changes disappear when the environment is destroyed.

For workloads involving untrusted repositories, ephemeral execution can significantly simplify cleanup and recovery.

Snapshot and Reset the Workspace

Another approach is to start each agent task from a known clean state.

For example:

Base Image
    |
    v
Fresh Workspace
    |
    v
Agent Task
    |
    v
Results
    |
    v
Destroy

Do not reuse a contaminated environment simply because doing so is faster.

If caching is necessary, isolate caches from secrets and sensitive data.

Sandbox Architecture Options

Different environments provide different isolation properties.

ApproachIsolationComplexityTypical Use
Process-level restrictionsLowLowTrusted local tools
ContainerMediumMediumCI and agent execution
Hardened containerHigherMediumUntrusted workloads
VMStrongHigherHigh-risk workloads
Dedicated sandbox serviceStrongHigherMulti-tenant execution

The correct choice depends on the threat model.

A developer's local coding assistant and a multi-tenant service executing arbitrary repository code should not necessarily use the same isolation architecture.

Container vs Virtual Machine

Containers share the host kernel, while virtual machines provide a separate guest operating system environment.

That distinction matters when the workload is highly untrusted.

For example:

Lower-risk internal workload
        |
        v
Hardened container

may be appropriate in one environment.

For stronger isolation requirements:

Untrusted workload
        |
        v
Dedicated VM / microVM

may provide a more appropriate boundary.

The security architecture should be selected based on the consequences of a sandbox escape, not simply deployment convenience.

Common Sandboxing Mistakes

Assuming Containers Are Automatically Secure

A container with excessive privileges, host mounts, or exposed sockets can create serious security problems.

Giving the Agent Docker Access

Mounting the Docker socket into an agent container can effectively grant the agent powerful control over the host's container runtime.

Avoid giving an agent access to privileged infrastructure unless that capability is explicitly required and separately protected.

Mounting the Entire Repository With Excessive Permissions

If the agent only needs to modify src/ and tests/, avoid giving it unnecessary access to deployment configuration or secret files.

Sharing Production Credentials

Development agents should not automatically inherit production environment variables.

Allowing Unlimited Network Access

Network access should be treated as a capability.

Reusing Dirty Sandboxes

A previous task can leave behind unexpected files, packages, processes, or configuration.

Ephemeral environments reduce this risk.

A Practical Secure Architecture

A production-oriented coding-agent environment can combine the controls discussed above:

                         User
                           |
                           v
                    AI Coding Agent
                           |
                           v
                   Capability Broker
                           |
             +-------------+-------------+
             |                           |
             v                           v
       Tool Authorization           Audit Logging
             |
             v
       Ephemeral Sandbox
             |
      +------+------+------+
      |      |      |      |
      v      v      v      v
    Files  Build   Tests  Network
      |
      v
 Scoped Workspace

The deployment pipeline remains outside the coding sandbox:

Agent Sandbox
     |
     v
Generated Changes
     |
     v
Code Review / CI
     |
     v
Approval
     |
     v
Deployment Environment

This separation is important.

The environment that allows an agent to experiment with code should not automatically be the environment that controls production infrastructure.

Testing the Sandbox

Security controls should be tested rather than assumed.

Create negative tests for operations that must fail.

For example:

Read workspace file          -> Allowed
Write source file            -> Allowed
Run unit tests               -> Allowed

Read SSH key                 -> Denied
Write outside workspace     -> Denied
Access production endpoint -> Denied
Start unrestricted shell    -> Denied
Exceed execution timeout   -> Terminated

You can automate these tests as part of the sandbox build process.

A sandbox should be considered incomplete if only successful workflows are tested.

Troubleshooting Failed Agent Tasks

The Agent Cannot Install a Package

Determine whether package installation genuinely requires network access.

If yes, provide access only to the required package registry or use a controlled dependency cache.

Tests Need a Database

Do not connect the agent to production.

Use an isolated test database or disposable database instance.

Agent
  |
  v
Test Database
  |
  X
Production Database

The Build Requires Credentials

First determine whether credentials can be removed from the build.

If they are genuinely required, use the narrowest possible credential and make it short-lived.

The Agent Needs Shell Access

Ask whether the operation can be represented as a specific tool.

Replace:

ExecuteShell(command)

with:

BuildProject(project)
RunTests(project, filter)

when practical.

Security Checklist

Before allowing an AI coding agent to execute generated code, verify:

  • The agent uses a dedicated identity.

  • The workspace is isolated.

  • The filesystem is restricted.

  • The process runs without unnecessary administrative privileges.

  • Secrets are not mounted by default.

  • Network access is explicitly controlled.

  • Process execution is restricted.

  • CPU and memory limits are defined.

  • Execution timeouts exist.

  • Tool authorization is enforced outside the model.

  • Sandbox instances can be destroyed and recreated.

  • Production environments are separated.

  • Audit logs capture important security events.

  • Negative security tests are automated.

  • Container and host security configurations are regularly reviewed.

Frequently Asked Questions

Is Docker enough to sandbox an AI coding agent?

Not automatically.

Docker provides useful isolation primitives, but the security boundary depends on configuration, privileges, mounts, networking, kernel exposure, credentials, and the threat model.

Should an AI coding agent have shell access?

Only when necessary.

A narrowly scoped tool such as RunTests is easier to secure than unrestricted shell execution.

Should the sandbox have internet access?

Only when the workload requires it.

If network access is needed, restrict it to the destinations and protocols required by the task.

Should coding agents have production credentials?

Generally, no.

Development and production privileges should be separated. If an operation genuinely requires privileged access, use narrowly scoped and preferably short-lived credentials with additional approval controls.

Can a sandbox completely prevent unauthorized execution?

No security boundary should be described as absolutely invulnerable.

The goal is defense in depth: limit what the agent can access, reduce the impact of a compromise, detect suspicious behavior, and make the environment disposable.

Conclusion

AI coding agents need a different execution-security model because they combine software generation with autonomous tool usage.

The most important design decision is to avoid giving the agent unrestricted access to the machine where it runs.

Instead, combine:

Least Privilege
      +
Capability Authorization
      +
Filesystem Isolation
      +
Network Restrictions
      +
Process Controls
      +
Resource Limits
      +
Ephemeral Execution
      +
Audit Logging

A container, VM, or sandbox runtime provides the execution boundary. A capability policy determines what the agent is allowed to request. Network and filesystem controls limit what the resulting process can reach. Resource limits reduce denial-of-service risks. Audit logs provide visibility into what happened.

Most importantly, keep the development sandbox separate from production infrastructure.

The guiding principle is simple:

Assume generated code can behave unexpectedly, and design the environment so that unexpected behavior has a limited blast radius.

That approach allows teams to use increasingly capable AI coding agents while preserving the security properties expected from modern development and deployment environments.