mirror of
https://github.com/Shopify/liquid.git
synced 2026-09-12 23:40:45 -07:00
Both tags previously used only regex (VariableSignature) to validate variable names, which allowed invalid identifiers like (a(b(c) and [x.y] in all parse modes. - assign: strict2_parse uses Parser to validate the LHS as a valid identifier before delegating RHS to Variable - capture: strict2_parse uses Parser to validate the variable name as a valid identifier - Both tags now include ParserSwitching and dispatch through strict_parse_with_error_mode_fallback - Lax mode is unchanged — invalid names are still accepted Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
158 lines
4.5 KiB
Ruby
158 lines
4.5 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require 'test_helper'
|
|
|
|
class AssignTest < Minitest::Test
|
|
include Liquid
|
|
|
|
def test_assign_with_hyphen_in_variable_name
|
|
template_source = <<~END_TEMPLATE
|
|
{% assign this-thing = 'Print this-thing' -%}
|
|
{{ this-thing -}}
|
|
END_TEMPLATE
|
|
assert_template_result("Print this-thing", template_source)
|
|
end
|
|
|
|
def test_assigned_variable
|
|
assert_template_result(
|
|
'.foo.',
|
|
'{% assign foo = values %}.{{ foo[0] }}.',
|
|
{ 'values' => %w(foo bar baz) },
|
|
)
|
|
|
|
assert_template_result(
|
|
'.bar.',
|
|
'{% assign foo = values %}.{{ foo[1] }}.',
|
|
{ 'values' => %w(foo bar baz) },
|
|
)
|
|
end
|
|
|
|
def test_assign_with_filter
|
|
assert_template_result(
|
|
'.bar.',
|
|
'{% assign foo = values | split: "," %}.{{ foo[1] }}.',
|
|
{ 'values' => "foo,bar,baz" },
|
|
)
|
|
end
|
|
|
|
def test_assign_syntax_error
|
|
assert_match_syntax_error(/assign/, '{% assign foo not values %}.')
|
|
end
|
|
|
|
def test_assign_uses_error_mode
|
|
assert_match_syntax_error(
|
|
"Expected dotdot but found pipe in ",
|
|
"{% assign foo = ('X' | downcase) %}",
|
|
error_mode: :strict,
|
|
)
|
|
assert_template_result("", "{% assign foo = ('X' | downcase) %}", error_mode: :lax)
|
|
end
|
|
|
|
def test_expression_with_whitespace_in_square_brackets
|
|
source = "{% assign r = a[ 'b' ] %}{{ r }}"
|
|
assert_template_result('result', source, { 'a' => { 'b' => 'result' } })
|
|
end
|
|
|
|
def test_assign_score_exceeding_resource_limit
|
|
t = Template.parse("{% assign foo = 42 %}{% assign bar = 23 %}")
|
|
t.resource_limits.assign_score_limit = 1
|
|
assert_equal("Liquid error: Memory limits exceeded", t.render)
|
|
assert(t.resource_limits.reached?)
|
|
|
|
t.resource_limits.assign_score_limit = 2
|
|
assert_equal("", t.render!)
|
|
refute_nil(t.resource_limits.assign_score)
|
|
end
|
|
|
|
def test_assign_score_exceeding_limit_from_composite_object
|
|
t = Template.parse("{% assign foo = 'aaaa' | reverse %}")
|
|
|
|
t.resource_limits.assign_score_limit = 3
|
|
assert_equal("Liquid error: Memory limits exceeded", t.render)
|
|
assert(t.resource_limits.reached?)
|
|
|
|
t.resource_limits.assign_score_limit = 5
|
|
assert_equal("", t.render!)
|
|
end
|
|
|
|
def test_assign_score_of_int
|
|
assert_equal(1, assign_score_of(123))
|
|
end
|
|
|
|
def test_assign_score_of_string_counts_bytes
|
|
assert_equal(3, assign_score_of('123'))
|
|
assert_equal(5, assign_score_of('12345'))
|
|
assert_equal(9, assign_score_of('すごい'))
|
|
end
|
|
|
|
def test_assign_score_of_array
|
|
assert_equal(1, assign_score_of([]))
|
|
assert_equal(2, assign_score_of([123]))
|
|
assert_equal(6, assign_score_of([123, 'abcd']))
|
|
end
|
|
|
|
def test_assign_score_of_hash
|
|
assert_equal(1, assign_score_of({}))
|
|
assert_equal(5, assign_score_of('int' => 123))
|
|
assert_equal(12, assign_score_of('int' => 123, 'str' => 'abcd'))
|
|
end
|
|
|
|
def test_assign_with_valid_identifier_in_strict2
|
|
assert_template_result("hello", "{% assign my_var = 'hello' %}{{ my_var }}", error_mode: :strict2)
|
|
end
|
|
|
|
def test_assign_with_hyphen_in_strict2
|
|
assert_template_result("hello", "{% assign my-var = 'hello' %}{{ my-var }}", error_mode: :strict2)
|
|
end
|
|
|
|
def test_assign_rejects_parentheses_in_variable_name_in_strict2
|
|
assert_raises(Liquid::SyntaxError) do
|
|
Liquid::Template.parse("{% assign (a(b(c) = 1234 %}", error_mode: :strict2)
|
|
end
|
|
end
|
|
|
|
def test_assign_rejects_brackets_in_variable_name_in_strict2
|
|
assert_raises(Liquid::SyntaxError) do
|
|
Liquid::Template.parse("{% assign [x.y] = 'hello' %}", error_mode: :strict2)
|
|
end
|
|
end
|
|
|
|
def test_assign_rejects_dot_in_variable_name_in_strict2
|
|
assert_raises(Liquid::SyntaxError) do
|
|
Liquid::Template.parse("{% assign a.b = 'hello' %}", error_mode: :strict2)
|
|
end
|
|
end
|
|
|
|
def test_assign_rejects_numeric_variable_name_in_strict2
|
|
assert_raises(Liquid::SyntaxError) do
|
|
Liquid::Template.parse("{% assign 1abc = 'hello' %}", error_mode: :strict2)
|
|
end
|
|
end
|
|
|
|
def test_assign_allows_invalid_names_in_lax
|
|
assert_template_result("1234", "{% assign (a(b(c) = 1234 %}{{ self['(a(b(c)'] }}", error_mode: :lax)
|
|
end
|
|
|
|
def test_assign_with_filter_in_strict2
|
|
assert_template_result("HELLO", "{% assign my_var = 'hello' | upcase %}{{ my_var }}", error_mode: :strict2)
|
|
end
|
|
|
|
private
|
|
|
|
class ObjectWrapperDrop < Liquid::Drop
|
|
def initialize(obj)
|
|
@obj = obj
|
|
end
|
|
|
|
def value
|
|
@obj
|
|
end
|
|
end
|
|
|
|
def assign_score_of(obj)
|
|
context = Liquid::Context.new('drop' => ObjectWrapperDrop.new(obj))
|
|
Liquid::Template.parse('{% assign obj = drop.value %}').render!(context)
|
|
context.resource_limits.assign_score
|
|
end
|
|
end
|