Add in-memory caching strategy for shared strings
In-memory implementation using SplFixedArray Updated code and tests to support errors when reading XML nodes (useful when reading XML files used for attacks) Removed LIBXML_NOENT option (which DOES substitute entities...) Added test for Quadratic Blowup attack
This commit is contained in:
1 parent
2dcb86aae9
commit
334f7087da
9 files changed
+199
-57
No files matched your search
@@ -2,6 +2,9 @@
|
||||
|
||||
namespace Box\Spout\Reader\Helper\XLSX;
|
||||
|
||||
use Box\Spout\Reader\Helper\XLSX\SharedStringsCaching\CachingStrategyFactory;
|
||||
use Box\Spout\Reader\Helper\XLSX\SharedStringsCaching\FileBasedStrategy;
|
||||
use Box\Spout\Reader\Helper\XLSX\SharedStringsCaching\InMemoryStrategy;
|
||||
use Box\Spout\TestUsingResource;
|
||||
|
||||
/**
|
||||
@@ -33,47 +36,6 @@ class SharedStringsHelperTest extends \PHPUnit_Framework_TestCase
|
||||
$this->sharedStringsHelper->cleanup();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testExtractSharedStringsShouldCreateTempFileWithSharedStrings()
|
||||
{
|
||||
$this->sharedStringsHelper->extractSharedStrings();
|
||||
|
||||
$cachingStrategy = \ReflectionHelper::getValueOnObject($this->sharedStringsHelper, 'cachingStrategy');
|
||||
$tempFolder = \ReflectionHelper::getValueOnObject($cachingStrategy, 'tempFolder');
|
||||
|
||||
$filesInTempFolder = $this->getFilesInFolder($tempFolder);
|
||||
$this->assertEquals(1, count($filesInTempFolder), 'One temp file should have been created in the temp folder.');
|
||||
|
||||
$tempFileContents = file_get_contents($filesInTempFolder[0]);
|
||||
$tempFileContentsPerLine = explode(PHP_EOL, $tempFileContents);
|
||||
|
||||
$this->assertEquals('s1--A1', $tempFileContentsPerLine[0]);
|
||||
$this->assertEquals('s1--E5', $tempFileContentsPerLine[24]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all files that are in the given folder.
|
||||
* It does not include "." and ".." and is not recursive.
|
||||
*
|
||||
* @param string $folderPath
|
||||
* @return array
|
||||
*/
|
||||
private function getFilesInFolder($folderPath)
|
||||
{
|
||||
$files = [];
|
||||
$directoryIterator = new \DirectoryIterator($folderPath);
|
||||
|
||||
foreach ($directoryIterator as $fileInfo) {
|
||||
if ($fileInfo->isFile()) {
|
||||
$files[] = $fileInfo->getPathname();
|
||||
}
|
||||
}
|
||||
|
||||
return $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* @expectedException \Box\Spout\Reader\Exception\SharedStringNotFoundException
|
||||
* @return void
|
||||
@@ -96,6 +58,9 @@ class SharedStringsHelperTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
$sharedString = $this->sharedStringsHelper->getStringAtIndex(24);
|
||||
$this->assertEquals('s1--E5', $sharedString);
|
||||
|
||||
$usedCachingStrategy = \ReflectionHelper::getValueOnObject($this->sharedStringsHelper, 'cachingStrategy');
|
||||
$this->assertTrue($usedCachingStrategy instanceof InMemoryStrategy);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -116,4 +81,26 @@ class SharedStringsHelperTest extends \PHPUnit_Framework_TestCase
|
||||
|
||||
$sharedStringsHelper->cleanup();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testGetStringAtIndexWithFileBasedStrategy()
|
||||
{
|
||||
$resourcePath = $this->getResourcePath('sheet_with_lots_of_shared_strings.xlsx');
|
||||
$sharedStringsHelper = new SharedStringsHelper($resourcePath);
|
||||
|
||||
$sharedStringsHelper->extractSharedStrings();
|
||||
|
||||
$sharedString = $sharedStringsHelper->getStringAtIndex(0);
|
||||
$this->assertEquals('str', $sharedString);
|
||||
|
||||
$sharedString = $sharedStringsHelper->getStringAtIndex(CachingStrategyFactory::MAX_NUM_STRINGS_PER_TEMP_FILE + 1);
|
||||
$this->assertEquals('str', $sharedString);
|
||||
|
||||
$usedCachingStrategy = \ReflectionHelper::getValueOnObject($sharedStringsHelper, 'cachingStrategy');
|
||||
$this->assertTrue($usedCachingStrategy instanceof FileBasedStrategy);
|
||||
|
||||
$sharedStringsHelper->cleanup();
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace Box\Spout\Reader;
|
||||
|
||||
use Box\Spout\Common\Exception\IOException;
|
||||
use Box\Spout\Common\Type;
|
||||
use Box\Spout\TestUsingResource;
|
||||
|
||||
@@ -243,18 +244,39 @@ class XLSXTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertEquals($expectedRow, $allRows[0], 'Pronunciation data should be removed.');
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public function dataProviderForTestReadShouldBeProtectedAgainstAttacks()
|
||||
{
|
||||
return [
|
||||
['attack_billion_laughs.xlsx'],
|
||||
['attack_quadratic_blowup.xlsx'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dataProviderForTestReadShouldBeProtectedAgainstAttacks
|
||||
* @NOTE: The LIBXML_NOENT is used to ACTUALLY substitute entities (and should therefore not be used)
|
||||
*
|
||||
* @param string $fileName
|
||||
* @return void
|
||||
*/
|
||||
public function testReadShouldBeProtectedAgainstBillionLaughsAttack()
|
||||
public function testReadShouldBeProtectedAgainstAttacks($fileName)
|
||||
{
|
||||
$allRows = $this->getAllRowsForFile('billion_laughs_test_file.xlsx');
|
||||
$startTime = microtime(true);
|
||||
|
||||
$expectedMaxMemoryUsage = 30 * 1024 * 1024; // 30MB
|
||||
$this->assertLessThan($expectedMaxMemoryUsage, memory_get_peak_usage(true), 'Entities should not be expanded and therefore consume all the memory.');
|
||||
try {
|
||||
$this->getAllRowsForFile($fileName);
|
||||
$this->fail('An exception should have been thrown');
|
||||
} catch (IOException $exception) {
|
||||
$duration = microtime(true) - $startTime;
|
||||
$this->assertLessThan(10, $duration, 'Entities should not be expanded and therefore take more than 10 seconds to be parsed.');
|
||||
|
||||
$expectedFirstRow = ['s1--A1', 's1--B1', 's1--C1', 's1--D1', 's1--E1'];
|
||||
$this->assertEquals($expectedFirstRow, $allRows[0], 'Entities should be ignored when reading XML files.');
|
||||
$expectedMaxMemoryUsage = 30 * 1024 * 1024; // 30MB
|
||||
$this->assertLessThan($expectedMaxMemoryUsage, memory_get_peak_usage(true), 'Entities should not be expanded and therefore consume all the memory.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in new issue
Block a user