Recently we upgraded our development environments to use npm v12.
One of the headline features of npm 12 (which admittedly was late to the party, as this is already a feature in pnpm, yarn, bun etc.) is that npm now blocks install / postinstall scripts by default, in response to a rise in supply-chain attacks using such scripts as a way to run code on unsuspecting developer machines.
Packages that do have a genuine need for these scripts can be allowed-listed using the npm approve-scripts command, but it requires every project with such dependencies occurring somewhere transitively in their dependency graph to make this decision independently. We are seeing many packages move away from install scripts as a result.
In our Forge apps, we noticed some common packages that use install or postinstall scripts, and digging deeper revealed some interesting things about the dependencies of certain @forge/* packages.
In no particular order, and in no way is this list exhaustive, here are some surprising things we have noted:
@forge/cli
The latest version of this package (14.0.0) still includes a postinstall hook that runs the script ./out/bin/postinstall.js.
As best we can tell, this script seems to report analytics?
The script itself is a bit of a scary read at first glance (it mentions things like getCredentials()), but if we understand correctly this is actually intended to ensure that any analytics payloads are sent unauthenticated(?).
Is there a better way of doing this that doesn’t require every consumer of the package to get an npm warning on install about a postinstall script, and then have to review a potentially scary looking (and poorly documented) script to make a decision about whether or not to allow this?
I’m assuming that doing nothing (script remains blocked) simply means that analytics won’t get sent, so that’s good, but it seems like we could avoid unnecessary concerns by not having this implemented as a postinstall script? (e.g. maybe report analytics on the first invocation of the cli rather than at install time)
cloudflared
This is a dependency of @forge/tunnel and is what I understand is the thing that actually enables tunnelling to a local development machine.
This package has a postinstall script that appears to first build the code, and then runs node lib/cloudflared.js bin install which presumably installs the built package.
In the github issues for this package, there is no mention of moving away from the postinstall script (e.g. distributing prebuilt binaries instead), so allowing this package to run install scripts is likely to be required for most Forge developers.
There is currently nothing in the Tunneling documentation to warn about this, so it requires any developer installing @forge/cli into their project for the first time to notice that cloudflared had blocked install scripts, and approve them.
If I recall correctly, debugging why tunnelling doesn’t work when cloudflared’s postinstall script didn’t run is also not obvious.
keytar
This is a dependency of @forge/cli-shared, and is used to manage the system keychain.
The concerning thing here is that the git repository for this package was archived by its owner in 2022, and the project has had no updates since.
You could perhaps argue that this simply means that the library is “feature complete”, but it also means that the likelihood of this package removing its install script (which runs node-gyp build to build a native binary) is also unlikely to ever be changed.
The project also has a number of issues that Socket.dev regards as supply chain risks:
Approving npm to run an install script from library that has had no attention for 4 years, contains a number of security issues, and has been archived by its owner, seems…problematic.
Could we perhaps review whether @forge/cli-shared still needs this dependency, and whether there is an alternate library that is both maintained and doesn’t require an install script?
iframe-resizer
This is a dependency of @forge/bridge, and is used to size app iframes to their content.
The concern here is that @forge/bridge pins to version ^4.4.5 of library, which was published back in July 2024 and has a postinstall script.
There are later versions of this library all the way up to 5.5.9 published in February of this year, and these later versions don’t include a postinstall script.
Looking at what the postinstall script in 4.4.5 actually does, it looks like it only displays a banner message saying that version 5 of the library splits into two separate packages, so it appears to be harmless to leave this script blocked.
But it is concerning that @forge/bridge relies on a 2 year old version of a library when there are newer versions available. Can we possibly look at upgrading @forge/bridge to use a later version of this library?
Conclusion
I’m sure there more packages deep in the dependency graphs of other @forge/* packages that have similar issues, but these are just the ones that appeared in our Forge apps.
For all the attention on security these days, including forcing vendors in the Bug Bounty program to make their programs public; it sure seems like the teams at Atlassian responsible for the @forge/* packages could be a bit more proactive in keeping their dependencies updated, especially in the face of changes like npm 12’s decision to follow the other package managers and start blocking install scripts by default.

