From 497ffdd4b88c20263fff81f272eb131e323e624b Mon Sep 17 00:00:00 2001 From: iseulde Date: Thu, 17 Oct 2019 12:23:18 +0200 Subject: [PATCH 1/8] Escape HTML: Always Escape Ampersand --- packages/block-library/src/code/utils.js | 25 ------------------------ packages/element/src/test/serialize.js | 2 +- packages/escape-html/src/index.js | 2 +- packages/escape-html/src/test/index.js | 2 +- 4 files changed, 3 insertions(+), 28 deletions(-) diff --git a/packages/block-library/src/code/utils.js b/packages/block-library/src/code/utils.js index fb8557a95b18e..9cc2180bcc7a1 100644 --- a/packages/block-library/src/code/utils.js +++ b/packages/block-library/src/code/utils.js @@ -11,7 +11,6 @@ import { flow } from 'lodash'; */ export function escape( content ) { return flow( - escapeAmpersands, escapeOpeningSquareBrackets, escapeProtocolInIsolatedUrls )( content || '' ); @@ -27,33 +26,9 @@ export function unescape( content ) { return flow( unescapeProtocolInIsolatedUrls, unescapeOpeningSquareBrackets, - unescapeAmpersands )( content || '' ); } -/** - * Returns the given content with all its ampersand characters converted - * into their HTML entity counterpart (i.e. & => &) - * - * @param {string} content The content of a code block. - * @return {string} The given content with its ampersands converted into - * their HTML entity counterpart (i.e. & => &) - */ -function escapeAmpersands( content ) { - return content.replace( /&/g, '&' ); -} - -/** - * Returns the given content with all & HTML entities converted into &. - * - * @param {string} content The content of a code block. - * @return {string} The given content with all & HTML entities - * converted into &. - */ -function unescapeAmpersands( content ) { - return content.replace( /&/g, '&' ); -} - /** * Returns the given content with all opening shortcode characters converted * into their HTML entity counterpart (i.e. [ => [). For instance, a diff --git a/packages/element/src/test/serialize.js b/packages/element/src/test/serialize.js index b6a8c906751d9..3717f8c7ca68a 100644 --- a/packages/element/src/test/serialize.js +++ b/packages/element/src/test/serialize.js @@ -188,7 +188,7 @@ describe( 'renderElement()', () => { it( 'renders escaped string element', () => { const result = renderElement( 'hello & world & friends ' ); - expect( result ).toBe( 'hello & world & friends <img/>' ); + expect( result ).toBe( 'hello & world &amp; friends <img/>' ); } ); it( 'renders numeric element as string', () => { diff --git a/packages/escape-html/src/index.js b/packages/escape-html/src/index.js index d31f43f63f70b..c4e0d2d3d8efb 100644 --- a/packages/escape-html/src/index.js +++ b/packages/escape-html/src/index.js @@ -31,7 +31,7 @@ const REGEXP_INVALID_ATTRIBUTE_NAME = /[\u007F-\u009F "'>/="\uFDD0-\uFDEF]/; * @return {string} Escaped string. */ export function escapeAmpersand( value ) { - return value.replace( /&(?!([a-z0-9]+|#[0-9]+|#x[a-f0-9]+);)/gi, '&' ); + return value.replace( /&/gi, '&' ); } /** diff --git a/packages/escape-html/src/test/index.js b/packages/escape-html/src/test/index.js index 9558c58c7e23a..ce7792b7470bf 100644 --- a/packages/escape-html/src/test/index.js +++ b/packages/escape-html/src/test/index.js @@ -22,7 +22,7 @@ function testEscapeAmpersand( implementation ) { it( 'should escape ampersand', () => { const result = implementation( 'foo & bar & & baz Σ &#bad; Σ Σ vil;' ); - expect( result ).toBe( 'foo & bar & & baz Σ &#bad; Σ Σ &#xevil;' ); + expect( result ).toBe( 'foo & bar &amp; &AMP; baz &#931; &#bad; &#x3A3; &#X3a3; &#xevil;' ); } ); } From e7b981b128426ae87a1c928e4e7eda442147cf07 Mon Sep 17 00:00:00 2001 From: iseulde Date: Thu, 17 Oct 2019 12:42:39 +0200 Subject: [PATCH 2/8] Revert "Escape HTML: Always Escape Ampersand" This reverts commit 70dfa53a433d91dde4aa7eaebede5a6b0fc70d73. --- packages/block-library/src/code/utils.js | 25 ++++++++++++++++++++++++ packages/element/src/test/serialize.js | 2 +- packages/escape-html/src/index.js | 2 +- packages/escape-html/src/test/index.js | 2 +- 4 files changed, 28 insertions(+), 3 deletions(-) diff --git a/packages/block-library/src/code/utils.js b/packages/block-library/src/code/utils.js index 9cc2180bcc7a1..fb8557a95b18e 100644 --- a/packages/block-library/src/code/utils.js +++ b/packages/block-library/src/code/utils.js @@ -11,6 +11,7 @@ import { flow } from 'lodash'; */ export function escape( content ) { return flow( + escapeAmpersands, escapeOpeningSquareBrackets, escapeProtocolInIsolatedUrls )( content || '' ); @@ -26,9 +27,33 @@ export function unescape( content ) { return flow( unescapeProtocolInIsolatedUrls, unescapeOpeningSquareBrackets, + unescapeAmpersands )( content || '' ); } +/** + * Returns the given content with all its ampersand characters converted + * into their HTML entity counterpart (i.e. & => &) + * + * @param {string} content The content of a code block. + * @return {string} The given content with its ampersands converted into + * their HTML entity counterpart (i.e. & => &) + */ +function escapeAmpersands( content ) { + return content.replace( /&/g, '&' ); +} + +/** + * Returns the given content with all & HTML entities converted into &. + * + * @param {string} content The content of a code block. + * @return {string} The given content with all & HTML entities + * converted into &. + */ +function unescapeAmpersands( content ) { + return content.replace( /&/g, '&' ); +} + /** * Returns the given content with all opening shortcode characters converted * into their HTML entity counterpart (i.e. [ => [). For instance, a diff --git a/packages/element/src/test/serialize.js b/packages/element/src/test/serialize.js index 3717f8c7ca68a..b6a8c906751d9 100644 --- a/packages/element/src/test/serialize.js +++ b/packages/element/src/test/serialize.js @@ -188,7 +188,7 @@ describe( 'renderElement()', () => { it( 'renders escaped string element', () => { const result = renderElement( 'hello & world & friends ' ); - expect( result ).toBe( 'hello & world &amp; friends <img/>' ); + expect( result ).toBe( 'hello & world & friends <img/>' ); } ); it( 'renders numeric element as string', () => { diff --git a/packages/escape-html/src/index.js b/packages/escape-html/src/index.js index c4e0d2d3d8efb..d31f43f63f70b 100644 --- a/packages/escape-html/src/index.js +++ b/packages/escape-html/src/index.js @@ -31,7 +31,7 @@ const REGEXP_INVALID_ATTRIBUTE_NAME = /[\u007F-\u009F "'>/="\uFDD0-\uFDEF]/; * @return {string} Escaped string. */ export function escapeAmpersand( value ) { - return value.replace( /&/gi, '&' ); + return value.replace( /&(?!([a-z0-9]+|#[0-9]+|#x[a-f0-9]+);)/gi, '&' ); } /** diff --git a/packages/escape-html/src/test/index.js b/packages/escape-html/src/test/index.js index ce7792b7470bf..9558c58c7e23a 100644 --- a/packages/escape-html/src/test/index.js +++ b/packages/escape-html/src/test/index.js @@ -22,7 +22,7 @@ function testEscapeAmpersand( implementation ) { it( 'should escape ampersand', () => { const result = implementation( 'foo & bar & & baz Σ &#bad; Σ Σ vil;' ); - expect( result ).toBe( 'foo & bar &amp; &AMP; baz &#931; &#bad; &#x3A3; &#X3a3; &#xevil;' ); + expect( result ).toBe( 'foo & bar & & baz Σ &#bad; Σ Σ &#xevil;' ); } ); } From 7f2d3e0dc2d3deb3d89b7c1b3f35081bb15d8fb7 Mon Sep 17 00:00:00 2001 From: iseulde Date: Thu, 17 Oct 2019 13:22:17 +0200 Subject: [PATCH 3/8] Escape HTML for Editable Text --- packages/block-editor/package.json | 1 + .../src/components/plain-text/index.js | 6 ++-- packages/block-library/src/code/block.json | 2 +- packages/block-library/src/code/test/utils.js | 19 ----------- packages/block-library/src/code/utils.js | 25 -------------- .../e2e-tests/fixtures/blocks/core__code.json | 2 +- .../blocks/core__code.serialized.html | 2 +- packages/escape-html/src/index.js | 34 +++++++++++++++++-- packages/rich-text/src/to-html-string.js | 4 +-- 9 files changed, 41 insertions(+), 54 deletions(-) diff --git a/packages/block-editor/package.json b/packages/block-editor/package.json index 9d9f00f675fb2..8081a61ec2014 100644 --- a/packages/block-editor/package.json +++ b/packages/block-editor/package.json @@ -32,6 +32,7 @@ "@wordpress/deprecated": "file:../deprecated", "@wordpress/dom": "file:../dom", "@wordpress/element": "file:../element", + "@wordpress/escape-html": "file:../escape-html", "@wordpress/hooks": "file:../hooks", "@wordpress/html-entities": "file:../html-entities", "@wordpress/i18n": "file:../i18n", diff --git a/packages/block-editor/src/components/plain-text/index.js b/packages/block-editor/src/components/plain-text/index.js index b05e48ed60b61..5fe36e995daff 100644 --- a/packages/block-editor/src/components/plain-text/index.js +++ b/packages/block-editor/src/components/plain-text/index.js @@ -3,6 +3,7 @@ * WordPress dependencies */ import { forwardRef } from '@wordpress/element'; +import { escapeEditableHTML, unescapeEditableHTML } from '@wordpress/escape-html'; /** * External dependencies @@ -13,12 +14,13 @@ import classnames from 'classnames'; /** * @see https://github.com/WordPress/gutenberg/blob/master/packages/block-editor/src/components/plain-text/README.md */ -const PlainText = forwardRef( ( { onChange, className, ...props }, ref ) => { +const PlainText = forwardRef( ( { onChange, className, value, ...props }, ref ) => { return ( onChange( event.target.value ) } + value={ unescapeEditableHTML( value ) } + onChange={ ( event ) => onChange( escapeEditableHTML( event.target.value ) ) } { ...props } /> ); diff --git a/packages/block-library/src/code/block.json b/packages/block-library/src/code/block.json index d39e5f213bbe6..354d30b04fd6d 100644 --- a/packages/block-library/src/code/block.json +++ b/packages/block-library/src/code/block.json @@ -4,7 +4,7 @@ "attributes": { "content": { "type": "string", - "source": "text", + "source": "html", "selector": "code" } } diff --git a/packages/block-library/src/code/test/utils.js b/packages/block-library/src/code/test/utils.js index 4926eef16d528..d3b1a3139fe17 100644 --- a/packages/block-library/src/code/test/utils.js +++ b/packages/block-library/src/code/test/utils.js @@ -5,11 +5,6 @@ import { escape, unescape } from '../utils'; describe( 'core/code', () => { describe( 'escape()', () => { - it( 'should escape ampersands', () => { - const text = escape( '&' ); - expect( text ).toBe( '&' ); - } ); - it( 'should escape opening square brackets', () => { const text = escape( '[shortcode][/shortcode]' ); expect( text ).toBe( '[shortcode][/shortcode]' ); @@ -24,20 +19,9 @@ describe( 'core/code', () => { const text = escape( 'Text https://example.com/test/' ); expect( text ).toBe( 'Text https://example.com/test/' ); } ); - - it( 'should escape ampersands last', () => { - const text = escape( '[shortcode][/shortcode]' ); - expect( text ).toBe( '[shortcode][/shortcode]' ); - expect( text ).not.toBe( '&#91;shortcode]&#91;/shortcode]' ); - } ); } ); describe( 'unescape()', () => { - it( 'should unescape escaped ampersands', () => { - const text = unescape( '&' ); - expect( text ).toBe( '&' ); - } ); - it( 'should unescape escaped opening square brackets', () => { const text = unescape( '[shortcode][/shortcode]' ); expect( text ).toBe( '[shortcode][/shortcode]' ); @@ -49,9 +33,6 @@ describe( 'core/code', () => { } ); it( 'should revert the result of escape()', () => { - const ampersand = unescape( escape( '&' ) ); - expect( ampersand ).toBe( '&' ); - const squareBracket = unescape( escape( '[shortcode][/shortcode]' ) ); expect( squareBracket ).toBe( '[shortcode][/shortcode]' ); diff --git a/packages/block-library/src/code/utils.js b/packages/block-library/src/code/utils.js index fb8557a95b18e..9cc2180bcc7a1 100644 --- a/packages/block-library/src/code/utils.js +++ b/packages/block-library/src/code/utils.js @@ -11,7 +11,6 @@ import { flow } from 'lodash'; */ export function escape( content ) { return flow( - escapeAmpersands, escapeOpeningSquareBrackets, escapeProtocolInIsolatedUrls )( content || '' ); @@ -27,33 +26,9 @@ export function unescape( content ) { return flow( unescapeProtocolInIsolatedUrls, unescapeOpeningSquareBrackets, - unescapeAmpersands )( content || '' ); } -/** - * Returns the given content with all its ampersand characters converted - * into their HTML entity counterpart (i.e. & => &) - * - * @param {string} content The content of a code block. - * @return {string} The given content with its ampersands converted into - * their HTML entity counterpart (i.e. & => &) - */ -function escapeAmpersands( content ) { - return content.replace( /&/g, '&' ); -} - -/** - * Returns the given content with all & HTML entities converted into &. - * - * @param {string} content The content of a code block. - * @return {string} The given content with all & HTML entities - * converted into &. - */ -function unescapeAmpersands( content ) { - return content.replace( /&/g, '&' ); -} - /** * Returns the given content with all opening shortcode characters converted * into their HTML entity counterpart (i.e. [ => [). For instance, a diff --git a/packages/e2e-tests/fixtures/blocks/core__code.json b/packages/e2e-tests/fixtures/blocks/core__code.json index 0958025736676..f967eeb8cb894 100644 --- a/packages/e2e-tests/fixtures/blocks/core__code.json +++ b/packages/e2e-tests/fixtures/blocks/core__code.json @@ -4,7 +4,7 @@ "name": "core/code", "isValid": true, "attributes": { - "content": "export default function MyButton() {\n\treturn ;\n}" + "content": "export default function MyButton() {\n\treturn <Button>Click Me!</Button>;\n}" }, "innerBlocks": [], "originalContent": "
export default function MyButton() {\n\treturn <Button>Click Me!</Button>;\n}
" diff --git a/packages/e2e-tests/fixtures/blocks/core__code.serialized.html b/packages/e2e-tests/fixtures/blocks/core__code.serialized.html index 651254a7cab49..3a45062483ea3 100644 --- a/packages/e2e-tests/fixtures/blocks/core__code.serialized.html +++ b/packages/e2e-tests/fixtures/blocks/core__code.serialized.html @@ -1,5 +1,5 @@
export default function MyButton() {
-	return <Button>Click Me!</Button>;
+	return <Button>Click Me!</Button>;
 }
diff --git a/packages/escape-html/src/index.js b/packages/escape-html/src/index.js index d31f43f63f70b..86fa30ac400c1 100644 --- a/packages/escape-html/src/index.js +++ b/packages/escape-html/src/index.js @@ -26,12 +26,14 @@ const REGEXP_INVALID_ATTRIBUTE_NAME = /[\u007F-\u009F "'>/="\uFDD0-\uFDEF]/; * @see https://w3c.github.io/html/syntax.html#ambiguous-ampersand * @see https://w3c.github.io/html/syntax.html#named-character-references * - * @param {string} value Original string. + * @param {string} value Original string. + * @param {boolean} all Set to true to escape all ampersands. * * @return {string} Escaped string. */ -export function escapeAmpersand( value ) { - return value.replace( /&(?!([a-z0-9]+|#[0-9]+|#x[a-f0-9]+);)/gi, '&' ); +export function escapeAmpersand( value, all ) { + const regExp = all ? /&/gi : /&(?!([a-z0-9]+|#[0-9]+|#x[a-f0-9]+);)/gi; + return value.replace( regExp, '&' ); } /** @@ -96,6 +98,32 @@ export function escapeHTML( value ) { return escapeLessThan( escapeAmpersand( value ) ); } +/** + * Returns an escaped Editable HTML element value. This is different from + * `escapeHTML`, because for editable HTML, all ampersands must be escaped in + * order to render the content correctly on the page. + * + * @param {string} value Element value. + * + * @return {string} Escaped HTML element value. + */ +export function escapeEditableHTML( value ) { + return escapeLessThan( escapeAmpersand( value, true ) ); +} + +/** + * Returns an unescaped Editable HTML element value. + * + * @param {string} value Element value. + * + * @return {string} Escaped HTML element value. + */ +export function unescapeEditableHTML( value ) { + return value + .replace( /(? { - return child.text === undefined ? createElementHTML( child ) : escapeHTML( child.text ); + return child.text === undefined ? createElementHTML( child ) : escapeEditableHTML( child.text ); } ).join( '' ); } From 365f0250539802a8590b094dbdcceb085efe560d Mon Sep 17 00:00:00 2001 From: iseulde Date: Thu, 17 Oct 2019 13:57:00 +0200 Subject: [PATCH 4/8] Revert PlainText changes --- packages/block-editor/package.json | 1 - .../src/components/plain-text/index.js | 6 ++--- packages/block-library/package.json | 1 + packages/block-library/src/code/block.json | 2 +- packages/block-library/src/code/edit.js | 5 ++--- packages/block-library/src/code/save.js | 12 +++++++++- packages/block-library/src/code/test/utils.js | 22 +------------------ .../e2e-tests/fixtures/blocks/core__code.json | 2 +- .../blocks/core__code.serialized.html | 2 +- packages/escape-html/README.md | 15 +++++++++++++ packages/escape-html/src/index.js | 13 ----------- 11 files changed, 35 insertions(+), 46 deletions(-) diff --git a/packages/block-editor/package.json b/packages/block-editor/package.json index 8081a61ec2014..9d9f00f675fb2 100644 --- a/packages/block-editor/package.json +++ b/packages/block-editor/package.json @@ -32,7 +32,6 @@ "@wordpress/deprecated": "file:../deprecated", "@wordpress/dom": "file:../dom", "@wordpress/element": "file:../element", - "@wordpress/escape-html": "file:../escape-html", "@wordpress/hooks": "file:../hooks", "@wordpress/html-entities": "file:../html-entities", "@wordpress/i18n": "file:../i18n", diff --git a/packages/block-editor/src/components/plain-text/index.js b/packages/block-editor/src/components/plain-text/index.js index 5fe36e995daff..b05e48ed60b61 100644 --- a/packages/block-editor/src/components/plain-text/index.js +++ b/packages/block-editor/src/components/plain-text/index.js @@ -3,7 +3,6 @@ * WordPress dependencies */ import { forwardRef } from '@wordpress/element'; -import { escapeEditableHTML, unescapeEditableHTML } from '@wordpress/escape-html'; /** * External dependencies @@ -14,13 +13,12 @@ import classnames from 'classnames'; /** * @see https://github.com/WordPress/gutenberg/blob/master/packages/block-editor/src/components/plain-text/README.md */ -const PlainText = forwardRef( ( { onChange, className, value, ...props }, ref ) => { +const PlainText = forwardRef( ( { onChange, className, ...props }, ref ) => { return ( onChange( escapeEditableHTML( event.target.value ) ) } + onChange={ ( event ) => onChange( event.target.value ) } { ...props } /> ); diff --git a/packages/block-library/package.json b/packages/block-library/package.json index 8d4cd8ba26854..185e039685887 100644 --- a/packages/block-library/package.json +++ b/packages/block-library/package.json @@ -37,6 +37,7 @@ "@wordpress/deprecated": "file:../deprecated", "@wordpress/editor": "file:../editor", "@wordpress/element": "file:../element", + "@wordpress/escape-html": "file:../escape-html", "@wordpress/i18n": "file:../i18n", "@wordpress/is-shallow-equal": "file:../is-shallow-equal", "@wordpress/keycodes": "file:../keycodes", diff --git a/packages/block-library/src/code/block.json b/packages/block-library/src/code/block.json index 354d30b04fd6d..d39e5f213bbe6 100644 --- a/packages/block-library/src/code/block.json +++ b/packages/block-library/src/code/block.json @@ -4,7 +4,7 @@ "attributes": { "content": { "type": "string", - "source": "html", + "source": "text", "selector": "code" } } diff --git a/packages/block-library/src/code/edit.js b/packages/block-library/src/code/edit.js index 2926774f46ff2..5aa4b337bd3f1 100644 --- a/packages/block-library/src/code/edit.js +++ b/packages/block-library/src/code/edit.js @@ -7,14 +7,13 @@ import { __ } from '@wordpress/i18n'; * Internal dependencies */ import { PlainText } from '@wordpress/block-editor'; -import { escape, unescape } from './utils'; export default function CodeEdit( { attributes, setAttributes, className } ) { return (
setAttributes( { content: escape( content ) } ) } + value={ attributes.content } + onChange={ ( content ) => setAttributes( { content } ) } placeholder={ __( 'Write codeā€¦' ) } aria-label={ __( 'Code' ) } /> diff --git a/packages/block-library/src/code/save.js b/packages/block-library/src/code/save.js index 21d74669c854b..410e47284011e 100644 --- a/packages/block-library/src/code/save.js +++ b/packages/block-library/src/code/save.js @@ -1,3 +1,13 @@ +/** + * WordPress dependencies + */ +import { escapeEditableHTML } from '@wordpress/escape-html'; + +/** + * Internal dependencies + */ +import { escape } from './utils'; + export default function save( { attributes } ) { - return <pre><code>{ attributes.content }</code></pre>; + return <pre><code>{ escape( escapeEditableHTML( attributes.content ) ) }</code></pre>; } diff --git a/packages/block-library/src/code/test/utils.js b/packages/block-library/src/code/test/utils.js index d3b1a3139fe17..d3e9de573cf77 100644 --- a/packages/block-library/src/code/test/utils.js +++ b/packages/block-library/src/code/test/utils.js @@ -1,7 +1,7 @@ /** * Internal dependencies */ -import { escape, unescape } from '../utils'; +import { escape } from '../utils'; describe( 'core/code', () => { describe( 'escape()', () => { @@ -20,24 +20,4 @@ describe( 'core/code', () => { expect( text ).toBe( 'Text https://example.com/test/' ); } ); } ); - - describe( 'unescape()', () => { - it( 'should unescape escaped opening square brackets', () => { - const text = unescape( '&#91;shortcode]&#91;/shortcode]' ); - expect( text ).toBe( '[shortcode][/shortcode]' ); - } ); - - it( 'should unescape the escaped protocol of an isolated url', () => { - const text = unescape( 'https:&#47;&#47;example.com/test/' ); - expect( text ).toBe( 'https://example.com/test/' ); - } ); - - it( 'should revert the result of escape()', () => { - const squareBracket = unescape( escape( '[shortcode][/shortcode]' ) ); - expect( squareBracket ).toBe( '[shortcode][/shortcode]' ); - - const url = unescape( escape( 'https://example.com/test/' ) ); - expect( url ).toBe( 'https://example.com/test/' ); - } ); - } ); } ); diff --git a/packages/e2e-tests/fixtures/blocks/core__code.json b/packages/e2e-tests/fixtures/blocks/core__code.json index f967eeb8cb894..0958025736676 100644 --- a/packages/e2e-tests/fixtures/blocks/core__code.json +++ b/packages/e2e-tests/fixtures/blocks/core__code.json @@ -4,7 +4,7 @@ "name": "core/code", "isValid": true, "attributes": { - "content": "export default function MyButton() {\n\treturn &lt;Button&gt;Click Me!&lt;/Button&gt;;\n}" + "content": "export default function MyButton() {\n\treturn <Button>Click Me!</Button>;\n}" }, "innerBlocks": [], "originalContent": "<pre class=\"wp-block-code\"><code>export default function MyButton() {\n\treturn &lt;Button&gt;Click Me!&lt;/Button&gt;;\n}</code></pre>" diff --git a/packages/e2e-tests/fixtures/blocks/core__code.serialized.html b/packages/e2e-tests/fixtures/blocks/core__code.serialized.html index 3a45062483ea3..651254a7cab49 100644 --- a/packages/e2e-tests/fixtures/blocks/core__code.serialized.html +++ b/packages/e2e-tests/fixtures/blocks/core__code.serialized.html @@ -1,5 +1,5 @@ <!-- wp:code --> <pre class="wp-block-code"><code>export default function MyButton() { - return &lt;Button&gt;Click Me!&lt;/Button&gt;; + return &lt;Button>Click Me!&lt;/Button>; }</code></pre> <!-- /wp:code --> diff --git a/packages/escape-html/README.md b/packages/escape-html/README.md index 764a6052d7e79..db2e16909ca39 100644 --- a/packages/escape-html/README.md +++ b/packages/escape-html/README.md @@ -32,6 +32,7 @@ _Related_ _Parameters_ - _value_ `string`: Original string. +- _all_ `boolean`: Set to true to escape all ampersands. _Returns_ @@ -64,6 +65,20 @@ _Returns_ - `string`: Escaped attribute value. +<a name="escapeEditableHTML" href="#escapeEditableHTML">#</a> **escapeEditableHTML** + +Returns an escaped Editable HTML element value. This is different from +`escapeHTML`, because for editable HTML, all ampersands must be escaped in +order to render the content correctly on the page. + +_Parameters_ + +- _value_ `string`: Element value. + +_Returns_ + +- `string`: Escaped HTML element value. + <a name="escapeHTML" href="#escapeHTML">#</a> **escapeHTML** Returns an escaped HTML element value. diff --git a/packages/escape-html/src/index.js b/packages/escape-html/src/index.js index 86fa30ac400c1..bc6136966c0b4 100644 --- a/packages/escape-html/src/index.js +++ b/packages/escape-html/src/index.js @@ -111,19 +111,6 @@ export function escapeEditableHTML( value ) { return escapeLessThan( escapeAmpersand( value, true ) ); } -/** - * Returns an unescaped Editable HTML element value. - * - * @param {string} value Element value. - * - * @return {string} Escaped HTML element value. - */ -export function unescapeEditableHTML( value ) { - return value - .replace( /(?<!&amp;)&lt;/gi, '<' ) - .replace( /&amp;/gi, '&' ); -} - /** * Returns true if the given attribute name is valid, or false otherwise. * From e043fdf83085f44a7365d3e1990fcec8fd90755c Mon Sep 17 00:00:00 2001 From: iseulde <wp@iseulde.com> Date: Thu, 17 Oct 2019 14:05:35 +0200 Subject: [PATCH 5/8] Adjust code block escape util --- packages/block-library/src/code/save.js | 7 +--- packages/block-library/src/code/utils.js | 46 ++++-------------------- 2 files changed, 7 insertions(+), 46 deletions(-) diff --git a/packages/block-library/src/code/save.js b/packages/block-library/src/code/save.js index 410e47284011e..ece9eef96dae3 100644 --- a/packages/block-library/src/code/save.js +++ b/packages/block-library/src/code/save.js @@ -1,13 +1,8 @@ -/** - * WordPress dependencies - */ -import { escapeEditableHTML } from '@wordpress/escape-html'; - /** * Internal dependencies */ import { escape } from './utils'; export default function save( { attributes } ) { - return <pre><code>{ escape( escapeEditableHTML( attributes.content ) ) }</code></pre>; + return <pre><code>{ escape( attributes.content ) }</code></pre>; } diff --git a/packages/block-library/src/code/utils.js b/packages/block-library/src/code/utils.js index 9cc2180bcc7a1..5178e3d0135fc 100644 --- a/packages/block-library/src/code/utils.js +++ b/packages/block-library/src/code/utils.js @@ -3,6 +3,11 @@ */ import { flow } from 'lodash'; +/** + * WordPress dependencies + */ +import { escapeEditableHTML } from '@wordpress/escape-html'; + /** * Escapes ampersands, shortcodes, and links. * @@ -11,24 +16,12 @@ import { flow } from 'lodash'; */ export function escape( content ) { return flow( + escapeEditableHTML, escapeOpeningSquareBrackets, escapeProtocolInIsolatedUrls )( content || '' ); } -/** - * Unescapes escaped ampersands, shortcodes, and links. - * - * @param {string} content Content with (maybe) escaped ampersands, shortcodes, and links. - * @return {string} The given content with escaped characters unescaped. - */ -export function unescape( content ) { - return flow( - unescapeProtocolInIsolatedUrls, - unescapeOpeningSquareBrackets, - )( content || '' ); -} - /** * Returns the given content with all opening shortcode characters converted * into their HTML entity counterpart (i.e. [ => &#91;). For instance, a @@ -46,16 +39,6 @@ function escapeOpeningSquareBrackets( content ) { return content.replace( /\[/g, '&#91;' ); } -/** - * Returns the given content translating all &#91; into [. - * - * @param {string} content The content of a code block. - * @return {string} The given content with all &#91; into [. - */ -function unescapeOpeningSquareBrackets( content ) { - return content.replace( /&#91;/g, '[' ); -} - /** * Converts the first two forward slashes of any isolated URL into their HTML * counterparts (i.e. // => &#47;&#47;). For instance, https://youtube.com/watch?x @@ -73,20 +56,3 @@ function unescapeOpeningSquareBrackets( content ) { function escapeProtocolInIsolatedUrls( content ) { return content.replace( /^(\s*https?:)\/\/([^\s<>"]+\s*)$/m, '$1&#47;&#47;$2' ); } - -/** - * Converts the first two forward slashes of any isolated URL from the HTML entity - * &#73; into /. - * - * An isolated URL is a URL that sits in its own line, surrounded only by spacing - * characters. - * - * See https://github.com/WordPress/wordpress-develop/blob/5.1.1/src/wp-includes/class-wp-embed.php#L403 - * - * @param {string} content The content of a code block. - * @return {string} The given content with the first two forward slashes of any - * isolated URL from the HTML entity &#73; into /. - */ -function unescapeProtocolInIsolatedUrls( content ) { - return content.replace( /^(\s*https?:)&#47;&#47;([^\s<>"]+\s*)$/m, '$1//$2' ); -} From 3b6409d9a60690f839341d04e195ed1e973b4626 Mon Sep 17 00:00:00 2001 From: iseulde <wp@iseulde.com> Date: Thu, 17 Oct 2019 14:28:09 +0200 Subject: [PATCH 6/8] Update package lock --- package-lock.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index e489d2de0e393..7d9582d1c9006 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7288,6 +7288,7 @@ "@wordpress/deprecated": "file:packages/deprecated", "@wordpress/editor": "file:packages/editor", "@wordpress/element": "file:packages/element", + "@wordpress/escape-html": "file:packages/escape-html", "@wordpress/i18n": "file:packages/i18n", "@wordpress/is-shallow-equal": "file:packages/is-shallow-equal", "@wordpress/keycodes": "file:packages/keycodes", @@ -22199,7 +22200,7 @@ "dependencies": { "clone-deep": { "version": "0.2.4", - "resolved": "http://registry.npmjs.org/clone-deep/-/clone-deep-0.2.4.tgz", + "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-0.2.4.tgz", "integrity": "sha1-TnPdCen7lxzDhnDF3O2cGJZIHMY=", "dev": true, "requires": { @@ -22233,7 +22234,7 @@ "dependencies": { "kind-of": { "version": "2.0.1", - "resolved": "http://registry.npmjs.org/kind-of/-/kind-of-2.0.1.tgz", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-2.0.1.tgz", "integrity": "sha1-AY7HpM5+OobLkUG+UZ0kyPqpgbU=", "dev": true, "requires": { From 03b8212c7efb527dd327a2e68c338b5ed1b242db Mon Sep 17 00:00:00 2001 From: Ella van Durpe <ella@vandurpe.com> Date: Thu, 7 Nov 2019 10:40:12 +0100 Subject: [PATCH 7/8] Add test case for escapeEditableHTML --- packages/escape-html/src/index.js | 12 +++++------- packages/escape-html/src/test/index.js | 9 +++++++++ 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/packages/escape-html/src/index.js b/packages/escape-html/src/index.js index bc6136966c0b4..8b72830a74c4b 100644 --- a/packages/escape-html/src/index.js +++ b/packages/escape-html/src/index.js @@ -26,14 +26,12 @@ const REGEXP_INVALID_ATTRIBUTE_NAME = /[\u007F-\u009F "'>/="\uFDD0-\uFDEF]/; * @see https://w3c.github.io/html/syntax.html#ambiguous-ampersand * @see https://w3c.github.io/html/syntax.html#named-character-references * - * @param {string} value Original string. - * @param {boolean} all Set to true to escape all ampersands. + * @param {string} value Original string. * * @return {string} Escaped string. */ -export function escapeAmpersand( value, all ) { - const regExp = all ? /&/gi : /&(?!([a-z0-9]+|#[0-9]+|#x[a-f0-9]+);)/gi; - return value.replace( regExp, '&amp;' ); +export function escapeAmpersand( value ) { + return value.replace( /&(?!([a-z0-9]+|#[0-9]+|#x[a-f0-9]+);)/gi, '&amp;' ); } /** @@ -100,7 +98,7 @@ export function escapeHTML( value ) { /** * Returns an escaped Editable HTML element value. This is different from - * `escapeHTML`, because for editable HTML, all ampersands must be escaped in + * `escapeHTML`, because for editable HTML, ALL ampersands must be escaped in * order to render the content correctly on the page. * * @param {string} value Element value. @@ -108,7 +106,7 @@ export function escapeHTML( value ) { * @return {string} Escaped HTML element value. */ export function escapeEditableHTML( value ) { - return escapeLessThan( escapeAmpersand( value, true ) ); + return escapeLessThan( value.replace( /&/g, '&amp;' ) ); } /** diff --git a/packages/escape-html/src/test/index.js b/packages/escape-html/src/test/index.js index 9558c58c7e23a..2c564a771f4bd 100644 --- a/packages/escape-html/src/test/index.js +++ b/packages/escape-html/src/test/index.js @@ -8,6 +8,7 @@ import { escapeAttribute, escapeHTML, isValidAttributeName, + escapeEditableHTML, } from '../'; import __unstableEscapeGreaterThan from '../escape-greater'; @@ -94,3 +95,11 @@ describe( 'isValidAttributeName', () => { expect( result ).toBe( true ); } ); } ); + +describe( 'escapeEditableHTML', () => { + it( 'should escape < and all ampersands', () => { + const result = escapeEditableHTML( '<a href="https://w.org">WP</a> & &lt;strong&gt;' ); + + expect( result ).toBe( '&lt;a href="https://w.org">WP&lt;/a> &amp; &amp;lt;strong&amp;gt;' ); + } ); +} ); From 29bb75d659c0eb1e220220e47f89eb9f72d03199 Mon Sep 17 00:00:00 2001 From: Ella van Durpe <ella@vandurpe.com> Date: Thu, 7 Nov 2019 10:47:46 +0100 Subject: [PATCH 8/8] Update docs --- packages/escape-html/README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/packages/escape-html/README.md b/packages/escape-html/README.md index db2e16909ca39..6f11227674813 100644 --- a/packages/escape-html/README.md +++ b/packages/escape-html/README.md @@ -32,7 +32,6 @@ _Related_ _Parameters_ - _value_ `string`: Original string. -- _all_ `boolean`: Set to true to escape all ampersands. _Returns_ @@ -68,7 +67,7 @@ _Returns_ <a name="escapeEditableHTML" href="#escapeEditableHTML">#</a> **escapeEditableHTML** Returns an escaped Editable HTML element value. This is different from -`escapeHTML`, because for editable HTML, all ampersands must be escaped in +`escapeHTML`, because for editable HTML, ALL ampersands must be escaped in order to render the content correctly on the page. _Parameters_