mirror of
https://github.com/Shopify/liquid.git
synced 2026-10-03 00:55:11 -07:00
Limit how much blocks can be nested during parsing (#894)
This commit is contained in:
committed by
GitHub
parent
62d4625468
commit
9c72ccb82f
+19
-9
@@ -1,5 +1,7 @@
|
|||||||
module Liquid
|
module Liquid
|
||||||
class Block < Tag
|
class Block < Tag
|
||||||
|
MAX_DEPTH = 100
|
||||||
|
|
||||||
def initialize(tag_name, markup, options)
|
def initialize(tag_name, markup, options)
|
||||||
super
|
super
|
||||||
@blank = true
|
@blank = true
|
||||||
@@ -48,17 +50,25 @@ module Liquid
|
|||||||
protected
|
protected
|
||||||
|
|
||||||
def parse_body(body, tokens)
|
def parse_body(body, tokens)
|
||||||
body.parse(tokens, parse_context) do |end_tag_name, end_tag_params|
|
if parse_context.depth >= MAX_DEPTH
|
||||||
@blank &&= body.blank?
|
raise StackLevelError, "Nesting too deep".freeze
|
||||||
|
end
|
||||||
|
parse_context.depth += 1
|
||||||
|
begin
|
||||||
|
body.parse(tokens, parse_context) do |end_tag_name, end_tag_params|
|
||||||
|
@blank &&= body.blank?
|
||||||
|
|
||||||
return false if end_tag_name == block_delimiter
|
return false if end_tag_name == block_delimiter
|
||||||
unless end_tag_name
|
unless end_tag_name
|
||||||
raise SyntaxError.new(parse_context.locale.t("errors.syntax.tag_never_closed".freeze, block_name: block_name))
|
raise SyntaxError.new(parse_context.locale.t("errors.syntax.tag_never_closed".freeze, block_name: block_name))
|
||||||
|
end
|
||||||
|
|
||||||
|
# this tag is not registered with the system
|
||||||
|
# pass it to the current block for special handling or error reporting
|
||||||
|
unknown_tag(end_tag_name, end_tag_params, tokens)
|
||||||
end
|
end
|
||||||
|
ensure
|
||||||
# this tag is not registered with the system
|
parse_context.depth -= 1
|
||||||
# pass it to the current block for special handling or error reporting
|
|
||||||
unknown_tag(end_tag_name, end_tag_params, tokens)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
true
|
true
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ module Liquid
|
|||||||
# Push new local scope on the stack. use <tt>Context#stack</tt> instead
|
# Push new local scope on the stack. use <tt>Context#stack</tt> instead
|
||||||
def push(new_scope = {})
|
def push(new_scope = {})
|
||||||
@scopes.unshift(new_scope)
|
@scopes.unshift(new_scope)
|
||||||
raise StackLevelError, "Nesting too deep".freeze if @scopes.length > 100
|
raise StackLevelError, "Nesting too deep".freeze if @scopes.length > Block::MAX_DEPTH
|
||||||
end
|
end
|
||||||
|
|
||||||
# Merge a hash of variables in the current local scope
|
# Merge a hash of variables in the current local scope
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
module Liquid
|
module Liquid
|
||||||
class ParseContext
|
class ParseContext
|
||||||
attr_accessor :locale, :line_number, :trim_whitespace
|
attr_accessor :locale, :line_number, :trim_whitespace, :depth
|
||||||
attr_reader :partial, :warnings, :error_mode
|
attr_reader :partial, :warnings, :error_mode
|
||||||
|
|
||||||
def initialize(options = {})
|
def initialize(options = {})
|
||||||
@template_options = options ? options.dup : {}
|
@template_options = options ? options.dup : {}
|
||||||
@locale = @template_options[:locale] ||= I18n.new
|
@locale = @template_options[:locale] ||= I18n.new
|
||||||
@warnings = []
|
@warnings = []
|
||||||
|
self.depth = 0
|
||||||
self.partial = false
|
self.partial = false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@@ -63,4 +63,18 @@ class SecurityTest < Minitest::Test
|
|||||||
|
|
||||||
assert_equal [], (Symbol.all_symbols - current_symbols)
|
assert_equal [], (Symbol.all_symbols - current_symbols)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_max_depth_nested_blocks_does_not_raise_exception
|
||||||
|
depth = Liquid::Block::MAX_DEPTH
|
||||||
|
code = "{% if true %}" * depth + "rendered" + "{% endif %}" * depth
|
||||||
|
assert_equal "rendered", Template.parse(code).render!
|
||||||
|
end
|
||||||
|
|
||||||
|
def test_more_than_max_depth_nested_blocks_raises_exception
|
||||||
|
depth = Liquid::Block::MAX_DEPTH + 1
|
||||||
|
code = "{% if true %}" * depth + "rendered" + "{% endif %}" * depth
|
||||||
|
assert_raises(Liquid::StackLevelError) do
|
||||||
|
Template.parse(code).render!
|
||||||
|
end
|
||||||
|
end
|
||||||
end # SecurityTest
|
end # SecurityTest
|
||||||
|
|||||||
@@ -137,7 +137,7 @@ class IncludeTagTest < Minitest::Test
|
|||||||
|
|
||||||
Liquid::Template.file_system = infinite_file_system.new
|
Liquid::Template.file_system = infinite_file_system.new
|
||||||
|
|
||||||
assert_raises(Liquid::StackLevelError, SystemStackError) do
|
assert_raises(Liquid::StackLevelError) do
|
||||||
Template.parse("{% include 'loop' %}").render!
|
Template.parse("{% include 'loop' %}").render!
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user