When a security vulnerability is discovered in one of the PHP libraries you use, there are several options how you can learn about the bug before it's too late. I've written about PHP Security Advisories Database in one of my previous posts and how you can use it with Roave Security Advisories and a few other ways. However all of them require an extra package or a tool.
The roave/security-advisories package has a long list of vulnerable libraries including versions, and trying to install one of those will fail with a warning. Besides the package I also use a GitHub Action called The PHP Security Checker, which in fact is the Symfony CLI security check packaged as an action. I've written about all these in my previous article, including examples.
But if you don't want to use anything extra and would like to utilize something you already use, then I have some good news for you. Starting with version 2.4 (released in summer 2022), Composer can query the database directly. It does that automatically after every composer require
and you can also check installed packages manually with composer audit
. Composer uses its own API, see for example a list of vulnerable guzzlehttp/guzzle versions.
Packages are also checked after running composer update
(in both cases the audit can be skipped with --no-audit
) but not by default after composer install
– the reason may be that it's often done automatically and there's no one to see the result anyway, or possibly the fact that it's done so often, for example when running tests, that it may lead to just too much API queries. But if you want, you can still run an audit after installation is complete with --audit
.
composer require
Let's preview how it looks like. Let's say I'd like to install a package version with a known vulnerability, like for example Guzzle 7.4.4 – Composer will install it but it will complain and alert me, check the last two lines:
$ composer require guzzlehttp/guzzle:7.4.4 ./composer.json has been created Running composer update guzzlehttp/guzzle Loading composer repositories with package information Updating dependencies Lock file operations: 8 installs, 0 updates, 0 removals - Locking guzzlehttp/guzzle (7.4.4) - Locking guzzlehttp/promises (1.5.2) - Locking guzzlehttp/psr7 (2.4.3) - Locking psr/http-client (1.0.1) - Locking psr/http-factory (1.0.1) - Locking psr/http-message (1.0.1) - Locking ralouphie/getallheaders (3.0.3) - Locking symfony/deprecation-contracts (v3.2.0) Writing lock file Installing dependencies from lock file (including require-dev) Package operations: 8 installs, 0 updates, 0 removals - Installing symfony/deprecation-contracts (v3.2.0): Extracting archive - Installing psr/http-message (1.0.1): Extracting archive - Installing psr/http-client (1.0.1): Extracting archive - Installing ralouphie/getallheaders (3.0.3): Extracting archive - Installing psr/http-factory (1.0.1): Extracting archive - Installing guzzlehttp/psr7 (2.4.3): Extracting archive - Installing guzzlehttp/promises (1.5.2): Extracting archive - Installing guzzlehttp/guzzle (7.4.4): Extracting archive 2 package suggestions were added by new dependencies, use `composer suggest` to see details. Generating autoload files 4 packages you are using are looking for funding. Use the `composer fund` command to find out more! Found 2 security vulnerability advisories affecting 1 package. Run composer audit for a full list of advisories.
It's more prominent with colors in your terminal:
You can change the format, see below, but still, you could easily miss it. Especially if you'd run composer require
with --quiet
meaning “do not output any message”.
composer audit
Maybe the very last line is more important then, it says to run composer audit
:
$ composer audit Found 2 security vulnerability advisories affecting 1 package: +-------------------+----------------------------------------------------------------------------------+ | Package | guzzlehttp/guzzle | | CVE | CVE-2022-31091 | | Title | Change in port should be considered a change in origin | | URL | https://github.com/guzzle/guzzle/security/advisories/GHSA-q559-8m2m-g699 | | Affected versions | >=7,<7.4.5|>=4,<6.5.8 | | Reported at | 2022-06-20T22:24:00+00:00 | +-------------------+----------------------------------------------------------------------------------+ +-------------------+----------------------------------------------------------------------------------+ | Package | guzzlehttp/guzzle | | CVE | CVE-2022-31090 | | Title | CURLOPT_HTTPAUTH option not cleared on change of origin | | URL | https://github.com/guzzle/guzzle/security/advisories/GHSA-25mq-v84q-4j7r | | Affected versions | >=7,<7.4.5|>=4,<6.5.8 | | Reported at | 2022-06-20T22:24:00+00:00 | +-------------------+----------------------------------------------------------------------------------+
That's much better. Here you see the vulnerable package name and even the vulnerability. Even the return value is not zero which means something went wrong, so this could be used in scripts, too:
$ echo $? 1
This is how it will look like when trying to install the newest version without known security vulnerabilities:
$ composer require guzzlehttp/guzzle [...] No security vulnerability advisories found Using version ^7.5 for guzzlehttp/guzzle [...]
And this is the composer audit
output then, including the command's return code which is now zero, meaning everything's alright:
$ composer audit No security vulnerability advisories found $ echo $? 0
For composer audit
to work properly the packages must be installed by default. But if you use --locked
(composer audit --locked
) then the audit is based just on the composer.lock
file and there's no need to install the packages beforehand.
Use --no-dev
if, for whatever reason, you'd like to disable auditing packages listed in require-dev
. I'd personally check everything though.
Thanks to the non-zero return value when a package with a known vulnerability is installed, composer audit
can easily be used in GitHub Actions for example. And that's exactly how I'm using it as well:
composer-audit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- run: composer audit
I run this check every two hours so that I know about a possible problem and the need to update in time. For a more important application than my website, I'd be happy to run it more often, like every hour.
Running a check on GitHub Actions
Composer can output the vulnerability information in several formats:
table
: a table, see above, default for composer audit
plain
: a textual output, no tablejson
: guesssummary
: just a short info whether something has been found, default for composer require
, composer update
, composer install
You can change the format with:
--audit-format=FORMAT
for composer require
, composer update
, composer install
--format=FORMAT
for composer audit
One more thing: you can update Composer by running composer self-update
.