mirror of
https://github.com/PHPOffice/PhpSpreadsheet.git
synced 2026-08-30 20:18:00 +00:00
Backport Security Patch
This commit is contained in:
@@ -287,7 +287,7 @@ parameters:
|
||||
|
||||
-
|
||||
message: "#^Offset 'mime' does not exist on array\\{\\}\\|array\\{0\\: int\\<0, max\\>, 1\\: int\\<0, max\\>, 2\\: int, 3\\: string, mime\\: string, channels\\?\\: int, bits\\?\\: int\\}\\.$#"
|
||||
count: 2
|
||||
count: 1
|
||||
path: src/PhpSpreadsheet/Writer/Html.php
|
||||
|
||||
-
|
||||
|
||||
@@ -1291,7 +1291,7 @@ class Xlsx extends BaseReader
|
||||
$hfImages[$shapeId]->setName((string) $imageData['title']);
|
||||
}
|
||||
|
||||
$hfImages[$shapeId]->setPath('zip://' . File::realpath($filename) . '#' . $drawings[(string) $imageData['relid']], false);
|
||||
$hfImages[$shapeId]->setPath('zip://' . File::realpath($filename) . '#' . $drawings[(string) $imageData['relid']], false, $zip);
|
||||
$hfImages[$shapeId]->setResizeProportional(false);
|
||||
$hfImages[$shapeId]->setWidth($style['width']);
|
||||
$hfImages[$shapeId]->setHeight($style['height']);
|
||||
@@ -1401,7 +1401,8 @@ class Xlsx extends BaseReader
|
||||
$objDrawing->setPath(
|
||||
'zip://' . File::realpath($filename) . '#' .
|
||||
$images[$embedImageKey],
|
||||
false
|
||||
false,
|
||||
$zip
|
||||
);
|
||||
} else {
|
||||
$linkImageKey = (string) self::getArrayItem(
|
||||
@@ -1412,6 +1413,9 @@ class Xlsx extends BaseReader
|
||||
$url = str_replace('xl/drawings/', '', $images[$linkImageKey]);
|
||||
$objDrawing->setPath($url);
|
||||
}
|
||||
if ($objDrawing->getPath() === '') {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
$objDrawing->setCoordinates(Coordinate::stringFromColumnIndex(((int) $oneCellAnchor->from->col) + 1) . ($oneCellAnchor->from->row + 1));
|
||||
|
||||
@@ -1486,7 +1490,8 @@ class Xlsx extends BaseReader
|
||||
$objDrawing->setPath(
|
||||
'zip://' . File::realpath($filename) . '#' .
|
||||
$images[$embedImageKey],
|
||||
false
|
||||
false,
|
||||
$zip
|
||||
);
|
||||
} else {
|
||||
$linkImageKey = (string) self::getArrayItem(
|
||||
@@ -1497,6 +1502,9 @@ class Xlsx extends BaseReader
|
||||
$url = str_replace('xl/drawings/', '', $images[$linkImageKey]);
|
||||
$objDrawing->setPath($url);
|
||||
}
|
||||
if ($objDrawing->getPath() === '') {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
$objDrawing->setCoordinates(Coordinate::stringFromColumnIndex(((int) $twoCellAnchor->from->col) + 1) . ($twoCellAnchor->from->row + 1));
|
||||
|
||||
|
||||
@@ -220,7 +220,7 @@ class BaseDrawing implements IComparable
|
||||
{
|
||||
if ($this->worksheet === null) {
|
||||
// Add drawing to \PhpOffice\PhpSpreadsheet\Worksheet\Worksheet
|
||||
if ($worksheet !== null) {
|
||||
if ($worksheet !== null && !($this instanceof Drawing && $this->getPath() === '')) {
|
||||
$this->worksheet = $worksheet;
|
||||
$this->worksheet->getCell($this->coordinates);
|
||||
$this->worksheet->getDrawingCollection()->append($this);
|
||||
|
||||
@@ -106,40 +106,72 @@ class Drawing extends BaseDrawing
|
||||
*/
|
||||
public function setPath($path, $verifyFile = true, $zip = null)
|
||||
{
|
||||
if ($verifyFile && preg_match('~^data:image/[a-z]+;base64,~', $path) !== 1) {
|
||||
// Check if a URL has been passed. https://stackoverflow.com/a/2058596/1252979
|
||||
if (filter_var($path, FILTER_VALIDATE_URL)) {
|
||||
$this->path = $path;
|
||||
// Implicit that it is a URL, rather store info than running check above on value in other places.
|
||||
$this->isUrl = true;
|
||||
$imageContents = file_get_contents($path);
|
||||
$this->isUrl = false;
|
||||
if (preg_match('~^data:image/[a-z]+;base64,~', $path) === 1) {
|
||||
$this->path = $path;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
$this->path = '';
|
||||
// Check if a URL has been passed. https://stackoverflow.com/a/2058596/1252979
|
||||
if (filter_var($path, FILTER_VALIDATE_URL)) {
|
||||
if (!preg_match('/^(http|https|file|ftp|s3):/', $path)) {
|
||||
throw new PhpSpreadsheetException('Invalid protocol for linked drawing');
|
||||
}
|
||||
// Implicit that it is a URL, rather store info than running check above on value in other places.
|
||||
$this->isUrl = true;
|
||||
$imageContents = @file_get_contents($path);
|
||||
if ($imageContents !== false) {
|
||||
$filePath = tempnam(sys_get_temp_dir(), 'Drawing');
|
||||
if ($filePath) {
|
||||
file_put_contents($filePath, $imageContents);
|
||||
if (file_exists($filePath)) {
|
||||
$this->setSizesAndType($filePath);
|
||||
$put = @file_put_contents($filePath, $imageContents);
|
||||
if ($put !== false) {
|
||||
if ($this->isImage($filePath)) {
|
||||
$this->path = $path;
|
||||
$this->setSizesAndType($filePath);
|
||||
}
|
||||
unlink($filePath);
|
||||
}
|
||||
}
|
||||
} elseif (file_exists($path)) {
|
||||
$this->path = $path;
|
||||
$this->setSizesAndType($path);
|
||||
} elseif ($zip instanceof ZipArchive) {
|
||||
$zipPath = explode('#', $path)[1];
|
||||
if ($zip->locateName($zipPath) !== false) {
|
||||
}
|
||||
} elseif ($zip instanceof ZipArchive) {
|
||||
$zipPath = explode('#', $path)[1];
|
||||
$locate = @$zip->locateName($zipPath);
|
||||
if ($locate !== false) {
|
||||
if ($this->isImage($path)) {
|
||||
$this->path = $path;
|
||||
$this->setSizesAndType($path);
|
||||
}
|
||||
} else {
|
||||
throw new PhpSpreadsheetException("File $path not found!");
|
||||
}
|
||||
} else {
|
||||
$this->path = $path;
|
||||
$exists = @file_exists($path);
|
||||
if ($exists !== false && $this->isImage($path)) {
|
||||
$this->path = $path;
|
||||
$this->setSizesAndType($path);
|
||||
}
|
||||
}
|
||||
if ($this->path === '' && $verifyFile) {
|
||||
throw new PhpSpreadsheetException("File $path not found!");
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function isImage(string $path): bool
|
||||
{
|
||||
$mime = (string) @mime_content_type($path);
|
||||
$retVal = false;
|
||||
if (str_starts_with($mime, 'image/')) {
|
||||
$retVal = true;
|
||||
} elseif ($mime === 'application/octet-stream') {
|
||||
$extension = pathinfo($path, PATHINFO_EXTENSION);
|
||||
$retVal = in_array($extension, ['bin', 'emf'], true);
|
||||
}
|
||||
|
||||
return $retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get isURL.
|
||||
*/
|
||||
|
||||
@@ -612,6 +612,9 @@ class Html extends BaseWriter
|
||||
[$rowMax, $colMax, $anyfound] = $this->extendRowsForCharts($worksheet, $row);
|
||||
|
||||
foreach ($worksheet->getDrawingCollection() as $drawing) {
|
||||
if ($drawing instanceof Drawing && $drawing->getPath() === '') {
|
||||
continue;
|
||||
}
|
||||
$anyfound = true;
|
||||
$imageTL = Coordinate::coordinateFromString($drawing->getCoordinates());
|
||||
$imageCol = Coordinate::columnIndexFromString($imageTL[0]);
|
||||
@@ -687,7 +690,7 @@ class Html extends BaseWriter
|
||||
}
|
||||
$filedesc = $drawing->getDescription();
|
||||
$filedesc = $filedesc ? htmlspecialchars($filedesc, ENT_QUOTES) : 'Embedded image';
|
||||
if ($drawing instanceof Drawing) {
|
||||
if ($drawing instanceof Drawing && $drawing->getPath() !== '') {
|
||||
$filename = $drawing->getPath();
|
||||
|
||||
// Strip off eventual '.'
|
||||
@@ -706,12 +709,15 @@ class Html extends BaseWriter
|
||||
$imageData = self::winFileToUrl($filename, $this->isMPdf);
|
||||
|
||||
if ($this->embedImages || substr($imageData, 0, 6) === 'zip://') {
|
||||
$imageData = 'data:,';
|
||||
$picture = @file_get_contents($filename);
|
||||
if ($picture !== false) {
|
||||
$imageDetails = getimagesize($filename) ?: [];
|
||||
// base64 encode the binary data
|
||||
$base64 = base64_encode($picture);
|
||||
$imageData = 'data:' . $imageDetails['mime'] . ';base64,' . $base64;
|
||||
$mimeContentType = (string) @mime_content_type($filename);
|
||||
if (substr($mimeContentType, 0, 6) === 'image/') {
|
||||
// base64 encode the binary data
|
||||
$base64 = base64_encode($picture);
|
||||
$imageData = 'data:' . $mimeContentType . ';base64,' . $base64;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -486,7 +486,7 @@ class Xls extends BaseWriter
|
||||
|
||||
private function processBaseDrawing(BstoreContainer &$bstoreContainer, BaseDrawing $drawing): void
|
||||
{
|
||||
if ($drawing instanceof Drawing) {
|
||||
if ($drawing instanceof Drawing && $drawing->getPath() !== '') {
|
||||
$this->processDrawing($bstoreContainer, $drawing);
|
||||
} elseif ($drawing instanceof MemoryDrawing) {
|
||||
$this->processMemoryDrawing($bstoreContainer, $drawing, $drawing->getRenderingFunction());
|
||||
|
||||
@@ -495,7 +495,9 @@ class Xlsx extends BaseWriter
|
||||
|
||||
// Media
|
||||
foreach ($this->spreadSheet->getSheet($i)->getHeaderFooter()->getImages() as $image) {
|
||||
$zipContent['xl/media/' . $image->getIndexedFilename()] = file_get_contents($image->getPath());
|
||||
if ($image->getPath() !== '') {
|
||||
$zipContent['xl/media/' . $image->getIndexedFilename()] = file_get_contents($image->getPath());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -511,6 +513,9 @@ class Xlsx extends BaseWriter
|
||||
if ($this->getDrawingHashTable()->getByIndex($i) instanceof WorksheetDrawing) {
|
||||
$imageContents = null;
|
||||
$imagePath = $this->getDrawingHashTable()->getByIndex($i)->getPath();
|
||||
if ($imagePath === '') {
|
||||
continue;
|
||||
}
|
||||
if (strpos($imagePath, 'zip://') !== false) {
|
||||
$imagePath = substr($imagePath, 6);
|
||||
$imagePathSplitted = explode('#', $imagePath);
|
||||
@@ -712,6 +717,9 @@ class Xlsx extends BaseWriter
|
||||
{
|
||||
$data = null;
|
||||
$filename = $drawing->getPath();
|
||||
if ($filename === '') {
|
||||
return null;
|
||||
}
|
||||
$imageData = getimagesize($filename);
|
||||
|
||||
if (!empty($imageData)) {
|
||||
|
||||
@@ -6,6 +6,7 @@ use PhpOffice\PhpSpreadsheet\Reader\Xlsx\Namespaces;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\File;
|
||||
use PhpOffice\PhpSpreadsheet\Shared\XMLWriter;
|
||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Drawing as WorksheetDrawing;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\MemoryDrawing;
|
||||
use PhpOffice\PhpSpreadsheet\Writer\Exception as WriterException;
|
||||
|
||||
@@ -132,18 +133,23 @@ class ContentTypes extends WriterPart
|
||||
$extension = '';
|
||||
$mimeType = '';
|
||||
|
||||
if ($this->getParentWriter()->getDrawingHashTable()->getByIndex($i) instanceof \PhpOffice\PhpSpreadsheet\Worksheet\Drawing) {
|
||||
$extension = strtolower($this->getParentWriter()->getDrawingHashTable()->getByIndex($i)->getExtension());
|
||||
$mimeType = $this->getImageMimeType($this->getParentWriter()->getDrawingHashTable()->getByIndex($i)->getPath());
|
||||
} elseif ($this->getParentWriter()->getDrawingHashTable()->getByIndex($i) instanceof MemoryDrawing) {
|
||||
$extension = strtolower($this->getParentWriter()->getDrawingHashTable()->getByIndex($i)->getMimeType());
|
||||
$drawing = $this->getParentWriter()->getDrawingHashTable()->getByIndex($i);
|
||||
if ($drawing instanceof WorksheetDrawing && $drawing->getPath() !== '') {
|
||||
$extension = strtolower($drawing->getExtension());
|
||||
if ($drawing->getIsUrl()) {
|
||||
$mimeType = image_type_to_mime_type($drawing->getType());
|
||||
} else {
|
||||
$mimeType = $this->getImageMimeType($drawing->getPath());
|
||||
}
|
||||
} elseif ($drawing instanceof MemoryDrawing) {
|
||||
$extension = strtolower($drawing->getMimeType());
|
||||
$extension = explode('/', $extension);
|
||||
$extension = $extension[1];
|
||||
|
||||
$mimeType = $this->getParentWriter()->getDrawingHashTable()->getByIndex($i)->getMimeType();
|
||||
$mimeType = $drawing->getMimeType();
|
||||
}
|
||||
|
||||
if (!isset($aMediaContentTypes[$extension])) {
|
||||
if ($mimeType !== '' && !isset($aMediaContentTypes[$extension])) {
|
||||
$aMediaContentTypes[$extension] = $mimeType;
|
||||
|
||||
$this->writeDefaultContentType($objWriter, $extension, $mimeType);
|
||||
@@ -162,7 +168,7 @@ class ContentTypes extends WriterPart
|
||||
for ($i = 0; $i < $sheetCount; ++$i) {
|
||||
if (count($spreadsheet->getSheet($i)->getHeaderFooter()->getImages()) > 0) {
|
||||
foreach ($spreadsheet->getSheet($i)->getHeaderFooter()->getImages() as $image) {
|
||||
if (!isset($aMediaContentTypes[strtolower($image->getExtension())])) {
|
||||
if ($image->getPath() !== '' && !isset($aMediaContentTypes[strtolower($image->getExtension())])) {
|
||||
$aMediaContentTypes[strtolower($image->getExtension())] = $this->getImageMimeType($image->getPath());
|
||||
|
||||
$this->writeDefaultContentType($objWriter, strtolower($image->getExtension()), $aMediaContentTypes[strtolower($image->getExtension())]);
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheetTests\Reader\Xlsx;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Exception as SpreadsheetException;
|
||||
use PhpOffice\PhpSpreadsheet\IOFactory;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Drawing;
|
||||
use PhpOffice\PhpSpreadsheetTests\Reader\Utility\File;
|
||||
@@ -9,7 +10,8 @@ use PHPUnit\Framework\TestCase;
|
||||
|
||||
class URLImageTest extends TestCase
|
||||
{
|
||||
public function testURLImageSource(): void
|
||||
// https://github.com/readthedocs/readthedocs.org/issues/11615
|
||||
public function xtestURLImageSource(): void
|
||||
{
|
||||
if (getenv('SKIP_URL_IMAGE_TEST') === '1') {
|
||||
self::markTestSkipped('Skipped due to setting of environment variable');
|
||||
@@ -39,4 +41,28 @@ class URLImageTest extends TestCase
|
||||
self::assertSame('png', $extension);
|
||||
}
|
||||
}
|
||||
|
||||
public function xtestURLImageSourceNotFound(): void
|
||||
{
|
||||
if (getenv('SKIP_URL_IMAGE_TEST') === '1') {
|
||||
self::markTestSkipped('Skipped due to setting of environment variable');
|
||||
}
|
||||
$filename = realpath(__DIR__ . '/../../../data/Reader/XLSX/urlImage.notfound.xlsx');
|
||||
self::assertNotFalse($filename);
|
||||
$reader = IOFactory::createReader('Xlsx');
|
||||
$spreadsheet = $reader->load($filename);
|
||||
$worksheet = $spreadsheet->getActiveSheet();
|
||||
$collection = $worksheet->getDrawingCollection();
|
||||
self::assertCount(0, $collection);
|
||||
}
|
||||
|
||||
public function testURLImageSourceBadProtocol(): void
|
||||
{
|
||||
$filename = realpath(__DIR__ . '/../../../data/Reader/XLSX/urlImage.bad.dontuse');
|
||||
self::assertNotFalse($filename);
|
||||
$this->expectException(SpreadsheetException::class);
|
||||
$this->expectExceptionMessage('Invalid protocol for linked drawing');
|
||||
$reader = IOFactory::createReader('Xlsx');
|
||||
$reader->load($filename);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheetTests\Writer\Html;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
|
||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Drawing;
|
||||
use PhpOffice\PhpSpreadsheet\Writer\Html;
|
||||
@@ -57,7 +58,9 @@ class ExtendForChartsAndImagesTest extends Functional\AbstractFunctional
|
||||
|
||||
// Add a drawing to the worksheet
|
||||
$drawing = new Drawing();
|
||||
$drawing->setPath('foo.png', false);
|
||||
$path = 'tests/data/Writer/XLSX/blue_square.png';
|
||||
$drawing->setPath($path);
|
||||
self::assertSame($path, $drawing->getPath());
|
||||
$drawing->setCoordinates('A5');
|
||||
$drawing->setWorksheet($sheet);
|
||||
|
||||
@@ -72,13 +75,51 @@ class ExtendForChartsAndImagesTest extends Functional\AbstractFunctional
|
||||
|
||||
// Add a drawing to the worksheet
|
||||
$drawing = new Drawing();
|
||||
$drawing->setPath('foo.png', false);
|
||||
$path = 'tests/data/Writer/XLSX/blue_square.png';
|
||||
$drawing->setPath($path);
|
||||
self::assertSame($path, $drawing->getPath());
|
||||
$drawing->setCoordinates('E1');
|
||||
$drawing->setWorksheet($sheet);
|
||||
|
||||
$this->assertMaxColumnAndMaxRow($spreadsheet, 5, 3);
|
||||
}
|
||||
|
||||
public function testSheetWithBadImageRightOfData(): void
|
||||
{
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$sheet = $spreadsheet->getActiveSheet();
|
||||
$sheet->setCellValue('B3', 'foo');
|
||||
|
||||
// Add a drawing to the worksheet
|
||||
$drawing = new Drawing();
|
||||
$path = 'tests/data/Writer/XLSX/xblue_square.png';
|
||||
$drawing->setPath($path, false);
|
||||
self::assertSame('', $drawing->getPath());
|
||||
$drawing->setCoordinates('E1');
|
||||
$drawing->setWorksheet($sheet);
|
||||
|
||||
$this->assertMaxColumnAndMaxRow($spreadsheet, 2, 3);
|
||||
}
|
||||
|
||||
public function testSheetWithBadImageRightOfDataThrow(): void
|
||||
{
|
||||
$this->expectException(PhpSpreadsheetException::class);
|
||||
$this->expectExceptionMessage('not found!');
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$sheet = $spreadsheet->getActiveSheet();
|
||||
$sheet->setCellValue('B3', 'foo');
|
||||
|
||||
// Add a drawing to the worksheet
|
||||
$drawing = new Drawing();
|
||||
$path = 'tests/data/Writer/XLSX/xblue_square.png';
|
||||
$drawing->setPath($path);
|
||||
self::assertSame('', $drawing->getPath());
|
||||
$drawing->setCoordinates('E1');
|
||||
$drawing->setWorksheet($sheet);
|
||||
|
||||
$this->assertMaxColumnAndMaxRow($spreadsheet, 2, 3);
|
||||
}
|
||||
|
||||
private function assertMaxColumnAndMaxRow(Spreadsheet $spreadsheet, int $expectedColumnCount, int $expectedRowCount): void
|
||||
{
|
||||
$writer = new Html($spreadsheet);
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheetTests\Writer\Html;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Spreadsheet;
|
||||
use PhpOffice\PhpSpreadsheet\Worksheet\Drawing;
|
||||
use PhpOffice\PhpSpreadsheet\Writer\Html as HtmlWriter;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class ImageEmbedTest extends TestCase
|
||||
{
|
||||
public function testImageEmbed(): void
|
||||
{
|
||||
$spreadsheet = new Spreadsheet();
|
||||
$sheet = $spreadsheet->getActiveSheet();
|
||||
|
||||
$drawing = new Drawing();
|
||||
$drawing->setName('Not an image');
|
||||
$drawing->setDescription('Non-image');
|
||||
$drawing->setPath(__FILE__, false);
|
||||
$drawing->setCoordinates('A1');
|
||||
$drawing->setCoordinates2('E4');
|
||||
$drawing->setWorksheet($sheet);
|
||||
|
||||
$drawing = new Drawing();
|
||||
$drawing->setName('Blue Square');
|
||||
$drawing->setPath('tests/data/Writer/XLSX/blue_square.png');
|
||||
$drawing->setCoordinates('A5');
|
||||
$drawing->setCoordinates2('E8');
|
||||
$drawing->setWorksheet($sheet);
|
||||
|
||||
$writer = new HtmlWriter($spreadsheet);
|
||||
$writer->setEmbedImages(true);
|
||||
$html = $writer->generateHTMLAll();
|
||||
self::assertSame(1, substr_count($html, '<img'));
|
||||
self::assertSame(1, substr_count($html, 'src="data'));
|
||||
self::assertSame(1, substr_count($html, 'src="data:image/png;base64,'));
|
||||
self::assertSame(0, substr_count($html, 'blue_square.png'));
|
||||
//self::assertSame(1, substr_count($html, 'src="data:," alt="Non-image"'));
|
||||
|
||||
$spreadsheet->disconnectWorksheets();
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user