WordPress.org

Making WordPress.org

Changeset 2085


Ignore:
Timestamp:
11/16/15 23:31:23 (2 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.