npm audit fix semver rules incorrect

What I Wanted to Do

I ran npm audit fix, thinking that this would only perform semver-safe upgrades.

What Happened Instead

Packages with 0.x versions were upgraded, even though minor bumps for 0.x versions are considered breaking, I got upgrades like axios@0.9 -> axios@0.19.

Reproduction Steps

npm audit fix

Platform Info

$ npm --versions
{ npm: '6.9.0',
  ares: '1.15.0',
  cldr: '33.1',
  http_parser: '2.8.0',
  icu: '62.1',
  modules: '64',
  napi: '3',
  nghttp2: '1.34.0',
  node: '10.15.3',
  openssl: '1.1.0j',
  tz: '2018e',
  unicode: '11.0',
  uv: '1.23.2',
  v8: '6.8.275.32-node.51',
  zlib: '1.2.11' }

$ node -p process.platform
darwin

How was axios specified in your project’s package.json? β€œ0.x” or β€œ^0.9”?

0.x should allow upgrading to 0.19, but the caret should not.

For reference: https://github.com/npm/node-semver

It was a ^ version, something like ^0.9.0

(sorry for the dual logins; one’s for work, one for play)

I just had it happen again. In package.json there was "axios": "^0.18.0". After an npm audit fix, "axios": "^0.19.0" was installed. Seems dangerous.

That sounds like a big deal. According to the npm audit page the --force argument is only supposed to install versions what aren’t semver-compatible with your package.json.
Can you create a repro or minimal set of steps to reproduce?

It’s really as simple as posted earlier.

For another take on this, I just did it with these steps in an empty folder:

npm init                     # fill in prompts
npm i --save axios@^0.9.0    # Shown as "^0.9.0" dependency in package.json
npm audit fix                # axios@0.19.0 is installed

I’ve tried some mild source diving, and from what I gather this isn’t a problem from the CLI’s logic but more of a problem from the audit report coming from the registry mis-representing a fix as not breaking (is that part closed-source?)

Here’s the output from npm audit, which seems to expect that npm audit fix will install the fix:

                       === npm audit security report ===                        
                                                                                
# Run  npm install axios@0.19.0  to resolve 1 vulnerability
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
β”‚ Moderate      β”‚ Denial of Service                                            β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Package       β”‚ axios                                                        β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Dependency of β”‚ axios                                                        β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ Path          β”‚ axios                                                        β”‚
β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”Όβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
β”‚ More info     β”‚ https://npmjs.com/advisories/880                             β”‚
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”΄β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜


found 1 moderate severity vulnerability in 5 scanned packages
  run `npm audit fix` to fix 1 of them.

You’re right, I can confirm this. npm audit fix does change the package.json and it does install a version not compatible with the original semver specifier.

$ npm -v; node -v
6.9.0
v12.3.1
$ npm init -y
Wrote to /home/mdailey/Downloads/test/package.json:

{
  "name": "test",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}


$ npm i axios@^0.9.0
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN test@1.0.0 No description
npm WARN test@1.0.0 No repository field.

+ axios@0.9.1
added 5 packages from 7 contributors and audited 5 packages in 1.412s
found 1 moderate severity vulnerability
  run `npm audit fix` to fix them, or `npm audit` for details
$ cat package.json 
{
  "name": "test",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "axios": "^0.9.1"
  }
}
$ cat package-lock.json
{
  "name": "test",
  "version": "1.0.0",
  "lockfileVersion": 1,
  "requires": true,
  "dependencies": {
    "axios": {
      "version": "0.9.1",
      "resolved": "https://registry.npmjs.org/axios/-/axios-0.9.1.tgz",
      "integrity": "sha1-lWCLFkR+4psDNYmFTD/H7iwGv24=",
      "requires": {
        "follow-redirects": "0.0.7"
      }
    },
    "debug": {
      "version": "2.6.9",
      "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
      "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
      "requires": {
        "ms": "2.0.0"
      }
    },
    "follow-redirects": {
      "version": "0.0.7",
      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-0.0.7.tgz",
      "integrity": "sha1-NLkLqyqRGqNHVx2pDyK9NuzYqRk=",
      "requires": {
        "debug": "^2.2.0",
        "stream-consume": "^0.1.0"
      }
    },
    "ms": {
      "version": "2.0.0",
      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
      "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
    },
    "stream-consume": {
      "version": "0.1.1",
      "resolved": "https://registry.npmjs.org/stream-consume/-/stream-consume-0.1.1.tgz",
      "integrity": "sha512-tNa3hzgkjEP7XbCkbRXe1jpg+ievoa0O4SCFlMOYEscGSS4JJsckGL8swUyAa/ApGU3Ae4t6Honor4HhL+tRyg=="
    }
  }
}
$ npm audit fix
npm WARN test@1.0.0 No description
npm WARN test@1.0.0 No repository field.

+ axios@0.19.0
added 1 package from 1 contributor, removed 1 package and updated 3 packages in 0.235s
fixed 1 of 1 vulnerability in 5 scanned packages
$ cat package.json 
{
  "name": "test",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "axios": "^0.19.0"
  }
}
$ cat package-lock.json
{
  "name": "test",
  "version": "1.0.0",
  "lockfileVersion": 1,
  "requires": true,
  "dependencies": {
    "axios": {
      "version": "0.19.0",
      "resolved": "https://registry.npmjs.org/axios/-/axios-0.19.0.tgz",
      "integrity": "sha512-1uvKqKQta3KBxIz14F2v06AEHZ/dIoeKfbTRkK1E5oqjDnuEerLmYTgJB5AiQZHJcljpg1TuRzdjDR06qNk0DQ==",
      "requires": {
        "follow-redirects": "1.5.10",
        "is-buffer": "^2.0.2"
      }
    },
    "debug": {
      "version": "3.1.0",
      "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz",
      "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==",
      "requires": {
        "ms": "2.0.0"
      }
    },
    "follow-redirects": {
      "version": "1.5.10",
      "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz",
      "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==",
      "requires": {
        "debug": "=3.1.0"
      }
    },
    "is-buffer": {
      "version": "2.0.3",
      "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz",
      "integrity": "sha512-U15Q7MXTuZlrbymiz95PJpZxu8IlipAp4dtS3wOdgPXx3mqBnslrWU14kxfHB+Py/+2PVKSr37dMAgM2A4uArw=="
    },
    "ms": {
      "version": "2.0.0",
      "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
      "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
    }
  }
}

Any update on this? Just saw the same thing happen where mixpanel@0.8.0 got auto-upgraded to mixpanel@0.10.2. This is still a super dangerous bug.

I don’t work on npm actually, I’m just a random guy on the forum.