From 4f17abfb4a02601e16b387c0fe33b80208a68316 Mon Sep 17 00:00:00 2001 From: Dylan Thacker-Smith Date: Fri, 9 Sep 2022 11:07:15 -0400 Subject: [PATCH 1/3] Handle truncatewords word length out of range as if no truncation is needed --- lib/liquid/standardfilters.rb | 8 ++++---- test/integration/standard_filter_test.rb | 6 ++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/lib/liquid/standardfilters.rb b/lib/liquid/standardfilters.rb index 1470243c..21bed3c5 100644 --- a/lib/liquid/standardfilters.rb +++ b/lib/liquid/standardfilters.rb @@ -6,7 +6,7 @@ require 'bigdecimal' module Liquid module StandardFilters - MAX_INT = (1 << 31) - 1 + MAX_I32 = (1 << 31) - 1 HTML_ESCAPE = { '&' => '&', '>' => '>', @@ -239,9 +239,9 @@ module Liquid wordlist = begin input.split(" ", words + 1) rescue RangeError - raise if words + 1 < MAX_INT - # e.g. integer #{words} too big to convert to `int' - raise Liquid::ArgumentError, "integer #{words} too big for truncatewords" + # integer too big for String#split, but we can semantically assume no truncation is needed + return input if words + 1 > MAX_I32 + raise # unexpected error end return input if wordlist.length <= words diff --git a/test/integration/standard_filter_test.rb b/test/integration/standard_filter_test.rb index 3b2cffa3..519a7f3f 100644 --- a/test/integration/standard_filter_test.rb +++ b/test/integration/standard_filter_test.rb @@ -227,10 +227,8 @@ class StandardFiltersTest < Minitest::Test assert_equal('one two three...', @filters.truncatewords("one two\tthree\nfour", 3)) assert_equal('one two...', @filters.truncatewords("one two three four", 2)) assert_equal('one...', @filters.truncatewords("one two three four", 0)) - exception = assert_raises(Liquid::ArgumentError) do - @filters.truncatewords("one two three four", 1 << 31) - end - assert_equal("Liquid error: integer #{1 << 31} too big for truncatewords", exception.message) + assert_equal('one two three four', @filters.truncatewords("one two three four", 1 << 31)) + assert_equal('one...', @filters.truncatewords("one two three four", -(1 << 32))) end def test_strip_html From 6765d9393861978388baa1d3c763a9d884de10e9 Mon Sep 17 00:00:00 2001 From: Dylan Thacker-Smith Date: Fri, 9 Sep 2022 11:37:21 -0400 Subject: [PATCH 2/3] Avoid internal errors for large arguments to slice filter Use saturating conversion, which has expected semantics for large integers. --- lib/liquid/standardfilters.rb | 24 ++++++++++++++++++++---- test/integration/standard_filter_test.rb | 8 ++++++++ 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/lib/liquid/standardfilters.rb b/lib/liquid/standardfilters.rb index 21bed3c5..18c79f3c 100644 --- a/lib/liquid/standardfilters.rb +++ b/lib/liquid/standardfilters.rb @@ -7,6 +7,13 @@ require 'bigdecimal' module Liquid module StandardFilters MAX_I32 = (1 << 31) - 1 + private_constant :MAX_I32 + + MIN_I64 = -(1 << 63) + MAX_I64 = (1 << 63) - 1 + I64_RANGE = MIN_I64..MAX_I64 + private_constant :MIN_I64, :MAX_I64, :I64_RANGE + HTML_ESCAPE = { '&' => '&', '>' => '>', @@ -186,10 +193,19 @@ module Liquid offset = Utils.to_integer(offset) length = length ? Utils.to_integer(length) : 1 - if input.is_a?(Array) - input.slice(offset, length) || [] - else - input.to_s.slice(offset, length) || '' + begin + if input.is_a?(Array) + input.slice(offset, length) || [] + else + input.to_s.slice(offset, length) || '' + end + rescue RangeError + if I64_RANGE.cover?(length) && I64_RANGE.cover?(offset) + raise # unexpected error + end + offset = offset.clamp(I64_RANGE) + length = length.clamp(I64_RANGE) + retry end end diff --git a/test/integration/standard_filter_test.rb b/test/integration/standard_filter_test.rb index 519a7f3f..d95f5c9c 100644 --- a/test/integration/standard_filter_test.rb +++ b/test/integration/standard_filter_test.rb @@ -109,6 +109,10 @@ class StandardFiltersTest < Minitest::Test assert_raises(Liquid::ArgumentError) do @filters.slice('foobar', 0, "") end + assert_equal("", @filters.slice("foobar", 0, -(1 << 64))) + assert_equal("foobar", @filters.slice("foobar", 0, 1 << 63)) + assert_equal("", @filters.slice("foobar", 1 << 63, 6)) + assert_equal("", @filters.slice("foobar", -(1 << 63), 6)) end def test_slice_on_arrays @@ -123,6 +127,10 @@ class StandardFiltersTest < Minitest::Test assert_equal(%w(r), @filters.slice(input, -1)) assert_equal(%w(), @filters.slice(input, 100, 10)) assert_equal(%w(), @filters.slice(input, -100, 10)) + assert_equal([], @filters.slice(input, 0, -(1 << 64))) + assert_equal(input, @filters.slice(input, 0, 1 << 63)) + assert_equal([], @filters.slice(input, 1 << 63, 6)) + assert_equal([], @filters.slice(input, -(1 << 63), 6)) end def test_truncate From c0c191cabd1138753c0a04373fc2b7bf47238055 Mon Sep 17 00:00:00 2001 From: Dylan Thacker-Smith Date: Fri, 9 Sep 2022 11:48:37 -0400 Subject: [PATCH 3/3] Add assertions for truncate filter with large integers --- test/integration/standard_filter_test.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/integration/standard_filter_test.rb b/test/integration/standard_filter_test.rb index d95f5c9c..b633024b 100644 --- a/test/integration/standard_filter_test.rb +++ b/test/integration/standard_filter_test.rb @@ -140,6 +140,8 @@ class StandardFiltersTest < Minitest::Test assert_equal('1234567890', @filters.truncate('1234567890')) assert_equal("测试...", @filters.truncate("测试测试测试测试", 5)) assert_equal('12341', @filters.truncate("1234567890", 5, 1)) + assert_equal("foobar", @filters.truncate("foobar", 1 << 63)) + assert_equal("...", @filters.truncate("foobar", -(1 << 63))) end def test_split