On the 12th of July, GitHub pushed a security advisory for the popular JavaScript sandbox, vm2; not one, but two sandbox escape vulnerabilities leading to remote code execution.
As a CVE Analyst on Semgrep Supply Chain, which uses SAST (i.e., Semgrep OSS) to do reachability analysis on projects like vm2, it’s my job to research these vulnerabilities as they come out and write reachable code detection rules for them so that anyone using these repositories can know if they’re affected. This CVE was an ideal finding, a code library with an obvious main function new VM().run()
. Typically, I would write a custom Semgrep rule to identify and flag usage of that function as dangerous and move on to other CVEs, but that didn’t end up happening this time.
The undeniable importance of vm2
The updated README for the vm2 repository informed all users that a fix for the new vulnerabilities did not exist and is not planned for the future, and furthermore, the entire vm2 project is being discontinued. The most popular open source solution for sandboxing unsafe JavaScript code, used by over 200k projects on GitHub alone, was reportedly broken beyond repair! The owner, Patrick Simek, thanked all the contributors, recommended a promising replacement library, and closed the project for good.
As someone new to the application security community, I was shocked. It’s not hard to believe that a passion project, maintained by essentially two people, would one day run out of steam. What really boggles me is that thousands of other software products use it without investing any time or resources to help the maintainers or even having the foresight to use a better-maintained library. When the announcement happened, developers were asking the questions they should have been asking months before: “Is my app impacted?”, “How much time do I have to migrate?”, “Is this issue possible to fix with more help?”, “How can I, or my company, help fix this problem?”
Open source software tradeoffs
However, the more senior members of my team saw this coming a mile away. This is a much bigger issue than vm2. Practically every tech company depends on open source software projects without considering their maintainer count, number of recent commits, bug reports, and security advisories. Free, working software provides massive productivity increases that are too good, sometimes even impossible, to pass up. In a fast-paced industry, where solutions need to be deployed yesterday in order to stay competitive, tradeoffs need to be made. Usually, this results in a “replace it later” mentality that only sometimes replaces dependencies much later.
After years of this mentality, we’ve kicked the collective can so far down the road that real problems are becoming more frequent (John McBride wrote a great article on this topic specifically). HummusJS was replaced with MuhammaraJS in 2019, Node’s request module was put in maintenance mode in 2020, mysqldump was archived in 2021, and the Gorilla web toolkit was archived in 2022. And now, in 2023, vm2 has been added to the software graveyard. Every discontinued dependency brings weeks of crunch, desperately migrating codebases to prevent an open vulnerability or a gap in service. This doesn’t need to be our reality.
Vm2 discontinuation was inevitable
The latest disclosures in vm2 weren’t an isolated incident. In the past year, there were 8 total GitHub security advisories tied to the vm2 project, almost all of them being sandbox escape vulnerabilities. What’s interesting is that there was not a single security advisory in the first 8 years leading up to this; and furthermore, the majority of these disclosures happened in April and May, meaning that 7 of the 8 total advisories happened within a 4-month time frame. Not only would this be an unusually high strain on the maintainers, but it would also help explain why many in the community were surprised at the discontinuation of the library.
Even if project dependents were paying attention to the issues arising in vm2, they would only have a couple of months to help fix it or migrate to another library. There were a few organizations that were able to pivot away from vm2 quickly, like backstage, but most either did not see this massive wave of vulnerabilities as a risk, or didn’t notice. So, taking a step back, let’s imagine a situation where most dependents were actively following vm2’s development and did react to the warning signs, either by volunteering to help with the project or by moving to a different one. Would that have changed anything?
With some deprecated projects, it’s easy to see where things went wrong after the fact, but with vm2 it’s not so clear. The maintainers have stated that this problem would have come up sooner or later, regardless of help or attention. XmiliaH, the main contributor up until discontinuation, has noted that “The problems that piled up are caused by node
intercepting calls from the sandbox and therefore arguments not being wrapped in a Proxy
” (see the issue thread). For context, under the hood, the vm2 module is using the standard Node module vm
, and is using JavaScript object proxies to isolate object access, along with various other protections. The vm module is notoriously insecure, and escaping it may be a fun exercise for any of you technically savvy readers. From this description, it can be inferred that they are unable to properly proxy certain arguments because Node itself is intercepting calls, which is an issue with the vm module itself and cannot be fixed without removing the module altogether. This is a rare and unfortunate circumstance, sometimes, certain approaches to technical problems just reach dead ends. So maybe this project was handled as well as it could have been handled, and it would be best to just let it rest, but if that’s the case, where do we go from here?
The potential solution: Isolated-vm
I mentioned earlier that Simek recommended another project to transition to, called isolated-vm. This package is fundamentally different from vm2 in that it does not rely on Node’s vm
module and instead uses v8’s Isolate
interface (for context, v8 is a JavaScript/WebAssembly engine written in C++). This project might actually be more promising than vm2, making it possible to isolate JavaScript code in a reliable manner long-term securely, but only if it’s given the proper attention it deserves. As it stands, isolated-vm is a relatively new project that doesn’t have many maintainers nor much documentation and is currently in maintenance mode. We already have concerned community members trying to help projects transition from vm2 to isolated-vm (for example, this discussion post). If you or your organization recently made this transition, I highly suggest sharing your knowledge with the community; you’ll be helping thousands of developers in the same position. And if you have any knowledge in this domain and might be interested in contributing, don’t be afraid to do so. This project is going to need a lot of help managing the likely influx of users in the coming months.
Going forward, it may be in our best interest to contribute what we can to the isolated-vm project, either by helping out with maintenance, filling out documentation, or performing security research. The fact of the matter is that if isolated-vm gets discontinued, there will no longer be any easy-to-use solution for sandboxing JavaScript code. We have a chance to learn from past mistakes here; we can’t let this project die.
In the spirit of helping open source software, as I mentioned earlier, I work on Semgrep Supply Chain, which Semgrep OSS to do reachability analysis on projects like vm2. If you have any questions about Semgrep or want to contribute to it, feel free to let us know on our Community Slack.