-
Notifications
You must be signed in to change notification settings - Fork 251
Expand file tree
/
Copy pathmodels.py
More file actions
383 lines (307 loc) · 11.5 KB
/
Copy pathmodels.py
File metadata and controls
383 lines (307 loc) · 11.5 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
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
"""
Pydantic models for API requests and responses
"""
from typing import Optional, Dict, Any, List, Annotated
from datetime import datetime
from pydantic import BaseModel, Field, RootModel
from enum import Enum
MAX_BULK_DELETE = 500
class SafetyLevel(str, Enum):
"""Plugin safety level classification"""
SAFE = "safe"
INTRUSIVE = "intrusive"
EXPLOIT = "exploit"
class TaskStatus(str, Enum):
"""Task execution status"""
QUEUED = "queued"
RUNNING = "running"
COMPLETED = "completed"
FAILED = "failed"
CANCELLED = "cancelled"
class SandboxConfig(BaseModel):
"""Resource constraints applied to every plugin subprocess execution"""
timeout_seconds: int = Field(default=120, description="Max wall-clock seconds before SIGTERM")
max_memory_mb: int = Field(default=512, description="Max virtual memory in MB (RLIMIT_AS on Linux)")
max_output_bytes: int = Field(default=5_242_880, description="Max bytes captured from stdout/stderr")
allow_network: bool = Field(default=True, description="Whether subprocess can make network calls")
class SandboxViolation(Exception):
"""Raised when sandbox constraints are violated."""
def __init__(self, reason: str):
super().__init__(reason)
self.reason = reason
class ScanPhase(str, Enum):
"""Granular scan phase for progress display"""
QUEUED = "queued"
RUNNING_COMMAND = "running_command"
PARSING = "parsing"
REPORTING = "reporting"
FINISHED = "finished"
class PluginFieldType(str, Enum):
"""Plugin field input types"""
STRING = "string"
TEXT = "text"
INTEGER = "integer"
BOOLEAN = "boolean"
SELECT = "select"
MULTISELECT = "multiselect"
FILE = "file"
KEYVALUE = "keyvalue"
class PluginImplementationStatus(str, Enum):
"""How production-ready a plugin integration currently is."""
NATIVE = "native"
INTEGRATED = "integrated"
PLACEHOLDER = "placeholder"
class ValidationMode(str, Enum):
"""How far SecuScan is allowed to validate a suspected issue."""
DETECT_ONLY = "detect_only"
PROOF = "proof"
CONTROLLED_EXTRACT = "controlled_extract"
class EvidenceLevel(str, Enum):
"""How much evidence the platform should retain per finding."""
MINIMAL = "minimal"
STANDARD = "standard"
FULL = "full"
class FindingKind(str, Enum):
"""Normalized finding classification."""
OBSERVATION = "observation"
SUSPECTED_ISSUE = "suspected_issue"
VALIDATED_ISSUE = "validated_issue"
class AnalystStatus(str, Enum):
"""Analyst review state for a finding."""
NEW = "new"
CONFIRMED = "confirmed"
NEEDS_REVIEW = "needs_review"
FALSE_POSITIVE = "false_positive"
ACCEPTED_RISK = "accepted_risk"
FIXED = "fixed"
class RetestStatus(str, Enum):
"""Retest lifecycle state for a finding."""
NOT_REQUESTED = "not_requested"
PENDING = "pending"
PASSED = "passed"
FAILED = "failed"
class ExecutionContext(BaseModel):
"""Task/workflow execution policy selected by the operator."""
target_policy_id: Optional[str] = None
scan_profile: str = "standard"
credential_profile_id: Optional[str] = None
session_profile_id: Optional[str] = None
validation_mode: ValidationMode = ValidationMode.PROOF
evidence_level: EvidenceLevel = EvidenceLevel.STANDARD
class WorkflowStep(BaseModel):
"""Single workflow step."""
plugin_id: str
inputs: Dict[str, Any]
preset: Optional[str] = None
execution_context: ExecutionContext = Field(default_factory=ExecutionContext)
class PluginField(BaseModel):
"""Plugin input field definition"""
id: str
label: str
type: PluginFieldType
required: bool = False
default: Optional[Any] = None
placeholder: Optional[str] = None
validation: Optional[Dict[str, Any]] = None
help: Optional[str] = None
options: Optional[List[Dict[str, str]]] = None
class PluginMetadata(BaseModel):
"""Plugin metadata schema"""
id: str
name: str
version: str
description: str
long_description: Optional[str] = None
category: str
author: Optional[Dict[str, str]] = None
license: Optional[str] = "MIT"
icon: Optional[str] = "🔧"
engine: Dict[str, str]
command_template: List[str]
fields: List[PluginField]
presets: Dict[str, Dict[str, Any]]
output: Dict[str, Any]
safety: Dict[str, Any]
capabilities: Optional[List[str]] = None
implementation_status: Optional[PluginImplementationStatus] = None
supports_authenticated_crawling: bool = False
supports_session_reuse: bool = False
learning: Optional[Dict[str, Any]] = None
dependencies: Optional[Dict[str, List[str]]] = None
docker_image: Optional[str] = None
checksum: Optional[str] = None
signature: Optional[str] = None
class TaskCreateRequest(BaseModel):
"""Request to create a new task"""
plugin_id: str
preset: Optional[str] = None
inputs: Dict[str, Any]
consent_granted: bool = False
execution_context: ExecutionContext = Field(default_factory=ExecutionContext)
class TaskResponse(BaseModel):
"""Task information response"""
task_id: str
plugin_id: str
tool: str
target: str
status: TaskStatus
created_at: datetime
started_at: Optional[datetime] = None
completed_at: Optional[datetime] = None
duration_seconds: Optional[float] = None
inputs: Optional[Dict[str, Any]] = None
preset: Optional[str] = None
execution_context: ExecutionContext = Field(default_factory=ExecutionContext)
error_message: Optional[str] = None
exit_code: Optional[int] = None
class Finding(BaseModel):
"""Structured security finding"""
id: Optional[str] = None
title: str
category: str
severity: str
target: str
description: str
remediation: Optional[str] = ""
cvss: Optional[float] = None
cve: Optional[str] = None
proof: Optional[str] = None
discovered_at: Optional[datetime] = None
metadata: Dict[str, Any] = Field(default_factory=dict)
exploitability: Optional[float] = None
confidence: Optional[float] = None
validated: bool = False
validation_method: Optional[str] = None
confidence_reason: Optional[str] = None
evidence: List[Dict[str, Any]] = Field(default_factory=list)
asset_refs: List[str] = Field(default_factory=list)
service_fingerprint: Optional[str] = None
cpe: Optional[str] = None
references: List[Dict[str, Any]] = Field(default_factory=list)
asset_exposure: Optional[str] = None
risk_score: Optional[float] = None
risk_factors: List[Dict[str, Any]] = Field(default_factory=list)
finding_kind: FindingKind = FindingKind.OBSERVATION
finding_group_id: Optional[str] = None
asset_id: Optional[str] = None
first_seen_at: Optional[datetime] = None
last_seen_at: Optional[datetime] = None
occurrence_count: int = 1
corroborating_sources: List[str] = Field(default_factory=list)
evidence_count: int = 0
analyst_status: AnalystStatus = AnalystStatus.NEW
retest_status: RetestStatus = RetestStatus.NOT_REQUESTED
safe_to_apply: Optional[bool] = None
compatible_range: Optional[str] = None
alternatives: Optional[List[str]] = None
# AI Triage Engine (opt-in) — set when triage_engine_enabled and the
# finding was eligible (see triage_engine.is_eligible_for_triage).
triage_verdict: Optional[str] = None
triage_confidence: Optional[float] = None
triage_reasoning: Optional[str] = None
triage_remediation: Optional[str] = None
class TaskResult(BaseModel):
"""Task execution result"""
task_id: str
plugin_id: str
tool: str
target: str
timestamp: datetime
duration_seconds: Optional[float]
status: TaskStatus
execution_context: ExecutionContext = Field(default_factory=ExecutionContext)
summary: List[str] = []
severity_counts: Dict[str, int] = Field(default_factory=dict)
findings: List[Finding] = Field(default_factory=list)
finding_groups: List[Dict[str, Any]] = Field(default_factory=list)
asset_summary: List[Dict[str, Any]] = Field(default_factory=list)
scan_diff: Dict[str, Any] = Field(default_factory=dict)
structured: Dict[str, Any] = Field(default_factory=dict)
raw_output_path: Optional[str] = None
raw_output_excerpt: Optional[str] = None
errors: List[Dict[str, Any]] = []
error_message: Optional[str] = None
exit_code: Optional[int] = None
metadata: Dict[str, Any] = Field(default_factory=dict)
class HealthResponse(BaseModel):
"""Health check response"""
status: str
version: str
uptime_seconds: Optional[int] = None
system: Dict[str, Any]
limits: Optional[Dict[str, int]] = None
class PluginListResponse(BaseModel):
"""List of available plugins"""
plugins: List[Dict[str, Any]]
total: int
class ErrorResponse(BaseModel):
"""Error response"""
error: str
message: str
field: Optional[str] = None
details: Optional[Dict[str, Any]] = None
class NotificationChannelType(str, Enum):
"""Supported notification delivery channels."""
WEBHOOK = "webhook"
EMAIL = "email"
class NotificationSeverityThreshold(str, Enum):
"""Minimum finding severity that can trigger a notification rule."""
CRITICAL = "critical"
HIGH = "high"
MEDIUM = "medium"
LOW = "low"
INFO = "info"
class NotificationDeliveryStatus(str, Enum):
"""Outcome of a notification delivery attempt."""
SUCCESS = "success"
FAILED = "failed"
class NotificationRuleCreate(BaseModel):
"""Request payload for creating or updating a notification rule."""
name: str = Field(..., max_length=255)
severity_threshold: NotificationSeverityThreshold
channel_type: NotificationChannelType
target_url_or_email: str = Field(..., max_length=2000)
is_active: bool = True
class NotificationRuleUpdate(BaseModel):
"""Partial update payload for a notification rule."""
name: Optional[str] = Field(default=None, max_length=255)
severity_threshold: Optional[NotificationSeverityThreshold] = None
channel_type: Optional[NotificationChannelType] = None
target_url_or_email: Optional[str] = Field(default=None, max_length=2000)
is_active: Optional[bool] = None
class ScanWebhookSettingsRequest(BaseModel):
"""Request payload for setting the scan-completion webhook URL."""
webhook_url: str = Field(..., max_length=2000)
class ScanWebhookSettingsResponse(BaseModel):
"""Stored scan-completion webhook setting returned by the API."""
webhook_url: Optional[str] = None
platform: Optional[str] = None
configured: bool = False
updated_at: Optional[datetime] = None
class NotificationRuleResponse(BaseModel):
"""Stored notification rule returned by the API."""
id: str
name: str
severity_threshold: str
channel_type: str
target_url_or_email: str
is_active: bool
created_at: datetime
updated_at: datetime
class NotificationHistoryResponse(BaseModel):
"""Record of a single notification delivery attempt."""
id: str
rule_id: str
finding_id: str
status: str
error_message: Optional[str] = None
sent_at: datetime
class NotificationDiagnosticsResponse(BaseModel):
"""Diagnostic configuration details for notification delivery."""
webhook_timeout_seconds: float
webhook_connect_timeout_seconds: float
max_retries: int
backoff_factor_seconds: float
class BulkDeleteRequest(RootModel[Annotated[List[str], Field(max_length=MAX_BULK_DELETE)]]):
"""Accepts a JSON array of task IDs directly. Max 500 per request."""
pass