Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Calculate meta-vulnerabilities from package security advisories

License

NotificationsYou must be signed in to change notification settings

npm/metavuln-calculator

Repository files navigation

Calculate meta-vulnerabilities from package security advisories

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

USAGE

constCalculator=require('@npmcli/metavuln-calculator')// pass in any options for cacache and pacote// see those modules for option descriptionsconstcalculator=newCalculator(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// }constarb=newAborist(options)consttree=awaitarb.loadActual()constadvisories=awaitgetBulkAdvisoryReportSomehow(tree)// then to get a comprehensive set of advisories including metavulns:constset=newSet()for(const[name,advisory]ofObject.entries(advisories)){// make sure we have the advisories loaded with latest version listsset.add(awaitcalculator.calculate(name,{advisory}))}for(constvulnofset){for(constnodeoftree.inventory.query('name',vuln.name)){// not vulnerable, just keep lookingif(!vuln.testVersion(node.version))continuefor(const{from:dep, spec}ofnode.edgesIn){constmetaAdvisory=awaitcalculator.calculate(dep.name,vuln)if(metaAdvisory.testVersion(dep.version,spec)){set.add(metaAdvisory)}}}}

API

Class: Advisory

TheCalculator.calculate method returns a Promise that resolves to aAdvisory object, filled in from the cache and updated if necessary withthe available advisory data.

Do not instantiateAdvisory objects directly. Use thecalculate()method to get one with appropriate data filled in.

Do not mutateAdvisory 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. (SeeCache Keysbelow.)
  • dependency For metavulns, the dependency that causes this package to behave a vulnerability. For advisories, the same asname.
  • type Either'advisory' or'metavuln', depending on the type ofvulnerability 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 thevulnerability that causes this metavuln
  • updated Boolean indicating whether this vulnerability was updated sincebeing read from cache.
  • packument The packument object for the package that this vulnerabilityis about.

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

Check to see if a given version is vulnerable. Returnstrue if theversion is vulnerable, and should be avoided.

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

Cache Keys

The cache keys are calculated by hashing together thesource andnamefields, prefixing with the string'security-advisory:' and the name ofthe 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 offoothat is vulnerable by virtue of dependending exclusively on versions ofbar which are vulnerable by virtue of depending exclusively on versionsofbaz which are vulnerable by virtue of advisory ID123.

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

calculator = new Calculator(options)

Options object is used forcacache andpacote 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 aAdvisoryobject returned by a previous call to thecalculate() 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 avulnerability object described above.

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

Dependent Version Sampling

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

So, we see things like this:

3.0.4 - not vulnerable3.0.3 - vulnerable3.0.2 - vulnerable3.0.1 - vulnerable3.0.0 - vulnerable2.3.107 - not vulnerable2.3.106 - not vulnerable2.3.105 - vulnerable... 523 more vulnerable versions ...2.0.0 - vulnerable1.1.102 - not vulnerable1.1.101 - vulnerable... 387 more vulnerable versions ...0.0.0 - vulnerable

In order to determine which versions of a package are affected by avulnerability in a dependency, this module uses the following algorithm tominimize the number of tests required by performing a binary search on eachversion set, and presuming that versionsbetween vulnerable versionswithin 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.42.3.0 - 2.3.1072.2.0 - 2.2.432.1.0 - 2.1.4322.0.0 - 2.0.1021.1.0 - 1.1.1021.0.0 - 1.0.1570.1.0 - 0.1.1230.0.0 - 0.0.57
  3. Test the highest and lowest in each MAJOR/MINOR set, and mark highestand 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 isvulnerable, and continue to next set. Ie, in the example, throw outthe 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 iteration3.0.2(v) - 3.0.4(s)2.3.0(v) - 2.3.54(v) <-- thrown out on next iteration2.3.54(v) - 2.3.107(s)1.1.0(v) - 1.1.51(v) <-- thrown out on next iteration1.1.51(v) - 1.1.102(s)

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

Testing Version for MetaVuln Status

When the dependency is inbundleDependencies, we treat any dependentversion thatmay be vulnerable as a vulnerability. If the dependency isnot inbundleDependencies, then we treat the dependent module as avulnerability if it canonly resolve to dependency versions that arevulnerable.

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

For example:

Packagefoo depends on packagebar at the following version ranges:

foo version   bar version range1.0.0         ^1.2.31.0.1         ^1.2.41.0.2         ^1.2.51.1.0         ^1.3.11.1.1         ^1.3.21.1.2         ^1.3.32.0.0         ^2.0.02.0.1         ^2.0.12.0.2         ^2.0.2

There is an advisory forbar@1.2.4 - 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 themanifest of the dependency, using the vulnerable version set as theavoidversions. If we end up selecting a version that should be avoided, thenthat means that the package is vulnerable by virtue of its dependency.

About

Calculate meta-vulnerabilities from package security advisories

Topics

Resources

License

Code of conduct

Security policy

Stars

Watchers

Forks

Packages

No packages published

Contributors14


[8]ページ先頭

©2009-2025 Movatter.jp