@@ -371,6 +371,64 @@ defmodule Phoenix.HTML do
371371
372372 defp javascript_escape ( << >> , acc ) , do: acc
373373
374+ @ doc ~S"""
375+ Escapes encoded JSON for safe insertion into an HTML script tag.
376+
377+ The given JSON must already be encoded. The characters `<`, `>`, and `&`
378+ are encoded as JSON Unicode escape sequences and the result is marked as
379+ HTML safe.
380+
381+ iex> json = ~s({"name":"</script>"})
382+ iex> json |> json_escape() |> safe_to_string()
383+ "{\" name\" :\" \\u003C/script\\u003E\" }"
384+
385+ """
386+ @ spec json_escape ( binary ) :: safe
387+ def json_escape ( data ) when is_binary ( data ) do
388+ { :safe , json_escape ( data , 0 , data , [ ] ) }
389+ end
390+
391+ escapes = [
392+ { ?< , "\\ u003C" } ,
393+ { ?> , "\\ u003E" } ,
394+ { ?& , "\\ u0026" }
395+ ]
396+
397+ for { match , insert } <- escapes do
398+ defp json_escape ( << unquote ( match ) , rest :: bits >> , skip , original , acc ) do
399+ json_escape ( rest , skip + 1 , original , [ acc | unquote ( insert ) ] )
400+ end
401+ end
402+
403+ defp json_escape ( << _char , rest :: bits >> , skip , original , acc ) do
404+ json_escape ( rest , skip , original , acc , 1 )
405+ end
406+
407+ defp json_escape ( << >> , 0 , original , _acc ) do
408+ original
409+ end
410+
411+ defp json_escape ( << >> , _skip , _original , acc ) do
412+ acc
413+ end
414+
415+ for { match , insert } <- escapes do
416+ defp json_escape ( << unquote ( match ) , rest :: bits >> , skip , original , acc , len ) do
417+ part = binary_part ( original , skip , len )
418+ json_escape ( rest , skip + len + 1 , original , [ acc , part | unquote ( insert ) ] , 0 )
419+ end
420+ end
421+
422+ defp json_escape ( << _char , rest :: bits >> , skip , original , acc , len ) do
423+ json_escape ( rest , skip , original , acc , len + 1 )
424+ end
425+
426+ defp json_escape ( << >> , 0 , original , _acc , _len ) , do: original
427+
428+ defp json_escape ( << >> , skip , original , acc , len ) do
429+ [ acc | binary_part ( original , skip , len ) ]
430+ end
431+
374432 @ doc """
375433 Escapes a string for use as a CSS identifier.
376434
0 commit comments