Ensure YAML safe loading in Rails 6.1

As part of the fix for CVE-2022-32224 Rails intruduced safe YAML loading
and the `ActiveRecord.yaml_column_permitted_classes` config.

PaperTrail added support for respecting the new configuration here
https://github.com/paper-trail-gem/paper_trail/pull/1397

The CVE-2022-32224 fix was also backported to Rails versions 5.2.8.1,
6.0.5.1, and, 6.1.6.1, however the name of the confiuration is slightly
different from that in Rails 7.x.

    7.0.3.1 ActiveRecord.yaml_column_permitted_classes
    6.1.6.1 ActiveRecord::Base.yaml_column_permitted_classes
    6.0.5.1 ActiveRecord::Base.yaml_column_permitted_classes
    5.2.8.1 ActiveRecord::Base.yaml_column_permitted_classes

PaperTrail currently doesn't support this alternative configuration
naming, which means it will silent fall back to unsafe YAML loading.

This commit updates `PaperTrail::Serializers::YAML` to be compatible
with safe YAML loading for the Rails 5.2 / 6.0 / 6.1 branches.
This commit is contained in:
Tim Connor 2022-08-25 15:21:52 +12:00 committed by Jared Beck
parent e71c65604f
commit 172ac1d747
3 changed files with 52 additions and 19 deletions

View File

@ -12,7 +12,7 @@ module PaperTrail
if use_safe_load?
::YAML.safe_load(
string,
permitted_classes: ::ActiveRecord.yaml_column_permitted_classes,
permitted_classes: yaml_column_permitted_classes,
aliases: true
)
elsif ::YAML.respond_to?(:unsafe_load)
@ -39,10 +39,28 @@ module PaperTrail
private
# `use_yaml_unsafe_load` was added in 7.0.3.1, will be removed in 7.1.0?
def use_safe_load?
defined?(ActiveRecord.use_yaml_unsafe_load) &&
!ActiveRecord.use_yaml_unsafe_load
if ::ActiveRecord.gem_version >= Gem::Version.new("7.0.3.1")
# `use_yaml_unsafe_load` may be removed in the future, at which point safe loading will be
# the default.
!defined?(ActiveRecord.use_yaml_unsafe_load) || !ActiveRecord.use_yaml_unsafe_load
elsif defined?(ActiveRecord::Base.use_yaml_unsafe_load)
# Rails 5.2.8.1, 6.0.5.1, 6.1.6.1
!ActiveRecord::Base.use_yaml_unsafe_load
else
false
end
end
def yaml_column_permitted_classes
if ::ActiveRecord.gem_version >= Gem::Version.new("7.0.3.1")
ActiveRecord.yaml_column_permitted_classes
elsif defined?(ActiveRecord::Base.yaml_column_permitted_classes)
# Rails 5.2.8.1, 6.0.5.1, 6.1.6.1
ActiveRecord::Base.yaml_column_permitted_classes
else
[]
end
end
end
end

View File

@ -13,7 +13,17 @@ require File.expand_path("boot", __dir__)
module Dummy
class Application < Rails::Application
config.load_defaults(::Rails.gem_version.segments.take(2).join("."))
YAML_COLUMN_PERMITTED_CLASSES = [
::ActiveRecord::Type::Time::Value,
::ActiveSupport::TimeWithZone,
::ActiveSupport::TimeZone,
::BigDecimal,
::Date,
::Symbol,
::Time
].freeze
config.load_defaults(::ActiveRecord.gem_version.segments.take(2).join("."))
config.encoding = "utf-8"
config.filter_parameters += [:password]
@ -21,18 +31,13 @@ module Dummy
config.active_support.test_order = :sorted
config.secret_key_base = "A fox regularly kicked the screaming pile of biscuits."
# `use_yaml_unsafe_load` was added in 7.0.3.1, will be removed in 7.1.0?
if ::ActiveRecord.respond_to?(:use_yaml_unsafe_load)
# `use_yaml_unsafe_load` was added in 5.2.8.1, 6.0.5.1, 6.1.6.1, and 7.0.3.1
if ::ActiveRecord.gem_version >= Gem::Version.new("7.0.3.1")
::ActiveRecord.use_yaml_unsafe_load = false
::ActiveRecord.yaml_column_permitted_classes = [
::ActiveRecord::Type::Time::Value,
::ActiveSupport::TimeWithZone,
::ActiveSupport::TimeZone,
::BigDecimal,
::Date,
::Symbol,
::Time
]
::ActiveRecord.yaml_column_permitted_classes = YAML_COLUMN_PERMITTED_CLASSES
elsif ::ActiveRecord::Base.respond_to?(:use_yaml_unsafe_load)
::ActiveRecord::Base.use_yaml_unsafe_load = false
::ActiveRecord::Base.yaml_column_permitted_classes = YAML_COLUMN_PERMITTED_CLASSES
end
end
end

View File

@ -24,12 +24,12 @@ module PaperTrail
end
it "calls the expected load method based on Psych version" do
# `use_yaml_unsafe_load` was added in 7.0.3.1, will be removed in 7.1.0?
if defined?(ActiveRecord.use_yaml_unsafe_load) && !ActiveRecord.use_yaml_unsafe_load
# `use_yaml_unsafe_load` was added in 5.2.8.1, 6.0.5.1, 6.1.6.1, and 7.0.3.1
if rails_supports_safe_load?
allow(::YAML).to receive(:safe_load)
described_class.load("string")
expect(::YAML).to have_received(:safe_load)
# Psych 4+ implements .unsafe_load
# Psych 4+ implements .unsafe_load
elsif ::YAML.respond_to?(:unsafe_load)
allow(::YAML).to receive(:unsafe_load)
described_class.load("string")
@ -60,6 +60,16 @@ module PaperTrail
expect(arel_value(matches.right)).to eq("%\narg1: Val 1\n%")
end
end
private
def rails_supports_safe_load?
# Rails 7.0.3.1 onwards will always support YAML safe loading
return true if ::ActiveRecord.gem_version >= Gem::Version.new("7.0.3.1")
# Older Rails versions may or may not, depending on whether they have been patched.
defined?(ActiveRecord::Base.use_yaml_unsafe_load)
end
end
end
end