main
py 230 lines 8.51 KB
Raw
1 from datetime import datetime
2 from typing import Dict
3 from typing import List
4 from typing import Optional
5
6 from sqlmodel import JSON
7 from sqlmodel import Column
8 from sqlmodel import Field
9 from sqlmodel import Relationship
10 from sqlmodel import SQLModel
11 from sqlmodel import Text
12
13
14 class GitHubAuditConfig(SQLModel, table=True):
15 """Configuration for GitHub organization security audits per customer."""
16
17 __tablename__ = "github_audit_config"
18
19 id: Optional[int] = Field(default=None, primary_key=True)
20 customer_code: str = Field(max_length=50, nullable=False, index=True)
21
22 # GitHub Authentication
23 github_token: str = Field(max_length=500, nullable=False, description="GitHub PAT or App token (encrypted)")
24 organization: str = Field(max_length=100, nullable=False, description="GitHub organization name")
25
26 # Token metadata
27 token_type: str = Field(
28 max_length=50,
29 default="pat",
30 description="Token type: 'pat' (Personal Access Token) or 'app' (GitHub App)",
31 )
32 token_expires_at: Optional[datetime] = Field(
33 nullable=True,
34 description="When the token expires (if applicable)",
35 )
36
37 # Audit configuration
38 enabled: bool = Field(default=True, description="Whether audits are enabled for this org")
39 auto_audit_enabled: bool = Field(
40 default=False,
41 description="Whether to run audits automatically on schedule",
42 )
43 audit_schedule_cron: Optional[str] = Field(
44 max_length=50,
45 nullable=True,
46 description="Cron expression for scheduled audits (e.g., '0 0 * * 1' for weekly Monday)",
47 )
48
49 # Scope options - what to include in audits
50 include_repos: bool = Field(default=True, description="Include repository-level audits")
51 include_workflows: bool = Field(default=True, description="Include GitHub Actions audits")
52 include_members: bool = Field(default=True, description="Include member/permission audits")
53 include_archived_repos: bool = Field(default=False, description="Include archived repositories")
54
55 # Filtering
56 repo_filter_mode: str = Field(
57 max_length=20,
58 default="all",
59 description="'all', 'include', or 'exclude'",
60 )
61 repo_filter_list: Optional[List[str]] = Field(
62 sa_column=Column(JSON, nullable=True),
63 description="List of repos to include/exclude based on filter_mode",
64 )
65
66 # Notification settings
67 notify_on_critical: bool = Field(default=True, description="Send notification on critical findings")
68 notify_on_high: bool = Field(default=False, description="Send notification on high findings")
69 notification_webhook_url: Optional[str] = Field(
70 max_length=500,
71 nullable=True,
72 description="Webhook URL for audit notifications",
73 )
74 notification_email: Optional[str] = Field(
75 max_length=255,
76 nullable=True,
77 description="Email for audit notifications",
78 )
79
80 # Thresholds
81 minimum_passing_score: float = Field(
82 default=70.0,
83 description="Minimum score to consider audit passing (0-100)",
84 )
85
86 # Metadata
87 created_at: datetime = Field(default_factory=datetime.utcnow)
88 updated_at: datetime = Field(default_factory=datetime.utcnow)
89 created_by: Optional[str] = Field(max_length=100, nullable=True)
90 updated_by: Optional[str] = Field(max_length=100, nullable=True)
91
92 # Last audit info
93 last_audit_at: Optional[datetime] = Field(nullable=True, description="When last audit was run")
94 last_audit_score: Optional[float] = Field(nullable=True, description="Score from last audit")
95 last_audit_grade: Optional[str] = Field(max_length=2, nullable=True, description="Grade from last audit")
96
97 # Relationship to audit reports
98 audit_reports: List["GitHubAuditReport"] = Relationship(back_populates="config")
99
100
101 class GitHubAuditReport(SQLModel, table=True):
102 """Stored GitHub audit reports."""
103
104 __tablename__ = "github_audit_report"
105
106 id: Optional[int] = Field(default=None, primary_key=True)
107 config_id: int = Field(foreign_key="github_audit_config.id", nullable=False, index=True)
108 customer_code: str = Field(max_length=50, nullable=False, index=True)
109
110 # Report identification
111 report_name: str = Field(max_length=255, nullable=False)
112 organization: str = Field(max_length=100, nullable=False)
113
114 # Audit timing
115 audit_started_at: datetime = Field(default_factory=datetime.utcnow)
116 audit_completed_at: Optional[datetime] = Field(nullable=True)
117 audit_duration_seconds: Optional[float] = Field(nullable=True)
118
119 # Summary data
120 total_repos_audited: int = Field(default=0)
121 total_checks: int = Field(default=0)
122 passed_checks: int = Field(default=0)
123 failed_checks: int = Field(default=0)
124 warning_checks: int = Field(default=0)
125 critical_findings: int = Field(default=0)
126 high_findings: int = Field(default=0)
127 medium_findings: int = Field(default=0)
128 low_findings: int = Field(default=0)
129 score: float = Field(default=0.0)
130 grade: str = Field(max_length=2, default="F")
131
132 # Status
133 status: str = Field(
134 max_length=50,
135 default="running",
136 description="'running', 'completed', 'failed'",
137 )
138 error_message: Optional[str] = Field(sa_column=Column(Text, nullable=True))
139
140 # Full report data stored as JSON
141 full_report: Optional[Dict] = Field(sa_column=Column(JSON, nullable=True), description="Complete audit report data")
142
143 # Top findings for quick access
144 top_findings: Optional[List[Dict]] = Field(sa_column=Column(JSON, nullable=True), description="Top priority findings")
145
146 # Triggered by
147 triggered_by: str = Field(
148 max_length=50,
149 default="manual",
150 description="'manual', 'scheduled', 'api'",
151 )
152 triggered_by_user: Optional[str] = Field(max_length=100, nullable=True)
153
154 # Relationship back to config
155 config: GitHubAuditConfig = Relationship(back_populates="audit_reports")
156
157
158 class GitHubAuditCheckExclusion(SQLModel, table=True):
159 """Exclusion rules for specific audit checks."""
160
161 __tablename__ = "github_audit_check_exclusion"
162
163 id: Optional[int] = Field(default=None, primary_key=True)
164 config_id: int = Field(foreign_key="github_audit_config.id", nullable=False, index=True)
165 customer_code: str = Field(max_length=50, nullable=False, index=True)
166
167 # What to exclude
168 check_id: str = Field(
169 max_length=100,
170 nullable=False,
171 description="The check ID to exclude (e.g., 'repo-branch-protection')",
172 )
173 resource_name: Optional[str] = Field(
174 max_length=255,
175 nullable=True,
176 description="Specific resource to exclude (e.g., repo name). Null = all resources",
177 )
178 resource_type: Optional[str] = Field(
179 max_length=50,
180 nullable=True,
181 description="Type of resource: 'organization', 'repository', 'workflow', 'member'",
182 )
183
184 # Why excluded
185 reason: str = Field(sa_column=Column(Text, nullable=False), description="Reason for exclusion")
186 approved_by: Optional[str] = Field(max_length=100, nullable=True)
187 approved_at: Optional[datetime] = Field(nullable=True)
188
189 # Expiration
190 expires_at: Optional[datetime] = Field(
191 nullable=True,
192 description="When this exclusion expires (null = never)",
193 )
194
195 # Metadata
196 enabled: bool = Field(default=True)
197 created_at: datetime = Field(default_factory=datetime.utcnow)
198 created_by: str = Field(max_length=100, nullable=False)
199
200
201 class GitHubAuditBaseline(SQLModel, table=True):
202 """Baseline configuration for expected audit results."""
203
204 __tablename__ = "github_audit_baseline"
205
206 id: Optional[int] = Field(default=None, primary_key=True)
207 config_id: int = Field(foreign_key="github_audit_config.id", nullable=False, index=True)
208 customer_code: str = Field(max_length=50, nullable=False, index=True)
209
210 # Baseline name
211 name: str = Field(max_length=255, nullable=False)
212 description: Optional[str] = Field(sa_column=Column(Text, nullable=True))
213
214 # Expected values
215 expected_checks: Optional[Dict] = Field(
216 sa_column=Column(JSON, nullable=True),
217 description="Expected check results by check_id: {check_id: expected_status}",
218 )
219
220 # Baseline from a previous report
221 baseline_report_id: Optional[int] = Field(
222 foreign_key="github_audit_report.id",
223 nullable=True,
224 description="Report used to create this baseline",
225 )
226
227 # Metadata
228 is_active: bool = Field(default=True, description="Whether this is the active baseline")
229 created_at: datetime = Field(default_factory=datetime.utcnow)
230 created_by: str = Field(max_length=100, nullable=False)