Skip to content
Snippets Groups Projects

Issue #3169693: Uploading inline image using CKEditor returns CDN URL and is then blocked by Restrict images to this site

Open Issue #3169693: Uploading inline image using CKEditor returns CDN URL and is then blocked by Restrict images to this site
2 files
+ 127
0
Compare changes
  • Side-by-side
  • Inline
Files
2
+ 111
0
<?php
namespace Drupal\cdn\Plugin\Filter;
use Drupal\Component\Utility\Html;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\filter\FilterProcessResult;
use Drupal\filter\Plugin\FilterBase;
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Provides a replacement filter for the 'Restrict images to site' filter.
* This is not registered as a plugin, and is instead set as the plugin class for the existing filter plugin, effectively replacing it.
*
* @see
*/
class CdnHtmlImageSecure extends FilterBase implements ContainerFactoryPluginInterface {
/**
* @var \Drupal\cdn\CdnSettings
*/
private $cdnSettings;
/**
* @var string
*/
private $allowedRegex;
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
$instance = new static($configuration, $plugin_id, $plugin_definition);
$instance->cdnSettings = $container->get('cdn.settings');
return $instance;
}
/**
* {@inheritdoc}
*/
public function process($text, $langcode) {
// Find the path (e.g. '/') to Drupal root.
$base_path = base_path();
$base_path_length = mb_strlen($base_path);
// Find the directory on the server where index.php resides.
$local_dir = \Drupal::root() . '/';
$html_dom = Html::load($text);
$images = $html_dom->getElementsByTagName('img');
foreach ($images as $image) {
$src = $image->getAttribute('src');
// Verify that $src starts with an allowed domain by stripping it
// This ensures that external images cannot be referenced unless served from an allowed CDN domain
$newSrc = preg_replace($this->getAllowedRegex(), '', $src);
if (mb_substr($newSrc, 0, $base_path_length) === $base_path) {
// Remove the $base_path to get the path relative to the Drupal root.
// Ensure the path refers to an actual image by prefixing the image source
// with the Drupal root and running getimagesize() on it.
$local_image_path = $local_dir . mb_substr($newSrc, $base_path_length);
$local_image_path = rawurldecode($local_image_path);
if (@getimagesize($local_image_path)) {
// The image has the right path. Erroneous images are dealt with below.
continue;
}
}
// Allow modules and themes to replace an invalid image with an error
// indicator. See filter_filter_secure_image_alter().
\Drupal::moduleHandler()->alter('filter_secure_image', $image);
}
return new FilterProcessResult(Html::serialize($html_dom));
}
/**
* {@inheritdoc}
*/
public function tips($long = FALSE) {
return $this->t('Only images hosted on this site or on a known CDN may be used in &lt;img&gt; tags.');
}
/**
* Get allowed domains as a regular expressions
*
* @return string
*/
private function getAllowedRegex() {
if (!empty($this->allowedRegex)) {
return $this->allowedRegex;
}
$request = \Drupal::request();
$host = $request->getHost();
$scheme = $request->getScheme();
$port = $request->getPort() ?: 80;
if (('http' == $scheme && $port == 80) || ('https' == $scheme && $port == 443)) {
$http_host = $host;
}
else {
$http_host = $host . ':' . $port;
}
$regexes = array_map(function ($http_host) {
return '^https?://' . preg_quote($http_host, '#');
}, array_merge([$http_host], $this->cdnSettings->getDomains()));
return $this->allowedRegex = '#' . implode('|', $regexes) . '#';
}
}
Loading