angelov_mm
angelov_mm

Reputation: 23

Psych errors after Rails version update

We recently updated a client's application from Rails 4 to rails 5. However, after running the test suite the following issue appeared whilst trying to create an object:

       Failure/Error:
         @ens_response = EnsResponse.create!(
           edi_request_body:    @response.edi_request_body,
           edi_body:            @response.edi_data,
           reject_reason:       @response.attributes.try(:[], :reject_reason).try(:[], :text),
           response_attributes: @response.attributes
         )
       
       Psych::DisallowedClass:
         Tried to load unspecified class: Policy

Where Policy is a model in our app/models/ folder.

We tried changing the loading of the YAML to the following:

      @service_hash ||= YAML.load_file(
        Rails.root.join('config', 'mcp_services.yml'),
        permitted_classes: [Policy ],
        aliases: true
      )[Rails.env]

But it was to no avail.

We also tried changing the application.rb file to use the following line:

    config.active_record.yaml_column_permitted_classes = [
      Symbol,
      ActiveSupport::HashWithIndifferentAccess,
      ActionController::Parameters

but just got the error:

Failure/Error: require File.expand_path("../../config/environment", __FILE__)

NoMethodError:
  undefined method `yaml_column_permitted_classes=' for ActiveRecord::Base:Class

Any idea what might be causing this issue? Local psych is at version psych (default: 3.0.2) and rails is on gem 'rails', '5.2.8'

Thanks in advance! :)

Upvotes: 1

Views: 1907

Answers (3)

Sayuto
Sayuto

Reputation: 57

This is my config for ruby 3.1.3 and rails 7.0.4

Hope this comment will help someone, I have been stuck with this problem for a long time

# config/initializers/active_record.rb
# You can add custom class here

Rails.application.config.after_initialize do |app|
  app.config.active_record.yaml_column_permitted_classes  ||= []
  ActiveRecord.yaml_column_permitted_classes =
    app.config.active_record.yaml_column_permitted_classes += [
      Symbol,
      Date,
      Time,
      ActiveModel::Attribute.const_get(:FromDatabase, false),
      ActiveModel::Attribute.const_get(:WithCastValue, false),
      ActiveSupport::HashWithIndifferentAccess,
      ActiveSupport::TimeWithZone,
      ActiveSupport::TimeZone,
    ]
end

Upvotes: 2

Shannarra
Shannarra

Reputation: 559

After more thorough investigation, it turns out that the error is being caused by accessing the @response.attributes property, something similar to this:

@ens_response = EnsResponse.create!(response_attributes: @response.attributes)

It was due the fact that the aforementioned property is Hash, that contains several objects, including a property called policy: that is an object from the same model class (Policy), hence Psych trying to load an unspecified class caused an error.

The fix was to force-convert this property to a JSON string (since that's the schema row value type required):

         @ens_response = EnsResponse.create!(
           edi_request_body:    @response.edi_request_body,
           edi_body:            @response.edi_data,
           reject_reason:       @response.attributes.try(:[], :reject_reason).try(:[], :text),
           response_attributes: @response.attributes.to_json
         )

Moral of the story: Use debuggers such as Pry and check the properties of all items before serialization.

Upvotes: 0

dbugger
dbugger

Reputation: 16464

Quick unsafe hack is to set this in application.rb...

  config.active_record.use_yaml_unsafe_load = true

More involved is to add an initializer in config/initializers. The initializer tells rails what classes to allow when loading yaml.

config/initializers/yaml_loader.rb

Psych::ClassLoader::ALLOWED_PSYCH_CLASSES = [Policy,
                                                  ActionController::Parameters,
                                                  ActiveSupport::HashWithIndifferentAccess,
                                                  ActiveSupport::TimeWithZone,
                                                  ActiveSupport::TimeZone,
                                                  DateTime,
                                                ]
    
    module Psych
      class ClassLoader
        ALLOWED_PSYCH_CLASSES = [] unless defined? ALLOWED_PSYCH_CLASSES
        class Restricted < ClassLoader
          def initialize classes, symbols
            @classes = classes + Psych::ClassLoader::ALLOWED_PSYCH_CLASSES.map(&:to_s)
            @symbols = symbols
            super()
          end
        end
      end
    end

Upvotes: 2

Related Questions