Skip to content

fix(xmtp): render reply inner content instead of dumping raw JSON#82

Merged
saulmc merged 1 commit into
mainfrom
fix/reply-attachment-rendering
Jun 24, 2026
Merged

fix(xmtp): render reply inner content instead of dumping raw JSON#82
saulmc merged 1 commit into
mainfrom
fix/reply-attachment-rendering

Conversation

@saulmc

@saulmc saulmc commented Jun 24, 2026

Copy link
Copy Markdown
Member

Summary

normalizeMessageContent serialized a reply's inner content with JSON.stringify whenever it wasn't a string. For a reply that carries a remote attachment (someone replies to a message with a photo), that dumped the decoded attachment envelope verbatim into the message text — including the AES key material (secret/salt/nonce) — instead of the [remote attachment: …] display string. Downstream consumers (e.g. the Herald agent webhook) then handed that raw blob to the agent.

This renders the reply's inner content through normalizeMessageContent recursively, using the inner contentType the decoded EnrichedReply already carries, so a reply-wrapped remote/inline attachment (or any nested content) becomes its display string:

reply to "see attached" (7739f5bd): [remote attachment: photo.jpg (4521 bytes) https://…]

A text reply still hits the existing string fast-path; an unknown inner type falls back to the prior JSON behavior.

Why

A reply was the one path that never delegated to the per-type renderers below it, so it was the only way encryption key material leaked into agent-visible text.

Tests

Added normalizeMessageContent coverage: text reply, reply→remote attachment (asserts no secret/salt/nonce in output), reply→inline attachment, and the unknown-type JSON fallback.

🤖 Generated with Claude Code

https://claude.ai/code/session_01SRUJWMudi3sSaGffXyzk52


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.

Note

Fix normalizeMessageContent to render reply inner content instead of raw JSON

Replies carrying non-text payloads (e.g. remote/inline attachments) previously serialized the decoded content as raw JSON, which could expose key material. normalizeMessageContent in xmtp.ts now reads the inner contentType of a reply and recursively normalizes it; it falls back to JSON.stringify only for unknown content types. New tests in xmtp.test.ts cover plain text, text replies, attachment replies, and the JSON fallback path.

Macroscope summarized 15c464b.

normalizeMessageContent serialized a reply's inner content with
JSON.stringify when it wasn't a string. For a reply carrying a remote
attachment that dumped the decoded envelope verbatim — including the AES
key material (secret/salt/nonce) — into the message text the agent sees.

Render the inner content through normalizeMessageContent using the inner
contentType the decoded reply carries, so a reply-wrapped remote/inline
attachment (or any nested content) becomes its display string, e.g.
`reply to "…": [remote attachment: photo.jpg (… bytes) https://…]`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SRUJWMudi3sSaGffXyzk52
@macroscopeapp

macroscopeapp Bot commented Jun 24, 2026

Copy link
Copy Markdown

Approvability

Verdict: Approved

Small, focused bug fix that prevents AES key material from leaking into message text by reusing the existing content normalizer for nested reply payloads. The change improves security, is well-tested, and follows established patterns in the codebase.

You can customize Macroscope's approvability policy. Learn more.

@saulmc saulmc merged commit 1a4a74c into main Jun 24, 2026
4 checks passed
@saulmc saulmc deleted the fix/reply-attachment-rendering branch June 24, 2026 17:32
@github-actions github-actions Bot mentioned this pull request Jun 24, 2026
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.

2 participants