Environment audit · Google AI Studio
Google AI Studio's Antigravity environment is routinely compared to agent code-execution sandboxes and scored as if it were one. It isn't. It is a Cloud Run container that hosts the app you are building, and almost every apparent weakness follows from that — not from a failure to harden.
Read the process tree before reading the findings. It settles the threat model.
PID 1 /bin/bash ./start.sh PID 5 nginx: master process → :8080 ingress + auth bridge PID 6 /app/control-plane-api/… → :8000 Go orchestration API PID 47 npm run dev --host 0.0.0.0 → :3000 Vite dev server (the user's app) PID 19 tail -f /dev/null keeps PID 1 alive
That is a web application host. start.sh templates an nginx config, starts nginx, starts a Go control plane, waits for /health, then blocks on tail -f /dev/null until a signal arrives. There is no tool dispatcher, no subprocess broker, no policy gate on execution.
Anthropic's sandboxes and Kimi's exist to run untrusted code safely. AI Studio's exists to serve a preview of the app under construction, to the right viewer. Its security engineering is concentrated almost entirely on ingress — who may load the iframe — rather than on egress or execution.
Scored on containment against dedicated execution sandboxes, it looks careless. Scored against what it is, most of the gap is category difference rather than negligence. The findings below are calibrated accordingly.
gVisor, and what gVisor looks like from the inside.
| Isolation | gVisor (runsc) on Google Cloud Run |
|---|---|
| Kernel string | Linux localhost 4.19.0-gvisor #1 SMP Sun Jan 10 15:06:54 PST 2016 |
| Base OS | Debian 12 (bookworm), largely unminimised |
| CPU / RAM | 2 vCPU (model masked as unknown) · 4 GB, no swap |
| Root filesystem | overlay, reported 378 GB |
| Process identity | root (uid 0); only nginx workers drop to nobody |
| Persistence | ephemeral overlay only — no FUSE, no remote mounts |
Two artifacts confirm gVisor rather than a conventional container, and both are useful fingerprints:
df /var/log returns Function not implemented — an unimplemented syscall path in the gVisor gofer, not a permissions error./proc rather than passing host CPU data through./, /dev, /dev/shm and /tmp each report 378 GB. That is one synthesised figure repeated across four overlay mounts, not four large volumes. Treat 378 GB as an artifact of the gVisor filesystem layer rather than a provisioned allowance.
The nginx Lua auth bridge is the most engineered component in the environment, and the audit reports mostly skip it.
All ingress lands on nginx :8080, and every request to / passes through access_by_lua_file /etc/nginx/user_auth_verification.lua — annotated in the config as strictly necessary and never to be removed. The script:
lua_shared_dict, with separate TTLs for public and unique tokens;document.cookie in cross-site iframes even after a storage-access grant.Only after that does traffic reach the Vite dev server on :3000, with an AI Studio iframe script injected into the HTML response. The control plane is exposed under its own prefix and proxied to :8000.
The Lua script honours an environment variable, DISABLE_AUTH_BRIDGE. When set to true, authentication is skipped entirely. The in-file comment states plainly that this removes authentication for the container and that anyone holding the URL can then access it, and restricts it to internal evaluation containers where an autorater cannot authenticate.
This is a deliberate, documented, correctly-warned switch — not a bug. It is nonetheless the highest-consequence control in the environment, because a single misapplied environment variable converts an authenticated preview into an open one. It deserves more attention than a metadata endpoint that behaves exactly as Cloud Run always does.
| Interfaces | lo 127.0.0.1/8 · eth1 169.254.8.1/32 · eth2 169.254.9.1/32, 169.254.169.1/32 |
|---|---|
| DNS | 169.254.169.254, search domain google.internal. |
| Egress policy | none — no proxy variables, no allowlist, no TLS interception |
| Observed | google.com 301 · registry.npmjs.org 200 · api.anthropic.com 404 (reached; 404 is that host's own response to a bare request) |
The absence of any RFC-1918 address, and the reliance on link-local 169.254.x.x interfaces, is the standard signature of GCP serverless networking. Egress is entirely open: the environment makes ordinary outbound TLS connections with no interception, which is the opposite of both Anthropic postures and of Kimi's browser-only proxy.
The original audit rated two findings "high". Neither survives contact with the evidence in the same probe.
| Finding | As filed | Calibrated | Reasoning |
|---|---|---|---|
| Metadata server reachable at 169.254.169.254; OAuth token extractable | high | by-default · mitigated | Every Cloud Run container can reach the metadata server; this is platform default, not something AI Studio introduced. The control is IAM scoping, and the same probe session confirmed it holds — testIamPermissions and run.services.list both returned 403 PERMISSION_DENIED, and Cloud Resource Manager is SERVICE_DISABLED on the project. Blast radius is contained by design. |
| Model API key injected as a plaintext environment variable | high | real, severity undetermined | The mechanism is genuine: any transitive npm dependency in the Vite app can read process.env and, with unrestricted egress, ship it anywhere. Severity turns entirely on whether the key is a disposable per-applet credential or a shared platform key — the probe never established which, so "high" is unearned. The question is answerable and worth answering. |
DISABLE_AUTH_BRIDGE disables ingress authentication wholesale |
not filed | design risk — highest consequence | Documented and warned in-file, restricted by intent to eval containers. Still the single switch whose misapplication most directly exposes a user's application. |
| Agent processes run as root (uid 0) | low | low — agreed | gVisor's syscall interception is doing the real containment work. Dropping privileges would still be better hygiene. |
| Unrestricted egress | high | correct as fact, mis-framed as defect | Accurate and worth stating. But an app-preview host that cannot reach the internet cannot serve most applications. The finding belongs in the threat-model discussion, not the vulnerability list. |
None of this means the environment is safe for arbitrary untrusted code. It plainly is not: root execution, open egress, and secrets in the process environment compose badly. The correction is to the framing — these are properties of a preview host being used as though it were an execution sandbox, not defects in an execution sandbox.
| Tool | Status | Note |
|---|---|---|
| Node.js | v22.23.1 | runs the Vite dev server |
| npm | 10.9.8 | |
| Python | 3.10.12 | oldest Python of the four environments surveyed |
| nginx + Lua | present | ndk and http-lua modules enabled |
| Rust / Go toolchains | absent | though the control plane is a statically linked Go binary (not a dynamic executable) |
| Docker | absent | |
| ip | present at /usr/sbin/ip | see §7 — the source audit claims otherwise |
| ifconfig / netstat | absent |
The toolchain is narrow and deliberate: full-stack JavaScript with Python alongside. Nothing here supports systems work, which is consistent with §1.
This is a self-probe. The same caveats apply as to every other environment audit in this series.
| Claim | Standing |
|---|---|
gVisor runtime, process tree, nginx and Lua configuration, start.sh contents | high — read directly from disk |
| Egress unrestricted | high — three destinations, three successful responses, no proxy in env |
| Metadata reachable; IAM scoped to deny | high — token retrieved, two API calls returned 403 |
| "ip is absent from $PATH" | contradicted — the same transcript shows which ip resolving to /usr/sbin/ip and ip a returning full interface output |
| 378 GB disk | artifact — repeated across four mounts; a gVisor reporting quirk, not a provisioned figure |
| Service account permissions "likely scoped tightly" | partially tested — two APIs denied; the account's full IAM binding set was never enumerated |
| Cross-session persistence | untested |
The source transcript contained a live model API key and a live OAuth bearer token. Both are replaced here with redacted; the OAuth token had a 1799-second lifetime and is long expired, and the API key has been revoked. Applet-specific identifiers and the generated Cloud Run preview URL are also generalised, since they identify a particular session rather than the platform.
| Runtime | gVisor (runsc) container on Cloud Run, europe-west3 |
|---|---|
| Compute | 2 vCPU · 4 GB · ephemeral overlay |
| Ingress | nginx + Lua token verification against aistudio.google.com |
| Egress | unrestricted; no proxy, no allowlist, no interception |
| Identity | metadata reachable by platform default; service account IAM scoped to deny the APIs tested |
| Best used for | generating and previewing full-stack JS/TS applications |
| Not suited to | executing untrusted third-party code — open egress plus secrets in the process environment compose badly |
The instructive comparison is not "is this weaker than Claude's sandbox." It is that two products can share a substrate class and diverge entirely on where they spend their security budget. AI Studio spent nearly all of it on ingress. That is a coherent choice for a preview host, and a poor fit for the execution-sandbox role it is increasingly asked to fill.