diff -u b/core/modules/rest/src/Plugin/views/display/RestExport.php b/core/modules/rest/src/Plugin/views/display/RestExport.php --- b/core/modules/rest/src/Plugin/views/display/RestExport.php +++ b/core/modules/rest/src/Plugin/views/display/RestExport.php @@ -284,13 +284,20 @@ $header = []; $header['Content-type'] = $this->getMimeType(); - // This is "safe" markup in the sense that other than in View preview, this - // isn't actually "markup". Since there's no markup, there's no markup in - // which to perform an XSS injection. In the case of Views preview, all that - // is added is a
tag, which again cannot contain an XSS vector.
- // @todo Decide how to support non-HTML in the render API in
- // https://www.drupal.org/node/2501313.
- $output['#markup'] = SafeMarkup::set($output['#markup']);
+ if ($this->view->getRequest()->getFormat($header['Content-type']) !== 'html') {
+ // This display plugin is primarily for returning non-HTML formats.
+ // However, we still invoke the renderer to collect cacheability metadata.
+ // Because the renderer is designed for HTML rendering, it filters
+ // #markup for XSS unless it is already known to be safe, but that filter
+ // only works for HTML. Therefore, we mark the contents as safe to bypass
+ // the filter. So long as we are returning this in a non-HTML response
+ // (checked above), this is safe, because an XSS attack only works when
+ // executed by an HTML agent.
+ // @todo Decide how to support non-HTML in the render API in
+ // https://www.drupal.org/node/2501313.
+ $output['#markup'] = SafeMarkup::set($output['#markup']);
+ }
+
$response = new CacheableResponse($this->renderer->renderRoot($output), 200);
$cache_metadata = CacheableMetadata::createFromRenderArray($output);