Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CVEDB] Fixes #4578(metric_finder function returns "unknown" #4656

Closed
wants to merge 12 commits into from
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ repos:
hooks:
- id: pyupgrade
exclude: ^fuzz/generated/
args: ["--py38-plus"]
args: ["--py39-plus"]

- repo: https://github.com/pycqa/flake8
rev: 7.1.1
Expand Down
5 changes: 1 addition & 4 deletions cve_bin_tool/checkers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,7 @@
from importlib import metadata as importlib_metadata
else:
import importlib_metadata
if sys.version_info >= (3, 9):
import importlib.resources as resources
else:
import importlib_resources as resources
import importlib.resources as resources

__all__ = [
"Checker",
Expand Down
4 changes: 2 additions & 2 deletions cve_bin_tool/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,9 @@ def __call__(self, parser, namespace, values, option_string=None):

def main(argv=None):
"""Scan a binary file for certain open source libraries that may have CVEs"""
if sys.version_info < (3, 8):
if sys.version_info < (3, 9):
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.7 as of June 2023. Please upgrade to python 3.9+ to use CVE Binary Tool."
)
argv = argv or sys.argv

Expand Down
2 changes: 1 addition & 1 deletion cve_bin_tool/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@

import sys
from collections import ChainMap
from collections.abc import Mapping
from logging import Logger
from pathlib import Path
from typing import Mapping

if sys.version_info >= (3, 11):
import tomllib as toml
Expand Down
4 changes: 2 additions & 2 deletions cve_bin_tool/csv2cve.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@

def main(argv: list[str] | None = None):
"""Used to scan a .csv file that lists the dependencies."""
if sys.version_info < (3, 8):
if sys.version_info < (3, 9):
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.7 as of June 2023. Please upgrade to python 3.9+ to use CVE Binary Tool."
)
logger: logging.Logger = LOGGER.getChild("CSV2CVE")
argv = argv or sys.argv
Expand Down
16 changes: 8 additions & 8 deletions cve_bin_tool/cve_scanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from logging import Logger
from pathlib import Path
from string import ascii_lowercase
from typing import DefaultDict, Dict, List
from typing import DefaultDict

from rich.console import Console

Expand All @@ -28,12 +28,12 @@ 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_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,
Expand All @@ -44,8 +44,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
Expand Down Expand Up @@ -211,10 +211,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:
Expand Down Expand Up @@ -385,7 +385,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"]
Expand Down
12 changes: 9 additions & 3 deletions cve_bin_tool/cvedb.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
DBNAME = "cve.db"
OLD_CACHE_DIR = Path("~") / ".cache" / "cvedb"

UNKNOWN_METRIC_ID = 0
EPSS_METRIC_ID = 1
CVSS_2_METRIC_ID = 2
CVSS_3_METRIC_ID = 3
Expand Down Expand Up @@ -416,6 +417,9 @@ def init_database(self) -> None:
for table in self.TABLE_SCHEMAS:
cursor.execute(self.TABLE_SCHEMAS[table])

# Ensure the UNKNOWN metric exists
self.ensure_unknown_metric(cursor)

# add indexes
for index in self.INDEXES:
cursor.execute(self.INDEXES[index])
Expand Down Expand Up @@ -619,6 +623,7 @@ def populate_metrics(self):
# Insert a row without specifying cve_metrics_id
insert_metrics = self.INSERT_QUERIES["insert_metrics"]
data = [
(UNKNOWN_METRIC_ID, "UNKNOWN"),
(EPSS_METRIC_ID, "EPSS"),
(CVSS_2_METRIC_ID, "CVSS-2"),
(CVSS_3_METRIC_ID, "CVSS-3"),
Expand All @@ -632,15 +637,15 @@ def populate_metrics(self):
def metric_finder(self, cursor, cve):
"""
SQL query to retrieve the metrics_name based on the metrics_id
currently cve["CVSS_version"] return 2,3 based on there version and they are mapped accordingly to there metrics name in metrics table.
currently cve["CVSS_version"] return 2,3 based on their version and they are mapped accordingly to their metrics name in metrics table.
"""
query = """
SELECT metrics_id FROM metrics
WHERE metrics_id=?
"""
metric = None
if cve["CVSS_version"] == "unknown":
metric = "unknown"
metric = 0
else:
cursor.execute(query, [cve.get("CVSS_version")])
# Fetch all the results of the query and use 'map' to extract only the 'metrics_name' from the result
Expand Down Expand Up @@ -1173,8 +1178,9 @@ def fetch_from_mirror(self, mirror, pubkey, ignore_signature, log_signature_erro

@contextlib.contextmanager
def with_cursor(self):
"""Context manager for database cursor."""
cursor = self.db_open_and_get_cursor()
try:
yield cursor
finally:
self.db_close()
self.db_close() # Ensure the database connection is closed
7 changes: 1 addition & 6 deletions cve_bin_tool/data_sources/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,10 @@

from __future__ import annotations

import sys
import importlib.resources as resources
from abc import ABC, abstractmethod
from pathlib import Path

if sys.version_info >= (3, 9):
import importlib.resources as resources
else:
import importlib_resources as resources

USER_HOME = Path("~")

# database defaults
Expand Down
2 changes: 1 addition & 1 deletion cve_bin_tool/helper_script.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
import sys
import textwrap
from collections import ChainMap
from collections.abc import MutableMapping
from logging import Logger
from pathlib import Path
from typing import MutableMapping

from rich import print as rprint
from rich.console import Console
Expand Down
7 changes: 4 additions & 3 deletions cve_bin_tool/input_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -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, DefaultDict, Union

from cve_bin_tool.cvedb import CVEDB
from cve_bin_tool.error_handler import (
Expand All @@ -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:
Expand Down Expand Up @@ -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.

Expand Down
10 changes: 5 additions & 5 deletions cve_bin_tool/package_list_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from logging import Logger
from pathlib import Path
from subprocess import PIPE, run
from typing import Any, Dict, List
from typing import Any

import distro

Expand Down Expand Up @@ -52,10 +52,10 @@ def __init__(
self.logger = logger

self.error_mode = error_mode
self.parsed_data_without_vendor: Dict[Any, Any] = defaultdict(dict)
self.parsed_data_with_vendor: Dict[Any, Any] = defaultdict(dict)
self.package_names_with_vendor: List[Any] = []
self.package_names_without_vendor: List[Any] = []
self.parsed_data_without_vendor: dict[Any, Any] = defaultdict(dict)
self.parsed_data_with_vendor: dict[Any, Any] = defaultdict(dict)
self.package_names_with_vendor: list[Any] = []
self.package_names_without_vendor: list[Any] = []

def parse_list(self):
"""
Expand Down
5 changes: 1 addition & 4 deletions cve_bin_tool/parsers/parse.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,7 @@
from importlib import metadata as importlib_metadata
else:
import importlib_metadata
if sys.version_info >= (3, 9):
import importlib.resources as resources
else:
import importlib_resources as resources
import importlib.resources as resources

from cve_bin_tool.parsers import Parser

Expand Down
6 changes: 3 additions & 3 deletions cve_bin_tool/strings.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)

Expand All @@ -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
Expand Down
6 changes: 4 additions & 2 deletions cve_bin_tool/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,11 @@
import os
import re
import sys
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 DefaultDict, NamedTuple, Union

import requests
from packageurl import PackageURL
Expand Down Expand Up @@ -212,7 +214,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
"""
Expand Down
2 changes: 1 addition & 1 deletion cve_bin_tool/version_scanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@

import subprocess
import sys
from collections.abc import Iterator
from logging import Logger
from pathlib import Path
from typing import Iterator

from cve_bin_tool.checkers import BUILTIN_CHECKERS, Checker
from cve_bin_tool.cvedb import CVEDB
Expand Down
8 changes: 4 additions & 4 deletions cve_bin_tool/vex_manager/generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from logging import Logger
from pathlib import Path
from typing import Dict, List, Optional
from typing import Optional

from lib4sbom.data.vulnerability import Vulnerability
from lib4vex.generator import VEXGenerator
Expand Down Expand Up @@ -67,7 +67,7 @@ def __init__(
vendor: str,
filename: str,
vextype: str,
all_cve_data: Dict[ProductInfo, CVEData],
all_cve_data: dict[ProductInfo, CVEData],
revision_reason: str = "",
sbom_serial_number: str = "",
sbom: Optional[str] = None,
Expand Down Expand Up @@ -154,7 +154,7 @@ def __generate_vex_filename(self) -> str:
)
return str(filename)

def __get_metadata(self) -> Dict:
def __get_metadata(self) -> dict:
"""
Generates metadata for the VEX document based on the specified VEX type, product, release,
and vendor information.
Expand Down Expand Up @@ -183,7 +183,7 @@ def __get_metadata(self) -> Dict:

return metadata

def __get_vulnerabilities(self) -> List[Vulnerability]:
def __get_vulnerabilities(self) -> list[Vulnerability]:
"""
Retrieves and constructs a list of vulnerability objects based on the current CVE data.

Expand Down
5 changes: 3 additions & 2 deletions cve_bin_tool/vex_manager/parse.py
Original file line number Diff line number Diff line change
@@ -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.abc 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:
Expand Down
Loading
Loading