Skip to content

Commit

Permalink
Merge pull request #1684 from Shopify/fix-variable-lookup-parse-timeout
Browse files Browse the repository at this point in the history
fix variable lookup parse timing out with missing closing bracket
  • Loading branch information
ggmichaelgo committed Feb 2, 2023
2 parents 59c445f + bd9c380 commit 4599e54
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 1 deletion.
2 changes: 1 addition & 1 deletion lib/liquid.rb
Expand Up @@ -41,7 +41,7 @@ module Liquid
AnyStartingTag = /#{TagStart}|#{VariableStart}/o
PartialTemplateParser = /#{TagStart}.*?#{TagEnd}|#{VariableStart}.*?#{VariableIncompleteEnd}/om
TemplateParser = /(#{PartialTemplateParser}|#{AnyStartingTag})/om
VariableParser = /\[(?:[^\[\]]+|\g<0>)*\]|#{VariableSegment}+\??/o
VariableParser = /\[(?>[^\[\]]+|\g<0>)*\]|#{VariableSegment}+\??/o

RAISE_EXCEPTION_LAMBDA = ->(_e) { raise }

Expand Down
35 changes: 35 additions & 0 deletions test/integration/variable_test.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true

require 'test_helper'
require 'timeout'

class VariableTest < Minitest::Test
include Liquid
Expand Down Expand Up @@ -169,4 +170,38 @@ def test_double_nested_variable_lookup
}
)
end

def test_variable_lookup_should_not_hang_with_invalid_syntax
Timeout.timeout(1) do
assert_template_result(
'bar',
"{{['foo'}}",
{
'foo' => 'bar',
},
error_mode: :lax,
)
end

very_long_key = "1234567890" * 100

template_list = [
"{{['#{very_long_key}']}}", # valid
"{{['#{very_long_key}'}}", # missing closing bracket
"{{[['#{very_long_key}']}}", # extra open bracket
]

template_list.each do |template|
Timeout.timeout(1) do
assert_template_result(
'bar',
template,
{
very_long_key => 'bar',
},
error_mode: :lax,
)
end
end
end
end
13 changes: 13 additions & 0 deletions test/unit/regexp_unit_test.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true

require 'test_helper'
require 'timeout'

class RegexpUnitTest < Minitest::Test
include Liquid
Expand Down Expand Up @@ -37,10 +38,22 @@ def test_quoted_words_in_the_middle

def test_variable_parser
assert_equal(['var'], 'var'.scan(VariableParser))
assert_equal(['[var]'], '[var]'.scan(VariableParser))
assert_equal(['var', 'method'], 'var.method'.scan(VariableParser))
assert_equal(['var', '[method]'], 'var[method]'.scan(VariableParser))
assert_equal(['var', '[method]', '[0]'], 'var[method][0]'.scan(VariableParser))
assert_equal(['var', '["method"]', '[0]'], 'var["method"][0]'.scan(VariableParser))
assert_equal(['var', '[method]', '[0]', 'method'], 'var[method][0].method'.scan(VariableParser))
end

def test_variable_parser_with_large_input
Timeout.timeout(1) { assert_equal(['[var]'], '[var]'.scan(VariableParser)) }

very_long_string = "foo" * 1000

# valid dynamic lookup
Timeout.timeout(1) { assert_equal(["[#{very_long_string}]"], "[#{very_long_string}]".scan(VariableParser)) }
# invalid dynamic lookup with missing closing bracket
Timeout.timeout(1) { assert_equal([very_long_string], "[#{very_long_string}".scan(VariableParser)) }
end
end # RegexpTest

0 comments on commit 4599e54

Please sign in to comment.