diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index b0884bdfc8..b10af6df63 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -26,7 +26,7 @@ repos: hooks: - id: pyupgrade exclude: ^fuzz/generated/ - args: ["--py38-plus"] + args: ["--py39-plus"] - repo: https://github.com/pycqa/flake8 rev: 7.2.0 diff --git a/cve_bin_tool/cli.py b/cve_bin_tool/cli.py index f1bfd7342e..a15c66212b 100644 --- a/cve_bin_tool/cli.py +++ b/cve_bin_tool/cli.py @@ -96,7 +96,7 @@ def main(argv=None): """Scan a binary file for certain open source libraries that may have CVEs""" if sys.version_info < (3, 8): raise OSError( - "Python no longer provides security updates for version 3.7 as of June 2023. Please upgrade to python 3.8+ to use CVE Binary Tool." + "Python no longer provides security updates for version 3.8 as of October 2024. Please upgrade to Python 3.9+ to use CVE Binary Tool." ) argv = argv or sys.argv diff --git a/cve_bin_tool/csv2cve.py b/cve_bin_tool/csv2cve.py index dee8ef1a90..7c591fa080 100644 --- a/cve_bin_tool/csv2cve.py +++ b/cve_bin_tool/csv2cve.py @@ -18,7 +18,7 @@ def main(argv: list[str] | None = None): """Used to scan a .csv file that lists the dependencies.""" if sys.version_info < (3, 8): raise OSError( - "Python no longer provides security updates for version 3.7 as of June 2023. Please upgrade to python 3.8+ to use CVE Binary Tool." + "Python no longer provides security updates for version 3.8 as of October 2024. Please upgrade to Python 3.9+ to use CVE Binary Tool." ) logger: logging.Logger = LOGGER.getChild("CSV2CVE") argv = argv or sys.argv diff --git a/cve_bin_tool/cve_scanner.py b/cve_bin_tool/cve_scanner.py index 0ea093a3cb..563d9a6c60 100644 --- a/cve_bin_tool/cve_scanner.py +++ b/cve_bin_tool/cve_scanner.py @@ -7,7 +7,6 @@ from logging import Logger from pathlib import Path from string import ascii_lowercase -from typing import DefaultDict, Dict, List from rich.console import Console @@ -19,6 +18,8 @@ from cve_bin_tool.util import CVE, CVEData, ProductInfo, Remarks, VersionInfo from cve_bin_tool.version_compare import Version +# from typing import Dict, List + class CVEScanner: """ @@ -27,13 +28,13 @@ class CVEScanner: products_with_cve: int products_without_cve: int - all_cve_data: DefaultDict[ProductInfo, CVEData] - all_cve_version_info: Dict[str, VersionInfo] + all_cve_data: defaultdict[ProductInfo, CVEData] + all_cve_version_info: dict[str, VersionInfo] RANGE_UNSET: str = "" dbname: str = str(Path(DISK_LOCATION_DEFAULT) / DBNAME) CONSOLE: Console = Console(file=sys.stderr, theme=cve_theme) - ALPHA_TO_NUM: Dict[str, int] = dict(zip(ascii_lowercase, range(26))) + ALPHA_TO_NUM: dict[str, int] = dict(zip(ascii_lowercase, range(26))) def __init__( self, @@ -44,8 +45,8 @@ def __init__( logger: Logger = None, error_mode: ErrorMode = ErrorMode.TruncTrace, check_exploits: bool = False, - exploits_list: List[str] = [], - disabled_sources: List[str] = [], + exploits_list: list[str] = [], + disabled_sources: list[str] = [], ): self.logger = logger or LOGGER.getChild(self.__class__.__name__) self.error_mode = error_mode @@ -211,10 +212,10 @@ def get_cves(self, product_info: ProductInfo, triage_data: TriageData): ) product_info_data: CVEData | None = self.all_cve_data.get(product_info) - prev_cves: List[CVE] = ( + prev_cves: list[CVE] = ( product_info_data.get("cves", []) if product_info_data is not None else [] # type: ignore ) - cves: List[CVE] = [] + cves: list[CVE] = [] # Go through and get all the severities if cve_list: @@ -385,7 +386,7 @@ def filter_triage_data(self): Filter out triage data that is not relevant to the CVEs found, specifically those marked as NotAffected or FalsePositives. """ - to_delete: List[ProductInfo] = [] + to_delete: list[ProductInfo] = [] for product_info, cve_data in self.all_cve_data.items(): original_cves = cve_data["cves"] diff --git a/cve_bin_tool/input_engine.py b/cve_bin_tool/input_engine.py index 3a90f959b4..9e9ab70e0d 100644 --- a/cve_bin_tool/input_engine.py +++ b/cve_bin_tool/input_engine.py @@ -11,9 +11,10 @@ import csv import json from collections import defaultdict +from collections.abc import Iterable from logging import Logger from pathlib import Path -from typing import Any, DefaultDict, Dict, Iterable, Set, Union +from typing import Any, Union from cve_bin_tool.cvedb import CVEDB from cve_bin_tool.error_handler import ( @@ -27,7 +28,7 @@ from cve_bin_tool.util import ProductInfo, Remarks # TriageData is dictionary of cve_number mapped to dictionary of remarks, comments and custom severity -TriageData = Dict[str, Union[Dict[str, Any], Set[str]]] +TriageData = dict[str, Union[dict[str, Any], set[str]]] class InputEngine: @@ -37,13 +38,13 @@ class InputEngine: This class is responsible for parsing various input file formats (CSV, JSON) in the CVE Bin Tool. Attributes: - - parsed_data (DefaultDict[ProductInfo, TriageData]): Dictionary containing parsed input data. + - parsed_data (defaultdict[ProductInfo, TriageData]): Dictionary containing parsed input data. Methods: - __init__(self, filename: str, logger: Logger = None, error_mode=ErrorMode.TruncTrace, filetype="autodetect"): Initializes the InputEngine with the specified filename, logger, error mode, and filetype. - - parse_input(self) -> DefaultDict[ProductInfo, TriageData]: + - parse_input(self) -> defaultdict[ProductInfo, TriageData]: Parses the input file based on its type (CSV, JSON) and returns the parsed data. - input_csv(self) -> None: @@ -58,7 +59,7 @@ class InputEngine: """ # parsed_data is a dictionary of vendor, product, version mapped to TriageData - parsed_data: DefaultDict[ProductInfo, TriageData] + parsed_data: defaultdict[ProductInfo, TriageData] def __init__( self, @@ -85,12 +86,12 @@ def __init__( # Connect to the database self.cvedb = CVEDB(version_check=False) - def parse_input(self) -> DefaultDict[ProductInfo, TriageData]: + def parse_input(self) -> defaultdict[ProductInfo, TriageData]: """ Parses the input file and returns the parsed data. Returns: - - DefaultDict[ProductInfo, TriageData]: Parsed input data. + - defaultdict[ProductInfo, TriageData]: Parsed input data. """ @@ -135,7 +136,7 @@ def input_json(self) -> None: self.parse_data(set(json_data[0].keys()), json_data) - def parse_data(self, fields: Set[str], data: Iterable) -> None: + def parse_data(self, fields: set[str], data: Iterable) -> None: """ Parses common data structure for CSV and JSON input formats. diff --git a/cve_bin_tool/output_engine/util.py b/cve_bin_tool/output_engine/util.py index 3ded2d86fc..82858c33e1 100644 --- a/cve_bin_tool/output_engine/util.py +++ b/cve_bin_tool/output_engine/util.py @@ -317,7 +317,7 @@ def group_cve_by_remark( { "cve_number": "CVE-XXX-XXX", "severity": "High", - "decription: "Lorem Ipsm", + "description: "Lorem Ipsm", }, {...} ], @@ -329,7 +329,7 @@ def group_cve_by_remark( cve_by_product (List[CVE]): List of CVE(s) that needs to be grouped Returns: - DefaultDict[Remarks, List[Dict[str, str]]]: CVEs grouped by remark stored in default dict + defaultdict[Remarks, list[dict[str, str]]]: CVEs grouped by remark stored in default dict """ cve_by_remarks: defaultdict[Remarks, list[dict[str, str]]] = defaultdict(list) for cve in cve_by_product: diff --git a/cve_bin_tool/sbom_manager/parse.py b/cve_bin_tool/sbom_manager/parse.py index c498b7925a..b2533254ef 100644 --- a/cve_bin_tool/sbom_manager/parse.py +++ b/cve_bin_tool/sbom_manager/parse.py @@ -35,7 +35,7 @@ class SBOMParse: It provides methods for scanning SBOM files, parsing them, and retrieving vendor information. Attributes: - - sbom_data (DefaultDict[ProductInfo, TriageData]): Dictionary containing parsed SBOM data. + - sbom_data (defaultdict[ProductInfo, TriageData]): Dictionary containing parsed SBOM data. """ @@ -147,7 +147,7 @@ def common_prefix_split(self, product, version) -> list[ProductInfo]: if not found_common_prefix: # if vendor not found after removing common prefix try splitting it LOGGER.debug( - f"No Vendor found for {product}, trying splitted product. " + f"No Vendor found for {product}, trying split product. " "Some results may be inaccurate due to vendor identification limitations." ) splitted_product = product.split("-") diff --git a/cve_bin_tool/strings.py b/cve_bin_tool/strings.py index 7ae84b8d36..61e0987f3d 100644 --- a/cve_bin_tool/strings.py +++ b/cve_bin_tool/strings.py @@ -8,7 +8,7 @@ """ import subprocess -from typing import ClassVar, List, Set +from typing import ClassVar from cve_bin_tool.async_utils import FileIO, run_coroutine from cve_bin_tool.util import inpath @@ -18,7 +18,7 @@ class Strings: """Utility class for parsing files and extracting printable characters.""" # printable characters - PRINTABLE: ClassVar[Set[int]] = set(range(32, 128)) + PRINTABLE: ClassVar[set[int]] = set(range(32, 128)) # add tab to the printable character PRINTABLE.add(9) @@ -35,7 +35,7 @@ async def aio_parse(self) -> str: str: The acuumulated printable characters from the file. """ async with FileIO(self.filename, "rb") as f: - tmp: List[str] = [] + tmp: list[str] = [] async for line in f: for char in line: # remove all unprintable characters diff --git a/cve_bin_tool/util.py b/cve_bin_tool/util.py index 11ee0533f4..db859d7da8 100644 --- a/cve_bin_tool/util.py +++ b/cve_bin_tool/util.py @@ -9,9 +9,12 @@ import os import re import sys +from collections import defaultdict +from collections.abc import Iterator from enum import Enum from pathlib import Path -from typing import DefaultDict, Iterator, List, NamedTuple, Pattern, Set, Union +from re import Pattern +from typing import NamedTuple, Union import requests from packageurl import PackageURL @@ -248,7 +251,7 @@ class VersionInfo(NamedTuple): end_excluding: str -class CVEData(DefaultDict[str, Union[List[CVE], Set[str]]]): +class CVEData(defaultdict[str, Union[list[CVE], set[str]]]): """ A Class representing a dictionary of CVEs and paths """ diff --git a/cve_bin_tool/vex_manager/parse.py b/cve_bin_tool/vex_manager/parse.py index b58d1fe1e0..1c25050c25 100644 --- a/cve_bin_tool/vex_manager/parse.py +++ b/cve_bin_tool/vex_manager/parse.py @@ -1,14 +1,15 @@ # Copyright (C) 2024 Intel Corporation # SPDX-License-Identifier: GPL-3.0-or-later -from typing import Any, DefaultDict, Dict, Set, Union +from collections import defaultdict +from typing import Any, Union from lib4vex.parser import VEXParser from cve_bin_tool.log import LOGGER from cve_bin_tool.util import ProductInfo, Remarks, decode_bom_ref, decode_purl -TriageData = Dict[str, Union[Dict[str, Any], Set[str]]] +TriageData = dict[str, Union[dict[str, Any], set[str]]] class VEXParse: @@ -24,7 +25,7 @@ class VEXParse: Methods: - __init__(self, filename: str, vextype: str, logger=None): Initializes the VEXParse object. - - parse_vex(self) -> DefaultDict[ProductInfo, TriageData]: Parses the VEX file and extracts the necessary fields from the vulnerabilities. + - parse_vex(self) -> defaultdict[ProductInfo, TriageData]: Parses the VEX file and extracts the necessary fields from the vulnerabilities. - process_metadata(self) -> None: Processes the metadata. - process_product(self) -> None: Processes the product information. - process_vulnerabilities(self, vulnerabilities) -> None: Processes the vulnerabilities and extracts the necessary fields. @@ -63,7 +64,7 @@ def __init__(self, filename: str, vextype: str, logger=None): self.parsed_data = {} self.serialNumbers = set() - def parse_vex(self) -> DefaultDict[ProductInfo, TriageData]: + def parse_vex(self) -> defaultdict[ProductInfo, TriageData]: """Parses the VEX file and extracts the necessary fields from the vulnerabilities.""" vexparse = VEXParser(vex_type=self.vextype) vexparse.parse(self.filename) diff --git a/dev-requirements.txt b/dev-requirements.txt index 526121ced7..76500bff8b 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -26,4 +26,4 @@ types-jsonschema types-PyYAML types-requests types-setuptools -types-toml +types-toml \ No newline at end of file diff --git a/doc/MANUAL.md b/doc/MANUAL.md index 568c17b66a..daa301ae97 100644 --- a/doc/MANUAL.md +++ b/doc/MANUAL.md @@ -369,7 +369,7 @@ On Windows, it requires - `Expand` Windows has `ar` and `Expand` installed in default, but `7z` in particular might need to be installed. -If you wan to run our test-suite or scan a zstd compressed file, We recommend installing this [7-zip-zstd](https://github.com/mcmilk/7-Zip-zstd) +If you want to run our test-suite or scan a zstd compressed file, We recommend installing this [7-zip-zstd](https://github.com/mcmilk/7-Zip-zstd) fork of 7zip. We are currently using `7z` for extracting `jar`, `apk`, `msi`, `exe` and `rpm` files. > **Note**: The CVE Binary Tool cannot scan password-protected ZIP files. If you attempt to scan such a file, an error message will be logged regarding the failure in extraction.. @@ -537,7 +537,7 @@ This data source provides the CVEs for the CURL product. ## Limitations The last release of this tool to support python 2.7 is 0.3.1. Please use -python 3.8+ for development and future versions. Linux and Windows are +python 3.10+ for development and future versions. Linux and Windows are supported, as is usage within cygwin on windows. This tool does not scan for all possible known public vulnerabilities, it only @@ -1165,7 +1165,7 @@ haxx,curl,7.34.0,CVE-2014-0139,MEDIUM,Unexplored, haxx , curl , 7.34.0 , CVE-2014-0015, MEDIUM " style="width:100%;white-space:pre;"> -
formated console output
+
formatted console output
4. `--format html` - creates a report in html format according to the specified HTML theme. @@ -1197,7 +1197,7 @@ haxx,curl,7.34.0,CVE-2014-0139,MEDIUM,Unexplored, libjpeg-turbo, 2.0.1, 2 ssh-ssh2, 2.0, 1" style="width:100%;white-space:pre;"> -
formated HTML report
+
formatted HTML report
The unexplored and new CVEs will be highlighted, it will look something like this: @@ -1214,7 +1214,7 @@ The unexplored and new CVEs will be highlighted, it will look something like thi sun, sunos, 5.4, 127, ssh, ssh2, 2.0, 4" style="width:100%;white-space:pre;"> -
formated HTML report
+
formatted HTML report
You can also filter scanned products by remark: @@ -1230,7 +1230,7 @@ You can also filter scanned products by remark: mit, kerberos, 1.15.1, 3, " style="width:100%;white-space:pre;"> -
formated HTML report
+
formatted HTML report
5. `--format pdf` - creates a report in PDF format. diff --git a/setup.py b/setup.py index 54c18248d5..ad7bae29f2 100644 --- a/setup.py +++ b/setup.py @@ -35,7 +35,7 @@ url="https://github.com/intel/cve-bin-tool", license="GPL-3.0-or-later", keywords=["security", "tools", "CVE"], - python_requires=">=3.8", + python_requires=">=3.10", classifiers=[ "Development Status :: 5 - Production/Stable", "Intended Audience :: Developers", diff --git a/test/README.md b/test/README.md index d04a607a4a..7c8b014ae5 100644 --- a/test/README.md +++ b/test/README.md @@ -74,14 +74,14 @@ The recommended way to do this yourself is to use python's `virtualenv` You can set up virtualenv for all these environments: ```console -virtualenv -p python3.8 venv3.8 -virtualenv -p python3.9 venv3.9 +virtualenv -p python3.11 venv3.11 +virtualenv -p python3.12 venv3.12 ``` -To activate one of these (the example uses 3.8), run the tests, and deactivate: +To activate one of these (the example uses 3.11), run the tests, and deactivate: ```console -source venv3.8/bin/activate +source venv3.11/bin/activate pytest deactivate diff --git a/test/test_util.py b/test/test_util.py index ed191ca4a0..8317a8593f 100644 --- a/test/test_util.py +++ b/test/test_util.py @@ -5,7 +5,7 @@ CVE-bin-tool util tests """ import inspect -from typing import DefaultDict +from collections import defaultdict from cve_bin_tool.cve_scanner import CVEScanner from cve_bin_tool.util import CVEData, ProductInfo, inpath @@ -35,7 +35,7 @@ def test_cve_scanner(self): instance_attrs = vars(CVEScanner)["__annotations__"] assert ( - instance_attrs["all_cve_data"] == DefaultDict[ProductInfo, CVEData] + instance_attrs["all_cve_data"] == defaultdict[ProductInfo, CVEData] ), "Type of all_cve_data has been changed. Make sure it isn't breaking OutputEngine!"