@@ -11,7 +11,7 @@ function isValidURL(val) {
1111
1212/*
1313 * Based on https://github.com/jonschlinkert/get-value/blob/2.0.6/index.js with some enhancements.
14- *
14+ *
1515 * - Performs "hasOwnProperty" checks for safety.
1616 * - Now accepts Handlebars.SafeString paths.
1717 */
@@ -67,6 +67,38 @@ function unwrapIfSafeString(handlebars, val) {
6767 return val ;
6868}
6969
70+ // Maps the char code of characters that are valid inside a JSON string but
71+ // dangerous when that JSON is emitted (unescaped) inside an HTML <script> tag,
72+ // to their \uXXXX escape sequences. Encoding `<` and `/` prevents the HTML
73+ // parser from ever seeing a closing tag such as `</script>`; `>` guards
74+ // against `-->`/`]]>`; U+2028/U+2029 are illegal in JS string literals. Every
75+ // replacement is still valid JSON and round-trips through `JSON.parse`.
76+ const HTML_UNSAFE_JSON_ESCAPES = {
77+ 0x3c : '\\u003c' , // <
78+ 0x3e : '\\u003e' , // >
79+ 0x2f : '\\u002f' , // /
80+ 0x2028 : '\\u2028' ,
81+ 0x2029 : '\\u2029' ,
82+ } ;
83+
84+ const HTML_UNSAFE_JSON_REGEX = / [ < > / \u2028 \u2029 ] / g;
85+
86+ /**
87+ * Escape the output of `JSON.stringify` so it can be safely embedded inside an
88+ * HTML <script> tag. Encodes `<`, `>`, `/` and the U+2028/U+2029 line
89+ * separators as unicode escape sequences. The result is still valid JSON and
90+ * round-trips through `JSON.parse`.
91+ *
92+ * @param {string } jsonString - The output of JSON.stringify
93+ * @returns {string } - The HTML-safe JSON string
94+ */
95+ function escapeJsonForHtml ( jsonString ) {
96+ if ( typeof jsonString !== 'string' ) {
97+ return jsonString ;
98+ }
99+ return jsonString . replace ( HTML_UNSAFE_JSON_REGEX , char => HTML_UNSAFE_JSON_ESCAPES [ char . charCodeAt ( 0 ) ] ) ;
100+ }
101+
70102const maximumPixelSize = 5120 ;
71103
72104/**
@@ -79,7 +111,7 @@ function appendLossyParam(url, lossy) {
79111 if ( ! lossy || typeof lossy !== 'boolean' ) {
80112 return url ;
81113 }
82-
114+
83115 const urlObj = new URL ( url ) ;
84116 urlObj . searchParams . set ( 'compression' , 'lossy' ) ;
85117 return urlObj . toString ( ) ;
@@ -89,6 +121,7 @@ module.exports = {
89121 isValidURL,
90122 getValue,
91123 unwrapIfSafeString,
124+ escapeJsonForHtml,
92125 maximumPixelSize,
93126 appendLossyParam
94127} ;
0 commit comments