-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathSvgSupport.php
More file actions
110 lines (101 loc) · 3.86 KB
/
Copy pathSvgSupport.php
File metadata and controls
110 lines (101 loc) · 3.86 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
<?php
/*
Plugin Name: SvgSupport
Description: Adds SVG support, securely.
Version: 0.1
Author: Shorix / 0xnoid
Author URI: https://github.com/0xnoid
Plugin URI: https://github.com/YatagarasuIndustries/SvgSupport
License: GPLv3
*/
/**
* Allow SVG and SVGZ
*
* @param array $mimes Existing allowed MIME types.
* @return array Modified MIME types including SVG and SVGZ.
*/
function csu_add_svg_mime_types( $mimes ) {
$mimes['svg'] = 'image/svg+xml';
$mimes['svgz'] = 'image/svg+xml';
return $mimes;
}
add_filter( 'upload_mimes', 'csu_add_svg_mime_types' );
/**
* Sanitize SVG uploads.
*
* This function reads the uploaded file, loads its XML content,
* removes potential security risks (like script elements,
* foreignObject elements, inline event handlers, and dangerous href attributes),
* and then rewrites the sanitized content back to the temporary file.
*
* @param array $file File upload array.
* @return array|WP_Error The modified file array or WP_Error if the file is invalid.
*/
function csu_sanitize_svg_upload( $file ) {
$extension = strtolower( pathinfo( $file['name'], PATHINFO_EXTENSION ) );
// Target SVGs only
if ( $extension === 'svg' ) {
$svg = file_get_contents( $file['tmp_name'] );
if ( false === $svg ) {
return new WP_Error( 'svg_error', __( 'Unable to read the SVG file.', 'custom-svg-uploader' ) );
}
libxml_use_internal_errors( true );
$dom = new DOMDocument();
// Disable network access while parsing
if ( ! $dom->loadXML( $svg, LIBXML_NONET ) ) {
libxml_clear_errors();
return new WP_Error( 'svg_error', __( 'Invalid SVG file.', 'custom-svg-uploader' ) );
}
// Remove <script> tags.
$scripts = $dom->getElementsByTagName( 'script' );
for ( $i = $scripts->length - 1; $i >= 0; $i-- ) {
$script = $scripts->item( $i );
$script->parentNode->removeChild( $script );
}
// Remove <foreignObject> tags.
$foreignObjects = $dom->getElementsByTagName( 'foreignObject' );
for ( $i = $foreignObjects->length - 1; $i >= 0; $i-- ) {
$foreign = $foreignObjects->item( $i );
$foreign->parentNode->removeChild( $foreign );
}
// Remove inline event handlers from elements.
$xpath = new DOMXPath( $dom );
$nodes = $xpath->query( '//*[@*[starts-with(name(), "on")]]' );
foreach ( $nodes as $node ) {
$attributes = $node->attributes;
$to_remove = [];
foreach ( $attributes as $attr ) {
if ( strpos( $attr->name, 'on' ) === 0 ) {
$to_remove[] = $attr->name;
}
}
foreach ( $to_remove as $attr_name ) {
$node->removeAttribute( $attr_name );
}
}
// Remove JavaScript references in href attributes.
$nodesWithHref = $xpath->query( '//*[@href or @xlink:href]' );
foreach ( $nodesWithHref as $node ) {
if ( $node->hasAttribute( 'href' ) ) {
$hrefVal = $node->getAttribute( 'href' );
if ( stripos( $hrefVal, 'javascript:' ) === 0 ) {
$node->removeAttribute( 'href' );
}
}
if ( $node->hasAttribute( 'xlink:href' ) ) {
$xlinkVal = $node->getAttribute( 'xlink:href' );
if ( stripos( $xlinkVal, 'javascript:' ) === 0 ) {
$node->removeAttribute( 'xlink:href' );
}
}
}
// Retrieve the sanitized SVG content.
$sanitized_svg = $dom->saveXML();
if ( $sanitized_svg ) {
file_put_contents( $file['tmp_name'], $sanitized_svg );
}
libxml_clear_errors();
}
return $file;
}
add_filter( 'wp_handle_upload_prefilter', 'csu_sanitize_svg_upload' );