One of our internal tools is just a ruby script that makes use of YAML::Store
, which is a version of PStore
that uses YAML as the underlying data structure. The script didn’t tie down any particular gem versions and out of the blue we hit the following error:
Tried to load unspecified class: Time (Psych::DisallowedClass)
How to resolve this? What was happening? Well it seems newer versions of Psych
the Ruby YAML parser have restrictions built in on which classes they will and wont parse out of YAML, Time
is not one that is allowed. You can see this happening with a very simple little snippet:
require 'yaml'
yaml = "---\nfoo: #{Time.now}"
works = YAML.safe_load yaml, permitted_classes: [Time]
puts works # {"foo"=>2021-08-29 08:31:06 +0100}
fails = YAML.load yaml
puts fails # Psych::DisallowedClass (Tried to load unspecified class: Time)
After looking around the source and the Internet in general there didn’t appear to be an obvious way to set a globally acceptable list of allowed classes. Hopefully there is one and we’ll identify it or some helpful soul out there will point out the obvious. You can see a few other places where people have been caught out.
Our problem lay in that our failure was burried and we didn’t really want to start hacking around with PStore
or rather YAML::Store
which is where this should be fixed, since it would probably require a bit more proper work and we needed a quick fix. In the end we opted to monkey patch Psych::ClassLoader::Restricted
since it is a very basic and simple class that is unlikly to change much between minor versions. Our monkey patch being:
Psych::ClassLoader::ALLOWED_PSYCH_CLASSES = [ Time ]
module Psych
class ClassLoader
ALLOWED_PSYCH_CLASSES = [] unless defined? ALLOWED_PSYCH_CLASSES
class Restricted < ClassLoader
def initialize classes, symbols
@classes = classes + Psych::ClassLoader::ALLOWED_PSYCH_CLASSES.map(&:to_s)
@symbols = symbols
super()
end
end
end
end
So all we’re doing is adding in an extra array of accepted classes from a constant. That at least got our script up and running again without excessive work. We then went to look to patch YAML::Store
to expose an accepted list of YAML data types / classes. However we were beaten to it and the load method has been substitued with the unsafe_load
one which bypassses the class checking! So in newer versions of Ruby than the one we happened to have (running our script) the problem should no longer exist for users of YAML::Store
(pstore).
If you wanted or needed just to hack that change in as a monkey patch you could use the following snippet:
class YAML::Store < PStore
def load(content)
table = YAML.unsafe_load(content)
if table == false
{}
else
table
end
end
end