diff --git a/src/Sanitize.php b/src/Sanitize.php index 819071391..9a5aca0cd 100644 --- a/src/Sanitize.php +++ b/src/Sanitize.php @@ -677,7 +677,7 @@ public function replace_urls(DOMDocument $document, string $tag, $attributes) $value = $this->registry->call(Misc::class, 'absolutize_url', [$element->getAttribute($attribute), $this->base]); if ($value !== false) { // Block disallowed URI protocols (e.g. javascript:), otherwise force HTTPS where applicable - $value = ($this->disallowed_uri_schemes !== [] && !$this->is_allowed_scheme($value)) + $value = ($this->disallowed_uri_schemes !== [] && !$this->is_allowed_uri($tag, $attribute, $value)) ? 'unsafe:' . $value : $this->https_url($value); $element->setAttribute($attribute, $value); @@ -779,6 +779,36 @@ public function is_allowed_scheme(string $uri): bool return !in_array($scheme, $this->disallowed_uri_schemes, true); } + /** + * Elements/attributes on which a `data:` URI carrying an image is still allowed + * even when the `data` scheme is otherwise disallowed. Such URIs are inert + * (an `` never executes script), whereas `data:` on navigational or framing + * attributes (``, `', + '', + ['javascript', 'data'], + ]; + + yield 'data scheme case insensitive' => [ + 'Click me', + 'Click me', + ['javascript', 'data'], + ]; + + yield 'data image kept on img src' => [ + '', + '', + ['javascript', 'data'], + ]; + + yield 'data non-image blocked on img src' => [ + '', + '', + ['javascript', 'data'], + ]; + + yield 'data image blocked on href' => [ + 'Click me', + 'Click me', + ['javascript', 'data'], + ]; + + yield 'data scheme unaffected when not disallowed' => [ + 'Click me', + 'Click me', + ['javascript'], + ]; } /**