mirror of
https://github.com/Shopify/liquid.git
synced 2026-09-19 02:40:41 -07:00
Convert Strainer to white-list method protection
After moving the method existence check from Context into Strainer, updated Strainer to only accept invokation methods that were added via filter Modules, and done in a way that respond_to? is never called, preventing unconstrained Symbol table growth.
This commit is contained in:
@@ -71,11 +71,7 @@ module Liquid
|
||||
end
|
||||
|
||||
def invoke(method, *args)
|
||||
if strainer.respond_to?(method)
|
||||
strainer.__send__(method, *args)
|
||||
else
|
||||
args.first
|
||||
end
|
||||
strainer.invoke(method, *args)
|
||||
end
|
||||
|
||||
# Push new local scope on the stack. use <tt>Context#stack</tt> instead
|
||||
|
||||
+18
-20
@@ -9,16 +9,11 @@ module Liquid
|
||||
end
|
||||
|
||||
# Strainer is the parent class for the filters system.
|
||||
# New filters are mixed into the strainer class which is then instanciated for each liquid template render run.
|
||||
# New filters are mixed into the strainer class which is then instantiated for each liquid template render run.
|
||||
#
|
||||
# One of the strainer's responsibilities is to keep malicious method calls out
|
||||
# The Strainer only allows method calls defined in filters given to it via Strainer.global_filter,
|
||||
# Context#add_filters or Template.register_filter
|
||||
class Strainer < parent_object #:nodoc:
|
||||
INTERNAL_METHOD = /^__/
|
||||
@@required_methods = Set.new([:__id__, :__send__, :respond_to?, :kind_of?, :extend, :methods, :singleton_methods, :class, :object_id])
|
||||
|
||||
# Ruby 1.9.2 introduces Object#respond_to_missing?, which is invoked by Object#respond_to?
|
||||
@@required_methods << :respond_to_missing? if Object.respond_to? :respond_to_missing?
|
||||
|
||||
@@filters = {}
|
||||
|
||||
def initialize(context)
|
||||
@@ -36,19 +31,22 @@ module Liquid
|
||||
strainer
|
||||
end
|
||||
|
||||
def respond_to?(method, include_private = false)
|
||||
method_name = method.to_s
|
||||
return false if method_name =~ INTERNAL_METHOD
|
||||
return false if @@required_methods.include?(method_name)
|
||||
super
|
||||
end
|
||||
|
||||
# remove all standard methods from the bucket so circumvent security
|
||||
# problems
|
||||
instance_methods.each do |m|
|
||||
unless @@required_methods.include?(m.to_sym)
|
||||
undef_method m
|
||||
def invoke(method, *args)
|
||||
if has_method?(method)
|
||||
send(method, *args)
|
||||
else
|
||||
args.first
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def has_method?(method)
|
||||
methods_to_check = self.methods - self.class.public_instance_methods
|
||||
methods_to_check.any? do |instance_method|
|
||||
instance_method.to_s == method.to_s
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user