Skip to content

Commit d836f2d

Browse files
committed
Backport Html Writer Security Patches
1 parent b8fac55 commit d836f2d

File tree

8 files changed

+76
-5
lines changed

8 files changed

+76
-5
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com)
66
and this project adheres to [Semantic Versioning](https://semver.org).
77

8-
# TBD - 2.3.5
8+
# 2024-12-26 - 2.3.5
99

1010
### Deprecated
1111

@@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org).
1515

1616
- More context options may be needed for http(s) image. Backport of [PR #4276](https://github.com/PHPOffice/PhpSpreadsheet/pull/4276)
1717
- Backported security patches for Samples.
18+
- Backported security patches for Html Writer.
1819

1920
## 2024-12-08 - 2.3.4
2021

src/PhpSpreadsheet/Writer/Html.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -392,12 +392,12 @@ public function generateHTMLHeader(bool $includeStyles = false): string
392392
} else {
393393
$propertyValue = (string) $propertyValue;
394394
}
395-
$html .= self::generateMeta($propertyValue, "custom.$propertyQualifier.$customProperty");
395+
$html .= self::generateMeta($propertyValue, htmlspecialchars("custom.$propertyQualifier.$customProperty"));
396396
}
397397
}
398398

399399
if (!empty($properties->getHyperlinkBase())) {
400-
$html .= ' <base href="' . $properties->getHyperlinkBase() . '" />' . PHP_EOL;
400+
$html .= ' <base href="' . htmlspecialchars($properties->getHyperlinkBase()) . '" />' . PHP_EOL;
401401
}
402402

403403
$html .= $includeStyles ? $this->generateStyles(true) : $this->generatePageDeclarations(true);
@@ -1526,8 +1526,9 @@ private function generateRow(Worksheet $worksheet, array $values, int $row, stri
15261526
// Hyperlink?
15271527
if ($worksheet->hyperlinkExists($coordinate) && !$worksheet->getHyperlink($coordinate)->isInternal()) {
15281528
$url = $worksheet->getHyperlink($coordinate)->getUrl();
1529-
$urldecode = strtolower(html_entity_decode(trim($url), encoding: 'UTF-8'));
1530-
$parseScheme = preg_match('/^(\\w+):/', $urldecode, $matches);
1529+
$urlDecode1 = html_entity_decode($url, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
1530+
$urlTrim = preg_replace('/^\\s+/u', '', $urlDecode1) ?? $urlDecode1;
1531+
$parseScheme = preg_match('/^([\\w\\s]+):/u', strtolower($urlTrim), $matches);
15311532
if ($parseScheme === 1 && !in_array($matches[1], ['http', 'https', 'file', 'ftp', 's3'], true)) {
15321533
$cellData = htmlspecialchars($url, Settings::htmlEntityFlags());
15331534
} else {
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpOffice\PhpSpreadsheetTests\Writer\Html;
6+
7+
use PhpOffice\PhpSpreadsheet\Reader\Xlsx as XlsxReader;
8+
use PhpOffice\PhpSpreadsheet\Writer\Html as HtmlWriter;
9+
use PHPUnit\Framework\TestCase;
10+
11+
class BadCustomPropertyTest extends TestCase
12+
{
13+
public function testBadCustomProperty(): void
14+
{
15+
$reader = new XlsxReader();
16+
$infile = 'tests/data/Reader/XLSX/sec-q229.dontuse';
17+
$spreadsheet = $reader->load($infile);
18+
$writer = new HtmlWriter($spreadsheet);
19+
$html = $writer->generateHtmlAll();
20+
self::assertStringContainsString('<meta name="custom.string.custom_property&quot;&gt;&lt;img src=1 onerror=alert()&gt;" content="test" />', $html);
21+
$spreadsheet->disconnectWorksheets();
22+
}
23+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpOffice\PhpSpreadsheetTests\Writer\Html;
6+
7+
use PhpOffice\PhpSpreadsheet\Reader\Xlsx as XlsxReader;
8+
use PhpOffice\PhpSpreadsheet\Writer\Html as HtmlWriter;
9+
use PHPUnit\Framework\TestCase;
10+
11+
class BadHyperlinkBaseTest extends TestCase
12+
{
13+
public function testBadHyperlinkBase(): void
14+
{
15+
$reader = new XlsxReader();
16+
$infile = 'tests/data/Reader/XLSX/sec-p66w.dontuse';
17+
$spreadsheet = $reader->load($infile);
18+
$writer = new HtmlWriter($spreadsheet);
19+
$html = $writer->generateHtmlAll();
20+
self::assertStringContainsString('<base href="&quot;&gt;&lt;img src=1 onerror=alert()&gt;" />', $html);
21+
$spreadsheet->disconnectWorksheets();
22+
}
23+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PhpOffice\PhpSpreadsheetTests\Writer\Html;
6+
7+
use PhpOffice\PhpSpreadsheet\Reader\Xlsx as XlsxReader;
8+
use PhpOffice\PhpSpreadsheet\Writer\Html as HtmlWriter;
9+
use PHPUnit\Framework\TestCase;
10+
11+
class BadHyperlinkTest extends TestCase
12+
{
13+
public function testBadHyperlink(): void
14+
{
15+
$reader = new XlsxReader();
16+
$infile = 'tests/data/Reader/XLSX/sec-j47r.dontuse';
17+
$spreadsheet = $reader->load($infile);
18+
$writer = new HtmlWriter($spreadsheet);
19+
$html = $writer->generateHtmlAll();
20+
self::assertStringContainsString("<td class=\"column0 style1 f\">jav\tascript:alert()</td>", $html);
21+
$spreadsheet->disconnectWorksheets();
22+
}
23+
}
8.68 KB
Binary file not shown.
8.11 KB
Binary file not shown.
8.73 KB
Binary file not shown.

0 commit comments

Comments
 (0)