NPM dependency hell: comparison with Symfony, Laravel and API Platform

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

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:

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:

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:

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.

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.

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).

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.

7 Comments

  1. > 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

    9 out of 14. Which also means 1/3 of the packages are out of this scope.
    Ok, it’s better than Angular but are we really saying that this means “OK, we are covered” ? and you are only describing the skeleton, without any added bundle or package.

    Worse, you describe packages of individuals as “trusted” because they are maintainers or former maintainers in the symfony community. But still, this does not address the recent problem. I am sure lots of people could have trusted
    Dominic Tarr before the issue. It only happens he gave control to someone else without notice. We are not speaking of a direct malicious original author.

    Could this happen to these individual maintainers and former maintainers ? sure ! At least I didn’t saw any formal statement that they won’t ever give control of their own individual packages on github. Maybe they won’t, maybe we trust they won’t, but nothing has ever been said about it. Maybe a lot of people had trust Dominic Tarr wouldn’t do that, but he still did.

    Symfony is at trust because it is a project with peer review and a process. Every package maintained by an individual (or where one individual has full control) is at risk. This is what happened.

    My count on the website skeleton is different from yours. I may trust doctrine, facebook, monolog, phodocumentor, sensio, psr, symfony, twig, zendframework, maybe swiftmailer because they are well known projects with multiple maintainers and peer reviews before any release, not individuals. This still leaves me with 1/3 of uncontrolled source.

    In don’t review them manually on every update. You probably don’t either. Does the symfony team control them on any update ? If you say so, do we have a formal statement on that ? I don’t think so (but I may have missed it).

    And what is sure, is that you can’t rely on “this is used everywhere, one would notice if there was a malicious update” because that is exactly what didn’t worked recently.

    And hey, I am wrong about trusting these 2/3 of organization with peer reviews because they are the one who did add the additional 1/3 as dependencies. Would they add one additionnal tomorow if they think it’s a good idea ? probably. Would *you* review it if it happen ? probably not. Do they have a clear formal statement about what to include as dependency and what garantee they expect ? not that I know of.

    Sure, you can say that it’s worse in NPM world because they are building packages for every single line of code, but this does not mean we are safe in PHP either. And remember, we are only speaking of the skeleton of symfony.

    Reply

    1. > 9 out of 14. Which also means 1/3 of the packages are out of this scope.

      Well,

      * doctrine : it’s optional, and it’s a very popular project maintained by a team
      
* easycorp : Symfony Core Team, if you don’t trust Javier, you shouldn’t use Symfony either
      * 
egulias
 : very popular lib, but indeed we could get rid of this one by default
      * facebook : very popular lib – but despite the name the current maintainer doesn’t work for Facebook
      
* fig : PHP FIG
      * jdorn : dependency of Doctrine (but it could be nice to drop this dependency)
      
* monolog : by the creator of Composer, if you don’t trust Jordi, don’t use Composer nor Symfony
      
* nikic
 : PHP Core Team, if you don’t trust Nikita, don’t use PHP…
      * ocramius : PHP Core Team, Doctrine Core Team, if you don’t trust Marco, don’t use PHP…
      
* phpdocumentor
 : very popular and historic project (could maybe be dropped)
      * psr : PHP FIG

      * sensio : the creators of Symfony, if you don’t trust them, don’t use Symfony
      * swiftmailer : part of the Symfony project (is being moved inside Symfony)
      
* symfony
 : –
      * twig : part of the Symfony project
      
* webmozart
 : former SF Core Team, and still 3rd contributor ever, that being said, this one is a transitive lib from phpdocumentator, and we probably can get rid of it
      * zendframework : well… they co-created PHP

      Among all these deps, all **look** safe, and most of them are from well known developers.

      > and you are only describing the skeleton, without any added bundle or package.

      Symfony, as a project, can only take care of the projects under its control. Some 3rd party libraries are also “trusted” because they have official recipes: Doctrine, API Platform, EasyAdmin, and they are carefully selected (popular projects maintained by people we know).

      If you install something else (a random bundle or lib), it’s up to you, it’s not the concern of Symfony, and it’s made very clear (for instance, to install a community recipe, you need to confirm that you know what you’re doing, and that this code isn’t maintained by the Symfony Core Team).

      > Does the symfony team control them on any update ?

      Symfony is a free project, mostly maintained by volunteers, on their free time.
      As stated in its license: “THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED”.

      We only provide a “best effort” guarantee. If you need a security audit, you’ll have to pay for it.
      Note that the community has already paid for it in the past: https://symfony.com/blog/symfony2-security-audit, it could be interesting to do that again, but it needs money.

      In this regard, the statement of Dominic Tarr is more relevant than ever, and it applies to Symfony too: https://gist.github.com/dominictarr/9fd9c1024c94592bc7268d36b8d83b3a

      > Would *you* review it if it happen ? probably not.

      Indeed, except if you pay me to do it, and **it’s exactly why we try to not add dependencies that are not very useful**.

      > Sure, you can say that it’s worse in NPM world because they are building packages for every single line of code, but this does not mean we are safe in PHP either.

      It’s exactly my conclusion. You can never be sure, but you can try to be reasonable. And if you want more security, you’ll have to pay for it, and/or to inject money in the open source ecosystem.

      To be clear, I don’t criticize any people, especially not Dominic Tarr. I only question the practice of using untrusted dependencies, even for things as trivial than a modulo, or padding.

      That being said, I also maintain a bunch of JS libraries, that suffer too from this very specific issue (some of them probably installed event-stream as transitive dependencies).
      Unfortunately, I cannot do much about it: they use popular deps that I can’t avoid such as React, that use trusted deps, that themselves use untrusted deps (the dependency hell). And we don’t speak about dozens of packages from around 10 organizations as for Symfony, but literally from hundred of authors.

      The only thing I can do to improve the situation, is to write such blog posts to raise awareness, and NOT doing that for my own packages.

      > And remember, we are only speaking of the skeleton of symfony.

      It’s not true, the skeleton of Symfony only comes with trusted package from SF itself, and some interfaces from the FIG. “website skeleton” comes with ALL features officially supported by Symfony, even the more touchy one such as E2E testing with Panther.

      Reply

  2. Probably is it’s not just the PHP elements that these depend on.

    Laravel comes with 72 php packages. But enter the new project and run npm install and it will add all the front-end dependencies along for vue and the like.

    Just tried on a new 5.7.* laravel project here: *added 1256 packages in 81.358s*

    Want to look though the vendors / maintainers of that nest?

    Reply

    1. Indeed, I mentioned that for API Platform, but Laravel has the same issue. My point is to compare PHP and JS ecosystems. Projects, such as Laravel and API Platform, that are part of both, of course suffer from the same problems than React, Vue, Angular… (basically because they use them).

      Reply

  3. The biggest difference is that PHP packages are built from source on packagist.org, while NPM packages have to be pushed, meaning that the binaries could contain anything.

    Reply

Leave a Reply