Skip to content

Commit ad33027

Browse files
authored
Fix #3748. Use Semver::satisfies. (#3774)
* Fix #3748. Use Semver::satisfies. * Remove min-version. We can't support it reliably. * Back to a working url. * fix spacing.
1 parent ba60acc commit ad33027

File tree

2 files changed

+7
-153
lines changed

2 files changed

+7
-153
lines changed

src/Commands/pm/SecurityUpdateCommands.php

Lines changed: 6 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
namespace Drush\Commands\pm;
33

44
use Composer\Semver\Comparator;
5+
use Composer\Semver\Semver;
56
use Consolidation\AnnotatedCommand\CommandData;
67
use Consolidation\OutputFormatters\StructuredData\RowsOfFields;
78
use Drush\Commands\DrushCommands;
@@ -35,8 +36,7 @@ class SecurityUpdateCommands extends DrushCommands
3536
* @field-labels
3637
* name: Name
3738
* version: Installed Version
38-
* min-version: Suggested version
39-
* @default-fields name,version,min-version
39+
* @default-fields name,version
4040
*
4141
* @filter-default-field name
4242
* @return \Consolidation\OutputFormatters\StructuredData\RowsOfFields
@@ -69,7 +69,7 @@ public function suggestComposerCommand($result, CommandData $commandData)
6969
if (!empty($this->securityUpdates)) {
7070
$suggested_command = 'composer require ';
7171
foreach ($this->securityUpdates as $package) {
72-
$suggested_command .= $package['name'] . ':^' . $package['min-version'] . ' ';
72+
$suggested_command .= $package['name'] . ' ';
7373
}
7474
$suggested_command .= '--update-with-dependencies';
7575
$this->logger()->warning("One or more of your dependencies has an outstanding security update. Please apply update(s) immediately.");
@@ -137,104 +137,15 @@ protected function loadSiteComposerLock()
137137
protected function registerAllSecurityUpdates($composer_lock_data, $security_advisories_composer_json)
138138
{
139139
$both = array_merge($composer_lock_data['packages-dev'], $composer_lock_data['packages']);
140+
$conflict = $security_advisories_composer_json['conflict'];
140141
foreach ($both as $package) {
141142
$name = $package['name'];
142-
$this->registerPackageSecurityUpdates($security_advisories_composer_json, $name, $package);
143-
}
144-
}
145-
146-
/**
147-
* Determines if update is available based on a conflict constraint.
148-
*
149-
* @param string $conflict_constraint
150-
* The constraint for the conflicting, insecure package version.
151-
* E.g., <1.0.0.
152-
* @param array $package
153-
* The package to be evaluated.
154-
* @param string $name
155-
* The human readable display name for the package.
156-
*
157-
* @return array
158-
* An associative array containing name, version, and min-version keys.
159-
*/
160-
public static function determineUpdatesFromConstraint(
161-
$conflict_constraint,
162-
$package,
163-
$name
164-
) {
165-
// Only parse constraints that follow pattern like "<1.0.0".
166-
if (substr($conflict_constraint, 0, 1) == '<') {
167-
$min_version = substr($conflict_constraint, 1);
168-
if (Comparator::lessThan(
169-
$package['version'],
170-
$min_version
171-
)) {
172-
return [
143+
if (!empty($conflict[$name]) && Semver::satisfies($package['version'], $security_advisories_composer_json['conflict'][$name])) {
144+
$this->securityUpdates[$name] = [
173145
'name' => $name,
174146
'version' => $package['version'],
175-
// Assume that conflict constraint of <1.0.0 indicates that
176-
// 1.0.0 is the available, secure version.
177-
'min-version' => $min_version,
178147
];
179148
}
180-
} // Compare exact versions that are insecure.
181-
elseif (preg_match(
182-
'/^[[:digit:]](?![-*><=~ ])/',
183-
$conflict_constraint
184-
)) {
185-
$exact_version = $conflict_constraint;
186-
if (Comparator::equalTo(
187-
$package['version'],
188-
$exact_version
189-
)) {
190-
$version_parts = explode('.', $package['version']);
191-
if (count($version_parts) == 3) {
192-
$version_parts[2]++;
193-
$min_version = implode('.', $version_parts);
194-
return [
195-
'name' => $name,
196-
'version' => $package['version'],
197-
// Assume that conflict constraint of 1.0.0 indicates that
198-
// 1.0.1 is the available, secure version.
199-
'min-version' => $min_version,
200-
];
201-
}
202-
}
203-
}
204-
return [];
205-
}
206-
207-
/**
208-
* Registers available security updates for a given package.
209-
*
210-
* @param array $security_advisories_composer_json
211-
* The composer.json array from drupal-security-advisories.
212-
* @param string $name
213-
* The human readable display name for the package.
214-
* @param array $package
215-
* The package to be evaluated.
216-
*/
217-
protected function registerPackageSecurityUpdates(
218-
$security_advisories_composer_json,
219-
$name,
220-
$package
221-
) {
222-
if (empty($this->securityUpdates[$name]) &&
223-
!empty($security_advisories_composer_json['conflict'][$name])) {
224-
$conflict_constraints = explode(
225-
',',
226-
$security_advisories_composer_json['conflict'][$name]
227-
);
228-
foreach ($conflict_constraints as $conflict_constraint) {
229-
$available_update = $this->determineUpdatesFromConstraint(
230-
$conflict_constraint,
231-
$package,
232-
$name
233-
);
234-
if ($available_update) {
235-
$this->securityUpdates[$name] = $available_update;
236-
}
237-
}
238149
}
239150
}
240151
}

tests/functional/SecurityUpdatesTest.php

Lines changed: 1 addition & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -20,67 +20,10 @@ public function testInsecurePackage()
2020
{
2121
$this->drush('pm:security', [], ['format' => 'json'], null, null, self::EXIT_ERROR);
2222
$this->assertContains('One or more of your dependencies has an outstanding security update. Please apply update(s) immediately.', $this->getErrorOutput());
23-
$this->assertContains('Try running: composer require drupal/alinks:^1.1 --update-with-dependencies', $this->getErrorOutput());
23+
$this->assertContains('Try running: composer require drupal/alinks --update-with-dependencies', $this->getErrorOutput());
2424
$security_advisories = $this->getOutputFromJSON();
2525
$this->assertObjectHasAttribute('drupal/alinks', $security_advisories);
2626
$this->assertEquals('drupal/alinks', $security_advisories->{"drupal/alinks"}->name);
2727
$this->assertEquals('1.0.0', $security_advisories->{"drupal/alinks"}->version);
28-
$this->assertEquals('1.1', $security_advisories->{"drupal/alinks"}->{"min-version"});
29-
}
30-
31-
32-
/**
33-
* Test that insecure packages are correctly identified.
34-
*
35-
* @dataProvider conflictConstraintParsingProvider
36-
*/
37-
public function testConflictConstraintParsing($package, $conflict_constraint, $min_version, $updates_are_available)
38-
{
39-
$available_updates = SecurityUpdateCommands::determineUpdatesFromConstraint($conflict_constraint, $package, $package['name']);
40-
$this->assertEquals($updates_are_available, (bool) $available_updates);
41-
42-
if ($available_updates) {
43-
$this->assertEquals($package['version'], $available_updates['version']);
44-
$this->assertEquals($min_version, $available_updates['min-version']);
45-
}
46-
}
47-
48-
/**
49-
* Data provider for testConflictConstraintParsing().
50-
*/
51-
public function conflictConstraintParsingProvider()
52-
{
53-
return [
54-
// Test "minimum version" conflict.
55-
[
56-
[
57-
'name' => 'Alinks',
58-
'version' => '1.0.0'
59-
],
60-
'<1.0.1',
61-
'1.0.1',
62-
true,
63-
],
64-
// Test "exact version" conflict.
65-
[
66-
[
67-
'name' => 'Alinks',
68-
'version' => '1.0.0'
69-
],
70-
'1.0.0',
71-
'1.0.1',
72-
true,
73-
],
74-
// Test "exact version" conflict with 2 digits. Should not work.
75-
[
76-
[
77-
'name' => 'Alinks',
78-
'version' => '1.0.0'
79-
],
80-
'1.0',
81-
'1.0.1',
82-
false,
83-
],
84-
];
8528
}
8629
}

0 commit comments

Comments
 (0)