main
py 121 lines 3.63 KB
Raw
1 from datetime import datetime
2 from enum import Enum
3 from typing import Any
4 from typing import Dict
5 from typing import List
6 from typing import Optional
7 from typing import Union
8
9 from pydantic import BaseModel
10 from pydantic import Field
11 from pydantic import field_validator
12 from pydantic import model_validator
13
14
15 class Technology(str, Enum):
16 """Technology types for active response scripts"""
17
18 WAZUH = "Wazuh"
19 LINUX = "Linux"
20 WINDOWS = "Windows"
21 MACOS = "macOS"
22 NETWORK = "Network"
23 CLOUD = "Cloud"
24 VELOCIRAPTOR = "Velociraptor"
25
26
27 class ScriptParameter(BaseModel):
28 """Parameters required for script execution"""
29
30 name: str
31 type: str
32 required: bool
33 description: Optional[str] = None
34 default: Optional[Union[str, int, float, bool, list, dict]] = None
35 enum: Optional[List[str]] = None
36 arg_position: Optional[str] = None
37
38 @field_validator("type")
39 @classmethod
40 def validate_type(cls, v):
41 allowed = {"string", "int", "float", "bool", "path", "enum", "list", "json", "integer", "boolean"}
42 if v not in allowed:
43 raise ValueError(f"type must be one of {sorted(allowed)}")
44 return v
45
46
47 class ActiveResponseItem(BaseModel):
48 """Individual active response script item"""
49
50 copilot_action_name: str
51 description: str
52 technology: Technology
53 icon: Optional[str] = None
54 script_parameters: List[ScriptParameter] = Field(default_factory=list)
55 repo_url: str # Changed from HttpUrl to str
56 script_name: Optional[str] = None
57 version: Optional[str] = None
58 last_updated: Optional[datetime] = None
59 category: Optional[str] = None
60 tags: Optional[List[str]] = None
61
62 @model_validator(mode="after")
63 def set_icon_default(self):
64 if self.icon is None and self.technology is not None:
65 self.icon = self.technology.value.lower()
66 return self
67
68
69 class InventoryQueryRequest(BaseModel):
70 """Request model for inventory queries"""
71
72 technology: Optional[Technology] = None
73 category: Optional[str] = None
74 tag: Optional[str] = None
75 q: Optional[str] = None # Free-text search
76 limit: int = Field(default=100, ge=1, le=1000)
77 offset: int = Field(default=0, ge=0)
78 refresh: bool = False
79 include: Optional[str] = None # Comma-separated extra fields
80
81
82 class InventoryResponse(BaseModel):
83 """Response model for inventory queries"""
84
85 copilot_actions: List[ActiveResponseItem]
86 message: str
87 success: bool
88
89 # Pagination metadata
90 total: Optional[int] = Field(None, description="Total number of items available")
91 count: Optional[int] = Field(None, description="Number of items in current response")
92 limit: Optional[int] = Field(None, description="Maximum items per page")
93 offset: Optional[int] = Field(None, description="Current offset")
94 has_more: Optional[bool] = Field(None, description="Whether there are more items available")
95 next_offset: Optional[int] = Field(None, description="Offset for next page")
96 prev_offset: Optional[int] = Field(None, description="Offset for previous page")
97
98
99 class ActionDetailResponse(BaseModel):
100 """Response model for single action details"""
101
102 copilot_action: ActiveResponseItem
103 message: str
104 success: bool
105
106
107 class InventoryMetricsResponse(BaseModel):
108 """Response model for inventory metrics"""
109
110 status: str
111 metrics: Dict[str, Any]
112 message: str = "Successfully retrieved inventory metrics"
113 success: bool = True
114
115
116 class InvokeCopilotActionResponse(BaseModel):
117 """Response model for invoking copilot actions"""
118
119 responses: List[Dict[str, Any]] # List of CollectArtifactResponse-like objects
120 message: str
121 success: bool