mirror of
https://github.com/PHPOffice/PhpSpreadsheet.git
synced 2026-08-30 03:59:11 +00:00
Security Patches
This commit is contained in:
+12
-12
@@ -20,7 +20,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Install locales
|
||||
run: sudo apt-get update && sudo apt-get install -y language-pack-fr language-pack-de
|
||||
@@ -37,7 +37,7 @@ jobs:
|
||||
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Cache composer dependencies
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v6
|
||||
with:
|
||||
path: ${{ steps.composer-cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
|
||||
@@ -68,7 +68,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
with:
|
||||
fetch-depth: 2
|
||||
|
||||
@@ -87,7 +87,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Setup PHP, with composer and extensions
|
||||
uses: shivammathur/setup-php@v2
|
||||
@@ -102,7 +102,7 @@ jobs:
|
||||
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Cache composer dependencies
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v6
|
||||
with:
|
||||
path: ${{ steps.composer-cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
|
||||
@@ -118,7 +118,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Setup PHP, with composer and extensions
|
||||
uses: shivammathur/setup-php@v2
|
||||
@@ -133,7 +133,7 @@ jobs:
|
||||
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Cache composer dependencies
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v6
|
||||
with:
|
||||
path: ${{ steps.composer-cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
|
||||
@@ -149,7 +149,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Setup PHP, with composer and extensions
|
||||
uses: shivammathur/setup-php@v2
|
||||
@@ -164,7 +164,7 @@ jobs:
|
||||
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Cache composer dependencies
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v6
|
||||
with:
|
||||
path: ${{ steps.composer-cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
|
||||
@@ -180,7 +180,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v6
|
||||
uses: actions/checkout@v7
|
||||
|
||||
- name: Setup PHP, with composer and extensions
|
||||
uses: shivammathur/setup-php@v2
|
||||
@@ -195,7 +195,7 @@ jobs:
|
||||
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Cache composer dependencies
|
||||
uses: actions/cache@v5
|
||||
uses: actions/cache@v6
|
||||
with:
|
||||
path: ${{ steps.composer-cache.outputs.dir }}
|
||||
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
|
||||
@@ -213,7 +213,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.event_name == 'push' && contains(github.ref, 'refs/tags/')
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v7
|
||||
with:
|
||||
ref: ${{ github.ref }} # Otherwise our annotated tag is not fetched and we cannot get correct version
|
||||
|
||||
|
||||
@@ -45,6 +45,7 @@ class Service
|
||||
// Get results from the the webservice
|
||||
$ctxArray = [
|
||||
'http' => [
|
||||
'follow_location' => 0,
|
||||
'user_agent' => 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
|
||||
],
|
||||
];
|
||||
@@ -57,7 +58,7 @@ class Service
|
||||
return ExcelError::VALUE(); // Output not a string or too long
|
||||
}
|
||||
|
||||
return $output;
|
||||
return ($output === '') ? Functions::NOT_YET_IMPLEMENTED : $output;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -58,6 +58,14 @@ class Gnumeric extends BaseReader
|
||||
],
|
||||
];
|
||||
|
||||
protected int $maxLength;
|
||||
|
||||
private const LENGTH_MULTIPLIER = [
|
||||
'G' => 1024 * 1024 * 1024,
|
||||
'M' => 1024 * 1024,
|
||||
'K' => 1024,
|
||||
];
|
||||
|
||||
/**
|
||||
* Create a new Gnumeric.
|
||||
*/
|
||||
@@ -66,6 +74,20 @@ class Gnumeric extends BaseReader
|
||||
parent::__construct();
|
||||
$this->referenceHelper = ReferenceHelper::getInstance();
|
||||
$this->securityScanner = XmlScanner::getInstance($this);
|
||||
$limit = ini_get('memory_limit') ?: '128M';
|
||||
$limit = trim(str_replace('-1', '128M', $limit));
|
||||
$unit = strtoupper(substr($limit, -1));
|
||||
$limit = (int) $limit;
|
||||
$multiplier = self::LENGTH_MULTIPLIER[$unit] ?? 1;
|
||||
$limit *= $multiplier;
|
||||
$this->maxLength = intdiv($limit, 4);
|
||||
}
|
||||
|
||||
public function setMaxLength(int $maxLength): self
|
||||
{
|
||||
$this->maxLength = $maxLength;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -177,7 +199,7 @@ class Gnumeric extends BaseReader
|
||||
if (str_starts_with($contents, "\x1f\x8b")) {
|
||||
// Check if gzlib functions are available
|
||||
if (function_exists('gzdecode')) {
|
||||
$contents = @gzdecode($contents);
|
||||
$contents = @gzdecode($contents, $this->maxLength);
|
||||
if ($contents !== false) {
|
||||
$data = $contents;
|
||||
}
|
||||
|
||||
@@ -60,6 +60,9 @@ class OLERead
|
||||
|
||||
private array $props = [];
|
||||
|
||||
/** @var int[] */
|
||||
private array $possibleLoop = [];
|
||||
|
||||
/**
|
||||
* Read the file.
|
||||
*/
|
||||
@@ -136,7 +139,9 @@ class OLERead
|
||||
|
||||
$sbdBlock = $this->sbdStartBlock;
|
||||
$this->smallBlockChain = '';
|
||||
$this->possibleLoop = [];
|
||||
while ($sbdBlock != -2) {
|
||||
$this->catchLoop($sbdBlock);
|
||||
$pos = ($sbdBlock + 1) * self::BIG_BLOCK_SIZE;
|
||||
|
||||
$this->smallBlockChain .= substr($this->data, $pos, 4 * $bbs);
|
||||
@@ -152,6 +157,14 @@ class OLERead
|
||||
$this->readPropertySets();
|
||||
}
|
||||
|
||||
private function catchLoop(int $sbdBlock): void
|
||||
{
|
||||
if (in_array($sbdBlock, $this->possibleLoop, true)) {
|
||||
throw new ReaderException('Detected loop while iterating blocks');
|
||||
}
|
||||
$this->possibleLoop[] = $sbdBlock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract binary stream data.
|
||||
*/
|
||||
@@ -168,7 +181,9 @@ class OLERead
|
||||
|
||||
$block = $this->props[$stream]['startBlock'];
|
||||
|
||||
$this->possibleLoop = [];
|
||||
while ($block != -2) {
|
||||
$this->catchLoop($block);
|
||||
$pos = $block * self::SMALL_BLOCK_SIZE;
|
||||
$streamData .= substr($rootdata, $pos, self::SMALL_BLOCK_SIZE);
|
||||
|
||||
@@ -188,7 +203,9 @@ class OLERead
|
||||
|
||||
$block = $this->props[$stream]['startBlock'];
|
||||
|
||||
$this->possibleLoop = [];
|
||||
while ($block != -2) {
|
||||
$this->catchLoop($block);
|
||||
$pos = ($block + 1) * self::BIG_BLOCK_SIZE;
|
||||
$streamData .= substr($this->data, $pos, self::BIG_BLOCK_SIZE);
|
||||
$block = self::getInt4d($this->bigBlockChain, $block * 4);
|
||||
@@ -208,7 +225,9 @@ class OLERead
|
||||
{
|
||||
$data = '';
|
||||
|
||||
$this->possibleLoop = [];
|
||||
while ($block != -2) {
|
||||
$this->catchLoop($block);
|
||||
$pos = ($block + 1) * self::BIG_BLOCK_SIZE;
|
||||
$data .= substr($this->data, $pos, self::BIG_BLOCK_SIZE);
|
||||
$block = self::getInt4d($this->bigBlockChain, $block * 4);
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheetTests\Calculation\Functions\Web;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Reader\Xlsx as XlsxReader;
|
||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
class ServerTestx extends TestCase
|
||||
{
|
||||
private static Process $httpServer;
|
||||
|
||||
private const SERVER = 'localhost:8080';
|
||||
private const DIRECT = 'http://' . self::SERVER . '/direct.php';
|
||||
private const REDIRECT = 'http://' . self::SERVER . '/redirect.php';
|
||||
private const OLDVALUE = 'LOCAL_REDIRECT_SECRET2';
|
||||
private const NEWVALUE = 'LOCAL_REDIRECT_SECRET3';
|
||||
|
||||
public static function setUpBeforeClass(): void
|
||||
{
|
||||
$commandLine = 'php -S ' . self::SERVER . ' -t ' . __DIR__;
|
||||
|
||||
self::$httpServer = Process::fromShellCommandline($commandLine);
|
||||
self::$httpServer->start();
|
||||
while (!self::$httpServer->isRunning()) {
|
||||
usleep(1000);
|
||||
}
|
||||
}
|
||||
|
||||
public static function tearDownAfterClass(): void
|
||||
{
|
||||
self::$httpServer->stop();
|
||||
}
|
||||
|
||||
public function testServer(): void
|
||||
{
|
||||
self::assertSame(self::NEWVALUE, file_get_contents(self::DIRECT));
|
||||
self::assertSame(self::NEWVALUE, file_get_contents(self::REDIRECT));
|
||||
}
|
||||
|
||||
public function testReadFile(): void
|
||||
{
|
||||
$reader = new XlsxReader();
|
||||
$spreadsheet = $reader->load(__DIR__ . '/redirect.xlsx');
|
||||
$spreadsheet->setDomainWhiteList(['localhost']);
|
||||
$sheet = $spreadsheet->getActiveSheet();
|
||||
self::assertSame(self::OLDVALUE, $sheet->getCell('A1')->getOldCalculatedValue());
|
||||
self::assertSame(self::OLDVALUE, $sheet->getCell('A2')->getOldCalculatedValue());
|
||||
self::assertSame(self::NEWVALUE, $sheet->getCell('A1')->getCalculatedValue(), 'no redirect so recomputed');
|
||||
self::assertSame(self::OLDVALUE, $sheet->getCell('A2')->getCalculatedValue(), 'redirect so use old computed');
|
||||
}
|
||||
|
||||
public static function testNew(): void
|
||||
{
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$spreadsheet->setDomainWhiteList(['localhost']);
|
||||
$sheet = $spreadsheet->getActiveSheet();
|
||||
$sheet->getCell('G1')->setValue(self::DIRECT);
|
||||
$sheet->getCell('A1')->setValue('=WEBSERVICE(G1)');
|
||||
self::assertSame(self::NEWVALUE, $sheet->getCell('A1')->getCalculatedValue(), 'no redirect so computed');
|
||||
$sheet->getCell('G2')->setValue(self::REDIRECT);
|
||||
$sheet->getCell('A2')->setValue('=WEBSERVICE(G2)');
|
||||
self::assertNull(
|
||||
$sheet->getCell('A2')->getCalculatedValue(),
|
||||
'redirect so not computed'
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<?php
|
||||
|
||||
header('Content-Type: text/plain');
|
||||
|
||||
echo 'LOCAL_REDIRECT_SECRET3';
|
||||
@@ -0,0 +1,3 @@
|
||||
<?php
|
||||
|
||||
header('Location: http://localhost:8080/direct.php', true, 302);
|
||||
Binary file not shown.
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheetTests\Reader\Gnumeric;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException;
|
||||
use PhpOffice\PhpSpreadsheet\Reader\Gnumeric;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class LargeDecompressionTest extends TestCase
|
||||
{
|
||||
private const FILENAME = 'tests/data/Reader/Gnumeric/gzbomb.gnumeric';
|
||||
|
||||
public function testEnoughMemoryCanRead(): void
|
||||
{
|
||||
$reader = new Gnumeric();
|
||||
self::assertTrue($reader->canRead(self::FILENAME));
|
||||
}
|
||||
|
||||
public function testNotEnoughMemoryCanRead(): void
|
||||
{
|
||||
$reader = new Gnumeric();
|
||||
$reader->setMaxLength(64 * 1024 * 1024);
|
||||
self::assertFalse($reader->canRead(self::FILENAME));
|
||||
}
|
||||
|
||||
public function testNotEnoughMemoryListNames(): void
|
||||
{
|
||||
$this->expectException(ReaderException::class);
|
||||
$this->expectExceptionMessage('invalid Gnumeric file');
|
||||
$reader = new Gnumeric();
|
||||
$reader->setMaxLength(64 * 1024 * 1024);
|
||||
$reader->listWorksheetNames(self::FILENAME);
|
||||
}
|
||||
|
||||
public function testNotEnoughMemoryListInfo(): void
|
||||
{
|
||||
$this->expectException(ReaderException::class);
|
||||
$this->expectExceptionMessage('invalid Gnumeric file');
|
||||
$reader = new Gnumeric();
|
||||
$reader->setMaxLength(64 * 1024 * 1024);
|
||||
$reader->listWorksheetInfo(self::FILENAME);
|
||||
}
|
||||
|
||||
public function testNotEnoughMemoryLoad(): void
|
||||
{
|
||||
$this->expectException(ReaderException::class);
|
||||
$this->expectExceptionMessage('invalid Gnumeric file');
|
||||
$reader = new Gnumeric();
|
||||
$reader->setMaxLength(64 * 1024 * 1024);
|
||||
$reader->load(self::FILENAME);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheetTests\Reader\Xls;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException;
|
||||
use PhpOffice\PhpSpreadsheet\Reader\Xls as XlsReader;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class OleLoopTest extends TestCase
|
||||
{
|
||||
private const FILENAME = 'tests/data/Reader/XLS/oleloop.xls';
|
||||
|
||||
public function testDetectLoopCanRead(): void
|
||||
{
|
||||
$reader = new XlsReader();
|
||||
self::assertFalse($reader->canRead(self::FILENAME));
|
||||
}
|
||||
|
||||
public function testDetectLoopListNames(): void
|
||||
{
|
||||
$this->expectException(ReaderException::class);
|
||||
$this->expectExceptionMessage('Detected loop');
|
||||
$reader = new XlsReader();
|
||||
$reader->listWorksheetNames(self::FILENAME);
|
||||
}
|
||||
|
||||
public function testDetectLoopListInfo(): void
|
||||
{
|
||||
$this->expectException(ReaderException::class);
|
||||
$this->expectExceptionMessage('Detected loop');
|
||||
$reader = new XlsReader();
|
||||
$reader->listWorksheetInfo(self::FILENAME);
|
||||
}
|
||||
|
||||
public function testDetectLoopLoad(): void
|
||||
{
|
||||
$this->expectException(ReaderException::class);
|
||||
$this->expectExceptionMessage('Detected loop');
|
||||
$reader = new XlsReader();
|
||||
$reader->load(self::FILENAME);
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user