0

I've created a form builder in rails that allows users to construct their own forms. Many of the form inputs supply straight strings back to Rails (e.g. a text field). Some provide arrays of values (like date choosers). Right now I'm storing the values for these in a serialised column. It works well, and lets me re-display the custom forms when an error occurs with minimal effort. The problem is that someone entered:

--------

into a text field and ActiveRecord raised an error saying: You tried to assign already serialized content to value. This is disabled due to security issues.

I get that the string looks like YAML, but I'm wondering if there's a more graceful way around this as the user was just entering a bunch of dashes to indicate he had no phone number. I'd like it to basically fail gracefully and perhaps drop the value or store it as a serialised string if there is such a thing.

Brendon Muir
  • 4,540
  • 2
  • 33
  • 55

1 Answers1

0

In Rails 3.0.20 LTS they've patched the code to check for YAML strings being sent to serialised columns. I've overridden the assignment method on my model to fix the string instead of raising an error:

module ActiveRecord
  module AttributeMethods
    module Write
      extend ActiveSupport::Concern

      included do
        attribute_method_suffix "="
      end

      module ClassMethods
        protected
          def define_method_attribute=(attr_name)
            if self.serialized_attributes[attr_name]
              generated_attribute_methods.send(:define_method, "#{attr_name}=") do |new_value|
                if new_value.is_a?(String) and new_value =~ /^---/
                  raise ActiveRecordError, "You tried to assign already serialized content to #{attr_name}. This is disabled due to security issues."
                end
                write_attribute(attr_name, new_value)
              end
            elsif attr_name =~ /^[a-zA-Z_]\w*[!?=]?$/
              generated_attribute_methods.module_eval("def #{attr_name}=(new_value); write_attribute('#{attr_name}', new_value); end", __FILE__, __LINE__)
            else
              generated_attribute_methods.send(:define_method, "#{attr_name}=") do |new_value|
                write_attribute(attr_name, new_value)
              end
            end
          end
      end

      ...

I wanted to use super(new_value) here to allow the original method to make the assignment but unfortunately it seemed to be bypassing the check (thus bypassing the security measure too).

  def value=(new_value)
    if new_value.is_a?(String) and new_value =~ /^---/
      new_value.gsub!(/^-+/, '-')
    end
    write_attribute(:value, new_value)
  end
Brendon Muir
  • 4,540
  • 2
  • 33
  • 55