Forge npm dependencies and postinstall scripts

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.

@scottohara on cloudflared, the script can stay blocked as long as some cloudflared is on PATH. The CLI does a which for it.

I ran it this morning on the published @forge/cli 14.0.0 with --ignore-scripts. No bin/ under cloudflared. forge tunnel then fails with an error about network restrictions and reinstalling the CLI. Neither applies. Only its last line helps: “Alternatively, you can install cloudflared on your machine.” Not obvious, agreed.

With a PATH cloudflared (brew here) the same CLI prints Establishing forge tunnel with the local cloudflared binary, lints, bundles and listens. The fallback shipped in @forge/tunnel 5.9.0, Feb 2025. Staff said so in Cloudflared forge tunnel not working - #31 by LashaniPerera and 95823 #10. Zero mentions in the tunneling or corporate-network docs.

Keytar is the one that bites. No native build, forge login fails with “couldn’t securely store your login credentials in a local keychain” and whoami, lint, tunnel say “Not logged in”. FORGE_EMAIL and FORGE_API_TOKEN get past it. scott.dudley hit teh same in The CLI couldn't securely store your login credentials in a local keychain

Thanks @scottohara for digging into this and sharing with us.

This is really useful feedback. I think it’s fair to say we’ve got some gaps to close here. Even as a start, improving our documentation on some of these things should be a minimum.

keytar is the clearest example. We’re actively working on what the right path forward here should be including evaluating @napi-rs/keyring as a replacement or going in a different direction entirely.

More broadly, we’ll take a look at where we’re relying on install and postinstall scripts across the Forge npm packages and the dependencies called out in this thread. We’ll also follow up with the internal teams that own the relevant packages so we can work towards a future CLI release that behaves better with npm 12.

Appreciate you raising this so clearly. We’ll come back with more once we’ve worked through the options.