Skip to content

Commit aa0bf1c

Browse files
author
MateuszKolankowski
committed
Add filename validation in DownloadController and corresponding tests
1 parent 56ff8b6 commit aa0bf1c

2 files changed

Lines changed: 148 additions & 0 deletions

File tree

src/lib/MVC/Symfony/Controller/Content/DownloadController.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,10 @@ public function downloadBinaryFileAction(int $contentId, string $fieldIdentifier
116116
);
117117
}
118118

119+
if ($field->value->fileName !== $filename) {
120+
throw new NotFoundException('File', $filename);
121+
}
122+
119123
$response = new BinaryStreamResponse($this->ioService->loadBinaryFile($field->value->id), $this->ioService);
120124
$response->setContentDisposition(
121125
ResponseHeaderBag::DISPOSITION_ATTACHMENT,
Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
<?php
2+
3+
/**
4+
* @copyright Copyright (C) Ibexa AS. All rights reserved.
5+
* @license For full copyright and license information view LICENSE file distributed with this source code.
6+
*/
7+
namespace Ibexa\Tests\Core\MVC\Symfony\Controller\Controller\Content;
8+
9+
use DateTime;
10+
use Ibexa\Bundle\IO\BinaryStreamResponse;
11+
use Ibexa\Contracts\Core\Repository\ContentService;
12+
use Ibexa\Contracts\Core\Repository\Values\Content\ContentInfo;
13+
use Ibexa\Contracts\Core\Repository\Values\Content\Field;
14+
use Ibexa\Core\Base\Exceptions\NotFoundException;
15+
use Ibexa\Core\FieldType\BinaryFile\Value as BinaryFileValue;
16+
use Ibexa\Core\Helper\TranslationHelper;
17+
use Ibexa\Core\IO\IOServiceInterface;
18+
use Ibexa\Core\IO\Values\BinaryFile;
19+
use Ibexa\Core\MVC\Symfony\Controller\Content\DownloadController;
20+
use Ibexa\Core\Repository\Values\Content\Content;
21+
use Ibexa\Core\Repository\Values\Content\VersionInfo;
22+
use PHPUnit\Framework\TestCase;
23+
use Symfony\Component\HttpFoundation\Request;
24+
25+
/**
26+
* @covers \Ibexa\Core\MVC\Symfony\Controller\Content\DownloadController
27+
*/
28+
final class DownloadControllerTest extends TestCase
29+
{
30+
/** @var \Ibexa\Contracts\Core\Repository\ContentService|\PHPUnit\Framework\MockObject\MockObject */
31+
private $contentService;
32+
33+
/** @var \Ibexa\Core\IO\IOServiceInterface|\PHPUnit\Framework\MockObject\MockObject */
34+
private $ioService;
35+
36+
/** @var \Ibexa\Core\Helper\TranslationHelper|\PHPUnit\Framework\MockObject\MockObject */
37+
private $translationHelper;
38+
39+
protected function setUp(): void
40+
{
41+
parent::setUp();
42+
43+
$this->contentService = $this->createMock(ContentService::class);
44+
$this->ioService = $this->createMock(IOServiceInterface::class);
45+
$this->translationHelper = $this->createMock(TranslationHelper::class);
46+
}
47+
48+
public function testDownloadBinaryFileActionReturnsBinaryResponseWhenFilenameMatches(): void
49+
{
50+
$content = $this->createContent();
51+
$field = $content->getField('file', 'eng-GB');
52+
self::assertInstanceOf(Field::class, $field);
53+
54+
$request = new Request(['inLanguage' => 'eng-GB']);
55+
$binaryFile = new BinaryFile([
56+
'id' => 'binary-file-id',
57+
'mtime' => new DateTime(),
58+
'size' => 123,
59+
'uri' => 'binary-file-uri',
60+
]);
61+
62+
$this->contentService
63+
->expects($this->once())
64+
->method('loadContent')
65+
->with(42)
66+
->willReturn($content);
67+
$this->translationHelper
68+
->expects($this->once())
69+
->method('getTranslatedField')
70+
->with($content, 'file', 'eng-GB')
71+
->willReturn($field);
72+
$this->ioService
73+
->expects($this->once())
74+
->method('loadBinaryFile')
75+
->with('binary-file-id')
76+
->willReturn($binaryFile);
77+
78+
$response = $this->createController()->downloadBinaryFileAction(42, 'file', 'Test-file.pdf', $request);
79+
80+
self::assertInstanceOf(BinaryStreamResponse::class, $response);
81+
self::assertStringContainsString('attachment;', (string) $response->headers->get('Content-Disposition'));
82+
self::assertStringContainsString('Test-file.pdf', (string) $response->headers->get('Content-Disposition'));
83+
}
84+
85+
public function testDownloadBinaryFileActionReturnsNotFoundWhenFilenameDoesNotMatch(): void
86+
{
87+
$this->expectException(NotFoundException::class);
88+
89+
$content = $this->createContent();
90+
$field = $content->getField('file', 'eng-GB');
91+
self::assertInstanceOf(Field::class, $field);
92+
93+
$request = new Request(['inLanguage' => 'eng-GB']);
94+
95+
$this->contentService
96+
->expects($this->once())
97+
->method('loadContent')
98+
->with(42)
99+
->willReturn($content);
100+
$this->translationHelper
101+
->expects($this->once())
102+
->method('getTranslatedField')
103+
->with($content, 'file', 'eng-GB')
104+
->willReturn($field);
105+
$this->ioService
106+
->expects($this->never())
107+
->method('loadBinaryFile');
108+
109+
$this->createController()->downloadBinaryFileAction(42, 'file', 'SomeRandomText.txt', $request);
110+
}
111+
112+
private function createController(): DownloadController
113+
{
114+
return new DownloadController(
115+
$this->contentService,
116+
$this->ioService,
117+
$this->translationHelper
118+
);
119+
}
120+
121+
private function createContent(): Content
122+
{
123+
return new Content([
124+
'internalFields' => [
125+
new Field([
126+
'fieldDefIdentifier' => 'file',
127+
'languageCode' => 'eng-GB',
128+
'value' => new BinaryFileValue([
129+
'id' => 'binary-file-id',
130+
'fileName' => 'Test-file.pdf',
131+
]),
132+
]),
133+
],
134+
'versionInfo' => new VersionInfo([
135+
'contentInfo' => new ContentInfo([
136+
'id' => 42,
137+
'mainLanguageCode' => 'eng-GB',
138+
'name' => 'Test content',
139+
'status' => ContentInfo::STATUS_PUBLISHED,
140+
]),
141+
]),
142+
]);
143+
}
144+
}

0 commit comments

Comments
 (0)