What causes the extreme slowness when changing from rails 3.2.12 to 3.2.13
We had the same issues with Discourse. I extracted the relevant security fixes into a monkey patch you can apply to a Rails 3.2 application:
module HTML class WhiteListSanitizer # Sanitizes a block of css code. Used by #sanitize when it comes across a style attribute def sanitize_css(style) # disallow urls style = style.to_s.gsub(/url\s*\(\s*[^\s)]+?\s*\)\s*/, ' ') # gauntlet if style !~ /\A([:,;#%.\sa-zA-Z0-9!]|\w-\w|\'[\s\w]+\'|\"[\s\w]+\"|\([\d,\s]+\))*\z/ || style !~ /\A(\s*[-\w]+\s*:\s*[^:;]*(;|$)\s*)*\z/ return '' end clean = [] style.scan(/([-\w]+)\s*:\s*([^:;]*)/) do |prop,val| if allowed_css_properties.include?(prop.downcase) clean << prop + ': ' + val + ';' elsif shorthand_css_properties.include?(prop.split('-')[0].downcase) unless val.split().any? do |keyword| !allowed_css_keywords.include?(keyword) && keyword !~ /\A(#[0-9a-f]+|rgb\(\d+%?,\d*%?,?\d*%?\)?|\d{0,2}\.?\d{0,2}(cm|em|ex|in|mm|pc|pt|px|%|,|\))?)\z/ end clean << prop + ': ' + val + ';' end end end clean.join(' ') end endendmodule HTML class WhiteListSanitizer self.protocol_separator = /:|(�*58)|(p)|(�*3a)|(%|%)3A/i def contains_bad_protocols?(attr_name, value) uri_attributes.include?(attr_name) && (value =~ /(^[^\/:]*):|(�*58)|(p)|(�*3a)|(%|%)3A/i && !allowed_protocols.include?(value.split(protocol_separator).first.downcase.strip)) end endendmodule ActiveRecord class Relation def where_values_hash equalities = with_default_scope.where_values.grep(Arel::Nodes::Equality).find_all { |node| node.left.relation.name == table_name } Hash[equalities.map { |where| [where.left.name, where.right] }].with_indifferent_access end endendmodule ActiveRecord class PredicateBuilder # :nodoc: def self.build_from_hash(engine, attributes, default_table, allow_table_name = true) predicates = attributes.map do |column, value| table = default_table if allow_table_name && value.is_a?(Hash) table = Arel::Table.new(column, engine) if value.empty? '1 = 2' else build_from_hash(engine, value, table, false) end else column = column.to_s if allow_table_name && column.include?('.') table_name, column = column.split('.', 2) table = Arel::Table.new(table_name, engine) end attribute = table[column] case value when ActiveRecord::Relation value = value.select(value.klass.arel_table[value.klass.primary_key]) if value.select_values.empty? attribute.in(value.arel.ast) when Array, ActiveRecord::Associations::CollectionProxy values = value.to_a.map {|x| x.is_a?(ActiveRecord::Base) ? x.id : x} ranges, values = values.partition {|v| v.is_a?(Range) || v.is_a?(Arel::Relation)} array_predicates = ranges.map {|range| attribute.in(range)} if values.include?(nil) values = values.compact if values.empty? array_predicates << attribute.eq(nil) else array_predicates << attribute.in(values.compact).or(attribute.eq(nil)) end else array_predicates << attribute.in(values) end array_predicates.inject {|composite, predicate| composite.or(predicate)} when Range, Arel::Relation attribute.in(value) when ActiveRecord::Base attribute.eq(value.id) when Class # FIXME: I think we need to deprecate this behavior attribute.eq(value.name) when Integer, ActiveSupport::Duration # Arel treats integers as literals, but they should be quoted when compared with strings column = engine.connection.schema_cache.columns_hash[table.name][attribute.name.to_s] attribute.eq(Arel::Nodes::SqlLiteral.new(engine.connection.quote(value, column))) else attribute.eq(value) end end end predicates.flatten end endend
With the security patches applied and Rails 3.2.13 reverted the performance returns to normal. We also wereexperiencing UTF-8 errors when precompiling our assets and this is no longer happening. It seems there is a bunch of non-security related stuff in the 3.2.13 patch that is breaking stuff :(
@fredwu fixed this in a pull request: https://github.com/rails/rails/pull/9820
To use this fix right away (and don't have to wait on a new Rails release) you can monkey patch the AssetsPaths class in your own application by using the following code:
module Sprockets module Helpers module RailsHelper class AssetPaths < ::ActionView::AssetPaths private def rewrite_extension(source, dir, ext) source_ext = File.extname(source)[1..-1] if !ext || ext == source_ext source elsif source_ext.blank? "#{source}.#{ext}" elsif File.exists?(source) || exact_match_present?(source) source else "#{source}.#{ext}" end end end end endend