summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFacundo Tuesca <facundo.tuesca@trailofbits.com>2022-12-03 04:00:33 +0000
committerDanielle Adams <adamzdanielle@gmail.com>2023-01-04 20:31:50 -0500
commit10a7ff35dbc27d7e4206dd4942f6cf7c3d01b213 (patch)
tree18552d0ca65e83229280d0b203d9117d14f1ae23
parentedcb31d1f559871425ec860551ec5c047e08a995 (diff)
downloadnode-new-10a7ff35dbc27d7e4206dd4942f6cf7c3d01b213.tar.gz
tools: remove dependency vulnerability checker
This change removes the script used to check for new vulnerabilities in Node.js' dependencies, since it has been moved to its own repository. Refs: https://github.com/nodejs/nodejs-dependency-vuln-assessments PR-URL: https://github.com/nodejs/node/pull/45675 Reviewed-By: Rafael Gonzaga <rafael.nunu@hotmail.com> Reviewed-By: Michael Dawson <midawson@redhat.com> Reviewed-By: Rich Trott <rtrott@gmail.com> Reviewed-By: Luigi Pinca <luigipinca@gmail.com> Reviewed-By: Richard Lau <rlau@redhat.com>
-rw-r--r--tools/dep_checker/README.md72
-rw-r--r--tools/dep_checker/dependencies.py99
-rw-r--r--tools/dep_checker/main.py180
-rw-r--r--tools/dep_checker/requirements.txt3
-rw-r--r--tools/dep_checker/versions_parser.py169
5 files changed, 0 insertions, 523 deletions
diff --git a/tools/dep_checker/README.md b/tools/dep_checker/README.md
deleted file mode 100644
index 84e1fab640..0000000000
--- a/tools/dep_checker/README.md
+++ /dev/null
@@ -1,72 +0,0 @@
-# Node.js dependency vulnerability checker
-
-This script queries the [National Vulnerability Database (NVD)](https://nvd.nist.gov/) and
-the [GitHub Advisory Database](https://github.com/advisories) for vulnerabilities found
-in Node's dependencies.
-
-## How to use
-
-### Database authentication
-
-- In order to query the GitHub Advisory Database,
- a [Personal Access Token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/creating-a-personal-access-token)
- has to be created (no permissions need to be given to the token, since it's only used to query the public database).
-- The NVD can be queried without authentication, but it will be rate limited to one query every six seconds. In order to
- remove
- that limitation [request an API key](https://nvd.nist.gov/developers/request-an-api-key) and pass it as a parameter.
-
-### Running the script
-
-Once acquired, the script can be run as follows:
-
-```shell
-cd node/tools/dep_checker/
-pip install -r requirements.txt
-
-# Python >= 3.9 required
-python main.py --gh-token=$PERSONAL_ACCESS_TOKEN --nvd-key=$NVD_API_KEY
-
-# The command can also be run without parameters
-# This will skip querying the GitHub Advisory Database, and query the NVD
-# using the anonymous (rate-limited) API
-python main.py
-```
-
-## Example output
-
-```
-WARNING: New vulnerabilities found
-- npm (version 1.2.1) :
- - GHSA-v3jv-wrf4-5845: https://github.com/advisories/GHSA-v3jv-wrf4-5845
- - GHSA-93f3-23rq-pjfp: https://github.com/advisories/GHSA-93f3-23rq-pjfp
- - GHSA-m6cx-g6qm-p2cx: https://github.com/advisories/GHSA-m6cx-g6qm-p2cx
- - GHSA-4328-8hgf-7wjr: https://github.com/advisories/GHSA-4328-8hgf-7wjr
- - GHSA-x8qc-rrcw-4r46: https://github.com/advisories/GHSA-x8qc-rrcw-4r46
- - GHSA-m5h6-hr3q-22h5: https://github.com/advisories/GHSA-m5h6-hr3q-22h5
-- acorn (version 6.0.0) :
- - GHSA-6chw-6frg-f759: https://github.com/advisories/GHSA-6chw-6frg-f759
-
-For each dependency and vulnerability, check the following:
-- Check the vulnerability's description to see if it applies to the dependency as
-used by Node. If not, the vulnerability ID (either a CVE or a GHSA) can be added to the ignore list in
-dependencies.py. IMPORTANT: Only do this if certain that the vulnerability found is a false positive.
-- Otherwise, the vulnerability found must be remediated by updating the dependency in the Node repo to a
-non-affected version.
-```
-
-## Implementation details
-
-- For each dependency in Node's `deps/` folder, the script parses their version number and queries the databases to find
- vulnerabilities for that specific version.
-- The queries can return false positives (
- see [this](https://github.com/nodejs/security-wg/issues/802#issuecomment-1144207417) comment for an example). These
- can be ignored by adding the vulnerability to the `ignore_list` in `dependencies.py`
-- If no NVD API key is provided, the script will take a while to finish (~2 min) because queries to the NVD
- are [rate-limited](https://nvd.nist.gov/developers/start-here)
-- If any vulnerabilities are found, the script returns 1 and prints out a list with the ID and a link to a description
- of
- the vulnerability. This is the case except when the ID matches one in the ignore-list (inside `dependencies.py`) in
- which case the vulnerability is ignored.
-
-
-
diff --git a/tools/dep_checker/dependencies.py b/tools/dep_checker/dependencies.py
deleted file mode 100644
index b0c6943aa4..0000000000
--- a/tools/dep_checker/dependencies.py
+++ /dev/null
@@ -1,99 +0,0 @@
-"""A list of dependencies, including their CPE, names and keywords for querying different vulnerability databases"""
-
-from typing import Optional
-import versions_parser as vp
-
-
-class CPE:
- def __init__(self, vendor: str, product: str):
- self.vendor = vendor
- self.product = product
-
-
-class Dependency:
- def __init__(
- self,
- version: str,
- cpe: Optional[CPE] = None,
- npm_name: Optional[str] = None,
- keyword: Optional[str] = None,
- ):
- self.version = version
- self.cpe = cpe
- self.npm_name = npm_name
- self.keyword = keyword
-
- def get_cpe(self) -> Optional[str]:
- if self.cpe:
- return f"cpe:2.3:a:{self.cpe.vendor}:{self.cpe.product}:{self.version}:*:*:*:*:*:*:*"
- else:
- return None
-
-
-ignore_list: list[str] = [
- "CVE-2018-25032", # zlib, already fixed in the fork Node uses (Chromium's)
- "CVE-2007-5536", # openssl, old and only in combination with HP-UX
- "CVE-2019-0190", # openssl, can be only triggered in combination with Apache HTTP Server version 2.4.37
-]
-
-dependencies: dict[str, Dependency] = {
- "zlib": Dependency(
- version=vp.get_zlib_version(), cpe=CPE(vendor="zlib", product="zlib")
- ),
- # TODO: Add V8
- # "V8": Dependency("cpe:2.3:a:google:chrome:*:*:*:*:*:*:*:*", "v8"),
- "uvwasi": Dependency(version=vp.get_uvwasi_version(), cpe=None, keyword="uvwasi"),
- "libuv": Dependency(
- version=vp.get_libuv_version(), cpe=CPE(vendor="libuv_project", product="libuv")
- ),
- "undici": Dependency(
- version=vp.get_undici_version(),
- cpe=CPE(vendor="nodejs", product="undici"),
- npm_name="undici",
- ),
- "OpenSSL": Dependency(
- version=vp.get_openssl_version(), cpe=CPE(vendor="openssl", product="openssl")
- ),
- "npm": Dependency(
- version=vp.get_npm_version(),
- cpe=CPE(vendor="npmjs", product="npm"),
- npm_name="npm",
- ),
- "nghttp3": Dependency(
- version=vp.get_nghttp3_version(), cpe=None, keyword="nghttp3"
- ),
- "ngtcp2": Dependency(version=vp.get_ngtcp2_version(), cpe=None, keyword="ngtcp2"),
- "nghttp2": Dependency(
- version=vp.get_nghttp2_version(), cpe=CPE(vendor="nghttp2", product="nghttp2")
- ),
- "llhttp": Dependency(
- version=vp.get_llhttp_version(),
- cpe=CPE(vendor="llhttp", product="llhttp"),
- npm_name="llhttp",
- ),
- "ICU": Dependency(
- version=vp.get_icu_version(),
- cpe=CPE(vendor="icu-project", product="international_components_for_unicode"),
- ),
- "HdrHistogram": Dependency(version="0.11.2", cpe=None, keyword="hdrhistogram"),
- "corepack": Dependency(
- version=vp.get_corepack_version(),
- cpe=None,
- keyword="corepack",
- npm_name="corepack",
- ),
- "CJS Module Lexer": Dependency(
- version=vp.get_cjs_lexer_version(),
- cpe=None,
- keyword="cjs-module-lexer",
- npm_name="cjs-module-lexer",
- ),
- "c-ares": Dependency(
- version=vp.get_c_ares_version(),
- cpe=CPE(vendor="c-ares_project", product="c-ares"),
- ),
- "brotli": Dependency(
- version=vp.get_brotli_version(), cpe=CPE(vendor="google", product="brotli")
- ),
- "acorn": Dependency(version=vp.get_acorn_version(), cpe=None, npm_name="acorn"),
-}
diff --git a/tools/dep_checker/main.py b/tools/dep_checker/main.py
deleted file mode 100644
index 6675f48f57..0000000000
--- a/tools/dep_checker/main.py
+++ /dev/null
@@ -1,180 +0,0 @@
-""" Node.js dependency vulnerability checker
-
-This script queries the National Vulnerability Database (NVD) and the GitHub Advisory Database for vulnerabilities found
-in Node's dependencies.
-
-For each dependency in Node's `deps/` folder, the script parses their version number and queries the databases to find
-vulnerabilities for that specific version.
-
-If any vulnerabilities are found, the script returns 1 and prints out a list with the ID and a link to a description of
-the vulnerability. This is the case except when the ID matches one in the ignore-list (inside `dependencies.py`) in
-which case the vulnerability is ignored.
-"""
-
-from argparse import ArgumentParser
-from collections import defaultdict
-from dependencies import ignore_list, dependencies
-from gql import gql, Client
-from gql.transport.aiohttp import AIOHTTPTransport
-from nvdlib import searchCVE # type: ignore
-from packaging.specifiers import SpecifierSet
-from typing import Optional
-
-
-class Vulnerability:
- def __init__(self, id: str, url: str):
- self.id = id
- self.url = url
-
-
-vulnerability_found_message = """For each dependency and vulnerability, check the following:
-- Check that the dependency's version printed by the script corresponds to the version present in the Node repo.
-If not, update dependencies.py with the actual version number and run the script again.
-- If the version is correct, check the vulnerability's description to see if it applies to the dependency as
-used by Node. If not, the vulnerability ID (either a CVE or a GHSA) can be added to the ignore list in
-dependencies.py. IMPORTANT: Only do this if certain that the vulnerability found is a false positive.
-- Otherwise, the vulnerability found must be remediated by updating the dependency in the Node repo to a
-non-affected version, followed by updating dependencies.py with the new version.
-"""
-
-
-github_vulnerabilities_query = gql(
- """
- query($package_name:String!) {
- securityVulnerabilities(package:$package_name, last:10) {
- nodes {
- vulnerableVersionRange
- advisory {
- ghsaId
- permalink
- withdrawnAt
- }
- }
- }
- }
-"""
-)
-
-
-def query_ghad(gh_token: str) -> dict[str, list[Vulnerability]]:
- """Queries the GitHub Advisory Database for vulnerabilities reported for Node's dependencies.
-
- The database supports querying by package name in the NPM ecosystem, so we only send queries for the dependencies
- that are also NPM packages.
- """
-
- deps_in_npm = {
- name: dep for name, dep in dependencies.items() if dep.npm_name is not None
- }
-
- transport = AIOHTTPTransport(
- url="https://api.github.com/graphql",
- headers={"Authorization": f"bearer {gh_token}"},
- )
- client = Client(
- transport=transport,
- fetch_schema_from_transport=True,
- serialize_variables=True,
- parse_results=True,
- )
-
- found_vulnerabilities: dict[str, list[Vulnerability]] = defaultdict(list)
- for name, dep in deps_in_npm.items():
- variables_package = {
- "package_name": dep.npm_name,
- }
- result = client.execute(
- github_vulnerabilities_query, variable_values=variables_package
- )
- matching_vulns = [
- v
- for v in result["securityVulnerabilities"]["nodes"]
- if v["advisory"]["withdrawnAt"] is None
- and dep.version in SpecifierSet(v["vulnerableVersionRange"])
- and v["advisory"]["ghsaId"] not in ignore_list
- ]
- if matching_vulns:
- found_vulnerabilities[name].extend(
- [
- Vulnerability(
- id=vuln["advisory"]["ghsaId"], url=vuln["advisory"]["permalink"]
- )
- for vuln in matching_vulns
- ]
- )
-
- return found_vulnerabilities
-
-
-def query_nvd(api_key: Optional[str]) -> dict[str, list[Vulnerability]]:
- """Queries the National Vulnerability Database for vulnerabilities reported for Node's dependencies.
-
- The database supports querying by CPE (Common Platform Enumeration) or by a keyword present in the CVE's
- description.
- Since some of Node's dependencies don't have an associated CPE, we use their name as a keyword in the query.
- """
- deps_in_nvd = {
- name: dep
- for name, dep in dependencies.items()
- if dep.cpe is not None or dep.keyword is not None
- }
- found_vulnerabilities: dict[str, list[Vulnerability]] = defaultdict(list)
- for name, dep in deps_in_nvd.items():
- query_results = [
- cve
- for cve in searchCVE(
- cpeMatchString=dep.get_cpe(), keyword=dep.keyword, key=api_key
- )
- if cve.id not in ignore_list
- ]
- if query_results:
- found_vulnerabilities[name].extend(
- [Vulnerability(id=cve.id, url=cve.url) for cve in query_results]
- )
-
- return found_vulnerabilities
-
-
-def main():
- parser = ArgumentParser(
- description="Query the NVD and the GitHub Advisory Database for new vulnerabilities in Node's dependencies"
- )
- parser.add_argument(
- "--gh-token",
- help="the GitHub authentication token for querying the GH Advisory Database",
- )
- parser.add_argument(
- "--nvd-key",
- help="the NVD API key for querying the National Vulnerability Database",
- )
- gh_token = parser.parse_args().gh_token
- nvd_key = parser.parse_args().nvd_key
- if gh_token is None:
- print(
- "Warning: GitHub authentication token not provided, skipping GitHub Advisory Database queries"
- )
- if nvd_key is None:
- print(
- "Warning: NVD API key not provided, queries will be slower due to rate limiting"
- )
- ghad_vulnerabilities: dict[str, list[Vulnerability]] = (
- {} if gh_token is None else query_ghad(gh_token)
- )
- nvd_vulnerabilities: dict[str, list[Vulnerability]] = query_nvd(nvd_key)
-
- if not ghad_vulnerabilities and not nvd_vulnerabilities:
- print(f"No new vulnerabilities found ({len(ignore_list)} ignored)")
- return 0
- else:
- print("WARNING: New vulnerabilities found")
- for source in (ghad_vulnerabilities, nvd_vulnerabilities):
- for name, vulns in source.items():
- print(f"- {name} (version {dependencies[name].version}) :")
- for v in vulns:
- print(f"\t- {v.id}: {v.url}")
- print(f"\n{vulnerability_found_message}")
- return 1
-
-
-if __name__ == "__main__":
- exit(main())
diff --git a/tools/dep_checker/requirements.txt b/tools/dep_checker/requirements.txt
deleted file mode 100644
index 3a41c5824b..0000000000
--- a/tools/dep_checker/requirements.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-gql[aiohttp]
-nvdlib==0.5.8
-packaging
diff --git a/tools/dep_checker/versions_parser.py b/tools/dep_checker/versions_parser.py
deleted file mode 100644
index 3a385bf1d2..0000000000
--- a/tools/dep_checker/versions_parser.py
+++ /dev/null
@@ -1,169 +0,0 @@
-"""Utility functions to parse version numbers from each of Node's dependencies"""
-
-from pathlib import Path
-import re
-
-
-def get_package_json_version(path: Path) -> str:
- with open(path, "r") as f:
- matches = re.search('"version": "(?P<version>.*)"', f.read())
- if matches is None:
- raise RuntimeError(f"Error extracting version number from {path}")
- return matches.groupdict()["version"]
-
-
-def get_acorn_version() -> str:
- return get_package_json_version(Path("../../deps/acorn/acorn/package.json"))
-
-
-def get_brotli_version() -> str:
- with open("../../deps/brotli/c/common/version.h", "r") as f:
- matches = re.search("#define BROTLI_VERSION (?P<version>.*)", f.read())
- if matches is None:
- raise RuntimeError("Error extracting version number for brotli")
- hex_version = matches.groupdict()["version"]
- major_version = int(hex_version, 16) >> 24
- minor_version = int(hex_version, 16) >> 12 & 0xFF
- patch_version = int(hex_version, 16) & 0xFFFFF
- return f"{major_version}.{minor_version}.{patch_version}"
-
-
-def get_c_ares_version() -> str:
- with open("../../deps/cares/include/ares_version.h", "r") as f:
- matches = re.search('#define ARES_VERSION_STR "(?P<version>.*)"', f.read())
- if matches is None:
- raise RuntimeError("Error extracting version number for c-ares")
- return matches.groupdict()["version"]
-
-
-def get_cjs_lexer_version() -> str:
- return get_package_json_version(Path("../../deps/cjs-module-lexer/package.json"))
-
-
-def get_corepack_version() -> str:
- return get_package_json_version(Path("../../deps/corepack/package.json"))
-
-
-def get_icu_version() -> str:
- with open("../../deps/icu-small/source/common/unicode/uvernum.h", "r") as f:
- matches = re.search('#define U_ICU_VERSION "(?P<version>.*)"', f.read())
- if matches is None:
- raise RuntimeError("Error extracting version number for ICU")
- return matches.groupdict()["version"]
-
-
-def get_llhttp_version() -> str:
- with open("../../deps/llhttp/include/llhttp.h", "r") as f:
- matches = re.search(
- "#define LLHTTP_VERSION_MAJOR (?P<major>.*)\n"
- "#define LLHTTP_VERSION_MINOR (?P<minor>.*)\n"
- "#define LLHTTP_VERSION_PATCH (?P<patch>.*)",
- f.read(),
- re.MULTILINE,
- )
- if matches is None:
- raise RuntimeError("Error extracting version number for llhttp")
- versions = matches.groupdict()
- return f"{versions['major']}.{versions['minor']}.{versions['patch']}"
-
-
-def get_nghttp2_version() -> str:
- with open("../../deps/nghttp2/lib/includes/nghttp2/nghttp2ver.h", "r") as f:
- matches = re.search('#define NGHTTP2_VERSION "(?P<version>.*)"', f.read())
- if matches is None:
- raise RuntimeError("Error extracting version number for nghttp2")
- return matches.groupdict()["version"]
-
-
-def get_ngtcp2_version() -> str:
- with open("../../deps/ngtcp2/ngtcp2/lib/includes/ngtcp2/version.h", "r") as f:
- matches = re.search('#define NGTCP2_VERSION "(?P<version>.*)"', f.read())
- if matches is None:
- raise RuntimeError("Error extracting version number for ngtcp2")
- return matches.groupdict()["version"]
-
-
-def get_nghttp3_version() -> str:
- with open("../../deps/ngtcp2/nghttp3/lib/includes/nghttp3/version.h", "r") as f:
- matches = re.search('#define NGHTTP3_VERSION "(?P<version>.*)"', f.read())
- if matches is None:
- raise RuntimeError("Error extracting version number for nghttp3")
- return matches.groupdict()["version"]
-
-
-def get_npm_version() -> str:
- return get_package_json_version(Path("../../deps/npm/package.json"))
-
-
-def get_openssl_version() -> str:
- with open("../../deps/openssl/openssl/VERSION.dat", "r") as f:
- matches = re.search(
- "MAJOR=(?P<major>.*)\n" "MINOR=(?P<minor>.*)\n" "PATCH=(?P<patch>.*)",
- f.read(),
- re.MULTILINE,
- )
- if matches is None:
- raise RuntimeError("Error extracting version number for openssl")
- versions = matches.groupdict()
- return f"{versions['major']}.{versions['minor']}.{versions['patch']}"
-
-
-def get_undici_version() -> str:
- return get_package_json_version(Path("../../deps/undici/src/package.json"))
-
-
-def get_libuv_version() -> str:
- with open("../../deps/uv/include/uv/version.h", "r") as f:
- matches = re.search(
- "#define UV_VERSION_MAJOR (?P<major>.*)\n"
- "#define UV_VERSION_MINOR (?P<minor>.*)\n"
- "#define UV_VERSION_PATCH (?P<patch>.*)",
- f.read(),
- re.MULTILINE,
- )
- if matches is None:
- raise RuntimeError("Error extracting version number for libuv")
- versions = matches.groupdict()
- return f"{versions['major']}.{versions['minor']}.{versions['patch']}"
-
-
-def get_uvwasi_version() -> str:
- with open("../../deps/uvwasi/include/uvwasi.h", "r") as f:
- matches = re.search(
- "#define UVWASI_VERSION_MAJOR (?P<major>.*)\n"
- "#define UVWASI_VERSION_MINOR (?P<minor>.*)\n"
- "#define UVWASI_VERSION_PATCH (?P<patch>.*)",
- f.read(),
- re.MULTILINE,
- )
- if matches is None:
- raise RuntimeError("Error extracting version number for uvwasi")
- versions = matches.groupdict()
- return f"{versions['major']}.{versions['minor']}.{versions['patch']}"
-
-
-def get_v8_version() -> str:
- with open("../../deps/v8/include/v8-version.h", "r") as f:
- matches = re.search(
- "#define V8_MAJOR_VERSION (?P<major>.*)\n"
- "#define V8_MINOR_VERSION (?P<minor>.*)\n"
- "#define V8_BUILD_NUMBER (?P<build>.*)\n"
- "#define V8_PATCH_LEVEL (?P<patch>.*)\n",
- f.read(),
- re.MULTILINE,
- )
- if matches is None:
- raise RuntimeError("Error extracting version number for v8")
- versions = matches.groupdict()
- patch_suffix = "" if versions["patch"] == "0" else f".{versions['patch']}"
- return (
- f"{versions['major']}.{versions['minor']}.{versions['build']}{patch_suffix}"
- )
-
-
-def get_zlib_version() -> str:
- with open("../../deps/zlib/zlib.h", "r") as f:
- matches = re.search('#define ZLIB_VERSION "(?P<version>.*)"', f.read())
- if matches is None:
- raise RuntimeError("Error extracting version number for zlib")
- return matches.groupdict()["version"]