berry: [Bug?]: `yarn npm audit` fails with 400 error

Self-service

  • I’d be willing to implement a fix

Describe the bug

Running yarn npm audit -AR fails in the Jest repo with a 400 error from the npm registry.

To reproduce

$ git clone git@github.com:facebook/jest.git
$ cd jest
$ yarn
$ yarn npm audit -AR

Environment

System:
    OS: macOS 12.1
    CPU: (12) x64 Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz
  Binaries:
    Node: 16.14.0 - /private/var/folders/gj/0mygpdfn6598xh34njlyrqzc0000gn/T/xfs-9e7a75e5/node
    Yarn: 3.2.0-rc.15.git.20220211.hash-32c522a7c - /private/var/folders/gj/0mygpdfn6598xh34njlyrqzc0000gn/T/xfs-9e7a75e5/yarn
    npm: 8.4.1 - ~/.nvm/versions/node/v16.14.0/bin/npm
  npmPackages:
    jest: workspace:* => 28.0.0-alpha.0

Additional context

I’ve added a log statement after the network call, and the reported error from npm is

{
  statusCode: 400,
  error: 'Bad Request',
  message: 'Invalid package tree, run  npm install  to rebuild your package-lock.json'
}

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Reactions: 1
  • Comments: 20 (7 by maintainers)

Commits related to this issue

Most upvoted comments

now npm audit use /-/npm/v1/security/advisories/bulk and only fall back to /-/npm/v1/security/audits/quick if it fails. (code)

npm-audit.md

As of version 7, npm uses the much faster Bulk Advisory endpoint to optimize the speed of calculating audit results.

I cloned the npm repository and tested the same scenario, and if npm doesn’t use /-/npm/v1/security/advisories/bulk, they also receive 400 errors

In my opinion, the solution to this issue is:

  1. yarn npm audit lacks maintenance, it still always uses /-/npm/v1/security/audits/quick, but it should have the same behavior as npm.
  2. should there be an error in this case /-/npm/v1/security/audits/quick 🤔? if it can be fixed, then there will be no error here.

I did a bit of digging with an HTTPS proxy.

Yarn v3

Here’s a sample request body for the /quick endpoint that Yarn uses, generated from my above linked repro project.

POST https://registry.yarnpkg.com/-/npm/v1/security/audits/quick
{
  "requires": {
    "@sargunv/testlib-c": "^0.1.0"
  },
  "dependencies": {
    "audit-bug-repro": {
      "version": "0.0.0-use.local",
      "integrity": "0069b1507df7fda1a72cbcea230138bbf288eb1b1217be39c92e38aa99e645d8c2590978a7fb82eaf176ad2861b62ce5e1766063d605534146ccb96874f44f73",
      "requires": {
        "@sargunv/testlib-c": "^0.1.0"
      },
      "dev": false
    },
    "@sargunv/testlib-c": {
      "version": "0.1.0",
      "integrity": "6cb68cb06e6edf019ff5a5cd314ca8a3d00ec0853a3e9f19af0d108c169f1c85b364641654cc452dd53cc50b0745a3fedd78b8899f4cf02a5ea7f97a25e34400",
      "requires": {
        "@sargunv/fake-testlib-a": "@sargunv/testlib-a@^0.1.0"
      },
      "dev": false
    },
    "@sargunv/testlib-a": {
      "version": "0.1.0",
      "integrity": "7732f389a7070c3eae9d0b14ef941a3d7d36568d5b83ea58d823d70a070e80ff8ff6c8354a4391762da3ffc243167e117f2a247ded5cb0d38fca24dfca5bc2f7",
      "requires": {},
      "dev": false
    }
  }
}

My best guess is that the audit endpoint rejects it because the dependency tree doesn’t look complete; there appears to be a dependency @sargunv/fake-testlib-a that’s not present. Looking at this format, it’s unclear how multiple versions of the same package in one tree would be represented, and I can’t find an NPM API spec anywhere.

The error from NPM’s /quick endpoint for the above request is as follows:

{
  "statusCode": 400,
  "error": "Bad Request",
  "message": "Invalid package tree, run  npm install  to rebuild your package-lock.json"
}

Additionally, I can confirm that this issue affects any released version of Yarn Berry since yarn npm audit was added (tested as far back as v2.4.0 and as far forward as v4.0.0-rc.34).

NPM

Now, here’s what the request looks like when using the /bulk endpoint (with the npm cli, on the same project):

POST https://registry.npmjs.org/-/npm/v1/security/advisories/bulk
{
  "@sargunv/testlib-a": [
    "0.1.0"
  ],
  "@sargunv/testlib-c": [
    "0.1.0"
  ]
}

The “fake” dependency name is no longer relevant; the request simply includes a hash of package names, each with a list of versions included in the tree. This request succeeds.

Yarn v1

Finally, this is the request Yarn v1 sends. It uses neither the /quick nor the /bulk endpoint, but a different /audits endpoint.

POST https://registry.yarnpkg.com/-/npm/v1/security/audits
{
  "name": "audit-bug-repro",
  "install": [],
  "remove": [],
  "metadata": {},
  "requires": {
    "@sargunv/testlib-c": "^0.1.0"
  },
  "dependencies": {
    "@sargunv/testlib-c": {
      "version": "0.1.0",
      "integrity": "sha512-yYzZOaFg/OKwogF4IXxrB/fKctJOTE3MhhbyaR7LU+C0uAlXfzvlLP2Ie6WYU1IXEWLFfOXDy8lmEmRyQk1ZsQ==",
      "requires": {
        "@sargunv/fake-testlib-a": "npm:@sargunv/testlib-a@^0.1.0"
      },
      "dependencies": {},
      "dev": false
    },
    "@sargunv/fake-testlib-a": {
      "version": "0.1.0",
      "integrity": "sha512-Fn64vahG+47n9xScHD9vViPF+twOhSg9g7sH9hTsJ4DwRHJZgIgeYf82MRoX6nURgzpksRoE4n3hxcd3o8XFxg==",
      "requires": {},
      "dependencies": {},
      "dev": false
    }
  },
  "dev": false
}

In this case, Yarn v1 forms the request using the “fake” name of the dependency key, not the “real” name of the dependency version/resolution. This request also succeeds. Sure enough, if I manually tweak the original request to do the same, NPM no longer says “bad request”!:

CleanShot 2023-01-04 at 15 39 43@2x

I’ve looked in the payload by logging the request in the source code, but yes a native way would be great @Glandos.

The dependency I have issues with is "@vue/cli-service": "~5.0.1" with yarn@3.1.0 and yarn@4.0.0-rc.26.dev, just like @revyh in this comment

Does anybody have any clue by now?

It should be great if yarn was able to output the payload sent to the registry server. Right now, the only way to inspect it seems to be changing npmRegistryServer to an HTTP one, and listen with tcpdump/wireshark for the incoming payload.

Hm - I can reproduce it on this very repository as well. I’m sure it used to work, so perhaps a backend change broke something 🙁