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:
Filesystem access
Process execution
Network access
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:
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.
| Approach | Isolation | Complexity | Typical Use |
|---|
| Process-level restrictions | Low | Low | Trusted local tools |
| Container | Medium | Medium | CI and agent execution |
| Hardened container | Higher | Medium | Untrusted workloads |
| VM | Strong | Higher | High-risk workloads |
| Dedicated sandbox service | Strong | Higher | Multi-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.