main
py 283 lines 9.6 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
8 from pydantic import BaseModel
9 from pydantic import Field
10
11
12 class AuditStatus(str, Enum):
13 PASS = "pass"
14 FAIL = "fail"
15 WARNING = "warning"
16 NOT_APPLICABLE = "not_applicable"
17
18
19 class SeverityLevel(str, Enum):
20 CRITICAL = "critical"
21 HIGH = "high"
22 MEDIUM = "medium"
23 LOW = "low"
24 INFO = "info"
25
26
27 # ==================== Request Schemas ====================
28
29
30 class GitHubAuditRequest(BaseModel):
31 """Request to run a GitHub organization audit."""
32
33 organization: str = Field(..., description="GitHub organization name to audit")
34 include_repos: bool = Field(True, description="Include repository-level audits")
35 include_workflows: bool = Field(True, description="Include workflow/actions audits")
36 include_members: bool = Field(True, description="Include member/permission audits")
37 repo_filter: Optional[List[str]] = Field(None, description="Specific repos to audit (None = all)")
38
39
40 class GitHubAuditConfigCreate(BaseModel):
41 """Request to create a GitHub Audit configuration."""
42
43 customer_code: str = Field(..., max_length=50, description="Customer code")
44 github_token: str = Field(..., max_length=500, description="GitHub PAT or App token")
45 organization: str = Field(..., max_length=100, description="GitHub organization name")
46 token_type: str = Field("pat", max_length=50, description="Token type: 'pat' or 'app'")
47 token_expires_at: Optional[datetime] = Field(None, description="When the token expires")
48 enabled: bool = Field(True, description="Whether audits are enabled")
49 auto_audit_enabled: bool = Field(False, description="Enable scheduled audits")
50 audit_schedule_cron: Optional[str] = Field(None, max_length=50, description="Cron schedule")
51 include_repos: bool = Field(True, description="Include repository audits")
52 include_workflows: bool = Field(True, description="Include workflow audits")
53 include_members: bool = Field(True, description="Include member audits")
54 include_archived_repos: bool = Field(False, description="Include archived repos")
55 repo_filter_mode: str = Field("all", description="'all', 'include', or 'exclude'")
56 repo_filter_list: Optional[List[str]] = Field(None, description="Repos to include/exclude")
57 notify_on_critical: bool = Field(True, description="Notify on critical findings")
58 notify_on_high: bool = Field(False, description="Notify on high findings")
59 notification_webhook_url: Optional[str] = Field(None, max_length=500)
60 notification_email: Optional[str] = Field(None, max_length=255)
61 minimum_passing_score: float = Field(70.0, ge=0, le=100)
62 created_by: Optional[str] = Field(None, max_length=100)
63
64
65 class GitHubAuditConfigUpdate(BaseModel):
66 """Request to update a GitHub Audit configuration."""
67
68 github_token: Optional[str] = Field(None, max_length=500)
69 organization: Optional[str] = Field(None, max_length=100)
70 token_type: Optional[str] = Field(None, max_length=50)
71 token_expires_at: Optional[datetime] = None
72 enabled: Optional[bool] = None
73 auto_audit_enabled: Optional[bool] = None
74 audit_schedule_cron: Optional[str] = Field(None, max_length=50)
75 include_repos: Optional[bool] = None
76 include_workflows: Optional[bool] = None
77 include_members: Optional[bool] = None
78 include_archived_repos: Optional[bool] = None
79 repo_filter_mode: Optional[str] = None
80 repo_filter_list: Optional[List[str]] = None
81 notify_on_critical: Optional[bool] = None
82 notify_on_high: Optional[bool] = None
83 notification_webhook_url: Optional[str] = Field(None, max_length=500)
84 notification_email: Optional[str] = Field(None, max_length=255)
85 minimum_passing_score: Optional[float] = Field(None, ge=0, le=100)
86 updated_by: Optional[str] = Field(None, max_length=100)
87
88
89 class GitHubAuditExclusionCreate(BaseModel):
90 """Request to create a check exclusion."""
91
92 check_id: str = Field(..., max_length=100, description="Check ID to exclude")
93 resource_name: Optional[str] = Field(None, max_length=255, description="Specific resource")
94 resource_type: Optional[str] = Field(None, max_length=50)
95 reason: str = Field(..., description="Reason for exclusion")
96 approved_by: Optional[str] = Field(None, max_length=100)
97 expires_at: Optional[datetime] = None
98 created_by: str = Field(..., max_length=100)
99
100
101 class GitHubAuditExclusionUpdate(BaseModel):
102 """Request to update a check exclusion."""
103
104 reason: Optional[str] = None
105 approved_by: Optional[str] = Field(None, max_length=100)
106 expires_at: Optional[datetime] = None
107 enabled: Optional[bool] = None
108
109
110 class GitHubAuditBaselineCreate(BaseModel):
111 """Request to create a baseline."""
112
113 name: str = Field(..., max_length=255)
114 description: Optional[str] = None
115 expected_checks: Optional[Dict[str, str]] = None
116 baseline_report_id: Optional[int] = None
117 is_active: bool = Field(True)
118 created_by: str = Field(..., max_length=100)
119
120
121 class GitHubAuditBaselineUpdate(BaseModel):
122 """Request to update a baseline."""
123
124 name: Optional[str] = Field(None, max_length=255)
125 description: Optional[str] = None
126 is_active: Optional[bool] = None
127
128
129 # ==================== Audit Check Result Schemas ====================
130
131
132 class AuditCheck(BaseModel):
133 """Individual audit check result."""
134
135 check_id: str = Field(..., description="Unique identifier for the check")
136 check_name: str = Field(..., description="Human-readable check name")
137 category: str = Field(..., description="Category (e.g., 'repository', 'organization')")
138 status: AuditStatus = Field(..., description="Pass/Fail/Warning status")
139 severity: SeverityLevel = Field(..., description="Severity if failed")
140 description: str = Field(..., description="Description of what was checked")
141 recommendation: Optional[str] = Field(None, description="Remediation recommendation")
142 details: Optional[Dict[str, Any]] = Field(None, description="Additional context")
143 resource_name: Optional[str] = Field(None, description="Name of resource being checked")
144 resource_type: Optional[str] = Field(None, description="Type of resource")
145
146
147 class RepositoryAuditResult(BaseModel):
148 """Audit results for a single repository."""
149
150 repo_name: str
151 repo_full_name: str
152 repo_url: str
153 is_private: bool
154 is_archived: bool = False
155 default_branch: str
156 checks: List[AuditCheck] = Field(default_factory=list)
157 passed_count: int = 0
158 failed_count: int = 0
159 warning_count: int = 0
160
161
162 class OrganizationAuditResult(BaseModel):
163 """Audit results for organization-level settings."""
164
165 org_name: str
166 org_url: str
167 checks: List[AuditCheck] = Field(default_factory=list)
168 passed_count: int = 0
169 failed_count: int = 0
170 warning_count: int = 0
171
172
173 class WorkflowAuditResult(BaseModel):
174 """Audit results for GitHub Actions workflows."""
175
176 repo_name: str
177 workflow_name: str
178 workflow_path: str
179 checks: List[AuditCheck] = Field(default_factory=list)
180
181
182 class MemberAuditResult(BaseModel):
183 """Audit results for organization members."""
184
185 username: str
186 role: str
187 has_2fa: Optional[bool] = None
188 checks: List[AuditCheck] = Field(default_factory=list)
189
190
191 class AuditSummary(BaseModel):
192 """Summary of the entire audit."""
193
194 organization: str
195 audit_timestamp: str
196 total_repos_audited: int = 0
197 total_checks: int = 0
198 passed_checks: int = 0
199 failed_checks: int = 0
200 warning_checks: int = 0
201 critical_findings: int = 0
202 high_findings: int = 0
203 medium_findings: int = 0
204 low_findings: int = 0
205 score: float = 0.0
206 grade: str = "F"
207
208
209 # ==================== Response Schemas ====================
210
211
212 class GitHubAuditResponse(BaseModel):
213 """Full GitHub audit response."""
214
215 success: bool
216 message: str
217 summary: Optional[AuditSummary] = None
218 organization_results: Optional[OrganizationAuditResult] = None
219 repository_results: List[RepositoryAuditResult] = Field(default_factory=list)
220 workflow_results: List[WorkflowAuditResult] = Field(default_factory=list)
221 member_results: List[MemberAuditResult] = Field(default_factory=list)
222 top_findings: List[AuditCheck] = Field(default_factory=list)
223
224
225 class GitHubAuditSummaryResponse(BaseModel):
226 """Lightweight summary response."""
227
228 success: bool
229 message: str
230 summary: Optional[AuditSummary] = None
231 top_findings: List[AuditCheck] = Field(default_factory=list)
232
233
234 class GitHubAuditConfigResponse(BaseModel):
235 """Response for config operations."""
236
237 success: bool
238 message: str
239 config: Optional[Any] = None # GitHubAuditConfig model
240 configs: Optional[List[Any]] = None # List of configs
241
242
243 class GitHubAuditReportListResponse(BaseModel):
244 """Response for listing reports."""
245
246 success: bool
247 message: str
248 reports: List[Dict[str, Any]] = Field(default_factory=list)
249 total_count: int = 0
250
251
252 class GitHubAuditReportResponse(BaseModel):
253 """Response for single report."""
254
255 success: bool
256 message: str
257 report: Optional[Any] = None # GitHubAuditReport model
258
259
260 class GitHubAuditExclusionResponse(BaseModel):
261 """Response for exclusion operations."""
262
263 success: bool
264 message: str
265 exclusion: Optional[Any] = None
266 exclusions: Optional[List[Any]] = None
267
268
269 class GitHubAuditBaselineResponse(BaseModel):
270 """Response for baseline operations."""
271
272 success: bool
273 message: str
274 baseline: Optional[Any] = None
275 baselines: Optional[List[Any]] = None
276
277
278 class AvailableChecksResponse(BaseModel):
279 """Response for available checks."""
280
281 success: bool
282 message: str
283 checks: List[Dict[str, str]] = Field(default_factory=list)