@@ -8,6 +8,7 @@ class << self
88 delegate :use_standard_json_time_format , :use_standard_json_time_format= ,
99 :time_precision , :time_precision= ,
1010 :escape_html_entities_in_json , :escape_html_entities_in_json= ,
11+ :escape_js_separators_in_json , :escape_js_separators_in_json= ,
1112 :json_encoder , :json_encoder= ,
1213 to : :'ActiveSupport::JSON::Encoding'
1314 end
@@ -67,8 +68,9 @@ module Encoding # :nodoc:
6768 "&" . b => '\u0026' . b ,
6869 }
6970
70- ESCAPE_REGEX_WITH_HTML_ENTITIES = Regexp . union ( *ESCAPED_CHARS . keys )
71- ESCAPE_REGEX_WITHOUT_HTML_ENTITIES = Regexp . union ( U2028 , U2029 )
71+ HTML_ENTITIES_REGEX = Regexp . union ( *( ESCAPED_CHARS . keys - [ U2028 , U2029 ] ) )
72+ FULL_ESCAPE_REGEX = Regexp . union ( *ESCAPED_CHARS . keys )
73+ JS_SEPARATORS_REGEX = Regexp . union ( U2028 , U2029 )
7274
7375 class JSONGemEncoder # :nodoc:
7476 attr_reader :options
@@ -86,14 +88,15 @@ def encode(value)
8688
8789 return json unless @options . fetch ( :escape , true )
8890
89- # Rails does more escaping than the JSON gem natively does (we
90- # escape \u2028 and \u2029 and optionally >, <, & to work around
91- # certain browser problems).
9291 json . force_encoding ( ::Encoding ::BINARY )
9392 if @options . fetch ( :escape_html_entities , Encoding . escape_html_entities_in_json )
94- json . gsub! ( ESCAPE_REGEX_WITH_HTML_ENTITIES , ESCAPED_CHARS )
95- else
96- json . gsub! ( ESCAPE_REGEX_WITHOUT_HTML_ENTITIES , ESCAPED_CHARS )
93+ if Encoding . escape_js_separators_in_json
94+ json . gsub! ( FULL_ESCAPE_REGEX , ESCAPED_CHARS )
95+ else
96+ json . gsub! ( HTML_ENTITIES_REGEX , ESCAPED_CHARS )
97+ end
98+ elsif Encoding . escape_js_separators_in_json
99+ json . gsub! ( JS_SEPARATORS_REGEX , ESCAPED_CHARS )
97100 end
98101 json . force_encoding ( ::Encoding ::UTF_8 )
99102 end
@@ -184,14 +187,15 @@ def encode(value)
184187
185188 return json unless @escape
186189
187- # Rails does more escaping than the JSON gem natively does (we
188- # escape \u2028 and \u2029 and optionally >, <, & to work around
189- # certain browser problems).
190190 json . force_encoding ( ::Encoding ::BINARY )
191191 if @options . fetch ( :escape_html_entities , Encoding . escape_html_entities_in_json )
192- json . gsub! ( ESCAPE_REGEX_WITH_HTML_ENTITIES , ESCAPED_CHARS )
193- else
194- json . gsub! ( ESCAPE_REGEX_WITHOUT_HTML_ENTITIES , ESCAPED_CHARS )
192+ if Encoding . escape_js_separators_in_json
193+ json . gsub! ( FULL_ESCAPE_REGEX , ESCAPED_CHARS )
194+ else
195+ json . gsub! ( HTML_ENTITIES_REGEX , ESCAPED_CHARS )
196+ end
197+ elsif Encoding . escape_js_separators_in_json
198+ json . gsub! ( JS_SEPARATORS_REGEX , ESCAPED_CHARS )
195199 end
196200 json . force_encoding ( ::Encoding ::UTF_8 )
197201 end
@@ -207,6 +211,13 @@ class << self
207211 # as a safety measure.
208212 attr_accessor :escape_html_entities_in_json
209213
214+ # If true, encode LINE SEPARATOR (U+2028) and PARAGRAPH SEPARATOR (U+2029)
215+ # as escaped unicode sequences ('\u2028' and '\u2029').
216+ # Historically these characters were not valid inside JavaScript strings
217+ # but that changed in ECMAScript 2019. As such it's no longer a concern in
218+ # modern browsers: https://caniuse.com/mdn-javascript_builtins_json_json_superset.
219+ attr_accessor :escape_js_separators_in_json
220+
210221 # Sets the precision of encoded time values.
211222 # Defaults to 3 (equivalent to millisecond precision)
212223 attr_accessor :time_precision
@@ -232,6 +243,7 @@ def encode_without_escape(value) # :nodoc:
232243
233244 self . use_standard_json_time_format = true
234245 self . escape_html_entities_in_json = true
246+ self . escape_js_separators_in_json = true
235247 self . json_encoder =
236248 if defined? ( JSONGemCoderEncoder )
237249 JSONGemCoderEncoder
0 commit comments