Improve handling of content and profiles from remote servers - #3703
Merged
Conversation
There was a problem hiding this comment.
Pull request overview
This PR hardens the WordPress ActivityPub plugin’s handling of remote-authored fields by sanitizing content at ingest (before storage) and ensuring the reader/UI renders stored values without a second decoding/unescaping pass that could re-activate inert text.
Changes:
- Add/standardize sanitization utilities for remote plain-text fields and remote HTML (posts vs comments), including removing inline styles and HTML comments (block delimiters) before storage.
- Apply the same ingest sanitization across federation paths, the Mastodon importer (including improved de-duplication), and reactions author-name rendering.
- Expand PHPUnit + React/Jest coverage to pin security-relevant invariants (no entity-decoding before
dangerouslySetInnerHTML, no style/block delimiter persistence, no duplicate imports).
Reviewed changes
Copilot reviewed 42 out of 43 changed files in this pull request and generated 6 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/phpunit/tests/includes/wp-admin/import/class-test-mastodon.php | Adds importer regression tests for sanitization and de-duplication/threading behavior. |
| tests/phpunit/tests/includes/transformer/class-test-attachment.php | Pins attachment alt-text behavior for bare < and entity decoding. |
| tests/phpunit/tests/includes/rest/class-test-post-controller.php | Expands reactions endpoint tests to cover “bare less-than” author names. |
| tests/phpunit/tests/includes/collection/class-test-remote-posts.php | Adds tests for excerpt/title sanitization and slash preservation. |
| tests/phpunit/tests/includes/collection/class-test-remote-actors.php | Adds search regression test for escaped display names. |
| tests/phpunit/tests/includes/collection/class-test-interactions.php | Adds tests ensuring comment sanitization is independent of current user and uses the comment allowlist for quotes. |
| tests/phpunit/tests/includes/class-test-sanitize.php | Adds coverage for stripping inline styles, block delimiters, and deep-encoded payload safety. |
| tests/phpunit/tests/includes/class-test-functions-comment.php | Adds tests for reaction-author name cleaning. |
| tests/phpunit/tests/includes/class-test-comment.php | Adds tests ensuring emoji unescape is scoped to ActivityPub comments and filter wiring passes comment IDs. |
| tests/phpunit/tests/includes/class-test-attachments.php | Adds tests ensuring attachment captions/alt/title are sanitized and preserve bare <. |
| templates/embed.php | Routes embed content through remote-HTML sanitization. |
| templates/emails/new-follower.php | Tightens email summary allowlist to user-description HTML plus <br>. |
| src/reactions/render.php | Uses shared reaction-author-name helper for consistent plain-text output. |
| src/app/routes/feed/inspector.tsx | Stops decoding/unescaping before dangerouslySetInnerHTML; renders plain-text title as a React child. |
| src/app/routes/feed/tests/inspector.test.tsx | Adds tests to ensure entity-encoded payloads are not revived in inspector rendering. |
| src/app/README.md | Documents the “never decode rendered fields before innerHTML” rule and why. |
| src/app/components/fields/title/index.tsx | Removes backslash-unescaping; keeps entity decoding only for React-child rendering. |
| src/app/components/fields/title/tests/index.test.tsx | Adds tests pinning safe text rendering for decoded titles. |
| src/app/components/fields/content/index.tsx | Stops decoding before rendering stored HTML; avoids double-decoding when using stripHTML(). |
| src/app/components/fields/content/tests/index.test.tsx | Adds tests preventing entity-decoding regressions and verifying safe rendering. |
| includes/wp-admin/import/class-mastodon.php | Sanitizes imported post/comment content on write; de-dupes via _source_id first and preserves reply threading. |
| includes/transformer/class-attachment.php | Ensures attachment alt text is cleaned safely and decoded for plain-text JSON output. |
| includes/rest/class-post-controller.php | Uses shared reaction-author-name helper in REST reactions output. |
| includes/functions-comment.php | Adds shared get_reaction_author_name() helper for consistent plain-text reaction author handling. |
| includes/collection/class-remote-posts.php | Sanitizes remote post title/summary as plain text, and ensures values are slashed appropriately for storage. |
| includes/collection/class-remote-actors.php | Stores remote actor names as cleaned plain text and broadens search to match escaped names. |
| includes/collection/class-interactions.php | Ensures remote comment content/author are sanitized independent of request kses state; unifies allowlist logic. |
| includes/class-sanitize.php | Introduces remote-specific sanitization APIs (plain text, HTML, comment HTML) and removes style/comments to prevent bypass. |
| includes/class-comment.php | Scopes emoji unescape to ActivityPub comments and ensures filter receives/derives the comment ID. |
| includes/class-attachments.php | Cleans remote attachment “name” before storing title/content/alt to avoid unsafe HTML persistence. |
| build/reactions/render.php | Generated build artifact updated (not reviewed). |
| build/app/routes/feed/content.asset.php | Generated build artifact updated (not reviewed). |
| .github/changelog/fix-remote-plain-text-fields | Changelog entry for remote plain-text field preservation. |
| .github/changelog/fix-remote-content-inline-styles | Changelog entry for stripping inline styles from remote content. |
| .github/changelog/fix-remote-comment-sanitization | Changelog entry for sanitizing remote replies imported as comments. |
| .github/changelog/fix-remote-block-delimiters | Changelog entry for stripping remote block markup before save. |
| .github/changelog/fix-remote-backslashes | Changelog entry for preserving backslashes from remote content. |
| .github/changelog/fix-reader-remote-content | Changelog entry for rendering stored remote content as-is in the reader. |
| .github/changelog/fix-mastodon-import-duplicates | Changelog entry for Mastodon importer de-duplication. |
| .github/changelog/fix-mastodon-import-comment-content | Changelog entry for sanitizing imported Mastodon reply comments. |
| .github/changelog/fix-mastodon-import-attachment-name | Changelog entry for sanitizing imported Mastodon media descriptions. |
| .github/changelog/fix-comment-author-emoji-scope | Changelog entry for scoping comment-author emoji behavior to ActivityPub comments. |
Suppressed comments (1)
includes/functions-comment.php:280
- html_entity_decode() here should also specify encoding (and ENT_HTML5) for consistency with the decode loop and other usages.
return \html_entity_decode( $author, ENT_QUOTES );
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
jeherve
previously approved these changes
Sep 1, 2026
jeherve
left a comment
Member
There was a problem hiding this comment.
This is looking good, should be ready to merge!
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Proposed changes:
This hardens how the plugin handles content and profile fields that come from remote servers. Titles, summaries, captions, display names, post and comment bodies are now cleaned on the way in, once, at the point where they are stored, so what is saved is already safe for the sinks that later render it. Plain-text fields are treated as plain text, rich fields keep a narrow allowlist that drops inline styles, and the same treatment is applied to the Mastodon importer and the Social Web reader.
A few fields were reaching an output sink under a wider policy than the one guarding the inbox. Those are routed through the same cleaning as everything else, so there is one policy per field rather than two.
Other information:
Testing instructions:
Changelog entry
The entries are already part of the branch.