python-ubel 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- python_ubel-0.1.0.dist-info/METADATA +224 -0
- python_ubel-0.1.0.dist-info/RECORD +17 -0
- python_ubel-0.1.0.dist-info/WHEEL +5 -0
- python_ubel-0.1.0.dist-info/entry_points.txt +4 -0
- python_ubel-0.1.0.dist-info/licenses/LICENSE +21 -0
- python_ubel-0.1.0.dist-info/top_level.txt +1 -0
- ubel/__init__.py +0 -0
- ubel/cli.py +141 -0
- ubel/client.py +50 -0
- ubel/cvss_parser.py +61 -0
- ubel/info.py +37 -0
- ubel/linux_runner.py +290 -0
- ubel/node_runner.py +399 -0
- ubel/policy.py +44 -0
- ubel/python_runner.py +126 -0
- ubel/ubel_engine.py +640 -0
- ubel/utils.py +32 -0
ubel/policy.py
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
def evaluate_policy(report: dict):
|
|
2
|
+
stats = report.get("stats", {})
|
|
3
|
+
policy = report.get("policy")
|
|
4
|
+
|
|
5
|
+
if not policy:
|
|
6
|
+
raise RuntimeError("No policy returned by API (fail-closed)")
|
|
7
|
+
|
|
8
|
+
# Infections
|
|
9
|
+
if policy.get("infections") == "block":
|
|
10
|
+
if stats.get("total_infections", 0) > 0:
|
|
11
|
+
return False, "Blocked by policy: infections detected"
|
|
12
|
+
|
|
13
|
+
# KEV
|
|
14
|
+
if policy.get("kev") == "block":
|
|
15
|
+
kev_total = (
|
|
16
|
+
stats.get("vulnerabilities_stats", {})
|
|
17
|
+
.get("kev", {})
|
|
18
|
+
.get("total", 0)
|
|
19
|
+
)
|
|
20
|
+
if kev_total > 0:
|
|
21
|
+
return False, "Blocked by policy: KEV vulnerabilities detected"
|
|
22
|
+
|
|
23
|
+
# Weaponized
|
|
24
|
+
if policy.get("weaponized") == "block":
|
|
25
|
+
weaponized = (
|
|
26
|
+
stats.get("vulnerabilities_stats", {})
|
|
27
|
+
.get("exploitability", {})
|
|
28
|
+
.get("are_weaponized", 0)
|
|
29
|
+
)
|
|
30
|
+
if weaponized > 0:
|
|
31
|
+
return False, "Blocked by policy: weaponized vulnerabilities detected"
|
|
32
|
+
|
|
33
|
+
# Severity rules
|
|
34
|
+
severity_policy = policy.get("severity", {})
|
|
35
|
+
severity_stats = (
|
|
36
|
+
stats.get("vulnerabilities_stats", {})
|
|
37
|
+
.get("severity", {})
|
|
38
|
+
)
|
|
39
|
+
for sev, action in severity_policy.items():
|
|
40
|
+
if action == "block":
|
|
41
|
+
if severity_stats.get(sev.lower(), 0) > 0:
|
|
42
|
+
return False, f"Blocked by policy: {sev} severity vulnerabilities detected"
|
|
43
|
+
|
|
44
|
+
return True, "Policy passed"
|
ubel/python_runner.py
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
import sys
|
|
3
|
+
import tempfile
|
|
4
|
+
import json
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
from importlib.metadata import distributions
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class Pypi_Manager:
|
|
10
|
+
|
|
11
|
+
@staticmethod
|
|
12
|
+
def get_installed():
|
|
13
|
+
return [
|
|
14
|
+
f'pkg:pypi/{dist.metadata["Name"].lower()}@{dist.version}'
|
|
15
|
+
for dist in distributions()
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
@staticmethod
|
|
19
|
+
def get_installed_inventory():
|
|
20
|
+
components_list = []
|
|
21
|
+
|
|
22
|
+
for dist in distributions():
|
|
23
|
+
meta = dist.metadata
|
|
24
|
+
|
|
25
|
+
# Convert EntryPoints to a serializable structure
|
|
26
|
+
entry_points = [
|
|
27
|
+
{
|
|
28
|
+
"name": ep.name,
|
|
29
|
+
"group": ep.group,
|
|
30
|
+
"value": ep.value
|
|
31
|
+
}
|
|
32
|
+
for ep in dist.entry_points
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
# Convert files to serializable form with full paths
|
|
36
|
+
files = []
|
|
37
|
+
if dist.files:
|
|
38
|
+
for f in dist.files:
|
|
39
|
+
files.append({
|
|
40
|
+
"path": str(f),
|
|
41
|
+
"full_path": str(dist.locate_file(f)),
|
|
42
|
+
"hash": getattr(f, "hash", None).__dict__ if getattr(f, "hash", None) else None,
|
|
43
|
+
"size": None # could populate by os.stat if needed
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
# Try reading optional metadata fields safely
|
|
47
|
+
def m(key, empty=""):
|
|
48
|
+
return meta.get(key) or empty
|
|
49
|
+
|
|
50
|
+
dist_info = {
|
|
51
|
+
"name": dist.metadata["Name"],
|
|
52
|
+
"version": dist.version,
|
|
53
|
+
"metadata": meta.json, # full metadata JSON
|
|
54
|
+
|
|
55
|
+
# Common metadata fields
|
|
56
|
+
"summary": m("Summary"),
|
|
57
|
+
"license": m("License"),
|
|
58
|
+
"author": m("Author"),
|
|
59
|
+
"author_email": m("Author-email"),
|
|
60
|
+
"home_page": m("Home-page"),
|
|
61
|
+
"requires_python": m("Requires-Python"),
|
|
62
|
+
|
|
63
|
+
# Dependencies
|
|
64
|
+
"requires": dist.requires or [],
|
|
65
|
+
|
|
66
|
+
# Entry points (console scripts, plugins…)
|
|
67
|
+
"entry_points": entry_points,
|
|
68
|
+
|
|
69
|
+
# Installation info
|
|
70
|
+
"location": dist.locate_file("").as_posix(),
|
|
71
|
+
"origin": dist._path.as_posix() if hasattr(dist, "_path") else None,
|
|
72
|
+
|
|
73
|
+
# Files
|
|
74
|
+
"files": files,
|
|
75
|
+
|
|
76
|
+
# Additional metadata fields
|
|
77
|
+
"platform": m("Platform"),
|
|
78
|
+
"installer": m("Installer"),
|
|
79
|
+
"python_version": m("Requires-Python"),
|
|
80
|
+
"keywords": m("Keywords"),
|
|
81
|
+
"classifiers": meta.get_all("Classifier") or [],
|
|
82
|
+
|
|
83
|
+
# Top-level modules/packages
|
|
84
|
+
"packages": meta.get_all("Top-Level") or []
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
components_list.append(dist_info)
|
|
88
|
+
|
|
89
|
+
return components_list
|
|
90
|
+
|
|
91
|
+
@staticmethod
|
|
92
|
+
def run_dry_run(initial_args):
|
|
93
|
+
with tempfile.NamedTemporaryFile(delete=False, suffix=".json") as tmp:
|
|
94
|
+
report_path = Path(tmp.name)
|
|
95
|
+
args=[]
|
|
96
|
+
for item in initial_args:
|
|
97
|
+
if item!="--":
|
|
98
|
+
args.append(item)
|
|
99
|
+
|
|
100
|
+
cmd = [
|
|
101
|
+
sys.executable,
|
|
102
|
+
"-m",
|
|
103
|
+
"pip",
|
|
104
|
+
"install",
|
|
105
|
+
"--dry-run",
|
|
106
|
+
"--report",
|
|
107
|
+
str(report_path),
|
|
108
|
+
] + args
|
|
109
|
+
|
|
110
|
+
result = subprocess.run(cmd,capture_output=True)
|
|
111
|
+
|
|
112
|
+
if result.returncode != 0:
|
|
113
|
+
raise RuntimeError(f"pip dry-run failed:\nCMD: {' '.join(cmd)}\nOutput:{result.stdout}\nError:{result.stderr}")
|
|
114
|
+
|
|
115
|
+
with open(report_path, "r", encoding="utf-8") as f:
|
|
116
|
+
data = json.load(f)
|
|
117
|
+
|
|
118
|
+
report_path.unlink(missing_ok=True)
|
|
119
|
+
return data
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
@staticmethod
|
|
123
|
+
def run_real_install(file_name,engine):
|
|
124
|
+
if engine=="pip":
|
|
125
|
+
cmd = [sys.executable, "-m", "pip" , "install","-r",file_name]
|
|
126
|
+
return subprocess.run(cmd)
|