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.
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)