All notes
Engineering6 min read

Prompt Injection Just Became a Project Management Problem

The Rovo injection chain works because reading untrusted content and holding authority to act are the same step. They shouldn't be.

Here's the attack, in four sentences.

A user uploads a document — a spec, a vendor brief, a PDF from a partner. Hidden inside it is text addressed not to the human but to the assistant. The user asks Rovo to organise their Jira tickets. Rovo searches Jira and Confluence as instructed, appends what it finds to a URL the attacker controls, opens it, and the attacker reads the contents out of their own server logs.

PromptArmor published the chain on 5 August. A separate route — a crafted rovoChatPrompt URL parameter, found by Varonis and nicknamed RovoBlast — was fixed server-side on 8 July. The content-based chain was still working at publication.

The detail worth pausing on: it succeeds with organisation-wide web search disabled. Turning off web search doesn't remove the underlying URL-retrieval capability (Fig. 1). The setting that looks like it closes this door doesn't.

Fig. 01The published chain in the order it runs, and where the web-search setting actually sits
  1. Attacker-controlled
    An uploaded document — a spec, a vendor brief, a partner PDF — carrying text addressed to the assistant rather than to the reader.
  2. reaches the model as content
    Ordinary request
    “Organise my Jira tickets.”
  3. model decides how to answer
    The model's own steps
    Searches Jira and Confluence — everything the authenticated user can reach.
  4. retrieved text becomes part of a URL
    URL retrieval capabilityOrg-wide web searchOff
    Exfiltration
    Appends what it found to a URL the attacker controls, and opens it.
  5. the outbound request leaves
    Off-system
    The attacker reads the contents out of their own server logs.
Separate route · fixed 8 JulyrovoChatPromptURL parameter, patched server-side. The chain above was still working at publication.
Only the first hop is attacker-controlled; every hop after it is the product working as designed against an unremarkable request. The last hop is an outbound URL fetch, which is why the chain survives organisation-wide web search being switched off — that setting sits inside the retrieval capability rather than around it.

Every writeup so far has framed this as a security story for security people. It's also an operating-model story, and that's the part that affects how you run a team.

Why it works

Not a parsing bug. Not a missing input filter. There's no patch shaped like "escape the untrusted string."

It works because one component does two jobs at once: it reads untrusted content, and it holds authority to act. Those are the same step. Once they are, any text the model reads is a candidate instruction, and the model has no reliable way to tell your request apart from text that merely looks like a request.

That isn't a Rovo flaw so much as the default shape of every agentic feature shipping in this category right now. Jira, Linear, Notion, ClickUp — the pattern is the same everywhere: an assistant with broad read access across your workspace and a set of tools it can invoke on its own judgment. Rovo got the writeup because Rovo has 80% of the Fortune 500 and researchers go where the blast radius is.

And "organise my Jira tickets" is about as suspicious a request as making a cup of tea. There's no user error here to train away.

Propose and execute are different jobs

The useful frame is separation of duties, which project managers already understand in a different context. You don't let the person who raises the purchase order also approve it. Not because they're untrustworthy — because concentrating both in one place means a single compromise is total.

Same structure:

The propose path. Reads everything. Your Confluence, your tickets, the attached PDF, the vendor's spec. Suggests freely. This is where models are genuinely good, and constraining it wastes the thing you're paying for.

The execute path. Writes to Jira. Declared in advance. Reviewable as a unit before it lands. Deterministic — the same input produces the same mutation, and the mutation is one you could have described before you ran it (Fig. 2).

Under that split, the PromptArmor chain still gets to say something malicious. The hidden text still reaches the model. The model may even be persuaded. It has nothing to act with.

Fig. 02The same work with reading and acting as one step, and with them separated by a review
Reading and acting as one step
Untrusted content in
Reads

Confluence, tickets, the attached PDF — everything the authenticated user can reach.

Holds authority to act
search(jira)search(confluence)fetch(url)

A write-permission review asks about none of these.

No boundary between the two — they are the same step, so any text it reads is a candidate instruction.

Data leaveson the model's own judgement
Propose and execute, separated
Untrusted content in
Propose
Reads everything: Confluence, tickets, the attached PDF, the vendor's spec. Suggests freely.
Review
Reach ends hereThe full set of mutations, as a unit, before it runs. The hidden text still arrives and may still persuade — it has nothing to act with.
Execute
Deterministic — the same input produces the same mutation.write(issue)Set declared before the model saw anything.
Both lanes read the same hostile document, and both may be persuaded by it — nothing here is a filter. What differs is the set the acting side can invoke. On the left that set includes fetch(url), which is why the published chain exfiltrates without writing to Jira at all, and why a review that only asks about write permissions passes it.

The exfiltration in that attack isn't a write to Jira — it's an outbound URL fetch, which is why it slips past people thinking only about write permissions. The general principle survives: enumerate what the model can invoke, assume everything it reads is hostile, and ask what damage the invocable set permits. If "fetch an arbitrary URL" is in that set, data leaves. It's a data-flow question, and data-flow questions are answerable in advance.

Being fair to Atlassian

Atlassian's position is that this is controlled through Rovo access scoping — Rovo can only reach what the authenticated user can reach, so scoping user permissions properly limits the blast radius.

That's a real mitigation and it deserves stating straight rather than strawmanning. If your Confluence is locked down tightly, the attack retrieves less.

Two honest problems with it. Most organisations do not have tight Confluence permissions — the whole value proposition of a wiki cuts against it, and "everyone in engineering can read everything in engineering" is the normal state of the world. And it moves the burden onto a control most teams have already declined to invest in, then calls the result a customer configuration issue.

The 8 July server-side fix for the URL-parameter route was the right response, quickly done. The architectural question is the harder one, and it isn't specific to Atlassian.

Three questions for any AI-in-Jira vendor

Including us. Especially us.

What can the model write? Not "what can the app do" — what can the model trigger without a human in the loop. If the answer is a long list, or the vendor hasn't enumerated it, that's your answer.

What's declared in advance? Can you see the full set of mutations before they run, as a unit? "The agent will figure out what to do" and "the agent will apply this defined change" are different products wearing the same badge.

What's reviewable before it lands? Not an audit log after the fact. A diff, before.

Ask these of every AI feature you're evaluating this year. The vendors who've thought about it will have crisp answers, and the ones who haven't will talk about their security certifications instead.

Where we sit

SuperTemplates writes to Jira from a template you defined and reviewed. The generation step reads your text and proposes structure; you see the full set of issues in the editor before anything is created. The write path is the template, not the model's improvisation against whatever it read last.

That's not a claim to be secure — it'd be a stupid claim to make, and this is exactly the wrong month to make it. It's a claim about surface area. There's no step where content we read becomes authority we act on, because those steps are separate by construction. That property came from wanting predictable bulk creation rather than from any security foresight, which is worth admitting.

The interesting shift is that this stopped being an architecture preference and became a procurement question. Enterprise buyers are going to start asking what your model can invoke. Not this quarter, probably. But the writeups are out, and security review questionnaires follow writeups by about two quarters.

Worth having an answer ready. Worth more to have an architecture where the answer is short.

The product these notes come from

Stop creating Jira issues one by one.

SuperTemplates turns unstructured text into structured backlogs. AI handles hierarchy. Templates handle scale. You review the whole batch before anything is created.

Try free on the Atlassian Marketplace