VYPR
Critical severity9.8NVD Advisory· Published Dec 5, 2022· Updated May 11, 2026

CVE-2022-32224

CVE-2022-32224

Description

A possible escalation to RCE vulnerability exists when using YAML serialized columns in Active Record < 7.0.3.1, <6.1.6.1, <6.0.5.1 and <5.2.8.1 which could allow an attacker, that can manipulate data in the database (via means like SQL injection), the ability to escalate to an RCE.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
activerecordRubyGems
>= 7.0.0, < 7.0.3.17.0.3.1
activerecordRubyGems
>= 6.1.0, < 6.1.6.16.1.6.1
activerecordRubyGems
>= 6.0.0, < 6.0.5.16.0.5.1
activerecordRubyGems
< 5.2.8.15.2.8.1

Affected products

1
  • Active Record/Active Recorddescription

Patches

1
611990f1a6c1

Change ActiveRecord::Coders::YAMLColumn default to safe_load

https://github.com/rails/railsZack DeveauApr 22, 2022via ghsa
21 files changed · +368 42
  • activerecord/lib/active_record/coders/yaml_column.rb+9 7 modified
    @@ -45,13 +45,15 @@ def check_arity_of_constructor
               raise ArgumentError, "Cannot serialize #{object_class}. Classes passed to `serialize` must have a 0 argument constructor."
             end
     
    -        if YAML.respond_to?(:unsafe_load)
    -          def yaml_load(payload)
    -            YAML.unsafe_load(payload)
    -          end
    -        else
    -          def yaml_load(payload)
    -            YAML.load(payload)
    +        def yaml_load(payload)
    +          if !ActiveRecord.use_yaml_unsafe_load
    +            YAML.safe_load(payload, permitted_classes: ActiveRecord.yaml_column_permitted_classes, aliases: true)
    +          else
    +            if YAML.respond_to?(:unsafe_load)
    +              YAML.unsafe_load(payload)
    +            else
    +              YAML.load(payload)
    +            end
               end
             end
         end
    
  • activerecord/lib/active_record/railtie.rb+18 0 modified
    @@ -414,5 +414,23 @@ class Railtie < Rails::Railtie # :nodoc:
             end
           end
         end
    +
    +    initializer "active_record.use_yaml_unsafe_load" do |app|
    +      config.after_initialize do
    +        unless app.config.active_record.use_yaml_unsafe_load.nil?
    +          ActiveRecord.use_yaml_unsafe_load =
    +            app.config.active_record.use_yaml_unsafe_load
    +        end
    +      end
    +    end
    +
    +    initializer "active_record.yaml_column_permitted_classes" do |app|
    +      config.after_initialize do
    +        unless app.config.active_record.yaml_column_permitted_classes.nil?
    +          ActiveRecord.yaml_column_permitted_classes =
    +            app.config.active_record.yaml_column_permitted_classes
    +        end
    +      end
    +    end
       end
     end
    
  • activerecord/lib/active_record.rb+14 0 modified
    @@ -353,6 +353,20 @@ def self.global_executor_concurrency # :nodoc:
       singleton_class.attr_accessor :query_transformers
       self.query_transformers = []
     
    +  ##
    +  # :singleton-method:
    +  # Application configurable boolean that instructs the YAML Coder to use
    +  # an unsafe load if set to true.
    +  singleton_class.attr_accessor :use_yaml_unsafe_load
    +  self.use_yaml_unsafe_load = false
    +
    +  ##
    +  # :singleton-method:
    +  # Application configurable array that provides additional permitted classes
    +  # to Psych safe_load in the YAML Coder
    +  singleton_class.attr_accessor :yaml_column_permitted_classes
    +  self.yaml_column_permitted_classes = []
    +
       def self.eager_load!
         super
         ActiveRecord::Locking.eager_load!
    
  • activerecord/test/cases/attribute_methods_test.rb+10 11 modified
    @@ -46,9 +46,8 @@ def setup
     
       test "attribute_for_inspect with an array" do
         t = topics(:first)
    -    t.content = [Object.new]
    -
    -    assert_match %r(\[#<Object:0x[0-9a-f]+>\]), t.attribute_for_inspect(:content)
    +    t.content = ["some_value"]
    +    assert_match %r(\["some_value"\]), t.attribute_for_inspect(:content)
       end
     
       test "attribute_for_inspect with a long array" do
    @@ -218,7 +217,7 @@ def setup
     
       test "read attributes_for_database" do
         topic = Topic.new
    -    topic.content = { one: 1, two: 2 }
    +    topic.content = { "one" => 1, "two" => 2 }
     
         db_attributes = Topic.instantiate(topic.attributes_for_database).attributes
         before_type_cast_attributes = Topic.instantiate(topic.attributes_before_type_cast).attributes
    @@ -291,16 +290,16 @@ def setup
       end
     
       test "hashes are not mangled" do
    -    new_topic = { title: "New Topic", content: { key: "First value" } }
    -    new_topic_values = { title: "AnotherTopic", content: { key: "Second value" } }
    +    new_topic = { "title" => "New Topic", "content" => { "key" => "First value" } }
    +    new_topic_values = { "title" => "AnotherTopic", "content" => { "key" => "Second value" } }
     
         topic = Topic.new(new_topic)
    -    assert_equal new_topic[:title], topic.title
    -    assert_equal new_topic[:content], topic.content
    +    assert_equal new_topic["title"], topic.title
    +    assert_equal new_topic["content"], topic.content
     
         topic.attributes = new_topic_values
    -    assert_equal new_topic_values[:title], topic.title
    -    assert_equal new_topic_values[:content], topic.content
    +    assert_equal new_topic_values["title"], topic.title
    +    assert_equal new_topic_values["content"], topic.content
       end
     
       test "create through factory" do
    @@ -624,7 +623,7 @@ def topic.approved; false; end
       end
     
       test "should unserialize attributes for frozen records" do
    -    myobj = { value1: :value2 }
    +    myobj = { "value1" => "value2" }
         topic = Topic.create(content: myobj)
         topic.freeze
         assert_equal myobj, topic.content
    
  • activerecord/test/cases/base_test.rb+1 1 modified
    @@ -98,7 +98,7 @@ def test_arel_attribute_normalization
     
       def test_incomplete_schema_loading
         topic = Topic.first
    -    payload = { foo: 42 }
    +    payload = { "foo" => 42 }
         topic.update!(content: payload)
     
         Topic.reset_column_information
    
  • activerecord/test/cases/calculations_test.rb+2 2 modified
    @@ -883,8 +883,8 @@ def test_pluck_on_aliased_attribute
       end
     
       def test_pluck_with_serialization
    -    t = Topic.create!(content: { foo: :bar })
    -    assert_equal [{ foo: :bar }], Topic.where(id: t.id).pluck(:content)
    +    t = Topic.create!(content: { "foo" => "bar" })
    +    assert_equal [{ "foo" => "bar" }], Topic.where(id: t.id).pluck(:content)
       end
     
       def test_pluck_with_qualified_column_name
    
  • activerecord/test/cases/coders/yaml_column_test.rb+34 0 modified
    @@ -5,6 +5,10 @@
     module ActiveRecord
       module Coders
         class YAMLColumnTest < ActiveRecord::TestCase
    +      setup do
    +        ActiveRecord.use_yaml_unsafe_load = true
    +      end
    +
           def test_initialize_takes_class
             coder = YAMLColumn.new("attr_name", Object)
             assert_equal Object, coder.object_class
    @@ -62,5 +66,35 @@ def test_load_doesnt_handle_undefined_class_or_module
             end
           end
         end
    +
    +    class YAMLColumnTestWithSafeLoad < YAMLColumnTest
    +      setup do
    +        @yaml_column_permitted_classes_default = ActiveRecord.yaml_column_permitted_classes
    +        ActiveRecord.use_yaml_unsafe_load = false
    +      end
    +
    +      def test_yaml_column_permitted_classes_are_consumed_by_safe_load
    +        ActiveRecord.yaml_column_permitted_classes = [Symbol, Time]
    +
    +        coder = YAMLColumn.new("attr_name")
    +        time_yaml = YAML.dump(Time.new)
    +        symbol_yaml = YAML.dump(:somesymbol)
    +
    +        assert_nothing_raised do
    +          coder.load(time_yaml)
    +          coder.load(symbol_yaml)
    +        end
    +
    +        ActiveRecord.yaml_column_permitted_classes = @yaml_column_permitted_classes_default
    +      end
    +
    +      def test_load_doesnt_handle_undefined_class_or_module
    +        coder = YAMLColumn.new("attr_name")
    +        missing_class_yaml = '--- !ruby/object:DoesNotExistAndShouldntEver {}\n'
    +        assert_raises(Psych::DisallowedClass) do
    +          coder.load(missing_class_yaml)
    +        end
    +      end
    +    end
       end
     end
    
  • activerecord/test/cases/dirty_test.rb+10 10 modified
    @@ -460,56 +460,56 @@ def test_reverted_changes_are_not_dirty_going_from_nil_to_value_and_back
     
       def test_save_should_store_serialized_attributes_even_with_partial_writes
         with_partial_writes(Topic) do
    -      topic = Topic.create!(content: { a: "a" })
    +      topic = Topic.create!(content: { "a" => "a" })
     
           assert_not_predicate topic, :changed?
     
    -      topic.content[:b] = "b"
    +      topic.content["b"] = "b"
     
           assert_predicate topic, :changed?
     
           topic.save!
     
           assert_not_predicate topic, :changed?
    -      assert_equal "b", topic.content[:b]
    +      assert_equal "b", topic.content["b"]
     
           topic.reload
     
    -      assert_equal "b", topic.content[:b]
    +      assert_equal "b", topic.content["b"]
         end
       end
     
       def test_save_always_should_update_timestamps_when_serialized_attributes_are_present
         with_partial_writes(Topic) do
    -      topic = Topic.create!(content: { a: "a" })
    +      topic = Topic.create!(content: { "a" => "a" })
           topic.save!
     
           updated_at = topic.updated_at
           travel(1.second) do
    -        topic.content[:hello] = "world"
    +        topic.content["hello"] = "world"
             topic.save!
           end
     
           assert_not_equal updated_at, topic.updated_at
    -      assert_equal "world", topic.content[:hello]
    +      assert_equal "world", topic.content["hello"]
         end
       end
     
       def test_save_should_not_save_serialized_attribute_with_partial_writes_if_not_present
         with_partial_writes(Topic) do
    -      topic = Topic.create!(author_name: "Bill", content: { a: "a" })
    +      topic = Topic.create!(author_name: "Bill", content: { "a" => "a" })
           topic = Topic.select("id, author_name").find(topic.id)
           topic.update_columns author_name: "John"
           assert_not_nil topic.reload.content
         end
       end
     
       def test_changes_to_save_should_not_mutate_array_of_hashes
    -    topic = Topic.new(author_name: "Bill", content: [{ a: "a" }])
    +    topic = Topic.new(author_name: "Bill", content: [{ "a" => "a" }])
     
         topic.changes_to_save
     
    -    assert_equal [{ a: "a" }], topic.content
    +    assert_equal [{ "a" => "a" }], topic.content
       end
     
       def test_previous_changes
    
  • activerecord/test/cases/encryption/encryptable_record_test.rb+2 2 modified
    @@ -42,13 +42,13 @@ class ActiveRecord::Encryption::EncryptableRecordTest < ActiveRecord::Encryption
       end
     
       test "encrypts serialized attributes" do
    -    states = %i[ green red ]
    +    states = ["green", "red"]
         traffic_light = EncryptedTrafficLight.create!(state: states, long_state: states)
         assert_encrypted_attribute(traffic_light, :state, states)
       end
     
       test "encrypts store attributes with accessors" do
    -    traffic_light = EncryptedTrafficLightWithStoreState.create!(color: "red", long_state: %i[ green red ])
    +    traffic_light = EncryptedTrafficLightWithStoreState.create!(color: "red", long_state: ["green", "red"])
         assert_equal "red", traffic_light.color
         assert_encrypted_attribute(traffic_light, :state, { "color" => "red" })
       end
    
  • activerecord/test/cases/filter_attributes_test.rb+1 0 modified
    @@ -13,6 +13,7 @@ class FilterAttributesTest < ActiveRecord::TestCase
       setup do
         @previous_filter_attributes = ActiveRecord::Base.filter_attributes
         ActiveRecord::Base.filter_attributes = [:name]
    +    ActiveRecord.use_yaml_unsafe_load = true
       end
     
       teardown do
    
  • activerecord/test/cases/json_serialization_test.rb+1 1 modified
    @@ -33,7 +33,7 @@ def setup
           avatar: "binarydata",
           created_at: Time.utc(2006, 8, 1),
           awesome: true,
    -      preferences: { shows: "anime" }
    +      preferences: { "shows" => "anime" }
         )
       end
     
    
  • activerecord/test/cases/serialization_test.rb+1 1 modified
    @@ -19,7 +19,7 @@ def setup
           avatar: "binarydata",
           created_at: Time.utc(2006, 8, 1),
           awesome: false,
    -      preferences: { gem: "<strong>ruby</strong>" },
    +      preferences: { "gem" => "<strong>ruby</strong>" },
           alternative_id: nil,
           id: nil
         }
    
  • activerecord/test/cases/serialized_attribute_test.rb+115 2 modified
    @@ -7,6 +7,10 @@
     require "models/binary_field"
     
     class SerializedAttributeTest < ActiveRecord::TestCase
    +  def setup
    +    ActiveRecord.use_yaml_unsafe_load = true
    +  end
    +
       fixtures :topics, :posts
     
       MyObject = Struct.new :attribute1, :attribute2
    @@ -446,7 +450,7 @@ def test_decorated_type_with_type_for_attribute
     
         klass = Class.new(ActiveRecord::Base) do
           self.table_name = Topic.table_name
    -      store :content
    +      store :content, coder: ActiveRecord::Coders::JSON
           attribute :content, :encrypted, subtype: type_for_attribute(:content)
         end
     
    @@ -460,7 +464,7 @@ def test_decorated_type_with_type_for_attribute
       def test_decorated_type_with_decorator_block
         klass = Class.new(ActiveRecord::Base) do
           self.table_name = Topic.table_name
    -      store :content
    +      store :content, coder: ActiveRecord::Coders::JSON
           attribute(:content) { |subtype| EncryptedType.new(subtype: subtype) }
         end
     
    @@ -530,3 +534,112 @@ def test_serialized_attribute_works_under_concurrent_initial_access
         assert_equal [1] * threads.size, threads.map(&:value)
       end
     end
    +
    +class SerializedAttributeTestWithYamlSafeLoad < SerializedAttributeTest
    +  def setup
    +    ActiveRecord.use_yaml_unsafe_load = false
    +  end
    +
    +  def test_serialized_attribute
    +    Topic.serialize("content", String)
    +
    +    myobj = String.new("value1")
    +    topic = Topic.create("content" => myobj)
    +    assert_equal(myobj, topic.content)
    +
    +    topic.reload
    +    assert_equal(myobj, topic.content)
    +  end
    +
    +  def test_serialized_attribute_on_custom_attribute_with_default
    +    klass = Class.new(ActiveRecord::Base) do
    +      self.table_name = Topic.table_name
    +      attribute :content, default: { "key" => "value" }
    +      serialize :content, Hash
    +    end
    +
    +    t = klass.new
    +    assert_equal({ "key" => "value" }, t.content)
    +  end
    +
    +  def test_nil_is_always_persisted_as_null
    +    Topic.serialize(:content, Hash)
    +
    +    topic = Topic.create!(content: { "foo" => "bar" })
    +    topic.update_attribute :content, nil
    +    assert_equal [topic], Topic.where(content: nil)
    +  end
    +
    +  def test_serialized_attribute_with_default
    +    klass = Class.new(ActiveRecord::Base) do
    +      self.table_name = Topic.table_name
    +      serialize(:content, Hash, default: { "key" => "value" })
    +    end
    +
    +    t = klass.new
    +    assert_equal({ "key" => "value" }, t.content)
    +  end
    +
    +  def test_serialized_attributes_from_database_on_subclass
    +    Topic.serialize :content, Hash
    +
    +    t = ImportantTopic.new(content: { "foo" => "bar" })
    +    assert_equal({ "foo" => "bar" }, t.content)
    +    t.save!
    +    t = ImportantTopic.last
    +    assert_equal({ "foo" => "bar" }, t.content)
    +  end
    +
    +  def test_serialized_attribute_on_alias_attribute
    +    klass = Class.new(ActiveRecord::Base) do
    +      self.table_name = Topic.table_name
    +      alias_attribute :object, :content
    +      serialize :object, Hash
    +    end
    +
    +    myobj = { "somevalue" => "thevalue" }
    +    topic = klass.create!(object: myobj)
    +    assert_equal(myobj, topic.object)
    +
    +    topic.reload
    +    assert_equal(myobj, topic.object)
    +  end
    +
    +  def test_unexpected_serialized_type
    +    Topic.serialize :content, Hash
    +    topic = Topic.create!(content: { "zomg" => true })
    +
    +    Topic.serialize :content, Array
    +
    +    topic.reload
    +    error = assert_raise(ActiveRecord::SerializationTypeMismatch) do
    +      topic.content
    +    end
    +    expected = "can't load `content`: was supposed to be a Array, but was a Hash. -- {\"zomg\"=>true}"
    +    assert_equal expected, error.to_s
    +  end
    +
    +  def test_serialize_attribute_via_select_method_when_time_zone_available
    +    with_timezone_config aware_attributes: true do
    +      Topic.serialize(:content, Hash)
    +
    +      myobj = { "somevalue" => "thevalue" }
    +      topic = Topic.create(content: myobj)
    +
    +      assert_equal(myobj, Topic.select(:content).find(topic.id).content)
    +      assert_raise(ActiveModel::MissingAttributeError) { Topic.select(:id).find(topic.id).content }
    +    end
    +  end
    +
    +  def test_should_raise_exception_on_serialized_attribute_with_type_mismatch
    +    myobj = { "somevalue" => "thevalue" }
    +    topic = Topic.new(content: myobj)
    +    assert topic.save
    +    Topic.serialize(:content, String)
    +    assert_raise(ActiveRecord::SerializationTypeMismatch) { Topic.find(topic.id).content }
    +  end
    +
    +  def test_serialized_time_attribute
    +    skip "Time is a DisallowedClass in Psych safe_load()."
    +  end
    +end
    
  • activerecord/test/cases/store_test.rb+21 1 modified
    @@ -3,6 +3,7 @@
     require "cases/helper"
     require "models/admin"
     require "models/admin/user"
    +require "models/admin/user_json"
     require "models/account"
     
     class StoreTest < ActiveRecord::TestCase
    @@ -15,6 +16,7 @@ class StoreTest < ActiveRecord::TestCase
           parent_name: "Quinn", partner_name: "Dallas",
           partner_birthday: "1997-11-1"
         )
    +    ActiveRecord.use_yaml_unsafe_load = true
       end
     
       test "reading store attributes through accessors" do
    @@ -179,7 +181,7 @@ class StoreTest < ActiveRecord::TestCase
         assert_equal "heavy", @john.json_data["weight"]
       end
     
    -  test "convert store attributes from Hash to HashWithIndifferentAccess saving the data and access attributes indifferently" do
    +  def test_convert_store_attributes_from_Hash_to_HashWithIndifferentAccess_saving_the_data_and_access_attributes_indifferently
         user = Admin::User.find_by_name("Jamis")
         assert_equal "symbol",  user.settings[:symbol]
         assert_equal "symbol",  user.settings["symbol"]
    @@ -319,3 +321,21 @@ class StoreTest < ActiveRecord::TestCase
         assert_equal [:secret_question, :two_factor_auth, :login_retry], Admin::User.stored_attributes[:configs]
       end
     end
    +
    +class StoreTestWithYAMLSafeLoad < StoreTest
    +  fixtures :'admin/users'
    +
    +  setup do
    +    @john = Admin::UserJSON.create!(
    +      name: "Jim Doe", color: "black", remember_login: true,
    +      height: "tall", is_a_good_guy: true,
    +      parent_name: "Quinn", partner_name: "Dallas",
    +      partner_birthday: "1997-11-1"
    +    )
    +    ActiveRecord.use_yaml_unsafe_load = false
    +  end
    +
    +  def test_convert_store_attributes_from_Hash_to_HashWithIndifferentAccess_saving_the_data_and_access_attributes_indifferently
    +    skip "Symbol is not a supported class in Psych::safe_load"
    +  end
    +end
    
  • activerecord/test/cases/yaml_serialization_test.rb+3 3 modified
    @@ -24,8 +24,8 @@ def test_roundtrip
       end
     
       def test_roundtrip_serialized_column
    -    topic = Topic.new(content: { omg: :lol })
    -    assert_equal({ omg: :lol }, yaml_load(YAML.dump(topic)).content)
    +    topic = Topic.new(content: { "omg" => "lol" })
    +    assert_equal({ "omg" => "lol" }, yaml_load(YAML.dump(topic)).content)
       end
     
       def test_psych_roundtrip
    @@ -113,7 +113,7 @@ def test_deserializing_rails_v1_mysql_yaml
     
       def test_deserializing_rails_41_yaml
         error = assert_raises(RuntimeError) do
    -      yaml_load(yaml_fixture("rails_4_1"))
    +      yaml_load(yaml_fixture("rails_4_1_no_symbol"))
         end
     
         assert_equal "Active Record doesn't know how to load YAML with this format.", error.message
    
  • activerecord/test/models/admin/user_json.rb+48 0 added
    @@ -0,0 +1,48 @@
    +# frozen_string_literal: true
    +
    +class Admin::UserJSON < ActiveRecord::Base
    +  class Coder
    +    def initialize(default = {})
    +      @default = default
    +    end
    +
    +    def dump(o)
    +      ActiveSupport::JSON.encode(o || @default)
    +    end
    +
    +    def load(s)
    +      s.present? ? ActiveSupport::JSON.decode(s) : @default.clone
    +    end
    +  end
    +
    +  belongs_to :account
    +  store :params, accessors: [ :token ], coder: JSON
    +  store :settings, accessors: [ :color, :homepage ], coder: Coder.new
    +  store_accessor :settings, :favorite_food
    +  store :parent, accessors: [:birthday, :name], prefix: true, coder: Coder.new
    +  store :spouse, accessors: [:birthday], prefix: :partner, coder: Coder.new
    +  store_accessor :spouse, :name, prefix: :partner
    +  store :configs, accessors: [ :secret_question ], coder: Coder.new
    +  store :configs, accessors: [ :two_factor_auth ], suffix: true, coder: Coder.new
    +  store_accessor :configs, :login_retry, suffix: :config
    +  store :preferences, accessors: [ :remember_login ], coder: Coder.new
    +  store :json_data, accessors: [ :height, :weight ], coder: Coder.new
    +  store :json_data_empty, accessors: [ :is_a_good_guy ], coder: Coder.new
    +
    +  def phone_number
    +    read_store_attribute(:settings, :phone_number).gsub(/(\d{3})(\d{3})(\d{4})/, '(\1) \2-\3')
    +  end
    +
    +  def phone_number=(value)
    +    write_store_attribute(:settings, :phone_number, value && value.gsub(/[^\d]/, ""))
    +  end
    +
    +  def color
    +    super || "red"
    +  end
    +
    +  def color=(value)
    +    value = "blue" unless %w(black red green blue).include?(value)
    +    super
    +  end
    +end
    
  • activerecord/test/models/traffic_light_encrypted.rb+1 1 modified
    @@ -8,6 +8,6 @@ class EncryptedTrafficLight < TrafficLight
     end
     
     class EncryptedTrafficLightWithStoreState < TrafficLight
    -  store :state, accessors: %i[ color ]
    +  store :state, accessors: %i[ color ], coder: ActiveRecord::Coders::JSON
       encrypts :state
     end
    
  • activerecord/test/schema/schema.rb+15 0 modified
    @@ -38,6 +38,21 @@
         t.references :account
       end
     
    +  create_table :admin_user_jsons, force: true do |t|
    +    t.string :name
    +    t.string :settings, null: true, limit: 1024
    +    t.string :parent, null: true, limit: 1024
    +    t.string :spouse, null: true, limit: 1024
    +    t.string :configs, null: true, limit: 1024
    +    # MySQL does not allow default values for blobs. Fake it out with a
    +    # big varchar below.
    +    t.string :preferences, null: true, default: "", limit: 1024
    +    t.string :json_data, null: true, limit: 1024
    +    t.string :json_data_empty, null: true, default: "", limit: 1024
    +    t.text :params
    +    t.references :account
    +  end
    +
       create_table :aircraft, force: true do |t|
         t.string :name
         t.integer :wheels_count, default: 0, null: false
    
  • activerecord/test/support/yaml_compatibility_fixtures/rails_4_1_no_symbol.yml+22 0 added
    @@ -0,0 +1,22 @@
    +--- !ruby/object:Topic
    +  attributes:
    +    id:
    +    title: The First Topic
    +    author_name: David
    +    author_email_address: david@loudthinking.com
    +    written_on: 2003-07-16 14:28:11.223300000 Z
    +    bonus_time: 2000-01-01 14:28:00.000000000 Z
    +    last_read: 2004-04-15
    +    content: |
    +      ---
    +      omg: lol
    +    important:
    +    approved: false
    +    replies_count: 1
    +    unique_replies_count: 0
    +    parent_id:
    +    parent_title:
    +    type:
    +    group:
    +    created_at: 2015-03-10 17:05:42.000000000 Z
    +    updated_at: 2015-03-10 17:05:42.000000000 Z
    
  • guides/source/configuring.md+8 0 modified
    @@ -2272,6 +2272,14 @@ has no effect if Sprockets is not used. The default value is `true`.
     
     `config.active_storage` provides the following configuration options:
     
    +#### `config.active_record.yaml_column_permitted_classes`
    +
    +Defaults to `[]`. Allows applications to include additional permitted classes to `safe_load()` on the `ActiveStorage::Coders::YamlColumn`.
    +
    +#### `config.active_storage.use_yaml_unsafe_load`
    +
    +Defaults to `false`. Allows applications to opt into using `unsafe_load` on the `ActiveStorage::Coders::YamlColumn`.
    +
     #### `config.active_storage.variant_processor`
     
     Accepts a symbol `:mini_magick` or `:vips`, specifying whether variant transformations and blob analysis will be performed with MiniMagick or ruby-vips.
    
  • railties/test/application/configuration_test.rb+32 0 modified
    @@ -1727,6 +1727,38 @@ def index
           assert_not ActiveRecord.suppress_multiple_database_warning
         end
     
    +    test "config.active_record.use_yaml_unsafe_load is false by default" do
    +      app "production"
    +      assert_not ActiveRecord.use_yaml_unsafe_load
    +    end
    +
    +    test "config.active_record.use_yaml_unsafe_load can be configured" do
    +      remove_from_config '.*config\.load_defaults.*\n'
    +
    +      app_file "config/initializers/use_yaml_unsafe_load.rb", <<-RUBY
    +        Rails.application.config.active_record.use_yaml_unsafe_load = true
    +      RUBY
    +
    +      app "production"
    +      assert ActiveRecord.use_yaml_unsafe_load
    +    end
    +
    +    test "config.active_record.yaml_column_permitted_classes is [] by default" do
    +      app "production"
    +      assert_equal([], ActiveRecord.yaml_column_permitted_classes)
    +    end
    +
    +    test "config.active_record.yaml_column_permitted_classes can be configured" do
    +      remove_from_config '.*config\.load_defaults.*\n'
    +
    +      app_file "config/initializers/yaml_permitted_classes.rb", <<-RUBY
    +        Rails.application.config.active_record.yaml_column_permitted_classes = [Symbol]
    +      RUBY
    +
    +      app "production"
    +      assert_equal([Symbol], ActiveRecord.yaml_column_permitted_classes)
    +    end
    +
         test "config.annotations wrapping SourceAnnotationExtractor::Annotation class" do
           make_basic_app do |application|
             application.config.annotations.register_extensions("coffee") do |tag|
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

8

News mentions

0

No linked articles in our index yet.