DEV Community

Cover image for Your Agent's Share Link Is a Bearer Token, and Its SSH Socket Is You
KarmaBox
KarmaBox

Posted on

Your Agent's Share Link Is a Bearer Token, and Its SSH Socket Is You

Two security stories broke within a day of each other this week. They look different, but they describe the same problem.

On November 10, Ars Technica reported research from security firm Claroc: Grok Build, the vibe-coding agent from SpaceXSI, has been leaking data from thousands of users through its share links. The URLs are randomized, so in theory only someone who knows the exact address can open an app. A design decision in the platform, however, makes it possible to enumerate thousands of live projects, and clicking through opens the app and, in all but rare cases, everything stored with it. The exposed haul in Claroc's sampling: internal business plans, ad proposals, medical exam results, galley checklists from a celebrity cruise line, employee names and ID numbers. The embarrassing detail: 24 of the leaking apps were built by SpaceXSI itself, including a confidential onboarding plan for its enterprise sales team, a sales territory map of 594 accounts, and internal system addresses.

The day before, a post on Hacker News described a quieter exit. A user reported that a desktop AI coding setup had forwarded their local SSH agent to the remote execution environment, meaning agents running on machines they did not control could authenticate as them, including pushing to private repositories. The mechanism is not a rumor; the openai/codex repository has a merged pull request, #28131 from June 2026, implementing an explicit opt-in flag, --forward-ssh-agent, that validates and refreshes the forwarded agent socket across reconnects. The capability is real, documented, and deliberate; the HN report is one user's account of being surprised by where their identity ended up.

In the first story, data walks out through a link. In the second, your identity walks out through a socket. Neither exit required an exploit, both shipped as features, and both are present in ordinary developer setups today. This post is an audit of those two exits.

Exit one: why a random URL is not an access control

The Grok Build failure is worth understanding precisely, because the pattern is everywhere, and because nothing about it required breaking cryptography.

An unguessable URL is a bearer token: whoever holds the string gets the resource, no further questions. As a security primitive this is legitimate and widely used; the entropy of the random suffix does the work a password would do. The arithmetic only holds under one assumption: the token never appears anywhere an attacker can read.

That assumption is what the design decision destroyed. If any surface of the platform lists projects, indexes them, exposes sequential identifiers, or lets a crawler walk from one share to another, the randomness of the suffix becomes decoration. Claroc did not guess URLs; per Ars' description, the platform made it possible to enumerate live projects. The platform had one job here: make sure nobody could pull a list of those links. That is the job that failed.

A rule you can use in your own audit: a bearer URL is exactly as private as the most public place it is ever written down. Share links live in chat logs, in email bodies scanned by filters, in browser histories synced to clouds, in analytics referrer fields, in the platform's own index. Each of those is a place your "private" link is stored by someone who never promised you privacy. The link does not expire when the meeting ends or the demo finishes, and almost nobody goes back to revoke links, because almost nobody keeps a list of the links they have minted.

Note the pattern, because it repeats: the cryptography was fine, the policy was fine, the leak lived in an adjacent design decision nobody audited as a security surface. The sharing feature was built by people solving "make collaboration easy." The enumeration surface was invisible to them because it was not in the threat model of a sharing feature.

a random URL
It is worth being precise about who was responsible for what. Making the suffix random enough was the vendor's job, and they did it. Keeping the project list from being enumerable was also the vendor's job, and they did not, and no user could have seen that until Claroc looked. The only thing users ever controlled was the decision of what to store behind such links in the first place. That is the whole case for auditing your own side of every boundary: the side you can see is the only side you can fix.

Exit two: the socket that signs as you

The SSH agent story needs one paragraph of mechanism for anyone who has only ever used the agent without looking at it.

Your SSH agent holds your private keys and answers signing requests over a Unix socket, advertised in SSH_AUTH_SOCK. Agent forwarding extends that socket to a remote machine: processes there can ask your local agent to sign authentication challenges. The keys never leave your laptop, which is the detail that makes forwarding feel safe, and the signing capability absolutely leaves your laptop, which is the detail that matters. Anything on the remote end that can reach the socket can authenticate as you, to any system that trusts your key, for as long as the session lasts.

local laptop                      remote execution env
┌───────────────┐                 ┌─────────────────────┐
│ ssh-agent     │◄── socket ─────►│ any process here    │
│ (your keys)   │   forwarded     │ can request a       │
└───────────────┘                 │ signature as YOU    │
                                  └─────────────────────┘
Enter fullscreen mode Exit fullscreen mode

The ssh documentation has warned about this for as long as the flag has existed: forward only to hosts you trust, because the socket is reachable by root on the remote end. The warning was written for a world where the remote end was a machine you picked and the session was your hands on a keyboard.

Forwarding to a server you administer, for a session you watch, is a calculated risk ops people have taken for decades. The new ingredient is the autonomous occupant. An AI agent in that remote environment is a process that runs for hours, acts on its own schedule, executes code it just wrote, and ingests instructions from files and web pages it was asked to read. Prompt injection turns "can reach the socket" from a hypothetical into an attack path: a malicious README that convinces an agent to run git push somewhere it should not is now operating with your identity.

The codex PR deserves to be read fairly: --forward-ssh-agent is opt-in, and the engineering in #28131 is about keeping the socket valid across reconnects, which is solid work for people who asked for the feature. The HN user's surprise is the actual finding. Forwarding your identity to an autonomous agent is a different risk from forwarding it to a session you drive by hand, and the flag's name does not warn you about that difference.

The common geometry

Put the two exits side by side and the shared structure is visible:

Both failures happened where two individually reasonable features met, and combining features is exactly what agent platforms do all day. Neither vendor shipped a vulnerability in the CVE sense; both shipped conveniences whose interaction with real usage produced an exit. That distinction matters for your audit, because scanners find vulnerabilities and nobody scans for conveniences. An agent is a thing that combines your tools, your credentials, your data, and a remote execution environment, automatically, on a loop. Every convenience the platform adds is a new connection between those pieces, and the leaks happen at the connections.

The same week supplied a third data point from the other direction: Fireworks disclosed on November 9 that attackers accessed some customers' model weights, datasets, and billing information, per TechCrunch. Different exit, same lesson: whatever sits inside somebody's boundary is exposed to that boundary's failures, and the only variable you choose is whose boundary your valuables sit inside.

The sovereign-AI framing gives this a name. Condition two of a sovereign AI avatar: the data an agent works with stays within the boundary its owner set. The useful move for a developer is to treat identity as data, the most sensitive record you own: when a document leaks, the damage is its contents; when your identity leaks, whoever holds it can act as you.

Where else the bearer pattern lives in your stack

Grok Build is the example with a headline. The pattern ships in tools you already use, and the audit should walk all of them.

Meeting recordings and their transcripts travel as "anyone with the link" pages. Cloud documents and notebooks default to the same mode the moment someone hits share under deadline. Agent platforms mint trace viewers and eval dashboards with tokenized URLs, and those traces contain your prompts, which contain your data. Object storage pre-signed URLs are the one well-behaved member of the family, because they carry an expiry by construction; they are built around a token that expires on a schedule, which is exactly what the rest of the family lacks.

The well-behaved version of the pattern needs three properties, and you can demand them from any tool: expiry by default, revocation that actually works, and a ledger of minted links someone can review. A sharing feature with all three is an access control. A sharing feature with none of them is a publishing button with a delay.

The socket has a family too

SSH agent forwarding is the member of its family that made the news this week. The family is ambient identity: credentials reachable from an execution environment without anyone handing them over per use.

The cloud metadata endpoint, 169.254.169.254, answers with instance credentials to any process that asks from inside. A mounted docker.sock makes a container root on its host. A logged-in browser profile, driven by an agent that browses, is a wallet of session cookies. CLI config files hold OAuth refresh tokens that outlive any session. A kubeconfig in the home directory is a cluster. None of these was placed for the agent; all of them are reachable by it, because the agent runs where they live.

ambient identity
The audit question is uniform across the family: what identity-shaped things can a process in this environment reach without asking a human, and which of them would still be dangerous in the hands of a contractor who obeys instructions found in documents. Enumerate, then either remove, scope, or gate each one.

Why "the model is trustworthy" is the wrong variable

One mechanism paragraph, because it explains why this audit targets the environment rather than the model.

A language model has no privilege separation between instructions and data. Everything in context, the system prompt, your request, the README it just read, the web page it fetched, arrives as the same kind of token stream, and the model has no way to verify where an instruction came from. Vendors add mitigations, and the mitigations help, but the property is structural: anything the agent reads is a potential instruction. Which means the security boundary cannot be drawn around the model's intentions. It has to be drawn around what the environment lets any occupant do: what is reachable, what is signable, what can leave. That is the entire logic of auditing sockets and tokens instead of auditing prompts.

The audit: one afternoon, five commands

Nothing below needs new tools. Budget an afternoon.

Start with the bearer-token inventory. List every place your agent tooling mints share links: coding agents' app shares, notebook links, trace viewers, playground snapshots. For each, answer three questions: can anything enumerate it (try the obvious: list endpoints, sequential IDs, sitemap, search engine site: query), does it expire, and can you revoke it. A link that is enumerable, immortal, and irrevocable is a public page with extra steps, and should carry nothing you would not put on one.

Then the credential manifest, for every remote or containerized agent run:

# in the agent's execution environment, not your laptop:
env | grep -iE 'token|key|secret|auth'
ls -la "$SSH_AUTH_SOCK" 2>/dev/null && echo "agent socket PRESENT"
cat ~/.git-credentials 2>/dev/null
curl -s --max-time 1 http://169.254.169.254/ >/dev/null && echo "cloud metadata REACHABLE"
Enter fullscreen mode Exit fullscreen mode

Everything that prints is something the agent can use, which means something a prompt injection can use. Run it in every distinct environment your agents touch, laptop, container, remote sandbox, CI runner, because the manifest differs per environment and the dangerous one is usually the environment nobody considered an environment. The question for each line is never "do I trust the model"; it is "would I hand this to a contractor whose instructions include every file he reads."

Then split the identities. The agent gets its own: a deploy key scoped to one repo, a short-lived token minted per task, an account whose blast radius you chose on purpose. The test for the scope is the morning-after question: if this credential appears on a paste site tonight, what is the total damage, and the answer should be a shrug about one repository rather than an incident call about your identity. Your personal key signs nothing you did not type. If the platform offers agent forwarding, keep it off by default; turn it on per session, and only when you can say exactly what the remote end will be signing.

Then watch the egress once. Run a representative agent task and look at what actually leaves: network connections, uploads, pushes. One honest look recalibrates the mental model better than any policy document, and the look is cheap: a proxy in front of the sandbox, or even lsof -i sampled during the run, is enough to learn whether your picture of the agent's traffic matches the agent's traffic.

Last, write down the boundary you decided. Which data may ride in which direction, which identity acts where, who approves exceptions. One page. The Grok Build victims whose medical results and sales maps are now browsable did not decide a boundary; they inherited one from a sharing feature's defaults.

A worked example: the demo that outlived the demo

Consider a four-person agency that built a client demo on an agent platform in March. The demo needed realistic data, so someone exported a slice of the client's order history into it, eleven thousand rows, because the demo looked better with real numbers. The share link went to three people at the client. The deal closed in April. Nobody thought about the demo again.

The link is still live in November. It lives in three inboxes, one forwarded thread nobody tracked, a browser history synced across two laptops, and the platform's own project index, wherever that is readable from. The agency's security posture, meanwhile, is decent by any checklist: SSO everywhere, disks encrypted, dependencies scanned. None of that apparatus has the demo in scope, because the demo does not exist in any inventory. It was never deployed; it was only shared.

The audit above catches this in the first pass: the inventory question "what links have we minted" surfaces the demo, the three-questions test flags it (enumerable: unknown; expires: no; revocable: nobody remembers how), and the boundary page afterward says client data rides into demos only as synthetic rows. Total cost of prevention: one afternoon in March. Cost of the alternative: whatever a leak of eleven thousand rows of someone else's order history turns out to cost, at a moment the client chooses, not the agency.

The default stack, if you only remember five settings

For readers who want the end state without re-deriving it: links expire by default, and anything shared externally gets a calendar entry to revoke it. Agents authenticate as themselves, with per-task tokens, never as you. Forwarding of any ambient identity, SSH agent included, stays off until a specific session needs it. Egress from agent environments goes through an allowlist, so surprise destinations fail loudly. And outward actions, push, send, publish, pass an approval gate that emits a receipt.

Every one of those settings adds friction, and each is the cheap kind: seconds per event, in exchange for closing exits that stay invisible until the headline is about you.

How KarmaBox draws this boundary

KarmaBox is our product: a platform and network for running your own AI avatars, built so that condition two is enforced by architecture instead of discipline.

Avatars run on the owner's deployment, so the execution environment and the data boundary are the same place; what an avatar reads and produces stays on the machine unless a policy says otherwise, which is the property the agent data leakage guide walks through surface by surface. Credentials follow the same rule: keys stay in the owner's custody per the API key self-custody checklist, and outward actions, pushing, sending, publishing, are policy-gated per operation and wait for the owner's approval, so an injected instruction meets a gate instead of a socket. Setup specifics are in the docs.

The configuration split for this article's readers: work that touches private repos, customer data, or your own credentials runs on avatars inside your boundary; experiments with public code can use whatever is convenient.

What the audit does not buy you

The audit has limits of its own. It addresses exposure risk: fewer tokens in the wild, fewer sockets reachable, one page of decided policy. It does not address platform risk: Grok Build's users could have run this entire audit and still been burned, because the enumeration surface was on the vendor's side of the fence; the only defense was not storing sensitive data behind a sharing feature at all. It does not address judgment risk: an agent with a correctly scoped deploy key can still push bad code through the correct gate. And local execution narrows exits without closing the human one: whoever can read the records can still leak them, which no architecture fixes.

Also, the honest cost: per-task tokens and approval gates are friction, and the afternoon of audit is real hours. The cost of skipping them is the one in this week's news: 594 accounts on a sales map, browsable by URL.

One of the two exits in this post is probably open in your setup right now. Which one are you going to check first, the links you have minted or the socket you are forwarding?

Sources

  1. Grok Build coding agent leaks oodles of confidential info, some belonging to SpaceXSI, Ars Technica, November 10, 2026.

  2. openai/codex on GitHub, merged June 2026, accessed November 11, 2026.

  3. AI agent data leakage, Karma Box, 2026.

Top comments (0)