"Fossies" - the Fresh Open Source Software Archive

Member "drupal-9.1.0-rc1/core/modules/update/src/ProjectSecurityData.php" (18 Nov 2020, 10433 Bytes) of package /linux/www/drupal-9.1.0-rc1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) PHP source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "ProjectSecurityData.php" see the Fossies "Dox" file reference documentation.

    1 <?php
    2 
    3 namespace Drupal\update;
    4 
    5 /**
    6  * Calculates a project's security coverage information.
    7  *
    8  * @internal
    9  *   This class implements logic to determine security coverage for Drupal core
   10  *   according to Drupal core security policy. It should not be called directly.
   11  */
   12 final class ProjectSecurityData {
   13 
   14   /**
   15    * The number of minor versions of Drupal core that receive security coverage.
   16    *
   17    * For example, if this value is 2 and the existing version is 9.0.1, the
   18    * 9.0.x branch will receive security coverage until the release of version
   19    * 9.2.0.
   20    *
   21    * @todo In https://www.drupal.org/node/2998285 determine if we want this
   22    *   policy to be expressed in the updates.drupal.org feed, instead of relying
   23    *   on a hard-coded constant.
   24    *
   25    * @see https://www.drupal.org/core/release-cycle-overview
   26    */
   27   const CORE_MINORS_WITH_SECURITY_COVERAGE = 2;
   28 
   29   /**
   30    * Define constants for versions with security coverage end dates.
   31    *
   32    * Two types of constants are supported:
   33    * - SECURITY_COVERAGE_END_DATE_[VERSION_MAJOR]_[VERSION_MINOR]: A date in
   34    *   'Y-m-d' or 'Y-m' format.
   35    * - SECURITY_COVERAGE_ENDING_WARN_DATE_[VERSION_MAJOR]_[VERSION_MINOR]: A
   36    *   date in 'Y-m-d' format.
   37    *
   38    * @see \Drupal\update\ProjectSecurityRequirement::getDateEndRequirement()
   39    */
   40   const SECURITY_COVERAGE_END_DATE_8_8 = '2020-12-02';
   41 
   42   const SECURITY_COVERAGE_ENDING_WARN_DATE_8_8 = '2020-06-02';
   43 
   44   const SECURITY_COVERAGE_END_DATE_8_9 = '2021-11';
   45 
   46   /**
   47    * The existing (currently installed) version of the project.
   48    *
   49    * Because this class only handles the Drupal core project, values will be
   50    * semantic version numbers such as 8.8.0, 8.8.0-alpha1, or 9.0.0.
   51    *
   52    * @var string|null
   53    */
   54   protected $existingVersion;
   55 
   56   /**
   57    * Releases as returned by update_get_available().
   58    *
   59    * @var array
   60    *
   61    * Each release item in the array has metadata about that release. This class
   62    * uses the keys:
   63    * - status (string): The status of the release.
   64    * - version (string): The version number of the release.
   65    *
   66    * @see update_get_available()
   67    */
   68   protected $releases;
   69 
   70   /**
   71    * Constructs a ProjectSecurityData object.
   72    *
   73    * @param string $existing_version
   74    *   The existing (currently installed) version of the project.
   75    * @param array $releases
   76    *   Project releases as returned by update_get_available().
   77    */
   78   private function __construct($existing_version = NULL, array $releases = []) {
   79     $this->existingVersion = $existing_version;
   80     $this->releases = $releases;
   81   }
   82 
   83   /**
   84    * Creates a ProjectSecurityData object from project data and releases.
   85    *
   86    * @param array $project_data
   87    *   Project data from Drupal\update\UpdateManagerInterface::getProjects() and
   88    *   processed by update_process_project_info().
   89    * @param array $releases
   90    *   Project releases as returned by update_get_available().
   91    *
   92    * @return static
   93    */
   94   public static function createFromProjectDataAndReleases(array $project_data, array $releases) {
   95     if (!($project_data['project_type'] === 'core' && $project_data['name'] === 'drupal')) {
   96       // Only Drupal core has an explicit coverage range.
   97       return new static();
   98     }
   99     return new static($project_data['existing_version'], $releases);
  100   }
  101 
  102   /**
  103    * Gets the security coverage information for a project.
  104    *
  105    * Currently only Drupal core is supported.
  106    *
  107    * @return array
  108    *   The security coverage information, or an empty array if no security
  109    *   information is available for the project. If security coverage is based
  110    *   on release of a specific version, the array will have the following
  111    *   keys:
  112    *   - security_coverage_end_version (string): The minor version the existing
  113    *     version will receive security coverage until.
  114    *   - additional_minors_coverage (int): The number of additional minor
  115    *     versions the existing version will receive security coverage.
  116    *   If the security coverage is based on a specific date, the array will have
  117    *   the following keys:
  118    *   - security_coverage_end_date (string): The month or date security
  119    *     coverage will end for the existing version. It can be in either
  120    *     'YYYY-MM' or 'YYYY-MM-DD' format.
  121    *   - (optional) security_coverage_ending_warn_date (string): The date, in
  122    *     the format 'YYYY-MM-DD', after which a warning should be displayed
  123    *     about upgrading to another version.
  124    */
  125   public function getCoverageInfo() {
  126     if (empty($this->releases[$this->existingVersion])) {
  127       // If the existing version does not have a release, we cannot get the
  128       // security coverage information.
  129       return [];
  130     }
  131     $info = [];
  132     $existing_release_version = ModuleVersion::createFromVersionString($this->existingVersion);
  133 
  134     // Check if the installed version has a specific end date defined.
  135     $version_suffix = $existing_release_version->getMajorVersion() . '_' . $this->getSemanticMinorVersion($this->existingVersion);
  136     if (defined("self::SECURITY_COVERAGE_END_DATE_$version_suffix")) {
  137       $info['security_coverage_end_date'] = constant("self::SECURITY_COVERAGE_END_DATE_$version_suffix");
  138       $info['security_coverage_ending_warn_date'] =
  139         defined("self::SECURITY_COVERAGE_ENDING_WARN_DATE_$version_suffix")
  140           ? constant("self::SECURITY_COVERAGE_ENDING_WARN_DATE_$version_suffix")
  141           : NULL;
  142     }
  143     elseif ($security_coverage_until_version = $this->getSecurityCoverageUntilVersion()) {
  144       $info['security_coverage_end_version'] = $security_coverage_until_version;
  145       $info['additional_minors_coverage'] = $this->getAdditionalSecurityCoveredMinors($security_coverage_until_version);
  146     }
  147     return $info;
  148   }
  149 
  150   /**
  151    * Gets the release the current minor will receive security coverage until.
  152    *
  153    * For the sake of example, assume that the currently installed version of
  154    * Drupal is 8.7.11 and that static::CORE_MINORS_WITH_SECURITY_COVERAGE is 2.
  155    * When Drupal 8.9.0 is released, the supported minor versions will be 8.8
  156    * and 8.9. At that point, Drupal 8.7 will no longer have security coverage.
  157    * Therefore, this function would return "8.9.0".
  158    *
  159    * @todo In https://www.drupal.org/node/2998285 determine how we will know
  160    *    what the final minor release of a particular major version will be. This
  161    *    method should not return a version beyond that minor.
  162    *
  163    * @return string|null
  164    *   The version the existing version will receive security coverage until or
  165    *   NULL if this cannot be determined.
  166    */
  167   private function getSecurityCoverageUntilVersion() {
  168     $existing_release_version = ModuleVersion::createFromVersionString($this->existingVersion);
  169     if (!empty($existing_release_version->getVersionExtra())) {
  170       // Only full releases receive security coverage.
  171       return NULL;
  172     }
  173 
  174     return $existing_release_version->getMajorVersion() . '.'
  175       . ($this->getSemanticMinorVersion($this->existingVersion) + static::CORE_MINORS_WITH_SECURITY_COVERAGE)
  176       . '.0';
  177   }
  178 
  179   /**
  180    * Gets the number of additional minor releases with security coverage.
  181    *
  182    * This function compares the currently installed (existing) version of
  183    * the project with two things:
  184    * - The latest available official release of that project.
  185    * - The target minor release where security coverage for the current release
  186    *   should expire. This target release is determined by
  187    *   getSecurityCoverageUntilVersion().
  188    *
  189    * For the sake of example, assume that the currently installed version of
  190    * Drupal is 8.7.11 and that static::CORE_MINORS_WITH_SECURITY_COVERAGE is 2.
  191    *
  192    * Before the release of Drupal 8.8.0, this function would return 2.
  193    *
  194    * After the release of Drupal 8.8.0 and before the release of 8.9.0, this
  195    * function would return 1 to indicate that the next minor version release
  196    * will end security coverage for 8.7.
  197    *
  198    * When Drupal 8.9.0 is released, this function would return 0 to indicate
  199    * that security coverage is over for 8.7.
  200    *
  201    * If the currently installed version is 9.0.0, and there is no 9.1.0 release
  202    * yet, the function would return 2. Once 9.1.0 is out, it would return 1.
  203    * When 9.2.0 is released, it would again return 0.
  204    *
  205    * Note: callers should not test this function's return value with empty()
  206    * since 0 is a valid return value that has different meaning than NULL.
  207    *
  208    * @param string $security_covered_version
  209    *   The version until which the existing version receives security coverage.
  210    *
  211    * @return int|null
  212    *   The number of additional minor releases that receive security coverage,
  213    *   or NULL if this cannot be determined.
  214    *
  215    * @see \Drupal\update\ProjectSecurityData\getSecurityCoverageUntilVersion()
  216    */
  217   private function getAdditionalSecurityCoveredMinors($security_covered_version) {
  218     $security_covered_version_major = ModuleVersion::createFromVersionString($security_covered_version)->getMajorVersion();
  219     $security_covered_version_minor = $this->getSemanticMinorVersion($security_covered_version);
  220     foreach ($this->releases as $release) {
  221       $release_version = ModuleVersion::createFromVersionString($release['version']);
  222       if ($release_version->getMajorVersion() === $security_covered_version_major && $release['status'] === 'published' && !$release_version->getVersionExtra()) {
  223         // The releases are ordered with the most recent releases first.
  224         // Therefore, if we have found a published, official release with the
  225         // same major version as $security_covered_version, then this release
  226         // can be used to determine the latest minor.
  227         $latest_minor = $this->getSemanticMinorVersion($release['version']);
  228         break;
  229       }
  230     }
  231     // If $latest_minor is set, we know that $security_covered_version_minor and
  232     // $latest_minor have the same major version. Therefore, we can subtract to
  233     // determine the number of additional minor releases with security coverage.
  234     return isset($latest_minor) ? $security_covered_version_minor - $latest_minor : NULL;
  235   }
  236 
  237   /**
  238    * Gets the minor version for a semantic version string.
  239    *
  240    * @param string $version
  241    *   The semantic version string.
  242    *
  243    * @return int
  244    *   The minor version as an integer.
  245    */
  246   private function getSemanticMinorVersion($version) {
  247     return (int) (explode('.', $version)[1]);
  248   }
  249 
  250 }