HEX
Server: LiteSpeed
System: Linux server342.web-hosting.com 4.18.0-553.124.4.lve.el8.x86_64 #1 SMP Fri May 15 13:02:13 UTC 2026 x86_64
User: ksonpoau (1099)
PHP: 8.2.31
Disabled: NONE
Upload Files
File: //home/ksonpoau/www/wp-content/plugins/extendify/tests/Integration/QuickEdit/Schemas/ImageTest.php
<?php

namespace Extendify\Tests\Integration\QuickEdit\Schemas;

use Extendify\QuickEdit\Schemas\Image;
use WP_UnitTestCase;

class ImageTest extends WP_UnitTestCase
{
    public function test_fields_returns_single_image_field()
    {
        $fields = (new Image())->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  = '<figure class="wp-block-image"><img src="/old.jpg" alt="old" class="wp-image-1" /></figure>';
        $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  = '<figure class="wp-block-image"><img src="/old.jpg" class="wp-image-1" /></figure>';
        $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  = '<figure><img src="/old.jpg" srcset="/old-2x.jpg 2x" sizes="100vw" class="wp-image-1" /></figure>';
        $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  = '<figure class="wp-block-image extendify-image-import"><img src="/old.jpg" /></figure>';
        $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  = '<figure class="wp-block-image"><img src="http://ok/a.jpg" alt="" /></figure>';

        foreach (['javascript:alert(1)', 'data:text/html,<script>x</script>', '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   = '<figure class="wp-block-image"><img src="http://ok/a.jpg" alt="" /></figure>';
        $result = (new Image())->apply($this->imageBlock($html), 'image', [
            'url' => 'https://example.test/new.jpg',
            'alt' => '<script>alert(1)</script>caption',
        ]);

        $this->assertStringContainsString('alt="caption"', $result['innerHTML']);
        $this->assertStringNotContainsString('<script', $result['innerHTML']);
    }

    public function test_apply_with_no_url_returns_block_unchanged()
    {
        $block = $this->imageBlock('<figure><img src="/old.jpg" /></figure>');

        $this->assertSame($block, (new Image())->apply($block, 'image', ['url' => '']));
        $this->assertSame($block, (new Image())->apply($block, 'image', []));
    }

    public function test_apply_with_non_array_value_returns_block_unchanged()
    {
        $block = $this->imageBlock('<figure><img src="/old.jpg" /></figure>');

        $this->assertSame($block, (new Image())->apply($block, 'image', 'just-a-string'));
    }

    public function test_apply_with_unknown_field_returns_block_unchanged()
    {
        $block = $this->imageBlock('<figure><img src="/old.jpg" /></figure>');

        $this->assertSame($block, (new Image())->apply($block, 'alt', 'just alt'));
    }

    private function imageBlock(string $innerHTML, array $attrs = []): array
    {
        return [
            'blockName'    => 'core/image',
            'attrs'        => $attrs,
            'innerBlocks'  => [],
            'innerHTML'    => $innerHTML,
            'innerContent' => [$innerHTML],
        ];
    }

    private function writeStubPng(): string
    {
        // 1×1 transparent PNG so create_upload_object yields a real image attachment.
        $bytes = base64_decode(
            'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII='
        );
        $path = wp_tempnam('qe-test-stub') . '.png';
        file_put_contents($path, $bytes);
        return $path;
    }
}