Make WordPress Core


Ignore:
Timestamp:
09/04/2025 06:30:56 PM (14 months ago)
Author:
jonsurrell
Message:

Editor: Use Unicode escape encoding for "\" characters in block attributes.

Corrects an issue with block attribute encoding where JSON strings ending in the \ character would be misencoded and cause block attributes to be lost.

Client-side block serialization was updated with matching logic in ​https://github.com/WordPress/gutenberg/commit/10453ab3a4e665b8403a0cb466dba64689b4b491.

Developed in ​https://github.com/WordPress/wordpress-develop/pull/9558.

Props jonsurrell, westonruter, mamaduka, dmsnell, shailu25.
Fixes #63917.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/tests/phpunit/tests/blocks/serialize.php

    r56970 r60708  
    1111 */
    1212class Tests_Blocks_Serialize extends WP_UnitTestCase {
     13        /**
     14         * Ensure there are no issues with special character encoding.
     15         *
     16         * @ticket 63917
     17         */
     18        public function test_attribute_encoding() {
     19                $block = array(
     20                        'blockName'    => 'test',
     21                        'attrs'        => array(
     22                                'lt'         => '<',
     23                                'gt'         => '>',
     24                                'amp'        => '&',
     25                                'bs'         => '\\',
     26                                'quot'       => '"',
     27                                'bs-bs-quot' => '\\\\"',
     28                        ),
     29                        'innerBlocks'  => array(),
     30                        'innerHTML'    => '',
     31                        'innerContent' => array(),
     32                );
     33
     34                $expected = '<!-- wp:test {"lt":"\\u003c","gt":"\\u003e","amp":"\\u0026","bs":"\\u005c","quot":"\\u0022","bs-bs-quot":"\\u005c\\u005c\\u0022"} /-->';
     35                $this->assertSame( $expected, serialize_block( $block ) );
     36        }
    1337
    1438        /**
    … …  
    1640         *
    1741         * @param string $original Original block markup.
     42         *
     43         * @ticket 63917
    1844         */
    1945        public function test_serialize_identity_from_parsed( $original ) {
    … …  
    2551        }
    2652
    27         public function data_serialize_identity_from_parsed() {
     53        public static function data_serialize_identity_from_parsed(): array {
    2854                return array(
    29                         // Void block.
    30                         array( '<!-- wp:void /-->' ),
    31 
    32                         // Freeform content ($block_name = null).
    33                         array( 'Example.' ),
    34 
    35                         // Block with content.
    36                         array( '<!-- wp:content -->Example.<!-- /wp:content -->' ),
    37 
    38                         // Block with attributes.
    39                         array( '<!-- wp:attributes {"key":"value"} /-->' ),
    40 
    41                         // Block with inner blocks.
    42                         array( "<!-- wp:outer --><!-- wp:inner {\"key\":\"value\"} -->Example.<!-- /wp:inner -->\n\nExample.\n\n<!-- wp:void /--><!-- /wp:outer -->" ),
    43 
    44                         // Block with attribute values that may conflict with HTML comment.
    45                         array( '<!-- wp:attributes {"key":"\\u002d\\u002d\\u003c\\u003e\\u0026\\u0022"} /-->' ),
    46 
    47                         // Block with attribute values that should not be escaped.
    48                         array( '<!-- wp:attributes {"key":"€1.00 / 3 for €2.00"} /-->' ),
     55                        'Void block'                                  =>
     56                                array( '<!-- wp:void /-->' ),
     57
     58                        'Freeform content ($block_name = null)'       =>
     59                                array( 'Example.' ),
     60
     61                        'Block with content'                          =>
     62                                array( '<!-- wp:content -->Example.<!-- /wp:content -->' ),
     63
     64                        'Block with attributes'                       =>
     65                                array( '<!-- wp:attributes {"key":"value"} /-->' ),
     66
     67                        'Block with inner blocks'                     =>
     68                                array( "<!-- wp:outer --><!-- wp:inner {\"key\":\"value\"} -->Example.<!-- /wp:inner -->\n\nExample.\n\n<!-- wp:void /--><!-- /wp:outer -->" ),
     69
     70                        'Block with attribute values that may conflict with HTML comment' =>
     71                                array( '<!-- wp:attributes {"key":"\\u002d\\u002d\\u003c\\u003e\\u0026\\u0022"} /-->' ),
     72
     73                        'Block with attribute values that should not be escaped' =>
     74                                array( '<!-- wp:attributes {"key":"€1.00 / 3 for €2.00"} /-->' ),
     75
     76                        'Backslashes in attributes, Gutenberg #16508' =>
     77                                array( '<!-- wp:attributes {"bs":"\\u005c","bsQuote":"\\u005c\\u0022","bsQuoteBs":"\\u005c\\u0022\\u005c"} /-->' ),
     78
     79                        'Tricky backslashes'                          =>
     80                                array( '<!-- wp:attributes {"bsbsQbsbsbsQ":"\\u005c\\u005c\\u0022\\u005c\\u005c\\u005c\\u005c\\u0022"} /-->' ),
     81                );
     82        }
     83
     84        /**
     85         * The serialization was adjusted to use unicode escapes sequences for escaped `\` and `"`
     86         * characters inside JSON strings.
     87         *
     88         * Ensure that the previous escape form can be parsed compatibly and serialized back to
     89         * the new form.
     90         *
     91         * @see https://github.com/WordPress/wordpress-develop/pull/9558
     92         * @see https://github.com/WordPress/gutenberg/pull/71291
     93         *
     94         * @ticket 63917
     95         *
     96         * @dataProvider data_serialize_compatible_forms
     97         *
     98         * @param string $before Previous serialization form.
     99         * @param string $after  New serialization form.
     100         */
     101        public function test_older_serialization_is_compatible( string $before, string $after ) {
     102                $this->assertNotSame( $before, $after, 'The same serialization should not be provided for before and after.' );
     103                $blocks = parse_blocks( $before );
     104                $actual = serialize_blocks( $blocks );
     105                $this->assertSame( $after, $actual );
     106        }
     107
     108        public static function data_serialize_compatible_forms(): array {
     109                return array(
     110                        'Special characters' => array(
     111                                '<!-- wp:attributes {"lt":"\\u003c","gt":"\\u003e","amp":"\\u0026","bs":"\\\\","quot":"\\u0022"} /-->',
     112                                '<!-- wp:attributes {"lt":"\\u003c","gt":"\\u003e","amp":"\\u0026","bs":"\\u005c","quot":"\\u0022"} /-->',
     113                        ),
     114
     115                        'Backslashes'        => array(
     116                                '<!-- wp:attributes {"bs":"\\\\","bsQuote":"\\\\\\u0022","bsQuoteBs":"\\\\\\u0022\\\\"} /-->',
     117                                '<!-- wp:attributes {"bs":"\\u005c","bsQuote":"\\u005c\\u0022","bsQuoteBs":"\\u005c\\u0022\\u005c"} /-->',
     118                        ),
    49119                );
    50120        }
Note: See TracChangeset for help on using the changeset viewer.