fields();
$this->assertCount(1, $fields);
$this->assertSame('image', $fields[0]['key']);
$this->assertSame('image', $fields[0]['control']);
}
public function test_apply_with_valid_id_derives_url_from_attachment_and_ignores_client_url()
{
$attId = self::factory()->attachment->create_upload_object($this->writeStubPng());
$expectedUrl = wp_get_attachment_image_url($attId, 'full');
$html = '
';
$block = $this->imageBlock($html, ['id' => 1]);
// A hostile client url accompanies a real attachment id; the
// server-derived url must win over what the client sent.
$result = (new Image())->apply($block, 'image', [
'url' => 'https://attacker.test/evil.jpg',
'id' => $attId,
'alt' => 'new',
]);
$this->assertSame($expectedUrl, $result['attrs']['url']);
$this->assertSame($attId, $result['attrs']['id']);
$this->assertStringContainsString('src="' . $expectedUrl . '"', $result['innerHTML']);
$this->assertStringNotContainsString('attacker.test', $result['innerHTML']);
$this->assertStringContainsString('alt="new"', $result['innerHTML']);
// Exact class proves the stale wp-image-1 was replaced by the new id
// (a substring check would trip on ids that start with "1").
$this->assertStringContainsString('class="wp-image-' . $attId . '"', $result['innerHTML']);
}
public function test_apply_with_positive_but_invalid_id_returns_block_unchanged()
{
$html = '
';
$block = $this->imageBlock($html, ['id' => 1]);
// id points at no real image attachment: reject the whole swap rather
// than trusting the accompanying client url.
$result = (new Image())->apply($block, 'image', [
'url' => 'https://attacker.test/evil.jpg',
'id' => 999999,
]);
$this->assertSame($block, $result);
}
public function test_apply_with_no_id_strips_srcset_and_sizes_for_url_only_swap()
{
$html = '
';
$block = $this->imageBlock($html, ['id' => 1]);
$result = (new Image())->apply($block, 'image', [
'url' => 'https://example.test/new.jpg',
]);
$this->assertStringContainsString('src="https://example.test/new.jpg"', $result['innerHTML']);
$this->assertStringNotContainsString('srcset=', $result['innerHTML']);
$this->assertStringNotContainsString('sizes=', $result['innerHTML']);
$this->assertStringNotContainsString('wp-image-', $result['innerHTML']);
$this->assertArrayNotHasKey('id', $result['attrs']);
}
public function test_apply_strips_extendify_image_import_marker_from_figure()
{
$html = '
';
$block = $this->imageBlock($html);
$result = (new Image())->apply($block, 'image', [
'url' => 'https://example.test/new.jpg',
]);
$this->assertStringNotContainsString('extendify-image-import', $result['innerHTML']);
$this->assertStringContainsString('wp-block-image', $result['innerHTML']);
}
public function test_apply_idless_url_with_dangerous_scheme_leaves_block_unchanged()
{
$html = '
';
foreach (['javascript:alert(1)', 'data:text/html,', 'vbscript:msgbox(1)'] as $url) {
$block = $this->imageBlock($html);
// esc_url_raw rejects the scheme → no usable url → swap is refused.
$this->assertSame($block, (new Image())->apply($block, 'image', ['url' => $url]), "url: {$url}");
}
}
public function test_apply_strips_tags_from_alt()
{
$html = '
';
$result = (new Image())->apply($this->imageBlock($html), 'image', [
'url' => 'https://example.test/new.jpg',
'alt' => 'caption',
]);
$this->assertStringContainsString('alt="caption"', $result['innerHTML']);
$this->assertStringNotContainsString('