From 42fd44a7f659955bd1d479a37ec74ff2f61031b2 Mon Sep 17 00:00:00 2001
From: Andrei Draganescu <andrei.draganescu@automattic.com>
Date: Tue, 3 Dec 2019 15:31:56 +0200
Subject: [PATCH] refreshes @Cybr 's patch

---
 src/wp-admin/edit-comments.php                |  3 ++
 .../includes/class-wp-comments-list-table.php |  3 +-
 src/wp-includes/formatting.php                | 47 +++++++++++++++++++
 3 files changed, 52 insertions(+), 1 deletion(-)

diff --git a/src/wp-admin/edit-comments.php b/src/wp-admin/edit-comments.php
index 0bd3b7a09d..0abf1ac794 100644
--- a/src/wp-admin/edit-comments.php
+++ b/src/wp-admin/edit-comments.php
@@ -16,6 +16,9 @@
 	);
 }
 
+//* TODO determine whether to move globally (i.e. filter "pre_comment_content"). Ticket #40916
+add_filter( 'comment_text', 'wp_unbind_links', 35 );
+
 $wp_list_table = _get_list_table( 'WP_Comments_List_Table' );
 $pagenum       = $wp_list_table->get_pagenum();
 
diff --git a/src/wp-admin/includes/class-wp-comments-list-table.php b/src/wp-admin/includes/class-wp-comments-list-table.php
index 1020aab127..895a10625c 100644
--- a/src/wp-admin/includes/class-wp-comments-list-table.php
+++ b/src/wp-admin/includes/class-wp-comments-list-table.php
@@ -833,7 +833,8 @@ public function column_author( $comment ) {
 		comment_author( $comment );
 		echo '</strong><br />';
 		if ( ! empty( $author_url_display ) ) {
-			printf( '<a href="%s">%s</a><br />', esc_url( $author_url ), esc_html( $author_url_display ) );
+			// Ticket #40916
+ 			printf( '<a href="%s" rel="noopener noreferrer" target="_blank">%s</a><br />', esc_url( $author_url ), esc_html( $author_url_display ) );
 		}
 
 		if ( $this->user_can ) {
diff --git a/src/wp-includes/formatting.php b/src/wp-includes/formatting.php
index f7d248294c..790c655f05 100644
--- a/src/wp-includes/formatting.php
+++ b/src/wp-includes/formatting.php
@@ -3262,6 +3262,53 @@ function wp_remove_targeted_link_rel_filters() {
 	};
 }
 
+/**
+ * Adds target="_blank" and rel="noreferrer noopener" strings to all HTML A elements
+ * in content.
+ *
+ * Removes current target and rel attributes prior.
+ *
+ * @since TODO Ticket #40916
+ *
+ * @param string $text Content that may contain HTML A elements.
+ * @return string Converted content.
+ */
+function wp_unbind_links( $text ) {
+       // Ticket #40916 Filter usage to be determined...
+       if ( 'pre_comment_content' === current_filter() ) {
+               // This is a pre save filter, so text is already escaped.
+               $text = stripslashes( $text );
+               $text = preg_replace_callback( '|<a (.+?)>|i', 'wp_unbind_links_callback', $text );
+               return wp_slash( $text );
+       }
+       return preg_replace_callback( '|<a (.+?)>|i', 'wp_unbind_links_callback', $text );
+}
+
+/**
+* Callback to add target="_blank" and rel="noreferrer noopener" string to HTML A element.
+*
+* Will remove already existing rel="noreferrer", rel='noreferrer', rel="noopener"
+* and rel='noopener' from the string to prevent from invalidating (X)HTML.
+*
+* @since TODO Ticket #40916
+*
+* @param array $matches Single Match
+* @return string HTML A Element with target="_blank" and rel="noreferrer noopener".
+*/
+function wp_unbind_links_callback( $matches ) {
+       $text = $matches[1];
+       /**
+        * Captures rel and target attributes with content.
+        * Closing/opening tag aware, i.e. it captures:
+        * rel='nofollow"', rel="nofollow", rel=nofollow, rel="nofollow noreferrer"
+        */
+       $regex = '/(target|rel)\=(\'|"?)((?:.(?!\2?\s+(?:\S+)=|[>]\2))+.)\2?/i';
+       //* TODO This will add stray whitespaces... It's not important enough to clean up.
+       $text = preg_replace( $regex, '', $text );
+       return sprintf( '<a %s target=_blank rel="noopener noreferrer">', $text );
+}
+   
+
 /**
  * Convert one smiley code to the icon graphic file equivalent.
  *
