diff --git a/composer.json b/composer.json index d0afa4f4927f6a7326543b78e6ea39b65cca77b8..9487be5030f5faf5a9c8bc3705fcc28819b256ee 100644 --- a/composer.json +++ b/composer.json @@ -84,7 +84,7 @@ "drupal/config_update": "^1.5", "drupal/console": "^1", "drupal/content_access": "1.0-alpha1", - "drupal/core": "8.6.2", + "drupal/core": "^8", "drupal/crop": "2.0-rc1", "drupal/ctools": "3.0", "drupal/ctools_views": "3.0", diff --git a/composer.lock b/composer.lock index 01b126a006a4d2306a55aec6d3ce9f12292e7049..a17af02b90a5f352661add2af218948777679730 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", "This file is @generated automatically" ], - "content-hash": "63d43b920a506eddccb3d90debb5fd5c", + "content-hash": "c7f966be2bf443d90f7fc6bea9496524", "packages": [ { "name": "alchemy/zippy", @@ -2376,16 +2376,16 @@ }, { "name": "drupal/core", - "version": "8.6.2", + "version": "8.6.6", "source": { "type": "git", "url": "https://github.com/drupal/core.git", - "reference": "356292934802bb1aecc478e88a3cba77442d7c62" + "reference": "6736973f399a3a9ac8ecd41f3a159e1153f7ee39" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/drupal/core/zipball/356292934802bb1aecc478e88a3cba77442d7c62", - "reference": "356292934802bb1aecc478e88a3cba77442d7c62", + "url": "https://api.github.com/repos/drupal/core/zipball/6736973f399a3a9ac8ecd41f3a159e1153f7ee39", + "reference": "6736973f399a3a9ac8ecd41f3a159e1153f7ee39", "shasum": "" }, "require": { @@ -2429,6 +2429,7 @@ "symfony/validator": "~3.4.0", "symfony/yaml": "~3.4.5", "twig/twig": "^1.35.0", + "typo3/phar-stream-wrapper": "^2.0.1", "zendframework/zend-diactoros": "^1.1", "zendframework/zend-feed": "^2.4" }, @@ -2551,7 +2552,7 @@ "jcalderonzumba/gastonjs": "^1.0.2", "jcalderonzumba/mink-phantomjs-driver": "^0.3.1", "mikey179/vfsstream": "^1.2", - "phpspec/prophecy": "^1.4", + "phpspec/prophecy": "^1.7", "phpunit/phpunit": "^4.8.35 || ^6.5", "symfony/css-selector": "^3.4.0", "symfony/debug": "^3.4.0", @@ -2613,7 +2614,7 @@ "GPL-2.0-or-later" ], "description": "Drupal is an open source content management platform powering millions of websites and applications.", - "time": "2018-10-17T22:19:50+00:00" + "time": "2019-01-15T17:41:52+00:00" }, { "name": "drupal/crop", @@ -9873,6 +9874,46 @@ ], "time": "2018-03-20T04:25:58+00:00" }, + { + "name": "typo3/phar-stream-wrapper", + "version": "v2.0.1", + "source": { + "type": "git", + "url": "https://github.com/TYPO3/phar-stream-wrapper.git", + "reference": "0469d9fefa0146ea4299d3b11cfbb76faa7045bf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/TYPO3/phar-stream-wrapper/zipball/0469d9fefa0146ea4299d3b11cfbb76faa7045bf", + "reference": "0469d9fefa0146ea4299d3b11cfbb76faa7045bf", + "shasum": "" + }, + "require": { + "php": "^5.3.3|^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.36" + }, + "type": "library", + "autoload": { + "psr-4": { + "TYPO3\\PharStreamWrapper\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Interceptors for PHP's native phar:// stream handling", + "homepage": "https://typo3.org/", + "keywords": [ + "phar", + "php", + "security", + "stream-wrapper" + ], + "time": "2018-10-18T08:46:28+00:00" + }, { "name": "webflo/drupal-core-strict", "version": "8.5.6", diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php index ac1f61ed5469c98b4e5f56cfdc23514f29e82cba..660e8053f189bef13a63e565c265ef247540c928 100644 --- a/vendor/composer/autoload_classmap.php +++ b/vendor/composer/autoload_classmap.php @@ -653,6 +653,7 @@ 'Drupal\\Component\\Utility\\Environment' => $baseDir . '/web/core/lib/Drupal/Component/Utility/Environment.php', 'Drupal\\Component\\Utility\\Html' => $baseDir . '/web/core/lib/Drupal/Component/Utility/Html.php', 'Drupal\\Component\\Utility\\Image' => $baseDir . '/web/core/lib/Drupal/Component/Utility/Image.php', + 'Drupal\\Component\\Utility\\Mail' => $baseDir . '/web/core/lib/Drupal/Component/Utility/Mail.php', 'Drupal\\Component\\Utility\\NestedArray' => $baseDir . '/web/core/lib/Drupal/Component/Utility/NestedArray.php', 'Drupal\\Component\\Utility\\Number' => $baseDir . '/web/core/lib/Drupal/Component/Utility/Number.php', 'Drupal\\Component\\Utility\\OpCodeCache' => $baseDir . '/web/core/lib/Drupal/Component/Utility/OpCodeCache.php', @@ -2230,6 +2231,7 @@ 'Drupal\\Core\\Render\\Placeholder\\SingleFlushStrategy' => $baseDir . '/web/core/lib/Drupal/Core/Render/Placeholder/SingleFlushStrategy.php', 'Drupal\\Core\\Render\\PlaceholderingRenderCache' => $baseDir . '/web/core/lib/Drupal/Core/Render/PlaceholderingRenderCache.php', 'Drupal\\Core\\Render\\Plugin\\DisplayVariant\\SimplePageVariant' => $baseDir . '/web/core/lib/Drupal/Core/Render/Plugin/DisplayVariant/SimplePageVariant.php', + 'Drupal\\Core\\Render\\PreviewFallbackInterface' => $baseDir . '/web/core/lib/Drupal/Core/Render/PreviewFallbackInterface.php', 'Drupal\\Core\\Render\\RenderCache' => $baseDir . '/web/core/lib/Drupal/Core/Render/RenderCache.php', 'Drupal\\Core\\Render\\RenderCacheInterface' => $baseDir . '/web/core/lib/Drupal/Core/Render/RenderCacheInterface.php', 'Drupal\\Core\\Render\\RenderContext' => $baseDir . '/web/core/lib/Drupal/Core/Render/RenderContext.php', @@ -2294,6 +2296,7 @@ 'Drupal\\Core\\Routing\\UrlGeneratorInterface' => $baseDir . '/web/core/lib/Drupal/Core/Routing/UrlGeneratorInterface.php', 'Drupal\\Core\\Routing\\UrlGeneratorTrait' => $baseDir . '/web/core/lib/Drupal/Core/Routing/UrlGeneratorTrait.php', 'Drupal\\Core\\Routing\\UrlMatcher' => $baseDir . '/web/core/lib/Drupal/Core/Routing/UrlMatcher.php', + 'Drupal\\Core\\Security\\PharExtensionInterceptor' => $baseDir . '/web/core/lib/Drupal/Core/Security/PharExtensionInterceptor.php', 'Drupal\\Core\\Security\\RequestSanitizer' => $baseDir . '/web/core/lib/Drupal/Core/Security/RequestSanitizer.php', 'Drupal\\Core\\Serialization\\Yaml' => $baseDir . '/web/core/lib/Drupal/Core/Serialization/Yaml.php', 'Drupal\\Core\\Session\\AccountInterface' => $baseDir . '/web/core/lib/Drupal/Core/Session/AccountInterface.php', @@ -4524,6 +4527,13 @@ 'Symfony\\Polyfill\\Iconv\\Iconv' => $vendorDir . '/symfony/polyfill-iconv/Iconv.php', 'Symfony\\Polyfill\\Mbstring\\Mbstring' => $vendorDir . '/symfony/polyfill-mbstring/Mbstring.php', 'Symfony\\Polyfill\\Php70\\Php70' => $vendorDir . '/symfony/polyfill-php70/Php70.php', + 'TYPO3\\PharStreamWrapper\\Assertable' => $vendorDir . '/typo3/phar-stream-wrapper/src/Assertable.php', + 'TYPO3\\PharStreamWrapper\\Behavior' => $vendorDir . '/typo3/phar-stream-wrapper/src/Behavior.php', + 'TYPO3\\PharStreamWrapper\\Exception' => $vendorDir . '/typo3/phar-stream-wrapper/src/Exception.php', + 'TYPO3\\PharStreamWrapper\\Helper' => $vendorDir . '/typo3/phar-stream-wrapper/src/Helper.php', + 'TYPO3\\PharStreamWrapper\\Interceptor\\PharExtensionInterceptor' => $vendorDir . '/typo3/phar-stream-wrapper/src/Interceptor/PharExtensionInterceptor.php', + 'TYPO3\\PharStreamWrapper\\Manager' => $vendorDir . '/typo3/phar-stream-wrapper/src/Manager.php', + 'TYPO3\\PharStreamWrapper\\PharStreamWrapper' => $vendorDir . '/typo3/phar-stream-wrapper/src/PharStreamWrapper.php', 'Twig\\Cache\\CacheInterface' => $vendorDir . '/twig/twig/src/Cache/CacheInterface.php', 'Twig\\Cache\\FilesystemCache' => $vendorDir . '/twig/twig/src/Cache/FilesystemCache.php', 'Twig\\Cache\\NullCache' => $vendorDir . '/twig/twig/src/Cache/NullCache.php', diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php index f483260e4130dddf38224a8b09e6ccd5b2b70a12..ca49397094920513f50c37f11055e261353a8d29 100644 --- a/vendor/composer/autoload_psr4.php +++ b/vendor/composer/autoload_psr4.php @@ -19,6 +19,7 @@ 'Webmozart\\Assert\\' => array($vendorDir . '/webmozart/assert/src'), 'Twig\\Extensions\\' => array($vendorDir . '/twig/extensions/src'), 'Twig\\' => array($vendorDir . '/twig/twig/src'), + 'TYPO3\\PharStreamWrapper\\' => array($vendorDir . '/typo3/phar-stream-wrapper/src'), 'Symfony\\Polyfill\\Php70\\' => array($vendorDir . '/symfony/polyfill-php70'), 'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'), 'Symfony\\Polyfill\\Iconv\\' => array($vendorDir . '/symfony/polyfill-iconv'), diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index 07e4e3629d9d30e8c7f9ad396488680e52fcc015..3fb16ee6db8aec04c112fa941572a336544782d7 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -53,6 +53,7 @@ class ComposerStaticInit5c689ffcd54b9e495ed983fdce09b530 array ( 'Twig\\Extensions\\' => 16, 'Twig\\' => 5, + 'TYPO3\\PharStreamWrapper\\' => 24, ), 'S' => array ( @@ -198,6 +199,10 @@ class ComposerStaticInit5c689ffcd54b9e495ed983fdce09b530 array ( 0 => __DIR__ . '/..' . '/twig/twig/src', ), + 'TYPO3\\PharStreamWrapper\\' => + array ( + 0 => __DIR__ . '/..' . '/typo3/phar-stream-wrapper/src', + ), 'Symfony\\Polyfill\\Php70\\' => array ( 0 => __DIR__ . '/..' . '/symfony/polyfill-php70', @@ -1173,6 +1178,7 @@ class ComposerStaticInit5c689ffcd54b9e495ed983fdce09b530 'Drupal\\Component\\Utility\\Environment' => __DIR__ . '/../..' . '/web/core/lib/Drupal/Component/Utility/Environment.php', 'Drupal\\Component\\Utility\\Html' => __DIR__ . '/../..' . '/web/core/lib/Drupal/Component/Utility/Html.php', 'Drupal\\Component\\Utility\\Image' => __DIR__ . '/../..' . '/web/core/lib/Drupal/Component/Utility/Image.php', + 'Drupal\\Component\\Utility\\Mail' => __DIR__ . '/../..' . '/web/core/lib/Drupal/Component/Utility/Mail.php', 'Drupal\\Component\\Utility\\NestedArray' => __DIR__ . '/../..' . '/web/core/lib/Drupal/Component/Utility/NestedArray.php', 'Drupal\\Component\\Utility\\Number' => __DIR__ . '/../..' . '/web/core/lib/Drupal/Component/Utility/Number.php', 'Drupal\\Component\\Utility\\OpCodeCache' => __DIR__ . '/../..' . '/web/core/lib/Drupal/Component/Utility/OpCodeCache.php', @@ -2750,6 +2756,7 @@ class ComposerStaticInit5c689ffcd54b9e495ed983fdce09b530 'Drupal\\Core\\Render\\Placeholder\\SingleFlushStrategy' => __DIR__ . '/../..' . '/web/core/lib/Drupal/Core/Render/Placeholder/SingleFlushStrategy.php', 'Drupal\\Core\\Render\\PlaceholderingRenderCache' => __DIR__ . '/../..' . '/web/core/lib/Drupal/Core/Render/PlaceholderingRenderCache.php', 'Drupal\\Core\\Render\\Plugin\\DisplayVariant\\SimplePageVariant' => __DIR__ . '/../..' . '/web/core/lib/Drupal/Core/Render/Plugin/DisplayVariant/SimplePageVariant.php', + 'Drupal\\Core\\Render\\PreviewFallbackInterface' => __DIR__ . '/../..' . '/web/core/lib/Drupal/Core/Render/PreviewFallbackInterface.php', 'Drupal\\Core\\Render\\RenderCache' => __DIR__ . '/../..' . '/web/core/lib/Drupal/Core/Render/RenderCache.php', 'Drupal\\Core\\Render\\RenderCacheInterface' => __DIR__ . '/../..' . '/web/core/lib/Drupal/Core/Render/RenderCacheInterface.php', 'Drupal\\Core\\Render\\RenderContext' => __DIR__ . '/../..' . '/web/core/lib/Drupal/Core/Render/RenderContext.php', @@ -2814,6 +2821,7 @@ class ComposerStaticInit5c689ffcd54b9e495ed983fdce09b530 'Drupal\\Core\\Routing\\UrlGeneratorInterface' => __DIR__ . '/../..' . '/web/core/lib/Drupal/Core/Routing/UrlGeneratorInterface.php', 'Drupal\\Core\\Routing\\UrlGeneratorTrait' => __DIR__ . '/../..' . '/web/core/lib/Drupal/Core/Routing/UrlGeneratorTrait.php', 'Drupal\\Core\\Routing\\UrlMatcher' => __DIR__ . '/../..' . '/web/core/lib/Drupal/Core/Routing/UrlMatcher.php', + 'Drupal\\Core\\Security\\PharExtensionInterceptor' => __DIR__ . '/../..' . '/web/core/lib/Drupal/Core/Security/PharExtensionInterceptor.php', 'Drupal\\Core\\Security\\RequestSanitizer' => __DIR__ . '/../..' . '/web/core/lib/Drupal/Core/Security/RequestSanitizer.php', 'Drupal\\Core\\Serialization\\Yaml' => __DIR__ . '/../..' . '/web/core/lib/Drupal/Core/Serialization/Yaml.php', 'Drupal\\Core\\Session\\AccountInterface' => __DIR__ . '/../..' . '/web/core/lib/Drupal/Core/Session/AccountInterface.php', @@ -5044,6 +5052,13 @@ class ComposerStaticInit5c689ffcd54b9e495ed983fdce09b530 'Symfony\\Polyfill\\Iconv\\Iconv' => __DIR__ . '/..' . '/symfony/polyfill-iconv/Iconv.php', 'Symfony\\Polyfill\\Mbstring\\Mbstring' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/Mbstring.php', 'Symfony\\Polyfill\\Php70\\Php70' => __DIR__ . '/..' . '/symfony/polyfill-php70/Php70.php', + 'TYPO3\\PharStreamWrapper\\Assertable' => __DIR__ . '/..' . '/typo3/phar-stream-wrapper/src/Assertable.php', + 'TYPO3\\PharStreamWrapper\\Behavior' => __DIR__ . '/..' . '/typo3/phar-stream-wrapper/src/Behavior.php', + 'TYPO3\\PharStreamWrapper\\Exception' => __DIR__ . '/..' . '/typo3/phar-stream-wrapper/src/Exception.php', + 'TYPO3\\PharStreamWrapper\\Helper' => __DIR__ . '/..' . '/typo3/phar-stream-wrapper/src/Helper.php', + 'TYPO3\\PharStreamWrapper\\Interceptor\\PharExtensionInterceptor' => __DIR__ . '/..' . '/typo3/phar-stream-wrapper/src/Interceptor/PharExtensionInterceptor.php', + 'TYPO3\\PharStreamWrapper\\Manager' => __DIR__ . '/..' . '/typo3/phar-stream-wrapper/src/Manager.php', + 'TYPO3\\PharStreamWrapper\\PharStreamWrapper' => __DIR__ . '/..' . '/typo3/phar-stream-wrapper/src/PharStreamWrapper.php', 'Twig\\Cache\\CacheInterface' => __DIR__ . '/..' . '/twig/twig/src/Cache/CacheInterface.php', 'Twig\\Cache\\FilesystemCache' => __DIR__ . '/..' . '/twig/twig/src/Cache/FilesystemCache.php', 'Twig\\Cache\\NullCache' => __DIR__ . '/..' . '/twig/twig/src/Cache/NullCache.php', diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index d845a313fe1de76c9999ed40a1952024d540bd67..0ce9ca16418187898358700e636cf28f4705603c 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -2455,17 +2455,17 @@ }, { "name": "drupal/core", - "version": "8.6.2", - "version_normalized": "8.6.2.0", + "version": "8.6.6", + "version_normalized": "8.6.6.0", "source": { "type": "git", "url": "https://github.com/drupal/core.git", - "reference": "356292934802bb1aecc478e88a3cba77442d7c62" + "reference": "6736973f399a3a9ac8ecd41f3a159e1153f7ee39" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/drupal/core/zipball/356292934802bb1aecc478e88a3cba77442d7c62", - "reference": "356292934802bb1aecc478e88a3cba77442d7c62", + "url": "https://api.github.com/repos/drupal/core/zipball/6736973f399a3a9ac8ecd41f3a159e1153f7ee39", + "reference": "6736973f399a3a9ac8ecd41f3a159e1153f7ee39", "shasum": "" }, "require": { @@ -2509,6 +2509,7 @@ "symfony/validator": "~3.4.0", "symfony/yaml": "~3.4.5", "twig/twig": "^1.35.0", + "typo3/phar-stream-wrapper": "^2.0.1", "zendframework/zend-diactoros": "^1.1", "zendframework/zend-feed": "^2.4" }, @@ -2631,13 +2632,13 @@ "jcalderonzumba/gastonjs": "^1.0.2", "jcalderonzumba/mink-phantomjs-driver": "^0.3.1", "mikey179/vfsstream": "^1.2", - "phpspec/prophecy": "^1.4", + "phpspec/prophecy": "^1.7", "phpunit/phpunit": "^4.8.35 || ^6.5", "symfony/css-selector": "^3.4.0", "symfony/debug": "^3.4.0", "symfony/phpunit-bridge": "^3.4.3" }, - "time": "2018-10-17T22:19:50+00:00", + "time": "2019-01-15T17:41:52+00:00", "type": "drupal-core", "extra": { "merge-plugin": { @@ -10197,6 +10198,48 @@ "templating" ] }, + { + "name": "typo3/phar-stream-wrapper", + "version": "v2.0.1", + "version_normalized": "2.0.1.0", + "source": { + "type": "git", + "url": "https://github.com/TYPO3/phar-stream-wrapper.git", + "reference": "0469d9fefa0146ea4299d3b11cfbb76faa7045bf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/TYPO3/phar-stream-wrapper/zipball/0469d9fefa0146ea4299d3b11cfbb76faa7045bf", + "reference": "0469d9fefa0146ea4299d3b11cfbb76faa7045bf", + "shasum": "" + }, + "require": { + "php": "^5.3.3|^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.36" + }, + "time": "2018-10-18T08:46:28+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "TYPO3\\PharStreamWrapper\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Interceptors for PHP's native phar:// stream handling", + "homepage": "https://typo3.org/", + "keywords": [ + "phar", + "php", + "security", + "stream-wrapper" + ] + }, { "name": "webflo/drupal-core-strict", "version": "8.5.6", diff --git a/vendor/typo3/phar-stream-wrapper/.gitignore b/vendor/typo3/phar-stream-wrapper/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..157ff0c59898127374760bc70c525dd0d727d710 --- /dev/null +++ b/vendor/typo3/phar-stream-wrapper/.gitignore @@ -0,0 +1,3 @@ +.idea +vendor/ +composer.lock diff --git a/vendor/typo3/phar-stream-wrapper/LICENSE b/vendor/typo3/phar-stream-wrapper/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..d71267a1adb5529500cd0750e6d1d09544fadd38 --- /dev/null +++ b/vendor/typo3/phar-stream-wrapper/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 TYPO3 project - https://typo3.org/ + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/typo3/phar-stream-wrapper/README.md b/vendor/typo3/phar-stream-wrapper/README.md new file mode 100644 index 0000000000000000000000000000000000000000..b632784bddaeb2343b7a199a7bf13720313c7bf5 --- /dev/null +++ b/vendor/typo3/phar-stream-wrapper/README.md @@ -0,0 +1,155 @@ +[](https://scrutinizer-ci.com/g/TYPO3/phar-stream-wrapper/?branch=v2) +[](https://travis-ci.org/TYPO3/phar-stream-wrapper) + +# PHP Phar Stream Wrapper + +## Abstract & History + +Based on Sam Thomas' findings concerning +[insecure deserialization in combination with obfuscation strategies](https://blog.secarma.co.uk/labs/near-phar-dangerous-unserialization-wherever-you-are) +allowing to hide Phar files inside valid image resources, the TYPO3 project +decided back then to introduce a `PharStreamWrapper` to intercept invocations +of the `phar://` stream in PHP and only allow usage for defined locations in +the file system. + +Since the TYPO3 mission statement is **inspiring people to share**, we thought +it would be helpful for others to release our `PharStreamWrapper` as standalone +package to the PHP community. + +The mentioned security issue was reported to TYPO3 on 10th June 2018 by Sam Thomas +and has been addressed concerning the specific attack vector and for this generic +`PharStreamWrapper` in TYPO3 versions 7.6.30 LTS, 8.7.17 LTS and 9.3.1 on 12th +July 2018. + +* https://typo3.org/security/advisory/typo3-core-sa-2018-002/ +* https://blog.secarma.co.uk/labs/near-phar-dangerous-unserialization-wherever-you-are +* https://youtu.be/GePBmsNJw6Y + +## License + +In general the TYPO3 core is released under the GNU General Public License version +2 or any later version (`GPL-2.0-or-later`). In order to avoid licensing issues and +incompatibilities this `PharStreamWrapper` is licenced under the MIT License. In case +you duplicate or modify source code, credits are not required but really appreciated. + +## Credits + +Thanks to [Alex Pott](https://github.com/alexpott), Drupal for creating +back-ports of all sources in order to provide compatibility with PHP v5.3. + +## Installation + +The `PharStreamWrapper` is provided as composer package `typo3/phar-stream-wrapper` +and has minimum requirements of PHP v5.3 ([`v2`](https://github.com/TYPO3/phar-stream-wrapper/tree/v2) branch) and PHP v7.0 ([`master`](https://github.com/TYPO3/phar-stream-wrapper) branch). + +### Installation for PHP v7.0 + +``` +composer require typo3/phar-stream-wrapper ^3.0 +``` + +### Installation for PHP v5.3 + +``` +composer require typo3/phar-stream-wrapper ^2.0 +``` + +## Example + +The following example is bundled within this package, the shown +`PharExtensionInterceptor` denies all stream wrapper invocations files +not having the `.phar` suffix. Interceptor logic has to be individual and +adjusted to according requirements. + +``` +$behavior = new \TYPO3\PharStreamWrapper\Behavior(); +Manager::initialize( + $behavior->withAssertion(new PharExtensionInterceptor()) +); + +if (in_array('phar', stream_get_wrappers())) { + stream_wrapper_unregister('phar'); + stream_wrapper_register('phar', 'TYPO3\\PharStreamWrapper\\PharStreamWrapper'); +} +``` + +* `PharStreamWrapper` defined as class reference will be instantiated each time + `phar://` streams shall be processed. +* `Manager` as singleton pattern being called by `PharStreamWrapper` instances + in order to retrieve individual behavior and settings. +* `Behavior` holds reference to interceptor(s) that shall assert correct/allowed + invocation of a given `$path` for a given `$command`. Interceptors implement + the interface `Assertable`. Interceptors can act individually on following + commands or handle all of them in case not defined specifically: + + `COMMAND_DIR_OPENDIR` + + `COMMAND_MKDIR` + + `COMMAND_RENAME` + + `COMMAND_RMDIR` + + `COMMAND_STEAM_METADATA` + + `COMMAND_STREAM_OPEN` + + `COMMAND_UNLINK` + + `COMMAND_URL_STAT` + +## Interceptor + +The following interceptor is shipped with the package and ready to use in order +to block any Phar invocation of files not having a `.phar` suffix. Besides that +individual interceptors are possible of course. + +``` +class PharExtensionInterceptor implements Assertable +{ + /** + * Determines whether the base file name has a ".phar" suffix. + * + * @param string $path + * @param string $command + * @return bool + * @throws Exception + */ + public function assert($path, $command) + { + if ($this->baseFileContainsPharExtension($path)) { + return true; + } + throw new Exception( + sprintf( + 'Unexpected file extension in "%s"', + $path + ), + 1535198703 + ); + } + + /** + * @param string $path + * @return bool + */ + private function baseFileContainsPharExtension($path) + { + $baseFile = Helper::determineBaseFile($path); + if ($baseFile === null) { + return false; + } + $fileExtension = pathinfo($baseFile, PATHINFO_EXTENSION); + return strtolower($fileExtension) === 'phar'; + } +} +``` + +## Helper + +* `Helper::determineBaseFile(string $path)`: Determines base file that can be + accessed using the regular file system. For instance the following path + `phar:///home/user/bundle.phar/content.txt` would be resolved to + `/home/user/bundle.phar`. +* `Helper::resetOpCache()`: Resets PHP's OPcache if enabled as work-around for + issues in `include()` or `require()` calls and OPcache delivering wrong + results. More details can be found in PHP's bug tracker, for instance like + https://bugs.php.net/bug.php?id=66569 + +## Security Contact + +In case of finding additional security issues in the TYPO3 project or in this +`PharStreamWrapper` package in particular, please get in touch with the +[TYPO3 Security Team](mailto:security@typo3.org). diff --git a/vendor/typo3/phar-stream-wrapper/composer.json b/vendor/typo3/phar-stream-wrapper/composer.json new file mode 100644 index 0000000000000000000000000000000000000000..d308f8c87413aba41ea6d47f8a2f85d0c707f36e --- /dev/null +++ b/vendor/typo3/phar-stream-wrapper/composer.json @@ -0,0 +1,24 @@ +{ + "name": "typo3/phar-stream-wrapper", + "description": "Interceptors for PHP's native phar:// stream handling", + "type": "library", + "license": "MIT", + "homepage": "https://typo3.org/", + "keywords": ["php", "phar", "stream-wrapper", "security"], + "require": { + "php": "^5.3.3|^7.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.36" + }, + "autoload": { + "psr-4": { + "TYPO3\\PharStreamWrapper\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "TYPO3\\PharStreamWrapper\\Tests\\": "tests/" + } + } +} diff --git a/vendor/typo3/phar-stream-wrapper/src/Assertable.php b/vendor/typo3/phar-stream-wrapper/src/Assertable.php new file mode 100644 index 0000000000000000000000000000000000000000..a21b1da2ab926b8bb2cbe6508ab368e78c826b0e --- /dev/null +++ b/vendor/typo3/phar-stream-wrapper/src/Assertable.php @@ -0,0 +1,22 @@ +<?php +namespace TYPO3\PharStreamWrapper; + +/* + * This file is part of the TYPO3 project. + * + * It is free software; you can redistribute it and/or modify it under the terms + * of the MIT License (MIT). For the full copyright and license information, + * please read the LICENSE file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +interface Assertable +{ + /** + * @param string $path + * @param string $command + * @return bool + */ + public function assert($path, $command); +} diff --git a/vendor/typo3/phar-stream-wrapper/src/Behavior.php b/vendor/typo3/phar-stream-wrapper/src/Behavior.php new file mode 100644 index 0000000000000000000000000000000000000000..0b640efd46327a98ee1bd9d79d06cb425025d532 --- /dev/null +++ b/vendor/typo3/phar-stream-wrapper/src/Behavior.php @@ -0,0 +1,124 @@ +<?php +namespace TYPO3\PharStreamWrapper; + +/* + * This file is part of the TYPO3 project. + * + * It is free software; you can redistribute it and/or modify it under the terms + * of the MIT License (MIT). For the full copyright and license information, + * please read the LICENSE file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +class Behavior implements Assertable +{ + const COMMAND_DIR_OPENDIR = 'dir_opendir'; + const COMMAND_MKDIR = 'mkdir'; + const COMMAND_RENAME = 'rename'; + const COMMAND_RMDIR = 'rmdir'; + const COMMAND_STEAM_METADATA = 'stream_metadata'; + const COMMAND_STREAM_OPEN = 'stream_open'; + const COMMAND_UNLINK = 'unlink'; + const COMMAND_URL_STAT = 'url_stat'; + + /** + * @var string[] + */ + private $availableCommands = array( + self::COMMAND_DIR_OPENDIR, + self::COMMAND_MKDIR, + self::COMMAND_RENAME, + self::COMMAND_RMDIR, + self::COMMAND_STEAM_METADATA, + self::COMMAND_STREAM_OPEN, + self::COMMAND_UNLINK, + self::COMMAND_URL_STAT, + ); + + /** + * @var Assertable[] + */ + private $assertions; + + /** + * @param Assertable $assertable + * @return static + */ + public function withAssertion(Assertable $assertable) + { + $commands = func_get_args(); + array_shift($commands); + $this->assertCommands($commands); + $commands = $commands ?: $this->availableCommands; + + $target = clone $this; + foreach ($commands as $command) { + $target->assertions[$command] = $assertable; + } + return $target; + } + + /** + * @param string $path + * @param string $command + * @return bool + */ + public function assert($path, $command) + { + $this->assertCommand($command); + $this->assertAssertionCompleteness(); + + return $this->assertions[$command]->assert($path, $command); + } + + /** + * @param array $commands + */ + private function assertCommands(array $commands) + { + $unknownCommands = array_diff($commands, $this->availableCommands); + if (empty($unknownCommands)) { + return; + } + throw new \LogicException( + sprintf( + 'Unknown commands: %s', + implode(', ', $unknownCommands) + ), + 1535189881 + ); + } + + private function assertCommand($command) + { + if (in_array($command, $this->availableCommands, true)) { + return; + } + throw new \LogicException( + sprintf( + 'Unknown command "%s"', + $command + ), + 1535189882 + ); + } + + private function assertAssertionCompleteness() + { + $undefinedAssertions = array_diff( + $this->availableCommands, + array_keys($this->assertions) + ); + if (empty($undefinedAssertions)) { + return; + } + throw new \LogicException( + sprintf( + 'Missing assertions for commands: %s', + implode(', ', $undefinedAssertions) + ), + 1535189883 + ); + } +} diff --git a/vendor/typo3/phar-stream-wrapper/src/Exception.php b/vendor/typo3/phar-stream-wrapper/src/Exception.php new file mode 100644 index 0000000000000000000000000000000000000000..690121a999a6b0b699ce41d18b819b0b7acf3ab5 --- /dev/null +++ b/vendor/typo3/phar-stream-wrapper/src/Exception.php @@ -0,0 +1,16 @@ +<?php +namespace TYPO3\PharStreamWrapper; + +/* + * This file is part of the TYPO3 project. + * + * It is free software; you can redistribute it and/or modify it under the terms + * of the MIT License (MIT). For the full copyright and license information, + * please read the LICENSE file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +class Exception extends \RuntimeException +{ +} diff --git a/vendor/typo3/phar-stream-wrapper/src/Helper.php b/vendor/typo3/phar-stream-wrapper/src/Helper.php new file mode 100644 index 0000000000000000000000000000000000000000..32d0da6975c364ec059bc1fa30b6aa0961199bbf --- /dev/null +++ b/vendor/typo3/phar-stream-wrapper/src/Helper.php @@ -0,0 +1,183 @@ +<?php +namespace TYPO3\PharStreamWrapper; + +/* + * This file is part of the TYPO3 project. + * + * It is free software; you can redistribute it and/or modify it under the terms + * of the MIT License (MIT). For the full copyright and license information, + * please read the LICENSE file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +class Helper +{ + /* + * Resets PHP's OPcache if enabled as work-around for issues in `include()` + * or `require()` calls and OPcache delivering wrong results. + * + * @see https://bugs.php.net/bug.php?id=66569 + */ + public static function resetOpCache() + { + if (function_exists('opcache_reset') + && function_exists('opcache_get_status') + ) { + $status = opcache_get_status(); + if (!empty($status['opcache_enabled'])) { + opcache_reset(); + } + } + } + + /** + * Determines base file that can be accessed using the regular file system. + * For e.g. "phar:///home/user/bundle.phar/content.txt" that would result + * into "/home/user/bundle.phar". + * + * @param string $path + * @return string|null + */ + public static function determineBaseFile($path) + { + $parts = explode('/', static::normalizePath($path)); + + while (count($parts)) { + $currentPath = implode('/', $parts); + if (@is_file($currentPath)) { + return $currentPath; + } + array_pop($parts); + } + + return null; + } + + /** + * @param string $path + * @return string + */ + public static function removePharPrefix($path) + { + $path = trim($path); + if (stripos($path, 'phar://') !== 0) { + return $path; + } + return substr($path, 7); + } + + /** + * Normalizes a path, removes phar:// prefix, fixes Windows directory + * separators. Result is without trailing slash. + * + * @param string $path + * @return string + */ + public static function normalizePath($path) + { + return rtrim( + static::getCanonicalPath( + static::removePharPrefix($path) + ), + '/' + ); + } + + /** + * Fixes a path for windows-backslashes and reduces double-slashes to single slashes + * + * @param string $path File path to process + * @return string + */ + private static function normalizeWindowsPath($path) + { + return str_replace('\\', '/', $path); + } + + /** + * Resolves all dots, slashes and removes spaces after or before a path... + * + * @param string $path Input string + * @return string Canonical path, always without trailing slash + */ + private static function getCanonicalPath($path) + { + $path = static::normalizeWindowsPath($path); + + $absolutePathPrefix = ''; + if (static::isAbsolutePath($path)) { + if (static::isWindows() && strpos($path, ':/') === 1) { + $absolutePathPrefix = substr($path, 0, 3); + $path = substr($path, 3); + } else { + $path = ltrim($path, '/'); + $absolutePathPrefix = '/'; + } + } + + $pathParts = explode('/', $path); + $pathPartsLength = count($pathParts); + for ($partCount = 0; $partCount < $pathPartsLength; $partCount++) { + // double-slashes in path: remove element + if ($pathParts[$partCount] === '') { + array_splice($pathParts, $partCount, 1); + $partCount--; + $pathPartsLength--; + } + // "." in path: remove element + if ((isset($pathParts[$partCount]) ? $pathParts[$partCount] : '') === '.') { + array_splice($pathParts, $partCount, 1); + $partCount--; + $pathPartsLength--; + } + // ".." in path: + if ((isset($pathParts[$partCount]) ? $pathParts[$partCount] : '') === '..') { + if ($partCount === 0) { + array_splice($pathParts, $partCount, 1); + $partCount--; + $pathPartsLength--; + } elseif ($partCount >= 1) { + // Rremove this and previous element + array_splice($pathParts, $partCount - 1, 2); + $partCount -= 2; + $pathPartsLength -= 2; + } elseif ($absolutePathPrefix) { + // can't go higher than root dir + // simply remove this part and continue + array_splice($pathParts, $partCount, 1); + $partCount--; + $pathPartsLength--; + } + } + } + + return $absolutePathPrefix . implode('/', $pathParts); + } + + /** + * Checks if the $path is absolute or relative (detecting either '/' or + * 'x:/' as first part of string) and returns TRUE if so. + * + * @param string $path File path to evaluate + * @return bool + */ + private static function isAbsolutePath($path) + { + // Path starting with a / is always absolute, on every system + // On Windows also a path starting with a drive letter is absolute: X:/ + return (isset($path[0]) ? $path[0] : null) === '/' + || static::isWindows() && ( + strpos($path, ':/') === 1 + || strpos($path, ':\\') === 1 + ); + } + + /** + * @return bool + */ + private static function isWindows() + { + return stripos(PHP_OS, 'WIN') === 0; + } +} \ No newline at end of file diff --git a/vendor/typo3/phar-stream-wrapper/src/Interceptor/PharExtensionInterceptor.php b/vendor/typo3/phar-stream-wrapper/src/Interceptor/PharExtensionInterceptor.php new file mode 100644 index 0000000000000000000000000000000000000000..db500afc8a730157984460b89f05652a952ca207 --- /dev/null +++ b/vendor/typo3/phar-stream-wrapper/src/Interceptor/PharExtensionInterceptor.php @@ -0,0 +1,55 @@ +<?php +namespace TYPO3\PharStreamWrapper\Interceptor; + +/* + * This file is part of the TYPO3 project. + * + * It is free software; you can redistribute it and/or modify it under the terms + * of the MIT License (MIT). For the full copyright and license information, + * please read the LICENSE file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +use TYPO3\PharStreamWrapper\Assertable; +use TYPO3\PharStreamWrapper\Helper; +use TYPO3\PharStreamWrapper\Exception; + +class PharExtensionInterceptor implements Assertable +{ + /** + * Determines whether the base file name has a ".phar" suffix. + * + * @param string $path + * @param string $command + * @return bool + * @throws Exception + */ + public function assert($path, $command) + { + if ($this->baseFileContainsPharExtension($path)) { + return true; + } + throw new Exception( + sprintf( + 'Unexpected file extension in "%s"', + $path + ), + 1535198703 + ); + } + + /** + * @param string $path + * @return bool + */ + private function baseFileContainsPharExtension($path) + { + $baseFile = Helper::determineBaseFile($path); + if ($baseFile === null) { + return false; + } + $fileExtension = pathinfo($baseFile, PATHINFO_EXTENSION); + return strtolower($fileExtension) === 'phar'; + } +} diff --git a/vendor/typo3/phar-stream-wrapper/src/Manager.php b/vendor/typo3/phar-stream-wrapper/src/Manager.php new file mode 100644 index 0000000000000000000000000000000000000000..1eb9735d9862af154ed042f32adb791781720c43 --- /dev/null +++ b/vendor/typo3/phar-stream-wrapper/src/Manager.php @@ -0,0 +1,85 @@ +<?php +namespace TYPO3\PharStreamWrapper; + +/* + * This file is part of the TYPO3 project. + * + * It is free software; you can redistribute it and/or modify it under the terms + * of the MIT License (MIT). For the full copyright and license information, + * please read the LICENSE file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +class Manager implements Assertable +{ + /** + * @var self + */ + private static $instance; + + /** + * @var Behavior + */ + private $behavior; + + /** + * @param Behavior $behaviour + * @return self + */ + public static function initialize(Behavior $behaviour) + { + if (self::$instance === null) { + self::$instance = new self($behaviour); + return self::$instance; + } + throw new \LogicException( + 'Manager can only be initialized once', + 1535189871 + ); + } + + /** + * @return self + */ + public static function instance() + { + if (self::$instance !== null) { + return self::$instance; + } + throw new \LogicException( + 'Manager needs to be initialized first', + 1535189872 + ); + } + + /** + * @return bool + */ + public static function destroy() + { + if (self::$instance === null) { + return false; + } + self::$instance = null; + return true; + } + + /** + * @param Behavior $behaviour + */ + private function __construct(Behavior $behaviour) + { + $this->behavior = $behaviour; + } + + /** + * @param string $path + * @param string $command + * @return bool + */ + public function assert($path, $command) + { + return $this->behavior->assert($path, $command); + } +} diff --git a/vendor/typo3/phar-stream-wrapper/src/PharStreamWrapper.php b/vendor/typo3/phar-stream-wrapper/src/PharStreamWrapper.php new file mode 100644 index 0000000000000000000000000000000000000000..5a924e4ccdf9c7e11c528f3bebd283977035c8af --- /dev/null +++ b/vendor/typo3/phar-stream-wrapper/src/PharStreamWrapper.php @@ -0,0 +1,477 @@ +<?php +namespace TYPO3\PharStreamWrapper; + +/* + * This file is part of the TYPO3 project. + * + * It is free software; you can redistribute it and/or modify it under the terms + * of the MIT License (MIT). For the full copyright and license information, + * please read the LICENSE file that was distributed with this source code. + * + * The TYPO3 project - inspiring people to share! + */ + +class PharStreamWrapper +{ + /** + * Internal stream constants that are not exposed to PHP, but used... + * @see https://github.com/php/php-src/blob/e17fc0d73c611ad0207cac8a4a01ded38251a7dc/main/php_streams.h + */ + const STREAM_OPEN_FOR_INCLUDE = 128; + + /** + * @var resource + */ + public $context; + + /** + * @var resource + */ + protected $internalResource; + + /** + * @return bool + */ + public function dir_closedir() + { + if (!is_resource($this->internalResource)) { + return false; + } + + $this->invokeInternalStreamWrapper( + 'closedir', + $this->internalResource + ); + return !is_resource($this->internalResource); + } + + /** + * @param string $path + * @param int $options + * @return bool + */ + public function dir_opendir($path, $options) + { + $this->assert($path, Behavior::COMMAND_DIR_OPENDIR); + $this->internalResource = $this->invokeInternalStreamWrapper( + 'opendir', + $path, + $this->context + ); + return is_resource($this->internalResource); + } + + /** + * @return string|false + */ + public function dir_readdir() + { + return $this->invokeInternalStreamWrapper( + 'readdir', + $this->internalResource + ); + } + + /** + * @return bool + */ + public function dir_rewinddir() + { + if (!is_resource($this->internalResource)) { + return false; + } + + $this->invokeInternalStreamWrapper( + 'rewinddir', + $this->internalResource + ); + return is_resource($this->internalResource); + } + + /** + * @param string $path + * @param int $mode + * @param int $options + * @return bool + */ + public function mkdir($path, $mode, $options) + { + $this->assert($path, Behavior::COMMAND_MKDIR); + return $this->invokeInternalStreamWrapper( + 'mkdir', + $path, + $mode, + (bool) ($options & STREAM_MKDIR_RECURSIVE), + $this->context + ); + } + + /** + * @param string $path_from + * @param string $path_to + * @return bool + */ + public function rename($path_from, $path_to) + { + $this->assert($path_from, Behavior::COMMAND_RENAME); + $this->assert($path_to, Behavior::COMMAND_RENAME); + return $this->invokeInternalStreamWrapper( + 'rename', + $path_from, + $path_to, + $this->context + ); + } + + /** + * @param string $path + * @param int $options + * @return bool + */ + public function rmdir($path, $options) + { + $this->assert($path, Behavior::COMMAND_RMDIR); + return $this->invokeInternalStreamWrapper( + 'rmdir', + $path, + $this->context + ); + } + + /** + * @param int $cast_as + */ + public function stream_cast($cast_as) + { + throw new Exception( + 'Method stream_select() cannot be used', + 1530103999 + ); + } + + public function stream_close() + { + $this->invokeInternalStreamWrapper( + 'fclose', + $this->internalResource + ); + } + + /** + * @return bool + */ + public function stream_eof() + { + return $this->invokeInternalStreamWrapper( + 'feof', + $this->internalResource + ); + } + + /** + * @return bool + */ + public function stream_flush() + { + return $this->invokeInternalStreamWrapper( + 'fflush', + $this->internalResource + ); + } + + /** + * @param int $operation + * @return bool + */ + public function stream_lock($operation) + { + return $this->invokeInternalStreamWrapper( + 'flock', + $this->internalResource, + $operation + ); + } + + /** + * @param string $path + * @param int $option + * @param string|int $value + * @return bool + */ + public function stream_metadata($path, $option, $value) + { + $this->assert($path, Behavior::COMMAND_STEAM_METADATA); + if ($option === STREAM_META_TOUCH) { + return call_user_func_array( + array($this, 'invokeInternalStreamWrapper'), + array_merge(array('touch', $path), (array) $value) + ); + } + if ($option === STREAM_META_OWNER_NAME || $option === STREAM_META_OWNER) { + return $this->invokeInternalStreamWrapper( + 'chown', + $path, + $value + ); + } + if ($option === STREAM_META_GROUP_NAME || $option === STREAM_META_GROUP) { + return $this->invokeInternalStreamWrapper( + 'chgrp', + $path, + $value + ); + } + if ($option === STREAM_META_ACCESS) { + return $this->invokeInternalStreamWrapper( + 'chmod', + $path, + $value + ); + } + return false; + } + + /** + * @param string $path + * @param string $mode + * @param int $options + * @param string|null $opened_path + * @return bool + */ + public function stream_open( + $path, + $mode, + $options, + &$opened_path = null + ) { + $this->assert($path, Behavior::COMMAND_STREAM_OPEN); + $arguments = array($path, $mode, (bool) ($options & STREAM_USE_PATH)); + // only add stream context for non include/require calls + if (!($options & static::STREAM_OPEN_FOR_INCLUDE)) { + $arguments[] = $this->context; + // work around https://bugs.php.net/bug.php?id=66569 + // for including files from Phar stream with OPcache enabled + } else { + Helper::resetOpCache(); + } + $this->internalResource = call_user_func_array( + array($this, 'invokeInternalStreamWrapper'), + array_merge(array('fopen'), $arguments) + ); + if (!is_resource($this->internalResource)) { + return false; + } + if ($opened_path !== null) { + $metaData = stream_get_meta_data($this->internalResource); + $opened_path = $metaData['uri']; + } + return true; + } + + /** + * @param int $count + * @return string + */ + public function stream_read($count) + { + return $this->invokeInternalStreamWrapper( + 'fread', + $this->internalResource, + $count + ); + } + + /** + * @param int $offset + * @param int $whence + * @return bool + */ + public function stream_seek($offset, $whence = SEEK_SET) + { + return $this->invokeInternalStreamWrapper( + 'fseek', + $this->internalResource, + $offset, + $whence + ) !== -1; + } + + /** + * @param int $option + * @param int $arg1 + * @param int $arg2 + * @return bool + */ + public function stream_set_option($option, $arg1, $arg2) + { + if ($option === STREAM_OPTION_BLOCKING) { + return $this->invokeInternalStreamWrapper( + 'stream_set_blocking', + $this->internalResource, + $arg1 + ); + } + if ($option === STREAM_OPTION_READ_TIMEOUT) { + return $this->invokeInternalStreamWrapper( + 'stream_set_timeout', + $this->internalResource, + $arg1, + $arg2 + ); + } + if ($option === STREAM_OPTION_WRITE_BUFFER) { + return $this->invokeInternalStreamWrapper( + 'stream_set_write_buffer', + $this->internalResource, + $arg2 + ) === 0; + } + return false; + } + + /** + * @return array + */ + public function stream_stat() + { + return $this->invokeInternalStreamWrapper( + 'fstat', + $this->internalResource + ); + } + + /** + * @return int + */ + public function stream_tell() + { + return $this->invokeInternalStreamWrapper( + 'ftell', + $this->internalResource + ); + } + + /** + * @param int $new_size + * @return bool + */ + public function stream_truncate($new_size) + { + return $this->invokeInternalStreamWrapper( + 'ftruncate', + $this->internalResource, + $new_size + ); + } + + /** + * @param string $data + * @return int + */ + public function stream_write($data) + { + return $this->invokeInternalStreamWrapper( + 'fwrite', + $this->internalResource, + $data + ); + } + + /** + * @param string $path + * @return bool + */ + public function unlink($path) + { + $this->assert($path, Behavior::COMMAND_UNLINK); + return $this->invokeInternalStreamWrapper( + 'unlink', + $path, + $this->context + ); + } + + /** + * @param string $path + * @param int $flags + * @return array|false + */ + public function url_stat($path, $flags) + { + $this->assert($path, Behavior::COMMAND_URL_STAT); + $functionName = $flags & STREAM_URL_STAT_QUIET ? '@stat' : 'stat'; + return $this->invokeInternalStreamWrapper($functionName, $path); + } + + /** + * @param string $path + * @param string $command + */ + protected function assert($path, $command) + { + if ($this->resolveAssertable()->assert($path, $command) === true) { + return; + } + + throw new Exception( + sprintf( + 'Denied invocation of "%s" for command "%s"', + $path, + $command + ), + 1535189880 + ); + } + + /** + * @return Assertable + */ + protected function resolveAssertable() + { + return Manager::instance(); + } + + /** + * Invokes commands on the native PHP Phar stream wrapper. + * + * @param string $functionName + * @param mixed ...$arguments + * @return mixed + */ + private function invokeInternalStreamWrapper($functionName) + { + $arguments = func_get_args(); + array_shift($arguments); + $silentExecution = $functionName{0} === '@'; + $functionName = ltrim($functionName, '@'); + $this->restoreInternalSteamWrapper(); + + try { + if ($silentExecution) { + $result = @call_user_func_array($functionName, $arguments); + } else { + $result = call_user_func_array($functionName, $arguments); + } + } catch (\Exception $exception) { + $this->registerStreamWrapper(); + throw $exception; + } catch (\Throwable $throwable) { + $this->registerStreamWrapper(); + throw $throwable; + } + + $this->registerStreamWrapper(); + return $result; + } + + private function restoreInternalSteamWrapper() + { + stream_wrapper_restore('phar'); + } + + private function registerStreamWrapper() + { + stream_wrapper_unregister('phar'); + stream_wrapper_register('phar', get_class($this)); + } +}