Agent security | July 26, 2026

The agent can stay inside its sandbox and the system can still lose

Recent disclosures show a repeatable trust gap: an agent writes an allowed file inside a repository, then a more privileged host component loads, executes, scans, or follows that file. The right boundary is not just around the process. It is around every path from untrusted workspace state to host authority.

Untrusted repositories Canonical-path approvals Disposable execution
Layered AI coding-agent security boundary from repository to sandbox and host

A process boundary is not a system boundary

A coding agent does not need to exploit the sandbox kernel, escape a container, or call a forbidden command to compromise its operator. It can remain within its declared workspace and change a file that another trusted program consumes later. The first process obeys policy. The composed system does not.

Pillar Security's July 20 research reproduced sandbox or boundary bypasses involving Cursor, Codex, Gemini CLI, and Antigravity. Its central observation is more important than any individual bug: repository files are active inputs to an ecosystem of host-side consumers. Git, IDE extensions, language servers, package managers, shell startup logic, container tooling, file watchers, and local daemons may interpret those files with privileges the agent never had.

Wiz Research's July 8 GhostApproval disclosure exposed a second version of the same mistake. A repository can contain a symbolic link whose visible name looks local while its canonical target points outside the workspace. If an assistant follows the link, or an approval dialog shows the unresolved filename instead of the real destination, a user can approve an edit to project_settings.json that lands in ~/.ssh/authorized_keys or a shell startup file. The presence of a human click does not make the decision informed.

These mechanisms change the practical threat model for unfamiliar repositories. Source code, README instructions, issue text, agent configuration, hooks, symlinks, build scripts, lockfiles, and test fixtures are not merely content. They are a supply-chain input that can shape model behavior and host behavior. Asking an agent to "inspect this repo and make it work" combines untrusted natural-language instructions with executable state.

The useful question is not "Did the agent break out?" It is "Which trusted component will consume what the agent was allowed to write?"

The answer is not to abandon sandboxes. Filesystem, process, network, and credential isolation still remove large classes of risk. The answer is to stop treating one sandbox as a complete security claim. A defensible workflow needs three boundaries: a repository trust boundary before interpretation, an execution boundary around agent actions, and a host-consumer boundary that validates every transition back to privileged state.

Four recurring ways workspace state becomes host execution

The recent research clusters into four mechanisms. They differ in trigger, but share the same shape: an untrusted workspace gains influence over something with more authority.

MechanismAllowed workspace actionPrivileged consumerPotential result
Deferred configuration executionWrite or modify project configurationIDE, Git integration, language server, local daemonCommand execution after the agent step finishes
Symlink target confusionEdit a workspace-looking pathFilesystem call follows canonical targetRead or write outside the intended root
Lifecycle-hook executionInstall, test, build, or switch branchesPackage manager, Git, build systemRepository scripts run with ambient host access
Authority amplificationCall an allowed local toolDocker socket, cloud CLI, credential helper, browser sessionAgent controls a more privileged subsystem

1. Deferred execution defeats "the command was sandboxed"

Imagine the agent may write anywhere in the repository but cannot execute outside it. The agent changes a configuration file that a host-side extension watches. The extension reloads the file and starts a task, resolves a tool path, invokes a formatter, or loads a plugin. Execution happens in the extension's process, not the agent's. The sandbox audit can honestly show no forbidden syscall while the host still runs attacker-influenced code.

This is a confused-deputy problem. The host component has legitimate authority, but it consumes data from a principal that should not control that authority. The fix belongs at the handoff: define which files can influence the consumer, validate their origin and content, and do not auto-load changed executable configuration from an untrusted session.

2. Approval must name the resolved object

A pathname is a reference, not the object itself. Relative segments, symlinks, junctions, mount points, case folding, and time-of-check/time-of-use changes can make the displayed path differ from the accessed target. A safe write gate resolves the parent and target as far as possible, verifies containment under the allowed root, rejects unsafe link traversal, and binds the approval to the object that will actually be changed.

function authorizeWrite(workspaceRoot, requestedPath):
  root = realpath(workspaceRoot)
  parent = realpath(dirname(requestedPath))
  target = join(parent, basename(requestedPath))

  if not isWithin(root, parent):
    deny("parent resolves outside workspace")

  if lstat(target).isSymlink():
    deny("target is a symbolic link")

  showToReviewer({
    requested: requestedPath,
    resolvedParent: parent,
    operation: "write",
    bytesChanged: previewDiff(target)
  })

  writeWithoutFollowingFinalSymlink(target)

Production code must also handle races, platform-specific path behavior, and atomic creation. The pseudocode illustrates the policy: a prompt that says only "edit project_settings.json" is not a meaningful authorization if the system already knows the effective destination is elsewhere.

3. Build tools are execution engines

npm install, pip install, cargo build, make, IDE task discovery, test runners, and Git hooks can execute repository-controlled code. Treating them as ordinary "developer commands" hides the boundary. An agent reviewing an unknown pull request should not run them on the host merely because the command is familiar.

Separate inspection from execution. The inspection phase reads files without loading workspace settings or running project scripts. The execution phase occurs only in a disposable environment with a clean home directory, no host credential mounts, no SSH agent forwarding, no cloud configuration, no browser cookies, and no Docker socket. Network starts disabled and is added by destination when required.

4. Local sockets can nullify a container boundary

A container that mounts /var/run/docker.sock can often ask the host daemon to create a more privileged container, mount host paths, or access networks unavailable to the original container. Similar amplification appears with Kubernetes credentials, cloud CLIs, package-manager tokens, credential helpers, and local automation daemons. "The agent runs in Docker" is therefore not a sufficient fact. The mounts and reachable control planes define the real boundary.

A safer design uses disposable workspaces and one-way promotion

The safest routine for an unfamiliar repository is fork, inspect, execute, report, and discard. The agent works on an isolated copy. Nothing from the copy becomes trusted host configuration automatically. The only promoted artifacts are a reviewed patch, a test report, and an explicit side-effect report.

Untrusted remote repository
          |
          v
Read-only intake scanner
  - inventory links, hooks, tasks, manifests
  - do not load workspace settings
          |
          v
Disposable agent environment
  - ephemeral home
  - no host sockets or credentials
  - network denied by default
  - scoped writable checkout
          |
          v
Verification gate
  - tests + diff + filesystem side effects
  - network attempts + denied operations
          |
          v
Reviewed patch only ---------> trusted repository

The one-way promotion rule matters. Copying the entire working directory back to the host also copies modified configuration, generated hooks, hidden files, symlinks, and caches. Export a patch or a constrained set of reviewed artifacts. Apply it in a clean checkout, then run the trusted project's normal validation under the project's established controls.

Cloud agents naturally provide more separation from a developer laptop, but they still need tenant isolation, scoped credentials, egress policy, secret handling, and safe promotion. Local agents can reach a similar posture with microVMs or carefully configured containers, but convenience mounts frequently erode it. Security review should inspect the effective environment, not the product label.

Build a repository preflight before the first agent command

A preflight scanner is not a malware verdict. It is an inventory of executable and boundary-crossing surfaces that determines which controls the next phase requires. Run it before an IDE or agent loads repository instructions.

# Inspect without executing repository code.
find . -type l -print
find . -path '*/.git/hooks/*' -type f -print
find . -name '.env*' -o -name 'settings.json' -o -name 'tasks.json'
find . -name 'package.json' -o -name 'pyproject.toml' -o -name 'Makefile'
git config --local --list --show-origin
git ls-files -s | awk '$1 == "120000" { print "symlink:", $4 }'

# Review lifecycle and task definitions as text.
rg -n '"(preinstall|install|postinstall|scripts)"|shell|command|executable' \
  package.json .vscode .idea .github .codex 2>/dev/null

Do not turn this into a blind command block pasted into every repository. Adapt paths to the platform, keep the scan read-only, and interpret results. A repository legitimately may contain symlinks or build hooks. The presence changes the execution plan: validate targets, remove host mounts, or keep the phase inspection-only.

Use an environment profile that fails closed

agent_environment:
  filesystem:
    checkout: writable
    home: ephemeral-empty
    host_mounts: none
    docker_socket: absent
  credentials:
    ssh_agent: absent
    cloud_profiles: absent
    git_credentials: read-only-token-if-required
  network:
    default: deny
    allowed:
      - registry.npmjs.org:443
  process:
    no_new_privileges: true
    resource_limits: true
  output:
    export: patch-and-reports-only

Where dependency access is necessary, prefer a proxy or allowlist over general internet access. Give the job a short-lived, read-only credential when private source access is required. Never inherit the user's entire environment. Environment variables, credential stores, SSH sockets, browser profiles, and local config directories are data-exfiltration paths even if the filesystem root itself is protected.

Separate approval from placement

Approval answers whether an action may happen. Sandboxing answers where and with what authority it happens. Keep those policy dimensions independent. A command may require human approval and still run inside isolation. Another may be pre-approved but remain network-denied. "Allow" should not silently imply "run on the host."

ActionApprovalPlacementReason
Read source filesPre-approvedInspection sandboxLow impact, untrusted content
Install dependenciesPrompt or policy gateDisposable environmentLifecycle scripts may execute
Open browser loginExplicit approvalSeparate profileSession authority is sensitive
Use Docker daemonNormally deniedDedicated remote builder if neededHost socket amplifies privilege
Apply patch to trusted branchReviewer approvalClean trusted checkoutOne-way promotion boundary

Verify side effects, not only the Git diff

A clean diff can coexist with a modified SSH key, shell profile, credential cache, Docker workload, or outbound network request. Acceptance tests for an agent environment should probe the boundary directly.

Outside-root writeAttempt to write through a symlink to a fake sensitive file. The operation must fail before mutation.
Credential readPlace canary secrets outside the checkout. The agent process must not read or echo them.
Network egressAttempt an unapproved DNS lookup and connection. Both should fail and appear in the report.
Daemon controlProbe Docker, Kubernetes, SSH-agent, and local automation sockets. They should be absent.
Lifecycle hookUse a harmless install script that attempts a canary side effect. Isolation should contain and report it.
Deferred consumerModify task or tool configuration, then confirm no privileged host component automatically loads it.

Store the test as an eval, not a one-time demo. Agent tools, IDEs, container profiles, operating systems, and workspace settings change. Re-run boundary probes after version upgrades and whenever the team adds a mount, plugin, MCP server, credential, or network exception.

Our guide to testing agent skills with controlled APIs applies the same principle at a different layer: hold the environment stable and score observable behavior. For sandbox tests, the observable behavior is not whether the assistant said it was safe. It is whether forbidden side effects failed closed.

Common controls fail when they protect the wrong object

Control that sounds safeFailureBetter test
"Only the repo is writable"Links or mounts resolve outside the repoValidate canonical targets and mount topology
"The user approved the edit"Prompt hid the effective target or consequenceDisplay resolved path, consumer, and operation
"Commands run in Docker"Host sockets, home, or credentials are mountedInspect mounts and reachable control planes
"Network is restricted"DNS, redirects, metadata, or local services remain reachableDefault deny, revalidate redirects, block private ranges
"We review the diff"Side effects occurred outside version controlCollect filesystem, process, socket, and egress reports
"We trusted this path before"Repository contents or origin changedBind trust to content and current origin, then re-evaluate

Another failure is concentrating only on prompt injection. Malicious instructions matter, but an agent can also trigger dangerous behavior while fulfilling a legitimate request. A normal dependency install can execute a malicious lifecycle script. A normal edit can follow a deceptive link. The control should remain effective even when the model behaves exactly as asked.

Do not overcorrect with permanent read-only mode. Useful agents need to edit, test, and iterate. The design goal is bounded authority plus promotion, not zero capability. A disposable environment lets the agent work freely where failure is cheap while keeping the trusted host and production systems outside the blast radius.

If an agent handled a suspicious repository, investigate the host

Stopping the agent and deleting the checkout may not remove a deferred or external side effect. Preserve relevant logs and inspect the effective environment: shell startup files, SSH authorization, Git configuration and hooks, IDE tasks and extensions, package-manager configuration, credential helpers, scheduled tasks, local containers, cloud sessions, and network activity.

  1. Disconnect the affected agent environment from sensitive networks and stop related processes.
  2. Preserve the repository, agent transcript, command trace, approval events, version information, and filesystem metadata.
  3. Compare sensitive host files and configuration against known-good state; follow canonical link targets.
  4. Rotate credentials that were present or reachable, not only credentials visibly printed.
  5. Review outbound connections and privileged local-daemon activity during and after the session.
  6. Rebuild from a trusted baseline when integrity cannot be established.
  7. Turn the discovered path into a regression probe before restoring agent access.

This is an endpoint and supply-chain investigation, not merely a prompt-quality issue. Involve the security team when the repository was untrusted, privileged resources were reachable, or the agent wrote executable configuration.

Untrusted-repository operating checklist

Inspect before loadInventory symlinks, hooks, task files, agent instructions, manifests, and workspace configuration as text.
Use a disposable homeDo not mount the developer's home, SSH agent, browser profile, cloud config, or credential helpers.
Deny network firstAdd exact destinations only when the task requires them; log attempts and revalidate redirects.
Remove control socketsNo host Docker, Kubernetes, virtualization, or automation daemon socket in the agent environment.
Resolve every approved pathShow canonical destinations and reject links that leave the allowed root.
Export a patch, not a workspacePromote reviewed diffs and reports into a clean checkout; discard the agent environment.
Test the boundary continuouslyRun canary probes after upgrades, policy changes, new plugins, or new mounts.

FAQ

Is a coding-agent sandbox enough for an untrusted repository?

No. It is an important layer, but repository-controlled files can influence components outside the sandbox. Combine process confinement with repository preflight, canonical-path enforcement, disposable execution, limited network and credentials, and one-way artifact promotion.

Should teams ban symlinks in source repositories?

Not necessarily. Symlinks have legitimate uses. Treat them as boundary-bearing objects: inventory them, validate their resolved targets, prevent unsafe traversal during agent writes, and avoid copying them blindly across trust boundaries.

Does human approval prevent these attacks?

Only if the approval is informed and precedes the effect. The reviewer must see the effective target and consequence. An undo prompt after the write, or a prompt that displays an unresolved filename, is not a reliable authorization gate.

Are cloud coding agents automatically safer than local agents?

They reduce direct exposure to a developer laptop, but security still depends on tenant isolation, credentials, network access, secret handling, host services, and artifact promotion. Evaluate the effective boundary rather than assuming the deployment model settles it.

What is the fastest meaningful improvement?

Run unfamiliar repositories in a disposable environment with no host home, no daemon sockets, no inherited credentials, and network denied by default. Export only a reviewed patch and side-effect report.

Sources and further reading

Current vulnerability and product information was verified online on July 26, 2026. Vendor status can change; check the linked advisories and release channels before making an exposure decision.