canvas-lms/spec/lib/safe_yaml_spec.rb

215 lines
5.8 KiB
Ruby

# frozen_string_literal: true
#
# Copyright (C) 2013 - present Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
#
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper.rb')
describe "safe_yaml" do
let(:test_yaml) do
<<~YAML
---
hwia: !map:HashWithIndifferentAccess
a: 1
b: 2
float: !float
5.1
float_with_exp: -1.7763568394002505e-15
float_inf: .inf
os: !ruby/object:OpenStruct
modifiable: true
table:
:a: 1
:b: 2
:sub: !ruby/object:OpenStruct
modifiable: true
table:
:c: 3
str: !str
hai
mime: !ruby/object:Mime::Type
string: png
symbol:
synonyms: []
http: !ruby/object:URI::HTTP
fragment:
host: example.com
opaque:
parser:
password:
path: /
port: 80
query:
registry:
scheme: http
user:
https: !ruby/object:URI::HTTPS
fragment:
host: example.com
opaque:
parser:
password:
path: /
port: 443
query:
registry:
scheme: https
user:
ab: !ruby/object:Class AcademicBenchmark::Converter
qt: !ruby/object:Class Qti::Converter
verbose_symbol: !ruby/symbol blah
oo: !ruby/object:OpenObject
table:
:a: 1
YAML
end
it "is used by default" do
yaml = <<~YAML
--- !ruby/regexp /regex/
YAML
expect { YAML.load yaml }.to raise_error(Psych::DisallowedClass)
result = YAML.unsafe_load yaml
expect(result.class).to eq Regexp
end
it "doesn't allow deserialization of arbitrary classes" do
expect { YAML.load(YAML.dump(/regex/)) }.to raise_error(Psych::DisallowedClass)
end
it "allows deserialization of arbitrary classes when unsafe_loading" do
regex = /regex/
expect(YAML.unsafe_load(YAML.dump(regex))).to eq regex
end
it "allows some whitelisted classes" do
result = YAML.load(test_yaml)
def verify(result, key, klass)
obj = result[key]
expect(obj.class).to eq klass
obj
end
hwia = verify(result, 'hwia', HashWithIndifferentAccess)
expect(hwia.values_at(:a, :b)).to eq [1, 2]
float = verify(result, 'float', Float)
expect(float).to eq 5.1
float_with_exp = verify(result, 'float_with_exp', Float)
expect(float_with_exp).to eq(-1.7763568394002505e-15)
float_inf = verify(result, 'float_inf', Float)
expect(float_inf).to eq(Float::INFINITY)
os = verify(result, 'os', OpenStruct)
expect(os.a).to eq 1
expect(os.b).to eq 2
expect(os.sub.class).to eq OpenStruct
expect(os.sub.c).to eq 3
str = verify(result, 'str', String)
expect(str).to eq "hai"
mime = verify(result, 'mime', Mime::Type)
expect(mime.to_s).to eq 'png'
http = verify(result, 'http', URI::HTTP)
expect(http.host).to eq 'example.com'
https = verify(result, 'https', URI::HTTPS)
expect(https.host).to eq 'example.com'
expect(result['ab']).to eq AcademicBenchmark::Converter
expect(result['qt']).to eq Qti::Converter
expect(result['verbose_symbol']).to eq :blah
oo = verify(result, 'oo', OpenObject)
expect(oo.a).to eq 1
end
it "allows some whitelisted classes through psych" do
old_result = YAML.load(test_yaml)
psych_yaml = YAML.dump(old_result)
expect(Psych.load(psych_yaml)).to eq old_result
expect(YAML.load(psych_yaml)).to eq old_result
end
it "works with aliases" do
hash = { :a => 1 }.with_indifferent_access
obj = { :blah => hash, :bloop => hash }.with_indifferent_access
yaml = Psych.dump(obj)
expect(YAML.load(yaml)).to eq obj
end
it "dumps whole floats correctly" do
expect(YAML.dump(1.0)).to include("1.0")
end
it "dumps freaky floaty-looking strings" do
str = "1.E+01"
expect(YAML.load(YAML.dump(str))).to eq str
end
it "dumps html-safe strings correctly" do
hash = { :blah => "42".html_safe }
expect(YAML.load(YAML.dump(hash))).to eq hash
end
it "dumps strings with underscores followed by an integer" do
# the ride never ends -_-
hash = { :blah => "_42" }
expect(YAML.load(YAML.dump(hash))).to eq hash
end
it "alsoes dump floaat looking strings followed by an underscore" do
hash = { :blah => "42._" }
expect(YAML.load(YAML.dump(hash))).to eq hash
end
it "dumps whatever this is too" do
hash = { :blah => "4,2:0." }
expect(YAML.load(YAML.dump(hash))).to eq hash
end
it "is able to dump and load Canvas:Plugin classes" do
plugin = Canvas::Plugin.find('canvas_cartridge_importer')
expect(YAML.unsafe_load(YAML.dump(plugin))).to eq plugin
end
it "is able to dump and load BigDecimals" do
hash = { blah: BigDecimal("1.2") }
expect(YAML.load(YAML.dump(hash))).to eq hash
end
it "is able to dump and load these strings in stuff" do
hash = { :blah => "<<" }
expect(YAML.load(YAML.dump(hash))).to eq hash
end
it "dumps and loads singletons" do
expect(YAML.load(YAML.dump(Mime::NullType.instance))).to eq Mime::NullType.instance
end
it "restores default value on sets on load" do
expect(YAML.unsafe_load(YAML.dump(Set.new)).include?("test")).to eq false
end
end