Make WordPress Core


Ignore:
Timestamp:
10/16/2018 04:12:21 AM (6 years ago)
Author:
peterwilsoncc
Message:

Formatting: Add pre-save content filter to make target=_blank always secure.

Props notnownikki, iseulde, azaozz.
Merges [42770] to the 5.0 branch.
Fixes #43187.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • branches/5.0/src/wp-includes/formatting.php

    r43444 r43732  
    27722772    }
    27732773    return "<a $text rel=\"$rel\">";
     2774}
     2775
     2776/**
     2777 * Adds rel noreferrer and noopener to all HTML A elements that have a target.
     2778 *
     2779 * @param string $text Content that may contain HTML A elements.
     2780 * @return string Converted content.
     2781 */
     2782function wp_targeted_link_rel( $text ) {
     2783    // Don't run (more expensive) regex if no links with targets.
     2784    if ( stripos( $text, 'target' ) !== false && stripos( $text, '<a ' ) !== false ) {
     2785        $text = preg_replace_callback( '|<a\s([^>]*target\s*=[^>]*)>|i', 'wp_targeted_link_rel_callback', $text );
     2786    }
     2787
     2788    return $text;
     2789}
     2790
     2791/**
     2792 * Callback to add rel="noreferrer noopener" string to HTML A element.
     2793 *
     2794 * Will not duplicate existing noreferrer and noopener values
     2795 * to prevent from invalidating the HTML.
     2796 *
     2797 * @param array $matches Single Match
     2798 * @return string HTML A Element with rel noreferrer noopener in addition to any existing values
     2799 */
     2800function wp_targeted_link_rel_callback( $matches ) {
     2801    $link_html = $matches[1];
     2802    $rel_match = array();
     2803
     2804    /**
     2805     * Filters the rel values that are added to links with `target` attribute.
     2806     *
     2807     * @since 5.0.0
     2808     *
     2809     * @param string The rel values.
     2810     * @param string $link_html The matched content of the link tag including all HTML attributes.
     2811     */
     2812    $rel = apply_filters( 'wp_targeted_link_rel', 'noopener noreferrer', $link_html );
     2813
     2814    // Value with delimiters, spaces around are optional.
     2815    $attr_regex = '|rel\s*=\s*?(\\\\{0,1}["\'])(.*?)\\1|i';
     2816    preg_match( $attr_regex, $link_html, $rel_match );
     2817
     2818    if ( empty( $rel_match[0] ) ) {
     2819        // No delimiters, try with a single value and spaces, because `rel =  va"lue` is totally fine...
     2820        $attr_regex = '|rel\s*=(\s*)([^\s]*)|i';
     2821        preg_match( $attr_regex, $link_html, $rel_match );
     2822    }
     2823
     2824    if ( ! empty( $rel_match[0] ) ) {
     2825        $parts = preg_split( '|\s+|', strtolower( $rel_match[2] ) );
     2826        $parts = array_map( 'esc_attr', $parts );
     2827        $needed = explode( ' ', $rel );
     2828        $parts = array_unique( array_merge( $parts, $needed ) );
     2829        $delimiter = trim( $rel_match[1] ) ? $rel_match[1] : '"';
     2830        $rel = 'rel=' . $delimiter . trim( implode( ' ', $parts ) ) . $delimiter;
     2831        $link_html = str_replace( $rel_match[0], $rel, $link_html );
     2832    } else {
     2833        $link_html .= " rel=\"$rel\"";
     2834    }
     2835
     2836    return "<a $link_html>";
    27742837}
    27752838
Note: See TracChangeset for help on using the changeset viewer.