Making WordPress.org

Changeset 2085


Ignore:
Timestamp:
11/16/2015 11:31:23 PM (8 years ago)
Author:
iandunn
Message:

Jetpack Tweaks: Sanitize Custom CSS input.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • sites/trunk/wordcamp.org/public_html/wp-content/mu-plugins/jetpack-tweaks.php

    r1682 r2085  
    7878}
    7979add_filter( 'pre_update_site_option_jetpack-network-settings', __NAMESPACE__ . '\auto_connect_new_sites', 10, 2 );
     80
     81/**
     82 * Sanitize parsed Custom CSS rules
     83 *
     84 * @import rules are stripped because they can introduce security vulnerabilities by embedding external
     85 * stylesheets that haven't been sanitized, and they also present a maintenance problem because they rely on
     86 * external resources which could go offline at any point.
     87 *
     88 * @charset rules are stripped because manipulating the charset can allow an attacker to introduce XSS
     89 * vulnerabilities by tricking the browser into interpreting the CSS as HTML.
     90 *
     91 * @param \safecss $safecss
     92 */
     93function sanitize_csstidy_parsed_rules( $safecss ) {
     94    if ( ! empty( $safecss->parser->import ) ) {
     95        update_option( 'custom_css_import_stripped', true );
     96    }
     97
     98    $safecss->parser->import  = array();
     99    $safecss->parser->charset = array();
     100}
     101add_action( 'csstidy_optimize_postparse', __NAMESPACE__ . '\sanitize_csstidy_parsed_rules' );
     102
     103/**
     104 * Notify the user that @import rules were stripped from their CSS
     105 */
     106function notify_import_rules_stripped() {
     107    global $current_screen;
     108    $relevant_screens = array( 'appearance_page_editcss', 'appearance_page_remote-css' );
     109
     110    if ( ! is_a( $current_screen, 'WP_Screen' ) || ! in_array( $current_screen->id, $relevant_screens, true ) ) {
     111        return;
     112    }
     113
     114    if ( ! get_option( 'custom_css_import_stripped' ) ) {
     115        return;
     116    }
     117
     118    delete_option( 'custom_css_import_stripped' );
     119
     120    ?>
     121
     122    <div class="notice notice-warning">
     123        <p>
     124            <?php printf(
     125                __( 'WARNING: <code>@import</code> rules were stripped for security reasons.
     126                Please use <a href="%s">the Fonts tool</a> to add web fonts, and copy/paste other stylesheets directly into your custom CSS.',
     127                'wordcamporg' ),
     128              admin_url( 'themes.php?page=wc-fonts-options' )
     129            ); ?>
     130        </p>
     131    </div>
     132
     133    <?php
     134}
     135add_action( 'admin_notices', __NAMESPACE__ . '\notify_import_rules_stripped' );
     136
     137/**
     138 * Sanitize Custom CSS subvalues
     139 *
     140 * @param \safecss $safecss
     141 */
     142function sanitize_csstidy_subvalues( $safecss ) {
     143    $safecss->sub_value = trim( $safecss->sub_value );
     144
     145    // Send any urls through our filter
     146    if ( preg_match( '!^\s*(?P<url_expression>url\s*(?P<opening_paren>\(|\\0028)(?P<parenthetical_content>.*)(?P<closing_paren>\)|\\0029))(.*)$!Dis', $safecss->sub_value, $matches ) ) {
     147        $safecss->sub_value = sanitize_urls_in_css_properties( $matches['parenthetical_content'], $safecss->property );
     148
     149        // Only replace the url([...]) portion of the sub_value so we don't
     150        // lose things like trailing commas or !important declarations.
     151        if ( $safecss->sub_value ) {
     152            $safecss->sub_value = str_replace( $matches['url_expression'], $safecss->sub_value, $matches[0] );
     153        }
     154    }
     155
     156    // Strip any expressions
     157    if ( preg_match( '!^\\s*expression!Dis', $safecss->sub_value ) ) {
     158        $safecss->sub_value = '';
     159    }
     160}
     161add_action( 'csstidy_optimize_subvalue', __NAMESPACE__ . '\sanitize_csstidy_subvalues' );
     162
     163/**
     164 * Sanitize URLs used in CSS properties
     165 *
     166 * @param string $url
     167 * @param string $property
     168 *
     169 * @return string
     170 */
     171function sanitize_urls_in_css_properties( $url, $property ) {
     172    $allowed_properties = array( 'background', 'background-image', 'border-image', 'content', 'cursor', 'list-style', 'list-style-image' );
     173    $allowed_protocols  = array( 'http', 'https' );
     174
     175    // Clean up the string
     176    $url = trim( $url, "' \" \r \n" );
     177
     178    // Check against whitelist for properties allowed to have URL values
     179    if ( ! in_array( trim( $property ), $allowed_properties, true ) ) {
     180        // trim() is because multiple properties with the same name are stored with
     181        // additional trailing whitespace so they don't overwrite each other in the hash.
     182        return '';
     183    }
     184
     185    $url = wp_kses_bad_protocol_once( $url, $allowed_protocols );
     186
     187    if ( empty( $url ) ) {
     188        return '';
     189    }
     190
     191    return "url('" . str_replace( "'", "\\'", $url ) . "')";
     192}
Note: See TracChangeset for help on using the changeset viewer.