mirror of
https://github.com/PHPOffice/PhpSpreadsheet.git
synced 2026-09-16 13:06:31 +00:00
Merge commit from fork
* Test Protocol Not Supplied Before Using is_file * added missing assertFile and renamed prohibitPhar * More Tests --------- Co-authored-by: calligraf0 <115566010+calligraf0@users.noreply.github.com>
This commit is contained in:
@@ -31,19 +31,16 @@ class Downloader
|
||||
|
||||
public function __construct(string $folder, string $filename, ?string $filetype = null)
|
||||
{
|
||||
if ((is_dir($folder) === false) || (is_readable($folder) === false)) {
|
||||
throw new Exception('Folder is not accessible');
|
||||
}
|
||||
$filepath = "{$folder}/{$filename}";
|
||||
$this->filepath = (string) realpath($filepath);
|
||||
$this->filename = basename($filepath);
|
||||
clearstatcache();
|
||||
if ((is_file($this->filepath) === false) || (is_readable($this->filepath) === false)) {
|
||||
throw new Exception('File not found, or not a regular file, or cannot be read');
|
||||
$filepath = realpath("{$folder}/{$filename}");
|
||||
if ($filepath === false || !is_file($filepath) || !is_readable($filepath)) {
|
||||
throw new Exception('File not found, or cannot be read');
|
||||
}
|
||||
$this->filepath = $filepath;
|
||||
$this->filename = basename($this->filepath);
|
||||
|
||||
$filetype ??= pathinfo($filename, PATHINFO_EXTENSION);
|
||||
if (array_key_exists(strtolower($filetype), self::CONTENT_TYPES) === false) {
|
||||
$filetype ??= pathinfo($this->filename, PATHINFO_EXTENSION);
|
||||
if (!array_key_exists(strtolower($filetype), self::CONTENT_TYPES)) {
|
||||
throw new Exception('Invalid filetype: file cannot be downloaded');
|
||||
}
|
||||
$this->filetype = strtolower($filetype);
|
||||
|
||||
@@ -285,4 +285,16 @@ abstract class IOFactory
|
||||
|
||||
self::$readers[$readerType] = $readerClass;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, class-string<IReader>>
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @codeCoverageIgnore
|
||||
*/
|
||||
public static function getReaders(): array
|
||||
{
|
||||
return self::$readers;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,6 +93,7 @@ class Xml extends BaseReader
|
||||
];
|
||||
|
||||
// Open file
|
||||
File::assertFile($filename);
|
||||
$data = (string) file_get_contents($filename);
|
||||
$data = $this->getSecurityScannerOrThrow()->scan($data);
|
||||
|
||||
|
||||
@@ -133,11 +133,30 @@ class File
|
||||
return tempnam(self::sysGetTempDir(), 'phpspreadsheet') ?: throw new Exception('Could not create temporary file');
|
||||
}
|
||||
|
||||
/**
|
||||
* All filenames starting with protocol (e.g. phar://) are prohibited.
|
||||
* Note that many protocols, including http and zip, will already
|
||||
* return false for is_file.
|
||||
* A whitelist of protocols may be added if needed in future.
|
||||
*/
|
||||
public static function prohibitWrappers(string $filename): void
|
||||
{
|
||||
$scheme = parse_url($filename, PHP_URL_SCHEME);
|
||||
// strlen check > 1 to avoid issues with Windows absolute paths (e.g. C:\...), Windows quirks :)
|
||||
// since no built-in or commonly registered PHP stream wrapper uses a single-character scheme, this should be ok, to my knowledge
|
||||
if (is_string($scheme) && strlen($scheme) > 1) {
|
||||
throw new Exception(
|
||||
"Stream wrappers are not permitted as file paths: {$filename}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Assert that given path is an existing file and is readable, otherwise throw exception.
|
||||
*/
|
||||
public static function assertFile(string $filename, string $zipMember = ''): void
|
||||
{
|
||||
self::prohibitWrappers($filename);
|
||||
if (!is_file($filename) || !is_readable($filename)) {
|
||||
throw new ReaderException('File "' . $filename . '" does not exist or is not readable.');
|
||||
}
|
||||
@@ -156,9 +175,11 @@ class File
|
||||
|
||||
/**
|
||||
* Same as assertFile, except return true/false and don't throw Exception.
|
||||
* Will nevertheless throw if filename uses invalid protocol, e.g. phar.
|
||||
*/
|
||||
public static function testFileNoThrow(string $filename, ?string $zipMember = null): bool
|
||||
{
|
||||
self::prohibitWrappers($filename);
|
||||
if (!is_file($filename) || !is_readable($filename)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheetTests\Reader;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Exception as SpreadsheetException;
|
||||
use PhpOffice\PhpSpreadsheet\IOFactory;
|
||||
use PhpOffice\PhpSpreadsheet\Reader\IReader;
|
||||
use PHPUnit\Framework\Attributes\DataProvider;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class NoPharTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @param class-string<IReader> $reader
|
||||
*/
|
||||
#[DataProvider('providerReaders')]
|
||||
public function testNoPhar(string $reader): void
|
||||
{
|
||||
$this->expectException(SpreadsheetException::class);
|
||||
$this->expectExceptionMessage('Stream wrappers are not permitted');
|
||||
$reader = new $reader();
|
||||
$reader->load('phar://anyoldname');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<array<class-string<IReader>>>
|
||||
*/
|
||||
public static function providerReaders(): array
|
||||
{
|
||||
$readers = IOFactory::getReaders();
|
||||
$array = [];
|
||||
foreach ($readers as $key => $reader) {
|
||||
$array[$key] = [$reader];
|
||||
}
|
||||
|
||||
return $array;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user