Skip to content

Commit

Permalink
Merge pull request #2 from lucasuyezu/eval_less_parsing
Browse files Browse the repository at this point in the history
Eval less parsing
  • Loading branch information
bibstha authored Mar 31, 2017
2 parents 0ba28c9 + 5a5e8ce commit c6c840b
Show file tree
Hide file tree
Showing 7 changed files with 152 additions and 115 deletions.
2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,5 @@ source "https://rubygems.org"
gem 'ruby_parser'
gem 'pry-byebug'
gem 'minitest'
gem 'rake'
gem 'hoe'
5 changes: 5 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ GEM
specs:
byebug (9.0.6)
coderay (1.1.1)
hoe (3.16.0)
rake (>= 0.8, < 13.0)
method_source (0.8.2)
minitest (5.10.1)
pry (0.10.4)
Expand All @@ -12,6 +14,7 @@ GEM
pry-byebug (3.4.2)
byebug (~> 9.0)
pry (~> 0.10)
rake (12.0.0)
ruby_parser (3.8.4)
sexp_processor (~> 4.1)
sexp_processor (4.8.0)
Expand All @@ -21,8 +24,10 @@ PLATFORMS
ruby

DEPENDENCIES
hoe
minitest
pry-byebug
rake
ruby_parser

BUNDLED WITH
Expand Down
4 changes: 2 additions & 2 deletions Manifest.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ History.txt
Manifest.txt
README.txt
Rakefile
lib/hash_parser.rb
test/test_hash_parser.rb
lib/safe_parser.rb
test/test_safe_parser.rb
30 changes: 0 additions & 30 deletions lib/hash_parser.rb

This file was deleted.

46 changes: 46 additions & 0 deletions lib/safe_parser.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
require "ruby_parser"

# Whiltelist based hash string parser
class SafeParser
VERSION = "1.0.0"

UnsafeError = Class.new(StandardError)
attr_reader :string

def initialize(string)
@string = string
end

def safe_load
parse(root_expression)
end

private

def parse(expression)
case expression.head
when :hash
Hash[*parse_into_array(expression.values)]
when :array
parse_into_array(expression.values)
when :true
true
when :false
false
when :nil
nil
when :lit, :str
expression.value
else
raise UnsafeError, "#{ string } is a bad hash"
end
end

def root_expression
@root_expression ||= RubyParser.new.parse(string)
end

def parse_into_array(expression)
expression.map { |child_expression| parse(child_expression) }
end
end
83 changes: 0 additions & 83 deletions test/test_hash_parser.rb

This file was deleted.

97 changes: 97 additions & 0 deletions test/test_safe_parser.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
require "minitest/autorun"
require "minitest/spec"
require "minitest/pride"
require "safe_parser"

describe SafeParser do
describe "#safe_load" do
it "fails if it is not a hash" do
strs = ["Book.delete_all", "puts 'hello'", '"#{puts 123}"', "system('ls /')"]
strs.each do |bad_str|
assert_raises SafeParser::UnsafeError, "#{ bad_str } should not be safe" do
SafeParser.new(bad_str).safe_load
end
end
end

it "fails if there are more than one expression" do
strs = ["{ :a => 1 }; 'hello'", "{ :a => 1 }\n{ :b => 2 }"]
strs.each do |bad_str|
assert_raises SafeParser::UnsafeError, "#{ bad_str } should not be safe" do
SafeParser.new(bad_str).safe_load
end
end
end

it "Does not define or redefine any methods" do
str = '{ :a => refine(UnsafeError) { def safe?; "HAHAHA"; end } }'
assert_raises SafeParser::UnsafeError, "#{ str } should not be safe" do
SafeParser.new(str).safe_load
end

str = '{ :a => def SafeParser.safe?; "HAHAHA"; end }'
assert_raises SafeParser::UnsafeError, "#{ str } should not be safe" do
SafeParser.new(str).safe_load
end
end

it "fails if it has assignment" do
strs = ['{ :a => (SafeParser::TEST_CONSTANT = 1) }',
'{ :a => (hello_world = 1) }']
strs.each do |str|
assert_raises SafeParser::UnsafeError, "#{ str } should not be safe" do
SafeParser.new(str).safe_load
end
end
end

it "fails if a hash has a method call" do
strs = ["{ :a => 2 * 2 }",
"{ :a => SOME_CONST }",
"{ :a => system('ls /') }",
"{ :a => Book.delete_all }",
'{ :a => "#{500}" }',
'{ :a => "#{ Book.delete_all }" }',
'{ :a => refine(UnsafeError) { def safe?; "HAHAHA"; end } }',
]
strs.each do |bad_str|
assert_raises SafeParser::UnsafeError, "#{ bad_str } should not be safe" do
SafeParser.new(bad_str).safe_load
end
end
end

it "passes for hashes" do
strs = ["{}", '{ "a" => "A" }', '{ :a => 123 }', '{ :a => true, :b => false, :c => true, "d" => nil }']
parsed = [{}, { "a" => "A" }, { a: 123 }, { a: true, b: false, c: true, "d" => nil }]
strs.each.with_index do |good_str, i|
assert_equal parsed[i], SafeParser.new(good_str).safe_load, "#{ good_str } should be safe"
end
end

it "passes for hashes with sub hashes" do
str = '{ :a => [1, 2, { "x" => "y" }] }'
parsed = { a: [1, 2, { "x" => "y" }] }
assert_equal parsed, SafeParser.new(str).safe_load
end

it "passes for simple literals" do
strs = ["1", "'a string'", ":a_symbol", "false", "true", "12.34"]
parsed = [1, "a string", :a_symbol, false, true, 12.34]
strs.each.with_index do |good_str, i|
assert_equal parsed[i], SafeParser.new(good_str).safe_load, "#{ good_str } should be safe"
end

assert_nil SafeParser.new("nil").safe_load, "The string 'nil' should be safe"
end

it "passes for a complex array" do
strs = ["[]", "['a_string', :a_symbol, true, false, nil, 1_234_567, 12.34]", "[[123], { a: 1, \"b\" => 2}]"]
parsed = [[], ['a_string', :a_symbol, true, false, nil, 1_234_567, 12.34], [[123], { a: 1, "b" => 2}]]
strs.each.with_index do |good_str, i|
assert_equal parsed[i], SafeParser.new(good_str).safe_load, "#{ good_str } should be safe"
end
end
end
end

0 comments on commit c6c840b

Please sign in to comment.