diff --git a/AutomaticUpdatesPsa.php b/AutomaticUpdatesPsa.php
index 9290631..de068a7 100644
--- a/AutomaticUpdatesPsa.php
+++ b/AutomaticUpdatesPsa.php
@@ -6,7 +6,6 @@
*/
use Composer\Semver\VersionParser;
-use Drupal\Component\Version\Constraint;
/**
* Class AutomaticUpdatesPsa.
@@ -21,7 +20,7 @@ class AutomaticUpdatesPsa {
public static function getPublicServiceMessages() {
$messages = array();
- if (!variable_get('automatic_updates_enable_psa')) {
+ if (!variable_get('automatic_updates_enable_psa', TRUE)) {
return $messages;
}
@@ -44,11 +43,20 @@ class AutomaticUpdatesPsa {
$json_payload = json_decode($response->data);
if (!is_null($json_payload)) {
foreach ($json_payload as $json) {
- if ($json->project === 'core') {
- static::coreParser($messages, $json);
+ try {
+ if ($json->is_psa && ($json->type === 'core' || static::isValidExtension($json->type, $json->project))) {
+ $messages[] = static::message($json->title, $json->link);
+ }
+ elseif ($json->type === 'core') {
+ static::parseConstraints($messages, $json, VERSION);
+ }
+ elseif (static::isValidExtension($json->type, $json->project)) {
+ static::contribParser($messages, $json);
+ }
}
- else {
- static::contribParser($messages, $json);
+ catch (\UnexpectedValueException $exception) {
+ watchdog_exception('automatic_updates', $exception);
+ $messages[] = t('Drupal PSA endpoint service is malformed.');
}
}
}
@@ -57,14 +65,44 @@ class AutomaticUpdatesPsa {
}
/**
- * Parse core project JSON version strings.
+ * Parse contrib project JSON version strings.
+ *
+ * @param array $messages
+ * The messages array.
+ * @param object $json
+ * The JSON object.
+ */
+ protected static function contribParser(array &$messages, $json) {
+ $extension_path = drupal_get_path($json->type, $json->project);
+ $info = drupal_parse_info_file($extension_path . DIRECTORY_SEPARATOR . $json->project . '.info');
+ $json->insecure = array_filter(array_map(function ($version) {
+ $version_array = explode('-', $version, 2);
+ if (count($version_array) === 2 && $version_array[0] === DRUPAL_CORE_COMPATIBILITY) {
+ return $version_array[1];
+ }
+ }, $json->insecure));
+ $version_array = explode('-', $info['version'], 2);
+ $extension_version = array_pop($version_array);
+ static::parseConstraints($messages, $json, $extension_version);
+ }
+
+ /**
+ * Compare versions and add a message, if appropriate.
*
* @param array $messages
* The messages array.
* @param object $json
* The JSON object.
+ * @param string $current_version
+ * The current extension version.
+ *
+ * @throws \UnexpectedValueException
*/
- protected static function coreParser(array &$messages, $json) {
+ protected static function parseConstraints(array &$messages, $json, $current_version) {
+ $version_string = implode('||', $json->insecure);
+ if (empty($version_string)) {
+ return;
+ }
$module_directory = drupal_get_path('module', 'automatic_updates');
include_once $module_directory . '/semver/src/Constraint/ConstraintInterface.php';
include_once $module_directory . '/semver/src/Constraint/Constraint.php';
@@ -72,76 +110,48 @@ class AutomaticUpdatesPsa {
include_once $module_directory . '/semver/src/Constraint/MultiConstraint.php';
include_once $module_directory . '/semver/src/VersionParser.php';
$parser = new VersionParser();
- // Remove Drupal 8 version.
- $json->secure_versions = array_filter($json->secure_versions, function ($version) {
- return substr($version, 0, 1) == 7;
- });
- array_walk($json->secure_versions, function (&$version) {
- $version = '<' . $version;
- });
-
- $version_string = implode('||', $json->secure_versions);
$psa_constraint = $parser->parseConstraints($version_string);
- $core_constraint = $parser->parseConstraints(VERSION);
- if ($psa_constraint->matches($core_constraint)) {
- $messages[] = t('Drupal Core PSA: %message', array(
- '%message' => $json->title,
- '!url' => $json->link,
- ));
+ $contrib_constraint = $parser->parseConstraints($current_version);
+ if ($psa_constraint->matches($contrib_constraint)) {
+ $messages[] = static::message($json->title, $json->link);
}
}
/**
- * Parse contrib project JSON version strings.
+ * Determine if extension exists and has a version string.
*
- * @param array $messages
- * The messages array.
- * @param object $json
- * The JSON object.
+ * @param string $extension_type
+ * The extension type i.e. module, theme.
+ * @param string $project_name
+ * The project.
+ *
+ * @return bool
+ * TRUE if extension exists, else FALSE.
*/
- protected static function contribParser(array &$messages, $json) {
- array_walk($json->secure_versions, function (&$version) {
- if (substr($version, 0, 4) === DRUPAL_CORE_COMPATIBILITY . '-') {
- $version = substr($version, 4);
- }
- });
- $modules = array_keys(module_list());
- $themes = array_keys(list_themes());
- $extensions = array_merge($modules, $themes);
- foreach ($json->extensions as $extension_name) {
- if (in_array($extension_name, $extensions)) {
- foreach (array('module', 'theme') as $type) {
- // If an extension doesn't exist, errors are triggered. Hide them.
- $extension_path = @drupal_get_path($type, $extension_name);
- if ($extension_path && ($info = drupal_parse_info_file($extension_path . DIRECTORY_SEPARATOR . $extension_name . '.info'))) {
- static::contribMessage($messages, $json, $info['version']);
- }
- }
- }
+ protected static function isValidExtension($extension_type, $project_name) {
+ $extension_path = @drupal_get_path($extension_type, $project_name);
+ if ($extension_path && ($info = drupal_parse_info_file($extension_path . DIRECTORY_SEPARATOR . $project_name . '.info'))) {
+ return isset($info['version']);
}
+ return FALSE;
}
/**
- * Add a contrib message PSA, if appropriate.
+ * Return a message.
*
- * @param array $messages
- * The messages array.
- * @param object $json
- * The JSON object.
- * @param string $extension_version
- * The extension version.
+ * @param string $title
+ * The title.
+ * @param string $link
+ * The link.
+ *
+ * @return string
+ * The PSA or SA message.
*/
- protected static function contribMessage(array &$messages, $json, $extension_version) {
- $module_directory = drupal_get_path('module', 'automatic_updates');
- include_once $module_directory . '/Drupal/Component/Version/Constraint.php';
- $version_string = implode('||', $json->secure_versions);
- $constraint = new Constraint("<$extension_version", DRUPAL_CORE_COMPATIBILITY);
- if (!$constraint->isCompatible($version_string)) {
- $messages[] = t('Drupal Contrib Project PSA: %message', array(
- '%message' => $json->title,
- '!url' => $json->link,
- ));
- }
+ protected static function message($title, $link) {
+ return format_string('@message', [
+ '@message' => $title,
+ '@url' => $link,
+ ]);
}
}
diff --git a/tests/automatic_updates_test.module b/tests/automatic_updates_test.module
index 3127310..1980c8c 100644
--- a/tests/automatic_updates_test.module
+++ b/tests/automatic_updates_test.module
@@ -26,80 +26,92 @@ function automatic_updates_test_menu() {
* Page callback for test JSON.
*/
function automatic_updates_test_json() {
- $feed = array();
- $feed[] = array(
- 'title' => 'Critical Release - PSA-2019-02-19',
- 'link' => 'https://www.drupal.org/psa-2019-02-19',
- 'project' => 'core',
- 'extensions' => array(),
- 'type' => 'module',
- 'secure_versions' => array(
- '7.999',
- '8.10.99',
- '8.9.99',
- '8.8.99',
- '8.7.99',
- '8.6.99',
- '8.5.99',
- ),
+ $feed = [];
+ $feed[] = [
+ 'title' => 'Critical Release - SA-2019-02-19',
+ 'link' => 'https://www.drupal.org/sa-2019-02-19',
+ 'project' => 'drupal',
+ 'type' => 'core',
+ 'insecure' => [
+ '7.65',
+ '8.5.14',
+ '8.5.14',
+ '8.6.13',
+ '8.7.0-alpha2',
+ '8.7.0-beta1',
+ '8.7.0-beta2',
+ '8.6.14',
+ '8.6.15',
+ '8.6.15',
+ '8.5.15',
+ '8.5.15',
+ '7.66',
+ '8.7.0',
+ VERSION,
+ ],
+ 'is_psa' => '0',
'pubDate' => 'Tue, 19 Feb 2019 14:11:01 +0000',
- );
- $feed[] = array(
+ ];
+ $feed[] = [
'title' => 'Critical Release - PSA-Really Old',
'link' => 'https://www.drupal.org/psa',
- 'project' => 'core',
- 'extensions' => array(),
- 'type' => 'module',
- 'secure_versions' => array(
- '7.22',
- '8.4.0',
- ),
+ 'project' => 'drupal',
+ 'type' => 'core',
+ 'is_psa' => '1',
+ 'insecure' => [],
'pubDate' => 'Tue, 19 Feb 2019 14:11:01 +0000',
- );
- $feed[] = array(
+ ];
+ $feed[] = [
'title' => 'Node - Moderately critical - Access bypass - SA-CONTRIB-2019',
'link' => 'https://www.drupal.org/sa-contrib-2019',
'project' => 'node',
- 'extensions' => array('node'),
'type' => 'module',
- 'secure_versions' => array('7.x-7.22', '8.x-8.8.0'),
- 'pubDate' => 'Tue, 19 Mar 2019 12:50:00 +0000',
- );
- $feed[] = array(
- 'title' => 'Standard - Moderately critical - Access bypass - SA-CONTRIB-2019',
- 'link' => 'https://www.drupal.org/sa-contrib-2019',
- 'project' => 'Standard Install Profile',
- 'extensions' => array('standard'),
- 'type' => 'profile',
- 'secure_versions' => array('7.x-7.999'),
+ 'is_psa' => '0',
+ 'insecure' => ['7.x-7.22', '8.x-8.2.0'],
'pubDate' => 'Tue, 19 Mar 2019 12:50:00 +0000',
- );
- $feed[] = array(
+ ];
+ $feed[] = [
'title' => 'Seven - Moderately critical - Access bypass - SA-CONTRIB-2019',
'link' => 'https://www.drupal.org/sa-contrib-2019',
'project' => 'seven',
- 'extensions' => array('seven'),
'type' => 'theme',
- 'secure_versions' => array('7.x-7.999'),
+ 'is_psa' => '0',
+ 'insecure' => ['8.x-8.7.0', VERSION],
'pubDate' => 'Tue, 19 Mar 2019 12:50:00 +0000',
- );
- $feed[] = array(
+ ];
+ $feed[] = [
'title' => 'Foobar - Moderately critical - Access bypass - SA-CONTRIB-2019',
'link' => 'https://www.drupal.org/sa-contrib-2019',
'project' => 'foobar',
- 'extensions' => array('foobar'),
'type' => 'foobar',
- 'secure_versions' => array('7.x-1.2'),
+ 'is_psa' => '1',
+ 'insecure' => [],
'pubDate' => 'Tue, 19 Mar 2019 12:50:00 +0000',
- );
- $feed[] = array(
+ ];
+ $feed[] = [
'title' => 'Token - Moderately critical - Access bypass - SA-CONTRIB-2019',
'link' => 'https://www.drupal.org/sa-contrib-2019',
'project' => 'token',
- 'extensions' => array('token'),
'type' => 'module',
- 'secure_versions' => array('7.x-1.7', '8.x-1.5'),
+ 'is_psa' => '0',
+ 'insecure' => ['7.x-1.7', '8.x-1.4'],
'pubDate' => 'Tue, 19 Mar 2019 12:50:00 +0000',
- );
+ ];
+ $feed[] = [
+ 'title' => 'Views - Moderately critical - Access bypass - SA-CONTRIB-2019',
+ 'link' => 'https://www.drupal.org/sa-contrib-2019',
+ 'project' => 'views',
+ 'type' => 'module',
+ 'insecure' => [
+ '7.x-3.16',
+ '7.x-3.17',
+ '7.x-3.18',
+ '7.x-3.19',
+ '7.x-3.19',
+ '8.x-8.7.0',
+ ],
+ 'is_psa' => '0',
+ 'pubDate' => 'Tue, 19 Mar 2019 12:50:00 +0000',
+ ];
return drupal_json_output($feed);
}