From d95bc290beb137d4118095b96f62ec47e0205cec Mon Sep 17 00:00:00 2001
From: oleibman <10341515+oleibman@users.noreply.github.com>
Date: Tue, 24 Sep 2024 05:49:02 -0700
Subject: [PATCH] Backport Security Patch
---
phpstan-baseline.neon | 2 +-
src/PhpSpreadsheet/Reader/Xlsx.php | 14 +++-
src/PhpSpreadsheet/Worksheet/BaseDrawing.php | 2 +-
src/PhpSpreadsheet/Worksheet/Drawing.php | 70 +++++++++++++-----
src/PhpSpreadsheet/Writer/Html.php | 16 ++--
src/PhpSpreadsheet/Writer/Xls.php | 2 +-
src/PhpSpreadsheet/Writer/Xlsx.php | 10 ++-
.../Writer/Xlsx/ContentTypes.php | 22 ++++--
.../Reader/Xlsx/URLImageTest.php | 28 ++++++-
.../Html/ExtendForChartsAndImagesTest.php | 45 ++++++++++-
.../Writer/Html/ImageEmbedTest.php | 45 +++++++++++
tests/data/Reader/XLSX/urlImage.bad.dontuse | Bin 0 -> 10114 bytes
tests/data/Reader/XLSX/urlImage.notfound.xlsx | Bin 0 -> 10116 bytes
13 files changed, 214 insertions(+), 42 deletions(-)
create mode 100644 tests/PhpSpreadsheetTests/Writer/Html/ImageEmbedTest.php
create mode 100644 tests/data/Reader/XLSX/urlImage.bad.dontuse
create mode 100644 tests/data/Reader/XLSX/urlImage.notfound.xlsx
diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon
index 120a4f4cef..e107b26679 100644
--- a/phpstan-baseline.neon
+++ b/phpstan-baseline.neon
@@ -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
-
diff --git a/src/PhpSpreadsheet/Reader/Xlsx.php b/src/PhpSpreadsheet/Reader/Xlsx.php
index 1c33fd6811..5f63743d08 100644
--- a/src/PhpSpreadsheet/Reader/Xlsx.php
+++ b/src/PhpSpreadsheet/Reader/Xlsx.php
@@ -1291,7 +1291,7 @@ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet
$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 @@ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet
$objDrawing->setPath(
'zip://' . File::realpath($filename) . '#' .
$images[$embedImageKey],
- false
+ false,
+ $zip
);
} else {
$linkImageKey = (string) self::getArrayItem(
@@ -1412,6 +1413,9 @@ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet
$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 @@ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet
$objDrawing->setPath(
'zip://' . File::realpath($filename) . '#' .
$images[$embedImageKey],
- false
+ false,
+ $zip
);
} else {
$linkImageKey = (string) self::getArrayItem(
@@ -1497,6 +1502,9 @@ protected function loadSpreadsheetFromFile(string $filename): Spreadsheet
$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));
diff --git a/src/PhpSpreadsheet/Worksheet/BaseDrawing.php b/src/PhpSpreadsheet/Worksheet/BaseDrawing.php
index 5001346ad5..49e2eff104 100644
--- a/src/PhpSpreadsheet/Worksheet/BaseDrawing.php
+++ b/src/PhpSpreadsheet/Worksheet/BaseDrawing.php
@@ -220,7 +220,7 @@ public function setWorksheet(?Worksheet $worksheet = null, bool $overrideOld = f
{
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);
diff --git a/src/PhpSpreadsheet/Worksheet/Drawing.php b/src/PhpSpreadsheet/Worksheet/Drawing.php
index 7d957539ef..bb65db2b97 100644
--- a/src/PhpSpreadsheet/Worksheet/Drawing.php
+++ b/src/PhpSpreadsheet/Worksheet/Drawing.php
@@ -106,40 +106,72 @@ public function getPath()
*/
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.
*/
diff --git a/src/PhpSpreadsheet/Writer/Html.php b/src/PhpSpreadsheet/Writer/Html.php
index 978d1764c1..66a0ec89b6 100644
--- a/src/PhpSpreadsheet/Writer/Html.php
+++ b/src/PhpSpreadsheet/Writer/Html.php
@@ -612,6 +612,9 @@ private function extendRowsForChartsAndImages(Worksheet $worksheet, int $row): s
[$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 @@ private function writeImageInCell(Worksheet $worksheet, $coordinates)
}
$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 @@ private function writeImageInCell(Worksheet $worksheet, $coordinates)
$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;
+ }
}
}
diff --git a/src/PhpSpreadsheet/Writer/Xls.php b/src/PhpSpreadsheet/Writer/Xls.php
index 983414fccc..e0480a5a36 100644
--- a/src/PhpSpreadsheet/Writer/Xls.php
+++ b/src/PhpSpreadsheet/Writer/Xls.php
@@ -486,7 +486,7 @@ private function processDrawing(BstoreContainer &$bstoreContainer, Drawing $draw
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());
diff --git a/src/PhpSpreadsheet/Writer/Xlsx.php b/src/PhpSpreadsheet/Writer/Xlsx.php
index 6ed12d4aa1..601f0f5c4c 100644
--- a/src/PhpSpreadsheet/Writer/Xlsx.php
+++ b/src/PhpSpreadsheet/Writer/Xlsx.php
@@ -495,7 +495,9 @@ public function save($filename, int $flags = 0): void
// 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 @@ public function save($filename, int $flags = 0): void
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 @@ private function processDrawing(WorksheetDrawing $drawing)
{
$data = null;
$filename = $drawing->getPath();
+ if ($filename === '') {
+ return null;
+ }
$imageData = getimagesize($filename);
if (!empty($imageData)) {
diff --git a/src/PhpSpreadsheet/Writer/Xlsx/ContentTypes.php b/src/PhpSpreadsheet/Writer/Xlsx/ContentTypes.php
index 73657fc380..56f062b562 100644
--- a/src/PhpSpreadsheet/Writer/Xlsx/ContentTypes.php
+++ b/src/PhpSpreadsheet/Writer/Xlsx/ContentTypes.php
@@ -6,6 +6,7 @@
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 @@ public function writeContentTypes(Spreadsheet $spreadsheet, $includeCharts = fal
$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 @@ public function writeContentTypes(Spreadsheet $spreadsheet, $includeCharts = fal
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())]);
diff --git a/tests/PhpSpreadsheetTests/Reader/Xlsx/URLImageTest.php b/tests/PhpSpreadsheetTests/Reader/Xlsx/URLImageTest.php
index e7f9901013..bf86eb69c0 100644
--- a/tests/PhpSpreadsheetTests/Reader/Xlsx/URLImageTest.php
+++ b/tests/PhpSpreadsheetTests/Reader/Xlsx/URLImageTest.php
@@ -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 @@
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 @@ public function testURLImageSource(): void
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);
+ }
}
diff --git a/tests/PhpSpreadsheetTests/Writer/Html/ExtendForChartsAndImagesTest.php b/tests/PhpSpreadsheetTests/Writer/Html/ExtendForChartsAndImagesTest.php
index e3d23230c1..f2db5c5904 100644
--- a/tests/PhpSpreadsheetTests/Writer/Html/ExtendForChartsAndImagesTest.php
+++ b/tests/PhpSpreadsheetTests/Writer/Html/ExtendForChartsAndImagesTest.php
@@ -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 @@ public function testSheetWithImageBelowData(): void
// 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 @@ public function testSheetWithImageRightOfData(): void
// 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);
diff --git a/tests/PhpSpreadsheetTests/Writer/Html/ImageEmbedTest.php b/tests/PhpSpreadsheetTests/Writer/Html/ImageEmbedTest.php
new file mode 100644
index 0000000000..bc9e40ed4c
--- /dev/null
+++ b/tests/PhpSpreadsheetTests/Writer/Html/ImageEmbedTest.php
@@ -0,0 +1,45 @@
+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, '
disconnectWorksheets();
+ }
+}
diff --git a/tests/data/Reader/XLSX/urlImage.bad.dontuse b/tests/data/Reader/XLSX/urlImage.bad.dontuse
new file mode 100644
index 0000000000000000000000000000000000000000..3e39a809ff7306eec8c25b798682d30184f7b971
GIT binary patch
literal 10114
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
SWdHyO|2V+ua6|q5?*9OYvxM*f
literal 0
HcmV?d00001
diff --git a/tests/data/Reader/XLSX/urlImage.notfound.xlsx b/tests/data/Reader/XLSX/urlImage.notfound.xlsx
new file mode 100644
index 0000000000000000000000000000000000000000..e335a80de1cf752f325514998efacfb743625fe4
GIT binary patch
literal 10116
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
RG5~-M``E$Y0Qm9p{tt*_h93X`
literal 0
HcmV?d00001