Skip to content

Commit f36945a

Browse files
authored
Merge branch 'main' into pr_1452
2 parents ea7130f + d2c1e27 commit f36945a

File tree

15 files changed

+188
-50
lines changed

15 files changed

+188
-50
lines changed

.github/actions/spelling/allow.txt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
refactoring
2+
peb
3+
conventionalcommits
4+
nisamson
5+
bhargavh
6+
jerinjtitus
7+
Molkree
8+
Romi
19
abhaykatheria
210
ableabhinav
311
accountsservice
@@ -23,6 +31,7 @@ backported
2331
bcca
2432
bdbd
2533
bdist
34+
bestpractices
2635
bigbird
2736
binutils
2837
bksahu
@@ -52,6 +61,7 @@ codecov
5261
conda
5362
config
5463
copyleft
64+
coreinfrastructure
5565
cpio
5666
cpp
5767
CQA

.github/workflows/spelling.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,5 @@ jobs:
88
steps:
99
- uses: actions/checkout@v2
1010
- uses: check-spelling/[email protected]
11+
with:
12+
post_comment: '0'

.pre-commit-config.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
11
repos:
22
- repo: https://github.com/pycqa/isort
3-
rev: 5.9.3
3+
rev: 5.10.1
44
hooks:
55
- id: isort
66

77
- repo: https://github.com/python/black
8-
rev: 21.9b0
8+
rev: 21.11b1
99
hooks:
1010
- id: black
1111

1212
- repo: https://github.com/asottile/pyupgrade
13-
rev: v2.29.0
13+
rev: v2.29.1
1414
hooks:
1515
- id: pyupgrade
1616
args: ["--py36-plus"]

README.md

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,22 +7,22 @@
77
[![On PyPI](https://img.shields.io/pypi/v/cve-bin-tool)](https://pypi.org/project/cve-bin-tool/)
88
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/python/black)
99
[![Imports: isort](https://img.shields.io/badge/%20imports-isort-%231674b1?style=flat&labelColor=ef8336)](https://pycqa.github.io/isort/)
10+
[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/5380/badge)](https://bestpractices.coreinfrastructure.org/projects/5380)
1011

12+
The CVE Binary Tool is a free, open source tool to help you find known vulnerabilities in software, using data from the [National Vulnerability Database](https://nvd.nist.gov/) (NVD) list of [Common Vulnerabilities and Exposures](https://en.wikipedia.org/wiki/Common_Vulnerabilities_and_Exposures#:~:text=Common%20Vulnerabilities%20and%20Exposures%20(CVE)%20is%20a%20dictionary%20of%20common,publicly%20known%20information%20security%20vulnerabilities.) (CVEs).
1113

12-
The CVE Binary Tool scans for a number of common, vulnerable open source
13-
components such as openssl, libpng, libxml2, and expat to let you know
14-
if a given directory or binary file includes common libraries with
15-
known vulnerabilities., known as CVEs ([Common Vulnerabilities and Exposures](https://en.wikipedia.org/wiki/Common_Vulnerabilities_and_Exposures#:~:text=Common%20Vulnerabilities%20and%20Exposures%20(CVE)%20is%20a%20dictionary%20of%20common,publicly%20known%20information%20security%20vulnerabilities.)).
14+
The tool has two main modes of operation:
15+
16+
1. A binary scanner which helps you determine which packages may have been included as part of a piece of software. There are around 100 checkers which focus on common, vulnerable open source components such as openssl, libpng, libxml2 and expat.
17+
2. Tools for scanning known component lists in various formats, including .csv, Python's requirements.txt, several linux distribution package lists, and several Software Bill of Materials (SBOM) formats.
18+
19+
It is intended to be used as part of your continuous integration system to enable regular vulnerability scanning and give you early warning of known issues in your supply chain.
1620

1721
See our [documentation](https://cve-bin-tool.readthedocs.io/en/latest/) and [quickstart guide](https://cve-bin-tool.readthedocs.io/en/latest/README.html)
22+
1823
Usage:
1924
`cve-bin-tool <directory/file to scan> `
2025

21-
You can also do `python -m cve_bin_tool.cli`
22-
which is useful if you're trying the latest code from
23-
[the cve-bin-tool github](https://github.com/intel/cve-bin-tool).
24-
25-
2626
optional arguments:
2727
-h, --help show this help message and exit
2828
-e, --exclude exclude path while scanning
@@ -90,6 +90,10 @@ which is useful if you're trying the latest code from
9090
CVE Binary Tool autoextracts all compressed files by default now
9191

9292

93+
You can also do `python -m cve_bin_tool.cli`
94+
which is useful if you're trying the latest code from
95+
[the cve-bin-tool github](https://github.com/intel/cve-bin-tool).
96+
9397
Note that if the CVSS and Severity flags are both specified, the CVSS flag takes precedence.
9498

9599
`--input-file` extends the functionality of *csv2cve* for other formats like JSON. It also allows cve-bin-tool to specify triage data so you can group issues which may have been mitigated (through patches, configuration, or other methods not detectable by our version scanning method) or mark false positives. Triage data can be re-used and applied to multiple scans. You can provide either CSV or JSON file as input_file with vendor, product and version fields. You can also add optional fields like remarks, comments, cve_number, severity.

cve_bin_tool/cli.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
"""
1414

1515
import argparse
16+
import importlib.util
1617
import logging
1718
import os
1819
import platform
@@ -364,6 +365,15 @@ def main(argv=None):
364365
version_check = args["disable_version_check"]
365366
db_update = args["update"]
366367

368+
# Check for PDF support
369+
output_format = args["format"]
370+
if output_format == "pdf" and importlib.util.find_spec("reportlab") is None:
371+
LOGGER.info("PDF output not available. Default to console.")
372+
LOGGER.info(
373+
"If you want to produce PDF output, please install reportlab using pip install reportlab"
374+
)
375+
output_format = "console"
376+
367377
merged_reports = None
368378
if args["merge"]:
369379
LOGGER.info(
@@ -573,7 +583,7 @@ def main(argv=None):
573583
)
574584

575585
if not args["quiet"]:
576-
output.output_file(args["format"])
586+
output.output_file(output_format)
577587
if args["backport_fix"] or args["available_fix"]:
578588
distro_info = args["backport_fix"] or args["available_fix"]
579589
is_backport = True if args["backport_fix"] else False

cve_bin_tool/cve_scanner.py

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -75,19 +75,8 @@ def get_cves(self, product_info: ProductInfo, triage_data: TriageData):
7575
vendor = product_info.vendor.replace("*", "")
7676

7777
# Need to manipulate version to ensure canonical form of version
78-
if product_info.product == "openssl":
79-
pv = re.search(r"\d[.\d]*[a-z]?", product_info.version)
80-
parsed_version_between = parse_version(self.openssl_convert(pv.group(0)))
81-
else:
82-
# Ensure canonical form of version numbering
83-
if ":" in product_info.version:
84-
# Handle x:a.b<string> e.g. 2:7.4+23
85-
components = product_info.version.split(":")
86-
pv = re.search(r"\d[.\d]*", components[1])
87-
else:
88-
# Handle a.b.c<string> e.g. 1.20.9rel1
89-
pv = re.search(r"\d[.\d]*", product_info.version)
90-
parsed_version = parse_version(pv.group(0))
78+
79+
parsed_version, parsed_version_between = self.canonical_convert(product_info)
9180

9281
self.cursor.execute(query, [vendor, product_info.product, str(parsed_version)])
9382

@@ -262,6 +251,25 @@ def openssl_convert(self, version: str) -> str:
262251
version = f"{version[:-1]}.{self.ALPHA_TO_NUM[last_char]}"
263252
return version
264253

254+
def canonical_convert(self, product_info: ProductInfo) -> str:
255+
version_between = ""
256+
if product_info.version == "":
257+
return product_info.version, version_between
258+
if product_info.product == "openssl":
259+
pv = re.search(r"\d[.\d]*[a-z]?", product_info.version)
260+
version_between = parse_version(self.openssl_convert(pv.group(0)))
261+
else:
262+
# Ensure canonical form of version numbering
263+
if ":" in product_info.version:
264+
# Handle x:a.b<string> e.g. 2:7.4+23
265+
components = product_info.version.split(":")
266+
pv = re.search(r"\d[.\d]*", components[1])
267+
else:
268+
# Handle a.b.c<string> e.g. 1.20.9rel1
269+
pv = re.search(r"\d[.\d]*", product_info.version)
270+
parsed_version = parse_version(pv.group(0))
271+
return parsed_version, version_between
272+
265273
def affected(self):
266274
"""Returns list of product name and version tuples identified from
267275
scan"""

cve_bin_tool/extractor.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,16 @@ def __init__(self, logger=None, error_mode=ErrorMode.TruncTrace):
5454
".msi",
5555
".egg",
5656
".whl",
57+
".war",
58+
".ear",
5759
},
5860
}
5961

6062
def can_extract(self, filename):
6163
"""Check if the filename is something we know how to extract"""
64+
# Do not try to extract symlinks
65+
if os.path.islink(filename):
66+
return False
6267
for extension in itertools.chain(*self.file_extractors.values()):
6368
if filename.endswith(extension):
6469
return True
@@ -182,9 +187,9 @@ async def extract_file_zip(filename, extraction_path, process_can_fail=True):
182187
is_exe = filename.endswith(".exe")
183188
if await aio_inpath("unzip"):
184189
stdout, stderr, _ = await aio_run_command(
185-
["unzip", "-n", "-d", extraction_path, filename], process_can_fail
190+
["unzip", "-n", "-q", "-d", extraction_path, filename], process_can_fail
186191
)
187-
if stderr or not stdout:
192+
if stderr:
188193
if is_exe:
189194
return 0 # not all .exe files are zipfiles, no need for error
190195
return 1

cve_bin_tool/output_engine/pdfbuilder.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
from datetime import datetime
55

6+
from reportlab import rl_config
67
from reportlab.graphics.shapes import Drawing, Rect, String
78
from reportlab.lib import colors
89
from reportlab.lib.styles import ParagraphStyle as PS
@@ -169,6 +170,9 @@ def __init__(self, includeTOC=True):
169170
self.table_data = []
170171
self.note_data = []
171172
self.table_validation = None
173+
# Set default configuration parameters
174+
rl_config.trustedHosts = ["localhost", "127.0.0.1"]
175+
rl_config.trustedSchemes = ["http", "https"]
172176

173177
def _spacer(self):
174178
self.contents.append(self.spacer)

cve_bin_tool/package_list_parser.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
from cve_bin_tool.util import ProductInfo, Remarks
2222

2323
ROOT_PATH = join(dirname(__file__), "..")
24-
PYPI_CSV = join(ROOT_PATH, "package_list_parser", "pypi_list.csv")
2524

2625
DEB_DISTROS = ["debian", "pop", "ubuntu"]
2726
PACMAN_DISTROS = ["arch", "manjaro"]

cve_bin_tool/sbom_manager/__init__.py

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
# Copyright (C) 2021 Anthony Harrison
22
# SPDX-License-Identifier: GPL-3.0-or-later
33

4-
import sqlite3
54
from collections import defaultdict
65
from logging import Logger
76
from typing import DefaultDict, Dict, List, Optional
@@ -83,21 +82,11 @@ def scan_file(self) -> Dict[ProductInfo, TriageData]:
8382
return self.sbom_data
8483

8584
def get_vendor(self, product: str) -> Optional[str]:
86-
self.cvedb.db_open()
87-
if not self.cvedb.connection:
88-
raise ConnectionError()
89-
self.cursor = self.cvedb.connection.cursor()
90-
get_vendor_request = "SELECT DISTINCT VENDOR FROM cve_range where PRODUCT=?"
91-
self.cursor.execute(get_vendor_request, [product])
92-
try:
93-
# If multiple unique vendors then shouldn't proceed....
94-
vendor = self.cursor.fetchone()[0]
95-
# print(f"{product} is produced by {vendor}")
96-
except (sqlite3.Error, TypeError) as e:
97-
LOGGER.debug(e, exc_info=True)
98-
vendor = None
99-
self.cvedb.db_close()
100-
return vendor
85+
vendor_package_pair = self.cvedb.get_vendor_product_pairs(product)
86+
if vendor_package_pair != []:
87+
vendor = vendor_package_pair[0]["vendor"]
88+
return vendor
89+
return None
10190

10291

10392
if __name__ == "__main__":

cve_bin_tool/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99

1010
from cve_bin_tool.log import LOGGER
1111

12-
VERSION: str = "3.0.dev0"
12+
VERSION: str = "3.0"
1313

1414

1515
def check_latest_version():

dev-requirements.txt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
black==21.9b0
2-
isort==5.9.3
3-
pre-commit==2.15.0
1+
black==21.11b1
2+
isort==5.10.1
3+
pre-commit==2.16.0
44
flake8==4.0.1

doc/RELEASE.md

Lines changed: 50 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,57 @@
11
# CVE Binary Tool Release Notes
22

3+
## CVE Binary Tool 3.0
4+
5+
The CVE Binary Tool 3.0 release includes improved tools for checking known lists of packages including Linux distributions, improved methods of communication with NVD to get vulnerability data, additional checkers, and significant refactoring to streamline the output.
6+
7+
### New feature highlights:
8+
* **SBOM Scanning**: CVE Binary Tool can now take Software Bill of Materials (SBOM) files to help users improve their supply chain security data for all known dependencies. The initial feature can handle some versions of SPDX, CycloneDX and SWID formats. More information on SBOM scanning can be found here: https://github.com/intel/cve-bin-tool/blob/main/doc/how_to_guides/sbom.md
9+
* **Known vulnerability information**: Users scanning some linux distro packages can now get additional information about fixes available for those platforms.
10+
* **Vulnerability Data**: The default method for getting NVD vulnerability lists has been changed. Previously we downloaded full yearly JSON files if anything in the year had changed, the new API allows us to get only the latest changes. Users may see a speedup during the update phase as a result.
11+
* **(Breaking change) Return codes:** The return codes used by CVE Binary Tool have changed.
12+
* A 0 will be returned if no CVEs are found, a 1 will be returned if any CVEs were found (no matter how many), and codes 2+ indicate operational errors. A full list of error codes is available here: https://github.com/intel/cve-bin-tool/blob/main/cve_bin_tool/error_handler.py
13+
* Previously we returned the number of CVEs found, but this could exceed the expected range for return codes and cause unexpected behaviour.
14+
15+
Thanks especially to our 2021 GSoC students, @BreadGenie, @imsahil007 and @peb-peb whose final GSoC contributions are part of this release.
16+
17+
A full list of changes is available in GitHub. https://github.com/intel/cve-bin-tool/releases/tag/v3.0
18+
19+
Commit messages use the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) format.
20+
21+
22+
## CVE Binary Tool 2.2.1
23+
24+
Release date: 04 Aug 2021
25+
26+
The 2.2.1 release relaxes the behaviour when file extraction fails, which was causing problems for some users scanning files with .exe and .apk file extensions using the previous release. In 2.2 all extraction fails caused the tool to halt and throw an exception, in 2.2.1 the tool will log a warning and continue.
27+
28+
## CVE Binary Tool 2.2
29+
30+
Release date: 08 Jul 2021
31+
32+
The 2.2 release contains a number of bugfixes and improvements thanks to the many students who contributed as part of our Google Summer of Code selection process. Congratulations to @BreadGenie, @imsahil007 and @peb-peb who will be continuing to work with us for the next few months!
33+
34+
New feature highlights:
35+
- CVE Binary Tool can now be used to get lists of vulnerabilities affecting a python requirements.txt file, as well as lists of packages installed on .deb or .rpm based systems (Thanks to @BreadGenie)
36+
- Scan reports can now be merged (Thanks to @imsahil007)
37+
- Reports can now be generated in PDF format (Thanks to @anthonyharrison)
38+
- A new helper script is available to help new contributors find appropriate patterns for new checkers (Thanks to @peb-peb)
39+
- Reports can now be generated even if no CVEs are found (Thanks to @BreadGenie)
40+
- We've added rate limiting for our NVD requests (Thanks to @nisamson, @param211, @bhargavh)
41+
42+
There are also a number of new checkers and bug fixes.
43+
44+
Thanks also to @jerinjtitus, @Molkree, @alt-glitch, @CabTheProgrammer, @Romi-776, @chaitanyamogal, @Rahul2044, @utkarsh147-del , @SinghHrmn, @SaurabhK122, @pdxjohnny and @terriko for their contributions to this release.
45+
46+
## CVE Binary Tool 2.1.post1
47+
48+
Release date: 27 Apr 2021
49+
50+
Rate limiting temporary fix in response to NVD API update
51+
352
## CVE Binary Tool 2.1
453

5-
Release Date: 07 Dec 2020
54+
Release date: 07 Dec 2020
655

756
This release fixes an issue with jinja2 autoescape breaking the HTML reports
857
and includes some updates to tests.

test/test_cvescanner.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Copyright (C) 2021 Anthony Harrison
2+
# SPDX-License-Identifier: GPL-3.0-or-later
3+
4+
import pytest
5+
6+
from cve_bin_tool.cve_scanner import CVEScanner
7+
from cve_bin_tool.util import ProductInfo
8+
9+
10+
class TestCVEScanner:
11+
@pytest.mark.parametrize(
12+
"version, expected_result",
13+
(
14+
("1.1.0f", "1.1.0.5"),
15+
("1.1.0", "1.1.0"),
16+
),
17+
)
18+
def test_openssl_convert(self, version: str, expected_result: str):
19+
scanner = CVEScanner()
20+
assert scanner.openssl_convert(version) == expected_result
21+
22+
@pytest.mark.parametrize(
23+
"product, expected_result, between_result",
24+
(
25+
(ProductInfo(vendor="", product="glibc", version="2.11.1"), "2.11.1", ""),
26+
(
27+
ProductInfo(vendor="", product="glibc", version="2.11.1_pre1"),
28+
"2.11.1",
29+
"",
30+
),
31+
(
32+
ProductInfo(vendor="", product="openssl", version="1.1.0h"),
33+
"1.1.0h",
34+
"1.1.0.7",
35+
),
36+
(
37+
ProductInfo(vendor="", product="openssl", version="1.1.0h_kali2"),
38+
"1.1.0h",
39+
"1.1.0.7",
40+
),
41+
(ProductInfo(vendor="", product="openssl", version=""), "", ""),
42+
(ProductInfo(vendor="", product="php", version="2:7.4"), "7.4", ""),
43+
(ProductInfo(vendor="", product="php", version="2:7.4_deb0"), "7.4", ""),
44+
),
45+
)
46+
def test_canonical_convert(
47+
self, product: ProductInfo, expected_result: str, between_result: str
48+
):
49+
scanner = CVEScanner()
50+
res1, res2 = scanner.canonical_convert(product)
51+
assert str(res1) == expected_result
52+
assert str(res2) == between_result

0 commit comments

Comments
 (0)