Keeping track of upstream security issues

Sat, 06/08/2022 - 20:08 -- James Oakley

The need

On 20th June this year, the Drupal Security Team issued a public service announcement, logically named PSA-2022-06-20, entitled "Updated security policy for Drupal core Composer dependencies".

In a nutshell, it discusses what happens when a library that Drupal core is dependent on issues a new release because of a security vulnerability.

Previously, the Drupal maintainers would issue a new release of Drupal core, so that site maintainers update their installation and benefit from the secured version of the upstream library. Going forwards, this will no longer happen, unless Drupal core is known to be exploitable as a result. Site maintainers are now responsible for monitoring upstream libraries, and keeping them up to date.

That's reasonable enough in theory. In practice, there are a great many of them. A quick check on one site I manage showed that drupal/core requires a further 44 packages. They will announce new releases in different places. A workable strategy is needed to keep track of such things.

A Reminder giving us an example

The release notes for Drupal 9.4.4 reminded site maintainers of the importance of keeping track of such things. It was a bug-fix release, not a security release, but would fix a vulnerability in a dependent package (laminas/laminas-diactoros)

Drupal core uses the third-party Diactoros library as its PSR-7 implementation. Diactoros has issued a security advisory:

Drupal core is unlikely to be vulnerable. This bugfix release updates the version of Diactoros used in drupal/core-recommended to a secure version as a precaution. As a reminder, Drupal 9.4 and higher sites should update their third-party Composer dependencies when an upstream security advisory is issued.

That reminded me that I did not have an adequate way to monitor fixed vulnerabilities in dependent packages. In an ideal world, I would already have known of the Diactoros vulnerability and installed a version that fixes it.

Solutions that won't work

So how do we keep track?

One possibility would be to run composer outdated. The problem there is that a site can be up to date on all the packages you directly installed, but still have many outdated dependencies. I've just checked (composer outdated) on one site, and there were 48 outdated packages. None of them were security issues. So this approach wouldn't work because I'd be prompted about every minor dependent update, and those do not require urgent attention.

Running composer outdated --direct also doesn't work. It only lists top-level packages that are out of date. It would tell me if drupal/core-recommended needed updating, but not about a package dependent on it. This is the problem I'm trying to solve: Drupal core will no longer be updated just because of a vulnerability in a dependency.

Running composer outdated --major-only doesn't work. That shows outdated packages that are a major update in terms of their semver numbering, not in terms of their importance at a security level

Introducing the audit command

Composer 2.4 introduced a new command: composer audit.

Here's what their documentation says about it:

This command is used to audit the packages you have installed for possible security issues. It checks for and lists security vulnerability advisories according to the Packagist.org api.

Sounds perfect, let's give it a try.

On a site that had an outdated version of Diactoros (2.11.0), here was the result

$ composer audit --format=plain
Found 1 security vulnerability advisory affecting 1 package:
Package: laminas/laminas-diactoros
CVE: CVE-2022-31109
Title: Diactoros before 2.11.1 vulnerable to HTTP Host Header Attack.
URL: https://github.com/advisories/GHSA-8274-h5jp-97vr
Affected versions: <2.11.1

Now update the site to the latest version of Drupal core (9.4.5, at time of writing), and run it again:

$ composer audit --format=plain
No security vulnerability advisories found

Exactly what we wanted

Composer 2.4

I said that the audit command was new as of Composer 2.4. At time of writing, the stable version of Composer is 2.3, and 2.4 is in release candidate status. You'll either need to wait for 2.4 to be released to use this command, or run composer self-update --preview to pick it up early.

If you're running a global installation of Composer, and do not wish (or do not have root access) to update the whole server to 2.4, you can install a local copy and run that.

Running from cron

The last piece in the puzzle is that I want to run this command from cron.

I assume readers know how to use cron, but there are two things to watch.

Firstly, Composer needs to run from the CLI version of PHP, which may not be the one cron serves up. So you may need to specify which PHP the command is to use. Your server may vary, but something like:

/usr/local/bin/php ~/bin/composer audit

Second, you'd want the command to have no output unless an issue is found. For some reason, "No security vulnerability advisories found", is written to StdErr not StdOut. So it's not enough to remove that line from the output; we also need to redirect StdErr to StdOut first. Without repeating the paths to PHP and composer from the above snippet:

composer audit 2>&1 | grep -v "No security vulnerability"

Alternative Approach with Drush

Since I first published this, I've discovered a Drush command I didn't know of: drush pm:security-php.

Check non-Drupal PHP packages for pending security updates. Packages are discovered via composer.lock file. An exit code of 3 indicates that the check completed, and insecure packages were found.

I've just tested this on a site that was still running Diactoros 2.11.0, and it returned as follows:

 [warning] One or more of your dependencies has
an outstanding security update.
 [notice] Run composer why laminas/laminas-diactoros
to learn what module requires the package.
laminas/laminas-diactoros:
  version: 2.11.0
  time: '2022-07-06T09:24:53+00:00'
  advisories:
    -
      title: 'Diactoros before 2.11.1 vulnerable to HTTP Host Header Attack.'
      link: 'https://github.com/advisories/GHSA-8274-h5jp-97vr'
      cve: CVE-2022-31109

That's also exactly what is needed, and has the advantage of not needing a particular version of Composer.

Over to you

Thanks for reading, and I hope this helps. If you've found other ways to keep track of all relevant security advisories, I'd love to hear in the comments. I'd love to know if there are particular reasons to prefer a Drush approach or a Composer approach.

Blog Category: 

Comments

eiriksm's picture
Submitted by eiriksm on

Thanks for the excellent post! Two great suggestions, I would say it's a matter of taste what you prefer. Some additional resources:

There are also several other tools, plus third party services.

For example. You have https://github.com/fabpot/local-php-security-checker and https://symfony.com/doc/current/setup.html#security-checker

There is also https://github.com/Roave/SecurityAdvisories which you can use in your CI setup to get notified by that

Then there are several third party services. Full disclosure, i am the founder of one such service: violinist.io. You can configure violinist to create pull requests on all security updates (direct or indirect). Which would of course mostly help if you have your site in git and use a git hosting provider like GitHub, Bitbucket or Gitlab (hosted or self hosted)

James Oakley's picture
Submitted by James Oakley on

Thanks - I'll look into those. I use self-hosted gitea, which I guess is also supported?

eiriksm's picture
Submitted by eiriksm on

It is actually not :(

It has not been a much requested feature, but I would be glad to give out a free premium subscription if you want to beta test and support is added at some point? :)

James Oakley's picture
Submitted by James Oakley on

Thanks - I'll look into that. Time to drag the database into the 21st century, I guess. Or finish the D7->D9 migration which has been nearly done for too long now.

Add new comment

Additional Terms