| @@ -107,8 +107,22 @@ | ||
| 107 | 107 | */ |
| 108 | 108 | private function url_to_path( string $url ): ?string { |
| 109 | 109 | $url = strtok( $url, '?' ); // Drop query (dynamic CSS handled elsewhere). |
| 110 | 110 | |
| 111 | + // LFI guard. A linked stylesheet is always a real .css file with no | |
| 112 | + // path-traversal. Reject anything else BEFORE it can touch the disk, so | |
| 113 | + // a crafted href like "/wp-config.php" or "/a/../../../etc/passwd" can | |
| 114 | + // never be read. Decode first so percent-encoded "%2e%2e" is caught too. | |
| 115 | + $path = (string) wp_parse_url( $url, PHP_URL_PATH ); | |
| 116 | + | |
| 117 | + if ( false !== strpos( rawurldecode( (string) $url ), '..' ) ) { | |
| 118 | + return null; | |
| 119 | + } | |
| 120 | + | |
| 121 | + if ( ! preg_match( '/\.css$/i', $path ) ) { | |
| 122 | + return null; | |
| 123 | + } | |
| 124 | + | |
| 111 | 125 | $content_url = content_url(); |
| 112 | 126 | |
| 113 | 127 | if ( str_starts_with( $url, $content_url ) ) { |
| 114 | 128 | return str_replace( $content_url, WP_CONTENT_DIR, $url ); |