Skip to content

Commit 83f2d86

Browse files
committed
Validate Mime Type of Image Files
1 parent 3e7751e commit 83f2d86

File tree

8 files changed

+114
-43
lines changed

8 files changed

+114
-43
lines changed

src/PhpSpreadsheet/Reader/Xlsx.php

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1324,7 +1324,7 @@ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet
13241324
$hfImages[$shapeId]->setName((string) $imageData['title']);
13251325
}
13261326

1327-
$hfImages[$shapeId]->setPath('zip://' . File::realpath($filename) . '#' . $drawings[(string) $imageData['relid']], false);
1327+
$hfImages[$shapeId]->setPath('zip://' . File::realpath($filename) . '#' . $drawings[(string) $imageData['relid']], true, $zip);
13281328
$hfImages[$shapeId]->setResizeProportional(false);
13291329
$hfImages[$shapeId]->setWidth($style['width']);
13301330
$hfImages[$shapeId]->setHeight($style['height']);
@@ -1439,7 +1439,8 @@ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet
14391439
$objDrawing->setPath(
14401440
'zip://' . File::realpath($filename) . '#'
14411441
. $images[$embedImageKey],
1442-
false
1442+
true,
1443+
$zip
14431444
);
14441445
} else {
14451446
$linkImageKey = (string) self::getArrayItem(
@@ -1536,7 +1537,8 @@ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet
15361537
$objDrawing->setPath(
15371538
'zip://' . File::realpath($filename) . '#'
15381539
. $images[$embedImageKey],
1539-
false
1540+
true,
1541+
$zip
15401542
);
15411543
} else {
15421544
$linkImageKey = (string) self::getArrayItem(

src/PhpSpreadsheet/Worksheet/Drawing.php

Lines changed: 42 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -94,48 +94,61 @@ public function getPath(): string
9494
*/
9595
public function setPath(string $path, bool $verifyFile = true, ?ZipArchive $zip = null): static
9696
{
97-
if ($verifyFile && preg_match('~^data:image/[a-z]+;base64,~', $path) !== 1) {
98-
// Check if a URL has been passed. https://stackoverflow.com/a/2058596/1252979
99-
if (filter_var($path, FILTER_VALIDATE_URL)) {
100-
if (!preg_match('/^(http|https|file|ftp|s3):/', $path)) {
101-
throw new PhpSpreadsheetException('Invalid protocol for linked drawing');
102-
}
103-
$this->path = $path;
104-
// Implicit that it is a URL, rather store info than running check above on value in other places.
105-
$this->isUrl = true;
106-
$imageContents = @file_get_contents($path);
107-
if ($imageContents === false) {
108-
$this->path = '';
109-
} else {
110-
$filePath = tempnam(sys_get_temp_dir(), 'Drawing');
111-
if ($filePath) {
112-
file_put_contents($filePath, $imageContents);
113-
if (file_exists($filePath)) {
97+
if (preg_match('~^data:image/[a-z]+;base64,~', $path) === 1) {
98+
$this->path = $path;
99+
100+
return $this;
101+
}
102+
103+
if ($verifyFile === false && !str_starts_with($path, 'zip:')) {
104+
throw new PhpSpreadsheetException('Only zip files can set verifyFile to false');
105+
}
106+
107+
$this->path = '';
108+
// Check if a URL has been passed. https://stackoverflow.com/a/2058596/1252979
109+
if (filter_var($path, FILTER_VALIDATE_URL)) {
110+
if (!preg_match('/^(http|https|file|ftp|s3):/', $path)) {
111+
throw new PhpSpreadsheetException('Invalid protocol for linked drawing');
112+
}
113+
// Implicit that it is a URL, rather store info than running check above on value in other places.
114+
$this->isUrl = true;
115+
$imageContents = @file_get_contents($path);
116+
if ($imageContents !== false) {
117+
$filePath = tempnam(sys_get_temp_dir(), 'Drawing');
118+
if ($filePath) {
119+
file_put_contents($filePath, $imageContents);
120+
if (file_exists($filePath)) {
121+
if ($this->isImage($filePath)) {
122+
$this->path = $path;
114123
$this->setSizesAndType($filePath);
115-
unlink($filePath);
116124
}
125+
unlink($filePath);
117126
}
118127
}
119-
} elseif (file_exists($path)) {
120-
$this->path = $path;
121-
$this->setSizesAndType($path);
122-
} elseif ($zip instanceof ZipArchive) {
123-
$zipPath = explode('#', $path)[1];
124-
if ($zip->locateName($zipPath) !== false) {
128+
}
129+
} elseif ($zip instanceof ZipArchive) {
130+
$zipPath = explode('#', $path)[1];
131+
if ($zip->locateName($zipPath) !== false) {
132+
if ($this->isImage($path)) {
125133
$this->path = $path;
126134
$this->setSizesAndType($path);
127135
}
128-
} else {
129-
//throw new PhpSpreadsheetException("File $path not found!");
130-
$this->path = '';
131136
}
132-
} else {
133-
$this->path = $path;
137+
} elseif (file_exists($path)) {
138+
if ($this->isImage($path)) {
139+
$this->path = $path;
140+
$this->setSizesAndType($path);
141+
}
134142
}
135143

136144
return $this;
137145
}
138146

147+
private function isImage(string $path): bool
148+
{
149+
return str_starts_with((string) mime_content_type($path), 'image/');
150+
}
151+
139152
/**
140153
* Get isURL.
141154
*/

src/PhpSpreadsheet/Writer/Html.php

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -629,7 +629,7 @@ private function writeImageInCell(string $coordinates): string
629629
}
630630
$filedesc = $drawing->getDescription();
631631
$filedesc = $filedesc ? htmlspecialchars($filedesc, ENT_QUOTES) : 'Embedded image';
632-
if ($drawing instanceof Drawing) {
632+
if ($drawing instanceof Drawing && $drawing->getPath() !== '') {
633633
$filename = $drawing->getPath();
634634

635635
// Strip off eventual '.'
@@ -648,12 +648,15 @@ private function writeImageInCell(string $coordinates): string
648648
$imageData = self::winFileToUrl($filename, $this instanceof Pdf\Mpdf);
649649

650650
if ($this->embedImages || str_starts_with($imageData, 'zip://')) {
651+
$imageData = 'data:,';
651652
$picture = @file_get_contents($filename);
652653
if ($picture !== false) {
653654
$imageDetails = getimagesize($filename) ?: ['mime' => ''];
654-
// base64 encode the binary data
655-
$base64 = base64_encode($picture);
656-
$imageData = 'data:' . $imageDetails['mime'] . ';base64,' . $base64;
655+
if (str_starts_with($imageDetails['mime'], 'image/')) {
656+
// base64 encode the binary data
657+
$base64 = base64_encode($picture);
658+
$imageData = 'data:' . $imageDetails['mime'] . ';base64,' . $base64;
659+
}
657660
}
658661
}
659662

src/PhpSpreadsheet/Writer/Xls.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,7 @@ private function processDrawing(BstoreContainer &$bstoreContainer, Drawing $draw
467467

468468
private function processBaseDrawing(BstoreContainer &$bstoreContainer, BaseDrawing $drawing): void
469469
{
470-
if ($drawing instanceof Drawing) {
470+
if ($drawing instanceof Drawing && $drawing->getPath() !== '') {
471471
$this->processDrawing($bstoreContainer, $drawing);
472472
} elseif ($drawing instanceof MemoryDrawing) {
473473
$this->processMemoryDrawing($bstoreContainer, $drawing, $drawing->getRenderingFunction());

src/PhpSpreadsheet/Writer/Xlsx.php

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -455,7 +455,9 @@ public function save($filename, int $flags = 0): void
455455

456456
// Media
457457
foreach ($this->spreadSheet->getSheet($i)->getHeaderFooter()->getImages() as $image) {
458-
$zipContent['xl/media/' . $image->getIndexedFilename()] = file_get_contents($image->getPath());
458+
if ($image->getPath() !== '') {
459+
$zipContent['xl/media/' . $image->getIndexedFilename()] = file_get_contents($image->getPath());
460+
}
459461
}
460462
}
461463

@@ -471,6 +473,9 @@ public function save($filename, int $flags = 0): void
471473
if ($this->getDrawingHashTable()->getByIndex($i) instanceof WorksheetDrawing) {
472474
$imageContents = null;
473475
$imagePath = $this->getDrawingHashTable()->getByIndex($i)->getPath();
476+
if ($imagePath === '') {
477+
continue;
478+
}
474479
if (str_contains($imagePath, 'zip://')) {
475480
$imagePath = substr($imagePath, 6);
476481
$imagePathSplitted = explode('#', $imagePath);
@@ -664,6 +669,9 @@ private function processDrawing(WorksheetDrawing $drawing): string|null|false
664669
{
665670
$data = null;
666671
$filename = $drawing->getPath();
672+
if ($filename === '') {
673+
return null;
674+
}
667675
$imageData = getimagesize($filename);
668676

669677
if (!empty($imageData)) {

src/PhpSpreadsheet/Writer/Xlsx/ContentTypes.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ public function writeContentTypes(Spreadsheet $spreadsheet, bool $includeCharts
134134
$mimeType = '';
135135

136136
$drawing = $this->getParentWriter()->getDrawingHashTable()->getByIndex($i);
137-
if ($drawing instanceof WorksheetDrawing) {
137+
if ($drawing instanceof WorksheetDrawing && $drawing->getPath() !== '') {
138138
$extension = strtolower($drawing->getExtension());
139139
if ($drawing->getIsUrl()) {
140140
$mimeType = image_type_to_mime_type($drawing->getType());
@@ -149,7 +149,7 @@ public function writeContentTypes(Spreadsheet $spreadsheet, bool $includeCharts
149149
$mimeType = $drawing->getMimeType();
150150
}
151151

152-
if (!isset($aMediaContentTypes[$extension])) {
152+
if ($mimeType !== '' && !isset($aMediaContentTypes[$extension])) {
153153
$aMediaContentTypes[$extension] = $mimeType;
154154

155155
$this->writeDefaultContentType($objWriter, $extension, $mimeType);
@@ -168,7 +168,7 @@ public function writeContentTypes(Spreadsheet $spreadsheet, bool $includeCharts
168168
for ($i = 0; $i < $sheetCount; ++$i) {
169169
if (count($spreadsheet->getSheet($i)->getHeaderFooter()->getImages()) > 0) {
170170
foreach ($spreadsheet->getSheet($i)->getHeaderFooter()->getImages() as $image) {
171-
if (!isset($aMediaContentTypes[strtolower($image->getExtension())])) {
171+
if ($image->getPath() !== '' && !isset($aMediaContentTypes[strtolower($image->getExtension())])) {
172172
$aMediaContentTypes[strtolower($image->getExtension())] = $this->getImageMimeType($image->getPath());
173173

174174
$this->writeDefaultContentType($objWriter, strtolower($image->getExtension()), $aMediaContentTypes[strtolower($image->getExtension())]);

tests/PhpSpreadsheetTests/Writer/Html/ExtendForChartsAndImagesTest.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ public function testSheetWithImageBelowData(): void
5959

6060
// Add a drawing to the worksheet
6161
$drawing = new Drawing();
62-
$drawing->setPath('foo.png', false);
62+
$drawing->setPath('tests/data/Writer/XLSX/blue_square.png');
6363
$drawing->setCoordinates('A5');
6464
$drawing->setWorksheet($sheet);
6565

@@ -74,7 +74,7 @@ public function testSheetWithImageRightOfData(): void
7474

7575
// Add a drawing to the worksheet
7676
$drawing = new Drawing();
77-
$drawing->setPath('foo.png', false);
77+
$drawing->setPath('tests/data/Writer/XLSX/blue_square.png');
7878
$drawing->setCoordinates('E1');
7979
$drawing->setWorksheet($sheet);
8080

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpOffice\PhpSpreadsheetTests\Writer\Html;
6+
7+
use PhpOffice\PhpSpreadsheet\Spreadsheet;
8+
use PhpOffice\PhpSpreadsheet\Worksheet\Drawing;
9+
use PhpOffice\PhpSpreadsheet\Writer\Html as HtmlWriter;
10+
use PHPUnit\Framework\TestCase;
11+
12+
class ImageEmbedTest extends TestCase
13+
{
14+
public function testImageEmbed(): void
15+
{
16+
$spreadsheet = new Spreadsheet();
17+
$sheet = $spreadsheet->getActiveSheet();
18+
19+
$drawing = new Drawing();
20+
$drawing->setName('Not an image');
21+
$drawing->setDescription('Non-image');
22+
$drawing->setPath(__FILE__);
23+
$drawing->setCoordinates('A1');
24+
$drawing->setCoordinates2('E4');
25+
$drawing->setWorksheet($sheet);
26+
27+
$drawing = new Drawing();
28+
$drawing->setName('Blue Square');
29+
$drawing->setPath('tests/data/Writer/XLSX/blue_square.png');
30+
$drawing->setCoordinates('A5');
31+
$drawing->setCoordinates2('E8');
32+
$drawing->setWorksheet($sheet);
33+
34+
$writer = new HtmlWriter($spreadsheet);
35+
$writer->setEmbedImages(true);
36+
$html = $writer->generateHTMLAll();
37+
self::assertSame(1, substr_count($html, '<img'));
38+
self::assertSame(1, substr_count($html, 'src="data'));
39+
self::assertSame(1, substr_count($html, 'src="data:image/png;base64,'));
40+
self::assertSame(0, substr_count($html, 'blue_square.png'));
41+
//self::assertSame(1, substr_count($html, 'src="data:," alt="Non-image"'));
42+
43+
$spreadsheet->disconnectWorksheets();
44+
}
45+
}

0 commit comments

Comments
 (0)