Most active commenters
  • oulipo2(5)
  • johnisgood(4)
  • no_wizard(3)

←back to thread

441 points longcat | 64 comments | | HN request time: 2.049s | source | bottom
1. inbx0 ◴[] No.45040282[source]
Periodic reminder to disable npm install scripts.

    npm config set ignore-scripts true [--global]
It's easy to do both at project level and globally, and these days there are quite few legit packages that don't work without them. For those that don't, you can create a separate installation script to your project that cds into that folder and runs their install-script.

I know this isn't a silver bullet solution to supply chain attakcs, but, so far it has been effective against many attacks through npm.

https://docs.npmjs.com/cli/v8/commands/npm-config

replies(17): >>45040489 #>>45041292 #>>45041798 #>>45041820 #>>45041840 #>>45042872 #>>45043977 #>>45045311 #>>45045447 #>>45045979 #>>45046082 #>>45046981 #>>45047430 #>>45047994 #>>45049197 #>>45049793 #>>45050820 #
2. tiagod ◴[] No.45040489[source]
Or use pnpm. The latest versions have all dependency lifecycle scripts ignored by default. You must whitelist each package.
replies(3): >>45040822 #>>45041469 #>>45049075 #
3. chrisweekly ◴[] No.45040822[source]
pnpm is not only more secure, it's also faster, more efficient wrt disk usage, and more deterministic by design.
replies(1): >>45041523 #
4. halflife ◴[] No.45041292[source]
This sucks for libraries that download native binaries in their install script. There are quite a few.
replies(2): >>45043190 #>>45046036 #
5. jim201 ◴[] No.45041469[source]
This is the way. It’s a pain to manually disable the checks, but certainly better than becoming victim to an attack like this.
6. norskeld ◴[] No.45041523{3}[source]
It also has catalogs feature for defining versions or version ranges as reusable constants that you can reference in workspace packages. It was almost the only reason (besides speed) I switched a year ago from npm and never looked back.
replies(1): >>45043771 #
7. homebrewer ◴[] No.45041798[source]
I also use bubblewrap to isolate npm/pnpm/yarn (and everything started by them) from the rest of the system. Let's say all your source code resides in ~/code; put this somewhere in the beginning of your $PATH and name it `npm`; create symlinks/hardlinks to it for other package managers:

  #!/usr/bin/bash

  bin=$(basename "$0")

  exec bwrap \
    --bind ~/.cache/nodejs ~/.cache \
    --bind ~/code ~/code \
    --dev /dev \
    --die-with-parent \
    --disable-userns \
    --new-session \
    --proc /proc \
    --ro-bind /etc/ca-certificates /etc/ca-certificates \
    --ro-bind /etc/resolv.conf /etc/resolv.conf \
    --ro-bind /etc/ssl /etc/ssl \
    --ro-bind /usr /usr \
    --setenv PATH /usr/bin \
    --share-net \
    --symlink /tmp /var/tmp \
    --symlink /usr/bin /bin \
    --symlink /usr/bin /sbin \
    --symlink /usr/lib /lib \
    --symlink /usr/lib /lib64 \
    --tmpfs /tmp \
    --unshare-all \
    --unshare-user \
    "/usr/bin/$bin" "$@"
The package manager started through this script won't have access to anything but ~/code + read-only access to system libraries:

  bash-5.3$ ls -a ~
  .  ..  .cache  code
bubblewrap is quite well tested and reliable, it's used by Steam and (IIRC) flatpak.
replies(7): >>45043567 #>>45045504 #>>45046093 #>>45049572 #>>45049755 #>>45053685 #>>45092016 #
8. eitau_1 ◴[] No.45041820[source]
Why the same advice doesn't apply to `setup.py` or `build.rs`? Is it because npm is (ab)used for software distribution (eg. see sibling comment: https://news.ycombinator.com/item?id=45041292) instead of being used only for managing library-dependencies?
replies(3): >>45041933 #>>45046598 #>>45050487 #
9. arminiusreturns ◴[] No.45041840[source]
As a linux admin, I refuse to install npm or anything that requires it as a dep. It's been bad since the start. At least some people are starting to see it.
replies(1): >>45049302 #
10. ivape ◴[] No.45041933[source]
It should apply for anything. Truth be told the process of learning programming is so arduous at times that you basically just copy and paste and run fucking anything in terminal to get a project setup or fixed.

Go down the rabbit hole of just installing LLM software and you’ll find yourself in quite a copy and paste frenzy.

We got used to this GitHub shit of setting up every process of an install script in this way, so I’m surprised it’s not happening constantly.

11. sheerun ◴[] No.45042872[source]
Secondary reminder that it means nothing as soon as you run any of scripts or binaries
12. junon ◴[] No.45043190[source]
You can still whitelist them, though, and reinstall them.
13. shermantanktop ◴[] No.45043567[source]
This is trading one distribution problem (npx) for another (bubblewrap). I think it’s a reasonable trade, but there’s no free lunch.
replies(1): >>45045056 #
14. mirekrusin ◴[] No.45043771{4}[source]
workspace protocol in monorepo is also great, we're using it a lot.
replies(1): >>45044359 #
15. andix ◴[] No.45043977[source]
I guess this won't help with something like nx. It's a CLI tool that is supposed to be executed inside the source code repo, in CI jobs or on developer pcs.
replies(1): >>45045383 #
16. dvfjsdhgfv ◴[] No.45044359{5}[source]
OK so it seems too good now, what are the downsides?
replies(5): >>45044388 #>>45045456 #>>45046645 #>>45049226 #>>45050575 #
17. c-hendricks ◴[] No.45044388{6}[source]
If you relied on hoisting of transitive dependencies, you'll now have to declare that fact in a project's .npmrc

Small price to pay for all the advantages already listed.

replies(1): >>45047446 #
18. homebrewer ◴[] No.45045056{3}[source]
Not sure what this means. bubblewrap is as free as it gets, it's just a thin wrapper around the same kernel mechanisms used for containers, except that it uses your existing filesystems instead of creating a separate "chroot" from an OCI image (or something like it).

The only thing it does is hiding most of your system from the stuff that runs under it, whitelisting specific paths, and optionally making them readonly. It can be used to run npx, or anything else really — just shove move symblinks into the beginning of your $PATH, each referencing the script above. Run any of them and it's automatically restricted from accessing e.g. your ~/.ssh

https://wiki.archlinux.org/title/Bubblewrap

replies(1): >>45045600 #
19. ◴[] No.45045311[source]
20. inbx0 ◴[] No.45045383[source]
According to the description in advisory, this attack was in a postinstall script. So it would've helped in this case with nx. Even if you ran the tool, this particular attack wouldn't have been triggered if you had install scripts ignored.
21. herpdyderp ◴[] No.45045447[source]
Unfortunately this also blocks your own life cycle scripts.
22. nawgz ◴[] No.45045456{6}[source]
‘pnpm’ is great, swapped to it a year ago after yarn 1->4 looked like a new project every version and npm had an insane dependency resolution issue for platform specific packages

pnpm had good docs and was easy to put in place. Recommend

23. TheTaytay ◴[] No.45045504[source]
Very cool. Hadn't heard of this before. I appreciate you posting it.
24. conception ◴[] No.45045600{4}[source]
It means that someone just has to compromise bubblewrap instead of the other vectors.
replies(5): >>45045751 #>>45045980 #>>45046340 #>>45047397 #>>45049907 #
25. cozzyd ◴[] No.45045751{5}[source]
sure but surely one gets bubblewrap from their distro, and you have to trust your distro anyway.
26. ashishb ◴[] No.45045979[source]
I run all npm based tools inside Docker with no access beyond the current directory.

https://ashishb.net/programming/run-tools-inside-docker/

It does reduce the attach surface drastically.

27. theamk ◴[] No.45045980{5}[source]
Not "instead", it's "in addition to". Your classical defense-in-depth.
replies(1): >>45046101 #
28. lrvick ◴[] No.45046036[source]
Downloading binaries as part of an installation of a scripting language library should always be assumed to be malicious.

Everything must be provided as source code and any compilation must happen locally.

replies(1): >>45046109 #
29. oulipo2 ◴[] No.45046082[source]
Does it work the same for pnpm ?
30. oulipo2 ◴[] No.45046093[source]
Will this work on osX? and for pnpm?
replies(1): >>45050741 #
31. oulipo2 ◴[] No.45046101{6}[source]
No, "instead". If they compromise bubblewrap to send out your files, and you run bubblewrap anyway for any reason, you're still compromised.

But obviously you can probably safely pin bubblewrap to a given version, and you don't need to "install packages through it", which is the main weakness of package managers

replies(2): >>45050728 #>>45050891 #
32. oulipo2 ◴[] No.45046109{3}[source]
Sure, but then you need to have a way to whitelist
replies(1): >>45048400 #
33. haswell ◴[] No.45046340{5}[source]
While this may be true, this is still a major improvement, no?

i.e. it seems far more likely that a rapidly evolving hot new project will be targeted vs. something more stable and explicitly security focused like bubblewrap.

34. username223 ◴[] No.45046598[source]
It should, and also to Makefile.PL, etc. These systems were created at a time when you were dealing with a handful of dependencies, and software development was a friendlier place.

Now you're dealing with hundreds of recursive dependencies, all of which you should assume may become hostile at any time. If you neither audit your dependencies, nor have the ability to sue them for damages, you're in a precarious position.

35. TheRoque ◴[] No.45046645{6}[source]
Personally, I didn't find a way to create one docker image for each of my project (in a pnpm monorepo) in an efficient way
replies(1): >>45047451 #
36. peacebeard ◴[] No.45046981[source]
Looks like pnpm 10 does not run lifecycle scripts of dependencies unless they are listed in ‘onlyBuiltDependencies’.

Source: https://pnpm.io/settings#ignoredepscripts

37. throwawaysoxjje ◴[] No.45047397{5}[source]
Am I getting bubblewrap somewhere other than my distro? What makes it different from any other executable that comes from there?
replies(1): >>45054097 #
38. no_wizard ◴[] No.45047430[source]
Pnpm natively lets you selectively enable it on a package basis
39. no_wizard ◴[] No.45047446{7}[source]
They’re moving all that to the pnpm-workspace.yaml file now
40. no_wizard ◴[] No.45047451{7}[source]
That’s not really a pnpm problem on the face of it
41. lrvick ◴[] No.45048400{4}[source]
The whitelist is the package-lock.json of the hashes of libraries you or a security reviewer you trust has reviewed.
42. trw55 ◴[] No.45049075[source]
Same for bun, which I find faster than pnpm
43. antihero ◴[] No.45049197[source]
I wonder how many other packages are going to be compromised due to this also. Like a network effect.
44. mirekrusin ◴[] No.45049226{6}[source]
Downside is that you have to add "p" in front, ie. instead of "npm" you have to type "pnpm". That's all that I'm aware of.
45. azangru ◴[] No.45049302[source]
> As a linux admin, I refuse to install npm or anything that requires it as a dep. It's been bad since the start.

As a front-end web developer, I need a node package manager; and npm comes bundled with node.

46. internet_points ◴[] No.45049572[source]
Thanks, handy wrapper :) Note:

    --symlink /usr/lib /lib64 \
should probably be `/usr/lib64`

and

    --share-net \
should go after the `--unshare-all --unshare-user`

Also, my system doesn't have a symlink from /tmp to /var/tmp, so I'm guessing that's not needed for me (while /bin etc. are symlinks)

47. johnisgood ◴[] No.45049755[source]
Firejail is quite good, too. I have been using firejail more than bubblewrap.
48. johnisgood ◴[] No.45049793[source]
At this point why not just avoid npm (and friends) like the plague? Genuinely curious.
replies(1): >>45050476 #
49. johnisgood ◴[] No.45049907{5}[source]
This is such a defeatist perspective. You could say this about anything ad nauseum. I think bubblewrap (or firejail) is less likely to be a successful target.
50. ifwinterco ◴[] No.45050476[source]
I work for a company that needs to ship software so my salary can get paid
replies(1): >>45050821 #
51. ifwinterco ◴[] No.45050487[source]
For simple python libraries setup.py has been discouraged for a long time in favour of pyproject.toml for exactly this reason
52. Nathanba ◴[] No.45050575{6}[source]
A few years ago it didn't work in all cases when npm did. It made me stop using it because I didn't want to constantly check with two tools. The speed boost is nice but I don't need to npm install that often.
53. aragilar ◴[] No.45050728{7}[source]
How? bubblewrap isn't something someone has randomly uploaded to npm, it has well known maintainers and a well organised release process (including package signing). Which is easier to do: upload a package to npm and get people to use it, or spend 2+ years trying to become a maintainer of bubblewrap or one of its dependencies to compromise it.
replies(1): >>45051693 #
54. aragilar ◴[] No.45050741{3}[source]
No, bubblewrap uses linux namespaces. You can use for (almost) whatever software you want.
55. dns_snek ◴[] No.45050820[source]
Whenever I read this well-meaning advice I have to ask: Do you actually read hundreds of thousands of lines of code (or more) that NPM installed?

Because the workflow for 99.99% of developers is something resembling:

1. git clone

2. npm install (which pulls in a malicious dependency but disabling post-install scripts saved you for now!)

3. npm run (executing your malicious dependency, you're now infected)

The only way this advice helps you is if you also insert "audit the entirety of node_modules" in between steps 2 and 3 which nobody does.

replies(1): >>45053433 #
56. johnisgood ◴[] No.45050821{3}[source]
Can't you guys replace the most vulnerable parts with something better? I have been experimenting with Go + Fyne, it is pretty neat, all things considered.
replies(1): >>45051811 #
57. ChocolateGod ◴[] No.45050891{7}[source]
Bubblewrap uses the same Linux functions that billion dollar cloud infrastructure use. Bubblewrap does no sandboxing/restrictions itself, it's instructing the kernel to do it.
58. oulipo2 ◴[] No.45051693{8}[source]
Sure, but there's plenty of packages with well-known maintainers who get compromised...
replies(1): >>45052173 #
59. ◴[] No.45051811{4}[source]
60. haswell ◴[] No.45052173{9}[source]
The fact that something can happen is separate from how likely that thing is to happen, and that’s what matters here.

The comments here that point to this theoretical possibility seem to be missing the point, which is that using something like bubblewrap is an improvement over running arbitrary projects un-sandboxed, and the likelihood of such an attack is far less than the likelihood of any one of hundreds of rapidly evolving, lesser known, lesser scrutinized projects getting compromised.

61. IshKebab ◴[] No.45053433[source]
Yeah I guess it probably helps you specifically, because most malware is going to do the lazy thing and use install scripts. But it doesn't help everyone in general because if e.g. NPM disabled those scripts entirely (or made them opt-in) then the malware authors would just put their malware into the `npm run` as you say.
replies(1): >>45061183 #
62. shermantanktop ◴[] No.45054097{6}[source]
Nothing. Does your threat model assume 100% trust in your distro? I understand saying you trust it a lot more than the garbage on npm. But if your trust is anything less than 100%, you are balancing risk and benefit.
63. dns_snek ◴[] No.45061183{3}[source]
Indeed it may save you in case the malware is being particularly lazy but I think it may do more harm than good by giving people a false sense of security and it can also break packages that use post-install scrips for legitimate reasons.

For anyone who actually cares about supply chain attacks, the minimum you should be doing is running untrusted code in some sort of a sandbox that doesn't have access to important credentials like SSH keys, like a dev container of some sort.

You would still need to audit the code otherwise you might ship a backdoor to production but it would at least protect you against a developer machine compromise... unless you get particularly unlucky and it also leverages a container escape 0-day, but that's secure enough for me personally.

64. aorth ◴[] No.45092016[source]
Very cool idea. Thanks for sharing. I made some minor tweaks based on feedback to your comment:

  #!/usr/bin/env bash
  #
  # See: https://news.ycombinator.com/item?id=45034496
  
  bin=$(basename "$0")
  
  echo "==========================="
  echo "Wrapping $bin in bubblewrap"
  echo "==========================="
  
  exec bwrap \
    --bind ~/.cache ~/.cache \
    --bind "${PWD}" "${PWD}" \
    --dev /dev \
    --die-with-parent \
    --disable-userns \
    --new-session \
    --proc /proc \
    --ro-bind /etc/ca-certificates /etc/ca-certificates \
    --ro-bind /etc/resolv.conf /etc/resolv.conf \
    --ro-bind /etc/ssl /etc/ssl \
    --ro-bind /usr /usr \
    --setenv PATH /usr/bin \
    --symlink /usr/bin /bin \
    --symlink /usr/bin /sbin \
    --symlink /usr/lib /lib \
    --symlink /usr/lib64 /lib64 \
    --tmpfs /tmp \
    --unshare-all \
    --unshare-user \
    --share-net \
    /usr/bin/env "$bin" "$@"

Notably `--share-net` should be moved down since it is negated by `--unshare-all`. I also added a reminder that the command is being bubblewrapped, modified the second read-write bind to the current directory, and changed the final exec to use `/usr/bin/env` to find the binary so it can be more flexible. I tested it with npm and yarn just now and it seems to work well. Thanks!