Another alternative, if you wanted to minimize your vulnerability to #eval
(which is a rational thing to do) is to use String#scan
:
quoted = /"(?:\\.|[^\\"])*"/
pairs = '{"city": "London", "country": "England", "region": "Europe"}'.scan(/(#{quoted})\s*:\s*(#{quoted})/)
hash = Hash[ *pairs.flatten.map { |q| eval q } ]
We're still using #eval
, but we know that we're only #eval
'ing something that looks like a quoted string, so we're safe-ish.
Since ruby strings can contain arbitrary code (through the miracle of interpolation), we're still vulnerable, though:
# arbitrary code evaluation
p eval('"one two #{puts %{Give me a three!}; gets.chomp} four"')
The safest alternative is to bypass #eval
entirely, and use a library for unquoting strings that doesn't allow for
interpolation. The JSON library (as previously mentioned) is a great example of that. It may seem slower (since it's
not as optimized as #eval
is), but it's a whole heck of a lot safer.