8000 bug #44728 [Mime] Fix encoding filenames in multipart/form-data (nico… · symfony/symfony@0558be7 · GitHub
[go: up one dir, main page]

Skip to content

Commit 0558be7

Browse files
committed
bug #44728 [Mime] Fix encoding filenames in multipart/form-data (nicolas-grekas)
This PR was merged into the 4.4 branch. Discussion ---------- [Mime] Fix encoding filenames in multipart/form-data | Q | A | ------------- | --- | Branch? | 4.4 | Bug fix? | yes | New feature? | no | Deprecations? | no | Tickets | Fix #41249 | License | MIT | Doc PR | - File uploads that use forms have stick to browsers' behavior. This is captured in the WHATWG in their living standard: https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#multipart-form-data curl did the same a few weeks ago: curl/curl#7805 Commits ------- a58c342 [Mime] Fix encoding filenames in multipart/form-data
2 parents ac46701 + a58c342 commit 0558be7

File tree

2 files changed

+31
-1
lines changed

2 files changed

+31
-1
lines changed

src/Symfony/Component/Mime/Header/ParameterizedHeader.php

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,22 @@ private function createParameter(string $name, string $value): string
123123
$maxValueLength = $this->getMaxLineLength() - \strlen($name.'*N*="";') - 1;
124124
$firstLineOffset = \strlen($this->getCharset()."'".$this->getLanguage()."'");
125125
}
126+
127+
if (\in_array($name, ['name', 'filename'], true) && 'form-data' === $this->getValue() && 'content-disposition' === strtolower($this->getName()) && preg_match('//u', $value)) {
128+
// WHATWG HTML living standard 4.10.21.8 2 specifies:
129+
// For field names and filenames for file fields, the result of the
130+
// encoding in the previous bullet point must be escaped by replacing
131+
// any 0x0A (LF) bytes with the byte sequence `%0A`, 0x0D (CR) with `%0D`
132+
// and 0x22 (") with `%22`.
133+
// The user agent must not perform any other escapes.
134+
$value = str_replace(['"', "\r", "\n"], ['%22', '%0D', '%0A'], $value);
135+
136+
if (\strlen($value) <= $maxValueLength) {
137+
return $name.'="'.$value.'"';
138+
}
139+
140+
$value = $origValue;
141+
}
126142
}
127143

128144
// Encode if we need to
@@ -158,7 +174,7 @@ private function createParameter(string $name, string $value): string
158174
*/
159175
private function getEndOfParameterValue(string $value, bool $encoded = false, bool $firstLine = false): string
160176
{
161-
$forceHttpQuoting = 'content-disposition' === strtolower($this->getName()) && 'form-data' === $this->getValue();
177+
$forceHttpQuoting = 'form-data' === $this->getValue() && 'content-disposition' === strtolower($this->getName());
162178
if ($forceHttpQuoting || !preg_match('/^'.self::TOKEN_REGEX.'$/D', $value)) {
163179
$value = '"'.$value.'"';
164180
}

src/Symfony/Component/Mime/Tests/Header/ParameterizedHeaderTest.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,20 @@ public function testSpaceInParamResultsInQuotedString()
5858
$this->assertEquals('attachment; filename="my file.txt"', $header->getBodyAsString());
5959
}
6060

61+
public function testFormDataResultsInQuotedString()
62+
{
63+
$header = new ParameterizedHeader('Content-Disposition', 'form-data');
64+
$header->setParameters(['filename' => 'file.txt']);
65+
$this->assertEquals('form-data; filename="file.txt"', $header->getBodyAsString());
66+
}
67+
68+
public function testFormDataUtf8()
69+
{
70+
$header = new ParameterizedHeader('Content-Disposition', 'form-data');
71+
$header->setParameters(['filename' => "déjà%\"\n\r.txt"]);
72+
$this->assertEquals('form-data; filename="déjà%%22%0A%0D.txt"', $header->getBodyAsString());
73+
}
74+
6175
public function testLongParamsAreBrokenIntoMultipleAttributeStrings()
6276
{
6377
/* -- RFC 2231, 3.

0 commit comments

Comments
 (0)
0