mirror of
https://github.com/kemko/liquid.git
synced 2026-01-01 15:55:40 +03:00
Compare commits
14 Commits
c-tokenize
...
2-5-stable
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
46b7fd29df | ||
|
|
34f07dda59 | ||
|
|
712d97e37d | ||
|
|
ca72282dff | ||
|
|
e8a3fd10d4 | ||
|
|
e77b1a09b6 | ||
|
|
73b39beef2 | ||
|
|
fc63219087 | ||
|
|
53b6db48e3 | ||
|
|
0bbc22b027 | ||
|
|
145920738b | ||
|
|
6eb1f174de | ||
|
|
2e71ce1efe | ||
|
|
8204c61e31 |
22
History.md
22
History.md
@@ -1,5 +1,27 @@
|
||||
# Liquid Version History
|
||||
|
||||
## 2.5.5 / 2014-01-10 / branch "2-5-stable"
|
||||
|
||||
Security fix, cherry-picked from master (4e14a65):
|
||||
* Don't call to_sym when creating conditions for security reasons, see #273 [Bouke van der Bijl, bouk]
|
||||
* Prevent arbitrary method invocation on condition objects, see #274 [Dylan Thacker-Smith, dylanahsmith]
|
||||
|
||||
## 2.5.4 / 2013-11-11
|
||||
|
||||
* Fix "can't convert Fixnum into String" for "replace", see #173, [wǒ_is神仙, jsw0528]
|
||||
|
||||
## 2.5.3 / 2013-10-09
|
||||
|
||||
* #232, #234, #237: Fix map filter bugs [Florian Weingarten, fw42]
|
||||
|
||||
## 2.5.2 / 2013-09-03 / deleted
|
||||
|
||||
Yanked from rubygems, as it contained too many changes that broke compatibility. Those changes will be on following major releases.
|
||||
|
||||
## 2.5.1 / 2013-07-24
|
||||
|
||||
* #230: Fix security issue with map filter, Use invoke_drop in map filter [Florian Weingarten, fw42]
|
||||
|
||||
## 2.5.0 / 2013-03-06
|
||||
|
||||
* Prevent Object methods from being called on drops
|
||||
|
||||
@@ -54,7 +54,7 @@ module Liquid
|
||||
|
||||
# Check for method existence without invoking respond_to?, which creates symbols
|
||||
def self.invokable?(method_name)
|
||||
@invokable_methods ||= Set.new((public_instance_methods - Liquid::Drop.public_instance_methods).map(&:to_s))
|
||||
@invokable_methods ||= Set.new(["to_liquid"] + (public_instance_methods - Liquid::Drop.public_instance_methods).map(&:to_s))
|
||||
@invokable_methods.include?(method_name.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -43,7 +43,6 @@ module Liquid
|
||||
'index0' => index,
|
||||
'col' => col + 1,
|
||||
'col0' => col,
|
||||
'index0' => index,
|
||||
'rindex' => length - index,
|
||||
'rindex0' => length - index - 1,
|
||||
'first' => (index == 0),
|
||||
|
||||
@@ -93,21 +93,26 @@ module Liquid
|
||||
# map/collect on a given property
|
||||
def map(input, property)
|
||||
ary = [input].flatten
|
||||
if ary.first.respond_to?('[]') and !ary.first[property].nil?
|
||||
ary.map {|e| e[property] }
|
||||
elsif ary.first.respond_to?(property)
|
||||
ary.map {|e| e.send(property) }
|
||||
ary.map do |e|
|
||||
e = e.call if e.is_a?(Proc)
|
||||
e = e.to_liquid if e.respond_to?(:to_liquid)
|
||||
|
||||
if property == "to_liquid"
|
||||
e
|
||||
elsif e.respond_to?(:[])
|
||||
e[property]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Replace occurrences of a string with another
|
||||
def replace(input, string, replacement = '')
|
||||
input.to_s.gsub(string, replacement)
|
||||
input.to_s.gsub(string, replacement.to_s)
|
||||
end
|
||||
|
||||
# Replace the first occurrences of a string with another
|
||||
def replace_first(input, string, replacement = '')
|
||||
input.to_s.sub(string, replacement)
|
||||
input.to_s.sub(string, replacement.to_s)
|
||||
end
|
||||
|
||||
# remove a substring
|
||||
|
||||
@@ -15,6 +15,7 @@ module Liquid
|
||||
SyntaxHelp = "Syntax Error in tag 'if' - Valid syntax: if [expression]"
|
||||
Syntax = /(#{QuotedFragment})\s*([=!<>a-z_]+)?\s*(#{QuotedFragment})?/o
|
||||
ExpressionsAndOperators = /(?:\b(?:\s?and\s?|\s?or\s?)\b|(?:\s*(?!\b(?:\s?and\s?|\s?or\s?)\b)(?:#{QuotedFragment}|\S+)\s*)+)/o
|
||||
BOOLEAN_OPERATORS = %w(and or)
|
||||
|
||||
def initialize(tag_name, markup, tokens)
|
||||
@blocks = []
|
||||
@@ -61,7 +62,8 @@ module Liquid
|
||||
raise(SyntaxError, SyntaxHelp) unless expressions.shift.to_s =~ Syntax
|
||||
|
||||
new_condition = Condition.new($1, $2, $3)
|
||||
new_condition.send(operator.to_sym, condition)
|
||||
raise SyntaxError, "invalid boolean operator" unless BOOLEAN_OPERATORS.include?(operator)
|
||||
new_condition.send(operator, condition)
|
||||
condition = new_condition
|
||||
end
|
||||
|
||||
@@ -71,8 +73,6 @@ module Liquid
|
||||
@blocks.push(block)
|
||||
@nodelist = block.attach(Array.new)
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
Template.register_tag('if', If)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
Gem::Specification.new do |s|
|
||||
s.name = "liquid"
|
||||
s.version = "2.5.0"
|
||||
s.version = "2.5.5"
|
||||
s.platform = Gem::Platform::RUBY
|
||||
s.summary = "A secure, non-evaling end user template engine with aesthetic markup."
|
||||
s.authors = ["Tobias Luetke"]
|
||||
|
||||
@@ -71,23 +71,34 @@ class DropsTest < Test::Unit::TestCase
|
||||
include Liquid
|
||||
|
||||
def test_product_drop
|
||||
|
||||
assert_nothing_raised do
|
||||
tpl = Liquid::Template.parse( ' ' )
|
||||
tpl.render('product' => ProductDrop.new)
|
||||
end
|
||||
end
|
||||
|
||||
def test_drop_does_only_respond_to_whitelisted_methods
|
||||
assert_equal "", Liquid::Template.parse("{{ product.inspect }}").render('product' => ProductDrop.new)
|
||||
assert_equal "", Liquid::Template.parse("{{ product.pretty_inspect }}").render('product' => ProductDrop.new)
|
||||
assert_equal "", Liquid::Template.parse("{{ product.whatever }}").render('product' => ProductDrop.new)
|
||||
assert_equal "", Liquid::Template.parse('{{ product | map: "inspect" }}').render('product' => ProductDrop.new)
|
||||
assert_equal "", Liquid::Template.parse('{{ product | map: "pretty_inspect" }}').render('product' => ProductDrop.new)
|
||||
assert_equal "", Liquid::Template.parse('{{ product | map: "whatever" }}').render('product' => ProductDrop.new)
|
||||
end
|
||||
|
||||
def test_drops_respond_to_to_liquid
|
||||
assert_equal "text1", Liquid::Template.parse("{{ product.to_liquid.texts.text }}").render('product' => ProductDrop.new)
|
||||
assert_equal "text1", Liquid::Template.parse('{{ product | map: "to_liquid" | map: "texts" | map: "text" }}').render('product' => ProductDrop.new)
|
||||
end
|
||||
|
||||
def test_text_drop
|
||||
output = Liquid::Template.parse( ' {{ product.texts.text }} ' ).render('product' => ProductDrop.new)
|
||||
assert_equal ' text1 ', output
|
||||
|
||||
end
|
||||
|
||||
def test_unknown_method
|
||||
output = Liquid::Template.parse( ' {{ product.catchall.unknown }} ' ).render('product' => ProductDrop.new)
|
||||
assert_equal ' method: unknown ', output
|
||||
|
||||
end
|
||||
|
||||
def test_integer_argument_drop
|
||||
|
||||
@@ -4,6 +4,27 @@ class Filters
|
||||
include Liquid::StandardFilters
|
||||
end
|
||||
|
||||
class TestThing
|
||||
def initialize
|
||||
@foo = 0
|
||||
end
|
||||
|
||||
def to_s
|
||||
"woot: #{@foo}"
|
||||
end
|
||||
|
||||
def to_liquid
|
||||
@foo += 1
|
||||
self
|
||||
end
|
||||
end
|
||||
|
||||
class TestDrop < Liquid::Drop
|
||||
def test
|
||||
"testfoo"
|
||||
end
|
||||
end
|
||||
|
||||
class StandardFiltersTest < Test::Unit::TestCase
|
||||
include Liquid
|
||||
|
||||
@@ -86,6 +107,23 @@ class StandardFiltersTest < Test::Unit::TestCase
|
||||
'ary' => [{'foo' => {'bar' => 'a'}}, {'foo' => {'bar' => 'b'}}, {'foo' => {'bar' => 'c'}}]
|
||||
end
|
||||
|
||||
def test_map_doesnt_call_arbitrary_stuff
|
||||
assert_equal "", Liquid::Template.parse('{{ "foo" | map: "__id__" }}').render
|
||||
assert_equal "", Liquid::Template.parse('{{ "foo" | map: "inspect" }}').render
|
||||
end
|
||||
|
||||
def test_map_calls_to_liquid
|
||||
t = TestThing.new
|
||||
assert_equal "woot: 1", Liquid::Template.parse('{{ foo }}').render("foo" => t)
|
||||
end
|
||||
|
||||
def test_map_over_proc
|
||||
drop = TestDrop.new
|
||||
p = Proc.new{ drop }
|
||||
templ = '{{ procs | map: "test" }}'
|
||||
assert_equal "testfoo", Liquid::Template.parse(templ).render("procs" => [p])
|
||||
end
|
||||
|
||||
def test_date
|
||||
assert_equal 'May', @filters.date(Time.parse("2006-05-05 10:00:00"), "%B")
|
||||
assert_equal 'June', @filters.date(Time.parse("2006-06-05 10:00:00"), "%B")
|
||||
@@ -119,9 +157,9 @@ class StandardFiltersTest < Test::Unit::TestCase
|
||||
end
|
||||
|
||||
def test_replace
|
||||
assert_equal 'b b b b', @filters.replace("a a a a", 'a', 'b')
|
||||
assert_equal 'b a a a', @filters.replace_first("a a a a", 'a', 'b')
|
||||
assert_template_result 'b a a a', "{{ 'a a a a' | replace_first: 'a', 'b' }}"
|
||||
assert_equal '2 2 2 2', @filters.replace('1 1 1 1', '1', 2)
|
||||
assert_equal '2 1 1 1', @filters.replace_first('1 1 1 1', '1', 2)
|
||||
assert_template_result '2 1 1 1', "{{ '1 1 1 1' | replace_first: '1', 2 }}"
|
||||
end
|
||||
|
||||
def test_remove
|
||||
|
||||
@@ -157,4 +157,10 @@ class IfElseTagTest < Test::Unit::TestCase
|
||||
assert_template_result('yes',
|
||||
%({% if 'gnomeslab-and-or-liquid' contains 'gnomeslab-and-or-liquid' %}yes{% endif %}))
|
||||
end
|
||||
|
||||
def test_operators_are_whitelisted
|
||||
assert_raise(SyntaxError) do
|
||||
assert_template_result('', %({% if 1 or throw or or 1 %}yes{% endif %}))
|
||||
end
|
||||
end
|
||||
end # IfElseTest
|
||||
|
||||
Reference in New Issue
Block a user