forked from drupal/drupal
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathProjectSecurityData.php
More file actions
237 lines (215 loc) · 8.44 KB
/
ProjectSecurityData.php
File metadata and controls
237 lines (215 loc) · 8.44 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
<?php
namespace Drupal\update;
/**
* Calculates a project's security coverage information.
*
* @internal
* This class implements logic to determine security coverage for Drupal core
* according to Drupal core security policy. It should not be called directly.
*/
final class ProjectSecurityData {
/**
* The number of minor versions of Drupal core that receive security coverage.
*
* For example, if this value is 2 and the existing version is 9.0.1, the
* 9.0.x branch will receive security coverage until the release of version
* 9.2.0.
*/
const CORE_MINORS_WITH_SECURITY_COVERAGE = 2;
/**
* Define constants for versions with security coverage end dates.
*
* Two types of constants are supported:
* - SECURITY_COVERAGE_END_DATE_[VERSION_MAJOR]_[VERSION_MINOR]: A date in
* 'Y-m-d' or 'Y-m' format.
* - SECURITY_COVERAGE_ENDING_WARN_DATE_[VERSION_MAJOR]_[VERSION_MINOR]: A
* date in 'Y-m-d' format.
*
* @see \Drupal\update\ProjectSecurityRequirement::getDateEndRequirement()
*/
const SECURITY_COVERAGE_END_DATE_8_8 = '2020-12-02';
const SECURITY_COVERAGE_ENDING_WARN_DATE_8_8 = '2020-06-02';
const SECURITY_COVERAGE_END_DATE_8_9 = '2021-11';
/**
* The month or date security coverage will end for the existing version.
*
* The string can be in either 'YYYY-MM' or 'YYYY-MM-DD' format.
*
* @var string
*/
protected $coverageEndDate;
/**
* The version until which the existing version receives security coverage.
*
* @var string|null
*/
protected $coverageEndVersion;
/**
* The number of additional minor releases that receive security coverage.
*
* @var int|null
*/
protected $additionalMinorsCoverage;
/**
* The date after which a warning should be displayed.
*
* The date is in the format 'YYYY-MM-DD'.
*
* @var string|null
*/
protected $coverageEndingWarnDate;
/**
* Constructs a ProjectSecurityData object.
*
* @param string $existing_version
* The existing (currently installed) version of the project.
* @param array $releases
* Project releases as returned by update_get_available().
*/
private function __construct($existing_version = NULL, array $releases = []) {
if (empty($releases[$existing_version])) {
// If the existing version does not have a release, we cannot get the
// security coverage information.
return;
}
$existing_release_version = ModuleVersion::createFromVersionString($existing_version);
// Check if the installed version has a specific end date defined.
$version_suffix = $existing_release_version->getMajorVersion() . '_' . static::getSemanticMinorVersion($existing_version);
if (defined("self::SECURITY_COVERAGE_END_DATE_$version_suffix")) {
$this->coverageEndDate = constant("self::SECURITY_COVERAGE_END_DATE_$version_suffix");
$this->coverageEndingWarnDate =
defined("self::SECURITY_COVERAGE_ENDING_WARN_DATE_$version_suffix")
? constant("self::SECURITY_COVERAGE_ENDING_WARN_DATE_$version_suffix")
: NULL;
}
elseif ($security_coverage_until_version = $this->getSecurityCoverageUntilVersion($existing_version)) {
$this->coverageEndVersion = $security_coverage_until_version;
$this->additionalMinorsCoverage = $this->getAdditionalSecurityCoveredMinors($security_coverage_until_version, $releases);
}
}
/**
* Creates a ProjectSecurityData object from project data and releases.
*
* @param array $project_data
* Project data from Drupal\update\UpdateManagerInterface::getProjects() and
* processed by update_process_project_info().
* @param array $releases
* Project releases as returned by update_get_available().
*
* @return static
*/
public static function createFromProjectDataAndReleases(array $project_data, array $releases) {
if (!($project_data['project_type'] === 'core' && $project_data['name'] === 'drupal')) {
throw new \UnexpectedValueException('\Drupal\update\ProjectSecurityData can only be used with Drupal core');
}
return new static($project_data['existing_version'], $releases);
}
/**
* Gets the security coverage end date.
*
* @return string|null
* The month or date security coverage will end for the existing version. It
* can be in either 'YYYY-MM' or 'YYYY-MM-DD' format.
*/
public function getCoverageEndDate() {
return $this->coverageEndDate;
}
/**
* Gets the security coverage ending warning date.
*
* @return mixed|null
* The date, in the format 'YYYY-MM-DD', after which a warning should be
* displayed about upgrading to another version or NULL if not applicable.
*/
public function getCoverageEndingWarnDate() {
return $this->coverageEndingWarnDate;
}
/**
* Gets the security coverage end version.
*
* @return string|null
* The version until which the existing version receives security coverage
* or NULL if not applicable.
*/
public function getCoverageEndVersion() {
return $this->coverageEndVersion;
}
/**
* Gets the number of additional minor releases that receive coverage.
*
* @return int|null
* The number of additional minor releases that receive security coverage,
* or NULL if not applicable.
*/
public function getAdditionalMinorsCoverage() {
return $this->additionalMinorsCoverage;
}
/**
* Gets the release the current minor will receive security coverage until.
*
* @todo In https://www.drupal.org/node/2608062 determine how we will know
* what the final minor release of a particular major version will be. This
* method should not return a version beyond that minor.
*
* @param string $existing_version
* The existing (currently installed) version of the project.
*
* @return string|null
* The version the existing version will receive security coverage until or
* NULL if this cannot be determined.
*/
private static function getSecurityCoverageUntilVersion($existing_version) {
$existing_release_version = ModuleVersion::createFromVersionString($existing_version);
if (!empty($existing_release_version->getVersionExtra())) {
// Only full releases receive security coverage.
return NULL;
}
return $existing_release_version->getMajorVersion() . '.'
. (static::getSemanticMinorVersion($existing_version) + static::CORE_MINORS_WITH_SECURITY_COVERAGE)
. '.0';
}
/**
* Gets the number of additional minor security covered releases.
*
* @param string $security_covered_version
* The version until which the existing version receives security coverage.
* @param array $releases
* Project releases as returned by update_get_available().
*
* @return int|null
* The number of additional minor releases that receive security coverage,
* or NULL if this cannot be determined.
*/
private static function getAdditionalSecurityCoveredMinors($security_covered_version, array $releases) {
$security_covered_version_major = ModuleVersion::createFromVersionString($security_covered_version)->getMajorVersion();
$security_covered_version_minor = static::getSemanticMinorVersion($security_covered_version);
foreach ($releases as $release) {
$release_version = ModuleVersion::createFromVersionString($release['version']);
if ($release_version->getMajorVersion() === $security_covered_version_major && $release['status'] === 'published' && !$release_version->getVersionExtra()) {
// The releases are ordered with the most recent releases first.
// Therefore if we have found an official, published release with the
// same major version as $security_covered_version then this release
// can be used to determine the latest minor.
$latest_minor = static::getSemanticMinorVersion($release['version']);
break;
}
}
// If $latest_minor is set, we know that $latest_minor and
// $security_covered_version_minor have the same major version. Therefore we
// can simply subtract to determine the number of additional minor security
// covered releases.
return isset($latest_minor) ? $security_covered_version_minor - $latest_minor : NULL;
}
/**
* Gets the minor version for a semantic version string.
*
* @param string $version
* The semantic version string.
*
* @return int
* The minor version as an integer.
*/
private static function getSemanticMinorVersion($version) {
return (int) (explode('.', $version)[1]);
}
}