diff --git a/src/PhpSpreadsheet/Writer/Html.php b/src/PhpSpreadsheet/Writer/Html.php index 6ba55ef699..2258033584 100644 --- a/src/PhpSpreadsheet/Writer/Html.php +++ b/src/PhpSpreadsheet/Writer/Html.php @@ -1512,7 +1512,14 @@ private function generateRow(Worksheet $worksheet, array $values, $row, $cellTyp // Hyperlink? if ($worksheet->hyperlinkExists($coordinate) && !$worksheet->getHyperlink($coordinate)->isInternal()) { - $cellData = '' . $cellData . ''; + $url = $worksheet->getHyperlink($coordinate)->getUrl(); + $urldecode = strtolower(html_entity_decode(trim($url), encoding: 'UTF-8')); + $parseScheme = preg_match('/^(\\w+):/', $urldecode, $matches); + if ($parseScheme === 1 && !in_array($matches[1], ['http', 'https', 'file', 'ftp', 's3'], true)) { + $cellData = htmlspecialchars($url, Settings::htmlEntityFlags()); + } else { + $cellData = '' . $cellData . ''; + } } // Should the cell be written or is it swallowed by a rowspan or colspan? diff --git a/tests/PhpSpreadsheetTests/Writer/Html/NoJavascriptLinksTest.php b/tests/PhpSpreadsheetTests/Writer/Html/NoJavascriptLinksTest.php new file mode 100644 index 0000000000..7b1a633bc1 --- /dev/null +++ b/tests/PhpSpreadsheetTests/Writer/Html/NoJavascriptLinksTest.php @@ -0,0 +1,33 @@ +getActiveSheet(); + $sheet->getCell('A1')->setValue('Click me'); + $hyperlink = new Hyperlink('http://www.example.com'); + $sheet->getCell('A1')->setHyperlink($hyperlink); + $sheet->getCell('A2')->setValue('JS link'); + $hyperlink2 = new Hyperlink('javascript:alert(\'hello1\')'); + $sheet->getCell('A2')->setHyperlink($hyperlink2); + $sheet->getCell('A3')->setValue('=HYPERLINK("javascript:alert(\'hello2\')", "jsfunc click")'); + + $writer = new Html($spreadsheet); + $html = $writer->generateHTMLAll(); + self::assertStringContainsString('