audited icon indicating copy to clipboard operation
audited copied to clipboard

Error after upgrading to Rails 6.0.5.1: Psych::DisallowedClass: Tried to load unspecified class: ActiveSupport::TimeWithZone

Open KevinBongart opened this issue 2 years ago • 14 comments

Hello, I upgraded a Rails app from 6.0.5 to 6.0.5.1 which was released yesterday, and saw this error when saving an audited record:

Psych::DisallowedClass: Tried to load unspecified class: ActiveSupport::TimeZone

It happens when using YAML to deserialize an audit record with a column of ActiveSupport::TimeWithZone type:

From: /Users/kevin/.rbenv/versions/2.7.6/lib/ruby/gems/2.7.0/gems/audited-5.0.2/lib/audited/audit.rb:23 Audited::YAMLIfTextColumnType.load:

    20: def load(obj)
    21:   if text_column?
    22:     binding.pry
 => 23:     ActiveRecord::Coders::YAMLColumn.new(Object).load(obj)
    24:   else
    25:     obj
    26:   end
    27: end
pry(Audited::YAMLIfTextColumnType)> puts obj
---
first_name: Test
last_name: Test
email: [email protected]
phone: "+15555555555"
[…]
confirmation_sent_at: !ruby/object:ActiveSupport::TimeWithZone
  utc: &1 2022-07-13 07:42:50.642612000 Z
  zone: !ruby/object:ActiveSupport::TimeZone
    name: Etc/UTC
  time: *1
[…]

After investigating, it's a direct result from the security patch introduced in Rails 7.0.3.1, 6.1.6.1, 6.0.5.1 and 5.2.8.1 and I was able to configure the application to permit some classes. In my case:

# in config/application.rb

# List of classes deemed safe to load by YAML, and required by the Audited
# gem when deserialized audit records.
# As of Rails 6.0.5.1, YAML safe-loading method does not allow all classes
# to be deserialized by default: https://discuss.rubyonrails.org/t/cve-2022-32224-possible-rce-escalation-bug-with-serialized-columns-in-active-record/81017
config.active_record.yaml_column_permitted_classes = [
  ActiveSupport::HashWithIndifferentAccess,
  ActiveSupport::TimeWithZone,
  ActiveSupport::TimeZone,
  Date,
  Time
]

Hope this is useful to other Audited users! Perhaps Audited could come with this config out of the box, or including some guidance in the README?

KevinBongart avatar Jul 13 '22 08:07 KevinBongart

FYI got the same with

Psych::DisallowedClass: Tried to load unspecified class: ActiveSupport::HashWithIndifferentAccess

grosser avatar Jul 13 '22 09:07 grosser

@grosser Ah yes, I saw the same in my app after opening the issue above. Updating the issue to make it easier for other Audited users to copy/paste a solution.

KevinBongart avatar Jul 13 '22 10:07 KevinBongart

I created a script that can help to figure what actually needs to be whitelisted.

what_i_serialize.rb

crawler avatar Jul 13 '22 16:07 crawler

See this issue for some discussion and (personally untested) examples of migrating from YAML to JSON

barrywoolgar avatar Jul 14 '22 16:07 barrywoolgar

Pretty much same issue when upgrading to Rails 7.0.3.1:

Psych::DisallowedClass: Tried to load unspecified class: ActiveSupport::TimeWithZone

vrinek avatar Jul 15 '22 13:07 vrinek

@vrinek this is already mentioned in the initial message of this thread.

The fast, unsafe and wrong way:

config.active_record.use_yaml_unsafe_load = ture

The correct way is to eighter somehow migrate to the JSON store or whitelist ruby that being serialized classes:

My case:

config.active_record.yaml_column_permitted_classes =
  %w[String Integer NilClass Float Time Date FalseClass Hash Array DateTime TrueClass BigDecimal
      ActiveSupport::TimeWithZone ActiveSupport::TimeZone ActiveSupport::HashWithIndifferentAccess]

To find what needs to be whitelisted in your case you can try to use this script:

https://gist.github.com/crawler/47a1e66ee2c2ea37f56f9c0c2aac071a

crawler avatar Jul 15 '22 13:07 crawler

I do worry that we can't necessarily know what classes may end up in the audit log later on, so no matter how thorough @crawler's script is, it'll only need a new edge case to come along and potentially cause an exception.

It's a good thing this gem defaults to JSON now, so it's only existing users who will need to figure out a safe migration path.

barrywoolgar avatar Jul 15 '22 14:07 barrywoolgar

@barrywoolgar sure, this is a better way.

I think it is possible to stay with the YAML because you should know what types are present in the schema of the models that are under audit. But it looks like a large surface for the making mistake out of nothing.

crawler avatar Jul 15 '22 14:07 crawler

@crawler apologies for not reading the first post thoroughly, and thanks for pointing it out as a possible course of action.

@barrywoolgar "will need to figure out a safe migration path" :sweat_smile:. Yeah, that's gonna take a while to be prioritized. In the meanwhile if anyone has gone through this already, I would love to learn from their experiences.

For now, my migration references include only this:

  • https://github.com/collectiveidea/audited/issues/216#issuecomment-215376047

vrinek avatar Jul 15 '22 14:07 vrinek

Should we consider adding a config option similar to active record's but scoped only to audited? In our codebase the only library preventing the upgrade is audited. We don't need to specify the permitted classes array for anything else, so it seems a little overkill to do it for the whole app.

alejandrovelez7 avatar Jul 18 '22 13:07 alejandrovelez7

I'd also prefer a migration to JSON. However there doesn't seem to be a clear path yet besides removing the whole audited history.

MSchmidt avatar Jul 29 '22 15:07 MSchmidt

[deleted my issue] Operator error, carry on, nothing to see with my particular issue.

walterdavis avatar Aug 11 '22 15:08 walterdavis

lets move to json

@crawler is this list all complete?

config.active_record.yaml_column_permitted_classes =
  %w[String Integer NilClass Float Time Date FalseClass Hash Array DateTime TrueClass BigDecimal
      ActiveSupport::TimeWithZone ActiveSupport::TimeZone ActiveSupport::HashWithIndifferentAccess]

laptopmutia avatar Aug 13 '22 03:08 laptopmutia

@laptopmutia for two my apps where i used this list.

crawler avatar Aug 19 '22 13:08 crawler