You may have noticed the recent fuss about the compromise of event-stream, a popular NPM package:

An NPM package with 2,000,000 weekly downloads had malicious code injected into it. No one knows what the malicious code does yet. https://t.co/V4rdenu7Bm — Gary Bernhardt (@garybernhardt) November 26, 2018

event-stream is a transitive dependency of many popular JavaScript projects including Vue, Angular, Gatsby and VSCode (some of them are using a version that isn’t affected by the attack).

This attack raised, again, the problem of the JS dependency cascade: when you install a major project, it comes with hundreds of tiny libraries, sometimes not maintained, and sometimes coming from untrusted sources.

Some claimed that the problem wasn’t specific to the JavaScript ecosystem, and that projects in other languages such as Rails and Symfony were also suffering from a similar dependency hell.

A Twitter poll created by Rafael Dohms highlights that most developers believe that it’s only a matter of luck if this issue has affected the JavaScript ecosystem, and not the PHP one:

So in light of the NPM malicious code issue that came up this week. We have never seen anything similar in PHP/composer, why? (Poll) — Rafael Dohms (@rdohms) November 27, 2018

Regarding Symfony, as a maintainer I have the feeling that the Symfony Core Team (carefully) adds dependencies only when strictly necessary. However I had no metrics to prove it. So I checked. Then I compared with other PHP frameworks I’m interested in: Laravel and API Platform.

Symfony

When installing Symfony 4.1 using the official skeleton, only 20 packages are downloaded (19 when excluding dev dependencies). 16 are directly maintained by the Symfony project, and the 4 others are interfaces from the PHP Framework Interoperability Group:

$ composer create-project symfony/skeleton nb-deps $ composer info | wc -l 20 $ composer info | cut -d/ -f1 | uniq psr symfony

Even when installing the website skeleton, that comes with all features provided by the framework and third party (trusted) packages such as the Doctrine ORM, the Twig templating library or Swift Mailer, only 94 packages (75 when excluding dev dependencies), from 17 different organisations (14 without dev deps) are installed:

$ composer create-project symfony/website-skeleton nb-deps $ composer info | wc -l 94 $ composer info | cut -d/ -f1 | uniq doctrine easycorp egulias facebook fig jdorn monolog nikic ocramius phpdocumentor psr sensio swiftmailer symfony twig webmozart zendframework

Among these vendors, 7 are directly maintained by members (or former members) of the Symfony Core Team (easycorp, monolog, sensio, swiftmailer, symfony, twig, webmozart) and 2 are from the FIG. All these few libraries, except maybe jdorn/sql-formatter, are actively maintained, by prominent and well known members of the PHP community.

API Platform

A minimal installation of the server part (the one written in PHP) of the API Platform framework contains only 27 packages (26 without dev deps) from 5 vendors.

$ composer create-project symfony/skeleton nb-deps $ composer require api-platform/core $ composer info | wc -l 27 $ composer info | cut -d/ -f1 | uniq api-platform doctrine psr symfony willdurand

When installing the API pack, that provides all features you can expect from an advanced web API (hypermedia support, automatic persistence with the Doctrine ORM support, automatic generation of human-readable documentation, CORS support, authorisation rules…), 57 packages (56 without dev deps) from 10 vendors are shipped.

$ composer create-project symfony/skeleton nb-deps $ composer require api $ composer info | wc -l 57 $ composer info | cut -d/ -f1 | uniq api-platform doctrine jdorn nelmio phpdocumentor psr symfony twig webmozart willdurand

On the other hand, the API Platform distribution comes with (optional) frontend tools: an admin and a Progressive Web Apps and mobile apps generator. They are built-on top of React, so even if the JavaScript libraries directly maintained by the API Platform team depend of just a few carefully selected libraries… these dependencies are typical JavaScript ones, and come with hundreds of dependencies (see after).

Laravel

The default installation of Laravel (that is somewhat similar in term of features to the Symfony website skeleton) comes with 72 packages (39 when excluding dev deps) from 35 vendors (21 when excluding dev deps).

$ laravel new nb-deps $ composer info | wc -l 72 $ composer info | cut -d/ -f1 | uniq dnoegel doctrine dragonmantank egulias erusev fideloper jakub-onderka laravel league monolog nesbot nikic opis paragonie psr psy ramsey swiftmailer symfony tijsverkoyen vlucas

The number of maintainers you have to trust when using Laravel is just a bit larger than when using Symfony, but again it’s still a bunch of people that are well known in the PHP community, and who may fit in a single room.

In comparison, a default installation of React using Create React App – that is more similar to the minimal Symfony skeleton than to fully-featured frameworks such as the Symfony website skeleton or Laravel – comes with 809 packages, most of them being maintained by different teams or individuals.

Another main difference is that most JS libraries are distributed as compiled and minified builds. Therefore it’s very difficult to guarantee that what is shipped and executed behaves exactly the same way than the code in the sources. In PHP, the source of the libraries are used directly, without intermediate obfuscation. PHP builds are easy to reproduce, it helps a lot when auditing.

Of course, it doesn’t mean that major PHP frameworks are immune to this kind of attacks and – as any IT project – they also have their own security issues. However, the amount of third party code installed and the chain of trust you have to rely on is more under control than in the JS world.

By the way, you’d better actively monitor the security vulnerabilities that may affect your PHP projects.

