truthound-dashboard 1.3.0__py3-none-any.whl → 1.4.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.
- truthound_dashboard/api/alerts.py +258 -0
- truthound_dashboard/api/anomaly.py +1302 -0
- truthound_dashboard/api/cross_alerts.py +352 -0
- truthound_dashboard/api/deps.py +143 -0
- truthound_dashboard/api/drift_monitor.py +540 -0
- truthound_dashboard/api/lineage.py +1151 -0
- truthound_dashboard/api/maintenance.py +363 -0
- truthound_dashboard/api/middleware.py +373 -1
- truthound_dashboard/api/model_monitoring.py +805 -0
- truthound_dashboard/api/notifications_advanced.py +2452 -0
- truthound_dashboard/api/plugins.py +2096 -0
- truthound_dashboard/api/profile.py +211 -14
- truthound_dashboard/api/reports.py +853 -0
- truthound_dashboard/api/router.py +147 -0
- truthound_dashboard/api/rule_suggestions.py +310 -0
- truthound_dashboard/api/schema_evolution.py +231 -0
- truthound_dashboard/api/sources.py +47 -3
- truthound_dashboard/api/triggers.py +190 -0
- truthound_dashboard/api/validations.py +13 -0
- truthound_dashboard/api/validators.py +333 -4
- truthound_dashboard/api/versioning.py +309 -0
- truthound_dashboard/api/websocket.py +301 -0
- truthound_dashboard/core/__init__.py +27 -0
- truthound_dashboard/core/anomaly.py +1395 -0
- truthound_dashboard/core/anomaly_explainer.py +633 -0
- truthound_dashboard/core/cache.py +206 -0
- truthound_dashboard/core/cached_services.py +422 -0
- truthound_dashboard/core/charts.py +352 -0
- truthound_dashboard/core/connections.py +1069 -42
- truthound_dashboard/core/cross_alerts.py +837 -0
- truthound_dashboard/core/drift_monitor.py +1477 -0
- truthound_dashboard/core/drift_sampling.py +669 -0
- truthound_dashboard/core/i18n/__init__.py +42 -0
- truthound_dashboard/core/i18n/detector.py +173 -0
- truthound_dashboard/core/i18n/messages.py +564 -0
- truthound_dashboard/core/lineage.py +971 -0
- truthound_dashboard/core/maintenance.py +443 -5
- truthound_dashboard/core/model_monitoring.py +1043 -0
- truthound_dashboard/core/notifications/channels.py +1020 -1
- truthound_dashboard/core/notifications/deduplication/__init__.py +143 -0
- truthound_dashboard/core/notifications/deduplication/policies.py +274 -0
- truthound_dashboard/core/notifications/deduplication/service.py +400 -0
- truthound_dashboard/core/notifications/deduplication/stores.py +2365 -0
- truthound_dashboard/core/notifications/deduplication/strategies.py +422 -0
- truthound_dashboard/core/notifications/dispatcher.py +43 -0
- truthound_dashboard/core/notifications/escalation/__init__.py +149 -0
- truthound_dashboard/core/notifications/escalation/backends.py +1384 -0
- truthound_dashboard/core/notifications/escalation/engine.py +429 -0
- truthound_dashboard/core/notifications/escalation/models.py +336 -0
- truthound_dashboard/core/notifications/escalation/scheduler.py +1187 -0
- truthound_dashboard/core/notifications/escalation/state_machine.py +330 -0
- truthound_dashboard/core/notifications/escalation/stores.py +2896 -0
- truthound_dashboard/core/notifications/events.py +49 -0
- truthound_dashboard/core/notifications/metrics/__init__.py +115 -0
- truthound_dashboard/core/notifications/metrics/base.py +528 -0
- truthound_dashboard/core/notifications/metrics/collectors.py +583 -0
- truthound_dashboard/core/notifications/routing/__init__.py +169 -0
- truthound_dashboard/core/notifications/routing/combinators.py +184 -0
- truthound_dashboard/core/notifications/routing/config.py +375 -0
- truthound_dashboard/core/notifications/routing/config_parser.py +867 -0
- truthound_dashboard/core/notifications/routing/engine.py +382 -0
- truthound_dashboard/core/notifications/routing/expression_engine.py +1269 -0
- truthound_dashboard/core/notifications/routing/jinja2_engine.py +774 -0
- truthound_dashboard/core/notifications/routing/rules.py +625 -0
- truthound_dashboard/core/notifications/routing/validator.py +678 -0
- truthound_dashboard/core/notifications/service.py +2 -0
- truthound_dashboard/core/notifications/stats_aggregator.py +850 -0
- truthound_dashboard/core/notifications/throttling/__init__.py +83 -0
- truthound_dashboard/core/notifications/throttling/builder.py +311 -0
- truthound_dashboard/core/notifications/throttling/stores.py +1859 -0
- truthound_dashboard/core/notifications/throttling/throttlers.py +633 -0
- truthound_dashboard/core/openlineage.py +1028 -0
- truthound_dashboard/core/plugins/__init__.py +39 -0
- truthound_dashboard/core/plugins/docs/__init__.py +39 -0
- truthound_dashboard/core/plugins/docs/extractor.py +703 -0
- truthound_dashboard/core/plugins/docs/renderers.py +804 -0
- truthound_dashboard/core/plugins/hooks/__init__.py +63 -0
- truthound_dashboard/core/plugins/hooks/decorators.py +367 -0
- truthound_dashboard/core/plugins/hooks/manager.py +403 -0
- truthound_dashboard/core/plugins/hooks/protocols.py +265 -0
- truthound_dashboard/core/plugins/lifecycle/__init__.py +41 -0
- truthound_dashboard/core/plugins/lifecycle/hot_reload.py +584 -0
- truthound_dashboard/core/plugins/lifecycle/machine.py +419 -0
- truthound_dashboard/core/plugins/lifecycle/states.py +266 -0
- truthound_dashboard/core/plugins/loader.py +504 -0
- truthound_dashboard/core/plugins/registry.py +810 -0
- truthound_dashboard/core/plugins/reporter_executor.py +588 -0
- truthound_dashboard/core/plugins/sandbox/__init__.py +59 -0
- truthound_dashboard/core/plugins/sandbox/code_validator.py +243 -0
- truthound_dashboard/core/plugins/sandbox/engines.py +770 -0
- truthound_dashboard/core/plugins/sandbox/protocols.py +194 -0
- truthound_dashboard/core/plugins/sandbox.py +617 -0
- truthound_dashboard/core/plugins/security/__init__.py +68 -0
- truthound_dashboard/core/plugins/security/analyzer.py +535 -0
- truthound_dashboard/core/plugins/security/policies.py +311 -0
- truthound_dashboard/core/plugins/security/protocols.py +296 -0
- truthound_dashboard/core/plugins/security/signing.py +842 -0
- truthound_dashboard/core/plugins/security.py +446 -0
- truthound_dashboard/core/plugins/validator_executor.py +401 -0
- truthound_dashboard/core/plugins/versioning/__init__.py +51 -0
- truthound_dashboard/core/plugins/versioning/constraints.py +377 -0
- truthound_dashboard/core/plugins/versioning/dependencies.py +541 -0
- truthound_dashboard/core/plugins/versioning/semver.py +266 -0
- truthound_dashboard/core/profile_comparison.py +601 -0
- truthound_dashboard/core/report_history.py +570 -0
- truthound_dashboard/core/reporters/__init__.py +57 -0
- truthound_dashboard/core/reporters/base.py +296 -0
- truthound_dashboard/core/reporters/csv_reporter.py +155 -0
- truthound_dashboard/core/reporters/html_reporter.py +598 -0
- truthound_dashboard/core/reporters/i18n/__init__.py +65 -0
- truthound_dashboard/core/reporters/i18n/base.py +494 -0
- truthound_dashboard/core/reporters/i18n/catalogs.py +930 -0
- truthound_dashboard/core/reporters/json_reporter.py +160 -0
- truthound_dashboard/core/reporters/junit_reporter.py +233 -0
- truthound_dashboard/core/reporters/markdown_reporter.py +207 -0
- truthound_dashboard/core/reporters/pdf_reporter.py +209 -0
- truthound_dashboard/core/reporters/registry.py +272 -0
- truthound_dashboard/core/rule_generator.py +2088 -0
- truthound_dashboard/core/scheduler.py +822 -12
- truthound_dashboard/core/schema_evolution.py +858 -0
- truthound_dashboard/core/services.py +152 -9
- truthound_dashboard/core/statistics.py +718 -0
- truthound_dashboard/core/streaming_anomaly.py +883 -0
- truthound_dashboard/core/triggers/__init__.py +45 -0
- truthound_dashboard/core/triggers/base.py +226 -0
- truthound_dashboard/core/triggers/evaluators.py +609 -0
- truthound_dashboard/core/triggers/factory.py +363 -0
- truthound_dashboard/core/unified_alerts.py +870 -0
- truthound_dashboard/core/validation_limits.py +509 -0
- truthound_dashboard/core/versioning.py +709 -0
- truthound_dashboard/core/websocket/__init__.py +59 -0
- truthound_dashboard/core/websocket/manager.py +512 -0
- truthound_dashboard/core/websocket/messages.py +130 -0
- truthound_dashboard/db/__init__.py +30 -0
- truthound_dashboard/db/models.py +3375 -3
- truthound_dashboard/main.py +22 -0
- truthound_dashboard/schemas/__init__.py +396 -1
- truthound_dashboard/schemas/anomaly.py +1258 -0
- truthound_dashboard/schemas/base.py +4 -0
- truthound_dashboard/schemas/cross_alerts.py +334 -0
- truthound_dashboard/schemas/drift_monitor.py +890 -0
- truthound_dashboard/schemas/lineage.py +428 -0
- truthound_dashboard/schemas/maintenance.py +154 -0
- truthound_dashboard/schemas/model_monitoring.py +374 -0
- truthound_dashboard/schemas/notifications_advanced.py +1363 -0
- truthound_dashboard/schemas/openlineage.py +704 -0
- truthound_dashboard/schemas/plugins.py +1293 -0
- truthound_dashboard/schemas/profile.py +420 -34
- truthound_dashboard/schemas/profile_comparison.py +242 -0
- truthound_dashboard/schemas/reports.py +285 -0
- truthound_dashboard/schemas/rule_suggestion.py +434 -0
- truthound_dashboard/schemas/schema_evolution.py +164 -0
- truthound_dashboard/schemas/source.py +117 -2
- truthound_dashboard/schemas/triggers.py +511 -0
- truthound_dashboard/schemas/unified_alerts.py +223 -0
- truthound_dashboard/schemas/validation.py +25 -1
- truthound_dashboard/schemas/validators/__init__.py +11 -0
- truthound_dashboard/schemas/validators/base.py +151 -0
- truthound_dashboard/schemas/versioning.py +152 -0
- truthound_dashboard/static/index.html +2 -2
- {truthound_dashboard-1.3.0.dist-info → truthound_dashboard-1.4.0.dist-info}/METADATA +142 -18
- truthound_dashboard-1.4.0.dist-info/RECORD +239 -0
- truthound_dashboard/static/assets/index-BCA8H1hO.js +0 -574
- truthound_dashboard/static/assets/index-BNsSQ2fN.css +0 -1
- truthound_dashboard/static/assets/unmerged_dictionaries-CsJWCRx9.js +0 -1
- truthound_dashboard-1.3.0.dist-info/RECORD +0 -110
- {truthound_dashboard-1.3.0.dist-info → truthound_dashboard-1.4.0.dist-info}/WHEEL +0 -0
- {truthound_dashboard-1.3.0.dist-info → truthound_dashboard-1.4.0.dist-info}/entry_points.txt +0 -0
- {truthound_dashboard-1.3.0.dist-info → truthound_dashboard-1.4.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,446 @@
|
|
|
1
|
+
"""Plugin Security Management.
|
|
2
|
+
|
|
3
|
+
This module provides security features for plugins:
|
|
4
|
+
- Signature verification
|
|
5
|
+
- Permission management
|
|
6
|
+
- Security analysis
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import hashlib
|
|
12
|
+
import hmac
|
|
13
|
+
import logging
|
|
14
|
+
from dataclasses import dataclass, field
|
|
15
|
+
from datetime import datetime
|
|
16
|
+
from typing import Any
|
|
17
|
+
|
|
18
|
+
from .sandbox import CodeAnalyzer, SandboxConfig
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass
|
|
24
|
+
class SecurityReport:
|
|
25
|
+
"""Security analysis report for a plugin.
|
|
26
|
+
|
|
27
|
+
Attributes:
|
|
28
|
+
plugin_id: Plugin identifier.
|
|
29
|
+
analyzed_at: When analysis was performed.
|
|
30
|
+
risk_level: Overall risk level.
|
|
31
|
+
signature_valid: Whether signature is valid.
|
|
32
|
+
sandbox_compatible: Whether code can run in sandbox.
|
|
33
|
+
issues: Critical security issues found.
|
|
34
|
+
warnings: Non-critical warnings.
|
|
35
|
+
permissions_required: Required permissions.
|
|
36
|
+
code_hash: Hash of analyzed code.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
plugin_id: str
|
|
40
|
+
analyzed_at: datetime
|
|
41
|
+
risk_level: str # trusted, verified, unverified, sandboxed
|
|
42
|
+
signature_valid: bool = False
|
|
43
|
+
sandbox_compatible: bool = True
|
|
44
|
+
issues: list[str] = field(default_factory=list)
|
|
45
|
+
warnings: list[str] = field(default_factory=list)
|
|
46
|
+
permissions_required: list[str] = field(default_factory=list)
|
|
47
|
+
code_hash: str = ""
|
|
48
|
+
|
|
49
|
+
def is_safe(self) -> bool:
|
|
50
|
+
"""Check if plugin is considered safe.
|
|
51
|
+
|
|
52
|
+
Returns:
|
|
53
|
+
True if no critical issues and sandbox compatible.
|
|
54
|
+
"""
|
|
55
|
+
return len(self.issues) == 0 and self.sandbox_compatible
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class SignatureVerifier:
|
|
59
|
+
"""Verifies plugin signatures.
|
|
60
|
+
|
|
61
|
+
This class handles cryptographic verification of plugin
|
|
62
|
+
packages to ensure authenticity and integrity.
|
|
63
|
+
|
|
64
|
+
Attributes:
|
|
65
|
+
trusted_keys: Dictionary of trusted public keys by key ID.
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
def __init__(self, trusted_keys: dict[str, str] | None = None) -> None:
|
|
69
|
+
"""Initialize the verifier.
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
trusted_keys: Dictionary mapping key IDs to public keys.
|
|
73
|
+
"""
|
|
74
|
+
self.trusted_keys = trusted_keys or {}
|
|
75
|
+
|
|
76
|
+
def add_trusted_key(self, key_id: str, public_key: str) -> None:
|
|
77
|
+
"""Add a trusted public key.
|
|
78
|
+
|
|
79
|
+
Args:
|
|
80
|
+
key_id: Key identifier.
|
|
81
|
+
public_key: Public key (hex encoded).
|
|
82
|
+
"""
|
|
83
|
+
self.trusted_keys[key_id] = public_key
|
|
84
|
+
logger.info(f"Added trusted key: {key_id}")
|
|
85
|
+
|
|
86
|
+
def remove_trusted_key(self, key_id: str) -> None:
|
|
87
|
+
"""Remove a trusted public key.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
key_id: Key identifier.
|
|
91
|
+
"""
|
|
92
|
+
if key_id in self.trusted_keys:
|
|
93
|
+
del self.trusted_keys[key_id]
|
|
94
|
+
logger.info(f"Removed trusted key: {key_id}")
|
|
95
|
+
|
|
96
|
+
def verify_hmac(
|
|
97
|
+
self,
|
|
98
|
+
data: bytes,
|
|
99
|
+
signature: str,
|
|
100
|
+
secret_key: str,
|
|
101
|
+
) -> bool:
|
|
102
|
+
"""Verify HMAC signature.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
data: Data that was signed.
|
|
106
|
+
signature: HMAC signature (hex encoded).
|
|
107
|
+
secret_key: Secret key for HMAC.
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
True if signature is valid.
|
|
111
|
+
"""
|
|
112
|
+
try:
|
|
113
|
+
expected = hmac.new(
|
|
114
|
+
secret_key.encode(),
|
|
115
|
+
data,
|
|
116
|
+
hashlib.sha256,
|
|
117
|
+
).hexdigest()
|
|
118
|
+
return hmac.compare_digest(expected, signature)
|
|
119
|
+
except Exception as e:
|
|
120
|
+
logger.warning(f"HMAC verification failed: {e}")
|
|
121
|
+
return False
|
|
122
|
+
|
|
123
|
+
def verify_signature(
|
|
124
|
+
self,
|
|
125
|
+
data: bytes,
|
|
126
|
+
signature: str,
|
|
127
|
+
key_id: str | None = None,
|
|
128
|
+
) -> tuple[bool, str | None]:
|
|
129
|
+
"""Verify a signature.
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
data: Data that was signed.
|
|
133
|
+
signature: Signature (format depends on algorithm).
|
|
134
|
+
key_id: Optional key ID to use.
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
Tuple of (is_valid, error_message).
|
|
138
|
+
"""
|
|
139
|
+
if not signature:
|
|
140
|
+
return False, "No signature provided"
|
|
141
|
+
|
|
142
|
+
# For now, use simple HMAC verification
|
|
143
|
+
# In production, you would use proper asymmetric cryptography
|
|
144
|
+
# like Ed25519 or RSA
|
|
145
|
+
|
|
146
|
+
if key_id and key_id in self.trusted_keys:
|
|
147
|
+
key = self.trusted_keys[key_id]
|
|
148
|
+
if self.verify_hmac(data, signature, key):
|
|
149
|
+
return True, None
|
|
150
|
+
return False, "Signature verification failed"
|
|
151
|
+
|
|
152
|
+
# Try all trusted keys
|
|
153
|
+
for kid, key in self.trusted_keys.items():
|
|
154
|
+
if self.verify_hmac(data, signature, key):
|
|
155
|
+
return True, None
|
|
156
|
+
|
|
157
|
+
return False, "No matching trusted key found"
|
|
158
|
+
|
|
159
|
+
def create_signature(
|
|
160
|
+
self,
|
|
161
|
+
data: bytes,
|
|
162
|
+
secret_key: str,
|
|
163
|
+
) -> str:
|
|
164
|
+
"""Create a signature for data.
|
|
165
|
+
|
|
166
|
+
Args:
|
|
167
|
+
data: Data to sign.
|
|
168
|
+
secret_key: Secret key for signing.
|
|
169
|
+
|
|
170
|
+
Returns:
|
|
171
|
+
Signature (hex encoded).
|
|
172
|
+
"""
|
|
173
|
+
return hmac.new(
|
|
174
|
+
secret_key.encode(),
|
|
175
|
+
data,
|
|
176
|
+
hashlib.sha256,
|
|
177
|
+
).hexdigest()
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
class PluginSecurityManager:
|
|
181
|
+
"""Manages security for plugins.
|
|
182
|
+
|
|
183
|
+
This class provides comprehensive security analysis
|
|
184
|
+
and management for plugins.
|
|
185
|
+
|
|
186
|
+
Attributes:
|
|
187
|
+
signature_verifier: Signature verifier instance.
|
|
188
|
+
sandbox_config: Default sandbox configuration.
|
|
189
|
+
"""
|
|
190
|
+
|
|
191
|
+
def __init__(
|
|
192
|
+
self,
|
|
193
|
+
signature_verifier: SignatureVerifier | None = None,
|
|
194
|
+
sandbox_config: SandboxConfig | None = None,
|
|
195
|
+
) -> None:
|
|
196
|
+
"""Initialize the security manager.
|
|
197
|
+
|
|
198
|
+
Args:
|
|
199
|
+
signature_verifier: Signature verifier.
|
|
200
|
+
sandbox_config: Default sandbox configuration.
|
|
201
|
+
"""
|
|
202
|
+
self.signature_verifier = signature_verifier or SignatureVerifier()
|
|
203
|
+
self.sandbox_config = sandbox_config or SandboxConfig()
|
|
204
|
+
|
|
205
|
+
def analyze_code(self, code: str) -> tuple[list[str], list[str]]:
|
|
206
|
+
"""Analyze code for security issues.
|
|
207
|
+
|
|
208
|
+
Args:
|
|
209
|
+
code: Python source code.
|
|
210
|
+
|
|
211
|
+
Returns:
|
|
212
|
+
Tuple of (issues, warnings).
|
|
213
|
+
"""
|
|
214
|
+
analyzer = CodeAnalyzer()
|
|
215
|
+
return analyzer.analyze(code)
|
|
216
|
+
|
|
217
|
+
def detect_permissions(self, code: str) -> list[str]:
|
|
218
|
+
"""Detect required permissions from code.
|
|
219
|
+
|
|
220
|
+
Args:
|
|
221
|
+
code: Python source code.
|
|
222
|
+
|
|
223
|
+
Returns:
|
|
224
|
+
List of required permission strings.
|
|
225
|
+
"""
|
|
226
|
+
import ast
|
|
227
|
+
|
|
228
|
+
permissions = set()
|
|
229
|
+
|
|
230
|
+
try:
|
|
231
|
+
tree = ast.parse(code)
|
|
232
|
+
|
|
233
|
+
for node in ast.walk(tree):
|
|
234
|
+
# Check imports
|
|
235
|
+
if isinstance(node, (ast.Import, ast.ImportFrom)):
|
|
236
|
+
module = getattr(node, "module", None)
|
|
237
|
+
if module:
|
|
238
|
+
modules = [module]
|
|
239
|
+
else:
|
|
240
|
+
modules = [alias.name for alias in node.names]
|
|
241
|
+
|
|
242
|
+
for mod in modules:
|
|
243
|
+
if mod.startswith(("http", "urllib", "requests", "httpx", "socket")):
|
|
244
|
+
permissions.add("network_access")
|
|
245
|
+
elif mod.startswith(("os", "shutil", "pathlib")):
|
|
246
|
+
permissions.add("file_system")
|
|
247
|
+
elif mod.startswith(("subprocess", "multiprocessing")):
|
|
248
|
+
permissions.add("execute_code")
|
|
249
|
+
elif mod.startswith(("pickle", "shelve")):
|
|
250
|
+
permissions.add("read_data")
|
|
251
|
+
permissions.add("write_data")
|
|
252
|
+
|
|
253
|
+
# Check for file operations
|
|
254
|
+
if isinstance(node, ast.Call):
|
|
255
|
+
if isinstance(node.func, ast.Name):
|
|
256
|
+
if node.func.id == "open":
|
|
257
|
+
permissions.add("file_system")
|
|
258
|
+
|
|
259
|
+
except SyntaxError:
|
|
260
|
+
pass
|
|
261
|
+
|
|
262
|
+
return sorted(list(permissions))
|
|
263
|
+
|
|
264
|
+
def analyze_plugin(
|
|
265
|
+
self,
|
|
266
|
+
plugin_id: str,
|
|
267
|
+
code: str,
|
|
268
|
+
signature: str | None = None,
|
|
269
|
+
package_data: bytes | None = None,
|
|
270
|
+
) -> SecurityReport:
|
|
271
|
+
"""Perform security analysis on a plugin.
|
|
272
|
+
|
|
273
|
+
Args:
|
|
274
|
+
plugin_id: Plugin identifier.
|
|
275
|
+
code: Plugin code to analyze.
|
|
276
|
+
signature: Optional signature to verify.
|
|
277
|
+
package_data: Optional package data for signature verification.
|
|
278
|
+
|
|
279
|
+
Returns:
|
|
280
|
+
SecurityReport with analysis results.
|
|
281
|
+
"""
|
|
282
|
+
issues, warnings = self.analyze_code(code)
|
|
283
|
+
permissions = self.detect_permissions(code)
|
|
284
|
+
code_hash = hashlib.sha256(code.encode()).hexdigest()
|
|
285
|
+
|
|
286
|
+
# Verify signature if provided
|
|
287
|
+
signature_valid = False
|
|
288
|
+
if signature and package_data:
|
|
289
|
+
signature_valid, _ = self.signature_verifier.verify_signature(
|
|
290
|
+
package_data, signature
|
|
291
|
+
)
|
|
292
|
+
elif signature and code:
|
|
293
|
+
signature_valid, _ = self.signature_verifier.verify_signature(
|
|
294
|
+
code.encode(), signature
|
|
295
|
+
)
|
|
296
|
+
|
|
297
|
+
# Determine risk level
|
|
298
|
+
if signature_valid:
|
|
299
|
+
if not issues:
|
|
300
|
+
risk_level = "trusted"
|
|
301
|
+
else:
|
|
302
|
+
risk_level = "verified"
|
|
303
|
+
else:
|
|
304
|
+
if issues:
|
|
305
|
+
risk_level = "sandboxed"
|
|
306
|
+
else:
|
|
307
|
+
risk_level = "unverified"
|
|
308
|
+
|
|
309
|
+
# Check sandbox compatibility
|
|
310
|
+
sandbox_compatible = self._check_sandbox_compatible(code, issues)
|
|
311
|
+
|
|
312
|
+
return SecurityReport(
|
|
313
|
+
plugin_id=plugin_id,
|
|
314
|
+
analyzed_at=datetime.utcnow(),
|
|
315
|
+
risk_level=risk_level,
|
|
316
|
+
signature_valid=signature_valid,
|
|
317
|
+
sandbox_compatible=sandbox_compatible,
|
|
318
|
+
issues=issues,
|
|
319
|
+
warnings=warnings,
|
|
320
|
+
permissions_required=permissions,
|
|
321
|
+
code_hash=code_hash,
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
def _check_sandbox_compatible(
|
|
325
|
+
self,
|
|
326
|
+
code: str,
|
|
327
|
+
issues: list[str],
|
|
328
|
+
) -> bool:
|
|
329
|
+
"""Check if code is compatible with sandbox execution.
|
|
330
|
+
|
|
331
|
+
Args:
|
|
332
|
+
code: Python source code.
|
|
333
|
+
issues: Already detected issues.
|
|
334
|
+
|
|
335
|
+
Returns:
|
|
336
|
+
True if code can run in sandbox.
|
|
337
|
+
"""
|
|
338
|
+
# If there are critical issues, might not be compatible
|
|
339
|
+
if any("blocked" in issue.lower() for issue in issues):
|
|
340
|
+
return False
|
|
341
|
+
|
|
342
|
+
# Check for patterns that won't work in sandbox
|
|
343
|
+
dangerous_patterns = [
|
|
344
|
+
"__import__",
|
|
345
|
+
"importlib",
|
|
346
|
+
"sys.modules",
|
|
347
|
+
"globals()",
|
|
348
|
+
"locals()",
|
|
349
|
+
"__class__.__bases__",
|
|
350
|
+
"__subclasses__",
|
|
351
|
+
]
|
|
352
|
+
|
|
353
|
+
for pattern in dangerous_patterns:
|
|
354
|
+
if pattern in code:
|
|
355
|
+
return False
|
|
356
|
+
|
|
357
|
+
return True
|
|
358
|
+
|
|
359
|
+
def validate_permissions(
|
|
360
|
+
self,
|
|
361
|
+
required: list[str],
|
|
362
|
+
granted: list[str],
|
|
363
|
+
) -> tuple[bool, list[str]]:
|
|
364
|
+
"""Validate that required permissions are granted.
|
|
365
|
+
|
|
366
|
+
Args:
|
|
367
|
+
required: Required permissions.
|
|
368
|
+
granted: Granted permissions.
|
|
369
|
+
|
|
370
|
+
Returns:
|
|
371
|
+
Tuple of (all_granted, missing_permissions).
|
|
372
|
+
"""
|
|
373
|
+
granted_set = set(granted)
|
|
374
|
+
missing = [p for p in required if p not in granted_set]
|
|
375
|
+
return len(missing) == 0, missing
|
|
376
|
+
|
|
377
|
+
def get_security_level(
|
|
378
|
+
self,
|
|
379
|
+
report: SecurityReport,
|
|
380
|
+
user_trust_level: str = "normal",
|
|
381
|
+
) -> str:
|
|
382
|
+
"""Determine final security level based on analysis and trust.
|
|
383
|
+
|
|
384
|
+
Args:
|
|
385
|
+
report: Security analysis report.
|
|
386
|
+
user_trust_level: User's trust level (admin, normal, restricted).
|
|
387
|
+
|
|
388
|
+
Returns:
|
|
389
|
+
Final security level recommendation.
|
|
390
|
+
"""
|
|
391
|
+
if user_trust_level == "admin":
|
|
392
|
+
# Admins can run anything
|
|
393
|
+
return report.risk_level
|
|
394
|
+
|
|
395
|
+
if user_trust_level == "restricted":
|
|
396
|
+
# Restricted users can only run trusted plugins
|
|
397
|
+
if report.risk_level != "trusted":
|
|
398
|
+
return "sandboxed"
|
|
399
|
+
return report.risk_level
|
|
400
|
+
|
|
401
|
+
# Normal users
|
|
402
|
+
if report.issues:
|
|
403
|
+
return "sandboxed"
|
|
404
|
+
|
|
405
|
+
return report.risk_level
|
|
406
|
+
|
|
407
|
+
def create_sandbox_config_for_plugin(
|
|
408
|
+
self,
|
|
409
|
+
report: SecurityReport,
|
|
410
|
+
base_config: SandboxConfig | None = None,
|
|
411
|
+
) -> SandboxConfig:
|
|
412
|
+
"""Create appropriate sandbox config based on security analysis.
|
|
413
|
+
|
|
414
|
+
Args:
|
|
415
|
+
report: Security analysis report.
|
|
416
|
+
base_config: Base configuration to modify.
|
|
417
|
+
|
|
418
|
+
Returns:
|
|
419
|
+
Configured SandboxConfig.
|
|
420
|
+
"""
|
|
421
|
+
config = base_config or SandboxConfig()
|
|
422
|
+
|
|
423
|
+
# Adjust based on risk level
|
|
424
|
+
if report.risk_level == "trusted":
|
|
425
|
+
# Trusted plugins get more freedom
|
|
426
|
+
config.enabled = False
|
|
427
|
+
elif report.risk_level == "verified":
|
|
428
|
+
# Verified plugins get relaxed restrictions
|
|
429
|
+
config.memory_limit_mb = 512
|
|
430
|
+
config.cpu_time_limit_seconds = 60
|
|
431
|
+
config.network_enabled = "network_access" in report.permissions_required
|
|
432
|
+
elif report.risk_level == "unverified":
|
|
433
|
+
# Default restrictions
|
|
434
|
+
config.enabled = True
|
|
435
|
+
else: # sandboxed
|
|
436
|
+
# Strict restrictions
|
|
437
|
+
config.memory_limit_mb = 128
|
|
438
|
+
config.cpu_time_limit_seconds = 15
|
|
439
|
+
config.network_enabled = False
|
|
440
|
+
|
|
441
|
+
return config
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
# Default instances
|
|
445
|
+
signature_verifier = SignatureVerifier()
|
|
446
|
+
security_manager = PluginSecurityManager(signature_verifier)
|