fix: Sanitize filename on download (#9960)

* fix: Sanitize filename on download
* fix: filename encoding in the Content-Disposition header

This improves the handling of the filename* parameter in the Content-Disposition header. Now, the filename* parameter is only used when it differs from the fallback filename

* tests: Add test for the filename* parameter in Content-Disposition
This commit is contained in:
Oscar Di Manno
2025-09-14 11:50:31 +02:00
committed by GitHub
parent a03221041e
commit b7fb465486
2 changed files with 13 additions and 4 deletions

View File

@@ -256,15 +256,15 @@ abstract class rcube_output
// @phpstan-ignore-next-line
if (is_string($filename) && $filename !== '' && strlen($filename) <= 1024) {
// For non-ascii characters we'll use RFC2231 syntax
if (!preg_match('/[^a-zA-Z0-9_.:,?;@+ -]/', $filename)) {
$disposition .= "; filename=\"{$filename}\"";
} else {
$fallback_filename = preg_replace('/[^a-zA-Z0-9_.(),;@+ -]/', '_', $filename);
$disposition .= "; filename=\"{$fallback_filename}\"";
if ($fallback_filename != $filename) {
$filename = rawurlencode($filename);
$charset = $this->charset;
if (!empty($params['charset']) && rcube_charset::is_valid($params['charset'])) {
$charset = $params['charset'];
}
$disposition .= "; filename*={$charset}''{$filename}";
}
}

View File

@@ -26,6 +26,15 @@ class OutputTest extends TestCase
$this->assertContains('Content-Type: application/octet-stream', $output->headers);
$this->assertContains('Content-Security-Policy: default-src \'none\'; img-src \'self\'', $output->headers);
// Test handling of filename*
$output->reset();
$output->download_headers('test ? test');
$this->assertCount(3, $output->headers);
$this->assertContains('Content-Disposition: attachment; filename="test _ test"; filename*=' . RCUBE_CHARSET . "''" . rawurlencode('test ? test'), $output->headers);
$this->assertContains('Content-Type: application/octet-stream', $output->headers);
$this->assertContains('Content-Security-Policy: default-src \'none\'; img-src \'self\'', $output->headers);
// Invalid content type
$output->reset();
$params = ['type' => 'invalid'];