-
Notifications
You must be signed in to change notification settings - Fork 239
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
[RRFC] Change the default save-prefix from ^ to none #509
Comments
Related reply to ljharb:
This leads me to believe you just skimmed the post. This case clearly isn't exceedingly rare - bugs or malice being uploaded to npm in the form of patch updates is happening very frequently. I linked almost 7 examples that have happened just in recent memory. Lockfiles do not solve the problem when your lockfile gets changed. If you have the following package.json and a generated package-lock file: {
"foobar": "^1.0.0",
"react": "^17.5.4",
"something-else": "^3.0.0"
} Consider the case where Calling this "exceedingly rare" is missing the mark completely. The amount of people I've seen bitten by the colors.js and ua-parser-js thing is absurd. |
I don't think it is missing the mark. 8 packages out of hundreds of millions in a decade is statistically insignificant. This in no way means the problem should be dismissed, but it does mean the cost of a fix must be weighed carefully. A lockfile is the only way to pin dependencies. Pinning them in package.json achieves nothing because transitive deps can update at any time. |
It doesn't really matter how many packages are compromised. It only takes one compromised library depended on by a lot of others to do a ridiculous amount of damage.
I'm not suggesting to remove |
You'd use overrides, new in npm v8.3, to force the transitive dependency to be an alternative version, or alternative package. |
Interestingly, from my POV, this is the more salient point to make in all this (IMO)
Since as you correctly point out..
☝️ This is one of the main reasons I prefer Yarn, the |
Could you specify command by command what should be wrote to do that? At the moment it doesn't matter that your lockfile says EDIT: Npm v8.3 was released under a month ago! What should people do if they're on npm7 or npm6? |
npm 6 and 7 are both EOL, so those people should be upgrading to npm 8 regardless. |
Ah, I didn't know 6 and 7 were EOL. Regardless, not everyone on |
The current process is that applications should always have a lockfile. Your CI should be running These changes are pushed up, and validated by CI against your tests. Before being merged, it would of course go through code review - which would include both the lockfile changes as well as any code changes. Ideally, most applications also run This tends to work incredibly well for bugs, vulnerabilities, and malice. The |
I appreciate this, but you're not replying to what I've said. How do you safely install a new package
This is provably untrue, as both colors.js and ua-parser have affected thousands if not millions of machines. This system clearly does not work well. |
You don't - you let them update, and you catch it in review/CI.
can you cite your source for this info? |
What you're saying is that there is no way to do anything involving Catching security bugs by just letting arbitrary code run on a CI server is a terrible idea (exploit the CI server, mine bitcoin on it, run up lambda bills... Infact, I have had people personally mention to me that it caused their CI to log an obscene amount of information. That can be costly in the cloud!). What this means is that you cannot When I tell NPM to install a new package
And this definitely did not spread by 25m weekly users all individually choosing to update to 1.44-liberty or whatever it was - This was an attack that leverages this loose "update everything whenever possible" default behaviour. |
https://web.archive.org/web/20220110130906/https://www.npmjs.com/package/colors |
Thanks! I couldn't find the download counts now that the malicious versions were removed. so, 190,000 downloads out of 23 million, which is about 0.08%? |
1.4.1 and 1.4.44 were also compromised, pushing it closer to 650,000 out of 23m (2% ish). Please keep in mind that all those downloads came in in under 24 hours. The 23 million statistic is weekly downloads. If you were to naively multiply that 650,000 by 7, you'd see a far worse statistic of 4.55 million downloads. That said, the percentage of downloads that are bad has nothing to do with it - This still spread through the ecosystem at a rapid rate. For a more insightful look into the impact of this into organisations, look at the amount of linked PRs and Issues in the colors.js issue. This is clearly affecting the ecosystem in a significant way. Infact, the only reason this has such a small amount of downloads is because NPMs security team worked on it pretty fast. |
The colors issue caused temporary downtime in at least two development environments I was actively working in. A companies Review/CI team should not be expected to comb through every dependencies dependencies to find an issue, especially if the issue only affects fresh I agree this is on the package maintainers for not pinning dependencies, not the npm team, however this RFCs proposal will reduce the chances of this happening in the future.
It's a bit unreasonable to expect a developer to pin all shared dependencies in overrides to avoid issues with rogue packages. |
One could argue that this malicious action with colors / faker was specifically intended to get notoriety and draw attention to itself. How many of these go under the radar though? Miners and other data harvesting type attacks from scammers typically do not go out of their way to make their presence known, and when they are revealed, often it is too late or the damage is done. Also, isn't it a common strategy of scammers, to spam out scams to millions, and bet on the odds that if just a small percentage fall for it, then they've got what they need? I'm not saying we can protect everyone 100 percent of the time, but I think there is a lot of merit to discussing the point of could |
@pfych any environment in which it caused downtime failed to use a lockfile. Any environment which used one didn’t have any downtime. |
This isn't the point though. Almost every environment use lockfiles at this point (they've been included for years!). The default behaviour of |
I understand the impulse, but this feels more like a way of mitigating headlines than exploits. Looking through the list of critical and high severity vulnerabilities, it seems to me that:
ExamplesBugs
Malware
|
I know I'm a bit late to the discussion, sorry if I'm repeating something, haven't read all the comments. But when one of my colleagues joined my team, this was one of his first suggestions: to pin all versions of our packages. My reply was that the package-lock.json would do it for us, and he agreed. Lastly, I wanted to add that when I install something new, not all of the package-lock is affected, as stated above, only stuff related to the library I installed or updated. We have pretty much gotten used to using
7.24.2
@ant-design/charts ^1.2.14 → ^1.3.4
@mui/material ^5.2.6 → ^5.2.8
@okta/okta-auth-js ^5.9.1 → ^5.10.0
history ^4.10.1 → ^5.2.0
luxon ^2.2.0 → ^2.3.0
react-router-dom ^5.3.0 → ^6.2.1
react-virtuoso ^2.3.4 → ^2.4.0
@types/luxon ^2.0.8 → ^2.0.9
@types/node ^17.0.5 → ^17.0.8
@typescript-eslint/eslint-plugin ^5.8.1 → ^5.9.1
@typescript-eslint/parser ^5.8.1 → ^5.9.1
eslint ^8.5.0 → ^8.6.0
eslint-plugin-import ^2.25.3 → ^2.25.4
jest 27.4.5 → 27.4.7
msw ^0.36.3 → ^0.36.4
added 1 package, and audited 2071 packages in 5s
diff --git a/frontend/ui/package-lock.json b/frontend/ui/package-lock.json
index 11831e8a..ac4e2fca 100644
--- a/frontend/ui/package-lock.json
+++ b/frontend/ui/package-lock.json
@@ -18,6 +18,7 @@
"@okta/okta-auth-js": "^5.9.1",
"@okta/okta-react": "^6.3.0",
"@reduxjs/toolkit": "^1.7.1",
+ "axios": "^0.24.0",
"chroma-js": "^2.1.2",
"history": "^4.10.1",
"lodash": "^4.17.21",
@@ -6297,6 +6298,14 @@
"node": ">=4"
}
},
+ "node_modules/axios": {
+ "version": "0.24.0",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz",
+ "integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==",
+ "dependencies": {
+ "follow-redirects": "^1.14.4"
+ }
+ },
"node_modules/axobject-query": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz",
@@ -11000,7 +11009,6 @@
"version": "1.14.6",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.6.tgz",
"integrity": "sha512-fhUl5EwSJbbl8AR+uYL2KQDxLkdSjZGR36xy46AO7cOMTrCMON6Sa28FmAnC2tRTDbd/Uuzz3aJBv7EBN7JH8A==",
- "dev": true,
"funding": [
{
"type": "individual",
@@ -28876,6 +28884,14 @@
"integrity": "sha512-WKTW1+xAzhMS5dJsxWkliixlO/PqC4VhmO9T4juNYcaTg9jzWiJsou6m5pxWYGfigWbwzJWeFY6z47a+4neRXA==",
"dev": true
},
+ "axios": {
+ "version": "0.24.0",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-0.24.0.tgz",
+ "integrity": "sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==",
+ "requires": {
+ "follow-redirects": "^1.14.4"
+ }
+ },
"axobject-query": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz",
@@ -32635,8 +32651,7 @@
"follow-redirects": {
"version": "1.14.6",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.6.tgz",
- "integrity": "sha512-fhUl5EwSJbbl8AR+uYL2KQDxLkdSjZGR36xy46AO7cOMTrCMON6Sa28FmAnC2tRTDbd/Uuzz3aJBv7EBN7JH8A==",
- "dev": true
+ "integrity": "sha512-fhUl5EwSJbbl8AR+uYL2KQDxLkdSjZGR36xy46AO7cOMTrCMON6Sa28FmAnC2tRTDbd/Uuzz3aJBv7EBN7JH8A=="
},
"font-atlas": {
"version": "2.1.0", |
Just came off #510, and there's some excellent points made against this proposal. It seems like this is far too heavy handed for the issue, and I'm very much inclined to agree. However, I am very interested by a related issue that's been brought up in parallel to this, which is that the default behaviour for I think this is a surprising behaviour, and should probably be looked into being changed. I'm going to open a separate RRFC for that now, as I think it's far more actionable and far more interesting. Regardless, I'm pretty convinced that this (^ -> "") is not a good solution for this problem. |
An RRFC for my proposal already kind of exists at #415. I'll write what I think in there, then. |
I know this very late but @zkldi Can you elaborate a bit more on the counter arguments and the implications of having this as a default? Unfortunately the meeting on 12th of January is nowhere to be found and from the meeting notes it's not very clear what were the points 😞 |
The issue here is that it just moves the problem one-layer down. If a dependency of yours is pinned to a faulty version of a dependency, it's difficult to actually get that dependency to update to safe version. consider the case where
if bar was lenient and allowed any patch upgrades, these would be safe to meld together and you'd be using 1.0.2 throughout the stack. if it isn't, you have to wait for the In retrospect I'm not entirely convinced this is a critical blocker -- a way of specifying overrides would be nice. The real issue (which was highlighted in that call) was that lockfiles just aren't honoured by default, and a ridiculous amount of users run |
@zkldi its not difficult anymore - npm now has overrides. I don’t think the problem this rfc wants to solve is actually a problem now. |
(Ported from npm/cli#4227)
TLDR
This PR changes the default behaviour of NPM to not save package versions with the
^
prefix.Instead, packages would be saved with no prefix.
As a TL;DR, the implications of this is that all dependencies will be pinned to the version you installed by default.
Changing This Yourself
If you're reading this PR and want to apply it to your own environments, invoke
npm config set save-prefix=''
.Existing Behaviour
The
^1.0.0
range means that NPM will accept any package that is1.0.0
or greater in the MINOR or PATCH field. This means1.0.2
and1.2.0
are legal matches, but2.0.0
is not.Since packages on NPM are expected to follow semver, this has the presumed intention of automatically opting users into security patches. For details I'll go into in a minute, there are no actual security benefits to this design decision.
NPMs design has changed a lot since its original iteration, and I feel it is important to note that this decision was made back in npmv1 (or older). This means this design decision predates package-lock.json, --save being the default, and more.
Why change it?
There are massive security implications for this default. Semver is not enforceable, and what constitutes a
PATCH
/MINOR
to one person might be a breaking change to another.As an example,
mongodb
's3.6.4
update causedmonk
to endlessly spit out warnings.This can have pretty serious implications for people using NPM in production. Blindly running
npm install
on a different machine can result in different dependencies being installed to your local dev version! Sure, you installed3.6.3
, but^3.6.3
means that npm is perfectly within its right to get3.6.4
. (n.b.npm ci
should be used to avoid this behaviour, but why is the default dangerous?)Although
package-lock.json
exists to solve this,package-lock.json
is regenerated whenever an option touchespackage.json
ornode_modules
. This means thatnpm install
ing another module will result in all of your^x.x.x
packages being bumped aswell.Security Implications
If a library is compromised somehow, the author can publish a PATCH/MINOR version that performs malicious actions. Given that semver is not enforceable (NPM cannot possibly say -- hey, this change is too major for a patch release), this means that the compromised library can essentially do whatever it wants to almost every single one of its dependents.
Since this happens automatically as the result of doing other things (i.e. anything that interacts with
package.json
), this means even installing a new, entirely separate package can result in the compromised library getting exec access.Scope for attack
NPM is based around micropackages. For any given package, there are likely to be hundreds or even thousands of tiny packages holding it up. Every single one of those libraries is a target for attackers, as compromising any of the ones that make up a larger package can result in full code execution on millions of machines or applications.
Furthermore, NPM does not enforce 2FA on accounts of package authors. If a package authors password is leaked online, they could have their account hijacked. Although this would be bad on its own, the fact that they can then upload malware that near-automatically propogates across the entire ecosystem moves the scope from bad to incredibly bad.
While changing the default from
^
to nothing does not fix all of these issues, it would stunt the spread of malicious patch updates, as it would only affect users newly directly installing the package, rather than installing any package that depends on it. This moves the difficulty up (in theory) from compromising any micropackage to compromising a larger package (say,react
) in order to get such an impact.Examples
While
colors.js
is the example everyone is currently thinking about, this is not even close to the only time this has happened.Here is a list of just some packages in recent memory that have leveraged this default behaviour to perform large scale attacks.
Arguing against 'automatic patching'
A potential retort to this would be that the current behaviour causes users to get potential security patches automatically. However, this is not really the case.
Consider the case where a user has a bunch of packages, and
foobar@1.0.1
has a security vulnerability.The user's package json has
"foobar": "^1.0.0"
.A new version of
foobar
is released to deal with this security vulnerabilty.The user is not safe in any way against their vulnerability until they do anything that interacts with their
package.json
. When they do, it is possible thatnpm
will update the package to the latest patch version available, but it is not required to do so. If they have another dependency that perhaps isn't as lenient with its version offoobar
,npm
will not automatically apply the security patch.Furthermore, this automatic application of the latest version does not update
package.json
. It would be perfectly legal for NPM to install1.0.1
again, there's absolutely no guarantee that that would happen.The amount of edge cases and scenarios involved with hoping
npm
pulls the right version out of a set of versions that do contain significant security vulnerabilities is not worth it. You should explicitly state that you want the security patch and explicitly have it in yourpackage.json
that you do not ever want versionv1.0.1
.A tool like
npm audit
is already invoked on almost every operation. Even if it is critically flawed, it is still a useful way of alerting users to security issues that affect them, and encourage them to update their version of a package.Conclusion
In my opinion, the default behaviour should always be pinning dependencies to the exact version. Updating packages should be opt-in, not opt-out.
The text was updated successfully, but these errors were encountered: