PsychShield provides a filtering mechanism for YAML.load when using the Psych parser
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
lib
.gitignore
Gemfile
LICENSE
README.md
Rakefile
psych_shield.gemspec

README.md

Psych Shield

Psych Shield provides a way to filter objects during a YAML.load call when the Psych parser is used (default in Ruby 1.9). This can prevent malicious input to a YAML.load call from resulting in bad things within your application.

This is a dirty hack that allows applications that need to accept untrusted YAML input to continue doing so until they can be ported to a new format. Note that this DOES NOT protect against the full set of issues identified in CVE-2013-0156. The primary use case of this gem is to lock down YAML.load() calls.

To protect a Ruby on Rails application, add the following to Gemfile:

gem 'psych_shield'

Rails 2 applications should add an initializer that loads this gem.

$ echo 'require "psych_shield"' > config/initializers/load_psych_shield.rb

By default, Psych Shield allows the following types of objects:

Hash Array String Range
Numeric Fixnum Integer Bignum Float Rational Complex
Time DateTime
NilClass TrueClass FalseClass

To enable additional classes, add the stringified form using the "add" method:

PsychShield.add('MyClass::IsAwesome::And::Safe')

To disable all classes (even the defaults), use the clear method:

PsychShield.clear

To figure out what classes you need to allow, you can use the callback:

PsychShield.callback = Proc.new { |klass,result|
    $stderr.puts "#{ result ? "Allowed" : "Denied"} class #{klass}"
}

Denied objects are removed from the resulting ruby implementation

PsychShield passes all expected psych tests included in the Ruby source:

$ ruby -I lib/ -r psych_shield -I /usr/local/rvm/src/ruby-1.9.3-p194/test/ /usr/local/rvm/src/ruby-1.9.3-p194/test/psych/test_psych.rb
Run options: --seed 61780

# Running tests:

................F.....

Finished tests in 0.011105s, 1981.1699 tests/s, 3331.9675 assertions/s.

1) Failure:
test_non_existing_class_on_deserialize(TestPsych) [/usr/local/rvm/src/ruby-1.9.3-p194/test/psych/test_psych.rb:53]:
ArgumentError expected but nothing was raised.

22 tests, 37 assertions, 1 failures, 0 errors, 0 skips

The YAML test has one failure, as is expected (Struct::MyBookStruct was filtered):

$ ruby -I lib/ -r psych_shield -I /usr/local/rvm/src/ruby-1.9.3-p194/test/ /usr/local/rvm/src/ruby-1.9.3-p194/test/psych/test_yaml.rb
Run options: --seed 31567

# Running tests:

...........F.................................................

Finished tests in 0.200379s, 304.4230 tests/s, 1282.5688 assertions/s.

1) Failure:
test_ruby_struct(Psych_Unit_Tests) [/usr/local/rvm/src/ruby-1.9.3-p194/test/psych/test_yaml.rb:1039]:
--- expected
+++ actual
@@ -1 +1 @@
-[#<struct Struct::MyBookStruct author="Yukihiro Matsumoto", title="Ruby in a Nutshell", year=2002, isbn="0-596-00214-9">, #<struct Struct::MyBookStruct author=["Dave Thomas", "Andy Hunt"],     title="The     Pickaxe", year=2002, isbn=#<struct Struct::MyBookStruct author="This should be the ISBN", title="but I have another struct here", year=2002, isbn="None">>]
+[#<struct author="Yukihiro Matsumoto", title="Ruby in a Nutshell", year=2002, isbn="0-596-00214-9">, #<struct author=["Dave Thomas", "Andy Hunt"], title="The Pickaxe", year=2002, isbn=#<struct     author="This should be the ISBN", title="but I have another struct here", year=2002, isbn="None">>]


61 tests, 257 assertions, 1 failures, 0 errors, 0 skips