From 3e025966ccbb2fdb190a1b4c3c4542109dc72fa7 Mon Sep 17 00:00:00 2001 From: Ryan Grove Date: Sat, 11 Jan 2020 21:08:41 -0800 Subject: [PATCH] Limit number values to a sensible range Number values are now limited to a maximum of `Float::MAX` and a minimum of negative `Float::MAX`. Internally, `Integer` is now used for numbers parsed as the "integer" type (as defined in the spec), while `Float` is used for numbers parsed as the "number" type. Fixes #10 --- lib/crass/tokenizer.rb | 45 +++++++---- test/shared/parse_rules.rb | 149 +++++++++++++++++++++++++++++++++++++ 2 files changed, 181 insertions(+), 13 deletions(-) diff --git a/lib/crass/tokenizer.rb b/lib/crass/tokenizer.rb index b0a9471..d6ab9a1 100644 --- a/lib/crass/tokenizer.rb +++ b/lib/crass/tokenizer.rb @@ -429,27 +429,36 @@ def consume_number # 4.3.3. http://dev.w3.org/csswg/css-syntax/#consume-a-numeric-token def consume_numeric number = consume_number + repr = number[0] + value = number[1] + type = number[2] + + if type == :integer + value = value.to_i + else + value = value.to_f + end if start_identifier?(@s.peek(3)) create_token(:dimension, - :repr => number[0], - :type => number[2], - :unit => consume_name, - :value => number[1]) + :repr => repr, + :type => type, + :unit => consume_name, + :value => value) elsif @s.peek == '%' @s.consume create_token(:percentage, - :repr => number[0], - :type => number[2], - :value => number[1]) + :repr => repr, + :type => type, + :value => value) else create_token(:number, - :repr => number[0], - :type => number[2], - :value => number[1]) + :repr => repr, + :type => type, + :value => value) end end @@ -588,9 +597,19 @@ def convert_string_to_number(str) t = matches[:exponent_sign] == '-' ? -1 : 1 e = matches[:exponent].to_i - # I know this looks nutty, but it's exactly what's defined in the spec, - # and it works. - s * (i + f * 10**-d) * 10**(t * e) + # I know this formula looks nutty, but it's exactly what's defined in the + # spec, and it works. + value = s * (i + f * 10**-d) * 10**(t * e) + + # Maximum and minimum values aren't defined in the spec, but are enforced + # here for sanity. + if value > Float::MAX + value = Float::MAX + elsif value < -Float::MAX + value = -Float::MAX + end + + value end # Creates and returns a new token with the given _properties_. diff --git a/test/shared/parse_rules.rb b/test/shared/parse_rules.rb index 891fb69..2f9ef06 100644 --- a/test/shared/parse_rules.rb +++ b/test/shared/parse_rules.rb @@ -313,6 +313,155 @@ end end + # https://github.com/rgrove/crass/issues/10 + it 'should limit number values to a maximum of `Float::MAX`' do + tree = parse("div { height: 5e99999 }") + + assert_equal([ + {:node=>:style_rule, + :selector=> + {:node=>:selector, + :value=>"div", + :tokens=> + [{:node=>:ident, :pos=>0, :raw=>"div", :value=>"div"}, + {:node=>:whitespace, :pos=>3, :raw=>" "}]}, + :children=> + [{:node=>:whitespace, :pos=>5, :raw=>" "}, + {:node=>:property, + :name=>"height", + :value=>"5e99999", + :children=> + [{:node=>:whitespace, :pos=>13, :raw=>" "}, + {:node=>:number, + :pos=>14, + :raw=>"5e99999", + :repr=>"5e99999", + :type=>:number, + :value=>1.7976931348623157e+308}, + {:node=>:whitespace, :pos=>21, :raw=>" "}], + :important=>false, + :tokens=> + [{:node=>:ident, :pos=>6, :raw=>"height", :value=>"height"}, + {:node=>:colon, :pos=>12, :raw=>":"}, + {:node=>:whitespace, :pos=>13, :raw=>" "}, + {:node=>:number, + :pos=>14, + :raw=>"5e99999", + :repr=>"5e99999", + :type=>:number, + :value=>1.7976931348623157e+308}, + {:node=>:whitespace, :pos=>21, :raw=>" "}]}]} + ], tree) + end + + # https://github.com/rgrove/crass/issues/10 + it 'should limit number values to a minimum of `-Float::MAX`' do + tree = parse("div { margin: -5e99999 }") + + assert_equal([ + {:node=>:style_rule, + :selector=> + {:node=>:selector, + :value=>"div", + :tokens=> + [{:node=>:ident, :pos=>0, :raw=>"div", :value=>"div"}, + {:node=>:whitespace, :pos=>3, :raw=>" "}]}, + :children=> + [{:node=>:whitespace, :pos=>5, :raw=>" "}, + {:node=>:property, + :name=>"margin", + :value=>"-5e99999", + :children=> + [{:node=>:whitespace, :pos=>13, :raw=>" "}, + {:node=>:number, + :pos=>14, + :raw=>"-5e99999", + :repr=>"-5e99999", + :type=>:number, + :value=>-1.7976931348623157e+308}, + {:node=>:whitespace, :pos=>22, :raw=>" "}], + :important=>false, + :tokens=> + [{:node=>:ident, :pos=>6, :raw=>"margin", :value=>"margin"}, + {:node=>:colon, :pos=>12, :raw=>":"}, + {:node=>:whitespace, :pos=>13, :raw=>" "}, + {:node=>:number, + :pos=>14, + :raw=>"-5e99999", + :repr=>"-5e99999", + :type=>:number, + :value=>-1.7976931348623157e+308}, + {:node=>:whitespace, :pos=>22, :raw=>" "}]}]} + ], tree) + end + + # https://github.com/rgrove/crass/issues/10 + it 'should parse a class selector that looks like an exponent' do + tree = parse("p.5e1367490fa5f06927cafe55msonormal {}") + + assert_equal([ + {:node=>:style_rule, + :selector=> + {:node=>:selector, + :value=>"p.5e1367490fa5f06927cafe55msonormal", + :tokens=> + [{:node=>:ident, :pos=>0, :raw=>"p", :value=>"p"}, + {:node=>:dimension, + :pos=>1, + :raw=>".5e1367490fa5f06927cafe55msonormal", + :repr=>".5e1367490", + :type=>:number, + :unit=>"fa5f06927cafe55msonormal", + :value=>1.7976931348623157e+308}, + {:node=>:whitespace, :pos=>35, :raw=>" "}]}, + :children=>[]} + ], tree) + end + + # https://github.com/rgrove/crass/issues/10 + it 'should parse a property value that looks like an exponent' do + tree = parse("p { mso-style-name:5e1367490fa5f06927cafe55msonormal }") + + assert_equal([ + {:node=>:style_rule, + :selector=> + {:node=>:selector, + :value=>"p", + :tokens=> + [{:node=>:ident, :pos=>0, :raw=>"p", :value=>"p"}, + {:node=>:whitespace, :pos=>1, :raw=>" "}]}, + :children=> + [{:node=>:whitespace, :pos=>3, :raw=>" "}, + {:node=>:property, + :name=>"mso-style-name", + :value=>"5e1367490fa5f06927cafe55msonormal", + :children=> + [{:node=>:dimension, + :pos=>19, + :raw=>"5e1367490fa5f06927cafe55msonormal", + :repr=>"5e1367490", + :type=>:number, + :unit=>"fa5f06927cafe55msonormal", + :value=>1.7976931348623157e+308}, + {:node=>:whitespace, :pos=>52, :raw=>" "}], + :important=>false, + :tokens=> + [{:node=>:ident, + :pos=>4, + :raw=>"mso-style-name", + :value=>"mso-style-name"}, + {:node=>:colon, :pos=>18, :raw=>":"}, + {:node=>:dimension, + :pos=>19, + :raw=>"5e1367490fa5f06927cafe55msonormal", + :repr=>"5e1367490", + :type=>:number, + :unit=>"fa5f06927cafe55msonormal", + :value=>1.7976931348623157e+308}, + {:node=>:whitespace, :pos=>52, :raw=>" "}]}]} + ], tree) + end + it 'should parse property values containing functions' do tree = parse("p:before { content: a\\ttr(data-foo) \" \"; }")