Plugin Directory

Changeset 3296463


Ignore:
Timestamp:
05/19/2025 12:50:17 PM (10 months ago)
Author:
babbardel
Message:

Security patch XXE/SSRF

Location:
category-icon/trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • category-icon/trunk/category-icon.php

    r3167145 r3296463  
    44 * Plugin URI:  http://pixelgrade.com
    55 * Description: Easily attach an icon and/or an image to a category, tag or any other taxonomy term.
    6  * Version: 1.0.1
     6 * Version: 1.0.2
    77 * Author: Pixelgrade
    88 * Author URI: http://pixelgrade.com
     
    1313 * Domain Path: /lang
    1414 * Requires at least: 4.9.19
    15  * Tested up to:      6.6.2
     15 * Tested up to:      6.8.1
    1616 * Requires PHP:      5.6.40
    1717 */
     
    3131    protected $plugin_baseurl = null;
    3232    protected $plugin_screen_hook_suffix = null;
    33     protected $version = '1.0.0';
     33    protected $version = '1.0.2';
    3434    protected $plugin_slug = 'category-icon';
    3535    protected $plugin_key = 'category-icon';
     
    120120
    121121    /**
    122      * Sanitizes SVG content by removing <script> elements and any unsafe attributes.
    123      * This helps mitigate potential XSS vulnerabilities from SVG uploads.
     122     * Sanitize raw SVG markup before it is stored in the uploads directory.
    124123     *
    125      * @param string $svg_content The raw SVG content to be sanitized.
    126      * @return string The sanitized SVG content.
    127      */
    128     private function sanitize_svg_content($svg_content) {
    129         // Create a new DOMDocument instance to parse the SVG XML content
    130         $dom = new DOMDocument();
    131        
    132         // Suppress XML parsing errors for invalid SVG formats
    133         libxml_use_internal_errors(true);
    134        
    135         // Load the SVG content into the DOMDocument for manipulation
    136         $dom->loadXML($svg_content, LIBXML_NOENT | LIBXML_DTDLOAD);
    137        
    138         // Clear any XML parsing errors
    139         libxml_clear_errors();
    140        
    141         // Step 1: Remove any <script> elements, which can execute JavaScript
    142         $scripts = $dom->getElementsByTagName('script');
    143         while ($scripts->length > 0) {
    144             $scripts->item(0)->parentNode->removeChild($scripts->item(0));
    145         }
    146 
    147         // Step 2: Remove unsafe attributes that could contain JavaScript (like onclick, onload)
    148         $xpath = new DOMXPath($dom);
    149         foreach ($xpath->query('//@*') as $attr) {
    150             if (stripos($attr->name, 'on') === 0 || stripos($attr->value, 'javascript:') === 0) {
    151                 $attr->parentNode->removeAttribute($attr->name);
    152             }
    153         }
    154 
    155         // Return the sanitized SVG content as XML
    156         return $dom->saveXML();
    157     }
     124     * The function loads the SVG into a DOMDocument *without* expanding external
     125     * entities or allowing network I/O, preventing XXE/SSRF.  After a successful
     126     * parse it walks the document and strips any element or attribute that is not
     127     * explicitly allow-listed.
     128     *
     129     * @param  string $svg_content Raw SVG file contents.
     130     * @return string              Clean SVG (empty string on failure or if unsafe).
     131     */
     132    private function sanitize_svg_content( $svg_content ) {
     133        // Collect libxml errors internally – we decide what to do with them.
     134        libxml_use_internal_errors( true );
     135
     136        $dom       = new DOMDocument();
     137        $load_opts = LIBXML_NONET        // deny all network access (blocks XXE/SSRF)
     138                    | LIBXML_NOERROR     // suppress warnings
     139                    | LIBXML_NOWARNING;  // suppress notices
     140
     141        // PHP < 8.0 still allows the entity loader unless we disable it globally.
     142        if ( PHP_VERSION_ID < 80000 ) {
     143            $previous_loader_state = libxml_disable_entity_loader( true ); // phpcs:ignore PHPCompatibility.Extensions.RemovedFunctions
     144        }
     145
     146        // Abort if XML is invalid *or* references external entities.
     147        if ( ! $dom->loadXML( $svg_content, $load_opts ) ) {
     148            if ( PHP_VERSION_ID < 80000 ) {
     149                libxml_disable_entity_loader( $previous_loader_state ); // restore
     150            }
     151            return ''; // fail closed
     152        }
     153
     154        // Restore global entity loader setting for legacy PHP versions.
     155        if ( PHP_VERSION_ID < 80000 ) {
     156            libxml_disable_entity_loader( $previous_loader_state );
     157        }
     158
     159        /* --------------------------------------------------------------------
     160        * Phase 2 – Allow-list-based cleanup
     161        * ------------------------------------------------------------------ */
     162
     163        $allowed_tags = array(
     164            'svg', 'g', 'path', 'polygon', 'circle', 'ellipse', 'line',
     165            'polyline', 'rect', 'use', 'defs', 'linearGradient',
     166            'radialGradient', 'stop', 'clipPath', 'mask', 'title', 'desc',
     167        );
     168
     169        $allowed_attrs = array(
     170            'id', 'class', 'fill', 'stroke', 'stroke-width', 'stroke-linecap',
     171            'stroke-linejoin', 'stroke-miterlimit', 'stroke-dasharray',
     172            'stroke-dashoffset', 'transform', 'd', 'points', 'x', 'y', 'cx',
     173            'cy', 'r', 'rx', 'ry', 'width', 'height', 'x1', 'y1', 'x2', 'y2',
     174            'gradientUnits', 'gradientTransform', 'offset', 'stop-color',
     175            'stop-opacity', 'viewBox', 'xmlns', 'version',
     176            'preserveAspectRatio',
     177        );
     178
     179        $xpath = new DOMXPath( $dom );
     180
     181        /** @var DOMElement $node */
     182        foreach ( $xpath->query( '//*' ) as $node ) {
     183            // Drop any element that is not allowed.
     184            if ( ! in_array( $node->nodeName, $allowed_tags, true ) ) {
     185                $node->parentNode->removeChild( $node );
     186                continue;
     187            }
     188
     189            // Prune disallowed attributes.
     190            if ( $node->hasAttributes() ) {
     191                /** @var DOMAttr $attr */
     192                foreach ( iterator_to_array( $node->attributes ) as $attr ) {
     193                    if ( ! in_array( $attr->nodeName, $allowed_attrs, true ) ) {
     194                        $node->removeAttributeNode( $attr );
     195                    }
     196                }
     197            }
     198        }
     199
     200        // Return compact, sanitized SVG.
     201        $dom->formatOutput = false;
     202        return $dom->saveXML( $dom->documentElement );
     203    }
     204
    158205   
    159206   
  • category-icon/trunk/readme.txt

    r3167148 r3296463  
    33Tags: category, taxonomy, term, icon, image
    44Requires at least: 4.9.19
    5 Tested up to: 6.6.2
     5Tested up to: 6.8.1
    66Requires PHP: 5.6.40
    7 Stable tag: 1.0.1
     7Stable tag: 1.0.2
    88License: GPLv2 or later
    99License URI: http://www.gnu.org/licenses/gpl-2.0.html
     
    2626
    2727== Changelog ==
     28
     29= 1.0.2 =
     30* Security: patched blind XXE/SSRF in SVG upload flow – external entity loading and network access are now disabled; malformed SVGs are rejected (fail-closed).
    2831
    2932= 1.0.1 =
Note: See TracChangeset for help on using the changeset viewer.