Repository navigation
Comments: Allow the Notes @mention chip markup in comment content (kses) #12503
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Closed
adamsilverstein
wants to merge
27
commits into
WordPress:trunk
from
adamsilverstein:add/notes-mention-kses
Closed
Changes from 24 commits
Commits
Show all changes
27 commits
Select commit
Hold shift + click to select a range
7cc1245
Comments: Allow note mention attributes in comment content.
adamsilverstein 8e48638
Add @ticket annotations for Trac #65622 to the new kses tests.
adamsilverstein 9e7ecb2
Scope the note mention kses allowance to note comments.
adamsilverstein 91f701d
Align the note mention kses allowance with the span markup.
adamsilverstein f74c68b
Comments: Carry the note mention user ID in a class instead of a data…
adamsilverstein e94266c
Align the note mention kses allowance with the link markup.
adamsilverstein d5b62af
Merge branch 'trunk' into add/notes-mention-kses
adamsilverstein e31873f
Merge remote-tracking branch 'origin/trunk' into add/notes-mention-kses
adamsilverstein d237dd1
Update src/wp-includes/kses.php
adamsilverstein c091f34
Apply suggestions from code review
adamsilverstein d5d7895
Merge branch 'trunk' into add/notes-mention-kses
adamsilverstein d68a800
Improve typing of get_mention_commentdata helper method
westonruter 0a9c8d0
Remove unnecessary filter removal
westonruter d09d32e
Narrow type for comment_type param
westonruter a2d01f0
Fix phpcs
westonruter 69ec26b
Comments: Switch the note mention kses allowance to the span chip markup
adamsilverstein 5000cb4
Use assertEqualHTML for the class-attribute-removal assertion
adamsilverstein 1181a96
Port review refinements from the Gutenberg PR
adamsilverstein 21d00fb
Remove case-sensitive span substring bailout from mention class reduc…
adamsilverstein 659c556
Fix type for context param
westonruter 9cd3cf0
Remove unnecessary filter removals
westonruter 4a1a446
Add types to get_mention_commentdata helper
westonruter 0ca0786
Merge branch 'trunk' into add/notes-mention-kses
adamsilverstein f3130cc
Merge branch 'add/notes-mention-kses' of https://github.com/adamsilve…
westonruter 32f362b
Remove one more unnecessary filter removal
westonruter 1bef13b
Correct the context param
westonruter 82e63e6
Merge branch 'trunk' into add/notes-mention-kses
adamsilverstein File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
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
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -536,6 +536,230 @@ public function test_wp_kses_allowed_html() { | |
| $this->assertSame( $allowedtags, wp_kses_allowed_html( 'data' ) ); | ||
| } | ||
|
|
||
| /** | ||
| * Tests that the comment content context allows only the mention span beyond the defaults. | ||
| * | ||
| * @ticket 65622 | ||
| * | ||
| * @covers ::_wp_kses_allow_note_mention_span | ||
| */ | ||
| public function test_wp_kses_allowed_html_pre_comment_content_allows_only_the_mention_span() { | ||
| global $allowedtags; | ||
|
|
||
| $allowed = wp_kses_allowed_html( 'pre_comment_content' ); | ||
|
|
||
| $this->assertSame( | ||
| array( 'class' => true ), | ||
| $allowed['span'], | ||
| 'The mention span should be allowed in comment content.' | ||
| ); | ||
|
|
||
| unset( $allowed['span'] ); | ||
| $this->assertSame( | ||
| $allowedtags, | ||
| $allowed, | ||
| 'Nothing beyond the mention span should be allowed on top of the default comment tags.' | ||
| ); | ||
| } | ||
|
|
||
| /** | ||
| * Tests that a note mention survives content sanitization of a `note` comment. | ||
| * | ||
| * @ticket 65622 | ||
| * | ||
| * @covers ::_wp_kses_allow_note_mention_span | ||
| * @covers ::_wp_kses_sanitize_note_mention_classes | ||
| */ | ||
| public function test_note_mention_markup_survives_note_content_sanitization() { | ||
| add_filter( 'pre_comment_content', 'wp_filter_kses' ); | ||
|
|
||
| $content = 'Hello <span class="wp-note-mention user-2">@admin</span>!'; | ||
| $filtered = wp_filter_comment( wp_slash( $this->get_mention_commentdata( 'note', $content ) ) ); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Gotta love that this content is slashed! |
||
|
|
||
| remove_filter( 'pre_comment_content', 'wp_filter_kses' ); | ||
|
|
||
| $this->assertSame( $content, wp_unslash( $filtered['comment_content'] ) ); | ||
| } | ||
|
|
||
| /** | ||
| * Tests that the mention markup also survives in regular comment content. | ||
| * | ||
| * The allowance is always on rather than scoped per comment type: the | ||
| * mention markup is inert, so uniform sanitization avoids stateful | ||
| * arming and disarming of kses filters around each note write. | ||
| * | ||
| * @ticket 65622 | ||
| * | ||
| * @covers ::_wp_kses_allow_note_mention_span | ||
| * @covers ::_wp_kses_sanitize_note_mention_classes | ||
| */ | ||
| public function test_note_mention_markup_survives_regular_comment_content_sanitization() { | ||
| add_filter( 'pre_comment_content', 'wp_filter_kses' ); | ||
|
|
||
| $content = 'Hello <span class="wp-note-mention user-2">@admin</span>!'; | ||
| $filtered = wp_filter_comment( wp_slash( $this->get_mention_commentdata( 'comment', $content ) ) ); | ||
|
|
||
| remove_filter( 'pre_comment_content', 'wp_filter_kses' ); | ||
|
|
||
| $this->assertSame( $content, wp_unslash( $filtered['comment_content'] ) ); | ||
| } | ||
|
|
||
| /** | ||
| * Tests that span classes are reduced to the two mention tokens. | ||
| * | ||
| * @ticket 65622 | ||
| * | ||
| * @covers ::_wp_kses_sanitize_note_mention_classes | ||
| */ | ||
| public function test_note_mention_span_classes_are_reduced_to_the_mention_tokens() { | ||
| add_filter( 'pre_comment_content', 'wp_filter_kses' ); | ||
| $content = 'Hello <span class="wp-note-mention user-2 is-destructive components-button">@admin</span>!'; | ||
| $filtered = wp_filter_comment( wp_slash( $this->get_mention_commentdata( 'note', $content ) ) ); | ||
|
|
||
| $this->assertSame( | ||
| 'Hello <span class="wp-note-mention user-2">@admin</span>!', | ||
| wp_unslash( $filtered['comment_content'] ), | ||
| 'Class tokens beyond `wp-note-mention` and `user-N` should be stripped from spans.' | ||
| ); | ||
|
westonruter marked this conversation as resolved.
|
||
| } | ||
|
|
||
| /** | ||
| * Tests that class tokens are reduced on spans regardless of tag-name casing. | ||
| * | ||
| * kses preserves tag-name casing, so the class reduction must match `SPAN` | ||
| * case-insensitively rather than bail on a `<span` substring check. | ||
| * | ||
| * @ticket 65622 | ||
| * | ||
| * @covers ::_wp_kses_sanitize_note_mention_classes | ||
| */ | ||
| public function test_note_mention_class_tokens_are_reduced_on_uppercase_span_tags() { | ||
| add_filter( 'pre_comment_content', 'wp_filter_kses' ); | ||
| $content = 'Hello <SPAN class="wp-note-mention user-2 is-destructive">@admin</SPAN>!'; | ||
| $filtered = wp_filter_comment( wp_slash( $this->get_mention_commentdata( 'note', $content ) ) ); | ||
|
|
||
| $this->assertEqualHTML( | ||
| 'Hello <span class="wp-note-mention user-2">@admin</span>!', | ||
| wp_unslash( $filtered['comment_content'] ), | ||
| '<body>', | ||
| 'Class tokens should be reduced on spans regardless of tag-name casing.' | ||
| ); | ||
| } | ||
|
|
||
| /** | ||
| * Tests that the class attribute is removed when no mention tokens remain. | ||
| * | ||
| * @ticket 65622 | ||
| * | ||
| * @covers ::_wp_kses_sanitize_note_mention_classes | ||
| */ | ||
| public function test_note_mention_class_attribute_removed_when_no_tokens_remain() { | ||
| add_filter( 'pre_comment_content', 'wp_filter_kses' ); | ||
| $content = 'Hello <span class="is-destructive user-0 user-x wp-note-mention-foo">there</span>!'; | ||
| $filtered = wp_filter_comment( wp_slash( $this->get_mention_commentdata( 'comment', $content ) ) ); | ||
|
|
||
| // Markup-equivalence assertion: the HTML API's whitespace handling | ||
| // when removing the final attribute is not part of its contract. | ||
| $this->assertEqualHTML( | ||
| 'Hello <span>there</span>!', | ||
| wp_unslash( $filtered['comment_content'] ), | ||
| '<body>', | ||
| 'A span with no valid mention tokens should lose its class attribute entirely.' | ||
| ); | ||
| } | ||
|
|
||
| /** | ||
| * Tests that only the `class` attribute is allowed on mention spans. | ||
| * | ||
| * @ticket 65622 | ||
| * | ||
| * @covers ::_wp_kses_allow_note_mention_span | ||
| */ | ||
| public function test_note_mention_allows_only_class_on_mention_spans() { | ||
| add_filter( 'pre_comment_content', 'wp_filter_kses' ); | ||
| $content = 'Hello <span class="wp-note-mention user-2" data-user-id="2" onclick="alert(1)" style="color:red" id="mention">@admin</span>!'; | ||
| $filtered = wp_filter_comment( wp_slash( $this->get_mention_commentdata( 'note', $content ) ) ); | ||
|
|
||
| $this->assertSame( | ||
| 'Hello <span class="wp-note-mention user-2">@admin</span>!', | ||
| wp_unslash( $filtered['comment_content'] ), | ||
| 'Attributes beyond `class` should be stripped from spans.' | ||
| ); | ||
| } | ||
|
|
||
| /** | ||
| * Tests that `class` is still stripped from links in comment content. | ||
| * | ||
| * @ticket 65622 | ||
| * | ||
| * @covers ::_wp_kses_allow_note_mention_span | ||
| */ | ||
| public function test_class_is_still_stripped_from_links_in_comment_content() { | ||
| add_filter( 'pre_comment_content', 'wp_filter_kses' ); | ||
|
|
||
| /* | ||
| * The href is external to the test site so that wp_rel_ugc() - which | ||
| * applies to notes like any other comment - deterministically appends | ||
| * `rel="nofollow ugc"`. | ||
| */ | ||
| $content = 'Hello <a class="wp-note-mention user-2" href="https://example.com/author/admin/">@admin</a>!'; | ||
| $filtered = wp_filter_comment( wp_slash( $this->get_mention_commentdata( 'note', $content ) ) ); | ||
|
|
||
| $this->assertSame( | ||
| 'Hello <a href="https://example.com/author/admin/" rel="nofollow ugc">@admin</a>!', | ||
| wp_unslash( $filtered['comment_content'] ), | ||
| 'The class allowance is scoped to spans; links keep the default sanitization.' | ||
| ); | ||
| } | ||
|
|
||
| /** | ||
| * Tests that the class reduction is skipped while the restrictive comment kses is inactive. | ||
| * | ||
| * Users with `unfiltered_html` are filtered through `wp_filter_post_kses` | ||
| * (or not at all), where arbitrary classes are permitted; the mention | ||
| * class reduction must not narrow what they can post. | ||
| * | ||
| * @ticket 65622 | ||
| * | ||
| * @covers ::_wp_kses_sanitize_note_mention_classes | ||
| */ | ||
| public function test_note_mention_class_reduction_skipped_when_restrictive_kses_is_inactive() { | ||
| // kses_init() hooks wp_filter_kses by default in the test | ||
| // environment, so detach it to simulate the unfiltered_html setup. | ||
| // The test framework restores filters after each test. | ||
| remove_filter( 'pre_comment_content', 'wp_filter_kses' ); | ||
|
|
||
| $content = 'Hello <span class="components-button is-destructive">there</span>!'; | ||
|
|
||
| $this->assertSame( | ||
| wp_slash( $content ), | ||
| _wp_kses_sanitize_note_mention_classes( wp_slash( $content ) ), | ||
| 'Span classes should be left untouched when wp_filter_kses is not active.' | ||
| ); | ||
| } | ||
|
|
||
| /** | ||
| * Builds a complete commentdata array for wp_filter_comment(). | ||
| * | ||
| * @param 'note'|'comment' $comment_type The comment type. | ||
| * @param string $content The comment content. | ||
| * @return array{ | ||
| * comment_content: string, | ||
| * ... | ||
| * } | ||
| */ | ||
| private function get_mention_commentdata( string $comment_type, string $content ): array { | ||
| return array( | ||
| 'comment_content' => $content, | ||
| 'comment_type' => $comment_type, | ||
| 'comment_author' => 'admin', | ||
| 'comment_author_IP' => '127.0.0.1', | ||
| 'comment_author_url' => 'http://example.org', | ||
| 'comment_author_email' => 'admin@example.org', | ||
| 'comment_agent' => '', | ||
| ); | ||
| } | ||
|
|
||
| public function test_hyphenated_tag() { | ||
| $content = '<hyphenated-tag attribute="value" otherattribute="value2">Alot of hyphens.</hyphenated-tag>'; | ||
| $custom_tags = array( | ||
|
|
||
Oops, something went wrong.
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.
Uh oh!
There was an error while loading. Please reload this page.