VYPR
Moderate severityNVD Advisory· Published Dec 24, 2023· Updated Feb 23, 2026

CVE-2023-51763

CVE-2023-51763

Description

csv_builder.rb in ActiveAdmin (aka Active Admin) before 3.2.0 allows CSV injection.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

ActiveAdmin before 3.2.0 is vulnerable to CSV injection, allowing crafted data to execute arbitrary formulas when exported CSV files are opened by spreadsheet applications.

Root

Cause

CVE-2023-51763 describes a CSV injection vulnerability in the csv_builder.rb file of ActiveAdmin, a popular administration framework for Ruby on Rails. The vulnerability arises because column headers and cell values are not sanitized for characters that spreadsheet applications interpret as formula prefixes. When generating CSV output, the build method directly writes data without escaping leading characters such as =, +, -, @, \t, and \r [1][3]. As a result, any string starting with these characters is output verbatim into the CSV file.

Attack

Vector and Prerequisites

An attacker must first be able to inject or influence values that appear in the exported CSV. In a typical ActiveAdmin context, this could be achieved through user-submitted record attributes (e.g., model fields) that are later included in a CSV download. No special authentication is required beyond normal access to download the CSV; the threat is that a malicious user could plant a payload (such as =CMD(...) or =HYPERLINK(...) in the underlying data. When an administrator or other user opens the exported CSV in a spreadsheet program like Microsoft Excel or LibreOffice Calc, the spreadsheet executes the formula, potentially leading to remote code execution or data exfiltration [2][4].

Impact

Successful exploitation of a CSV injection can lead to arbitrary command execution on the machine of the user opening the file, or to the exfiltration of sensitive data (e.g., via outbound network requests). Because ActiveAdmin is often used to manage backend data, the attacker may be able to craft payloads that leverage formulas like DDE or WEBSERVICE to achieve these effects. The impact is dependent on the spreadsheet application and its security settings, but the potential for remote code execution is severe [2][4].

Mitigation

The vulnerability was addressed in ActiveAdmin version 3.2.0, released on December 24, 2023. The fix introduces a Sanitizer module that prepends a single quote (') to any string value beginning with one of the dangerous characters, causing the spreadsheet to treat the cell content as plain text rather than a formula [1][3]. Users are strongly advised to upgrade to v3.2.0 or later. For versions on the 3-0 stable branch, a backport commit is also available [3]. No known workaround exists for earlier versions, so upgrading is the recommended course of action.

AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
activeadminRubyGems
< 3.2.03.2.0

Affected products

2

Patches

1
7af735cf657c

Backport to 3-0 stable branch protect against CSV Injection (#8161) (#8167)

https://github.com/activeadmin/activeadminMatias GrunbergDec 11, 2023via ghsa
2 files changed · +71 2
  • lib/active_admin/csv_builder.rb+23 2 modified
    @@ -51,7 +51,7 @@ def build(controller, csv)
           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|
    @@ -70,7 +70,7 @@ def exec_columns(view_context = nil)
     
         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
     
    @@ -86,6 +86,10 @@ def encode(content, options)
           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
    @@ -120,4 +124,21 @@ 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
    
  • spec/unit/csv_builder_spec.rb+48 0 modified
    @@ -277,4 +277,52 @@ def view_context
           end
         end
       end
    +
    +  context "csv injection" do
    +    let(:dummy_controller) do
    +      class DummyController
    +        def in_paginated_batches(&block)
    +          Post.all.each(&block)
    +        end
    +
    +        def view_context
    +          MethodOrProcHelper
    +        end
    +      end
    +      DummyController.new
    +    end
    +
    +    let(:builder) do
    +      ActiveAdmin::CSVBuilder.new do
    +        column(:id)
    +        column(:title)
    +      end
    +    end
    +
    +    ["=", "+", "-", "@", "\t", "\r"].each do |char|
    +      it "prepends a single quote when column starts with a #{char} character" do
    +        attack = "#{char}1+2"
    +
    +        escaped_attack = "'#{attack}"
    +        escaped_attack = "\"#{escaped_attack}\"" if char == "\r"
    +
    +        post = Post.create!(title: attack)
    +        receiver = []
    +        builder.build dummy_controller, receiver
    +        line = receiver.last
    +        expect(line).to eq "#{post.id},#{escaped_attack}\n"
    +      end
    +
    +      it "accounts for the field separator when character #{char} is used to inject a formula" do
    +        attack = "#{char}1+2'\" ;,#{char}1+2"
    +        escaped_attack = "\"'#{attack.gsub('"', '""')}\""
    +
    +        post = Post.create!(title: attack)
    +        receiver = []
    +        builder.build dummy_controller, receiver
    +        line = receiver.last
    +        expect(line).to eq "#{post.id},#{escaped_attack}\n"
    +      end
    +    end
    +  end
     end
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

10

News mentions

0

No linked articles in our index yet.