PluginProbe
Extendify / 3.1.0
Extendify v3.1.0
3.2.1 3.2.0 3.1.6 3.1.5 3.1.4 3.1.3 3.1.2 3.1.1 3.1.0 3.0.6 3.0.5 3.0.4 trunk 0.1.0 0.10.0 0.10.1 0.10.2 0.11.0 0.11.1 0.2.0 0.3.0 0.3.1 0.4.0 0.5.0 0.6.0 All 127 releases
extendify / tests / Integration / QuickEdit / Schemas / ImageTest.php

ImageTest.php in Extendify 3.1.0, at tests/Integration/QuickEdit/Schemas/ImageTest.php

156 lines 6.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace Extendify\Tests\Integration\QuickEdit\Schemas;
4
5 use Extendify\QuickEdit\Schemas\Image;
6 use WP_UnitTestCase;
7
8 class ImageTest extends WP_UnitTestCase
9 {
10 public function test_fields_returns_single_image_field()
11 {
12 $fields = (new Image())->fields();
13
14 $this->assertCount(1, $fields);
15 $this->assertSame('image', $fields[0]['key']);
16 $this->assertSame('image', $fields[0]['control']);
17 }
18
19 public function test_apply_with_valid_id_derives_url_from_attachment_and_ignores_client_url()
20 {
21 $attId = self::factory()->attachment->create_upload_object($this->writeStubPng());
22 $expectedUrl = wp_get_attachment_image_url($attId, 'full');
23
24 $html = '<figure class="wp-block-image"><img src="/old.jpg" alt="old" class="wp-image-1" /></figure>';
25 $block = $this->imageBlock($html, ['id' => 1]);
26
27 // A hostile client url accompanies a real attachment id; the
28 // server-derived url must win over what the client sent.
29 $result = (new Image())->apply($block, 'image', [
30 'url' => 'https://attacker.test/evil.jpg',
31 'id' => $attId,
32 'alt' => 'new',
33 ]);
34
35 $this->assertSame($expectedUrl, $result['attrs']['url']);
36 $this->assertSame($attId, $result['attrs']['id']);
37 $this->assertStringContainsString('src="' . $expectedUrl . '"', $result['innerHTML']);
38 $this->assertStringNotContainsString('attacker.test', $result['innerHTML']);
39 $this->assertStringContainsString('alt="new"', $result['innerHTML']);
40 // Exact class proves the stale wp-image-1 was replaced by the new id
41 // (a substring check would trip on ids that start with "1").
42 $this->assertStringContainsString('class="wp-image-' . $attId . '"', $result['innerHTML']);
43 }
44
45 public function test_apply_with_positive_but_invalid_id_returns_block_unchanged()
46 {
47 $html = '<figure class="wp-block-image"><img src="/old.jpg" class="wp-image-1" /></figure>';
48 $block = $this->imageBlock($html, ['id' => 1]);
49
50 // id points at no real image attachment: reject the whole swap rather
51 // than trusting the accompanying client url.
52 $result = (new Image())->apply($block, 'image', [
53 'url' => 'https://attacker.test/evil.jpg',
54 'id' => 999999,
55 ]);
56
57 $this->assertSame($block, $result);
58 }
59
60 public function test_apply_with_no_id_strips_srcset_and_sizes_for_url_only_swap()
61 {
62 $html = '<figure><img src="/old.jpg" srcset="/old-2x.jpg 2x" sizes="100vw" class="wp-image-1" /></figure>';
63 $block = $this->imageBlock($html, ['id' => 1]);
64
65 $result = (new Image())->apply($block, 'image', [
66 'url' => 'https://example.test/new.jpg',
67 ]);
68
69 $this->assertStringContainsString('src="https://example.test/new.jpg"', $result['innerHTML']);
70 $this->assertStringNotContainsString('srcset=', $result['innerHTML']);
71 $this->assertStringNotContainsString('sizes=', $result['innerHTML']);
72 $this->assertStringNotContainsString('wp-image-', $result['innerHTML']);
73 $this->assertArrayNotHasKey('id', $result['attrs']);
74 }
75
76 public function test_apply_strips_extendify_image_import_marker_from_figure()
77 {
78 $html = '<figure class="wp-block-image extendify-image-import"><img src="/old.jpg" /></figure>';
79 $block = $this->imageBlock($html);
80
81 $result = (new Image())->apply($block, 'image', [
82 'url' => 'https://example.test/new.jpg',
83 ]);
84
85 $this->assertStringNotContainsString('extendify-image-import', $result['innerHTML']);
86 $this->assertStringContainsString('wp-block-image', $result['innerHTML']);
87 }
88
89 public function test_apply_idless_url_with_dangerous_scheme_leaves_block_unchanged()
90 {
91 $html = '<figure class="wp-block-image"><img src="http://ok/a.jpg" alt="" /></figure>';
92
93 foreach (['javascript:alert(1)', 'data:text/html,<script>x</script>', 'vbscript:msgbox(1)'] as $url) {
94 $block = $this->imageBlock($html);
95 // esc_url_raw rejects the scheme → no usable url → swap is refused.
96 $this->assertSame($block, (new Image())->apply($block, 'image', ['url' => $url]), "url: {$url}");
97 }
98 }
99
100 public function test_apply_strips_tags_from_alt()
101 {
102 $html = '<figure class="wp-block-image"><img src="http://ok/a.jpg" alt="" /></figure>';
103 $result = (new Image())->apply($this->imageBlock($html), 'image', [
104 'url' => 'https://example.test/new.jpg',
105 'alt' => '<script>alert(1)</script>caption',
106 ]);
107
108 $this->assertStringContainsString('alt="caption"', $result['innerHTML']);
109 $this->assertStringNotContainsString('<script', $result['innerHTML']);
110 }
111
112 public function test_apply_with_no_url_returns_block_unchanged()
113 {
114 $block = $this->imageBlock('<figure><img src="/old.jpg" /></figure>');
115
116 $this->assertSame($block, (new Image())->apply($block, 'image', ['url' => '']));
117 $this->assertSame($block, (new Image())->apply($block, 'image', []));
118 }
119
120 public function test_apply_with_non_array_value_returns_block_unchanged()
121 {
122 $block = $this->imageBlock('<figure><img src="/old.jpg" /></figure>');
123
124 $this->assertSame($block, (new Image())->apply($block, 'image', 'just-a-string'));
125 }
126
127 public function test_apply_with_unknown_field_returns_block_unchanged()
128 {
129 $block = $this->imageBlock('<figure><img src="/old.jpg" /></figure>');
130
131 $this->assertSame($block, (new Image())->apply($block, 'alt', 'just alt'));
132 }
133
134 private function imageBlock(string $innerHTML, array $attrs = []): array
135 {
136 return [
137 'blockName' => 'core/image',
138 'attrs' => $attrs,
139 'innerBlocks' => [],
140 'innerHTML' => $innerHTML,
141 'innerContent' => [$innerHTML],
142 ];
143 }
144
145 private function writeStubPng(): string
146 {
147 // 1×1 transparent PNG so create_upload_object yields a real image attachment.
148 $bytes = base64_decode(
149 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII='
150 );
151 $path = wp_tempnam('qe-test-stub') . '.png';
152 file_put_contents($path, $bytes);
153 return $path;
154 }
155 }
156