1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
|
from __future__ import annotations
from dataclasses import dataclass
from functools import lru_cache
from typing import Any, ClassVar
from sec_certs import constants
from sec_certs.serialization.json import ComplexSerializableType
from sec_certs.serialization.pandas import PandasSerializableType
from sec_certs.utils import helpers
@dataclass(init=False)
class CPE(PandasSerializableType, ComplexSerializableType):
uri: str
version: str
vendor: str
item_name: str
title: str | None
start_version: tuple[str, str] | None
end_version: tuple[str, str] | None
__slots__ = ["uri", "version", "vendor", "item_name", "title", "start_version", "end_version"]
pandas_columns: ClassVar[list[str]] = [
"uri",
"vendor",
"item_name",
"version",
"title",
]
def __init__(
self,
uri: str,
title: str | None = None,
start_version: tuple[str, str] | None = None,
end_version: tuple[str, str] | None = None,
):
super().__init__()
self.uri = uri
splitted = helpers.split_unescape(self.uri, ":")
self.vendor = " ".join(splitted[3].split("_"))
self.item_name = " ".join(splitted[4].split("_"))
self.version = self.normalize_version(" ".join(splitted[5].split("_")))
self.title = title
self.start_version = start_version
self.end_version = end_version
def __lt__(self, other: CPE) -> bool:
return self.uri < other.uri
@staticmethod
def normalize_version(version: str) -> str:
"""
Maps common empty versions (empty '', asterisk '*') to unified empty version (constants.CPE_VERSION_NA)
"""
if version in {"", "*"}:
return constants.CPE_VERSION_NA
return version
@classmethod
def from_dict(cls, dct: dict[str, Any]) -> CPE:
if isinstance(dct["start_version"], list):
dct["start_version"] = tuple(dct["start_version"])
if isinstance(dct["end_version"], list):
dct["end_version"] = tuple(dct["end_version"])
return super().from_dict(dct)
@property
def serialized_attributes(self) -> list[str]:
return ["uri", "title", "start_version", "end_version"]
@property
def update(self) -> str:
if self.uri is None:
raise RuntimeError("URI is missing.")
return " ".join(self.uri.split(":")[6].split("_"))
@property
def target_hw(self) -> str:
if self.uri is None:
raise RuntimeError("URI is missing.")
return " ".join(self.uri.split(":")[10].split("_"))
@property
def pandas_tuple(self) -> tuple:
return self.uri, self.vendor, self.item_name, self.version, self.title
def __hash__(self) -> int:
return hash((self.uri, self.start_version, self.end_version))
def __eq__(self, other: object) -> bool:
return isinstance(other, self.__class__) and self.uri == other.uri
@lru_cache(maxsize=4096)
def cached_cpe(*args, **kwargs):
return CPE(*args, **kwargs)
|