GithubHelp home page GithubHelp logo

metavuln-calculator's Introduction

@npmcli/metavuln-calculator

Calculate meta-vulnerabilities from package security advisories

This is a pretty low-level package to abstract out the parts of @npmcli/arborist that calculate metavulnerabilities from security advisories. If you just want to get an audit for a package tree, probably what you want to use is arborist.audit().

USAGE

const Calculator = require('@npmcli/metavuln-calculator')
// pass in any options for cacache and pacote
// see those modules for option descriptions
const calculator = new Calculator(options)

// get an advisory somehow, typically by POSTing a JSON payload like:
// {"pkgname":["1.2.3","4.3.5", ...versions], ...packages}
// to /-/npm/v1/security/advisories/bulk
// to get a payload response like:
// {
//   "semver": [
//     {
//       "id": 31,
//       "url": "https://npmjs.com/advisories/31",
//       "title": "Regular Expression Denial of Service",
//       "severity": "moderate",
//       "vulnerable_versions": "<4.3.2"
//     }
//   ],
//   ...advisories
// }
const arb = new Aborist(options)
const tree = await arb.loadActual()
const advisories = await getBulkAdvisoryReportSomehow(tree)

// then to get a comprehensive set of advisories including metavulns:
const set = new Set()
for (const [name, advisory] of Object.entries(advisories)) {
  // make sure we have the advisories loaded with latest version lists
  set.add(await calculator.calculate(name, {advisory}))
}

for (const vuln of set) {
  for (const node of tree.inventory.query('name', vuln.name)) {
    // not vulnerable, just keep looking
    if (!vuln.testVersion(node.version))
      continue
    for (const { from: dep, spec } of node.edgesIn) {
      const metaAdvisory = await calculator.calculate(dep.name, vuln)
      if (metaAdvisory.testVersion(dep.version, spec)) {
        set.add(metaAdvisory)
      }
    }
  }
}

API

Class: Advisory

The Calculator.calculate method returns a Promise that resolves to a Advisory object, filled in from the cache and updated if necessary with the available advisory data.

Do not instantiate Advisory objects directly. Use the calculate() method to get one with appropriate data filled in.

Do not mutate Advisory objects. Use the supplied methods only.

Fields

  • name The name of the package that this vulnerability is about
  • id The unique cache key for this vuln or metavuln. (See Cache Keys below.)
  • dependency For metavulns, the dependency that causes this package to be have a vulnerability. For advisories, the same as name.
  • type Either 'advisory' or 'metavuln', depending on the type of vulnerability that this object represents.
  • url The url for the advisory (null for metavulns)
  • title The text title of the advisory or metavuln
  • severity The severity level info/low/medium/high/critical
  • range The range that is vulnerable
  • versions The set of available versions of the package
  • vulnerableVersions The set of versions that are vulnerable
  • source The numeric ID of the advisory, or the cache key of the vulnerability that causes this metavuln
  • updated Boolean indicating whether this vulnerability was updated since being read from cache.
  • packument The packument object for the package that this vulnerability is about.

vuln.testVersion(version, [dependencySpecifier]) -> Boolean

Check to see if a given version is vulnerable. Returns true if the version is vulnerable, and should be avoided.

For metavulns, dependencySpecifier indicates the version range of the source of the vulnerability, which the module depends on. If not provided, will attempt to read from the packument. If not provided, and unable to read from the packument, then true is returned, indicating that the (not installable) package version should be avoided.

Cache Keys

The cache keys are calculated by hashing together the source and name fields, prefixing with the string 'security-advisory:' and the name of the dependency that is vulnerable.

So, a third-level metavulnerability might have a key like:

'security-advisory:foo:'+ hash(['foo', hash(['bar', hash(['baz', 123])])])

Thus, the cached entry with this key would reflect the version of foo that is vulnerable by virtue of dependending exclusively on versions of bar which are vulnerable by virtue of depending exclusively on versions of baz which are vulnerable by virtue of advisory ID 123.

Loading advisory data entirely from cache without hitting an npm registry security advisory endpoint is not supported at this time, but technically possible, and likely to come in a future version of this library.

calculator = new Calculator(options)

Options object is used for cacache and pacote calls.

calculator.calculate(name, source)

  • name The name of the package that the advisory is about
  • source Advisory object from the npm security endpoint, or a Advisory object returned by a previous call to the calculate() method. "Advisory" objects need to have:
    • id id of the advisory or Advisory object
    • vulnerable_versions range of versions affected
    • url
    • title
    • severity

Fetches the packument and returns a Promise that resolves to a vulnerability object described above.

Will perform required I/O to fetch package metadata from registry and read from cache. Advisory information written back to cache.

Dependent Version Sampling

Typically, dependency ranges don't change very frequently, and the most recent version published on a given release line is most likely to contain the fix for a given vulnerability.

So, we see things like this:

3.0.4 - not vulnerable
3.0.3 - vulnerable
3.0.2 - vulnerable
3.0.1 - vulnerable
3.0.0 - vulnerable
2.3.107 - not vulnerable
2.3.106 - not vulnerable
2.3.105 - vulnerable
... 523 more vulnerable versions ...
2.0.0 - vulnerable
1.1.102 - not vulnerable
1.1.101 - vulnerable
... 387 more vulnerable versions ...
0.0.0 - vulnerable

In order to determine which versions of a package are affected by a vulnerability in a dependency, this module uses the following algorithm to minimize the number of tests required by performing a binary search on each version set, and presuming that versions between vulnerable versions within a given set are also vulnerable.

  1. Sort list of available versions by SemVer precedence

  2. Group versions into sets based on MAJOR/MINOR versions.

    3.0.0 - 3.0.4
    2.3.0 - 2.3.107
    2.2.0 - 2.2.43
    2.1.0 - 2.1.432
    2.0.0 - 2.0.102
    1.1.0 - 1.1.102
    1.0.0 - 1.0.157
    0.1.0 - 0.1.123
    0.0.0 - 0.0.57
    
  3. Test the highest and lowest in each MAJOR/MINOR set, and mark highest and lowest with known-vulnerable status. ((s) means "safe" and (v) means "vulnerable".)

    3.0.0(v) - 3.0.4(s)
    2.3.0(v) - 2.3.107(s)
    2.2.0(v) - 2.2.43(v)
    2.1.0(v) - 2.1.432(v)
    2.0.0(v) - 2.0.102(v)
    1.1.0(v) - 1.1.102(s)
    1.0.0(v) - 1.0.157(v)
    0.1.0(v) - 0.1.123(v)
    0.0.0(v) - 0.0.57(v)
    
  4. For each set of package versions:

    1. If highest and lowest both vulnerable, assume entire set is vulnerable, and continue to next set. Ie, in the example, throw out the following version sets:

      2.2.0(v) - 2.2.43(v)
      2.1.0(v) - 2.1.432(v)
      2.0.0(v) - 2.0.102(v)
      1.0.0(v) - 1.0.157(v)
      0.1.0(v) - 0.1.123(v)
      0.0.0(v) - 0.0.57(v)
      
    2. Test middle version MID in set, splitting into two sets.

      3.0.0(v) - 3.0.2(v) - 3.0.4(s)
      2.3.0(v) - 2.3.54(v) - 2.3.107(s)
      1.1.0(v) - 1.1.51(v) - 1.1.102(s)
      
    3. If any untested versions in Set(mid..highest) or Set(lowest..mid), add to list of sets to test.

      3.0.0(v) - 3.0.2(v) <-- thrown out on next iteration
      3.0.2(v) - 3.0.4(s)
      2.3.0(v) - 2.3.54(v) <-- thrown out on next iteration
      2.3.54(v) - 2.3.107(s)
      1.1.0(v) - 1.1.51(v) <-- thrown out on next iteration
      1.1.51(v) - 1.1.102(s)
      

When the process finishes, all versions are either confirmed safe, or confirmed/assumed vulnerable, and we avoid checking large sets of versions where vulnerabilities went unfixed.

Testing Version for MetaVuln Status

When the dependency is in bundleDependencies, we treat any dependent version that may be vulnerable as a vulnerability. If the dependency is not in bundleDependencies, then we treat the dependent module as a vulnerability if it can only resolve to dependency versions that are vulnerable.

This relies on the reasonable assumption that the version of a bundled dependency will be within the stated dependency range, and accounts for the fact that we can't know ahead of time which version of a dependency may be bundled. So, we avoid versions that may bundle a vulnerable dependency.

For example:

Package foo depends on package bar at the following version ranges:

foo version   bar version range
1.0.0         ^1.2.3
1.0.1         ^1.2.4
1.0.2         ^1.2.5
1.1.0         ^1.3.1
1.1.1         ^1.3.2
1.1.2         ^1.3.3
2.0.0         ^2.0.0
2.0.1         ^2.0.1
2.0.2         ^2.0.2

There is an advisory for [email protected] - 1.3.2. So:

foo version   vulnerable?
1.0.0         if bundled (can use 1.2.3, which is not vulnerable)
1.0.1         yes (must use ^1.2.4, entirely contained in vuln range)
1.0.2         yes (must use ^1.2.5, entirely contained in vuln range)
1.1.0         if bundled (can use 1.3.3, which is not vulnerable)
1.1.1         if bundled (can use 1.3.3, which is not vulnerable)
1.1.2         no (dep is outside of vuln range)
2.0.0         no (dep is outside of vuln range)
2.0.1         no (dep is outside of vuln range)
2.0.2         no (dep is outside of vuln range)

To test a package version for metaVulnerable status, we attempt to load the manifest of the dependency, using the vulnerable version set as the avoid versions. If we end up selecting a version that should be avoided, then that means that the package is vulnerable by virtue of its dependency.

metavuln-calculator's People

Contributors

dependabot[bot] avatar github-actions[bot] avatar isaacs avatar lukekarrys avatar nlepage avatar nlf avatar vigan-abd avatar wraithgar avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

metavuln-calculator's Issues

[BUG] cannot convert undefined or null to object

Is there an existing issue for this?

  • I have searched the existing issues

Current Behavior

From this morning we have issue with our private repo. The repo been publish before time in npmjs and after that was removed. Now npm registry return json without object 'versions' and advisory.js is crashing.
https://registry.npmjs.org/ambire-common

Today I publish it again to make a tests, and then remove it again. This is the reason way dates are from today.

We've been using it for a few months now and everything was fine until today

I think this row have to be fix.
https://github.com/npm/metavuln-calculator/blob/main/lib/advisory.js#L109

Expected Behavior

if not return versions object from npm registry have to not return error.

Steps To Reproduce

  1. In this environment...
  2. With this config...
  3. Run '...'
  4. See error...

Environment

  • npm: 8.15.0
  • Node: 16.17.0
  • OS: Ununtu 20.04.4
  • platform: Intel

[FEATURE] skip prereleases if possible

Some packages have thousands of publishes, but in all cases observed in practice, almost all of them are prereleases anyway. There's little value in searching through them for metavuln status.

Suggestion: skip over prerelease versions in the version list if any non-prerelease versions exist in the set being analyzed. Assume that prereleases between two vulnerable versions are also vulnerable.

References

  • n/a

[BUG] v1.1.1: TypeError: semver.simplifyRange is not a function

What / Why

Hello npm team,

Maybe there is a bug in metavuln-calculator v1.1.1 according to the logs from npm:

4500 verbose stack TypeError: semver.simplifyRange is not a function
4500 verbose stack     at Advisory.[calculateRange] (/usr/lib/node_modules/npm/node_modules/@npmcli/metavuln-calculator/lib/advisory.js:191:16)
4500 verbose stack     at Advisory.load (/usr/lib/node_modules/npm/node_modules/@npmcli/metavuln-calculator/lib/advisory.js:148:28)
4500 verbose stack     at Calculator.[calculate] (/usr/lib/node_modules/npm/node_modules/@npmcli/metavuln-calculator/lib/index.js:59:14)
4500 verbose stack     at async Promise.all (index 39)
4500 verbose stack     at async Map.[init] (/usr/lib/node_modules/npm/node_modules/@npmcli/arborist/lib/audit-report.js:178:7)
4500 verbose stack     at async Map.run (/usr/lib/node_modules/npm/node_modules/@npmcli/arborist/lib/audit-report.js:106:7)
4501 verbose cwd /home/user/Documents/src/git/github.com/6prod/website
4502 verbose Linux 5.12.1-arch1-1
4503 verbose argv "/usr/bin/node" "/usr/bin/npm" "install"
4504 verbose node v16.1.0
4505 verbose npm  v7.13.0
4506 error semver.simplifyRange is not a function
4507 verbose exit 1

The version of metavuln-calculator on my system is 1.1.1 according to its package.json.

When

Whenever I run npm install

Where

  • npm public registry

How

I am trying to upgrade my nextjs project.

It tried different fix:

  • npm upgrade / npm install
  • rm -rf node_modules && npm install
  • rm -rf ~/.npm && npm cache clean --force && rm -rf node_modules && npm install

Current Behavior

% npm install            
npm ERR! semver.simplifyRange is not a function

npm ERR! A complete log of this run can be found in:
npm ERR!     /home/user/.npm/_logs/2021-05-18T02_16_10_879Z-debug.log

with /home/user/.npm/_logs/2021-05-18T02_16_10_879Z-debug.log ending with:

277 timing metavuln:cache:get:security-advisory:postcss-selector-matches:GBiyG6xpo+KckWadrabw1lxbPmjLCzFcARvKCaNwRXmHkG+66Znn2nDvm+xoQapzPs9gsFjMsuB/lBpZm62tFQ== Completed in 30ms
278 timing metavuln:cache:get:security-advisory:postcss-selector-not:vCEfw5YBLPriFqX9QPA6DTvKqM7npXQDO/F/q4JHefVN58FlKTdc5QN79724IyuPsyByAvDiU2VcVYjqqwHwVw== Completed in 30ms
279 timing metavuln:cache:get:security-advisory:sanitize-html:fMZM9Bt4doYYI0BO0W0NhVaPgoOW5L6QUULL1tS2wgS0QQiQiaxpErLMiaYcFBDnEBrhBvOxQMpRfM7eKskxuQ== Completed in 29ms
280 verbose stack TypeError: semver.simplifyRange is not a function
280 verbose stack     at Advisory.[calculateRange] (/usr/lib/node_modules/npm/node_modules/@npmcli/metavuln-calculator/lib/advisory.js:191:16)
280 verbose stack     at Advisory.load (/usr/lib/node_modules/npm/node_modules/@npmcli/metavuln-calculator/lib/advisory.js:148:28)
280 verbose stack     at Calculator.[calculate] (/usr/lib/node_modules/npm/node_modules/@npmcli/metavuln-calculator/lib/index.js:59:14)
280 verbose stack     at async Promise.all (index 39)
280 verbose stack     at async Map.[init] (/usr/lib/node_modules/npm/node_modules/@npmcli/arborist/lib/audit-report.js:178:7)
280 verbose stack     at async Map.run (/usr/lib/node_modules/npm/node_modules/@npmcli/arborist/lib/audit-report.js:106:7)
281 verbose cwd /home/user/Documents/src/git/github.com/6prod/website
282 verbose Linux 5.12.1-arch1-1
283 verbose argv "/usr/bin/node" "/usr/bin/npm" "install"
284 verbose node v16.1.0
285 verbose npm  v7.13.0
286 error semver.simplifyRange is not a function
287 verbose exit 1

Gist of the full debug file: https://gist.github.com/sbourlon/4ed004b59db36851ed55bbf08deb906c

Steps to Reproduce

Versions used:

  • npm:
% pacman -Qi npm
Name            : npm
Version         : 7.13.0-1
Description     : A package manager for javascript
  • semver:
% pacman -Qi semver
Name            : semver
Version         : 7.3.5-2
Description     : The semantic version parser used by npm
Architecture    : any
URL             : https://github.com/npm/node-semver

Reproduce:

  • npm install

Expected Behavior

npm install sucessfully finish.

Who

@npm

References

[BUG] Cannot convert undefined or null to object in case of installing a dependency from github

Is there an existing issue for this?

  • I have searched the existing issues

Current Behavior

So when running npm install with package https://github.com/bitfinexcom/bfx-svc-test-helper as dependency we're getting this error:

npm WARN deprecated [email protected]: this library is no longer supported
npm WARN deprecated [email protected]: Please upgrade  to version 7 or higher.  Older versions may use Math.random() in certain circumstances, which is known to be problematic.  See https://v8.dev/blog/math-random for details.
npm WARN deprecated [email protected]: request has been deprecated, see https://github.com/request/request/issues/3142
npm ERR! Cannot convert undefined or null to object

npm ERR! A complete log of this run can be found in: /***/.npm/_logs/2023-04-03T12_54_25_036Z-debug-0.log

Expected Behavior

successful npm installation

Steps To Reproduce

try this:

npm i https://github.com/bitfinexcom/bfx-svc-test-helper

Environment

  • npm: 9.6.3
  • Node: v14.18.3
  • OS: Ubuntu 22.04
  • platform: PC

[BUG] `npm i` fails during audit

Is there an existing issue for this?

  • I have searched the existing issues

Current Behavior

When there is a registry with no published versions like the following: https://registry.npmjs.org/exchange-ui, it causes the audit process to fail with a "Cannot convert undefined or null to object" Error.

There is another problem, my repository is marked as "private": true in the package.json, so I don't understand why it is being checked against the global registry? Shouldn't it be unchecked since it's not a repository I want to publish?

Expected Behavior

The install should be able to be done without problems.

Steps To Reproduce

  1. In this environment... Having a repository created with exchange-ui as a package name
  2. With this config... no specific configuration.
  3. Run 'npm ci'
  4. See error... "Cannot convert undefined or null to object"

image

2022-08-01T13_08_54_339Z-debug-0.log

Environment

  • npm: 8.15.1
  • Node: v16.16.0
  • OS: Mac Os (Monterrey)
  • platform: -

Cannot convert undefined or null to object

Is there an existing issue for this?

  • I have searched the existing issues

Current Behavior

When executing npm cli commands, example: npm audit --audit-level=moderate:

npm audit --audit-level=moderate                                       
npm ERR! Cannot convert undefined or null to object

npm ERR! A complete log of this run can be found in:
npm ERR!     /Users/yadirbatista/.npm/_logs/2023-03-15T14_42_49_384Z-debug-0.log

When I dug a little into the logs by running: cat /Users/yadirbatista/.npm/_logs/2023-03-15T14_42_49_384Z-debug-0.log I get this:

249 verbose stack TypeError: Cannot convert undefined or null to object
249 verbose stack     at Function.keys (<anonymous>)
249 verbose stack     at Advisory.load (/Users/yadirbatista/.nvm/versions/node/v18.11.0/lib/node_modules/npm/node_modules/@npmcli/metavuln-calculator/lib/advisory.js:109:33)
249 verbose stack     at [calculate] (/Users/yadirbatista/.nvm/versions/node/v18.11.0/lib/node_modules/npm/node_modules/@npmcli/metavuln-calculator/lib/index.js:60:14)
249 verbose stack     at async Promise.all (index 0)
249 verbose stack     at async [init] (/Users/yadirbatista/.nvm/versions/node/v18.11.0/lib/node_modules/npm/node_modules/@npmcli/arborist/lib/audit-report.js:188:9)
249 verbose stack     at async AuditReport.run (/Users/yadirbatista/.nvm/versions/node/v18.11.0/lib/node_modules/npm/node_modules/@npmcli/arborist/lib/audit-report.js:109:7)
249 verbose stack     at async Arborist.audit (/Users/yadirbatista/.nvm/versions/node/v18.11.0/lib/node_modules/npm/node_modules/@npmcli/arborist/lib/arborist/audit.js:37:24)
249 verbose stack     at async Audit.auditAdvisories (/Users/yadirbatista/.nvm/versions/node/v18.11.0/lib/node_modules/npm/lib/commands/audit.js:390:5)
249 verbose stack     at async Audit.exec (/Users/yadirbatista/.nvm/versions/node/v18.11.0/lib/node_modules/npm/lib/commands/audit.js:374:7)
249 verbose stack     at async module.exports (/Users/yadirbatista/.nvm/versions/node/v18.11.0/lib/node_modules/npm/lib/cli.js:78:5)

I have a solution for the issue, it's a minor fix to cover untested assumptions: for example:

// lib/advisory.js:109: 
const pakuVersions = Object.keys(packument.versions)

assumes that packument.versions always exists and it's an object. Solution:

const pakuVersions = packument.versions ? Object.keys(packument.versions) : []

Another issue in line 245 related with packument.versions:

// lib/advisory.js:245
const mani = this[_packument].versions[version] || {
      dependencies: {
        [this.dependency]: spec,
      },
    }

Solution:

const mani = this[_packument].versions && this[_packument].versions[version] ? this[_packument].versions[version] : {
      dependencies: {
        [this.dependency]: spec,
      },
    }

Expected Behavior

I expect npm to run fine.

Steps To Reproduce

  1. I have only seen this happen in one repository and it's a private source. I'm not sure if you can reproduce it.

Environment

  • npm: All versions
  • Node: 16*, 18*
  • OS: Unix
  • platform: arm64 (Ubuntu, Alpine, Mac OS)

[FEATURE] do metavulnerability calculation in worker thread

Calculating metavulnerabilities is a strictly CPU-bound task requiring no I/O, and it can sometimes be quite a lot to calculate.

While #2 addresses some of the excess/naive approaches to doing this work, there's fundamentally no reason why it can't be parallelized, except for Node's single-threaded JS. Let's throw it in a worker thread, see if that speeds things up!

[BUG] Cannot convert undefined or null to object when running security advisory

What / Why

npm install cordova-plugin-openwith failed with error npm ERR! Cannot convert undefined or null to object

When

  • every time I install

Where

  • npm public registry

How

Current Behavior

  • Cannot install cordova-plugin-openwith

Steps to Reproduce

  • npm install cordova-plugin-openwith

Expected Behavior

  • Install package successfully, without any error

Who

  • n/a

References

I compare registry data between cordova-plugin-openwith and other package I can install without error found cordova-plugin-openwith got different format. It doesn't contain versions field cause advisory try to get keys from undefined.

const pakuVersions = Object.keys(packument.versions)

registry data reference

[BUG] bd.includes is not a function

Is there an existing issue for this?

  • I have searched the existing issues

Current Behavior

We get the the error bd.includes is not a function when run npm audit, in this line:

const bundled = bd && bd.includes(this[_source].name)

There are some codes in github that the bundleDependencies is not an array:
https://github.com/search?q=%22%5C%22bundleDependencies%5C%22%3A+true%22+path%3A**%2Fpackage.json&type=code

As described in npm Docs: https://docs.npmjs.com/cli/v9/configuring-npm/package-json#bundledependencies
Alternatively, "bundleDependencies" can be defined as a boolean value. A value of true will bundle all dependencies, a value of false will bundle none.

Expected Behavior

No errors when running npm audit if bundleDependencies in some dependecies is not an array. We need check bundleDependencies is an array before call includes.

Steps To Reproduce

in

bundleDependencies: ['minimist'],

change from bundleDependencies: ['minimist'], to bundleDependencies: true,
then npm run test

Environment

  • npm: 9.6.3
  • Node: 18.14.2
  • OS: MacOS Ventura 13.5
  • platform: Macbook Pro

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.