mirror of
https://github.com/PHPOffice/PhpSpreadsheet.git
synced 2026-09-27 03:15:21 +00:00
Merge commit from fork
* Security Patch * Minor Improvement to `unentity` * More Unentity Tweaks
This commit is contained in:
@@ -56,6 +56,11 @@ class XmlScanner
|
||||
throw new Reader\Exception('UTF-7 encoding not permitted');
|
||||
}
|
||||
if (substr($xml, 0, Reader\Csv::UTF8_BOM_LEN) === Reader\Csv::UTF8_BOM) {
|
||||
if (preg_match(self::ENCODING_PATTERN, $xml, $matches) === 1) {
|
||||
if (strtolower($matches[2]) !== 'utf-8') {
|
||||
throw new Reader\Exception("BOM says UTF-8 but encoding says {$matches[2]}");
|
||||
}
|
||||
}
|
||||
$xml = substr($xml, Reader\Csv::UTF8_BOM_LEN);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Reader;
|
||||
|
||||
use Composer\Pcre\Preg;
|
||||
use DateTime;
|
||||
use DateTimeZone;
|
||||
use PhpOffice\PhpSpreadsheet\Cell\AddressHelper;
|
||||
@@ -53,9 +54,14 @@ class Xml extends BaseReader
|
||||
|
||||
public static function unentity(string $contents): string
|
||||
{
|
||||
$contents = preg_replace('/&(amp|lt|gt|quot|apos);/', "\u{fffe}\u{feff}\$1;", trim($contents)) ?? $contents;
|
||||
// fffe is invalid, replace with replacement char
|
||||
$contents = str_replace("\u{fffe}", "\u{fffd}", $contents);
|
||||
// use positive lookahead to "protect" valid xml entities
|
||||
$contents = Preg::replace('/&(?=(?:amp|lt|gt|quot|apos|#[0-9]+|#x[0-9a-fA-F]+);)/', "\u{fffe}", trim($contents));
|
||||
// now decode remaining html entities
|
||||
$contents = html_entity_decode($contents, ENT_NOQUOTES | ENT_SUBSTITUTE | ENT_HTML401, 'UTF-8');
|
||||
$contents = str_replace("\u{fffe}\u{feff}", '&', $contents);
|
||||
// Escape remaining ampersands, restore those which were replaced with fffe
|
||||
$contents = str_replace(['&', "\u{fffe}"], ['&', '&'], $contents);
|
||||
|
||||
return $contents;
|
||||
}
|
||||
@@ -651,7 +657,7 @@ class Xml extends BaseReader
|
||||
}
|
||||
$rangeCalculated = false;
|
||||
if (isset($xmlX->WorksheetOptions->Panes->Pane->RangeSelection)) {
|
||||
if (1 === preg_match('/^R(\d+)C(\d+):R(\d+)C(\d+)$/', (string) $xmlX->WorksheetOptions->Panes->Pane->RangeSelection, $selectionMatches)) {
|
||||
if (Preg::isMatch('/^R(\d+)C(\d+):R(\d+)C(\d+)$/', (string) $xmlX->WorksheetOptions->Panes->Pane->RangeSelection, $selectionMatches)) {
|
||||
$selectedCell = Coordinate::stringFromColumnIndex((int) $selectionMatches[2])
|
||||
. $selectionMatches[1]
|
||||
. ':'
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheetTests\Reader\Xlsx;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException;
|
||||
use PhpOffice\PhpSpreadsheet\Reader\Xlsx as XlsxReader;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class Sec973cTest extends TestCase
|
||||
{
|
||||
public function test937c(): void
|
||||
{
|
||||
$this->expectException(ReaderException::class);
|
||||
$this->expectExceptionMessage('BOM says UTF-8 but encoding says ISO-2022-JP');
|
||||
$reader = new XlsxReader();
|
||||
$reader->load('tests/data/Reader/XLSX/sec937c.xlsx');
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ use PHPUnit\Framework\TestCase;
|
||||
|
||||
class HtmlEntitiesLoadTest extends TestCase
|
||||
{
|
||||
public static function testIssue2157(): void
|
||||
public function testIssue2157(): void
|
||||
{
|
||||
$infile = 'tests/data/Reader/Xml/issue.2157.small.xml';
|
||||
$contents = (string) file_get_contents($infile);
|
||||
@@ -26,4 +26,12 @@ class HtmlEntitiesLoadTest extends TestCase
|
||||
self::assertStringContainsString('</br>', $g2);
|
||||
$spreadsheet->disconnectWorksheets();
|
||||
}
|
||||
|
||||
public function testUnknownEntities(): void
|
||||
{
|
||||
$string = '& &Amp; Τ  㼒 g12; & &*3 <';
|
||||
$expected = '& &Amp; Τ  㼒 &#x3g12; & &*3 <';
|
||||
$result = XmlReader::unentity($string);
|
||||
self::assertSame($expected, $result);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,132 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheetTests\Reader\Xml;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Reader\Exception as ReaderException;
|
||||
use PhpOffice\PhpSpreadsheet\Reader\Security\XmlScanner;
|
||||
use PhpOffice\PhpSpreadsheet\Reader\Xml as XmlReader;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\File;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class Sec25mgTest extends TestCase
|
||||
{
|
||||
private string $filename = '';
|
||||
|
||||
private string $xmlns = XmlReader::NAMESPACES_SS;
|
||||
|
||||
private string $xmlnsss = XmlReader::NAMESPACES_SS;
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
if ($this->filename !== '') {
|
||||
unlink($this->filename);
|
||||
}
|
||||
}
|
||||
|
||||
public function testNumericEntities(): void
|
||||
{
|
||||
$this->filename = File::temporaryFilename();
|
||||
$entity_value = str_repeat('A', 100000);
|
||||
$refs = str_repeat('&big;', 200);
|
||||
|
||||
$xml = <<<EOF
|
||||
<?xml version="1.0"?>
|
||||
<?mso-application progid="Excel.Sheet"?>
|
||||
&#60;!DOCTYPE Workbook [
|
||||
&#60;!ENTITY big "$entity_value">
|
||||
]>
|
||||
<Workbook
|
||||
xmlns="{$this->xmlns}"
|
||||
xmlns:ss="{$this->xmlnsss}"
|
||||
>
|
||||
<Worksheet ss:Name="Sheet1">
|
||||
<Table><Row><Cell>
|
||||
<Data ss:Type="String">$refs</Data>
|
||||
</Cell></Row></Table>
|
||||
</Worksheet>
|
||||
</Workbook>
|
||||
EOF;
|
||||
self::assertNotFalse(
|
||||
file_put_contents($this->filename, $xml)
|
||||
);
|
||||
$this->expectException(ReaderException::class);
|
||||
$this->expectExceptionMessage('Cannot load invalid XML file');
|
||||
$reader = new XmlReader();
|
||||
$reader->load($this->filename);
|
||||
}
|
||||
|
||||
public function testDetectDoctype(): void
|
||||
{
|
||||
$this->filename = File::temporaryFilename();
|
||||
$entity_value = str_repeat('A', 100000);
|
||||
$refs = str_repeat('&big;', 200);
|
||||
|
||||
$xml = <<<EOF
|
||||
<?xml version="1.0"?>
|
||||
<?mso-application progid="Excel.Sheet"?>
|
||||
<!DOCTYPE Workbook [
|
||||
&#60;!ENTITY big "$entity_value">
|
||||
]>
|
||||
<Workbook
|
||||
xmlns="{$this->xmlns}"
|
||||
xmlns:ss="{$this->xmlnsss}"
|
||||
>
|
||||
<Worksheet ss:Name="Sheet1">
|
||||
<Table><Row><Cell>
|
||||
<Data ss:Type="String">$refs</Data>
|
||||
</Cell></Row></Table>
|
||||
</Worksheet>
|
||||
</Workbook>
|
||||
EOF;
|
||||
self::assertNotFalse(
|
||||
file_put_contents($this->filename, $xml)
|
||||
);
|
||||
$this->expectException(ReaderException::class);
|
||||
$this->expectExceptionMessage('Detected use of ENTITY');
|
||||
$reader = new XmlReader();
|
||||
$reader->load($this->filename);
|
||||
}
|
||||
|
||||
public function testNoDoctype(): void
|
||||
{
|
||||
$this->filename = File::temporaryFilename();
|
||||
$refs = str_repeat('&πϏ0', 200);
|
||||
$refsOut = str_repeat('&πϏ0', 200);
|
||||
|
||||
$xml = <<<EOF
|
||||
<?xml version="1.0"?>
|
||||
<?mso-application progid="Excel.Sheet"?>
|
||||
<Workbook
|
||||
xmlns="{$this->xmlns}"
|
||||
xmlns:ss="{$this->xmlnsss}"
|
||||
>
|
||||
<Worksheet ss:Name="Sheet1">
|
||||
<Table><Row><Cell>
|
||||
<Data ss:Type="String">$refs</Data>
|
||||
</Cell></Row></Table>
|
||||
</Worksheet>
|
||||
</Workbook>
|
||||
EOF;
|
||||
self::assertNotFalse(
|
||||
file_put_contents($this->filename, $xml)
|
||||
);
|
||||
$reader = new XmlReader();
|
||||
$spreadsheet = $reader->load($this->filename);
|
||||
$sheet = $spreadsheet->getActiveSheet();
|
||||
self::assertSame($refsOut, $sheet->getCell('A1')->getValue());
|
||||
$spreadsheet->disconnectWorksheets();
|
||||
}
|
||||
|
||||
public function testIsolated(): void
|
||||
{
|
||||
$scanner = new XmlScanner('<!DOCTYPE');
|
||||
$scanner->setAdditionalCallback([XmlReader::class, 'unentity']);
|
||||
$input = '&#60;!DOCTYPE test';
|
||||
$pass1 = $scanner->scan($input);
|
||||
self::assertStringNotContainsString('<!DOCTYPE', $pass1);
|
||||
$pass2 = $scanner->scan($pass1);
|
||||
self::assertStringNotContainsString('<!DOCTYPE', $pass2);
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Reference in New Issue
Block a user