Problem/Motivation

in #2990511-39: Add comprehensive test coverage for update status for security releases cases were found where in Update reports, if security release is needed for current minor and is also available for minor the previous minor security update is listed also.

An example:
The current core version is 8.1.0 which insecure

  1. 8.1.2 is a security update, not insecure
  2. 8.0.2 is a security update, not insecure
  3. 8.0.2, in addition to 8.1.2, is shown as security update even though it would be downgrade

This seems separate issue than #2865920: Add tests for when a single supported branch has 2 security updates that are both not insecure

Proposed resolution

Downgrades should never be shown as a Security update.

The current fix for #2804155: If the next minor version of core has a security release, status still says "Security update required!" even if the site is on an equivalent, secure release already does not fix this issue.

Remaining tasks

Figure out the fix
patch
review

User interface changes

None

API changes

None

Data model changes

None

Comments

tedbow created an issue. See original summary.

xjm’s picture

Downgrades should never be shown as a Security update.

Well, I would say "Downgrades within the same major release should never be shown as security updates." There are times that an older security release really could be the only supported option, if newer releases have been marked insecure/revoked/etc. without newer security releases being created. This also applies to the case where the site is on 8.x-3.0-beta2 of a module and the most recent security release is 8.x-2.2. In that scenario, 2.2 really is the most recommended version for a stable site.

xjm’s picture

Title: Update reports, if security release is needed for current minor and is also available for minor the previous minor security update is listed also » Update report incorrectly recommends security releases for old minors when a security update is needed and a secure version of the old minor is also avaoilable
Related issues: +#2909665: [plan] Extend security support to cover the previous minor version of Drupal
cilefen’s picture

Title: Update report incorrectly recommends security releases for old minors when a security update is needed and a secure version of the old minor is also avaoilable » Update report incorrectly recommends security releases for old minors when a security update is needed and a secure version of the old minor is also available

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

xjm’s picture

Priority: Normal » Critical
tedbow’s picture

Status: Active » Needs review
StatusFileSize
new5 KB

Here is patch to start

There were some todos in our tests that point to #2865920: Add tests for when a single supported branch has 2 security updates that are both not insecure but actually are the problem that is being described here. I removed those todos and added the test cases.

re #3 I left one of the todos in that points to this problem

// @todo In https://www.drupal.org/node/2865920 add test cases:
// - 8.x-3.0-beta1 using fixture 'sec.8.x-1.2_8.x-2.2' to ensure that
// 8.x-2.2 is the only security update.

I am not sure if we should also solve that here.

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

dww’s picture

I think I just ran into another example of this in the real world.

I'm trying to test #3114707: Add color to summary element to improve UX of core compatibility info on available updates report and I searched for a contrib that's got a requirement on 8.8.x core and a security update required. I found VBO (side note, see #3116581: Use core_version_requirement, not a version dependency on drupal:views).

Right now with a clean 8.9.x install of core, if you install VBO 8.x-3.3, your available update report will look like this:

Available updates report with VBO 8.x-3.3 currently installed, showing too many releases

This is the same bug, right?

I re-rolled #7 (no longer applies), and now my available updates report for VBO looks as expected:

Available updates report with VBO 8.x-3.3 currently installed, after patch applied, showing correct releases

Not entirely clear what else is needed here. I'll more closely review when I can and see if there's any reason not to RTBC this.

Thanks,
-Derek

p.s. Interdiff is all kinds of confused by the re-roll, so here's a raw diff of the two patch files.

p.p.s. EDIT: changed the embedded screenshots to use the ones from comment #10.

dww’s picture

Sorry for the noise, but my test site was a bit screwy when I took those screenshots. Needed to clear caches to get the Download links. ;) These are the accurate screenshots.

dww’s picture

Status: Needs review » Needs work

Initial review:

  1. +++ b/core/modules/update/tests/src/Functional/UpdateCoreTest.php
    @@ -357,24 +365,25 @@ public function securityUpdateAvailabilityProvider() {
    +      // If the site is on an alpha/beta/RC of an upcoming minor and there is
    +      // RC versions with a security update it should be recommended.
    

    Comment grammar doesn't parse. Should be:

    // If the site is on an alpha/beta/RC of an upcoming minor and there is
    // an RC version with a security update, it should be recommended.

  2. +++ b/core/modules/update/update.compare.inc
    @@ -10,6 +10,8 @@
     use Drupal\update\ModuleVersion;
     use Drupal\update\ProjectCoreCompatibility;
     
    +use Drupal\Component\Version\Constraint;
    +
    

    I botched the spacing on the reroll. I think Component should go first, without a newline between it and the others...

  3. +++ b/core/modules/update/update.compare.inc
    @@ -473,7 +475,14 @@ function update_calculate_project_update_status(&$project_data, $available) {
    +      // Remove the major version prefix, for example '8.x-', if any.
    +      $constraint_testable_version = str_replace(\Drupal::CORE_COMPATIBILITY . '-', '', $release['version']);
    

    We shouldn't use str_replace() for this, or we screw up things like 8.x-8.x-dev releases. We should be more careful and do a preg_replace() anchored to the start of the string. Or something...

Bigger picture... I'm confused why we have this bug at all. Once upon a time, update_calculate_project_update_status() would stop searching the release history as soon as it found your currently installed release. So I'm not sure how it gets to this state where it's finding security releases that are older than your installed release. I'd have to dig a lot deeper to figure out why that changed. But it makes me nervous that there might be other bugs lurking if update_calculate_project_update_status() keeps processing releases that are older than what you've got...

Yeah, weird, it's still there:

    // Stop searching once we hit the currently installed version.
    if ($project_data['existing_version'] === $version) {
      break;
    }

So, yeah, needs further investigation to understand why this patch is needed at all. ;)

p.s. #3085662: Remove Drupal::CORE_COMPATIBILITY because it is not accurate when modules can be compatible with multiple core branches -- sad to keep adding usages of \Drupal::CORE_COMPATIBILITY

swatichouhan012’s picture

StatusFileSize
new5.04 KB
new1.1 KB

I have updated patch wrt #11, point 1 and point 2.

dww’s picture

Assigned: Unassigned » dww

I'm looking into the "bigger picture" from #10. Stay tuned...

dww’s picture

Assigned: dww » Unassigned
Status: Needs work » Needs review
Related issues: +#3100110: Convert update_calculate_project_update_status() into a class
StatusFileSize
new3.7 KB
new4.92 KB

Ok, here's the deal...

B/c of this:

      // Otherwise, ignore unpublished, insecure, or unsupported releases.
      if ($release['status'] == 'unpublished' ||
          !$is_in_supported_branch($release['version']) ||
          (isset($release['terms']['Release type']) &&
           (in_array('Insecure', $release['terms']['Release type']) ||
            in_array('Unsupported', $release['terms']['Release type'])))) {
        continue;
      }

if your currently-installed version is one of those conditions (e.g. insecure), then we never hit this code block later:

    // Stop searching once we hit the currently installed version.
    if ($project_data['existing_version'] === $version) {
      break;
    }

So this function loops over every possible release. :( In most cases, we skip things that are older, but not for security updates. That's what the fix in #7 is addressing -- special-case the security release stuff to ignore older releases.

IMHO, the better solution is to not skip the current version, even if insecure, so that we properly bail out of this function when we mean to.

I first tried adding the break; at the end of the initial code block that notices if we're on the current release:

    // First, if this is the existing release, check a few conditions.
    if ($project_data['existing_version'] === $version) {
      ...
    }

However, if we break that soon, then we never set recommend or latest_version, and if you've got a module that's up-to-date (current should be recommended) eventually we hit this:

  // If we don't know what to recommend, there's nothing we can report.
  // Bail out early.
  if (!isset($project_data['recommended'])) {
    $project_data['status'] = UpdateFetcherInterface::UNKNOWN;
    $project_data['reason'] = t('No available releases found');
    return;
  }

So that's no good, either...

Therefore, I think the smallest, safest fix for this bug (which also prevents us from needlessly looping over a ton of releases we don't care about) is the attached patch. We only do the "otherwise, ignore stuff..." code block in an else of testing if we're looking at the current release.

Identical test changes from #7. In fact, attaching those as a test-only patch so we see the failures from the new test coverage here. Fails locally. With the full patch, all update tests are passing locally. I hope the bot agrees.

Interdiff is pretty meaningless, since it's an entirely different approach to the changes for update.compare.inc.

p.s. @swatichouhan012 re: #12: thanks for helping. Unfortunately you didn't fix #11.2 as intended. Thankfully, it doesn't matter since this new approach doesn't need any new use statements at all. ;)

p.p.s. Adding #3100110: Convert update_calculate_project_update_status() into a class as related. I'll be the first to admit that this function is way too long and complicated for anyone other than the original author (me) to really understand all the subtleties and nuances for the kind of analysis / fix I just did. :( Yes, we should re-factor / re-organize it into a class and make it easier for other people to make sense of.

The last submitted patch, 14: 2992631-14.test-only.patch, failed testing. View results

dww’s picture

Version: 8.9.x-dev » 8.8.x-dev
Related issues: +#1539712: [policy, no patch] Coding standards for breaking function calls and language constructs across lines
StatusFileSize
new4.74 KB
new1.04 KB

Self-review:

+++ b/core/modules/update/update.compare.inc
@@ -379,14 +379,15 @@ function update_calculate_project_update_status(&$project_data, $available) {
+    else {
+      // Otherwise, ignore unpublished, insecure, or unsupported releases.
+      if ($release['status'] == 'unpublished' ||
+          !$is_in_supported_branch($release['version']) ||
+          (isset($release['terms']['Release type']) &&
+           (in_array('Insecure', $release['terms']['Release type']) ||
+            in_array('Unsupported', $release['terms']['Release type'])))) {
+        continue;
+      }

This can be elseif(), then we don't have to indent it all. Although we'd still have to change the indentation of all the conditions for if vs. elseif. :/ So it'd only save the indentation of the continue and would eliminate the need for the extra }. ;)

Wish we had a better standard for multi-line complex conditionals like this. Sadly #1539712: [policy, no patch] Coding standards for breaking function calls and language constructs across lines wouldn't help and we'd still need to indent the conditions differently to account for if vs. elseif. /shrug

Meanwhile, I believe this is back-portable to 8.8.x, so moving the version selector accordingly. Patch still applies cleanly to 8.8.x and 9.0.x, too.

Status: Needs review » Needs work

The last submitted patch, 16: 2992631-16.patch, failed testing. View results

dww’s picture

Status: Needs work » Needs review
StatusFileSize
new4.75 KB
new953 bytes

#17 was just a random fail, but I realized I uploaded the wrong version of the formatting I was aiming for, anyway.

tedbow’s picture

Status: Needs review » Needs work
+++ b/core/modules/update/update.compare.inc
@@ -390,13 +390,13 @@ function update_calculate_project_update_status(&$project_data, $available) {
     // Otherwise, ignore unpublished, insecure, or unsupported releases.
-    if ($release['status'] == 'unpublished' ||

I am comparing the patched version with the version in 8.9.x. I think now that this is a esleif for

if ($project_data['existing_version'] === $version) {
and not it's own if block, we should change this comment indicate this will only happen for versions other than the current.

If the if block above was smaller and didn't also contain it's own if,elseif,elseif block we might not need this but looking at it is confusing what the new elseif corresponds

maybe something like: "If $release is not the existing release and is unpublished, insecure or unsupported, ignore it."

Otherwise I think it looks good

dww’s picture

Status: Needs work » Needs review
StatusFileSize
new4.86 KB
new588 bytes

Re: #19: 👍

How's this?

See also my latest thoughts at #3100110-21: Convert update_calculate_project_update_status() into a class

dww’s picture

9.1.x was random fail in Drupal\Tests\quickedit\FunctionalJavascript\QuickEditIntegrationTest -- requeued.

jungle’s picture

StatusFileSize
new298.53 KB
new286.69 KB

$ composer require drupal/metatag:1.5.0 drupal/profile:1.0.0-rc1 drupal/views_bulk_operations:3.3.0 -vvv

Manually tested the patch in #18 against 8.9.x#225d565b9060b0f7357d5fd3232d90fd5d7e766b

The patch works as expected, RTBC +1.

jungle’s picture

Meanwhile, I am thinking of the following

  1. Change Recommended version: to Recommended version by maintainer. To me, if I am using a branch, recommended version from the same branch would make more sense. Take the screenshot in #22 as an example, I was using Drupal core 8.9.x-dev, why recommended me 8.8.4. I'd like 8.9.0-beta1 being recommended rather than 8.8.4.

    To distinguish the recommended version by the maintainer and by the update system is unnecessary. But we could make it more clear that it's by the maintainer.

  2. Should we display all releases available which > current version? Take the VBO module in #22 as example, the current version is 3.3.0 and the latest version is 3.6.0. My point is that version 3.4.0, 3.5.0 and 3.6.0 should be listed too. With all available releases listed, it may push/encourage people to do the update. It looks like saying that, hey, site owner, you are too far away from the latest version.
  3. Email notification threshold

    • All newer versions
    • Only security updates

    I think we should introduce a filter or setting which similar to the Email notification threshold Under the Update Manager settings

    To allow end-users care about Stable releases only, or RC/beta/alpha/dev releases are ok. The use case is that some people only care about the stable releases, some people may care of RC, beta, even alpha releases. We should provide such an option.

Of course, all the above can be done in separate issues if they will be proceeding

tedbow’s picture

@jungle thanks for the review.

  1. Changing this string is out of scope for this issue. Also Recommended version by maintainer is actually not accurate the current XML doesn't have this information and it was never used in exactly this way.

    There is check box for recommended branch on the project page for maintainers but that is what is recommended on the project page not what the update module recommends, which currently that latest in a version in the major. See #3100115: The update module should recommend updates based on supported_branches rather than major versions majors

  2. This issue only deals with security issues. But you might be interested in this issue #2865920: Add tests for when a single supported branch has 2 security updates that are both not insecure
  3. Interesting idea. You might want to search if there is an issue for this an create on if not. I don't know of 1.

Of course, all the above can be done in separate issues

whoops didn't see this. Yes these would all have to be separate issues.

clayfreeman’s picture

Status: Needs review » Reviewed & tested by the community

LGTM; tests seem to be passing and I can't find anything wrong with the proposed changes.

tedbow’s picture

+1 for the RTBC. Thanks @dww

alexpott’s picture

Status: Reviewed & tested by the community » Patch (to be ported)

Committed e1b9321 and pushed to 9.1.x. Thanks!

I'm going ping the release managers about backport.

This looks okay to me. Hard t review but it makes sense once I groked what

    // Stop searching once we hit the currently installed version.
    if ($project_data['existing_version'] === $version) {
      break;
    }

was doing.

The code is definitely over-complex and hard to review / test. So it's great that our coverage is improving.

  • alexpott committed e1b9321 on 9.1.x
    Issue #2992631 by dww, swatichouhan012, tedbow, jungle, xjm: Update...
dww’s picture

Sweet, thanks! FYI: #20 applies cleanly all the way back to 8.8.x branch, so this should be cherry-pickable and doesn't require any new patches.

Cheers,
-Derek

  • alexpott committed e52f51a on 8.8.x
    Issue #2992631 by dww, swatichouhan012, tedbow, jungle, xjm: Update...

  • alexpott committed 90a553d on 8.9.x
    Issue #2992631 by dww, swatichouhan012, tedbow, jungle, xjm: Update...

  • alexpott committed f62c808 on 9.0.x
    Issue #2992631 by dww, swatichouhan012, tedbow, jungle, xjm: Update...
alexpott’s picture

Status: Patch (to be ported) » Fixed

Discussed with @catch and we agreed to backport this all the way back to 8.8.x

dww’s picture

Sweet, thanks!

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.