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