From 1b70a7aabe2c988fad0618dda03d557d3272ced7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20R?= Date: Mon, 4 Nov 2019 17:52:04 +0100 Subject: [PATCH 01/11] Update php7.md --- doc/bc/5.90/php7.md | 51 ++++++++++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/doc/bc/5.90/php7.md b/doc/bc/5.90/php7.md index 334b9d2f555..9630b881e00 100644 --- a/doc/bc/5.90/php7.md +++ b/doc/bc/5.90/php7.md @@ -1,12 +1,16 @@ # PHP 7 support +## PHP 7.0 support For the [2017.10 release](https://github.com/ezsystems/ezpublish-legacy/releases/tag/v2017.10.0), eZ Publish recived changes to switch to PHP 5 style constuctors all over the code base. -Reason is to reach full PHP 7 support by avoiding deprecation warnings for still using PHP 4 +Reason is to reach full PHP 7.0 support by avoiding deprecation warnings for still using PHP 4 style constructors. +And while there are compatability functions kept around in most cases to avoid any fatal errors for you, +to avoid warnings you'll need to adapt too. + Here is an example of how you might need to adapt for this change in your code: ```diff @@ -30,24 +34,37 @@ Other more common examples are classes extending `eZPersistentObject` or `eZData You should also consider changing your own code to use PHP 5 style constructor while doing this, in example above that would imply changing `function eZFindResultNode` to `function __construct`. -Further reading: http://php.net/manual/en/language.oop5.decon.phpi +Further reading: +- http://php.net/manual/en/language.oop5.decon.php +- https://www.php.net/manual/en/migration70.incompatible.php +- https://www.php.net/manual/en/migration71.incompatible.php + +## PHP 7.2 support + +Starting with 2019.03 release, issues on PHP 7.2 and PHP 7.3 has been fixed, but in your own code you'll ideally also need to handle some of this. + +Most notably is `Warn when counting non-countable types` added in PHP 7.2. + +To hande this across all supported PHP versions, we intropduced use of [symfony/polyfill-php73](https://github.com/symfony/polyfill-php73) package, witch backports PHP 7.3's function [is_countable](https://www.php.net/is_countable). +Here is an example of changes you might need to do in your own code around this: -Note: You should also increase requriment for ezplublish-legacy once the above changes are done like following example: ```diff -diff --git a/composer.json b/composer.json -index de225eb1..d0389c6d 100644 ---- a/composer.json -+++ b/composer.json -@@ -11,7 +11,8 @@ - ], - "minimum-stability": "dev", - "require": { -- "ezsystems/ezpublish-legacy-installer": "*" -+ "ezsystems/ezpublish-legacy-installer": "*", -+ "ezsystems/ezpublish-legacy": ">=2017.10" - }, - "extra": { - "ezpublish-legacy-extension-name": "ezfind" +diff --git a/kernel/common/eztemplatedesignresource.php b/kernel/common/eztemplatedesignresource.php +index b0fc28faa9a..9b8ca2a8d94 100644 +--- a/kernel/common/eztemplatedesignresource.php ++++ b/kernel/common/eztemplatedesignresource.php +@@ -86,7 +86,7 @@ function templateNodeTransformation( $functionName, &$node, + $matchCount = 0; + foreach ( $customMatchList as $customMatch ) + { +- $matchConditionCount = count( $customMatch['conditions'] ); ++ $matchConditionCount = is_countable( $customMatch['conditions'] ) ? count( $customMatch['conditions'] ) : 0; + $code = ''; + if ( $matchCount > 0 ) + { ``` +Further reading: +- https://www.php.net/manual/en/migration72.incompatible.php +- https://www.php.net/manual/en/migration73.incompatible.php From 625d836f4c4f8a0e854156f63487a1a483cac5fc Mon Sep 17 00:00:00 2001 From: Pr0metheus42 Date: Wed, 18 Dec 2019 13:53:08 +0100 Subject: [PATCH 02/11] Make autoloads ignore anonymous classes (#1448) --- kernel/private/classes/ezautoloadgenerator.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/kernel/private/classes/ezautoloadgenerator.php b/kernel/private/classes/ezautoloadgenerator.php index 0efd3692283..1b636700e7e 100644 --- a/kernel/private/classes/ezautoloadgenerator.php +++ b/kernel/private/classes/ezautoloadgenerator.php @@ -638,6 +638,14 @@ protected function getClassFileList( $fileList, $mode ) if ($tokens[$key-1][1] === '::') { break; } + /** + * Ignore token if class is anonymous: "new Class() {}" + * @see https://www.php.net/manual/en/language.oop5.anonymous.php + * NEW_TOKEN - WHITESPACE_TOKEN - CLASS_TOKEN + */ + if($tokens[$key-2][0] === T_NEW) { + break; + } // Increment stat for found class. $this->incrementProgressStat( self::OUTPUT_PROGRESS_PHASE2, 'classCount' ); From 5a391309cb71d180eed32603e352cea17bc9095d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20R?= Date: Wed, 18 Dec 2019 13:53:22 +0100 Subject: [PATCH 03/11] Fix transformURI() on ignoreIndexDir & ! htmlEscape (#1449) * Fix transformURI() on ignoreIndexDir & ! htmlEscape Fix of issue introduced in 07de12622320c4648dd03d20647ac012a1411d90 "Fix EZP-23086: Image thumbnail not shown on backend if alias contains quotes" when escaping was introduced. * Update ezuri.php --- lib/ezutils/classes/ezuri.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ezutils/classes/ezuri.php b/lib/ezutils/classes/ezuri.php index 1bb94ee18cf..c2b1993a63e 100644 --- a/lib/ezutils/classes/ezuri.php +++ b/lib/ezutils/classes/ezuri.php @@ -605,7 +605,7 @@ public static function transformURI( &$href, $ignoreIndexDir = false, $serverURL $modifiedHref = eZClusterFileHandler::instance()->applyServerUri( $trimmedHref ); if ( $modifiedHref != $trimmedHref ) { - $href = $htmlEscape ? self::escapeHtmlTransformUri( $href ) : $href; + $href = $htmlEscape ? self::escapeHtmlTransformUri( $modifiedHref ) : $modifiedHref; return true; } unset( $modifiedHref ); From 56e779d91697b34fd44d2f2c289115df04337ada Mon Sep 17 00:00:00 2001 From: Peter Keung Date: Wed, 18 Dec 2019 05:04:57 -0800 Subject: [PATCH 04/11] Do not support literal HTML in the Administration Interface (#1408) --- .../admin/templates/content/datatype/view/ezxmltags/literal.tpl | 1 + 1 file changed, 1 insertion(+) create mode 100644 design/admin/templates/content/datatype/view/ezxmltags/literal.tpl diff --git a/design/admin/templates/content/datatype/view/ezxmltags/literal.tpl b/design/admin/templates/content/datatype/view/ezxmltags/literal.tpl new file mode 100644 index 00000000000..4ef64b0183f --- /dev/null +++ b/design/admin/templates/content/datatype/view/ezxmltags/literal.tpl @@ -0,0 +1 @@ +{* Do not output literal HTML in the Administration Interface *}{$content|wash(xhtml)} \ No newline at end of file From b3a83269d974c65b79a6fe5d97ed11390957258a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Edi=20Modri=C4=87?= Date: Wed, 18 Dec 2019 14:51:49 +0100 Subject: [PATCH 05/11] Fix notice when checking for anonymous classes in autoload generator (#1450) --- kernel/private/classes/ezautoloadgenerator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/private/classes/ezautoloadgenerator.php b/kernel/private/classes/ezautoloadgenerator.php index 1b636700e7e..96f4a1b38d4 100644 --- a/kernel/private/classes/ezautoloadgenerator.php +++ b/kernel/private/classes/ezautoloadgenerator.php @@ -643,7 +643,7 @@ protected function getClassFileList( $fileList, $mode ) * @see https://www.php.net/manual/en/language.oop5.anonymous.php * NEW_TOKEN - WHITESPACE_TOKEN - CLASS_TOKEN */ - if($tokens[$key-2][0] === T_NEW) { + if(isset($tokens[$key-2][0]) && $tokens[$key-2][0] === T_NEW) { break; } From c039ef87f02c439073850300c19fd1631d02d92d Mon Sep 17 00:00:00 2001 From: Gaetano Giunta Date: Thu, 19 Dec 2019 15:57:40 +0000 Subject: [PATCH 06/11] improve php 7 bc doc (#1452) --- doc/bc/5.90/php7.md | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/doc/bc/5.90/php7.md b/doc/bc/5.90/php7.md index 9630b881e00..d8b3847a361 100644 --- a/doc/bc/5.90/php7.md +++ b/doc/bc/5.90/php7.md @@ -3,15 +3,17 @@ ## PHP 7.0 support For the [2017.10 release](https://github.com/ezsystems/ezpublish-legacy/releases/tag/v2017.10.0), -eZ Publish recived changes to switch to PHP 5 style constuctors all over the code base. +eZ Publish received changes all over the code base, switching object constructor methods to PHP 5 style. -Reason is to reach full PHP 7.0 support by avoiding deprecation warnings for still using PHP 4 +The reason is to achieve full PHP 7.0 support by avoiding the deprecation warnings when using PHP 4 style constructors. -And while there are compatability functions kept around in most cases to avoid any fatal errors for you, -to avoid warnings you'll need to adapt too. +Care has been taken to keep around compatibility functions in all known cases to avoid fatal errors +for custom extensions, however to avoid warnings you might need to adapt your code as well. -Here is an example of how you might need to adapt for this change in your code: +Common cases are classes extending `eZPersistentObject` or `eZDataType`. + +Here is an example of how you should adapt your code for the constructor change: ```diff diff --git a/classes/ezfindresultnode.php b/classes/ezfindresultnode.php @@ -29,10 +31,10 @@ index fafca310..b6462159 100644 $this->ContentObjectID = $rows['id']; ``` -Other more common examples are classes extending `eZPersistentObject` or `eZDataType`. - -You should also consider changing your own code to use PHP 5 style constructor while doing this, -in example above that would imply changing `function eZFindResultNode` to `function __construct`. +For best results you should also consider changing your own code to use PHP 5 style constructors. +In the example above that would mean renaming `function eZFindResultNode` to `function __construct` and, +if you think that other code might exist which extends the `eZFindResultNode` class, add back a courtesy +`eZFindResultNode` function that does nothing but call `__construct` Further reading: - http://php.net/manual/en/language.oop5.decon.php @@ -41,13 +43,15 @@ Further reading: ## PHP 7.2 support -Starting with 2019.03 release, issues on PHP 7.2 and PHP 7.3 has been fixed, but in your own code you'll ideally also need to handle some of this. +Starting with the 2019.03 release, issues happening on PHP 7.2 and PHP 7.3 have been fixed, but in your own code you'll +also need to handle some of those. -Most notably is `Warn when counting non-countable types` added in PHP 7.2. +Most notable is the `Warn when counting non-countable types` change, added in PHP 7.2. -To hande this across all supported PHP versions, we intropduced use of [symfony/polyfill-php73](https://github.com/symfony/polyfill-php73) package, witch backports PHP 7.3's function [is_countable](https://www.php.net/is_countable). +To handle this across all supported PHP versions, we introduced use of [symfony/polyfill-php73](https://github.com/symfony/polyfill-php73) +package, witch backports PHP 7.3's function [is_countable](https://www.php.net/is_countable). -Here is an example of changes you might need to do in your own code around this: +Here is an example of changes you might need to apply in your own code to work around that: ```diff diff --git a/kernel/common/eztemplatedesignresource.php b/kernel/common/eztemplatedesignresource.php From 2f7d73917b8cef4cc5f75be975aca68271f7fe14 Mon Sep 17 00:00:00 2001 From: Anis Jrad Date: Wed, 5 Feb 2020 10:35:23 +0100 Subject: [PATCH 07/11] Show images after used url_prefix (#1453) * Show images after used url_prefix * CS --- extension/ezjscore/classes/ezjscajaxcontent.php | 4 ++++ .../ezoe/design/standard/javascript/ezoe/popup_utils.js | 2 +- .../design/standard/templates/ezoe/tag_embed_images.tpl | 6 +++--- extension/ezoe/ezxmltext/handlers/input/ezoexmlinput.php | 3 ++- 4 files changed, 10 insertions(+), 5 deletions(-) diff --git a/extension/ezjscore/classes/ezjscajaxcontent.php b/extension/ezjscore/classes/ezjscajaxcontent.php index 609aa0809af..ed119d4ffa2 100644 --- a/extension/ezjscore/classes/ezjscajaxcontent.php +++ b/extension/ezjscore/classes/ezjscajaxcontent.php @@ -478,6 +478,10 @@ function ( &$element, $key ) (string)$element, 'UTF-8' ); } + if( $key === 'url' ) + { + eZURI::transformURI( $element, true ); + } } ); diff --git a/extension/ezoe/design/standard/javascript/ezoe/popup_utils.js b/extension/ezoe/design/standard/javascript/ezoe/popup_utils.js index 8436e042e0f..56bda2096a3 100644 --- a/extension/ezoe/design/standard/javascript/ezoe/popup_utils.js +++ b/extension/ezoe/design/standard/javascript/ezoe/popup_utils.js @@ -843,7 +843,7 @@ var eZOEPopupUtils = { { tag = document.createElement("span"); tag.className = 'image_preview'; - var previewUrl = ed.settings.ez_root_url + encodeURI( n.data_map[ n.image_attributes[imageIndex] ].content[eZOEPopupUtils.settings.browseImageAlias].url ) + var previewUrl = encodeURI( n.data_map[ n.image_attributes[imageIndex] ].content[eZOEPopupUtils.settings.browseImageAlias].url ) tag.innerHTML += ' ' + ed.getLang('preview.preview_desc') + ''; td.appendChild( tag ); hasImage = true; diff --git a/extension/ezoe/design/standard/templates/ezoe/tag_embed_images.tpl b/extension/ezoe/design/standard/templates/ezoe/tag_embed_images.tpl index 26460de3644..181d49baffc 100644 --- a/extension/ezoe/design/standard/templates/ezoe/tag_embed_images.tpl +++ b/extension/ezoe/design/standard/templates/ezoe/tag_embed_images.tpl @@ -55,7 +55,7 @@ tinyMCEPopup.onInit.add( eZOEPopupUtils.BIND( eZOEPopupUtils.init, window, { else { var imageAtr = eZOEPopupUtils.embedObject['data_map'][ imageAttributes[0] ], imageSizeObj = imageAtr['content'][ args['alt'] ]; - args['src'] = ed.settings.ez_root_url + imageSizeObj['url']; + args['src'] = imageSizeObj['url']; args['title'] = eZOEPopupUtils.safeHtml( imageAtr['alternative_text'] || eZOEPopupUtils.embedObject['name'] ); args['width'] = imageSizeObj['width']; args['height'] = imageSizeObj['height']; @@ -153,7 +153,7 @@ function loadImageSize( e, el ) } else if ( attribObj[size] ) { - previewImageNode.attr( 'src', eds.ez_root_url + attribObj[size]['url'] ); + previewImageNode.attr( 'src', attribObj[size]['url'] ); tinyMCEPopup.resizeToInnerSize(); } else @@ -165,7 +165,7 @@ function loadImageSize( e, el ) { var size = jQuery('#embed_size_source').val(), imageAttributes = eZOEPopupUtils.embedObject['image_attributes']; eZOEPopupUtils.embedObject['data_map'][ imageAttributes[0] ]['content'][ size ] = data['content']['data_map'][ imageAttributes[0] ]['content'][ size ]; - previewImageNode.attr( 'src', eds.ez_root_url + eZOEPopupUtils.embedObject['data_map'][ imageAttributes[0] ]['content'][ size ]['url'] ); + previewImageNode.attr( 'src', eZOEPopupUtils.embedObject['data_map'][ imageAttributes[0] ]['content'][ size ]['url'] ); } }); } diff --git a/extension/ezoe/ezxmltext/handlers/input/ezoexmlinput.php b/extension/ezoe/ezxmltext/handlers/input/ezoexmlinput.php index 6f2de54d77d..19c4ce8384b 100644 --- a/extension/ezoe/ezxmltext/handlers/input/ezoexmlinput.php +++ b/extension/ezoe/ezxmltext/handlers/input/ezoexmlinput.php @@ -1208,7 +1208,8 @@ function inputTagXML( &$tag, $currentSectionLevel, $tdSectionLevel = null ) if ( $content->hasAttribute( $size ) ) { $imageAlias = $content->imageAlias( $size ); - $srcString = $URL . '/' . $imageAlias['url']; + eZURI::transformURI( $imageAlias['url'], true ); + $srcString = $imageAlias['url']; $imageWidth = $imageAlias['width']; $imageHeight = $imageAlias['height']; break; From 8c9b70662785b53ebca13f7c7fdaece05bfb132f Mon Sep 17 00:00:00 2001 From: Gunnstein Lye Date: Wed, 15 Jan 2020 16:13:22 +0000 Subject: [PATCH 08/11] EZP-31040: Remote Code Execution in file uploads --- autoload/ezp_kernel.php | 1 + .../ezbinaryfile/ezbinaryfiletype.php | 42 ++++++++++++-- .../classes/datatypes/ezimage/ezimagetype.php | 26 ++++++++- .../classes/datatypes/ezmedia/ezmediatype.php | 42 ++++++++++++-- .../ezfileextensionblacklistvalidator.php | 58 +++++++++++++++++++ settings/file.ini | 11 ++++ 6 files changed, 168 insertions(+), 12 deletions(-) create mode 100644 lib/ezutils/classes/ezfileextensionblacklistvalidator.php diff --git a/autoload/ezp_kernel.php b/autoload/ezp_kernel.php index b8ef66449c6..396efc7fea8 100755 --- a/autoload/ezp_kernel.php +++ b/autoload/ezp_kernel.php @@ -176,6 +176,7 @@ 'eZExtensionPackageHandler' => 'kernel/classes/packagehandlers/ezextension/ezextensionpackagehandler.php', 'eZFSFileHandler' => 'kernel/classes/clusterfilehandlers/ezfsfilehandler.php', 'eZFile' => 'lib/ezfile/classes/ezfile.php', + 'eZFileExtensionBlackListValidator' => 'lib/ezutils/classes/ezfileextensionblacklistvalidator.php', 'eZFileHandler' => 'lib/ezfile/classes/ezfilehandler.php', 'eZFilePackageHandler' => 'kernel/classes/packagehandlers/ezfile/ezfilepackagehandler.php', 'eZFilePassthroughHandler' => 'kernel/classes/binaryhandlers/ezfilepassthrough/ezfilepassthroughhandler.php', diff --git a/kernel/classes/datatypes/ezbinaryfile/ezbinaryfiletype.php b/kernel/classes/datatypes/ezbinaryfile/ezbinaryfiletype.php index c57ad2084bb..ec82e546c56 100644 --- a/kernel/classes/datatypes/ezbinaryfile/ezbinaryfiletype.php +++ b/kernel/classes/datatypes/ezbinaryfile/ezbinaryfiletype.php @@ -27,6 +27,7 @@ public function __construct() { parent::__construct( self::DATA_TYPE_STRING, ezpI18n::tr( 'kernel/classes/datatypes', "File", 'Datatype name' ), array( 'serialize_supported' => true ) ); + $this->FileExtensionBlackListValidator = new eZFileExtensionBlackListValidator(); } /*! @@ -246,16 +247,38 @@ function validateObjectAttributeHTTPInput( $http, $base, $contentObjectAttribute $httpFileName = $base . "_data_binaryfilename_" . $contentObjectAttribute->attribute( "id" ); $maxSize = 1024 * 1024 * $classAttribute->attribute( self::MAX_FILESIZE_FIELD ); - if ( $contentObjectAttribute->validateIsRequired() ) + $contentObjectAttributeID = $contentObjectAttribute->attribute( 'id' ); + $version = $contentObjectAttribute->attribute( 'version' ); + $binary = eZBinaryFile::fetch( $contentObjectAttributeID, $version ); + $extensionsBlackList = implode(', ', $this->FileExtensionBlackListValidator->extensionsBlackList() ); + if ( $binary === null ) { - $contentObjectAttributeID = $contentObjectAttribute->attribute( "id" ); - $version = $contentObjectAttribute->attribute( "version" ); - $binary = eZBinaryFile::fetch( $contentObjectAttributeID, $version ); - if ( $binary === null ) + if ( $contentObjectAttribute->validateIsRequired() ) { $mustUpload = true; } } + else + { + $state = $this->FileExtensionBlackListValidator->validate( $binary->attribute( 'filename' ) ); + if ( $state === eZInputValidator::STATE_INVALID || $state === eZInputValidator::STATE_INTERMEDIATE ) + { + $contentObjectAttribute->setValidationError( ezpI18n::tr( 'kernel/classes/datatypes', + "A valid file is required. The following file extensions are blacklisted: $extensionsBlackList" ) ); + return eZInputValidator::STATE_INVALID; + } + } + + if ( isset( $_FILES[$httpFileName] ) && $_FILES[$httpFileName]['tmp_name'] !== '') + { + $state = $this->FileExtensionBlackListValidator->validate( $_FILES[$httpFileName]['name'] ); + if ( $state === eZInputValidator::STATE_INVALID || $state === eZInputValidator::STATE_INTERMEDIATE ) + { + $contentObjectAttribute->setValidationError( ezpI18n::tr( 'kernel/classes/datatypes', + "A valid file is required. The following file extensions are blacklisted: $extensionsBlackList" ) ); + return eZInputValidator::STATE_INVALID; + } + } $canFetchResult = eZHTTPFile::canFetch( $httpFileName, $maxSize ); if ( $mustUpload && $canFetchResult == eZHTTPFile::UPLOADEDFILE_DOES_NOT_EXIST ) @@ -290,6 +313,11 @@ function fetchObjectAttributeHTTPInput( $http, $base, $contentObjectAttribute ) return false; } + if ( $this->validateObjectAttributeHTTPInput( $http, $base, $contentObjectAttribute ) !== eZInputValidator::STATE_ACCEPTED ) + { + return false; + } + if ( !eZHTTPFile::canFetch( $base . "_data_binaryfilename_" . $contentObjectAttribute->attribute( "id" ) ) ) return false; @@ -765,6 +793,10 @@ private function isDeletingFile( eZHTTPTool $http, eZContentObjectAttribute $con return $isDeletingFile; } + + /// \privatesection + /// The file extension blacklist validator + private $FileExtensionBlackListValidator; } eZDataType::register( eZBinaryFileType::DATA_TYPE_STRING, "eZBinaryFileType" ); diff --git a/kernel/classes/datatypes/ezimage/ezimagetype.php b/kernel/classes/datatypes/ezimage/ezimagetype.php index 9319175708c..00eb902d1a8 100644 --- a/kernel/classes/datatypes/ezimage/ezimagetype.php +++ b/kernel/classes/datatypes/ezimage/ezimagetype.php @@ -27,6 +27,7 @@ public function __construct() { parent::__construct( self::DATA_TYPE_STRING, ezpI18n::tr( 'kernel/classes/datatypes', "Image", 'Datatype name' ), array( 'serialize_supported' => true ) ); + $this->FileExtensionBlackListValidator = new eZFileExtensionBlackListValidator(); } function initializeObjectAttribute( $contentObjectAttribute, $currentVersion, $originalContentObjectAttribute ) @@ -210,16 +211,25 @@ function validateObjectAttributeHTTPInput( $http, $base, $contentObjectAttribute $maxSize = 1024 * 1024 * $classAttribute->attribute( self::FILESIZE_FIELD ); $mustUpload = false; + $tmpImgObj = $contentObjectAttribute->attribute( 'content' ); + $original = $tmpImgObj->attribute( 'original' ); if( $contentObjectAttribute->validateIsRequired() ) { - $tmpImgObj = $contentObjectAttribute->attribute( 'content' ); - $original = $tmpImgObj->attribute( 'original' ); if ( !$original['is_valid'] ) { $mustUpload = true; } } + $extensionsBlackList = implode(', ', $this->FileExtensionBlackListValidator->extensionsBlackList() ); + $state = $this->FileExtensionBlackListValidator->validate( $original['filename'] ); + if ( $state === eZInputValidator::STATE_INVALID || $state === eZInputValidator::STATE_INTERMEDIATE ) + { + $contentObjectAttribute->setValidationError( ezpI18n::tr( 'kernel/classes/datatypes', + "A valid file is required. The following file extensions are blacklisted: $extensionsBlackList" ) ); + return eZInputValidator::STATE_INVALID; + } + $canFetchResult = eZHTTPFile::canFetch( $httpFileName, $maxSize ); if ( isset( $_FILES[$httpFileName] ) and $_FILES[$httpFileName]["tmp_name"] != "" ) { @@ -231,6 +241,14 @@ function validateObjectAttributeHTTPInput( $http, $base, $contentObjectAttribute return eZInputValidator::STATE_INVALID; } + $state = $this->FileExtensionBlackListValidator->validate( $_FILES[$httpFileName]['name'] ); + if ( $state === eZInputValidator::STATE_INVALID || $state === eZInputValidator::STATE_INTERMEDIATE ) + { + $contentObjectAttribute->setValidationError( ezpI18n::tr( 'kernel/classes/datatypes', + "A valid file is required. The following file extensions are on the blacklist: $extensionsBlackList" ) ); + return eZInputValidator::STATE_INVALID; + } + if ( !self::validateImageFileExtension( $_FILES[$httpFileName]['name'] ) ) { $contentObjectAttribute->setValidationError( ezpI18n::tr( 'kernel/classes/datatypes', @@ -652,6 +670,10 @@ function postStore( $objectAttribute ) eZImageFile::appendFilepath( $objectAttributeId, $url, true ); } } + + /// \privatesection + /// The file extension blacklist validator + private $FileExtensionBlackListValidator; } eZDataType::register( eZImageType::DATA_TYPE_STRING, "eZImageType" ); diff --git a/kernel/classes/datatypes/ezmedia/ezmediatype.php b/kernel/classes/datatypes/ezmedia/ezmediatype.php index 42d1345c576..bf13a3a1f18 100644 --- a/kernel/classes/datatypes/ezmedia/ezmediatype.php +++ b/kernel/classes/datatypes/ezmedia/ezmediatype.php @@ -27,6 +27,7 @@ public function __construct() { parent::__construct( self::DATA_TYPE_STRING, ezpI18n::tr( 'kernel/classes/datatypes', "Media", 'Datatype name' ), array( 'serialize_supported' => true ) ); + $this->FileExtensionBlackListValidator = new eZFileExtensionBlackListValidator(); } /*! @@ -181,16 +182,38 @@ function validateObjectAttributeHTTPInput( $http, $base, $contentObjectAttribute $maxSize = 1024 * 1024 * $classAttribute->attribute( self::MAX_FILESIZE_FIELD ); $mustUpload = false; - if ( $contentObjectAttribute->validateIsRequired() ) + $contentObjectAttributeID = $contentObjectAttribute->attribute( 'id' ); + $version = $contentObjectAttribute->attribute( 'version' ); + $media = eZMedia::fetch( $contentObjectAttributeID, $version ); + $extensionsBlackList = implode(', ', $this->FileExtensionBlackListValidator->extensionsBlackList() ); + if ( $media === null || !$media->attribute( 'filename' ) ) { - $contentObjectAttributeID = $contentObjectAttribute->attribute( "id" ); - $version = $contentObjectAttribute->attribute( "version" ); - $media = eZMedia::fetch( $contentObjectAttributeID, $version ); - if ( $media === null || !$media->attribute( 'filename' ) ) + if ( $contentObjectAttribute->validateIsRequired() ) { $mustUpload = true; } } + else + { + $state = $this->FileExtensionBlackListValidator->validate( $media->attribute( 'filename' ) ); + if ( $state === eZInputValidator::STATE_INVALID || $state === eZInputValidator::STATE_INTERMEDIATE ) + { + $contentObjectAttribute->setValidationError( ezpI18n::tr( 'kernel/classes/datatypes', + "A valid file is required. The following file extensions are blacklisted: $extensionsBlackList" ) ); + return eZInputValidator::STATE_INVALID; + } + } + + if ( isset( $_FILES[$httpFileName] ) && $_FILES[$httpFileName]['tmp_name'] !== '') + { + $state = $this->FileExtensionBlackListValidator->validate( $_FILES[$httpFileName]['name'] ); + if ( $state === eZInputValidator::STATE_INVALID || $state === eZInputValidator::STATE_INTERMEDIATE ) + { + $contentObjectAttribute->setValidationError( ezpI18n::tr( 'kernel/classes/datatypes', + "A valid file is required. The following file extensions are blacklisted: $extensionsBlackList" ) ); + return eZInputValidator::STATE_INVALID; + } + } $canFetchResult = eZHTTPFile::canFetch( $httpFileName, $maxSize ); if ( $mustUpload && $canFetchResult == eZHTTPFile::UPLOADEDFILE_DOES_NOT_EXIST ) @@ -274,6 +297,11 @@ function fetchObjectAttributeHTTPInput( $http, $base, $contentObjectAttribute ) eZMediaType::checkFileUploads(); + if ( $this->validateObjectAttributeHTTPInput( $http, $base, $contentObjectAttribute ) !== eZInputValidator::STATE_ACCEPTED ) + { + return false; + } + $classAttribute = $contentObjectAttribute->contentClassAttribute(); $player = $classAttribute->attribute( "data_text1" ); $pluginPage = eZMediaType::pluginPage( $player ); @@ -799,6 +827,10 @@ function supportsBatchInitializeObjectAttribute() { return true; } + + /// \privatesection + /// The file extension blacklist validator + private $FileExtensionBlackListValidator; } eZDataType::register( eZMediaType::DATA_TYPE_STRING, "eZMediaType" ); diff --git a/lib/ezutils/classes/ezfileextensionblacklistvalidator.php b/lib/ezutils/classes/ezfileextensionblacklistvalidator.php new file mode 100644 index 00000000000..c81aca8d7c7 --- /dev/null +++ b/lib/ezutils/classes/ezfileextensionblacklistvalidator.php @@ -0,0 +1,58 @@ +constraints['extensionsBlackList'] = $fileIni->variable('FileSettings','FileExtensionBlackList'); + } + + /*! + Tries to validate to the filename \a $filename and returns one of the validator states + eZInputValidator::STATE_ACCEPTED, eZInputValidator::STATE_INTERMEDIATE or + eZInputValidator::STATE_INVALID. + */ + function validate( $filename ) + { + if ( + pathinfo($filename, PATHINFO_BASENAME) !== $filename || + in_array(strtolower(pathinfo($filename, PATHINFO_EXTENSION)), $this->constraints['extensionsBlackList'], true) + ) { + return eZInputValidator::STATE_INVALID; + } + + return eZInputValidator::STATE_ACCEPTED; + } + + /*! + Return the list of blacklisted file extensions. + */ + function extensionsBlackList() + { + return $this->constraints['extensionsBlackList']; + } + + /// \privatesection + protected $constraints = array( + 'extensionsBlackList' => array(), + ); +} diff --git a/settings/file.ini b/settings/file.ini index cd5ef60325e..1a5595a419c 100644 --- a/settings/file.ini +++ b/settings/file.ini @@ -33,6 +33,17 @@ Handlers[gzip]=ezgzipcompressionhandler Handlers[gzipzlib]=ezgzipzlibcompressionhandler Handlers[gzipshell]=ezgzipshellcompressionhandler +# The file extension blacklist is used to validate uploaded files. +# File types on the blacklist are rejected, for security or other reasons. +FileExtensionBlackList[] +FileExtensionBlackList[]=php +FileExtensionBlackList[]=php3 +FileExtensionBlackList[]=phar +FileExtensionBlackList[]=phpt +FileExtensionBlackList[]=pht +FileExtensionBlackList[]=phtml +FileExtensionBlackList[]=pgif + [ClusteringSettings] # Cluster file handler. # Since 4.1 name of the filehandlers have changed From 8d4e0f0ef5570d494ebf102c86463a76c8acc606 Mon Sep 17 00:00:00 2001 From: Gunnstein Lye Date: Tue, 3 Mar 2020 15:55:06 +0000 Subject: [PATCH 09/11] Fixed wrong old-style constructor usage --- lib/ezutils/classes/ezfileextensionblacklistvalidator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/ezutils/classes/ezfileextensionblacklistvalidator.php b/lib/ezutils/classes/ezfileextensionblacklistvalidator.php index c81aca8d7c7..d1ad84d911e 100644 --- a/lib/ezutils/classes/ezfileextensionblacklistvalidator.php +++ b/lib/ezutils/classes/ezfileextensionblacklistvalidator.php @@ -20,7 +20,7 @@ class eZFileExtensionBlackListValidator extends eZInputValidator */ function __construct() { - parent::eZInputValidator(); + parent::__construct(); $fileIni = eZINI::instance('file.ini'); $this->constraints['extensionsBlackList'] = $fileIni->variable('FileSettings','FileExtensionBlackList'); From eb5a66b0f1eead906c98f581be5d8e1e0afa46f8 Mon Sep 17 00:00:00 2001 From: Gunnstein Lye Date: Tue, 3 Mar 2020 16:18:30 +0000 Subject: [PATCH 10/11] Removed invalid constructor call --- lib/ezutils/classes/ezfileextensionblacklistvalidator.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/lib/ezutils/classes/ezfileextensionblacklistvalidator.php b/lib/ezutils/classes/ezfileextensionblacklistvalidator.php index d1ad84d911e..452ba0e9a01 100644 --- a/lib/ezutils/classes/ezfileextensionblacklistvalidator.php +++ b/lib/ezutils/classes/ezfileextensionblacklistvalidator.php @@ -20,8 +20,6 @@ class eZFileExtensionBlackListValidator extends eZInputValidator */ function __construct() { - parent::__construct(); - $fileIni = eZINI::instance('file.ini'); $this->constraints['extensionsBlackList'] = $fileIni->variable('FileSettings','FileExtensionBlackList'); } From fe61c8ae5331ac61af26fba8cdabe8c07d83dfa6 Mon Sep 17 00:00:00 2001 From: Stian Torjussen <53934716+StianTorjussen@users.noreply.github.com> Date: Wed, 18 Mar 2020 17:18:01 +0100 Subject: [PATCH 11/11] PHP7.2 fix: Added array check before count. (#1454) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Added array check before counting * Apply suggestions from code review Check for existence of post variable Co-Authored-By: André R. <289757+andrerom@users.noreply.github.com> --- .../ezobjectrelationlist/ezobjectrelationlisttype.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/classes/datatypes/ezobjectrelationlist/ezobjectrelationlisttype.php b/kernel/classes/datatypes/ezobjectrelationlist/ezobjectrelationlisttype.php index fccdac0a6fd..1b0f341611e 100644 --- a/kernel/classes/datatypes/ezobjectrelationlist/ezobjectrelationlisttype.php +++ b/kernel/classes/datatypes/ezobjectrelationlist/ezobjectrelationlisttype.php @@ -76,7 +76,8 @@ function validateObjectAttributeHTTPInput( $http, $base, $contentObjectAttribute // If in browse mode and relations have been added using the search field // items are stored in the post variable if ( - $http->postVariable( $postVariableName ) != array( "no_relation" ) + $http->hasPostVariable( $postVariableName ) + && $http->postVariable( $postVariableName ) != array( "no_relation" ) && count( $http->postVariable( $postVariableName ) ) > 0 ) {