From f989b9cb61b7512a0423a14e622462ca57341bd9 Mon Sep 17 00:00:00 2001 From: Andrew Duthie Date: Thu, 21 Jun 2018 03:50:57 -0400 Subject: [PATCH] Block API: Replace JSON-escaped quotation mark with unicode escape sequence (#6619) * Block API: Replace JSON-escaped quotation mark with unicode escape sequence * Parser: Test full lifecycle of quote-escaped attributes parse --- blocks/api/serializer.js | 29 +++++++++++++++++++++++------ blocks/api/test/parser.js | 21 +++++++++++++++++++++ blocks/api/test/serializer.js | 7 +++++++ 3 files changed, 51 insertions(+), 6 deletions(-) diff --git a/blocks/api/serializer.js b/blocks/api/serializer.js index 7e2293d9619ef..cb44083495f11 100644 --- a/blocks/api/serializer.js +++ b/blocks/api/serializer.js @@ -164,12 +164,29 @@ export function getCommentAttributes( allAttributes, blockType ) { return attributes; } -export function serializeAttributes( attrs ) { - return JSON.stringify( attrs ) - .replace( /--/g, '\\u002d\\u002d' ) // don't break HTML comments - .replace( //g, '\\u003e' ) // ibid - .replace( /&/g, '\\u0026' ); // ibid +/** + * Given an attributes object, returns a string in the serialized attributes + * format prepared for post content. + * + * @param {Object} attributes Attributes object. + * + * @return {string} Serialized attributes. + */ +export function serializeAttributes( attributes ) { + return JSON.stringify( attributes ) + // Don't break HTML comments. + .replace( /--/g, '\\u002d\\u002d' ) + + // Don't break non-standard-compliant tools. + .replace( //g, '\\u003e' ) + .replace( /&/g, '\\u0026' ) + + // Bypass server stripslashes behavior which would unescape stringify's + // escaping of quotation mark. + // + // See: https://developer.wordpress.org/reference/functions/wp_kses_stripslashes/ + .replace( /\\"/g, '\\u0022' ); } /** diff --git a/blocks/api/test/parser.js b/blocks/api/test/parser.js index b883efbd17a87..1538fd2c8753b 100644 --- a/blocks/api/test/parser.js +++ b/blocks/api/test/parser.js @@ -16,6 +16,8 @@ import { getBlockTypes, setUnknownTypeHandlerName, } from '../registration'; +import { createBlock } from '../factory'; +import serialize from '../serializer'; describe( 'block parser', () => { const defaultBlockSettings = { @@ -571,5 +573,24 @@ describe( 'block parser', () => { 'core/test-block', 'core/void-block', ] ); } ); + + it( 'should parse with unicode escaped returned to original representation', () => { + registerBlockType( 'core/code', { + category: 'common', + title: 'Code Block', + attributes: { + content: { + type: 'string', + }, + }, + save: ( { attributes } ) => attributes.content, + } ); + + const content = '$foo = "My \"escaped\" text.";'; + const block = createBlock( 'core/code', { content } ); + const serialized = serialize( block ); + const parsed = parse( serialized ); + expect( parsed[ 0 ].attributes.content ).toBe( content ); + } ); } ); } ); diff --git a/blocks/api/test/serializer.js b/blocks/api/test/serializer.js index f7c7c293b0889..8d14dfafa98d5 100644 --- a/blocks/api/test/serializer.js +++ b/blocks/api/test/serializer.js @@ -130,15 +130,22 @@ describe( 'block serializer', () => { it( 'should not break HTML comments', () => { expect( serializeAttributes( { a: '-- and --' } ) ).toBe( '{"a":"\\u002d\\u002d and \\u002d\\u002d"}' ); } ); + it( 'should not break standard-non-compliant tools for "<"', () => { expect( serializeAttributes( { a: '< and <' } ) ).toBe( '{"a":"\\u003c and \\u003c"}' ); } ); + it( 'should not break standard-non-compliant tools for ">"', () => { expect( serializeAttributes( { a: '> and >' } ) ).toBe( '{"a":"\\u003e and \\u003e"}' ); } ); + it( 'should not break standard-non-compliant tools for "&"', () => { expect( serializeAttributes( { a: '& and &' } ) ).toBe( '{"a":"\\u0026 and \\u0026"}' ); } ); + + it( 'should replace quotation marks', () => { + expect( serializeAttributes( { a: '" and "' } ) ).toBe( '{"a":"\\u0022 and \\u0022"}' ); + } ); } ); describe( 'getCommentDelimitedContent()', () => {