diff --git a/core/modules/update/images/shield-icon.svg b/core/modules/update/images/shield-icon.svg new file mode 100644 index 0000000..9a7c56d --- /dev/null +++ b/core/modules/update/images/shield-icon.svg @@ -0,0 +1,35 @@ + + + + + + + image/svg+xml + + + + + + + + + diff --git a/core/modules/update/src/UpdateProcessor.php b/core/modules/update/src/UpdateProcessor.php index 25dfc7a..9115357 100644 --- a/core/modules/update/src/UpdateProcessor.php +++ b/core/modules/update/src/UpdateProcessor.php @@ -231,6 +231,9 @@ protected function parseXml($raw_xml) { foreach ($release->children() as $k => $v) { $data['releases'][$version][$k] = (string) $v; } + if ($release->security['covered']) { + $data['releases'][$version]['security_covered'] = TRUE; + } $data['releases'][$version]['terms'] = []; if ($release->terms) { foreach ($release->terms->children() as $term) { diff --git a/core/modules/update/tests/modules/update_test/bbb_update_test.1_0.xml b/core/modules/update/tests/modules/update_test/bbb_update_test.1_0.xml index 8d705b5..2142c04 100644 --- a/core/modules/update/tests/modules/update_test/bbb_update_test.1_0.xml +++ b/core/modules/update/tests/modules/update_test/bbb_update_test.1_0.xml @@ -16,11 +16,11 @@ bbb_update_test 8.x-1.0 8.x-1.0 - DRUPAL-7--1-0 + 8.x-1.0 1 0 published - http://example.com/bbb_update_test-7-x-1-0-release + http://example.com/bbb_update_test-8-x-1-0-release http://example.com/bbb_update_test-8.x-1.0.tar.gz 1250424521 b966255555d9c9b86d480ca08cfaa98e @@ -29,6 +29,26 @@ Release typeNew features Release typeBug fixes + + + + bbb_update_test 8.x-1.0-beta1 + 8.x-1.0-beta1 + 8.x-1.0-beta1 + 1 + 0 + beta1 + published + http://example.com/bbb_update_test-8-x-1-0-beta1 + http://example.com/bbb_update_test-8.x-1.0-beta1.tar.gz + 1250424521 + 7da7b18ce17cef2122f5cbca1bfe626a + 1073751331 + + Release typeNew features + Release typeBug fixes + + Not covered! diff --git a/core/modules/update/update.compare.inc b/core/modules/update/update.compare.inc index 2ba5fdc..fff81b7 100644 --- a/core/modules/update/update.compare.inc +++ b/core/modules/update/update.compare.inc @@ -177,6 +177,14 @@ function update_calculate_project_update_status(&$project_data, $available) { } } + // For dev releases, existing_version looks like '8.4.0-dev', while + // updates.drupal.org provides '8.4.x-dev'. + $version_normalized = preg_replace('/0-dev$/', 'x-dev', $project_data['existing_version']); + if (isset($available['releases'][$version_normalized])) { + $project_data['security'] = $available['releases'][$version_normalized]['security']; + $project_data['security_covered'] = !empty($available['releases'][$version_normalized]['security_covered']); + } + // If the project status is marked as something bad, there's nothing else // to consider. if (isset($available['project_status'])) { diff --git a/core/modules/update/update.install b/core/modules/update/update.install index c616412..74bfc5b 100644 --- a/core/modules/update/update.install +++ b/core/modules/update/update.install @@ -33,8 +33,27 @@ function update_requirements($phase) { if ($available = update_get_available(FALSE)) { module_load_include('inc', 'update', 'update.compare'); $data = update_calculate_project_data($available); - // First, populate the requirements for core: + + // Check if all projects have security advisory coverage. + $requirements['update_covered'] = [ + 'title' => t('Drupal.org security advisory coverage'), + 'value' => t('All modules and themes from Drupal.org receive coverage.'), + 'description' => t('Learn more about Drupal.org security advisory coverage.'), + ]; + foreach ($data as $project) { + // 'security_covered' boolean makes a positive assertion of coverage. + // 'security' string confirms there is no coverage. Check both so + // non-www.drupal.org projects are not false positives. + if (!$project['security_covered'] && !empty($project['security'])) { + $requirements['update_covered']['value'] = \Drupal::l(t('Not all Drupal.org modules and themes receive security advisory coverage'), new Url('update.status')); + $requirements['update_covered']['severity'] = REQUIREMENT_WARNING; + break; + } + } + + // Populate the requirements for core: $requirements['update_core'] = _update_requirement_check($data['drupal'], 'core'); + // We don't want to check drupal a second time. unset($data['drupal']); if (!empty($data)) { diff --git a/core/modules/update/update.module b/core/modules/update/update.module index 32b5b8c..d2a18bc 100644 --- a/core/modules/update/update.module +++ b/core/modules/update/update.module @@ -118,7 +118,17 @@ function update_help($route_name, RouteMatchInterface $route_match) { return $output; case 'update.status': - return '

' . t('Here you can find information about available updates for your installed modules and themes. Note that each module or theme is part of a "project", which may or may not have the same name, and might include multiple modules or themes within it.') . '

'; + $icon = [ + '#theme' => 'image', + '#width' => 18, + '#height' => 18, + '#uri' => 'core/modules/update/images/shield-icon.svg', + '#alt' => t('Shield'), + '#title' => t('Shield'), + ]; + $output = '

' . t('Here you can find information about available updates for your installed modules and themes. Note that each module or theme is part of a "project", which may or may not have the same name, and might include multiple modules or themes within it.') . '

'; + $output .= '

' . t('Modules and themes with a shield icon @icon are covered by the Drupal Security Team’s advisory policy. Vulnerabilities reported to the Security Team will be responsibly disclosed.', ['@icon' => \Drupal::service('renderer')->render($icon)]) . '

'; + return $output; case 'system.modules_list': if (_update_manager_access()) { diff --git a/core/modules/update/update.report.inc b/core/modules/update/update.report.inc index 7b14a05..6f6f506 100644 --- a/core/modules/update/update.report.inc +++ b/core/modules/update/update.report.inc @@ -260,6 +260,35 @@ function template_preprocess_update_project_status(&$variables) { $variables['status']['attributes'] = new Attribute(); $variables['status']['reason'] = (isset($project['reason'])) ? $project['reason'] : NULL; + if (isset($project['security'])) { + $variables['status']['security'] = $project['security']; + } + if ($project['security_covered']) { + // The update server confirms this release is covered for security + // announcements. + $variables['status']['security_icon'] = [ + '#theme' => 'image', + '#width' => 18, + '#height' => 18, + '#uri' => 'core/modules/update/images/shield-icon.svg', + '#alt' => t('Shield'), + '#title' => t('Shield'), + ]; + } + elseif (!empty($project['security'])) { + // The update server confirms this release is NOT covered for security + // announcements. + $variables['status']['security_icon'] = [ + '#theme' => 'image', + '#width' => 18, + '#height' => 18, + '#uri' => 'core/misc/icons/e29700/warning.svg', + '#alt' => t('Warning'), + '#title' => t('Warning'), + ]; + drupal_set_message(t('Your site uses modules and/or themes that are not subject to the Drupal Security Team’s advisory policy. When vulnerabilities are discovered, they may be disclosed publicly without a fix, and will not have security announcements.'), 'warning'); + } + switch ($project['status']) { case UPDATE_CURRENT: $uri = 'core/misc/icons/73b355/check.svg'; diff --git a/core/themes/stable/css/update/update.admin.theme.css b/core/themes/stable/css/update/update.admin.theme.css index abf0a88..c3711f4 100644 --- a/core/themes/stable/css/update/update.admin.theme.css +++ b/core/themes/stable/css/update/update.admin.theme.css @@ -8,10 +8,12 @@ font-size: 110%; } .project-update__status { + text-align: right; /* LTR */ float: right; /* LTR */ font-size: 110%; } [dir="rtl"] .project-update__status { + text-align: left; float: left; } .project-update__status--not-supported { @@ -32,6 +34,9 @@ padding-left: 0; padding-right: 0.5em; } +.project-update__status-icon img { + vertical-align: top; +} .project-update__details { padding: 1em 1em 0.25em 1em; } diff --git a/core/themes/stable/templates/admin/update-project-status.html.twig b/core/themes/stable/templates/admin/update-project-status.html.twig index 5a6d2ec..8a7b775 100644 --- a/core/themes/stable/templates/admin/update-project-status.html.twig +++ b/core/themes/stable/templates/admin/update-project-status.html.twig @@ -43,9 +43,15 @@ {{ status.icon }} +
+ {{ status.security }} + + {{ status.security_icon }} + +
-
+
{%- if url -%} {{ title }} {%- else -%}