-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathsup_funcs.py
More file actions
109 lines (81 loc) · 3.29 KB
/
Copy pathsup_funcs.py
File metadata and controls
109 lines (81 loc) · 3.29 KB
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
101
102
103
104
105
106
107
108
109
from __future__ import annotations
import re
from pathlib import Path
import pandas as pd
SEVERITY_TRANSLATIONS = {
"Critical": "Критический",
"High": "Высокий",
"Medium": "Средний",
"Low": "Низкий",
}
def upd_regs(frame: pd.DataFrame) -> pd.DataFrame:
"""Normalize RedCheck remediation text and extract KB identifiers."""
result = frame.copy()
def normalize(value: object) -> str:
if not isinstance(value, str):
return ""
kb_matches = re.findall(r">([A-Z]{2}[0-9]{3,9})", value)
if kb_matches:
return "Необходимо установить обновления " + " ".join(kb_matches)
# Legacy reports sometimes stored a URL inside an HTML anchor.
href_match = re.search(r'href=["\']([^"\']+)["\']', value, flags=re.IGNORECASE)
if href_match:
return href_match.group(1)
# Preserve readable text when it is not one of the known legacy patterns.
return re.sub(r"<[^>]+>", "", value).strip()
result["def_remediation"] = result["def_remediation"].map(normalize)
return result
def ip2names(frame: pd.DataFrame, file_soot: str | Path | None) -> pd.DataFrame:
"""Replace address with a mapped network name when an optional CSV is provided."""
result = frame.copy()
mapping_path = Path(file_soot).expanduser() if file_soot else None
if mapping_path:
names = pd.read_csv(mapping_path, sep=";", encoding="utf-8")
required = {"address", "name"}
missing = required.difference(names.columns)
if missing:
raise ValueError(
"В CSV соответствия отсутствуют колонки: " + ", ".join(sorted(missing))
)
names = names[["address", "name"]].drop_duplicates(subset=["address"], keep="last")
result = result.merge(names, on="address", how="left")
result["name"] = result["name"].fillna(result["address"])
else:
result["name"] = result["address"]
keep_columns = [
"name",
"product",
"title",
"def_description",
"def_severity",
"def_remediation",
"reference_source",
"ref_id",
"ref_url",
"def_altx_id",
]
return result[keep_columns]
def severity_rus(frame: pd.DataFrame) -> pd.DataFrame:
result = frame.copy()
result["def_severity"] = result["def_severity"].replace(SEVERITY_TRANSLATIONS)
return result
def col_fstec(frame: pd.DataFrame) -> pd.DataFrame:
result = frame.copy()
result["BDU FSTEC"] = "-"
result.loc[result["reference_source"] == "FSTEC", "BDU FSTEC"] = "+"
return result
def group_name(frame: pd.DataFrame) -> pd.DataFrame:
"""Group one vulnerability across hosts while preserving report fields."""
result = frame.copy()
def host_list(values: pd.Series) -> str:
unique = sorted({str(value) for value in values.dropna()})
return str(unique)
host_groups = (
result.groupby("def_altx_id", dropna=False)["name"]
.agg(host_list)
.rename("_grouped_names")
)
result = result.drop_duplicates(subset=["def_altx_id"], keep="first")
result = result.join(host_groups, on="def_altx_id")
result["name"] = result.pop("_grouped_names")
return result