3 namespace Drupal\Tests\filter\Functional;
5 use Drupal\comment\Tests\CommentTestTrait;
6 use Drupal\Core\StreamWrapper\PublicStream;
7 use Drupal\filter\Entity\FilterFormat;
8 use Drupal\Tests\BrowserTestBase;
9 use Drupal\Tests\TestFileCreationTrait;
12 * Tests restriction of IMG tags in HTML input.
16 class FilterHtmlImageSecureTest extends BrowserTestBase {
19 use TestFileCreationTrait;
26 public static $modules = ['filter', 'node', 'comment'];
29 * An authenticated user.
31 * @var \Drupal\user\UserInterface
35 protected function setUp() {
38 // Setup Filtered HTML text format.
39 $filtered_html_format = FilterFormat::create([
40 'format' => 'filtered_html',
41 'name' => 'Filtered HTML',
46 'allowed_html' => '<img src testattribute> <a>',
52 'filter_html_image_secure' => [
57 $filtered_html_format->save();
60 $this->webUser = $this->drupalCreateUser([
64 'skip comment approval',
65 $filtered_html_format->getPermissionName(),
67 $this->drupalLogin($this->webUser);
69 // Setup a node to comment and test on.
70 $this->drupalCreateContentType(['type' => 'page', 'name' => 'Basic page']);
71 // Add a comment field.
72 $this->addDefaultCommentField('node', 'page');
73 $this->node = $this->drupalCreateNode();
77 * Tests removal of images having a non-local source.
79 public function testImageSource() {
82 $public_files_path = PublicStream::basePath();
84 $http_base_url = preg_replace('/^https?/', 'http', $base_url);
85 $https_base_url = preg_replace('/^https?/', 'https', $base_url);
86 $files_path = base_path() . $public_files_path;
87 $csrf_path = $public_files_path . '/' . implode('/', array_fill(0, substr_count($public_files_path, '/') + 1, '..'));
89 $druplicon = 'core/misc/druplicon.png';
90 $red_x_image = base_path() . 'core/misc/icons/e32700/error.svg';
91 $alt_text = t('Image removed.');
92 $title_text = t('This image has been removed. For security reasons, only images from the local domain are allowed.');
94 // Put a test image in the files directory.
95 $test_images = $this->getTestFiles('image');
96 $test_image = $test_images[0]->filename;
98 // Put a test image in the files directory with special filename.
99 $special_filename = 'tést fïle nàme.png';
100 $special_image = rawurlencode($special_filename);
101 $special_uri = str_replace($test_images[0]->filename, $special_filename, $test_images[0]->uri);
102 file_unmanaged_copy($test_images[0]->uri, $special_uri);
104 // Create a list of test image sources.
105 // The keys become the value of the IMG 'src' attribute, the values are the
106 // expected filter conversions.
107 $host = \Drupal::request()->getHost();
108 $host_pattern = '|^http\://' . $host . '(\:[0-9]{0,5})|';
110 $http_base_url . '/' . $druplicon => base_path() . $druplicon,
111 $https_base_url . '/' . $druplicon => base_path() . $druplicon,
112 // Test a url that includes a port.
113 preg_replace($host_pattern, 'http://' . $host . ':', $http_base_url . '/' . $druplicon) => base_path() . $druplicon,
114 preg_replace($host_pattern, 'http://' . $host . ':80', $http_base_url . '/' . $druplicon) => base_path() . $druplicon,
115 preg_replace($host_pattern, 'http://' . $host . ':443', $http_base_url . '/' . $druplicon) => base_path() . $druplicon,
116 preg_replace($host_pattern, 'http://' . $host . ':8080', $http_base_url . '/' . $druplicon) => base_path() . $druplicon,
117 base_path() . $druplicon => base_path() . $druplicon,
118 $files_path . '/' . $test_image => $files_path . '/' . $test_image,
119 $http_base_url . '/' . $public_files_path . '/' . $test_image => $files_path . '/' . $test_image,
120 $https_base_url . '/' . $public_files_path . '/' . $test_image => $files_path . '/' . $test_image,
121 $http_base_url . '/' . $public_files_path . '/' . $special_image => $files_path . '/' . $special_image,
122 $https_base_url . '/' . $public_files_path . '/' . $special_image => $files_path . '/' . $special_image,
123 $files_path . '/example.png' => $red_x_image,
124 'http://example.com/' . $druplicon => $red_x_image,
125 'https://example.com/' . $druplicon => $red_x_image,
126 'javascript:druplicon.png' => $red_x_image,
127 $csrf_path . '/logout' => $red_x_image,
130 foreach ($images as $image => $converted) {
131 // Output the image source as plain text for debugging.
132 $comment[] = $image . ':';
133 // Hash the image source in a custom test attribute, because it might
134 // contain characters that confuse XPath.
135 $comment[] = '<img src="' . $image . '" testattribute="' . hash('sha256', $image) . '" />';
138 'comment_body[0][value]' => implode("\n", $comment),
140 $this->drupalPostForm('node/' . $this->node->id(), $edit, t('Save'));
141 foreach ($images as $image => $converted) {
143 foreach ($this->xpath('//img[@testattribute="' . hash('sha256', $image) . '"]') as $element) {
145 if ($converted == $red_x_image) {
146 $this->assertEqual($element->getAttribute('src'), $red_x_image);
147 $this->assertEqual($element->getAttribute('alt'), $alt_text);
148 $this->assertEqual($element->getAttribute('title'), $title_text);
149 $this->assertEqual($element->getAttribute('height'), '16');
150 $this->assertEqual($element->getAttribute('width'), '16');
153 $this->assertEqual($element->getAttribute('src'), $converted);
156 $this->assertTrue($found, format_string('@image was found.', ['@image' => $image]));