Skip to content

Commit 3379d3e

Browse files
security #cve-2021-41270 [Serializer] Use single quote to escape formulas (jderusse)
This PR was merged into the 4.4 branch.
2 parents af881a9 + 3da6f2d commit 3379d3e

File tree

2 files changed

+81
-11
lines changed

2 files changed

+81
-11
lines changed

src/Symfony/Component/Serializer/Encoder/CsvEncoder.php

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,8 @@ class CsvEncoder implements EncoderInterface, DecoderInterface
3535

3636
private const UTF8_BOM = "\xEF\xBB\xBF";
3737

38-
private $formulasStartCharacters = ['=', '-', '+', '@'];
38+
private const FORMULAS_START_CHARACTERS = ['=', '-', '+', '@', "\t", "\r"];
39+
3940
private $defaultContext = [
4041
self::DELIMITER_KEY => ',',
4142
self::ENCLOSURE_KEY => '"',
@@ -238,8 +239,8 @@ private function flatten(iterable $array, array &$result, string $keySeparator,
238239
if (is_iterable($value)) {
239240
$this->flatten($value, $result, $keySeparator, $parentKey.$key.$keySeparator, $escapeFormulas);
240241
} else {
241-
if ($escapeFormulas && \in_array(substr((string) $value, 0, 1), $this->formulasStartCharacters, true)) {
242-
$result[$parentKey.$key] = "\t".$value;
242+
if ($escapeFormulas && \in_array(substr((string) $value, 0, 1), self::FORMULAS_START_CHARACTERS, true)) {
243+
$result[$parentKey.$key] = "'".$value;
243244
} else {
244245
// Ensures an actual value is used when dealing with true and false
245246
$result[$parentKey.$key] = false === $value ? 0 : (true === $value ? 1 : $value);

src/Symfony/Component/Serializer/Tests/Encoder/CsvEncoderTest.php

Lines changed: 77 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -285,31 +285,52 @@ private function doTestEncodeFormulas(bool $legacy = false)
285285

286286
$this->assertSame(<<<'CSV'
287287
0
288-
" =2+3"
288+
'=2+3
289289

290290
CSV
291291
, $this->encoder->encode(['=2+3'], 'csv'));
292292

293293
$this->assertSame(<<<'CSV'
294294
0
295-
" -2+3"
295+
'-2+3
296296

297297
CSV
298298
, $this->encoder->encode(['-2+3'], 'csv'));
299299

300300
$this->assertSame(<<<'CSV'
301301
0
302-
" +2+3"
302+
'+2+3
303303

304304
CSV
305305
, $this->encoder->encode(['+2+3'], 'csv'));
306306

307307
$this->assertSame(<<<'CSV'
308308
0
309-
" @MyDataColumn"
309+
'@MyDataColumn
310310

311311
CSV
312312
, $this->encoder->encode(['@MyDataColumn'], 'csv'));
313+
314+
$this->assertSame(<<<'CSV'
315+
0
316+
"' tab"
317+
318+
CSV
319+
, $this->encoder->encode(["\ttab"], 'csv'));
320+
321+
$this->assertSame(<<<'CSV'
322+
0
323+
"'=1+2"";=1+2"
324+
325+
CSV
326+
, $this->encoder->encode(['=1+2";=1+2'], 'csv'));
327+
328+
$this->assertSame(<<<'CSV'
329+
0
330+
"'=1+2'"" ;,=1+2"
331+
332+
CSV
333+
, $this->encoder->encode(['=1+2\'" ;,=1+2'], 'csv'));
313334
}
314335

315336
public function testDoNotEncodeFormulas()
@@ -341,13 +362,34 @@ public function testDoNotEncodeFormulas()
341362

342363
CSV
343364
, $this->encoder->encode(['@MyDataColumn'], 'csv'));
365+
366+
$this->assertSame(<<<'CSV'
367+
0
368+
" tab"
369+
370+
CSV
371+
, $this->encoder->encode(["\ttab"], 'csv'));
372+
373+
$this->assertSame(<<<'CSV'
374+
0
375+
"=1+2"";=1+2"
376+
377+
CSV
378+
, $this->encoder->encode(['=1+2";=1+2'], 'csv'));
379+
380+
$this->assertSame(<<<'CSV'
381+
0
382+
"=1+2'"" ;,=1+2"
383+
384+
CSV
385+
, $this->encoder->encode(['=1+2\'" ;,=1+2'], 'csv'));
344386
}
345387

346388
public function testEncodeFormulasWithSettingsPassedInContext()
347389
{
348390
$this->assertSame(<<<'CSV'
349391
0
350-
" =2+3"
392+
'=2+3
351393

352394
CSV
353395
, $this->encoder->encode(['=2+3'], 'csv', [
@@ -356,7 +398,7 @@ public function testEncodeFormulasWithSettingsPassedInContext()
356398

357399
$this->assertSame(<<<'CSV'
358400
0
359-
" -2+3"
401+
'-2+3
360402

361403
CSV
362404
, $this->encoder->encode(['-2+3'], 'csv', [
@@ -365,7 +407,7 @@ public function testEncodeFormulasWithSettingsPassedInContext()
365407

366408
$this->assertSame(<<<'CSV'
367409
0
368-
" +2+3"
410+
'+2+3
369411

370412
CSV
371413
, $this->encoder->encode(['+2+3'], 'csv', [
@@ -374,12 +416,39 @@ public function testEncodeFormulasWithSettingsPassedInContext()
374416

375417
$this->assertSame(<<<'CSV'
376418
0
377-
" @MyDataColumn"
419+
'@MyDataColumn
378420

379421
CSV
380422
, $this->encoder->encode(['@MyDataColumn'], 'csv', [
381423
CsvEncoder::ESCAPE_FORMULAS_KEY => true,
382424
]));
425+
426+
$this->assertSame(<<<'CSV'
427+
0
428+
"' tab"
429+
430+
CSV
431+
, $this->encoder->encode(["\ttab"], 'csv', [
432+
CsvEncoder::ESCAPE_FORMULAS_KEY => true,
433+
]));
434+
435+
$this->assertSame(<<<'CSV'
436+
0
437+
"'=1+2"";=1+2"
438+
439+
CSV
440+
, $this->encoder->encode(['=1+2";=1+2'], 'csv', [
441+
CsvEncoder::ESCAPE_FORMULAS_KEY => true,
442+
]));
443+
444+
$this->assertSame(<<<'CSV'
445+
0
446+
"'=1+2'"" ;,=1+2"
447+
448+
CSV
449+
, $this->encoder->encode(['=1+2\'" ;,=1+2'], 'csv', [
450+
CsvEncoder::ESCAPE_FORMULAS_KEY => true,
451+
]));
383452
}
384453

385454
public function testEncodeWithoutHeader()

0 commit comments

Comments
 (0)