Skip to content

Commit

Permalink
Security Patch Special Characters in Protocol
Browse files Browse the repository at this point in the history
  • Loading branch information
oleibman committed Jan 24, 2025
1 parent 0a3d70c commit f68f688
Show file tree
Hide file tree
Showing 17 changed files with 488 additions and 149 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com)
and this project adheres to [Semantic Versioning](https://semver.org).

# TBD - 2.1.8

### Fixed

- Backported security patch for control characters in protocol.
- Use Composer\Pcre in Xls/Parser. Partial backport of [PR #4203](https://github.com/PHPOffice/PhpSpreadsheet/pull/4203)

# 2025-01-11 - 2.1.7

### Deprecated
Expand Down
1 change: 1 addition & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@
"ext-xmlwriter": "*",
"ext-zip": "*",
"ext-zlib": "*",
"composer/pcre": "^3.2",
"maennchen/zipstream-php": "^2.1 || ^3.0",
"markbaker/complex": "^3.0",
"markbaker/matrix": "^3.0",
Expand Down
162 changes: 85 additions & 77 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions phpstan.neon.dist
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ includes:
- phpstan-baseline.neon
- vendor/phpstan/phpstan-phpunit/extension.neon
- vendor/phpstan/phpstan-phpunit/rules.neon
- vendor/composer/pcre/extension.neon

parameters:
level: 8
Expand All @@ -22,9 +23,9 @@ parameters:
- src/PhpSpreadsheet/Writer/ZipStream3.php
parallel:
processTimeout: 300.0
checkMissingIterableValueType: false
checkGenericClassInNonGenericObjectType: false
ignoreErrors:
- identifier: missingType.iterableValue
- identifier: missingType.generics
# Accept a bit anything for assert methods
- '~^Parameter \#2 .* of static method PHPUnit\\Framework\\Assert\:\:assert\w+\(\) expects .*, .* given\.$~'
- '~^Variable \$helper might not be defined\.$~'
2 changes: 2 additions & 0 deletions src/PhpSpreadsheet/Calculation/DateTimeExcel/Helpers.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ class Helpers
*/
public static function isLeapYear(int|string $year): bool
{
$year = (int) $year;

return (($year % 4) === 0) && (($year % 100) !== 0) || (($year % 400) === 0);
}

Expand Down
2 changes: 1 addition & 1 deletion src/PhpSpreadsheet/Calculation/DateTimeExcel/TimeValue.php
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ public static function fromString(null|array|string|int|bool|float $timeValue):

$arraySplit = preg_split('/[\/:\-\s]/', $timeValue) ?: [];
if ((count($arraySplit) == 2 || count($arraySplit) == 3) && $arraySplit[0] > 24) {
$arraySplit[0] = ($arraySplit[0] % 24);
$arraySplit[0] = ($arraySplit[0] % 24); // @phpstan-ignore-line
$timeValue = implode(':', $arraySplit);
}

Expand Down
2 changes: 1 addition & 1 deletion src/PhpSpreadsheet/Helper/Html.php
Original file line number Diff line number Diff line change
Expand Up @@ -693,7 +693,7 @@ private function rgbToColour(string $rgbValue): string
{
preg_match_all('/\d+/', $rgbValue, $values);
foreach ($values[0] as &$value) {
$value = str_pad(dechex($value), 2, '0', STR_PAD_LEFT);
$value = str_pad(dechex($value), 2, '0', STR_PAD_LEFT); // @phpstan-ignore-line
}

return implode('', $values[0]);
Expand Down
7 changes: 5 additions & 2 deletions src/PhpSpreadsheet/Reader/Html.php
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ private function readBeginning(): string
private function readEnding(): string
{
$meta = stream_get_meta_data($this->fileHandle);
$filename = $meta['uri'];
$filename = $meta['uri']; // @phpstan-ignore-line

$size = (int) filesize($filename);
if ($size === 0) {
Expand Down Expand Up @@ -1031,7 +1031,10 @@ private function insertImage(Worksheet $sheet, string $column, int $row, array $
$name = $attributes['alt'] ?? null;

$drawing = new Drawing();
$drawing->setPath($src);
$drawing->setPath($src, false);
if ($drawing->getPath() === '') {
return;
}
$drawing->setWorksheet($sheet);
$drawing->setCoordinates($column . $row);
$drawing->setOffsetX(0);
Expand Down
2 changes: 1 addition & 1 deletion src/PhpSpreadsheet/Reader/Slk.php
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,7 @@ private function processPRecord(array $rowData, Spreadsheet &$spreadsheet): void
private function processPColors(string $rowDatum, array &$formatArray): void
{
if (preg_match('/L([1-9]\\d*)/', $rowDatum, $matches)) {
$fontColor = $matches[1] % 8;
$fontColor = $matches[1] % 8; // @phpstan-ignore-line
$formatArray['font']['color']['argb'] = self::COLOR_ARRAY[$fontColor];
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/PhpSpreadsheet/Worksheet/Drawing.php
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ public function setPath(string $path, bool $verifyFile = true, ?ZipArchive $zip

$this->path = '';
// Check if a URL has been passed. https://stackoverflow.com/a/2058596/1252979
if (filter_var($path, FILTER_VALIDATE_URL)) {
if (filter_var($path, FILTER_VALIDATE_URL) || (preg_match('/^([\\w\\s\\x00-\\x1f]+):/u', $path) && !preg_match('/^([\\w]+):/u', $path))) {
if (!preg_match('/^(http|https|file|ftp|s3):/', $path)) {
throw new PhpSpreadsheetException('Invalid protocol for linked drawing');
}
Expand Down
2 changes: 1 addition & 1 deletion src/PhpSpreadsheet/Worksheet/MemoryDrawing.php
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ private function cloneResource(): void
$transparent = imagecolortransparent($this->imageResource);
if ($transparent >= 0) {
$rgb = imagecolorsforindex($this->imageResource, $transparent);
if (empty($rgb)) {
if (empty($rgb)) { // @phpstan-ignore-line
throw new Exception('Could not get image colors');
}

Expand Down
32 changes: 24 additions & 8 deletions src/PhpSpreadsheet/Writer/Html.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace PhpOffice\PhpSpreadsheet\Writer;

use Composer\Pcre\Preg;
use PhpOffice\PhpSpreadsheet\Calculation\Calculation;
use PhpOffice\PhpSpreadsheet\Cell\Cell;
use PhpOffice\PhpSpreadsheet\Cell\Coordinate;
Expand Down Expand Up @@ -621,13 +622,13 @@ private function writeImageInCell(string $coordinates): string
$filename = $drawing->getPath();

// Strip off eventual '.'
$filename = (string) preg_replace('/^[.]/', '', $filename);
$filename = Preg::replace('/^[.]/', '', $filename);

// Prepend images root
$filename = $this->getImagesRoot() . $filename;

// Strip off eventual '.' if followed by non-/
$filename = (string) preg_replace('@^[.]([^/])@', '$1', $filename);
$filename = Preg::replace('@^[.]([^/])@', '$1', $filename);

// Convert UTF8 data to PCDATA
$filename = htmlspecialchars($filename, Settings::htmlEntityFlags());
Expand Down Expand Up @@ -1350,7 +1351,7 @@ private function generateRowCellData(Worksheet $worksheet, null|Cell|string $cel

// Converts the cell content so that spaces occuring at beginning of each new line are replaced by  
// Example: " Hello\n to the world" is converted to "  Hello\n to the world"
$cellData = (string) preg_replace('/(?m)(?:^|\\G) /', ' ', $cellData);
$cellData = Preg::replace('/(?m)(?:^|\\G) /', ' ', $cellData);

// convert newline "\n" to '<br>'
$cellData = nl2br($cellData);
Expand Down Expand Up @@ -1495,10 +1496,11 @@ private function generateRow(Worksheet $worksheet, array $values, int $row, stri
if ($worksheet->hyperlinkExists($coordinate) && !$worksheet->getHyperlink($coordinate)->isInternal()) {
$url = $worksheet->getHyperlink($coordinate)->getUrl();
$urlDecode1 = html_entity_decode($url, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
$urlTrim = preg_replace('/^\\s+/u', '', $urlDecode1) ?? $urlDecode1;
$parseScheme = preg_match('/^([\\w\\s]+):/u', strtolower($urlTrim), $matches);
if ($parseScheme === 1 && !in_array($matches[1], ['http', 'https', 'file', 'ftp', 's3'], true)) {
$urlTrim = Preg::replace('/^\\s+/u', '', $urlDecode1);
$parseScheme = Preg::isMatch('/^([\\w\\s\\x00-\\x1f]+):/u', strtolower($urlTrim), $matches);
if ($parseScheme && !in_array($matches[1], ['http', 'https', 'file', 'ftp', 'mailto', 's3'], true)) {
$cellData = htmlspecialchars($url, Settings::htmlEntityFlags());
$cellData = self::replaceControlChars($cellData);
} else {
$cellData = '<a href="' . htmlspecialchars($url, Settings::htmlEntityFlags()) . '" title="' . htmlspecialchars($worksheet->getHyperlink($coordinate)->getTooltip(), Settings::htmlEntityFlags()) . '">' . $cellData . '</a>';
}
Expand Down Expand Up @@ -1540,6 +1542,20 @@ private function generateRow(Worksheet $worksheet, array $values, int $row, stri
return $html;
}

public static function replaceNonAscii(array $matches): string
{
return '&#' . mb_ord($matches[0], 'UTF-8') . ';';
}

private static function replaceControlChars(string $convert): string
{
return Preg::replaceCallback(
'/[\\x00-\\x1f]/',
[self::class, 'replaceNonAscii'],
$convert
);
}

/**
* Takes array where of CSS properties / values and converts to CSS string.
*/
Expand Down Expand Up @@ -1627,8 +1643,8 @@ public function formatColor(string $value, string $format): string
$matches = [];

$color_regex = '/^\\[[a-zA-Z]+\\]/';
if (preg_match($color_regex, $format, $matches)) {
$color = str_replace(['[', ']'], '', $matches[0]);
if (Preg::isMatch($color_regex, $format, $matches)) {
$color = str_replace(['[', ']'], '', $matches[0]); // @phpstan-ignore-line
$color = strtolower($color);
}

Expand Down
Loading

0 comments on commit f68f688

Please sign in to comment.