8 ms·
Getting rid of NPM scripts
- weare138 6y agoWhat bugs me about npm is that many of the problems with npm have already been solved in the majority other open-source distribution systems. npm isn't doing is something new. There's no reason it should be suffering from old problems.
- thunderbong 6y ago<rant> IMHO, this is a problem not only with npm but with entire Javascript eco-system. Every new framework has to re-imagine and re-invent what has been done forever. Not only that, we need to wrap it with new jargon, because otherwise it'll not be the next cool thing (TM). Regardless of the framework, we finally end up with a declarative format for representing things. But that isn't how it is planned from the beginning. Instead, an entire generation of developers go through one version, and then have to migrate to the next version which breaks everything. So now we have groups of developers defending their decisions. Considering that most of the frameworks are open source, I don't see a collective approach to designing APIs. Yes, I know about web components, but that isn't going anywhere in the near future. </rant>
- tobyhinloopen 6y agoOnce a package is “hijacked” like this, it might as well contain a spawn(...) command in the JS code.
- coffeefirst 6y agoRight. The scary part is not code you're running can do something bad. Of course it can. It's that given the depth of the dependency trees, I don't have any confidence we can tell the difference between trustworthy and untrustworthy packages.
- turminal 6y agoGiving up sadly appears to be the only option for most use cases of npm and the like.
- efortis 6y agoI agree, but that's a different attack vector, and doesn't render this mitigation useless. @haburka gave an example for that.
- Waterluvian 6y agoI love git open[0] but I was always a bit mystified by how a simple "npm install" command can modify what it needs to in order for "git open" to become a valid command. It's convenient but, in my opinion, at a cost of "magic" being done to my machine with a "don't worry about it" attitude. A UX I could get down with would be "npx run git-open" followed by a short CLI that tells me what it's about to do, why, and asks for confirmation. [0]https://github.com/paulirish/git-open https://github.com/paulirish/git-open
- jessaustin 6y agoHow many people are this careful about install scripts, but then allow the installed code to run with just the same permissions as the install script would have run? Is that a meaningful difference? Is it more difficult to audit install scripts than to audit the rest of the code?
- haburka 6y agoActually the installed code may have less permissions and may execute in a different context. For example, an npm js library for a photo gallery may only ever run in the context of the browser. Of course some libraries for server code / build code may run in node js which ostensibly allows for ACE. But the surface area is smaller with this.
- burnthrow 6y agoInstall scripts typically have access to build environments.
- deleted 6y ago[deleted]
- jessaustin 6y agoOf course you're right in many situations, but in general secrets should not be allowed in build environments. If one doesn't expend the effort to limit permissions in the production environment, however, it's no different.
- paledot 6y agoDatabase credentials hopefully not, but the build environment will almost certainly have access to a private git repo somewhere.
- dtech 6y agoI've never seen a CI/CD without secrets, how else are you going to access private repos, access some live dependency during e2e testing or deploy the binary to its target?
- dblotsky 6y agoThe `./make` shell script kills me. Why not just use Make?
- smichel17 6y agoAnd as an added bonus, Make gives you tab autocompletion
- slmjkdbtl 6y ago(not strong arguments but some possible explanations) 1) ./make can be more portable e.g. you might use some GNU Make syntax that does not run on BSD and have to insall gmake just to run that 2) Make is not designed to be a command runner and have to manually add .PHONY to everything 3) case looks minimal and flexible enough (although I'm sure sh's confusing syntax can cause a lot of pain for non sh experts like esac wtf)
- dblotsky 6y agoThose all sound plausible! But... 1) I suspect GNU Make will be installed on any system where node code will run. 2) You don’t have to .PHONY anything that will never be a real file. 3) My bet is the Makefile will be shorter and clearer, but I am of course biased since I’m used to the syntax.
- efortis 6y agoI'm working on the Makefile, but I'm stuck with the `time ./make-production.js` task. `$ which time time: shell reserved word`
- tanepiper 6y agoA while back I wrote a opt-in tool called npl-lint[1] that would allow some CI-level enforcement of rules in package.json although I didn't go too far with it - one thing was to check the scripts section and allow whitelisted apps, or whitelisted sources for dependencies. It came about because I ended up having a spat with one of the NPM engineers at the time because they launched npx with the ability to run arbitrary gists[2] and this was before 2FA (FWIW you can still absolutely do this with npx). I wrote a proof of concept[3] that showed you could, inside a package.json add a command to install another package from a gist location, and then use that to steal credentials, bash history, etc. [1] https://github.com/tanepiper/npm-lint https://github.com/tanepiper/npm-lint [2] https://medium.com/@maybekatz/introducing-npx-an-npm-package-runner-55f7d4bd282b https://medium.com/@maybekatz/introducing-npx-an-npm-package... [3] https://github.com/tanepiper/steal-ur-stuff https://github.com/tanepiper/steal-ur-stuff
- efortis 6y agoThis is a valid approach. In rules.js, besides `.split('&&')`, I'd try other separators like ';' and '||' and encodings like '\046'
- pygy_ 6y agoSee also https://james-forbes.com/#!/posts/alternative-to-npm-scripts https://james-forbes.com/#!/posts/alternative-to-npm-scripts Which uses `eval "$@"` and bash functions, which let one pass arguments to your scripts. If you call the script file `run`, you end up with `bash run my-task`.
- hinkley 6y agoExcept yet again we have someone who doesn’t understand how exit codes work in bash shell scripts. Npm errors out if the sub commands generate a nonzero exit code. I’ve fixed so many bash scripts that ignore errors and just keep going.
- forty 6y agoWhen using the node_module/.bin path, can't a malicious package simply declare a binary called "mocha" so it's in the whitelist? Or does the ignore scripts option prevent npm from creating the symlink there? Also what I'm not sure I understand is: if you are installing a package that you don't want to run as a binary, it's probably that it's a library that you are going to import at some point. At this point, the malicious module will then be able to run arbitrary code with the same right as the current user...
- efortis 6y agoHi forty, this mitigation is for the install time vulnerability. To answer your question, at install time: no, at runtime: yes, but not always. For instance, if it's a frontend package it will run in a browser. Therefore, it can't create the symlink.
- forty 6y agoI understand, but this malicious package issue is also a problem when installing on the local dev environment. And the dev is probably going to run some kind of unit tests at some point, which will import the package and run whatever malicious code is there
- laurent92 6y agoThe only way to know is to forge a malicious script that encrypts the filesystem, slip it into a popular library, and see who complains. Otherwise people will always take half-decisions, only masking the types of attacks they know about. This, or black hats are already using this backdoor silently. Which makes me wonder: Should dev machines be considered high-risk? They run all sorts of software, with permissions loosened, then pass programs to CI tools, which build them and deploy to prod with AWS authentication tokens enabled... I’m shaking just seeing how many Chrome extensions my devs have (with all permissions enabled of course) when they access prod instances... Should startup management (Safe environnements with Excel, no Chrome extensions, accessing the company’s bank accounts, etc) be done on separate machines than development activities?
- 29athrowaway 6y agonpm wasn't built with security in mind. npm doesn't implement permissions. node doesn't implement permissions. deno does have slightly more emphasis on security, but JavaScript is not and will never be a security focused language. Everything is mutable, even type definitions.
- turminal 6y agoI think it's possible (and not impossibly hard) to overcome the security shortcomings of the language, but the real problem is persuading the ecosystem (starting with its leaders that design core libraries and infrastructure like npm) that security is something they should care about. The way javascript ecosystem designs its products affects basically everyone with an internet connection. It's almost like they had a moral responsibility to improve themselves.
- e12e 6y agoI'm not sure I get this. You can be pretty sure the platform on which your js package is installed has a js runtime. But it might not have shell or make (eg: windows). If you have specific needs and no ambition for being cross-platform ; sure, go ahead and use rc, batch, shell scripts. And no, just staying with js isn't enough to be cross platform - but you'll be closer. I don't see why a package would have a backdoor in install, but not run - or not in the package code itself?
- dubbel 6y agoIt's by far the most popular method of malicious payload execution for these kinds of attacks. It's easy to write, definitely executed (if not for the settings suggested by the blog posts), doesn't depend on the usage of the library by the victim, and you don't really need to know anything about the setup the victim has. You are right in that it's not the patch that fixes the entire problem, but that is rarely the case in security. Source: earlier this year i analysed every package dependency compromise between 2006-2017. https://www.haukeluebbers.de/blog/2020-01-timeline-of-package-dependency-compromises/ https://www.haukeluebbers.de/blog/2020-01-timeline-of-packag... You can see that the most common method for npm, Rubygems and pypi is something like "Method: executed by install hook"
- turminal 6y agoThis is terrible on so many levels. Why does npm execute scripts by default if they are not to be trusted? Why would anyone install some software via npm unless they are confident it isn't malicious? Why does npm use a single flag for scripts in all contexts and if this thing is a known problem, why don't they add a flag for handling this specifically?
- richajak 6y agonpm is full of gotchas as I encountered one recursive bug today. I realize that can put 'npm' as one of the dependency packages in package.json It did not complain, suddenly it downloaded 400+ packages. How about a typo? malicious actors can benefit from this.
- atom_arranger 6y agoAnother possible reason to get rid of npm scripts is performance. I'm using esbuild (https://esbuild.github.io/ https://esbuild.github.io/) in a monorepo setup currently and the time to build the whole repo (~200ms) is the same as the overhead of running `yarn command`. Running `yarn esbuild ...` is ~400ms, running `esbuild ...` directly is ~200ms.