Look I love composer. I’ve written about it a lot (and if my analytics are any indication, you all love when I do)! But, this week illustrates one of the very few times when composer can actually be a problem on projects: when you have a dependency chain that is actually blocking you from performing a security (or other, needed) update.
So this post is all about how to work around a time when composer is physically blocking you from doing something you need. As with any “let’s work around it” tutorial writeups, I strongly suggest you do this rarely and not make it common practice. Why? Well the whole point of a dependency manager is to ensure you have the right versions of dependencies in your project. If you fake it out constantly, you’re more and more likely to break your crap. So tread lightly and remember, just because you have a very powerful tool doesn’t mean everything you do with it is safe.
The Situation
The particular situation that arose this week was that the Archive_Tar library had a vulnerability discovered per CVE-2020-36193. A release was made for this in the pear/archive_tar package at version 1.4.12. However, the drupal/core-recommended project, this particular package was pinned at version 1.4.11 (see the composer.json file). Obviously Drupal is very responsive to security vulnerabilities and they jumped on a release today (January 20th) but for some projects that run security testing that prevents builds from passing with known vulnerabilities (like BLT powered builds) or projects that are particularly at risk to this vulnerability… this composer definition is really problematic. You only have a few options:
remove drupal/core-recommended (which is ironically, not recommended)
disable your security testing to allow builds to pass
“just wait” for drupal/core to cut a release that allows the update
alias the dependency.
Option 4 is what we’re here to talk about today!
Creating a Composer Alias
When you alias a dependency using composer, what you are essentially doing is:
installing a dependency at a particular version)
telling composer (and its dependency tree mapping) that the dependency is at an entirely different version.
In the case of pear/archive_tar this might look like:
pear/archive_tar: 1.4.12 as 1.4.11
This essentially gets you the best of both worlds. The 1.4.12 version of the package will be installed (resolving the security vulnerability) but composer will track this dependency as if you were actually installing version 1.4.11.
To do the alias, you can simply edit your composer.json file as above. Then run a composer update of pear/archive_tar --with-all-dependencies.
Concerns with Composer Aliasing
As I mentioned up front in the intro, this is a technique you should use sparingly. There are times when you might need to test a new version of something, implement a security update ahead of other packages that might block the update (which is the scenario outlined above), etc. that might make aliasing a necessity. Having said that, it’s still quite dangerous. When a piece of software declares a particular version requirement in a dependency manager, this is an explicit statement that “this software is known to work with these versions of these dependencies.” When you alias around these specific requirements you exponentially raise the chances of something going wrong. The more you do it, the more likely something is to go wrong.
What could go wrong?
The most likely thing is that code somewhere in your project may try to use something in a dependency that has been changed / removed and suddenly it doesn’t work as intended. I saw a video recently of a dog (who was unfortunately blind) trying to jump onto a couch that was no longer there. It’s much the same with underlying dependency changes when an alias is in place. The thing making the requirement thinks it’s running one version when in reality, you’ve changed the installed dependency.
Again, for a security update, this is probably (hopefully) ok, especially given circumstances such as the pear/archive_tar scenario. It’s a short term fix, it’s only one minor version tick for a single library, etc. But I guess the point I’m trying to make is that this is a technique you should keep in your back pocket for emergency use only (or, use in a testing scenario that doesn’t actually get committed and deployed).
And, if you’re wondering why I’m teaching you a technique that is “dangerous” and “maybe shouldn’t be used…” well, again… if you’re in this situation there’s not really a great option to deal with the vulnerability. If you depend on something that depends on something else (and so on) and somewhere down that dependency tree there’s a version pinned, you literally cannot change that pin until that defining dependency updates.
In Conclusion
Anytime you use technology to help automate something, I believe it’s important to know “how to get out of trouble” with that technology. That might be temporarily disabling coding standards. That might be aliasing a dependency. Regardless of what it is, knowing what to do, when to do it, why to do it, and how to undo it is a key part of these technologies! Remember, when you’re manually doing everything, it’s a lot easier to “just change” what you’re up to. When it’s automated and you entirely rely on something like composer to change your dependencies, there are now rules to follow for those dependencies. Just like in other disciplines (like art and design) there are sometimes very valid reasons to break the rules. Just don’t make a habit of it!
Dependency trees can be massive, and with the upcoming Drupal 10 release PHP 8.1 could have a significant impact on your project.