VYPR
Moderate severityNVD Advisory· Published Nov 24, 2021· Updated Aug 4, 2024

CSV Injection in Symfony

CVE-2021-41270

Description

Symfony/Serializer handles serializing and deserializing data structures for Symfony, a PHP framework for web and console applications and a set of reusable PHP components. Symfony versions 4.1.0 before 4.4.35 and versions 5.0.0 before 5.3.12 are vulnerable to CSV injection, also known as formula injection. In Symfony 4.1, maintainers added the opt-in csv_escape_formulas option in the CsvEncoder, to prefix all cells starting with =, +, - or @ with a tab \t. Since then, OWASP added 2 chars in that list: Tab (0x09) and Carriage return (0x0D). This makes the previous prefix char (Tab \t) part of the vulnerable characters, and OWASP suggests using the single quote ' for prefixing the value. Starting with versions 4.4.34 and 5.3.12, Symfony now follows the OWASP recommendations and uses the single quote ' to prefix formulas and add the prefix to cells starting by \t, \r as well as =, +, - and @.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
symfony/serializerPackagist
>= 5.0.0, < 5.3.125.3.12
symfony/serializerPackagist
>= 4.1.0, < 4.4.354.4.35
symfony/symfonyPackagist
>= 4.1.0, < 4.4.354.4.35
symfony/symfonyPackagist
>= 5.0.0, < 5.3.125.3.12

Affected products

1

Patches

1
3da6f2d45e75

Use single quote to escape formulas

https://github.com/symfony/symfonyJérémy DerusséNov 15, 2021via ghsa
2 files changed · +81 11
  • src/Symfony/Component/Serializer/Encoder/CsvEncoder.php+4 3 modified
    @@ -35,7 +35,8 @@ class CsvEncoder implements EncoderInterface, DecoderInterface
     
         private const UTF8_BOM = "\xEF\xBB\xBF";
     
    -    private $formulasStartCharacters = ['=', '-', '+', '@'];
    +    private const FORMULAS_START_CHARACTERS = ['=', '-', '+', '@', "\t", "\r"];
    +
         private $defaultContext = [
             self::DELIMITER_KEY => ',',
             self::ENCLOSURE_KEY => '"',
    @@ -238,8 +239,8 @@ private function flatten(iterable $array, array &$result, string $keySeparator,
                 if (is_iterable($value)) {
                     $this->flatten($value, $result, $keySeparator, $parentKey.$key.$keySeparator, $escapeFormulas);
                 } else {
    -                if ($escapeFormulas && \in_array(substr((string) $value, 0, 1), $this->formulasStartCharacters, true)) {
    -                    $result[$parentKey.$key] = "\t".$value;
    +                if ($escapeFormulas && \in_array(substr((string) $value, 0, 1), self::FORMULAS_START_CHARACTERS, true)) {
    +                    $result[$parentKey.$key] = "'".$value;
                     } else {
                         // Ensures an actual value is used when dealing with true and false
                         $result[$parentKey.$key] = false === $value ? 0 : (true === $value ? 1 : $value);
    
  • src/Symfony/Component/Serializer/Tests/Encoder/CsvEncoderTest.php+77 8 modified
    @@ -285,31 +285,52 @@ private function doTestEncodeFormulas(bool $legacy = false)
     
             $this->assertSame(<<<'CSV'
     0
    -"	=2+3"
    +'=2+3
     
     CSV
                 , $this->encoder->encode(['=2+3'], 'csv'));
     
             $this->assertSame(<<<'CSV'
     0
    -"	-2+3"
    +'-2+3
     
     CSV
                 , $this->encoder->encode(['-2+3'], 'csv'));
     
             $this->assertSame(<<<'CSV'
     0
    -"	+2+3"
    +'+2+3
     
     CSV
                 , $this->encoder->encode(['+2+3'], 'csv'));
     
             $this->assertSame(<<<'CSV'
     0
    -"	@MyDataColumn"
    +'@MyDataColumn
     
     CSV
                 , $this->encoder->encode(['@MyDataColumn'], 'csv'));
    +
    +        $this->assertSame(<<<'CSV'
    +0
    +"'	tab"
    +
    +CSV
    +            , $this->encoder->encode(["\ttab"], 'csv'));
    +
    +        $this->assertSame(<<<'CSV'
    +0
    +"'=1+2"";=1+2"
    +
    +CSV
    +            , $this->encoder->encode(['=1+2";=1+2'], 'csv'));
    +
    +        $this->assertSame(<<<'CSV'
    +0
    +"'=1+2'"" ;,=1+2"
    +
    +CSV
    +            , $this->encoder->encode(['=1+2\'" ;,=1+2'], 'csv'));
         }
     
         public function testDoNotEncodeFormulas()
    @@ -341,13 +362,34 @@ public function testDoNotEncodeFormulas()
     
     CSV
                 , $this->encoder->encode(['@MyDataColumn'], 'csv'));
    +
    +        $this->assertSame(<<<'CSV'
    +0
    +"	tab"
    +
    +CSV
    +            , $this->encoder->encode(["\ttab"], 'csv'));
    +
    +        $this->assertSame(<<<'CSV'
    +0
    +"=1+2"";=1+2"
    +
    +CSV
    +            , $this->encoder->encode(['=1+2";=1+2'], 'csv'));
    +
    +        $this->assertSame(<<<'CSV'
    +0
    +"=1+2'"" ;,=1+2"
    +
    +CSV
    +            , $this->encoder->encode(['=1+2\'" ;,=1+2'], 'csv'));
         }
     
         public function testEncodeFormulasWithSettingsPassedInContext()
         {
             $this->assertSame(<<<'CSV'
     0
    -"	=2+3"
    +'=2+3
     
     CSV
                 , $this->encoder->encode(['=2+3'], 'csv', [
    @@ -356,7 +398,7 @@ public function testEncodeFormulasWithSettingsPassedInContext()
     
             $this->assertSame(<<<'CSV'
     0
    -"	-2+3"
    +'-2+3
     
     CSV
                 , $this->encoder->encode(['-2+3'], 'csv', [
    @@ -365,7 +407,7 @@ public function testEncodeFormulasWithSettingsPassedInContext()
     
             $this->assertSame(<<<'CSV'
     0
    -"	+2+3"
    +'+2+3
     
     CSV
                 , $this->encoder->encode(['+2+3'], 'csv', [
    @@ -374,12 +416,39 @@ public function testEncodeFormulasWithSettingsPassedInContext()
     
             $this->assertSame(<<<'CSV'
     0
    -"	@MyDataColumn"
    +'@MyDataColumn
     
     CSV
                 , $this->encoder->encode(['@MyDataColumn'], 'csv', [
                     CsvEncoder::ESCAPE_FORMULAS_KEY => true,
                 ]));
    +
    +        $this->assertSame(<<<'CSV'
    +0
    +"'	tab"
    +
    +CSV
    +            , $this->encoder->encode(["\ttab"], 'csv', [
    +                CsvEncoder::ESCAPE_FORMULAS_KEY => true,
    +            ]));
    +
    +        $this->assertSame(<<<'CSV'
    +0
    +"'=1+2"";=1+2"
    +
    +CSV
    +            , $this->encoder->encode(['=1+2";=1+2'], 'csv', [
    +                CsvEncoder::ESCAPE_FORMULAS_KEY => true,
    +            ]));
    +
    +        $this->assertSame(<<<'CSV'
    +0
    +"'=1+2'"" ;,=1+2"
    +
    +CSV
    +            , $this->encoder->encode(['=1+2\'" ;,=1+2'], 'csv', [
    +                CsvEncoder::ESCAPE_FORMULAS_KEY => true,
    +            ]));
         }
     
         public function testEncodeWithoutHeader()
    

Vulnerability mechanics

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

References

15

News mentions

0

No linked articles in our index yet.