TYPO3 CMS  TYPO3_8-7
ImageMagickFileTest.php
Go to the documentation of this file.
1 <?php
2 declare(strict_types = 1);
4 
5 /*
6  * This file is part of the TYPO3 CMS project.
7  *
8  * It is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License, either version 2
10  * of the License, or any later version.
11  *
12  * For the full copyright and license information, please read the
13  * LICENSE.txt file that was distributed with this source code.
14  *
15  * The TYPO3 project - inspiring people to share!
16  */
17 
25 
26 class ImageMagickFileTest extends FunctionalTestCase
27 {
31  private $directory;
32 
33  protected function setUp()
34  {
35  parent::setUp();
36 
37  $fixturePath = __DIR__ . '/Fixtures';
38  $structure = [];
39  $this->addFiles($structure, ['file.ai', 'file.ai.jpg'], $fixturePath . '/file.ai');
40  $this->addFiles($structure, ['file.bmp', 'file.bmp.jpg'], $fixturePath . '/file.bmp');
41  $this->addFiles($structure, ['file.gif', 'file.gif.jpg'], $fixturePath . '/file.gif');
42  $this->addFiles($structure, ['file.fax', 'file.fax.jpg'], $fixturePath . '/file.fax');
43  $this->addFiles($structure, ['file.jpg', 'file.jpg.png'], $fixturePath . '/file.jpg');
44  $this->addFiles($structure, ['file.png', 'file.png.jpg'], $fixturePath . '/file.png');
45  $this->addFiles($structure, ['file.svg', 'file.svg.jpg'], $fixturePath . '/file.svg');
46  $this->addFiles($structure, ['file.tif', 'file.tif.jpg'], $fixturePath . '/file.tif');
47  $this->addFiles($structure, ['file.webp', 'file.webp.jpg'], $fixturePath . '/file.webp');
48  $this->addFiles($structure, ['file.pdf', 'file.pdf.jpg'], $fixturePath . '/file.pdf');
49  $this->addFiles($structure, ['file.ps', 'file.ps.jpg'], $fixturePath . '/file.ps');
50  $this->addFiles($structure, ['file.eps', 'file.eps.jpg'], $fixturePath . '/file.eps');
51  $this->directory = vfsStream::setup('root', null, $structure);
52  }
53 
54  protected function tearDown()
55  {
56  unset($this->directory);
57  parent::tearDown();
58  }
59 
63  public function framesAreConsideredDataProvider(): array
64  {
65  return [
66  'file.pdf' => ['file.pdf', null, '\'pdf:{directory}/file.pdf\''],
67  'file.pdf[0]' => ['file.pdf', 0, '\'pdf:{directory}/file.pdf[0]\''],
68  ];
69  }
70 
79  public function framesAreConsidered(string $fileName, $frame, string $expectation)
80  {
81  $expectation = $this->substituteVariables($expectation);
82  $filePath = sprintf('%s/%s', $this->directory->url(), $fileName);
83  $file = ImageMagickFile::fromFilePath($filePath, $frame);
84  self::assertSame($expectation, (string)$file);
85  }
86 
90  public function resultIsEscapedDataProvider(): array
91  {
92  // probably Windows system
93  if (DIRECTORY_SEPARATOR === '\\') {
94  return [
95  'without frame' => ['file.pdf', null, '"pdf:{directory}/file.pdf"'],
96  'with first frame' => ['file.pdf', 0, '"pdf:{directory}/file.pdf[0]"'],
97  'special literals' => ['\'`%$!".png', 0, '"png:{directory}/\'` $ .png[0]"'],
98  ];
99  }
100  // probably Unix system
101  return [
102  'without frame' => ['file.pdf', null, '\'pdf:{directory}/file.pdf\''],
103  'with first frame' => ['file.pdf', 0, '\'pdf:{directory}/file.pdf[0]\''],
104  'special literals' => ['\'`%$!".png', 0, '\'png:{directory}/\'\\\'\'`%$!".png[0]\''],
105  ];
106  }
107 
116  public function resultIsEscaped(string $fileName, $frame, string $expectation)
117  {
118  $expectation = $this->substituteVariables($expectation);
119  $filePath = sprintf('%s/%s', $this->directory->url(), $fileName);
120  $file = ImageMagickFile::fromFilePath($filePath, $frame);
121  self::assertSame($expectation, (string)$file);
122  }
123 
127  public function fileStatementIsResolvedDataProvider(): array
128  {
129  return [
130  'file.ai' => ['file.ai', '\'pdf:{directory}/file.ai\''],
131  'file.ai.jpg' => ['file.ai.jpg', '\'pdf:{directory}/file.ai.jpg\''],
132  'file.gif' => ['file.gif', '\'gif:{directory}/file.gif\''],
133  'file.gif.jpg' => ['file.gif.jpg', '\'gif:{directory}/file.gif.jpg\''],
134  'file.jpg' => ['file.jpg', '\'jpg:{directory}/file.jpg\''],
135  'file.jpg.png' => ['file.jpg.png', '\'jpg:{directory}/file.jpg.png\''],
136  'file.png' => ['file.png', '\'png:{directory}/file.png\''],
137  'file.png.jpg' => ['file.png.jpg', '\'png:{directory}/file.png.jpg\''],
138  'file.svg' => ['file.svg', '\'svg:{directory}/file.svg\''],
139  'file.svg.jpg' => ['file.svg.jpg', '\'svg:{directory}/file.svg.jpg\''],
140  'file.tif' => ['file.tif', '\'tif:{directory}/file.tif\''],
141  'file.tif.jpg' => ['file.tif.jpg', '\'tif:{directory}/file.tif.jpg\''],
142  'file.webp' => ['file.webp', '\'webp:{directory}/file.webp\''],
143  'file.webp.jpg' => ['file.webp.jpg', '\'webp:{directory}/file.webp.jpg\''],
144  'file.pdf' => ['file.pdf', '\'pdf:{directory}/file.pdf\''],
145  'file.pdf.jpg' => ['file.pdf.jpg', '\'pdf:{directory}/file.pdf.jpg\''],
146  // accepted, since postscript files are converted using 'jpg:' format
147  'file.ps.jpg' => ['file.ps.jpg', '\'jpg:{directory}/file.ps.jpg\''],
148  'file.eps.jpg' => ['file.eps.jpg', '\'jpg:{directory}/file.eps.jpg\''],
149  ];
150  }
151 
159  public function fileStatementIsResolved(string $fileName, string $expectation)
160  {
161  $expectation = $this->substituteVariables($expectation);
162  $filePath = sprintf('%s/%s', $this->directory->url(), $fileName);
163  $file = ImageMagickFile::fromFilePath($filePath, null);
164  self::assertSame($expectation, (string)$file);
165  }
166 
174  {
175  return [
176  'file.ai.jpg' => ['file.ai.jpg', '\'jpg:{directory}/file.ai.jpg\'', 'inode/x-empty'],
177  'file.bmp.jpg' => ['file.bmp.jpg', '\'jpg:{directory}/file.bmp.jpg\'', 'inode/x-empty'],
178  'file.fax.jpg' => ['file.fax.jpg', '\'jpg:{directory}/file.fax.jpg\'', 'inode/x-empty'],
179  'file.gif.jpg' => ['file.gif.jpg', '\'jpg:{directory}/file.gif.jpg\'', 'inode/x-empty'],
180  'file.jpg' => ['file.jpg', '\'jpg:{directory}/file.jpg\'', 'inode/x-empty'],
181  'file.jpg.png' => ['file.jpg.png', '\'png:{directory}/file.jpg.png\'', 'inode/x-empty'],
182  'file.png' => ['file.png', '\'png:{directory}/file.png\'', 'inode/x-empty'],
183  'file.png.jpg' => ['file.png.jpg', '\'jpg:{directory}/file.png.jpg\'', 'inode/x-empty'],
184  'file.svg.jpg' => ['file.svg.jpg', '\'jpg:{directory}/file.svg.jpg\'', 'inode/x-empty'],
185  'file.tif' => ['file.tif', '\'tif:{directory}/file.tif\'', 'inode/x-empty'],
186  'file.tif.jpg' => ['file.tif.jpg', '\'jpg:{directory}/file.tif.jpg\'', 'inode/x-empty'],
187  'file.webp' => ['file.webp', '\'webp:{directory}/file.webp\'', 'inode/x-empty'],
188  'file.webp.jpg' => ['file.webp.jpg', '\'jpg:{directory}/file.webp.jpg\'', 'inode/x-empty'],
189  'file.pdf.jpg' => ['file.pdf.jpg', '\'jpg:{directory}/file.pdf.jpg\'', 'inode/x-empty'],
190  ];
191  }
192 
201  public function fileStatementIsResolvedForEnforcedMimeType(string $fileName, string $expectation, string $mimeType)
202  {
203  $this->simulateNextFileInfoInvocation($mimeType);
204  $expectation = $this->substituteVariables($expectation);
205  $filePath = sprintf('%s/%s', $this->directory->url(), $fileName);
206  $file = ImageMagickFile::fromFilePath($filePath, null);
207  self::assertSame($expectation, (string)$file);
208  }
209 
214  {
215  return [
216  'file.fax' => ['file.fax', '\'g3:{directory}/file.fax\''],
217  'file.bmp' => ['file.bmp', '\'dib:{directory}/file.bmp\''],
218  ];
219  }
220 
228  public function fileStatementIsResolvedForConfiguredMimeType(string $fileName, string $expectation)
229  {
230  $GLOBALS['TYPO3_CONF_VARS']['SYS']['FileInfo']['fileExtensionToMimeType']['g3'] = 'image/g3fax';
231  $GLOBALS['TYPO3_CONF_VARS']['SYS']['FileInfo']['fileExtensionToMimeType']['fax'] = 'image/g3fax';
232  $GLOBALS['TYPO3_CONF_VARS']['SYS']['FileInfo']['fileExtensionToMimeType']['dib'] = 'image/x-ms-bmp';
233  $GLOBALS['TYPO3_CONF_VARS']['SYS']['FileInfo']['fileExtensionToMimeType']['bmp'] = 'image/x-ms-bmp';
234 
235  $expectation = $this->substituteVariables($expectation);
236  $filePath = sprintf('%s/%s', $this->directory->url(), $fileName);
237  $file = ImageMagickFile::fromFilePath($filePath, null);
238  self::assertSame($expectation, (string)$file);
239  }
240 
244  public function fileStatementIsDeniedDataProvider(): array
245  {
246  return [
247  'file.ps' => ['file.ps'],
248  'file.eps' => ['file.eps'],
249  // denied since not defined in allowed extensions
250  'file.ai' => ['file.ai', 'inode/x-empty'],
251  'file.svg' => ['file.svg', 'inode/x-empty'],
252  'file.pdf' => ['file.pdf', 'inode/x-empty'],
253  ];
254  }
255 
263  public function fileStatementIsDenied(string $fileName, string $mimeType = null)
264  {
265  self::expectException(Exception::class);
266  self::expectExceptionCode(1550060977);
267 
268  if ($mimeType !== null) {
269  $this->simulateNextFileInfoInvocation($mimeType);
270  }
271 
272  $filePath = sprintf('%s/%s', $this->directory->url(), $fileName);
273  ImageMagickFile::fromFilePath($filePath, null);
274  }
275 
280  {
281  return [
282  'file.ps' => ['file.ps'],
283  'file.eps' => ['file.eps'],
284  ];
285  }
286 
293  public function fileStatementIsDeniedForConfiguredMimeType(string $fileName)
294  {
295  $GLOBALS['TYPO3_CONF_VARS']['SYS']['FileInfo']['fileExtensionToMimeType']['ps'] = 'image/x-see-no-evil';
296  $GLOBALS['TYPO3_CONF_VARS']['SYS']['FileInfo']['fileExtensionToMimeType']['eps'] = 'image/x-see-no-evil';
297 
298  self::expectException(Exception::class);
299  self::expectExceptionCode(1550060977);
300 
301  $filePath = sprintf('%s/%s', $this->directory->url(), $fileName);
302  ImageMagickFile::fromFilePath($filePath, null);
303  }
304 
310  private function addFiles(array &$structure, array $fileNames, string $sourcePath)
311  {
312  $structure = array_merge(
313  $structure,
314  array_fill_keys(
315  $fileNames,
316  file_get_contents($sourcePath)
317  )
318  );
319  }
320 
325  private function substituteVariables(string $value): string
326  {
327  return str_replace(
328  ['{directory}'],
329  [$this->directory->url()],
330  $value
331  );
332  }
333 
338  private function simulateNextFileInfoInvocation(string $mimeType, array $mimeExtensions = [])
339  {
341  $fileInfo = $this->getAccessibleMock(
342  FileInfo::class,
343  ['getMimeType', 'getMimeExtensions'],
344  [],
345  '',
346  false
347  );
348  $fileInfo->expects(self::atLeastOnce())->method('getMimeType')->willReturn($mimeType);
349  $fileInfo->expects(self::atLeastOnce())->method('getMimeExtensions')->willReturn($mimeExtensions);
350  GeneralUtility::addInstance(FileInfo::class, $fileInfo);
351  }
352 }
resultIsEscaped(string $fileName, $frame, string $expectation)
fileStatementIsResolved(string $fileName, string $expectation)
static addInstance($className, $instance)
framesAreConsidered(string $fileName, $frame, string $expectation)
fileStatementIsResolvedForEnforcedMimeType(string $fileName, string $expectation, string $mimeType)
addFiles(array &$structure, array $fileNames, string $sourcePath)
fileStatementIsDenied(string $fileName, string $mimeType=null)
static fromFilePath(string $filePath, int $frame=null)
if(TYPO3_MODE==='BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
fileStatementIsResolvedForConfiguredMimeType(string $fileName, string $expectation)