lib/active_admin/csv_builder.rb in activeadmin-3.1.0 vs lib/active_admin/csv_builder.rb in activeadmin-3.2.0

- old
+ new

@@ -49,11 +49,11 @@ csv_options = options.except :encoding_options, :humanize_name, :byte_order_mark csv << bom if bom if column_names - csv << CSV.generate_line(columns.map { |c| encode c.name, options }, **csv_options) + csv << CSV.generate_line(columns.map { |c| sanitize(encode(c.name, options)) }, **csv_options) end controller.send(:in_paginated_batches) do |resource| csv << CSV.generate_line(build_row(resource, columns, options), **csv_options) end @@ -68,11 +68,11 @@ columns end def build_row(resource, columns, options) columns.map do |column| - encode call_method_or_proc_on(resource, column.data), options + sanitize(encode(call_method_or_proc_on(resource, column.data), options)) end end def encode(content, options) if options[:encoding] @@ -84,10 +84,14 @@ else content end end + def sanitize(content) + Sanitizer.sanitize(content) + end + def method_missing(method, *args, &block) if @view_context.respond_to? method @view_context.public_send method, *args, &block else super @@ -116,8 +120,25 @@ private def column_transitive_options @column_transitive_options ||= @options.slice(*COLUMN_TRANSITIVE_OPTIONS) + end + end + + # Prevents CSV Injection according to https://owasp.org/www-community/attacks/CSV_Injection + module Sanitizer + extend self + + ATTACK_CHARACTERS = ["=", "+", "-", "@", "\t", "\r"].freeze + + def sanitize(value) + return "'#{value}" if require_sanitization?(value) + + value + end + + def require_sanitization?(value) + value.is_a?(String) && value.starts_with?(*ATTACK_CHARACTERS) end end end