From 2494b9d8c92c6eb2d94869d887082c8cfd37020c Mon Sep 17 00:00:00 2001 From: Nic Boie Date: Thu, 30 Oct 2025 17:50:53 -0500 Subject: [PATCH 01/10] Deal with nil and encoding issues ruby-3 --- lib/liquid/variable.rb | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/liquid/variable.rb b/lib/liquid/variable.rb index 5b686e2d3..27be2e088 100644 --- a/lib/liquid/variable.rb +++ b/lib/liquid/variable.rb @@ -95,10 +95,13 @@ def render_to_output_buffer(context, output) obj = render(context) if obj.is_a?(Array) - output << obj.join + output.to_s + obj.join elsif obj.nil? else - output << obj.to_s + obj = obj.encode('UTF-8', invalid: :replace, undef: :replace, replace: '') + output_str = output.to_s.encode('UTF-8', invalid: :replace, undef: :replace, replace: '') + + output_str + obj end output From 8e593c9cc1ee970744e45d86b2842195faabc4a2 Mon Sep 17 00:00:00 2001 From: Nic Boie Date: Thu, 30 Oct 2025 18:30:01 -0500 Subject: [PATCH 02/10] another nil string --- lib/liquid/tag.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/liquid/tag.rb b/lib/liquid/tag.rb index ffd22868d..32e648ce7 100644 --- a/lib/liquid/tag.rb +++ b/lib/liquid/tag.rb @@ -58,6 +58,7 @@ def disabled_error_message # of the `render_to_output_buffer` method will become the default and the `render` # method will be removed. def render_to_output_buffer(context, output) + output = output&.to_s || "" output << render(context) output end From 765b6f55c82f5601b742904b7f7518efd73ec9d9 Mon Sep 17 00:00:00 2001 From: Nic Boie Date: Fri, 31 Oct 2025 13:19:07 -0500 Subject: [PATCH 03/10] Return to using buffer properly --- lib/liquid/tag.rb | 1 - lib/liquid/variable.rb | 15 ++++++++++----- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/lib/liquid/tag.rb b/lib/liquid/tag.rb index 32e648ce7..ffd22868d 100644 --- a/lib/liquid/tag.rb +++ b/lib/liquid/tag.rb @@ -58,7 +58,6 @@ def disabled_error_message # of the `render_to_output_buffer` method will become the default and the `render` # method will be removed. def render_to_output_buffer(context, output) - output = output&.to_s || "" output << render(context) output end diff --git a/lib/liquid/variable.rb b/lib/liquid/variable.rb index 27be2e088..3f40ccc45 100644 --- a/lib/liquid/variable.rb +++ b/lib/liquid/variable.rb @@ -94,14 +94,19 @@ def render(context) def render_to_output_buffer(context, output) obj = render(context) + + # This is to find spots where nil is being cast to "", which is a frozen string literal + # in the exact case of nil.to_s, and isn't mutable N.B. + if output.frozen? + raise LiquidError, "Cannot mutate frozen output buffer" + end + if obj.is_a?(Array) - output.to_s + obj.join + output << obj.join elsif obj.nil? else - obj = obj.encode('UTF-8', invalid: :replace, undef: :replace, replace: '') - output_str = output.to_s.encode('UTF-8', invalid: :replace, undef: :replace, replace: '') - - output_str + obj + obj_encoded = obj.encode('UTF-8', invalid: :replace, undef: :replace, replace: '') + output << obj_encoded end output From 74ff12719da58e52fb3bc9c1c243b4318eb917b2 Mon Sep 17 00:00:00 2001 From: Nic Boie Date: Fri, 31 Oct 2025 13:55:10 -0500 Subject: [PATCH 04/10] more encoding and nil fixes --- lib/liquid/block_body.rb | 4 +++- lib/liquid/tag.rb | 7 ++++++- lib/liquid/variable.rb | 10 ++++++---- 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/lib/liquid/block_body.rb b/lib/liquid/block_body.rb index 45be8b8af..47bb064b9 100644 --- a/lib/liquid/block_body.rb +++ b/lib/liquid/block_body.rb @@ -127,7 +127,9 @@ def render_to_output_buffer(context, output) case node when String - output << node + output.force_encoding('UTF-8') if output.encoding != Encoding::UTF_8 + node_utf8 = node.encode('UTF-8', invalid: :replace, undef: :replace, replace: '') + output << node_utf8 when Variable render_node(context, output, node) when Block diff --git a/lib/liquid/tag.rb b/lib/liquid/tag.rb index ffd22868d..c080fb594 100644 --- a/lib/liquid/tag.rb +++ b/lib/liquid/tag.rb @@ -58,7 +58,12 @@ def disabled_error_message # of the `render_to_output_buffer` method will become the default and the `render` # method will be removed. def render_to_output_buffer(context, output) - output << render(context) + rendered = render(context) + if output.nil? + output = rendered.to_s + else + output << rendered.to_s + end output end diff --git a/lib/liquid/variable.rb b/lib/liquid/variable.rb index 3f40ccc45..3c0183d79 100644 --- a/lib/liquid/variable.rb +++ b/lib/liquid/variable.rb @@ -94,18 +94,20 @@ def render(context) def render_to_output_buffer(context, output) obj = render(context) - - # This is to find spots where nil is being cast to "", which is a frozen string literal - # in the exact case of nil.to_s, and isn't mutable N.B. - if output.frozen? + if output.nil? + output = +'' + elsif output.frozen? raise LiquidError, "Cannot mutate frozen output buffer" end if obj.is_a?(Array) output << obj.join elsif obj.nil? + # do nothing else obj_encoded = obj.encode('UTF-8', invalid: :replace, undef: :replace, replace: '') + # Force output to UTF-8 encoding to match obj_encoded + output.force_encoding('UTF-8') if output.encoding != Encoding::UTF_8 output << obj_encoded end From 728d91e85c0e25e5bba04bc2ad5d0422a4c975aa Mon Sep 17 00:00:00 2001 From: Nic Boie Date: Mon, 3 Nov 2025 14:48:45 -0600 Subject: [PATCH 05/10] Fixes to cast else to a string or empty string and return --- lib/liquid/variable.rb | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/lib/liquid/variable.rb b/lib/liquid/variable.rb index 3c0183d79..f82993040 100644 --- a/lib/liquid/variable.rb +++ b/lib/liquid/variable.rb @@ -102,13 +102,16 @@ def render_to_output_buffer(context, output) if obj.is_a?(Array) output << obj.join + elsif obj.kind_of?(Hash) + output << obj.to_s elsif obj.nil? # do nothing - else - obj_encoded = obj.encode('UTF-8', invalid: :replace, undef: :replace, replace: '') - # Force output to UTF-8 encoding to match obj_encoded + elsif obj.kind_of?(String) output.force_encoding('UTF-8') if output.encoding != Encoding::UTF_8 + obj_encoded = obj.encode('UTF-8', invalid: :replace, undef: :replace, replace: '') output << obj_encoded + else + output << obj.try(:to_s) || "" end output From 4fa6d8eaa031373f99bdac8482eee1eb8ae88295 Mon Sep 17 00:00:00 2001 From: Nic Boie Date: Mon, 10 Nov 2025 11:31:37 -0600 Subject: [PATCH 06/10] Changes for ruby mis-identifiying UTF-8 strings as binary --- lib/liquid/block_body.rb | 2 +- lib/liquid/variable.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/liquid/block_body.rb b/lib/liquid/block_body.rb index 47bb064b9..766444f6c 100644 --- a/lib/liquid/block_body.rb +++ b/lib/liquid/block_body.rb @@ -128,7 +128,7 @@ def render_to_output_buffer(context, output) case node when String output.force_encoding('UTF-8') if output.encoding != Encoding::UTF_8 - node_utf8 = node.encode('UTF-8', invalid: :replace, undef: :replace, replace: '') + node_utf8 = node.force_encoding('UTF-8') if node.encoding != Encoding::UTF_8 output << node_utf8 when Variable render_node(context, output, node) diff --git a/lib/liquid/variable.rb b/lib/liquid/variable.rb index f82993040..7c3a47c24 100644 --- a/lib/liquid/variable.rb +++ b/lib/liquid/variable.rb @@ -108,7 +108,7 @@ def render_to_output_buffer(context, output) # do nothing elsif obj.kind_of?(String) output.force_encoding('UTF-8') if output.encoding != Encoding::UTF_8 - obj_encoded = obj.encode('UTF-8', invalid: :replace, undef: :replace, replace: '') + obj_encoded = obj.force_encoding('UTF-8') if obj.encoding != Encoding::UTF_8 output << obj_encoded else output << obj.try(:to_s) || "" From ab88b92798d9eeca04a6c82db3cd4cd9168f0d87 Mon Sep 17 00:00:00 2001 From: Nic Boie Date: Mon, 10 Nov 2025 14:59:34 -0600 Subject: [PATCH 07/10] remove utf-8 forced encoding to replicate error --- lib/liquid/block_body.rb | 7 ++++--- lib/liquid/variable.rb | 7 ++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/liquid/block_body.rb b/lib/liquid/block_body.rb index 766444f6c..91eaf9a6e 100644 --- a/lib/liquid/block_body.rb +++ b/lib/liquid/block_body.rb @@ -127,9 +127,10 @@ def render_to_output_buffer(context, output) case node when String - output.force_encoding('UTF-8') if output.encoding != Encoding::UTF_8 - node_utf8 = node.force_encoding('UTF-8') if node.encoding != Encoding::UTF_8 - output << node_utf8 + # output.force_encoding('UTF-8') if output.encoding != Encoding::UTF_8 + # node_utf8 = node.force_encoding('UTF-8') if node.encoding != Encoding::UTF_8 + # output << node_utf8 + output << node when Variable render_node(context, output, node) when Block diff --git a/lib/liquid/variable.rb b/lib/liquid/variable.rb index 7c3a47c24..86d1dd01d 100644 --- a/lib/liquid/variable.rb +++ b/lib/liquid/variable.rb @@ -107,9 +107,10 @@ def render_to_output_buffer(context, output) elsif obj.nil? # do nothing elsif obj.kind_of?(String) - output.force_encoding('UTF-8') if output.encoding != Encoding::UTF_8 - obj_encoded = obj.force_encoding('UTF-8') if obj.encoding != Encoding::UTF_8 - output << obj_encoded + # output.force_encoding('UTF-8') if output.encoding != Encoding::UTF_8 + # obj_encoded = obj.force_encoding('UTF-8') if obj.encoding != Encoding::UTF_8 + # output << obj_encoded + output << obj else output << obj.try(:to_s) || "" end From e710bdfa77475b32eaf4cd7edeb54edf7f0ee853 Mon Sep 17 00:00:00 2001 From: Nic Boie Date: Mon, 10 Nov 2025 15:13:17 -0600 Subject: [PATCH 08/10] remove unused UTF-8 forced encoding code --- lib/liquid/block_body.rb | 3 --- lib/liquid/variable.rb | 3 --- 2 files changed, 6 deletions(-) diff --git a/lib/liquid/block_body.rb b/lib/liquid/block_body.rb index 91eaf9a6e..45be8b8af 100644 --- a/lib/liquid/block_body.rb +++ b/lib/liquid/block_body.rb @@ -127,9 +127,6 @@ def render_to_output_buffer(context, output) case node when String - # output.force_encoding('UTF-8') if output.encoding != Encoding::UTF_8 - # node_utf8 = node.force_encoding('UTF-8') if node.encoding != Encoding::UTF_8 - # output << node_utf8 output << node when Variable render_node(context, output, node) diff --git a/lib/liquid/variable.rb b/lib/liquid/variable.rb index 86d1dd01d..82f5a72b3 100644 --- a/lib/liquid/variable.rb +++ b/lib/liquid/variable.rb @@ -107,9 +107,6 @@ def render_to_output_buffer(context, output) elsif obj.nil? # do nothing elsif obj.kind_of?(String) - # output.force_encoding('UTF-8') if output.encoding != Encoding::UTF_8 - # obj_encoded = obj.force_encoding('UTF-8') if obj.encoding != Encoding::UTF_8 - # output << obj_encoded output << obj else output << obj.try(:to_s) || "" From d5373583c74e94b6e3c4aba53f6c111e5a571d43 Mon Sep 17 00:00:00 2001 From: Nic Boie Date: Mon, 24 Nov 2025 16:53:30 -0600 Subject: [PATCH 09/10] Force encoding on encoding error only for variables --- lib/liquid/variable.rb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/lib/liquid/variable.rb b/lib/liquid/variable.rb index 82f5a72b3..c367bfb64 100644 --- a/lib/liquid/variable.rb +++ b/lib/liquid/variable.rb @@ -113,6 +113,12 @@ def render_to_output_buffer(context, output) end output + rescue Encoding::CompatibilityError => e + if obj.kind_of?(String) + output.force_encoding('UTF-8') << obj + else + raise e + end end def disabled?(_context) From e3d180b0474195d9f1fa27e9553d9983233290cc Mon Sep 17 00:00:00 2001 From: Nic Boie Date: Fri, 5 Dec 2025 14:56:22 -0600 Subject: [PATCH 10/10] More encoding nonsense --- lib/liquid/block_body.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/liquid/block_body.rb b/lib/liquid/block_body.rb index 45be8b8af..b9d6a0ce5 100644 --- a/lib/liquid/block_body.rb +++ b/lib/liquid/block_body.rb @@ -127,7 +127,7 @@ def render_to_output_buffer(context, output) case node when String - output << node + output.force_encoding('UTF-8') << node.force_encoding('UTF-8') when Variable render_node(context, output, node) when Block