A Display Name Was Enough to Become the Owner: Security Review of a Self-Hosted AI Agent
In Rome's default setup, a stranger who set their WhatsApp name or email display name to the owner's name was treated as the owner. They got the owner's agent, with a shell and every connected account.
We found it during a security review of Rome's code. We reported it, and Rome fixed it the same day. A second report followed a few days later. Rome agreed that we could name them and link the repo.
What Rome is
Rome is an open-source, self-hosted agent system. It connects to messaging channels such as WhatsApp, email, Telegram and Discord, and runs a main agent for its owner, whom Rome calls the "guardian". Every sender gets a trust level, which Rome calls a "bond level". If the owner turns it on, Rome can also run scripts on the host VM.
Finding 1: a display name was enough to become the owner
We filed this as issue #624, from a review of commit de2d152. It applied to the default configuration. The path:
- Telegram, Discord and Feishu require pairing. Other channels, such as WhatsApp and email, deliver every sender to the inbox.
- A message arrives from an account Rome has never seen.
- Rome looks for an existing person whose display name matches the sender's display name, ignoring case. The owner was not excluded from that lookup.
- On a match, Rome linked the sender's account to that person immediately. An approval request was created, but only after the link was written, so it could undo the link, not prevent it.
- The sender now has the owner's bond level, and the message goes to the main agent on the trusted path.
The sender controls the field that decides all of this. On WhatsApp it is the push name. On email it is the display name in the From header.
The main agent has a shell in the container and reaches every connected account through actions: mail, GitHub, Slack, Stripe. Replies go back to the sender, and the link is stored, so every later message from that account is trusted too. If the owner has enabled host execution, this reaches root on the host VM.
The stronger check already existed next to this code. Rome's email channel puts unauthenticated senders in a separate namespace, and the comment in packages/core/src/channels/email.ts states the rule: "a spoofed From can never resolve to a mapped person (guardian, inner-circle, or acquaintance) and reach the trusted path." The inbox lookup used the display name, not that namespaced ID, so the rule did not hold. It did not even need spoofing. Mail from a domain that passes SPF and DKIM, with the owner's name as the display name, worked too.
There was a second guard nearby. A function that links an account to the owner existed, with checks, but it ran only during pairing and credential setup. The automatic link did not go through it.
Rome fixed this in PR #623, merged the same day the issue was filed. The display-name lookup and automatic linking are gone. An unknown sender now resolves to no person and the lowest bond level, other. A regression test covers the case where a sender uses the owner's name.
Rome also noted that the upgrade does not revoke links created before the fix. If you run Rome and had it connected to a non-pairing channel, review the accounts that were linked automatically.
Finding 2: a root action in read-only agents
We filed this as issue #679, from a review of commit 9837b71, re-checked on the current main branch before filing.
This one is off by default. It only applies when the owner enables host execution (ROME_HOST_EXECUTION_ENABLED=true) and installs the host helper.
Rome has an action that runs an unrestricted root shell script on the host VM. Agents get actions through an allow-list, and several agents use the wildcard "*". The wildcard gives an agent every action, unless the action is marked visibility: explicit.
The root action was not marked explicit. Rome's docs, in docs/concepts/actions.md, say that "visibility gates both agent discovery and agent execution", so this is the documented control for this case. No action in the repo used it outside tests.
As a result, three agents described as read-only or validation agents held the root action:
sentinel, which triages messages from untrusted senders;envoy, which validates outgoing actions;explore, described as read-only exploration.
None of them has a shell, so the action list was their only path to the host. The sentinel reads the untrusted sender's text in its prompt, so a crafted message could try to make it call the root action.
We did not report this for the main agent. The main agent has a shell, and Rome's docs state openly that access to the host socket gives host authority to code in the container. Rome documents that as a trade-off, so we left it out. The finding is only about agents that have no shell and should never have held root.
Rome fixed this in PR #740, merged on October 10, 2026. Both host root actions are now visibility: explicit and granted to the main agent by exact name, so the wildcard no longer gives them to anyone. The sentinel now holds no actions at all and only returns a decision. A regression test checks that the main agent is the only holder.
Rome's fix went further than our report. The PR lists more wildcard holders of the root actions than we did. During review, Rome also found and closed a second route, where an agent could schedule the root action through a routine instead of calling it directly. That is the right way to take a report: they fixed the class of problem.
What we did not prove
- We did not run Rome or send anything to any instance. This was a static review.
- We did not measure how reliably a crafted message makes the sentinel call the root action. The defect does not depend on that: a root action should not be in that agent's list at all.
- We traced finding 1 end to end for WhatsApp and email. WeChat and telegram-user are also non-pairing channels, but we did not trace where their display names come from.
- If two people share the owner's display name, the code did not make clear which one would match first.
The pattern
In both findings, trust was decided by a label instead of an authenticated identity plus a scoped permission. In the first, the label was a display name. In the second, it was a wildcard.
In both, a stronger control already existed in the codebase: the email namespace, the guarded pairing function, visibility: explicit. It just was not on the path that mattered. We saw the same shape in our Open Session review, where trust was checked on who started a run, not on the text fed into it.
AI agent security checklist
- Never let a sender-controlled field grant a trust level. A display name, a signature or a subject line is whatever the sender wants it to be.
- Require approval before trust is applied. An approval created after the link or the action already happened can only clean up.
- Make dangerous actions opt-in by exact name. Then a wildcard allow-list can never grant them by accident.
- Give agents that read untrusted text the smallest tool set. They are the most exposed agents in the system.
- Check that the controls your docs describe are used on the paths that matter. Rome's docs named visibility as the control, and no action in the repo used it.
- When you fix a trust bug, clean up the state it already created. Rome's fix stopped new links, but links made before the upgrade stayed until an operator removed them.
How we work
In a security review or code audit, we read the docs and security notes first, review the code statically and tie each finding to files and lines. We drop anything the project documents as a deliberate trade-off, as we did with the main agent here. For finding 1, Rome's co-founder asked us to file it as a GitHub issue, so we kept the public report at the level of conditions and offered a step-by-step proof of concept privately.
Thank you to the Rome team. Both issues were labelled P0 the day they were filed, and the first fix shipped the same day with a regression test and clear upgrade notes.
FAQ
How do you find security issues in an AI agent?
Start with inputs, not with the model. List every input a sender controls, such as a display name, a message body or an email header, and trace where each one affects trust or tool access. Then check which actions each agent can reach, including through wildcard allow-lists. In this review, both findings came from that trace, not from testing prompts.
What does an AI agent security review include?
It covers how the agent decides who it is talking to, which tools and actions each agent holds, what happens to untrusted text before it reaches a prompt, and whether the controls in the docs are used in the code. Ours is a static review: we read the code and docs and tie every finding to files and lines, without running the system.
What is least privilege for AI agents?
Each agent gets only the actions its job needs, granted by exact name. An agent that triages untrusted messages needs no actions at all, which is where Rome ended up after the fix. Wildcard grants like "*" are the opposite of least privilege, because every new action, including a dangerous one, is granted automatically.
How do you secure an AI agent that reads WhatsApp or email messages?
Treat every sender-controlled field as untrusted, including display names, and never let one decide a trust level. Link accounts to people only through pairing or another verified step, and ask for approval before trust is applied, not after. Keep the agent that reads raw messages away from actions that change anything.
What is excessive agency in AI agents?
OWASP's Top 10 for LLM applications uses the term for an agent that holds more permissions or tools than its task needs, so a manipulated agent can do real damage. Finding 2 is a direct example: triage and read-only agents held an action that runs root scripts on the host.
Is this prompt injection?
Finding 1 is not. No model was tricked: the identity mapping itself granted trust. Finding 2 would rely on injecting the sentinel, but the defect is structural. The action should not have been in its list.
We do code audits and security reviews of AI agent products and AI-generated codebases by hand, with findings tied to specific files. Tell us about your product.