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.
| Package | Affected versions | Patched versions |
|---|---|---|
activerecordRubyGems | >= 7.0.0, < 7.0.3.1 | 7.0.3.1 |
activerecordRubyGems | >= 6.1.0, < 6.1.6.1 | 6.1.6.1 |
activerecordRubyGems | >= 6.0.0, < 6.0.5.1 | 6.0.5.1 |
activerecordRubyGems | < 5.2.8.1 | 5.2.8.1 |
Affected products
1- Active Record/Active Recorddescription
Patches
1611990f1a6c1Change ActiveRecord::Coders::YAMLColumn default to safe_load
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- github.com/advisories/GHSA-3hhc-qp5v-9p2jnvdPatchThird Party AdvisoryADVISORY
- groups.google.com/g/rubyonrails-security/c/MmFO3LYQE8UnvdExploitMailing ListThird Party AdvisoryWEB
- nvd.nist.gov/vuln/detail/CVE-2022-32224ghsaADVISORY
- discuss.rubyonrails.org/t/cve-2022-32224-possible-rce-escalation-bug-with-serialized-columns-in-active-record/81017ghsaWEB
- github.com/rails/rails/commit/611990f1a6c137c2d56b1ba06b27e5d2434dcd6aghsaWEB
- github.com/rails/rails/commits/main/activerecordghsaPACKAGE
- github.com/rubysec/ruby-advisory-db/blob/master/gems/activerecord/CVE-2022-32224.ymlghsaWEB
- lists.debian.org/debian-lts-announce/2026/05/msg00022.htmlnvdWEB
News mentions
0No linked articles in our index yet.