Browse Source

updated core to 7.67

Bachir Soussi Chiadmi 2 months ago
parent
commit
cc3b64a193
100 changed files with 2173 additions and 208 deletions
  1. 26 0
      CHANGELOG.txt
  2. 1 1
      includes/bootstrap.inc
  3. 5 0
      includes/common.inc
  4. 48 3
      includes/file.inc
  5. 14 0
      includes/file.phar.inc
  6. 30 5
      includes/registry.inc
  7. 4 0
      misc/brumann/polyfill-unserialize/.gitignore
  8. 20 0
      misc/brumann/polyfill-unserialize/.travis.yml
  9. 21 0
      misc/brumann/polyfill-unserialize/LICENSE
  10. 61 0
      misc/brumann/polyfill-unserialize/README.md
  11. 26 0
      misc/brumann/polyfill-unserialize/composer.json
  12. 25 0
      misc/brumann/polyfill-unserialize/phpunit.xml.dist
  13. 58 0
      misc/brumann/polyfill-unserialize/src/Unserialize.php
  14. 112 0
      misc/jquery-extend-3.4.0.js
  15. 3 0
      misc/typo3/phar-stream-wrapper/.gitignore
  16. 66 3
      misc/typo3/phar-stream-wrapper/README.md
  17. 5 1
      misc/typo3/phar-stream-wrapper/composer.json
  18. 37 0
      misc/typo3/phar-stream-wrapper/src/Collectable.php
  19. 18 2
      misc/typo3/phar-stream-wrapper/src/Helper.php
  20. 88 0
      misc/typo3/phar-stream-wrapper/src/Interceptor/ConjunctionInterceptor.php
  21. 4 4
      misc/typo3/phar-stream-wrapper/src/Interceptor/PharExtensionInterceptor.php
  22. 73 0
      misc/typo3/phar-stream-wrapper/src/Interceptor/PharMetaDataInterceptor.php
  23. 56 6
      misc/typo3/phar-stream-wrapper/src/Manager.php
  24. 59 0
      misc/typo3/phar-stream-wrapper/src/Phar/Container.php
  25. 18 0
      misc/typo3/phar-stream-wrapper/src/Phar/DeserializationException.php
  26. 176 0
      misc/typo3/phar-stream-wrapper/src/Phar/Manifest.php
  27. 220 0
      misc/typo3/phar-stream-wrapper/src/Phar/Reader.php
  28. 18 0
      misc/typo3/phar-stream-wrapper/src/Phar/ReaderException.php
  29. 65 0
      misc/typo3/phar-stream-wrapper/src/Phar/Stub.php
  30. 36 2
      misc/typo3/phar-stream-wrapper/src/PharStreamWrapper.php
  31. 24 0
      misc/typo3/phar-stream-wrapper/src/Resolvable.php
  32. 125 0
      misc/typo3/phar-stream-wrapper/src/Resolver/PharInvocation.php
  33. 156 0
      misc/typo3/phar-stream-wrapper/src/Resolver/PharInvocationCollection.php
  34. 241 0
      misc/typo3/phar-stream-wrapper/src/Resolver/PharInvocationResolver.php
  35. 3 3
      modules/aggregator/aggregator.info
  36. 3 3
      modules/aggregator/tests/aggregator_test.info
  37. 3 3
      modules/block/block.info
  38. 3 3
      modules/block/tests/block_test.info
  39. 3 3
      modules/block/tests/themes/block_test_theme/block_test_theme.info
  40. 3 3
      modules/blog/blog.info
  41. 3 3
      modules/book/book.info
  42. 3 3
      modules/color/color.info
  43. 3 3
      modules/comment/comment.info
  44. 3 3
      modules/contact/contact.info
  45. 3 3
      modules/contextual/contextual.info
  46. 3 3
      modules/dashboard/dashboard.info
  47. 3 3
      modules/dblog/dblog.info
  48. 3 3
      modules/field/field.info
  49. 3 3
      modules/field/modules/field_sql_storage/field_sql_storage.info
  50. 3 3
      modules/field/modules/list/list.info
  51. 3 3
      modules/field/modules/list/tests/list_test.info
  52. 3 3
      modules/field/modules/number/number.info
  53. 1 1
      modules/field/modules/number/number.test
  54. 3 3
      modules/field/modules/options/options.info
  55. 3 3
      modules/field/modules/text/text.info
  56. 3 3
      modules/field/tests/field_test.info
  57. 3 3
      modules/field_ui/field_ui.info
  58. 1 1
      modules/file/file.field.inc
  59. 3 3
      modules/file/file.info
  60. 57 0
      modules/file/tests/file.test
  61. 3 3
      modules/file/tests/file_module_test.info
  62. 0 0
      modules/file/tests/fixtures/file_scan_ignore/a.txt
  63. 0 0
      modules/file/tests/fixtures/file_scan_ignore/frontend_framework/b.txt
  64. 0 0
      modules/file/tests/fixtures/file_scan_ignore/frontend_framework/c.txt
  65. 3 3
      modules/filter/filter.info
  66. 3 3
      modules/forum/forum.info
  67. 3 3
      modules/help/help.info
  68. 3 3
      modules/image/image.info
  69. 3 3
      modules/image/tests/image_module_test.info
  70. 3 3
      modules/locale/locale.info
  71. 3 3
      modules/locale/tests/locale_test.info
  72. 3 3
      modules/menu/menu.info
  73. 3 3
      modules/node/node.info
  74. 3 3
      modules/node/tests/node_access_test.info
  75. 3 3
      modules/node/tests/node_test.info
  76. 3 3
      modules/node/tests/node_test_exception.info
  77. 3 3
      modules/openid/openid.info
  78. 3 3
      modules/openid/tests/openid_test.info
  79. 3 3
      modules/overlay/overlay.info
  80. 3 3
      modules/path/path.info
  81. 3 3
      modules/php/php.info
  82. 3 3
      modules/poll/poll.info
  83. 3 3
      modules/profile/profile.info
  84. 3 3
      modules/rdf/rdf.info
  85. 3 3
      modules/rdf/tests/rdf_test.info
  86. 3 3
      modules/search/search.info
  87. 3 3
      modules/search/tests/search_embedded_form.info
  88. 3 3
      modules/search/tests/search_extra_type.info
  89. 3 3
      modules/search/tests/search_node_tags.info
  90. 3 3
      modules/shortcut/shortcut.info
  91. 2 2
      modules/simpletest/drupal_web_test_case.php
  92. 3 3
      modules/simpletest/simpletest.info
  93. 3 3
      modules/simpletest/tests/actions_loop_test.info
  94. 3 3
      modules/simpletest/tests/ajax_forms_test.info
  95. 3 3
      modules/simpletest/tests/ajax_test.info
  96. 3 3
      modules/simpletest/tests/batch_test.info
  97. 3 3
      modules/simpletest/tests/boot_test_1.info
  98. 3 3
      modules/simpletest/tests/boot_test_2.info
  99. 2 6
      modules/simpletest/tests/bootstrap.test
  100. 0 0
      modules/simpletest/tests/common_test.info

+ 26 - 0
CHANGELOG.txt

@@ -1,3 +1,29 @@
+Drupal 7.xx, xxxx-xx-xx (development version)
+-----------------------
+
+Drupal 7.67, 2019-05-08
+-----------------------
+- Fixed security issues:
+   - SA-CORE-2019-007
+
+Drupal 7.66, 2019-04-17
+-----------------------
+- Fixed security issues:
+   - SA-CORE-2019-006
+
+Drupal 7.65, 2019-03-20
+-----------------------
+- Fixed security issues:
+   - SA-CORE-2019-004
+
+Drupal 7.64, 2019-02-06
+-----------------------
+- [regression] Unset the 'host' header in drupal_http_request() during redirect
+- Fixed: 7.x does not have Phar protection and Phar tests are failing on Drupal 7
+- Fixed: Notice: Undefined index: display_field in file_field_widget_value() (line 582 of /module/file/file.field.inc)
+- Performance improvement: Registry rebuild should not parse the same file twice in the same request
+- Fixed _registry_update() to clear caches after transaction is committed
+
 Drupal 7.63, 2019-01-16
 -----------------------
 - Fixed a fatal error for some Drush users introduced by SA-CORE-2019-002.

+ 1 - 1
includes/bootstrap.inc

@@ -8,7 +8,7 @@
 /**
  * The current system version.
  */
-define('VERSION', '7.63');
+define('VERSION', '7.67');
 
 /**
  * Core API compatibility.

+ 5 - 0
includes/common.inc

@@ -1094,6 +1094,11 @@ function drupal_http_request($url, array $options = array()) {
       elseif ($options['max_redirects']) {
         // Redirect to the new location.
         $options['max_redirects']--;
+
+        // We need to unset the 'Host' header
+        // as we are redirecting to a new location.
+        unset($options['headers']['Host']);
+
         $result = drupal_http_request($location, $options);
         $result->redirect_code = $code;
       }

+ 48 - 3
includes/file.inc

@@ -993,8 +993,15 @@ function file_build_uri($path) {
  * @return
  *   The destination filepath, or FALSE if the file already exists
  *   and FILE_EXISTS_ERROR is specified.
+ *
+ * @throws RuntimeException
+ *   Thrown if the filename contains invalid UTF-8.
  */
 function file_destination($destination, $replace) {
+  $basename = drupal_basename($destination);
+  if (!drupal_validate_utf8($basename)) {
+    throw new RuntimeException(sprintf("Invalid filename '%s'", $basename));
+  }
   if (file_exists($destination)) {
     switch ($replace) {
       case FILE_EXISTS_REPLACE:
@@ -1002,7 +1009,6 @@ function file_destination($destination, $replace) {
         break;
 
       case FILE_EXISTS_RENAME:
-        $basename = drupal_basename($destination);
         $directory = drupal_dirname($destination);
         $destination = file_create_filename($basename, $directory);
         break;
@@ -1218,11 +1224,20 @@ function file_unmunge_filename($filename) {
  * @return
  *   File path consisting of $directory and a unique filename based off
  *   of $basename.
+ *
+ * @throws RuntimeException
+ *   Thrown if the $basename is not valid UTF-8 or another error occurs
+ *   stripping control characters.
  */
 function file_create_filename($basename, $directory) {
+  $original = $basename;
   // Strip control characters (ASCII value < 32). Though these are allowed in
   // some filesystems, not many applications handle them well.
   $basename = preg_replace('/[\x00-\x1F]/u', '_', $basename);
+  if (preg_last_error() !== PREG_NO_ERROR) {
+    throw new RuntimeException(sprintf("Invalid filename '%s'", $original));
+  }
+
   if (substr(PHP_OS, 0, 3) == 'WIN') {
     // These characters are not allowed in Windows filenames
     $basename = str_replace(array(':', '*', '?', '"', '<', '>', '|'), '_', $basename);
@@ -1563,7 +1578,13 @@ function file_save_upload($form_field_name, $validators = array(), $destination
   if (substr($destination, -1) != '/') {
     $destination .= '/';
   }
-  $file->destination = file_destination($destination . $file->filename, $replace);
+  try {
+    $file->destination = file_destination($destination . $file->filename, $replace);
+  }
+  catch (RuntimeException $e) {
+    drupal_set_message(t('The file %source could not be uploaded because the name is invalid.', array('%source' => $form_field_name)), 'error');
+    return FALSE;
+  }
   // If file_destination() returns FALSE then $replace == FILE_EXISTS_ERROR and
   // there's an existing file so we need to bail.
   if ($file->destination === FALSE) {
@@ -2130,9 +2151,33 @@ function file_download_access($uri) {
  *   'filename', and 'name' members corresponding to the matching files.
  */
 function file_scan_directory($dir, $mask, $options = array(), $depth = 0) {
+  // Default nomask option.
+  $nomask = '/(\.\.?|CVS)$/';
+
+  // Overrides the $nomask variable accordingly if $options['nomask'] is set.
+  //
+  // Allow directories specified in settings.php to be ignored. You can use this
+  // to not check for files in common special-purpose directories. For example,
+  // node_modules and bower_components. Ignoring irrelevant directories is a
+  // performance boost.
+  if (!isset($options['nomask'])) {
+    $ignore_directories = variable_get(
+      'file_scan_ignore_directories',
+      array()
+    );
+
+    foreach ($ignore_directories as $index => $ignore_directory) {
+      $ignore_directories[$index] = preg_quote($ignore_directory, '/');
+    }
+
+    if (!empty($ignore_directories)) {
+      $nomask = '/^(\.\.?)|CVS|' . implode('|', $ignore_directories) . '$/';
+    }
+  }
+
   // Merge in defaults.
   $options += array(
-    'nomask' => '/(\.\.?|CVS)$/',
+    'nomask' => $nomask,
     'callback' => 0,
     'recurse' => TRUE,
     'key' => 'uri',

+ 14 - 0
includes/file.phar.inc

@@ -18,7 +18,21 @@ function file_register_phar_wrapper() {
   include_once $directory . '/Helper.php';
   include_once $directory . '/Manager.php';
   include_once $directory . '/PharStreamWrapper.php';
+  include_once $directory . '/Collectable.php';
+  include_once $directory . '/Interceptor/ConjunctionInterceptor.php';
+  include_once $directory . '/Interceptor/PharMetaDataInterceptor.php';
+  include_once $directory . '/Phar/Container.php';
+  include_once $directory . '/Phar/DeserializationException.php';
+  include_once $directory . '/Phar/Manifest.php';
+  include_once $directory . '/Phar/Reader.php';
+  include_once $directory . '/Phar/ReaderException.php';
+  include_once $directory . '/Phar/Stub.php';
+  include_once $directory . '/Resolvable.php';
+  include_once $directory . '/Resolver/PharInvocation.php';
+  include_once $directory . '/Resolver/PharInvocationCollection.php';
+  include_once $directory . '/Resolver/PharInvocationResolver.php';
   include_once DRUPAL_ROOT . '/misc/typo3/drupal-security/PharExtensionInterceptor.php';
+  include_once DRUPAL_ROOT . '/misc/brumann/polyfill-unserialize/src/Unserialize.php';
 
   // Set up a stream wrapper to handle insecurities due to PHP's built-in
   // phar stream wrapper.

+ 30 - 5
includes/registry.inc

@@ -19,7 +19,6 @@
  * Does the work for registry_update().
  */
 function _registry_update() {
-
   // The registry serves as a central autoloader for all classes, including
   // the database query builders. However, the registry rebuild process
   // requires write ability to the database, which means having access to the
@@ -33,6 +32,11 @@ function _registry_update() {
   require_once DRUPAL_ROOT . '/includes/database/select.inc';
   require_once DRUPAL_ROOT . '/includes/database/' . $driver . '/query.inc';
 
+  // During the first registry rebuild in a request, we check all the files.
+  // During subsequent rebuilds, we only add new files. It makes the rebuilding
+  // process faster during installation of modules.
+  static $check_existing_files = TRUE;
+
   // Get current list of modules and their files.
   $modules = db_query("SELECT * FROM {system} WHERE type = 'module'")->fetchAll();
   // Get the list of files we are going to parse.
@@ -55,6 +59,9 @@ function _registry_update() {
     $files["$filename"] = array('module' => '', 'weight' => 0);
   }
 
+  // Initialize an empty array for the unchanged files.
+  $unchanged_files = array();
+
   $transaction = db_transaction();
   try {
     // Allow modules to manually modify the list of files before the registry
@@ -63,10 +70,19 @@ function _registry_update() {
     // list can then be added to the list of files that the registry will parse,
     // or modify attributes of a file.
     drupal_alter('registry_files', $files, $modules);
+
     foreach (registry_get_parsed_files() as $filename => $file) {
       // Add the hash for those files we have already parsed.
       if (isset($files[$filename])) {
-        $files[$filename]['hash'] = $file['hash'];
+        if ($check_existing_files === TRUE) {
+          $files[$filename]['hash'] = $file['hash'];
+        }
+        else {
+          // Ignore that file for this request, it has been parsed previously
+          // and it is unlikely it has changed.
+          unset($files[$filename]);
+          $unchanged_files[$filename] = $file;
+        }
       }
       else {
         // Flush the registry of resources in files that are no longer on disc
@@ -79,8 +95,12 @@ function _registry_update() {
           ->execute();
       }
     }
+
     $parsed_files = _registry_parse_files($files);
 
+    // Add unchanged files to the files.
+    $files += $unchanged_files;
+
     $unchanged_resources = array();
     $lookup_cache = array();
     if ($cache = cache_get('lookup_cache', 'cache_bootstrap')) {
@@ -89,12 +109,10 @@ function _registry_update() {
     foreach ($lookup_cache as $key => $file) {
       // If the file for this cached resource is carried over unchanged from
       // the last registry build, then we can safely re-cache it.
-      if ($file && in_array($file, array_keys($files)) && !in_array($file, $parsed_files)) {
+      if ($file && isset($files[$file]) && !in_array($file, $parsed_files, TRUE)) {
         $unchanged_resources[$key] = $file;
       }
     }
-    module_implements('', FALSE, TRUE);
-    _registry_check_code(REGISTRY_RESET_LOOKUP_CACHE);
   }
   catch (Exception $e) {
     $transaction->rollback();
@@ -102,6 +120,13 @@ function _registry_update() {
     throw $e;
   }
 
+  module_implements('', FALSE, TRUE);
+  _registry_check_code(REGISTRY_RESET_LOOKUP_CACHE);
+
+  // During the next run in this request, don't bother re-checking existing
+  // files.
+  $check_existing_files = FALSE;
+
   // We have some unchanged resources, warm up the cache - no need to pay
   // for looking them up again.
   if (count($unchanged_resources) > 0) {

+ 4 - 0
misc/brumann/polyfill-unserialize/.gitignore

@@ -0,0 +1,4 @@
+/vendor/
+/phpunit.xml
+/.composer.lock
+

+ 20 - 0
misc/brumann/polyfill-unserialize/.travis.yml

@@ -0,0 +1,20 @@
+language: php
+
+sudo: false
+
+php:
+  - '5.3'
+  - '5.4'
+  - '5.5'
+  - '5.6'
+  - '7.0'
+  - '7.1'
+
+before_install:
+  - phpenv config-rm xdebug.ini
+  - composer self-update
+
+install:
+  - composer install
+
+script: phpunit

+ 21 - 0
misc/brumann/polyfill-unserialize/LICENSE

@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2016 Denis Brumann
+
+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.

+ 61 - 0
misc/brumann/polyfill-unserialize/README.md

@@ -0,0 +1,61 @@
+Polyfill unserialize [![Build Status](https://travis-ci.org/dbrumann/polyfill-unserialize.svg?branch=master)](https://travis-ci.org/dbrumann/polyfill-unserialize)
+===
+
+Backports unserialize options introduced in PHP 7.0 to older PHP versions.
+This was originally designed as a Proof of Concept for Symfony Issue [#21090](https://github.com/symfony/symfony/pull/21090).
+
+You can use this package in projects that rely on PHP versions older than PHP 7.0.
+In case you are using PHP 7.0+ the original `unserialize()` will be used instead.
+
+From the [documentation](https://secure.php.net/manual/en/function.unserialize.php):
+
+> Warning: Do not pass untrusted user input to unserialize(). Unserialization can
+> result in code being loaded and executed due to object instantiation
+> and autoloading, and a malicious user may be able to exploit this.
+
+This warning holds true even when `allowed_classes` is used.
+
+Requirements
+------------
+
+ - PHP 5.3+
+
+Installation
+------------
+
+You can install this package via composer:
+
+```
+composer require brumann/polyfill-unserialize "^1.0"
+```
+
+Known Issues
+------------
+
+There is a mismatch in behavior when `allowed_classes` in `$options` is not
+of the correct type (array or boolean). PHP 7.1 will issue a warning, whereas
+PHP 7.0 will not. I opted to copy the behavior of the former.
+
+Tests
+-----
+
+You can run the test suite using PHPUnit. It is intentionally not bundled as
+dev dependency to make sure this package has the lowest restrictions on the
+implementing system as possible.
+
+Please read the [PHPUnit Manual](https://phpunit.de/manual/current/en/installation.html)
+for information how to install it on your system.
+
+You can run the test suite as follows:
+
+```
+phpunit -c phpunit.xml.dist tests/
+```
+
+Contributing
+------------
+
+This package is considered feature complete. As such I will likely not update it
+unless there are security issues.
+
+Should you find any bugs or have questions, feel free to submit an Issue or a Pull Request.

+ 26 - 0
misc/brumann/polyfill-unserialize/composer.json

@@ -0,0 +1,26 @@
+{
+    "name": "brumann/polyfill-unserialize",
+    "description": "Backports unserialize options introduced in PHP 7.0 to older PHP versions.",
+    "type": "library",
+    "license": "MIT",
+    "authors": [
+        {
+            "name": "Denis Brumann",
+            "email": "denis.brumann@sensiolabs.de"
+        }
+    ],
+    "autoload": {
+        "psr-4": {
+            "Brumann\\Polyfill\\": "src/"
+        }
+    },
+    "autoload-dev": {
+        "psr-4": {
+            "Tests\\Brumann\\Polyfill\\": "tests/"
+        }
+    },
+    "minimum-stability": "stable",
+    "require": {
+        "php": "^5.3|^7.0"
+    }
+}

+ 25 - 0
misc/brumann/polyfill-unserialize/phpunit.xml.dist

@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<phpunit
+    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
+    backupGlobals="false"
+    colors="true"
+    bootstrap="vendor/autoload.php"
+>
+    <php>
+        <ini name="error_reporting" value="-1" />
+    </php>
+
+    <testsuites>
+        <testsuite name="Brumann\Polyfill Test Suite">
+            <directory>./tests/</directory>
+        </testsuite>
+    </testsuites>
+
+    <filter>
+        <whitelist>
+            <directory>./src/</directory>
+        </whitelist>
+    </filter>
+</phpunit>

+ 58 - 0
misc/brumann/polyfill-unserialize/src/Unserialize.php

@@ -0,0 +1,58 @@
+<?php
+
+namespace Brumann\Polyfill;
+
+final class Unserialize
+{
+    /**
+     * @see https://secure.php.net/manual/en/function.unserialize.php
+     *
+     * @param string $serialized Serialized data
+     * @param array $options Associative array containing options
+     *
+     * @return mixed
+     */
+    public static function unserialize($serialized, array $options = array())
+    {
+        if (PHP_VERSION_ID >= 70000) {
+            return \unserialize($serialized, $options);
+        }
+        if (!array_key_exists('allowed_classes', $options)) {
+            $options['allowed_classes'] = true;
+        }
+        $allowedClasses = $options['allowed_classes'];
+        if (true === $allowedClasses) {
+            return \unserialize($serialized);
+        }
+        if (false === $allowedClasses) {
+            $allowedClasses = array();
+        }
+        if (!is_array($allowedClasses)) {
+            trigger_error(
+                'unserialize(): allowed_classes option should be array or boolean',
+                E_USER_WARNING
+            );
+            $allowedClasses = array();
+        }
+
+        $sanitizedSerialized = preg_replace_callback(
+            '/(^|;)O:\d+:"([^"]*)":(\d+):{/',
+            function ($match) use ($allowedClasses) {
+                list($completeMatch, $leftBorder, $className, $objectSize) = $match;
+                if (in_array($className, $allowedClasses)) {
+                    return $completeMatch;
+                } else {
+                    return sprintf(
+                        '%sO:22:"__PHP_Incomplete_Class":%d:{s:27:"__PHP_Incomplete_Class_Name";%s',
+                        $leftBorder,
+                        $objectSize + 1, // size of object + 1 for added string
+                        \serialize($className)
+                    );
+                }
+            },
+            $serialized
+        );
+
+        return \unserialize($sanitizedSerialized);
+    }
+}

+ 112 - 0
misc/jquery-extend-3.4.0.js

@@ -0,0 +1,112 @@
+/**
+ * For jQuery versions less than 3.4.0, this replaces the jQuery.extend
+ * function with the one from jQuery 3.4.0, slightly modified (documented
+ * below) to be compatible with older jQuery versions and browsers.
+ *
+ * This provides the Object.prototype pollution vulnerability fix to Drupal
+ * installations running older jQuery versions, including the versions shipped
+ * with Drupal core and https://www.drupal.org/project/jquery_update.
+ *
+ * @see https://github.com/jquery/jquery/pull/4333
+ */
+
+(function (jQuery) {
+
+// Do not override jQuery.extend() if the jQuery version is already >=3.4.0.
+var versionParts = jQuery.fn.jquery.split('.');
+var majorVersion = parseInt(versionParts[0]);
+var minorVersion = parseInt(versionParts[1]);
+var patchVersion = parseInt(versionParts[2]);
+var isPreReleaseVersion = (patchVersion.toString() !== versionParts[2]);
+if (
+  (majorVersion > 3) ||
+  (majorVersion === 3 && minorVersion > 4) ||
+  (majorVersion === 3 && minorVersion === 4 && patchVersion > 0) ||
+  (majorVersion === 3 && minorVersion === 4 && patchVersion === 0 && !isPreReleaseVersion)
+) {
+  return;
+}
+
+/**
+ * This is almost verbatim copied from jQuery 3.4.0.
+ *
+ * Only two minor changes have been made:
+ * - The call to isFunction() is changed to jQuery.isFunction().
+ * - The two calls to Array.isArray() is changed to jQuery.isArray().
+ *
+ * The above two changes ensure compatibility with all older jQuery versions
+ * (1.4.4 - 3.3.1) and older browser versions (e.g., IE8).
+ */
+jQuery.extend = jQuery.fn.extend = function() {
+  var options, name, src, copy, copyIsArray, clone,
+    target = arguments[ 0 ] || {},
+    i = 1,
+    length = arguments.length,
+    deep = false;
+
+  // Handle a deep copy situation
+  if ( typeof target === "boolean" ) {
+    deep = target;
+
+    // Skip the boolean and the target
+    target = arguments[ i ] || {};
+    i++;
+  }
+
+  // Handle case when target is a string or something (possible in deep copy)
+  if ( typeof target !== "object" && !jQuery.isFunction( target ) ) {
+    target = {};
+  }
+
+  // Extend jQuery itself if only one argument is passed
+  if ( i === length ) {
+    target = this;
+    i--;
+  }
+
+  for ( ; i < length; i++ ) {
+
+    // Only deal with non-null/undefined values
+    if ( ( options = arguments[ i ] ) != null ) {
+
+      // Extend the base object
+      for ( name in options ) {
+        copy = options[ name ];
+
+        // Prevent Object.prototype pollution
+        // Prevent never-ending loop
+        if ( name === "__proto__" || target === copy ) {
+          continue;
+        }
+
+        // Recurse if we're merging plain objects or arrays
+        if ( deep && copy && ( jQuery.isPlainObject( copy ) ||
+          ( copyIsArray = jQuery.isArray( copy ) ) ) ) {
+          src = target[ name ];
+
+          // Ensure proper type for the source value
+          if ( copyIsArray && !jQuery.isArray( src ) ) {
+            clone = [];
+          } else if ( !copyIsArray && !jQuery.isPlainObject( src ) ) {
+            clone = {};
+          } else {
+            clone = src;
+          }
+          copyIsArray = false;
+
+          // Never move original objects, clone them
+          target[ name ] = jQuery.extend( deep, clone, copy );
+
+          // Don't bring in undefined values
+        } else if ( copy !== undefined ) {
+          target[ name ] = copy;
+        }
+      }
+    }
+  }
+
+  // Return the modified object
+  return target;
+};
+
+})(jQuery);

+ 3 - 0
misc/typo3/phar-stream-wrapper/.gitignore

@@ -0,0 +1,3 @@
+.idea
+vendor/
+composer.lock

+ 66 - 3
misc/typo3/phar-stream-wrapper/README.md

@@ -63,7 +63,7 @@ adjusted to according requirements.
 
 ```
 $behavior = new \TYPO3\PharStreamWrapper\Behavior();
-Manager::initialize(
+\TYPO3\PharStreamWrapper\Manager::initialize(
     $behavior->withAssertion(new PharExtensionInterceptor())
 );
 
@@ -90,7 +90,7 @@ if (in_array('phar', stream_get_wrappers())) {
   + `COMMAND_UNLINK`
   + `COMMAND_URL_STAT`
 
-## Interceptor
+## Interceptors
 
 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
@@ -137,9 +137,72 @@ class PharExtensionInterceptor implements Assertable
 }
 ```
 
+### ConjunctionInterceptor
+
+This interceptor combines multiple interceptors implementing `Assertable`.
+It succeeds when all nested interceptors succeed as well (logical `AND`).
+
+```
+$behavior = new \TYPO3\PharStreamWrapper\Behavior();
+\TYPO3\PharStreamWrapper\Manager::initialize(
+    $behavior->withAssertion(new ConjunctionInterceptor(array(
+        new PharExtensionInterceptor(),
+        new PharMetaDataInterceptor()
+    )))
+);
+```
+
+### PharExtensionInterceptor
+
+This (basic) interceptor just checks whether the invoked Phar archive has
+an according `.phar` file extension. Resolving symbolic links as well as
+Phar internal alias resolving are considered as well.
+
+```
+$behavior = new \TYPO3\PharStreamWrapper\Behavior();
+\TYPO3\PharStreamWrapper\Manager::initialize(
+    $behavior->withAssertion(new PharExtensionInterceptor())
+);
+```
+
+### PharMetaDataInterceptor
+
+This interceptor is actually checking serialized Phar meta-data against
+PHP objects and would consider a Phar archive malicious in case not only
+scalar values are found. A custom low-level `Phar\Reader` is used in order to
+avoid using PHP's `Phar` object which would trigger the initial vulnerability.
+
+```
+$behavior = new \TYPO3\PharStreamWrapper\Behavior();
+\TYPO3\PharStreamWrapper\Manager::initialize(
+    $behavior->withAssertion(new PharMetaDataInterceptor())
+);
+```
+
+## Reader
+
+* `Phar\Reader::__construct(string $fileName)`: Creates low-level reader for Phar archive
+* `Phar\Reader::resolveContainer(): Phar\Container`: Resolves model representing Phar archive
+* `Phar\Container::getStub(): Phar\Stub`: Resolves (plain PHP) stub section of Phar archive
+* `Phar\Container::getManifest(): Phar\Manifest`: Resolves parsed Phar archive manifest as
+  documented at http://php.net/manual/en/phar.fileformat.manifestfile.php
+* `Phar\Stub::getMappedAlias(): string`: Resolves internal Phar archive alias defined in stub
+  using `Phar::mapPhar('alias.phar')` - actually the plain PHP source is analyzed here
+* `Phar\Manifest::getAlias(): string` - Resolves internal Phar archive alias defined in manifest
+  using `Phar::setAlias('alias.phar')`
+* `Phar\Manifest::getMetaData(): string`: Resolves serialized Phar archive meta-data
+* `Phar\Manifest::deserializeMetaData(): mixed`: Resolves deserialized Phar archive meta-data
+  containing only scalar values - in case an object is determined, an according
+  `Phar\DeserializationException` will be thrown
+
+```
+$reader = new Phar\Reader('example.phar');
+var_dump($reader->resolveContainer()->getManifest()->deserializeMetaData());
+```
+
 ## Helper
 
-* `Helper::determineBaseFile(string $path)`: Determines base file that can be
+* `Helper::determineBaseFile(string $path): string`: 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`.

+ 5 - 1
misc/typo3/phar-stream-wrapper/composer.json

@@ -6,9 +6,13 @@
     "homepage": "https://typo3.org/",
     "keywords": ["php", "phar", "stream-wrapper", "security"],
     "require": {
-        "php": "^5.3.3|^7.0"
+        "php": "^5.3.3|^7.0",
+        "ext-fileinfo": "*",
+        "ext-json": "*",
+        "brumann/polyfill-unserialize": "^1.0"
     },
     "require-dev": {
+        "ext-xdebug": "*",
         "phpunit/phpunit": "^4.8.36"
     },
     "autoload": {

+ 37 - 0
misc/typo3/phar-stream-wrapper/src/Collectable.php

@@ -0,0 +1,37 @@
+<?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!
+ */
+
+use TYPO3\PharStreamWrapper\Resolver\PharInvocation;
+
+interface Collectable
+{
+    /**
+     * @param PharInvocation $invocation
+     * @return bool
+     */
+    public function has(PharInvocation $invocation);
+
+    /**
+     * @param PharInvocation $invocation
+     * @param null $flags
+     * @return bool
+     */
+    public function collect(PharInvocation $invocation, $flags = null);
+
+    /**
+     * @param callable $callback
+     * @param bool $reverse
+     * @return null|PharInvocation
+     */
+    public function findByCallback($callback, $reverse = false);
+}

+ 18 - 2
misc/typo3/phar-stream-wrapper/src/Helper.php

@@ -11,6 +11,13 @@ namespace TYPO3\PharStreamWrapper;
  * The TYPO3 project - inspiring people to share!
  */
 
+/**
+ * Helper provides low-level tools on file name resolving. However it does not
+ * (and should not) maintain any runtime state information. In order to resolve
+ * Phar archive paths according resolvers have to be used.
+ *
+ * @see \TYPO3\PharStreamWrapper\Resolvable::resolve()
+ */
 class Helper
 {
     /*
@@ -56,12 +63,21 @@ class Helper
 
     /**
      * @param string $path
+     * @return bool
+     */
+    public static function hasPharPrefix($path)
+    {
+        return stripos($path, 'phar://') === 0;
+    }
+
+    /**
+     * @param string $path
      * @return string
      */
     public static function removePharPrefix($path)
     {
         $path = trim($path);
-        if (stripos($path, 'phar://') !== 0) {
+        if (!static::hasPharPrefix($path)) {
             return $path;
         }
         return substr($path, 7);
@@ -77,7 +93,7 @@ class Helper
     public static function normalizePath($path)
     {
         return rtrim(
-            static::getCanonicalPath(
+            static::normalizeWindowsPath(
                 static::removePharPrefix($path)
             ),
             '/'

+ 88 - 0
misc/typo3/phar-stream-wrapper/src/Interceptor/ConjunctionInterceptor.php

@@ -0,0 +1,88 @@
+<?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\Exception;
+
+class ConjunctionInterceptor implements Assertable
+{
+    /**
+     * @var Assertable[]
+     */
+    private $assertions;
+
+    public function __construct(array $assertions)
+    {
+        $this->assertAssertions($assertions);
+        $this->assertions = $assertions;
+    }
+
+    /**
+     * Executes assertions based on all contained assertions.
+     *
+     * @param string $path
+     * @param string $command
+     * @return bool
+     * @throws Exception
+     */
+    public function assert($path, $command)
+    {
+        if ($this->invokeAssertions($path, $command)) {
+            return true;
+        }
+        throw new Exception(
+            sprintf(
+                'Assertion failed in "%s"',
+                $path
+            ),
+            1539625084
+        );
+    }
+
+    /**
+     * @param Assertable[] $assertions
+     */
+    private function assertAssertions(array $assertions)
+    {
+        foreach ($assertions as $assertion) {
+            if (!$assertion instanceof Assertable) {
+                throw new \InvalidArgumentException(
+                    sprintf(
+                        'Instance %s must implement Assertable',
+                        get_class($assertion)
+                    ),
+                    1539624719
+                );
+            }
+        }
+    }
+
+    /**
+     * @param string $path
+     * @param string $command
+     * @return bool
+     */
+    private function invokeAssertions($path, $command)
+    {
+        try {
+            foreach ($this->assertions as $assertion) {
+                if (!$assertion->assert($path, $command)) {
+                    return false;
+                }
+            }
+        } catch (Exception $exception) {
+            return false;
+        }
+        return true;
+    }
+}

+ 4 - 4
misc/typo3/phar-stream-wrapper/src/Interceptor/PharExtensionInterceptor.php

@@ -12,8 +12,8 @@ namespace TYPO3\PharStreamWrapper\Interceptor;
  */
 
 use TYPO3\PharStreamWrapper\Assertable;
-use TYPO3\PharStreamWrapper\Helper;
 use TYPO3\PharStreamWrapper\Exception;
+use TYPO3\PharStreamWrapper\Manager;
 
 class PharExtensionInterceptor implements Assertable
 {
@@ -45,11 +45,11 @@ class PharExtensionInterceptor implements Assertable
      */
     private function baseFileContainsPharExtension($path)
     {
-        $baseFile = Helper::determineBaseFile($path);
-        if ($baseFile === null) {
+        $invocation = Manager::instance()->resolve($path);
+        if ($invocation === null) {
             return false;
         }
-        $fileExtension = pathinfo($baseFile, PATHINFO_EXTENSION);
+        $fileExtension = pathinfo($invocation->getBaseName(), PATHINFO_EXTENSION);
         return strtolower($fileExtension) === 'phar';
     }
 }

+ 73 - 0
misc/typo3/phar-stream-wrapper/src/Interceptor/PharMetaDataInterceptor.php

@@ -0,0 +1,73 @@
+<?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\Exception;
+use TYPO3\PharStreamWrapper\Manager;
+use TYPO3\PharStreamWrapper\Phar\DeserializationException;
+use TYPO3\PharStreamWrapper\Phar\Reader;
+
+/**
+ * @internal Experimental implementation of checking against serialized objects in Phar meta-data
+ * @internal This functionality has not been 100% pentested...
+ */
+class PharMetaDataInterceptor implements Assertable
+{
+    /**
+     * Determines whether the according Phar archive contains
+     * (potential insecure) serialized objects.
+     *
+     * @param string $path
+     * @param string $command
+     * @return bool
+     * @throws Exception
+     */
+    public function assert($path, $command)
+    {
+        if ($this->baseFileDoesNotHaveMetaDataIssues($path)) {
+            return true;
+        }
+        throw new Exception(
+            sprintf(
+                'Problematic meta-data in "%s"',
+                $path
+            ),
+            1539632368
+        );
+    }
+
+    /**
+     * @param string $path
+     * @return bool
+     */
+    private function baseFileDoesNotHaveMetaDataIssues($path)
+    {
+        $invocation = Manager::instance()->resolve($path);
+        if ($invocation === null) {
+            return false;
+        }
+        // directly return in case invocation was checked before
+        if ($invocation->getVariable(__CLASS__) === true) {
+            return true;
+        }
+        // otherwise analyze meta-data
+        try {
+            $reader = new Reader($invocation->getBaseName());
+            $reader->resolveContainer()->getManifest()->deserializeMetaData();
+            $invocation->setVariable(__CLASS__, true);
+        } catch (DeserializationException $exception) {
+            return false;
+        }
+        return true;
+    }
+}

+ 56 - 6
misc/typo3/phar-stream-wrapper/src/Manager.php

@@ -11,7 +11,11 @@ namespace TYPO3\PharStreamWrapper;
  * The TYPO3 project - inspiring people to share!
  */
 
-class Manager implements Assertable
+use TYPO3\PharStreamWrapper\Resolver\PharInvocation;
+use TYPO3\PharStreamWrapper\Resolver\PharInvocationCollection;
+use TYPO3\PharStreamWrapper\Resolver\PharInvocationResolver;
+
+class Manager
 {
     /**
      * @var self
@@ -24,13 +28,28 @@ class Manager implements Assertable
     private $behavior;
 
     /**
+     * @var Resolvable
+     */
+    private $resolver;
+
+    /**
+     * @var Collectable
+     */
+    private $collection;
+
+    /**
      * @param Behavior $behaviour
+     * @param Resolvable $resolver
+     * @param Collectable $collection
      * @return self
      */
-    public static function initialize(Behavior $behaviour)
-    {
+    public static function initialize(
+        Behavior $behaviour,
+        Resolvable $resolver = null,
+        Collectable $collection = null
+    ) {
         if (self::$instance === null) {
-            self::$instance = new self($behaviour);
+            self::$instance = new self($behaviour, $resolver, $collection);
             return self::$instance;
         }
         throw new \LogicException(
@@ -67,9 +86,22 @@ class Manager implements Assertable
 
     /**
      * @param Behavior $behaviour
+     * @param Resolvable $resolver
+     * @param Collectable $collection
      */
-    private function __construct(Behavior $behaviour)
-    {
+    private function __construct(
+        Behavior $behaviour,
+        Resolvable $resolver = null,
+        Collectable $collection = null
+    ) {
+        if ($collection === null) {
+            $collection = new PharInvocationCollection();
+        }
+        if ($resolver === null) {
+            $resolver = new PharInvocationResolver();
+        }
+        $this->collection = $collection;
+        $this->resolver = $resolver;
         $this->behavior = $behaviour;
     }
 
@@ -82,4 +114,22 @@ class Manager implements Assertable
     {
         return $this->behavior->assert($path, $command);
     }
+
+    /**
+     * @param string $path
+     * @param null|int $flags
+     * @return null|PharInvocation
+     */
+    public function resolve($path, $flags = null)
+    {
+        return $this->resolver->resolve($path, $flags);
+    }
+
+    /**
+     * @return Collectable
+     */
+    public function getCollection()
+    {
+        return $this->collection;
+    }
 }

+ 59 - 0
misc/typo3/phar-stream-wrapper/src/Phar/Container.php

@@ -0,0 +1,59 @@
+<?php
+namespace TYPO3\PharStreamWrapper\Phar;
+
+/*
+ * 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 Container
+{
+    /**
+     * @var Stub
+     */
+    private $stub;
+
+    /**
+     * @var Manifest
+     */
+    private $manifest;
+
+    /**
+     * @param Stub $stub
+     * @param Manifest $manifest
+     */
+    public function __construct(Stub $stub, Manifest $manifest)
+    {
+        $this->stub = $stub;
+        $this->manifest = $manifest;
+    }
+
+    /**
+     * @return Stub
+     */
+    public function getStub()
+    {
+        return $this->stub;
+    }
+
+    /**
+     * @return Manifest
+     */
+    public function getManifest()
+    {
+        return $this->manifest;
+    }
+
+    /**
+     * @return string
+     */
+    public function getAlias()
+    {
+        return $this->manifest->getAlias() ?: $this->stub->getMappedAlias();
+    }
+}

+ 18 - 0
misc/typo3/phar-stream-wrapper/src/Phar/DeserializationException.php

@@ -0,0 +1,18 @@
+<?php
+namespace TYPO3\PharStreamWrapper\Phar;
+
+/*
+ * 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\Exception;
+
+class DeserializationException extends Exception
+{
+}

+ 176 - 0
misc/typo3/phar-stream-wrapper/src/Phar/Manifest.php

@@ -0,0 +1,176 @@
+<?php
+namespace TYPO3\PharStreamWrapper\Phar;
+
+/*
+ * 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 Brumann\Polyfill\Unserialize;
+
+class Manifest
+{
+    /**
+     * @param string $content
+     * @return self
+     * @see http://php.net/manual/en/phar.fileformat.phar.php
+     */
+    public static function fromContent($content)
+    {
+        $target = new static();
+        $target->manifestLength = Reader::resolveFourByteLittleEndian($content, 0);
+        $target->amountOfFiles = Reader::resolveFourByteLittleEndian($content, 4);
+        $target->flags = Reader::resolveFourByteLittleEndian($content, 10);
+        $target->aliasLength = Reader::resolveFourByteLittleEndian($content, 14);
+        $target->alias = substr($content, 18, $target->aliasLength);
+        $target->metaDataLength = Reader::resolveFourByteLittleEndian($content, 18 + $target->aliasLength);
+        $target->metaData = substr($content, 22 + $target->aliasLength, $target->metaDataLength);
+
+        $apiVersionNibbles = Reader::resolveTwoByteBigEndian($content, 8);
+        $target->apiVersion = implode('.', array(
+            ($apiVersionNibbles & 0xf000) >> 12,
+            ($apiVersionNibbles & 0x0f00) >> 8,
+            ($apiVersionNibbles & 0x00f0) >> 4,
+        ));
+
+        return $target;
+    }
+
+    /**
+     * @var int
+     */
+    private $manifestLength;
+
+    /**
+     * @var int
+     */
+    private $amountOfFiles;
+
+    /**
+     * @var string
+     */
+    private $apiVersion;
+
+    /**
+     * @var int
+     */
+    private $flags;
+
+    /**
+     * @var int
+     */
+    private $aliasLength;
+
+    /**
+     * @var string
+     */
+    private $alias;
+
+    /**
+     * @var int
+     */
+    private $metaDataLength;
+
+    /**
+     * @var string
+     */
+    private $metaData;
+
+    /**
+     * Avoid direct instantiation.
+     */
+    private function __construct()
+    {
+    }
+
+    /**
+     * @return int
+     */
+    public function getManifestLength()
+    {
+        return $this->manifestLength;
+    }
+
+    /**
+     * @return int
+     */
+    public function getAmountOfFiles()
+    {
+        return $this->amountOfFiles;
+    }
+
+    /**
+     * @return string
+     */
+    public function getApiVersion()
+    {
+        return $this->apiVersion;
+    }
+
+    /**
+     * @return int
+     */
+    public function getFlags()
+    {
+        return $this->flags;
+    }
+
+    /**
+     * @return int
+     */
+    public function getAliasLength()
+    {
+        return $this->aliasLength;
+    }
+
+    /**
+     * @return string
+     */
+    public function getAlias()
+    {
+        return $this->alias;
+    }
+
+    /**
+     * @return int
+     */
+    public function getMetaDataLength()
+    {
+        return $this->metaDataLength;
+    }
+
+    /**
+     * @return string
+     */
+    public function getMetaData()
+    {
+        return $this->metaData;
+    }
+
+    /**
+     * @return mixed|null
+     */
+    public function deserializeMetaData()
+    {
+        if (empty($this->metaData)) {
+            return null;
+        }
+
+        $result = Unserialize::unserialize($this->metaData, array('allowed_classes' => false));
+
+        $serialized = json_encode($result);
+        if (strpos($serialized, '__PHP_Incomplete_Class_Name') !== false) {
+            throw new DeserializationException(
+                'Meta-data contains serialized object',
+                1539623382
+            );
+        }
+
+        return $result;
+    }
+}

+ 220 - 0
misc/typo3/phar-stream-wrapper/src/Phar/Reader.php

@@ -0,0 +1,220 @@
+<?php
+namespace TYPO3\PharStreamWrapper\Phar;
+
+/*
+ * 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 Reader
+{
+    /**
+     * @var string
+     */
+    private $fileName;
+
+    /**
+     * @var string
+     */
+    private $fileType;
+
+    /**
+     * @param string $fileName
+     */
+    public function __construct($fileName)
+    {
+        if (strpos($fileName, '://') !== false) {
+            throw new ReaderException(
+                'File name must not contain stream prefix',
+                1539623708
+            );
+        }
+
+        $this->fileName = $fileName;
+        $this->fileType = $this->determineFileType();
+    }
+
+    /**
+     * @return Container
+     */
+    public function resolveContainer()
+    {
+        $data = $this->extractData($this->resolveStream() . $this->fileName);
+
+        if ($data['stubContent'] === null) {
+            throw new ReaderException(
+                'Cannot resolve stub',
+                1547807881
+            );
+        }
+        if ($data['manifestContent'] === null || $data['manifestLength'] === null) {
+            throw new ReaderException(
+                'Cannot resolve manifest',
+                1547807882
+            );
+        }
+        if (strlen($data['manifestContent']) < $data['manifestLength']) {
+            throw new ReaderException(
+                sprintf(
+                    'Exected manifest length %d, got %d',
+                    strlen($data['manifestContent']),
+                    $data['manifestLength']
+                ),
+                1547807883
+            );
+        }
+
+        return new Container(
+            Stub::fromContent($data['stubContent']),
+            Manifest::fromContent($data['manifestContent'])
+        );
+    }
+
+    /**
+     * @param string $fileName e.g. '/path/file.phar' or 'compress.zlib:///path/file.phar'
+     * @return array
+     */
+    private function extractData($fileName)
+    {
+        $stubContent = null;
+        $manifestContent = null;
+        $manifestLength = null;
+
+        $resource = fopen($fileName, 'r');
+        if (!is_resource($resource)) {
+            throw new ReaderException(
+                sprintf('Resource %s could not be opened', $fileName),
+                1547902055
+            );
+        }
+
+        while (!feof($resource)) {
+            $line = fgets($resource);
+            // stop reading file when manifest can be extracted
+            if ($manifestLength !== null && $manifestContent !== null && strlen($manifestContent) >= $manifestLength) {
+                break;
+            }
+
+            $manifestPosition = strpos($line, '__HALT_COMPILER();');
+
+            // first line contains start of manifest
+            if ($stubContent === null && $manifestContent === null && $manifestPosition !== false) {
+                $stubContent = substr($line, 0, $manifestPosition - 1);
+                $manifestContent = preg_replace('#^.*__HALT_COMPILER\(\);(?>[ \n]\?>(?>\r\n|\n)?)?#', '', $line);
+                $manifestLength = $this->resolveManifestLength($manifestContent);
+            // line contains start of stub
+            } elseif ($stubContent === null) {
+                $stubContent = $line;
+            // line contains start of manifest
+            } elseif ($manifestContent === null && $manifestPosition !== false) {
+                $manifestContent = preg_replace('#^.*__HALT_COMPILER\(\);(?>[ \n]\?>(?>\r\n|\n)?)?#', '', $line);
+                $manifestLength = $this->resolveManifestLength($manifestContent);
+            // manifest has been started (thus is cannot be stub anymore), add content
+            } elseif ($manifestContent !== null) {
+                $manifestContent .= $line;
+                $manifestLength = $this->resolveManifestLength($manifestContent);
+            // stub has been started (thus cannot be manifest here, yet), add content
+            } elseif ($stubContent !== null) {
+                $stubContent .= $line;
+            }
+        }
+        fclose($resource);
+
+        return array(
+            'stubContent' => $stubContent,
+            'manifestContent' => $manifestContent,
+            'manifestLength' => $manifestLength,
+        );
+    }
+
+    /**
+     * Resolves stream in order to handle compressed Phar archives.
+     *
+     * @return string
+     */
+    private function resolveStream()
+    {
+        if ($this->fileType === 'application/x-gzip') {
+            return 'compress.zlib://';
+        } elseif ($this->fileType === 'application/x-bzip2') {
+            return 'compress.bzip2://';
+        }
+        return '';
+    }
+
+    /**
+     * @return string
+     */
+    private function determineFileType()
+    {
+        $fileInfo = new \finfo();
+        return $fileInfo->file($this->fileName, FILEINFO_MIME_TYPE);
+    }
+
+    /**
+     * @param string $content
+     * @return int|null
+     */
+    private function resolveManifestLength($content)
+    {
+        if (strlen($content) < 4) {
+            return null;
+        }
+        return static::resolveFourByteLittleEndian($content, 0);
+    }
+
+    /**
+     * @param string $content
+     * @param int $start
+     * @return int
+     */
+    public static function resolveFourByteLittleEndian($content, $start)
+    {
+        $payload = substr($content, $start, 4);
+        if (!is_string($payload)) {
+            throw new ReaderException(
+                sprintf('Cannot resolve value at offset %d', $start),
+                1539614260
+            );
+        }
+
+        $value = unpack('V', $payload);
+        if (!isset($value[1])) {
+            throw new ReaderException(
+                sprintf('Cannot resolve value at offset %d', $start),
+                1539614261
+            );
+        }
+        return $value[1];
+    }
+
+    /**
+     * @param string $content
+     * @param int $start
+     * @return int
+     */
+    public static function resolveTwoByteBigEndian($content, $start)
+    {
+        $payload = substr($content, $start, 2);
+        if (!is_string($payload)) {
+            throw new ReaderException(
+                sprintf('Cannot resolve value at offset %d', $start),
+                1539614263
+            );
+        }
+
+        $value = unpack('n', $payload);
+        if (!isset($value[1])) {
+            throw new ReaderException(
+                sprintf('Cannot resolve value at offset %d', $start),
+                1539614264
+            );
+        }
+        return $value[1];
+    }
+}

+ 18 - 0
misc/typo3/phar-stream-wrapper/src/Phar/ReaderException.php

@@ -0,0 +1,18 @@
+<?php
+namespace TYPO3\PharStreamWrapper\Phar;
+
+/*
+ * 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\Exception;
+
+class ReaderException extends Exception
+{
+}

+ 65 - 0
misc/typo3/phar-stream-wrapper/src/Phar/Stub.php

@@ -0,0 +1,65 @@
+<?php
+namespace TYPO3\PharStreamWrapper\Phar;
+
+/*
+ * 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!
+ */
+
+/**
+ * @internal Experimental implementation of Phar archive internals
+ */
+class Stub
+{
+    /**
+     * @param string $content
+     * @return self
+     */
+    public static function fromContent($content)
+    {
+        $target = new static();
+        $target->content = $content;
+
+        if (
+            stripos($content, 'Phar::mapPhar(') !== false
+            && preg_match('#Phar\:\:mapPhar\(([^)]+)\)#', $content, $matches)
+        ) {
+            // remove spaces, single & double quotes
+            // @todo `'my' . 'alias' . '.phar'` is not evaluated here
+            $target->mappedAlias = trim($matches[1], ' \'"');
+        }
+
+        return $target;
+    }
+
+    /**
+     * @var string
+     */
+    private $content;
+
+    /**
+     * @var string
+     */
+    private $mappedAlias = '';
+
+    /**
+     * @return string
+     */
+    public function getContent()
+    {
+        return $this->content;
+    }
+
+    /**
+     * @return string
+     */
+    public function getMappedAlias()
+    {
+        return $this->mappedAlias;
+    }
+}

+ 36 - 2
misc/typo3/phar-stream-wrapper/src/PharStreamWrapper.php

@@ -11,6 +11,8 @@ namespace TYPO3\PharStreamWrapper;
  * The TYPO3 project - inspiring people to share!
  */
 
+use TYPO3\PharStreamWrapper\Resolver\PharInvocation;
+
 class PharStreamWrapper
 {
     /**
@@ -30,6 +32,11 @@ class PharStreamWrapper
     protected $internalResource;
 
     /**
+     * @var PharInvocation
+     */
+    protected $invocation;
+
+    /**
      * @return bool
      */
     public function dir_closedir()
@@ -409,7 +416,8 @@ class PharStreamWrapper
      */
     protected function assert($path, $command)
     {
-        if ($this->resolveAssertable()->assert($path, $command) === true) {
+        if (Manager::instance()->assert($path, $command) === true) {
+            $this->collectInvocation($path);
             return;
         }
 
@@ -424,7 +432,33 @@ class PharStreamWrapper
     }
 
     /**
-     * @return Assertable
+     * @param string $path
+     */
+    protected function collectInvocation($path)
+    {
+        if (isset($this->invocation)) {
+            return;
+        }
+
+        $manager = Manager::instance();
+        $this->invocation = $manager->resolve($path);
+        if ($this->invocation === null) {
+            throw new Exception(
+                'Expected invocation could not be resolved',
+                1556389591
+            );
+        }
+        // confirm, previous interceptor(s) validated invocation
+        $this->invocation->confirm();
+        $collection = $manager->getCollection();
+        if (!$collection->has($this->invocation)) {
+            $collection->collect($this->invocation);
+        }
+    }
+
+    /**
+     * @return Manager|Assertable
+     * @deprecated Use Manager::instance() directly
      */
     protected function resolveAssertable()
     {

+ 24 - 0
misc/typo3/phar-stream-wrapper/src/Resolvable.php

@@ -0,0 +1,24 @@
+<?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!
+ */
+
+use TYPO3\PharStreamWrapper\Resolver\PharInvocation;
+
+interface Resolvable
+{
+    /**
+     * @param string $path
+     * @param null|int $flags
+     * @return null|PharInvocation
+     */
+    public function resolve($path, $flags = null);
+}

+ 125 - 0
misc/typo3/phar-stream-wrapper/src/Resolver/PharInvocation.php

@@ -0,0 +1,125 @@
+<?php
+namespace TYPO3\PharStreamWrapper\Resolver;
+
+/*
+ * 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\Exception;
+
+class PharInvocation
+{
+    /**
+     * @var string
+     */
+    private $baseName;
+
+    /**
+     * @var string
+     */
+    private $alias;
+
+    /**
+     * @var bool
+     * @see \TYPO3\PharStreamWrapper\PharStreamWrapper::collectInvocation()
+     */
+    private $confirmed = false;
+
+    /**
+     * Arbitrary variables to be used by interceptors as registry
+     * (e.g. in order to avoid duplicate processing and assertions)
+     *
+     * @var array
+     */
+    private $variables;
+
+    /**
+     * @param string $baseName
+     * @param string $alias
+     */
+    public function __construct($baseName, $alias = '')
+    {
+        if ($baseName === '') {
+            throw new Exception(
+                'Base-name cannot be empty',
+                1551283689
+            );
+        }
+        $this->baseName = $baseName;
+        $this->alias = $alias;
+    }
+
+    /**
+     * @return string
+     */
+    public function __toString()
+    {
+        return $this->baseName;
+    }
+
+    /**
+     * @return string
+     */
+    public function getBaseName()
+    {
+        return $this->baseName;
+    }
+
+    /**
+     * @return null|string
+     */
+    public function getAlias()
+    {
+        return $this->alias;
+    }
+
+    /**
+     * @return bool
+     */
+    public function isConfirmed()
+    {
+        return $this->confirmed;
+    }
+
+    public function confirm()
+    {
+        $this->confirmed = true;
+    }
+
+    /**
+     * @param string $name
+     * @return mixed|null
+     */
+    public function getVariable($name)
+    {
+        if (!isset($this->variables[$name])) {
+            return null;
+        }
+        return $this->variables[$name];
+    }
+
+    /**
+     * @param string $name
+     * @param mixed $value
+     */
+    public function setVariable($name, $value)
+    {
+        $this->variables[$name] = $value;
+    }
+
+    /**
+     * @param PharInvocation $other
+     * @return bool
+     */
+    public function equals(PharInvocation $other)
+    {
+        return $other->baseName === $this->baseName
+            && $other->alias === $this->alias;
+    }
+}

+ 156 - 0
misc/typo3/phar-stream-wrapper/src/Resolver/PharInvocationCollection.php

@@ -0,0 +1,156 @@
+<?php
+namespace TYPO3\PharStreamWrapper\Resolver;
+
+/*
+ * 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\Collectable;
+
+class PharInvocationCollection implements Collectable
+{
+    const UNIQUE_INVOCATION = 1;
+    const UNIQUE_BASE_NAME = 2;
+    const DUPLICATE_ALIAS_WARNING = 32;
+
+    /**
+     * @var PharInvocation[]
+     */
+    private $invocations = array();
+
+    /**
+     * @param PharInvocation $invocation
+     * @return bool
+     */
+    public function has(PharInvocation $invocation)
+    {
+        return in_array($invocation, $this->invocations, true);
+    }
+
+    /**
+     * @param PharInvocation $invocation
+     * @param null|int $flags
+     * @return bool
+     */
+    public function collect(PharInvocation $invocation, $flags = null)
+    {
+        if ($flags === null) {
+            $flags = static::UNIQUE_INVOCATION | static::DUPLICATE_ALIAS_WARNING;
+        }
+        if ($invocation->getBaseName() === ''
+            || $invocation->getAlias() === ''
+            || !$this->assertUniqueBaseName($invocation, $flags)
+            || !$this->assertUniqueInvocation($invocation, $flags)
+        ) {
+            return false;
+        }
+        if ($flags & static::DUPLICATE_ALIAS_WARNING) {
+            $this->triggerDuplicateAliasWarning($invocation);
+        }
+
+        $this->invocations[] = $invocation;
+        return true;
+    }
+
+    /**
+     * @param callable $callback
+     * @param bool $reverse
+     * @return null|PharInvocation
+     */
+    public function findByCallback($callback, $reverse = false)
+    {
+        foreach ($this->getInvocations($reverse) as $invocation) {
+            if (call_user_func($callback, $invocation) === true) {
+                return $invocation;
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Asserts that base-name is unique. This disallows having multiple invocations for
+     * same base-name but having different alias names.
+     *
+     * @param PharInvocation $invocation
+     * @param int $flags
+     * @return bool
+     */
+    private function assertUniqueBaseName(PharInvocation $invocation, $flags)
+    {
+        if (!($flags & static::UNIQUE_BASE_NAME)) {
+            return true;
+        }
+        return $this->findByCallback(
+                function (PharInvocation $candidate) use ($invocation) {
+                    return $candidate->getBaseName() === $invocation->getBaseName();
+                }
+            ) === null;
+    }
+
+    /**
+     * Asserts that combination of base-name and alias is unique. This allows having multiple
+     * invocations for same base-name but having different alias names (for whatever reason).
+     *
+     * @param PharInvocation $invocation
+     * @param int $flags
+     * @return bool
+     */
+    private function assertUniqueInvocation(PharInvocation $invocation, $flags)
+    {
+        if (!($flags & static::UNIQUE_INVOCATION)) {
+            return true;
+        }
+        return $this->findByCallback(
+                function (PharInvocation $candidate) use ($invocation) {
+                    return $candidate->equals($invocation);
+                }
+            ) === null;
+    }
+
+    /**
+     * Triggers warning for invocations with same alias and same confirmation state.
+     *
+     * @param PharInvocation $invocation
+     * @see \TYPO3\PharStreamWrapper\PharStreamWrapper::collectInvocation()
+     */
+    private function triggerDuplicateAliasWarning(PharInvocation $invocation)
+    {
+        $sameAliasInvocation = $this->findByCallback(
+            function (PharInvocation $candidate) use ($invocation) {
+                return $candidate->isConfirmed() === $invocation->isConfirmed()
+                    && $candidate->getAlias() === $invocation->getAlias();
+            },
+            true
+        );
+        if ($sameAliasInvocation === null) {
+            return;
+        }
+        trigger_error(
+            sprintf(
+                'Alias %s cannot be used by %s, already used by %s',
+                $invocation->getAlias(),
+                $invocation->getBaseName(),
+                $sameAliasInvocation->getBaseName()
+            ),
+            E_USER_WARNING
+        );
+    }
+
+    /**
+     * @param bool $reverse
+     * @return PharInvocation[]
+     */
+    private function getInvocations($reverse = false)
+    {
+        if ($reverse) {
+            return array_reverse($this->invocations);
+        }
+        return $this->invocations;
+    }
+}

+ 241 - 0
misc/typo3/phar-stream-wrapper/src/Resolver/PharInvocationResolver.php

@@ -0,0 +1,241 @@
+<?php
+namespace TYPO3\PharStreamWrapper\Resolver;
+
+/*
+ * 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\Helper;
+use TYPO3\PharStreamWrapper\Manager;
+use TYPO3\PharStreamWrapper\Phar\Reader;
+use TYPO3\PharStreamWrapper\Resolvable;
+
+class PharInvocationResolver implements Resolvable
+{
+    const RESOLVE_REALPATH = 1;
+    const RESOLVE_ALIAS = 2;
+    const ASSERT_INTERNAL_INVOCATION = 32;
+
+    /**
+     * @var string[]
+     */
+    private $invocationFunctionNames = array(
+        'include',
+        'include_once',
+        'require',
+        'require_once'
+    );
+
+    /**
+     * Contains resolved base names in order to reduce file IO.
+     *
+     * @var string[]
+     */
+    private $baseNames = array();
+
+    /**
+     * Resolves PharInvocation value object (baseName and optional alias).
+     *
+     * Phar aliases are intended to be used only inside Phar archives, however
+     * PharStreamWrapper needs this information exposed outside of Phar as well
+     * It is possible that same alias is used for different $baseName values.
+     * That's why PharInvocationCollection behaves like a stack when resolving
+     * base-name for a given alias. On the other hand it is not possible that
+     * one $baseName is referring to multiple aliases.
+     * @see https://secure.php.net/manual/en/phar.setalias.php
+     * @see https://secure.php.net/manual/en/phar.mapphar.php
+     *
+     * @param string $path
+     * @param int|null $flags
+     * @return null|PharInvocation
+     */
+    public function resolve($path, $flags = null)
+    {
+        $hasPharPrefix = Helper::hasPharPrefix($path);
+        if ($flags === null) {
+            $flags = static::RESOLVE_REALPATH | static::RESOLVE_ALIAS | static::ASSERT_INTERNAL_INVOCATION;
+        }
+
+        if ($hasPharPrefix && $flags & static::RESOLVE_ALIAS) {
+            $invocation = $this->findByAlias($path);
+            if ($invocation !== null) {
+                return $invocation;
+            }
+        }
+
+        $baseName = $this->resolveBaseName($path, $flags);
+        if ($baseName === null) {
+            return null;
+        }
+
+        if ($flags & static::RESOLVE_REALPATH) {
+            $baseName = $this->baseNames[$baseName];
+        }
+
+        return $this->retrieveInvocation($baseName, $flags);
+    }
+
+    /**
+     * Retrieves PharInvocation, either existing in collection or created on demand
+     * with resolving a potential alias name used in the according Phar archive.
+     *
+     * @param string $baseName
+     * @param int $flags
+     * @return PharInvocation
+     */
+    private function retrieveInvocation($baseName, $flags)
+    {
+        $invocation = $this->findByBaseName($baseName);
+        if ($invocation !== null) {
+            return $invocation;
+        }
+
+        if ($flags & static::RESOLVE_ALIAS) {
+            $reader = new Reader($baseName);
+            $alias = $reader->resolveContainer()->getAlias();
+        } else {
+            $alias = '';
+        }
+        // add unconfirmed(!) new invocation to collection
+        $invocation = new PharInvocation($baseName, $alias);
+        Manager::instance()->getCollection()->collect($invocation);
+        return $invocation;
+    }
+
+    /**
+     * @param string $path
+     * @param int $flags
+     * @return null|string
+     */
+    private function resolveBaseName($path, $flags)
+    {
+        $baseName = $this->findInBaseNames($path);
+        if ($baseName !== null) {
+            return $baseName;
+        }
+
+        $baseName = Helper::determineBaseFile($path);
+        if ($baseName !== null) {
+            $this->addBaseName($baseName);
+            return $baseName;
+        }
+
+        $possibleAlias = $this->resolvePossibleAlias($path);
+        if (!($flags & static::RESOLVE_ALIAS) || $possibleAlias === null) {
+            return null;
+        }
+
+        $trace = debug_backtrace();
+        foreach ($trace as $item) {
+            if (!isset($item['function']) || !isset($item['args'][0])
+                || !in_array($item['function'], $this->invocationFunctionNames, true)) {
+                continue;
+            }
+            $currentPath = $item['args'][0];
+            if (Helper::hasPharPrefix($currentPath)) {
+                continue;
+            }
+            $currentBaseName = Helper::determineBaseFile($currentPath);
+            if ($currentBaseName === null) {
+                continue;
+            }
+            // ensure the possible alias name (how we have been called initially) matches
+            // the resolved alias name that was retrieved by the current possible base name
+            $reader = new Reader($currentBaseName);
+            $currentAlias = $reader->resolveContainer()->getAlias();
+            if ($currentAlias !== $possibleAlias) {
+                continue;
+            }
+            $this->addBaseName($currentBaseName);
+            return $currentBaseName;
+        }
+
+        return null;
+    }
+
+    /**
+     * @param string $path
+     * @return null|string
+     */
+    private function resolvePossibleAlias($path)
+    {
+        $normalizedPath = Helper::normalizePath($path);
+        return strstr($normalizedPath, '/', true) ?: null;
+    }
+
+    /**
+     * @param string $baseName
+     * @return null|PharInvocation
+     */
+    private function findByBaseName($baseName)
+    {
+        return Manager::instance()->getCollection()->findByCallback(
+            function (PharInvocation $candidate) use ($baseName) {
+                return $candidate->getBaseName() === $baseName;
+            },
+            true
+        );
+    }
+
+    /**
+     * @param string $path
+     * @return null|string
+     */
+    private function findInBaseNames($path)
+    {
+        // return directly if the resolved base name was submitted
+        if (in_array($path, $this->baseNames, true)) {
+            return $path;
+        }
+
+        $parts = explode('/', Helper::normalizePath($path));
+
+        while (count($parts)) {
+            $currentPath = implode('/', $parts);
+            if (isset($this->baseNames[$currentPath])) {
+                return $currentPath;
+            }
+            array_pop($parts);
+        }
+
+        return null;
+    }
+
+    /**
+     * @param string $baseName
+     */
+    private function addBaseName($baseName)
+    {
+        if (isset($this->baseNames[$baseName])) {
+            return;
+        }
+        $this->baseNames[$baseName] = realpath($baseName);
+    }
+
+    /**
+     * Finds confirmed(!) invocations by alias.
+     *
+     * @param string $path
+     * @return null|PharInvocation
+     * @see \TYPO3\PharStreamWrapper\PharStreamWrapper::collectInvocation()
+     */
+    private function findByAlias($path)
+    {
+        $possibleAlias = $this->resolvePossibleAlias($path);
+        if ($possibleAlias === null) {
+            return null;
+        }
+        return Manager::instance()->getCollection()->findByCallback(
+            function (PharInvocation $candidate) use ($possibleAlias) {
+                return $candidate->isConfirmed() && $candidate->getAlias() === $possibleAlias;
+            },
+            true
+        );
+    }
+}

+ 3 - 3
modules/aggregator/aggregator.info

@@ -7,7 +7,7 @@ files[] = aggregator.test
 configure = admin/config/services/aggregator/settings
 stylesheets[all][] = aggregator.css
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/aggregator/tests/aggregator_test.info

@@ -5,7 +5,7 @@ version = VERSION
 core = 7.x
 hidden = TRUE
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/block/block.info

@@ -6,7 +6,7 @@ core = 7.x
 files[] = block.test
 configure = admin/structure/block
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/block/tests/block_test.info

@@ -5,7 +5,7 @@ version = VERSION
 core = 7.x
 hidden = TRUE
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/block/tests/themes/block_test_theme/block_test_theme.info

@@ -13,7 +13,7 @@ regions[footer] = Footer
 regions[highlighted] = Highlighted
 regions[help] = Help
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/blog/blog.info

@@ -5,7 +5,7 @@ version = VERSION
 core = 7.x
 files[] = blog.test
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/book/book.info

@@ -7,7 +7,7 @@ files[] = book.test
 configure = admin/content/book/settings
 stylesheets[all][] = book.css
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/color/color.info

@@ -5,7 +5,7 @@ version = VERSION
 core = 7.x
 files[] = color.test
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/comment/comment.info

@@ -9,7 +9,7 @@ files[] = comment.test
 configure = admin/content/comment
 stylesheets[all][] = comment.css
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/contact/contact.info

@@ -6,7 +6,7 @@ core = 7.x
 files[] = contact.test
 configure = admin/structure/contact
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/contextual/contextual.info

@@ -5,7 +5,7 @@ version = VERSION
 core = 7.x
 files[] = contextual.test
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/dashboard/dashboard.info

@@ -7,7 +7,7 @@ files[] = dashboard.test
 dependencies[] = block
 configure = admin/dashboard/customize
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/dblog/dblog.info

@@ -5,7 +5,7 @@ version = VERSION
 core = 7.x
 files[] = dblog.test
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/field/field.info

@@ -11,7 +11,7 @@ dependencies[] = field_sql_storage
 required = TRUE
 stylesheets[all][] = theme/field.css
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/field/modules/field_sql_storage/field_sql_storage.info

@@ -7,7 +7,7 @@ dependencies[] = field
 files[] = field_sql_storage.test
 required = TRUE
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/field/modules/list/list.info

@@ -7,7 +7,7 @@ dependencies[] = field
 dependencies[] = options
 files[] = tests/list.test
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/field/modules/list/tests/list_test.info

@@ -5,7 +5,7 @@ package = Testing
 version = VERSION
 hidden = TRUE
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/field/modules/number/number.info

@@ -6,7 +6,7 @@ core = 7.x
 dependencies[] = field
 files[] = number.test
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 1 - 1
modules/field/modules/number/number.test

@@ -69,7 +69,7 @@ class NumberFieldTestCase extends DrupalWebTestCase {
     preg_match('|test-entity/manage/(\d+)/edit|', $this->url, $match);
     $id = $match[1];
     $this->assertRaw(t('test_entity @id has been created.', array('@id' => $id)), 'Entity was created');
-    $this->assertRaw(round($value, 2), 'Value is displayed.');
+    $this->assertRaw($value, 'Value is displayed.');
 
     // Try to create entries with more than one decimal separator; assert fail.
     $wrong_entries = array(

+ 3 - 3
modules/field/modules/options/options.info

@@ -6,7 +6,7 @@ core = 7.x
 dependencies[] = field
 files[] = options.test
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/field/modules/text/text.info

@@ -7,7 +7,7 @@ dependencies[] = field
 files[] = text.test
 required = TRUE
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/field/tests/field_test.info

@@ -6,7 +6,7 @@ files[] = field_test.entity.inc
 version = VERSION
 hidden = TRUE
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/field_ui/field_ui.info

@@ -6,7 +6,7 @@ core = 7.x
 dependencies[] = field
 files[] = field_ui.test
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 1 - 1
modules/file/file.field.inc

@@ -599,7 +599,7 @@ function file_field_widget_value($element, $input = FALSE, $form_state) {
     // If the display field is present make sure its unchecked value is saved.
     $field = field_widget_field($element, $form_state);
     if (empty($input['display'])) {
-      $input['display'] = $field['settings']['display_field'] ? 0 : 1;
+      $input['display'] = !empty($field['settings']['display_field']) ? 0 : 1;
     }
   }
 

+ 3 - 3
modules/file/file.info

@@ -6,7 +6,7 @@ core = 7.x
 dependencies[] = field
 files[] = tests/file.test
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 57 - 0
modules/file/tests/file.test

@@ -1875,3 +1875,60 @@ class FileFieldAnonymousSubmission extends FileFieldTestCase {
   }
 
 }
+
+/**
+ * Tests the file_scan_directory() function.
+ */
+class FileScanDirectory extends FileFieldTestCase {
+
+  /**
+   * @var string
+   */
+  protected $path;
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getInfo() {
+    return array(
+      'name' => 'File ScanDirectory',
+      'description' => 'Tests the file_scan_directory() function.',
+      'group' => 'File',
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  function setUp() {
+    parent::setUp();
+
+    $this->path = 'modules/file/tests/fixtures/file_scan_ignore';
+  }
+
+  /**
+   * Tests file_scan_directory() obeys 'file_scan_ignore_directories' setting.
+   * If nomask is not passed as argument, it should use the default settings.
+   * If nomask is passed as argument, it should obey this rule.
+   */
+  public function testNoMask() {
+    $files = file_scan_directory($this->path, '/\.txt$/');
+    $this->assertEqual(3, count($files), '3 text files found when not ignoring directories.');
+
+    global $conf;
+    $conf['file_scan_ignore_directories'] = array('frontend_framework');
+
+    $files = file_scan_directory($this->path, '/\.txt$/');
+    $this->assertEqual(1, count($files), '1 text files found when ignoring directories called "frontend_framework".');
+
+    // Make that directories specified by default still work when a new nomask is provided.
+    $files = file_scan_directory($this->path, '/\.txt$/', array('nomask' => '/^c.txt/'));
+    $this->assertEqual(2, count($files), '2 text files found when an "nomask" option is passed in.');
+
+    // Ensure that the directories in file_scan_ignore_directories are escaped using preg_quote.
+    $conf['file_scan_ignore_directories'] = array('frontend.*');
+    $files = file_scan_directory($this->path, '/\.txt$/');
+    $this->assertEqual(3, count($files), '2 text files found when ignoring a directory that is not there.');
+  }
+
+}

+ 3 - 3
modules/file/tests/file_module_test.info

@@ -5,7 +5,7 @@ version = VERSION
 core = 7.x
 hidden = TRUE
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 0 - 0
modules/file/tests/fixtures/file_scan_ignore/a.txt


+ 0 - 0
modules/file/tests/fixtures/file_scan_ignore/frontend_framework/b.txt


+ 0 - 0
modules/file/tests/fixtures/file_scan_ignore/frontend_framework/c.txt


+ 3 - 3
modules/filter/filter.info

@@ -7,7 +7,7 @@ files[] = filter.test
 required = TRUE
 configure = admin/config/content/formats
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"
 project = "drupal"
-datestamp = "1547681965"
+datestamp = "1557336079"

+ 3 - 3
modules/forum/forum.info

@@ -9,7 +9,7 @@ files[] = forum.test
 configure = admin/structure/forum
 stylesheets[all][] = forum.css
 
-; Information added by Drupal.org packaging script on 2019-01-16
-version = "7.63"
+; Information added by Drupal.org packaging script on 2019-05-08
+version = "7.67"