-
Notifications
You must be signed in to change notification settings - Fork 145
Add RFC for install scripts whitelist #76
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
I like the idea. We treat package.json as a blueprint for the project and yarn.lock is more like a table of patterns to URLs and we don't have much more features inside it. |
It seems like there's actually right now a case of such a malicious package being used in the wild to steal peoples environment variables: https://twitter.com/o_cee/status/892306836199800836 Great to hear you like the idea @bestander 👍 I guess one question that impacts where the whitelist goes is what data it should actually contain. Should it whitelist all versions for a package or just a specific version range? Whitelisting version ranges would be the most powerful. One way to do so could be to just mimic how dependencies are added and do something like this in "allowScripts": {
"package-name": "1.0.0 - 1.2.0",
"another-package": "*"
} The existence of an Such a property could also be possible for |
We can also move this into |
Would the whitelist be for a particular version of a package, or for any version of the package? If it's not version-specific, it wouldn't protect against malicious code being added to an existing package, right? Edit: Oh, I just noticed you mentioned this above:
|
Can we use a slightly less problematic term than "whitelist"? Perhaps "inclusion list"? |
Why whitelist is problematic? |
@bestander "whitelist" is good, "blacklist" is bad, which relies on the connotations of one color being good and one bad, which has racial implications. (i really really hope nobody gets into a debate here on this, if anyone doesn't agree with the premise, a simple "no thanks" would be better than devolving into a debate on whether people's feelings matter) |
@ljharb Right, from my point of view I'll happily switch the wording to "install script opt-in" then, would convey the concept about as well and avoid anyone feeling offended. As a property in a file I think "allowScripts" make sense, so this would only change the RFC wording I think. @BYK Interesting idea, One benefit of that over Another is that it would also make it easy to share an opt-in list between projects, so that a team or company could enforce the same list across many projects. |
Given that naming a malicious package similarly to a popular one is a very common attack vector, and as an extra safety blanket, what if I also just found this discussion on |
I was going to suggest that it would be nice if you could opt-in to the script "content" itself (for a specific package) so that you didn't have to reapprove it when there had been no changes. The opt-in list would be hashes of the scripts themselves. But then I convinced myself that this opens up too many weird security edge cases, like a package changing structure in such a way that a previously harmless script became malicious. Just thought I'd share that one-person conversation. 😛 |
Allowing only packages at specific semvers is, theoretically, ideal. However, it is totally impractical in reality. Direct dependencies are already specified at specific version ranges, and the version of any subdependencies are specified by their respective This is a serious problem. One that really needs to be solved. However, it is not easily solved, and I don't think an inclusion list is practical, meaning it will do little for the security of the general populace of developers using NPM / Yarn, and only really serve the diehard security-conscious types, who probably have alternatives such as "don't use a package manager" to remedy their concerns already. |
@peabnuts123 I think an opt-in list can be manageable, at least in some cases. I too have a lot of unique modules in my work projects, but very very few of them needs to run any install scripts. For me personally I would probably run with something like this (in maybe "allowScripts": {
"node-sass": "*",
"gc-stats": "<=1.0.2",
"dtrace-provider": "<=0.8.3"
} That would narrow down the modules with vulnerable install scripts from a couple of hundreds to just a few, which would make me feel much more safe and which would be much more practical for me than todays only option, Of course things can be improved even further. This opt-in list can easily live side-by-side with other security measures and doesn't stop any work from happening on any such ones. To me this would just be a small simple step towards making install script security a bit more practical and a step that can be iterated on in the future – with eg. addition of better tools, both within yarn itself and within the wider ecosystem. |
Personally, I'd like to be able to just set Wherever this is stored, we should be sure to only respect this setting for the current project where Yarn is executing. In other words, an installed dependency should not be allowed to specify an I don't really like the idea of the community having to maintain a list of "bad" packages, because that is always reactionary. Someone has to realize it, add it to the list, and deploy the list. What if a "bad" package is already in my cache and I'm running --prefer-offline? how do I get the updated list? Just seems like a maintenance nightmare and should really be handled by NPM revoking known malicious packages from the registry. Whitelisting by semver range isn't a bad idea if it isn't too difficult to implement. There was an article recently where a huge number of NPM user's passwords were easily guessable (like just using "password") so an attack vector could certainly be pushing a new malicious version of existing packages if the publisher's credentials are weak. Of course this means that ranges |
i agree with @rally25rs regarding NPM's responsibility here. i think they should be able to revoke malicious packages or at least the other way around, flag "trusted packages" and make npm recognize only trusted sources in their repository by default. this could be done as a POC in yarn as from my understanding, they use a proxy for the official repo |
Lets keep to the topic of this RFC though @benyitzhaki, because as stated earlier – nothing in this RFC is incompatible with any such measures from npm:s side. Rather it would be great to have multiple layers of security. @rally25rs Yeah, version ranges in the opt-in list should rather be for targeting a range of earlier versions than for a range of upcoming ones (even though even such a range can, if wide, leave some room for malicious updates to earlier versions – like patch updates of old minor releases) I like the idea of using Using One thing regarding such inheritance though that would need to be defined is how I would say that whenever |
What about a prompt scripts option? Each script would ask permission to run, and the user could choose to remember that choice in yarn.lock. The user could also choose to inspect the script, or not run it. |
@xMrWhite, good idea. |
It should be able to determine all the packages that have script that could be run during the resolution phase. The scripts should continue to be run in parallel. I guess it depends on if the full package.json is queried during resolution. |
@xMrWhite I was thinking about something similar as well, but decided to keep this RFC focused on the minimal possible implementation so that it would be easier to later on try out different such interfaces and enable work on multiple such approaches in parallel, without any single one holding up the core mechanism itself. (For some such a mechanism that you suggest would probably be ideal, for others there can perhaps be a team-wide opt-in list instead, all depending on the type of project and the composition of development team. The sooner we can start experiment with different such interfaces, the better) |
I like the idea of prompting per script, but it would have to be an opt-in to stop and prompt. Keep in mind a lot of My thought for an initial implementation would be to just add a |
It should go into the yarn lock for the project, that already contains version information |
Good point raised, @xMrWhite. The role of yarn.lock is to store resolutions for all package patterns used in the project. .yarnrc sounds like the right place to store execution option, package whitelist is one of them. |
Maybe we should discuss the workflow. Would we expect a user to already have If someone already decided to disable scripts, then they probably already have a |
If anybody wants to audit what dependencies have
Output:
in my IDE (vscode) I can just click these file references and see exactly they are invoking, etc. |
|
This issue seems to have come up again: eslint/eslint-scope#39 A module stealing credentials (I think on install) I dropped the ball on this one due to life getting in the way pretty majorly, sorry 😐 I’ll try to pick this up again soon and rewrite the proposal to include the given feedback. |
Would an approach to allow specific features be more convenient? For example, when a script is executed, a custom "require", "fs", "http" and whatever else is provided. Whenever they are executed, they check if the requested operation has been already allowed in the yarn.lock previously, or if not would ask the user interactively if it's safe to execute. The allowed operations might be like "fs read relative_path", "http get url_wildcard" and so on. |
Implemented this as a separate package: https://www.npmjs.com/package/allow-scripts tl;dr:
fwiw, I hope I can deprecate that package soon, as |
Awesome @dominykas! Thanks for picking this up I see that npm is showing interest in this: I’ll happily try to update this PR with whatever the conclusion is there, or if there is none, whatever we would like to push here instead 👍 |
Fyi the v2 includes a way to disable postinstall scripts, via {
"dependenciesMeta": {
"[email protected]": {
"build": false
},
"webpack-cli": {
"build": false
}
}
} |
@arcanis interesting, does that mean it has them enabled by default? |
Yes, even though I was considering disabling them by default. This will likely be a rfc since it has far reaching consequences. Whatever we end up doing, the end goal is to give you the ability to choose whether they should execute or not both on a global basis and a per-package basis. |
@arcanis How do you see the status of this RFC and the ideas that npm puts forward, when seen in the light of the work in v2 here? Will the work in v2 be synced with npm in any way? Should this RFC be updated in any way to take the work you mention into consideration? |
Not particularly. Working with npm proved emotionally exhausting for me during this past year, and I've decided to stop being the only one of us making efforts to be the good neighboor, waiting months for them to share their wisdom, or seeing our features rebranded without so much as a notice. Even now I'd be happy to include them and discuss as peers, but it takes two to tango and I've lost hope seeing it happening under the present circumstances. npm be npm.
That sounds like a good idea - would you like me to update it? Or you can pop into Discord to discuss it, both are fine by me 🙂 |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
I'd like to ask everyone to assume good intent when addressing someone else. If you disagree with something someone else says, please consider that they believe that what they're doing is the right thing. In this context, using strong words like "absurd" is best avoided. Anyway, I believe the use case for the feature described in the RFC has been solved under the form of the new field |
Very neat @arcanis 👌 I would very much say that it solves this and it does so very elegantly! Thanks 🎉 |
Background
After discussions turned towards some security related topics in one of the front end chats I'm frequenting and we touched the fact it's not unheard of for scripts to accidentally delete peoples hard drives, or to even do something intentionally malicious, we moved the topic of npm scripts and the fact that one either allow them all to run any script they like on install, and exposes oneself to such risks, or one allows none to run anything, and then get a hard time with eg. modules that need to compile some native code.
Pretty much everyone in the discussions felt that it must be possible to do something better so I decided to wrap up some of the thoughts into a suggestion that could be proposed to one of the tools and since I use Yarn, I'm posting it here.
Suggestion
In summary the suggestion is to introduce the concept of a whitelist to allow a module to run install scripts so that one may vet them before they get to run anything automatically. That way modules that need to run stuff gets to run it after a vetting, the rest doesn't.
Pinging some of the people involved in the front end chat discussions: @aholstenson, @frozzare, @Kolombiken, @murm