HumboldtJoker commited on
Commit
d2a02c2
·
verified ·
1 Parent(s): 376d61e

Clean up nested duplicate: v2/v2/skills/security_review.py

Browse files
Files changed (1) hide show
  1. v2/v2/skills/security_review.py +0 -249
v2/v2/skills/security_review.py DELETED
@@ -1,249 +0,0 @@
1
- """Security review chip + the audit findings as structured data.
2
-
3
- AUDIT_FINDINGS is the single source of truth for the 13 confirmed
4
- findings from the Nexus audit (2026-07-10, adversarially red-teamed,
5
- 43% survival rate). engine/beliefs.py seeds BDI beliefs from this list;
6
- the discipline gate checks drafts against the same list. One place to
7
- update when the campus team fixes a finding.
8
- """
9
-
10
- import re
11
- from dataclasses import dataclass, field
12
-
13
- from kintsugi_core import (
14
- BaseSkillChip,
15
- EFEWeights,
16
- SkillCapability,
17
- SkillContext,
18
- SkillDomain,
19
- SkillRequest,
20
- SkillResponse,
21
- )
22
-
23
-
24
- @dataclass(frozen=True)
25
- class AuditFinding:
26
- id: str
27
- severity: str # CRITICAL | HIGH | MEDIUM | LOW
28
- title: str
29
- area: str # auth | webhook | transaction | realtime | schema | moderation | cost
30
- file_hint: str
31
- advice: str
32
- patterns: tuple = () # regexes that indicate the same mistake recurring
33
-
34
-
35
- AUDIT_FINDINGS = [
36
- AuditFinding(
37
- id="C1", severity="CRITICAL", area="schema",
38
- title="Lecture instructor_id type mismatch — all lectures broken",
39
- file_hint="server/src/services/lectureCapture.ts:48",
40
- advice=("socket.userId is a numeric string but lecture_sessions."
41
- "instructor_id is a UUID column. Use consistent ID types; "
42
- "never compare socket IDs to DB UUIDs with ===."),
43
- patterns=(r"socket\.userId\s*===", r"instructor_id"),
44
- ),
45
- AuditFinding(
46
- id="C2", severity="CRITICAL", area="webhook",
47
- title="Webhook signature bypass when env var is unset",
48
- file_hint="server/src/routes/webhook.ts:219",
49
- advice=("Never skip signature verification when a secret env var "
50
- "is missing — reject the request instead, or require "
51
- "NODE_ENV=development for any bypass."),
52
- patterns=(r"if\s*\(\s*!\s*secret\s*\)\s*return\s+true",
53
- r"WEBHOOK_SECRET\s*\|\|\s*['\"]['\"]"),
54
- ),
55
- AuditFinding(
56
- id="H1", severity="HIGH", area="auth",
57
- title="JWT_SECRET falls back to empty string",
58
- file_hint="server/src/middleware/auth.ts:12",
59
- advice=("Startup must be fatal (process.exit(1)) when JWT_SECRET "
60
- "is empty or a placeholder. Never sign or verify with a "
61
- "defaulted secret."),
62
- patterns=(r"JWT_SECRET\s*(\?\?|\|\|)\s*['\"]",),
63
- ),
64
- AuditFinding(
65
- id="H2", severity="HIGH", area="realtime",
66
- title="connect_error triggers rapid reconnection loop",
67
- file_hint="client/src/stores/presenceStore.ts:578",
68
- advice=("All reconnect paths need exponential backoff. Only "
69
- "refresh tokens on 401/403, not on every connect_error."),
70
- patterns=(r"connect_error", r"refreshToken\(\)\s*.*connect\(\)"),
71
- ),
72
- AuditFinding(
73
- id="H3", severity="HIGH", area="transaction",
74
- title="Gem debit without transaction protection",
75
- file_hint="server/src/routes/store.ts:1108",
76
- advice=("debitGems/creditGems plus any dependent operation must "
77
- "share one DB transaction. A crash between them loses "
78
- "currency permanently."),
79
- patterns=(r"debitGems\(", r"creditGems\("),
80
- ),
81
- AuditFinding(
82
- id="H4", severity="HIGH", area="realtime",
83
- title="Trade system has no accept handler — feature incomplete",
84
- file_hint="server/src/services/socketHandlers/tradeHandlers.ts",
85
- advice=("trade:create-offer exists but accept/decline/cancel do "
86
- "not. Don't build on the trade flow assuming it completes."),
87
- patterns=(r"trade:(accept|create)-offer",),
88
- ),
89
- AuditFinding(
90
- id="M1", severity="MEDIUM", area="auth",
91
- title="JWT_SECRET prefix leaked into NPC Matrix password",
92
- file_hint="server/src/services/shopkeeperTools.ts:368",
93
- advice=("Never derive user-visible values from signing secrets. "
94
- "Use a separate secret or an HMAC derivation."),
95
- patterns=(r"JWT_SECRET\?*\.slice\(",),
96
- ),
97
- AuditFinding(
98
- id="M2", severity="MEDIUM", area="auth",
99
- title="Matrix admins receive isAdmin=true in client response",
100
- file_hint="server/src/routes/auth.ts:87",
101
- advice=("Client-facing admin flags must reflect server-enforced "
102
- "roles only; Matrix server admin is not campus admin."),
103
- patterns=(r"isMatrixServerAdmin", r"isAdmin\s*[:=]\s*true"),
104
- ),
105
- AuditFinding(
106
- id="M3", severity="MEDIUM", area="auth",
107
- title="No security headers (helmet/CSP/HSTS missing)",
108
- file_hint="server/src/index.ts",
109
- advice="Add helmet middleware with an appropriate CSP.",
110
- patterns=(r"app\.use\(helmet",),
111
- ),
112
- AuditFinding(
113
- id="M4", severity="MEDIUM", area="moderation",
114
- title="Faculty can kick/ban other faculty and admins",
115
- file_hint="server/src/services/socketHandlers/facultyPanelHandlers.ts:259",
116
- advice=("Moderation handlers must check the TARGET's role, not "
117
- "just the actor's — no acting on equal/higher privilege."),
118
- patterns=(r"(kick|ban|timeout).*(faculty|moderator)",),
119
- ),
120
- AuditFinding(
121
- id="M5", severity="MEDIUM", area="cost",
122
- title="No per-student message cap on agent conversations",
123
- file_hint="server/src/services/socketHandlers/agentHandlers.ts",
124
- advice=("Every new LLM-calling path needs a per-student daily cap "
125
- "or token budget."),
126
- patterns=(r"npc:message", r"callLLM\("),
127
- ),
128
- AuditFinding(
129
- id="L1", severity="LOW", area="schema",
130
- title="Foreign keys without ON DELETE break agent deletion",
131
- file_hint="migrations 193/223/303",
132
- advice=("New FKs referencing agents (or similar parents) need "
133
- "ON DELETE CASCADE or explicit dependent cleanup."),
134
- patterns=(r"REFERENCES\s+\w+\s*\([^)]*\)\s*(?!.*ON DELETE)",),
135
- ),
136
- AuditFinding(
137
- id="L2", severity="LOW", area="transaction",
138
- title="Agent job payments without transaction",
139
- file_hint="server/src/services/agentAutonomy.ts:1704",
140
- advice=("gem_balance and earnings updates must share one "
141
- "transaction."),
142
- patterns=(r"gem_balance", r"total_gems_earned"),
143
- ),
144
- ]
145
-
146
- # Recurring anti-patterns the audit told the team to grep for.
147
- RECURRING_PATTERNS = [
148
- ("env_fallback_disables_security",
149
- r"(SECRET|_KEY|TOKEN)\w*\s*(\?\?|\|\|)\s*['\"]",
150
- "Env var fallback that silently degrades security (audit pattern 4)."),
151
- ("parseint_no_nan_guard",
152
- r"parseInt\(req\.params",
153
- "parseInt on route params without a NaN guard (audit pattern 3)."),
154
- ("socket_on_without_off",
155
- r"socket\.on\(",
156
- "Socket listener registration — confirm matching socket.off cleanup "
157
- "(audit pattern 2)."),
158
- ]
159
-
160
- AUTH_SIGNALS = [
161
- r"\bJWT_SECRET\b", r"\bverifyToken\b", r"\brequireAdmin\b",
162
- r"\brequireModerator\b", r"\bauthMiddleware\b", r"\bsession\s*cookie\b",
163
- r"\brejectIfIneligible\b", r"\bBearer\b", r"\brefresh[_ ]?token\b",
164
- r"\bwebhook\b", r"\bsignature\b",
165
- ]
166
-
167
-
168
- def findings_relevant_to(text: str) -> list:
169
- """Findings whose area keywords or patterns appear in the text."""
170
- hits = []
171
- lower = text.lower()
172
- for f in AUDIT_FINDINGS:
173
- matched = any(re.search(p, text, re.IGNORECASE) for p in f.patterns)
174
- area_hit = f.area in lower
175
- if matched or area_hit:
176
- hits.append({"id": f.id, "severity": f.severity,
177
- "title": f.title, "advice": f.advice,
178
- "pattern_matched": matched})
179
- return hits
180
-
181
-
182
- def recurring_pattern_hits(text: str) -> list:
183
- hits = []
184
- for name, pattern, message in RECURRING_PATTERNS:
185
- if re.search(pattern, text):
186
- hits.append({"pattern": name, "message": message})
187
- return hits
188
-
189
-
190
- def touches_auth(text: str) -> bool:
191
- return any(re.search(p, text, re.IGNORECASE) for p in AUTH_SIGNALS)
192
-
193
-
194
- class SecurityReviewChip(BaseSkillChip):
195
- """Reviews a request (and any code in it) against the audit's
196
- confirmed findings and the campus auth architecture. Optionally greps
197
- the live repo for the same patterns near files the request names."""
198
-
199
- name = "security_review"
200
- description = "Audit-findings and auth-impact review"
201
- version = "2.0.0"
202
- domain = SkillDomain.SECURITY
203
- efe_weights = EFEWeights(
204
- mission_alignment=0.15, stakeholder_benefit=0.30,
205
- resource_efficiency=0.10, transparency=0.30, equity=0.15,
206
- )
207
- capabilities = [SkillCapability.READ_DATA]
208
-
209
- def __init__(self, repo_files=None):
210
- super().__init__()
211
- self.repo_files = repo_files # tools.file_tools.RepoFiles or None
212
-
213
- async def handle(self, request: SkillRequest,
214
- context: SkillContext) -> SkillResponse:
215
- question = context.metadata.get("question", request.raw_input)
216
- session = context.metadata.get("session")
217
-
218
- auth = touches_auth(question)
219
- findings = findings_relevant_to(question)
220
- recurring = recurring_pattern_hits(question)
221
-
222
- repo_hits = []
223
- if self.repo_files is not None and auth:
224
- # Where does the repo actually enforce auth today?
225
- for probe in (r"verifyToken", r"requireAdmin"):
226
- repo_hits.extend(self.repo_files.search(probe)[:5])
227
-
228
- if session:
229
- for f in findings:
230
- session.record_evidence("audit_finding", f["id"], self.name)
231
-
232
- report = {
233
- "auth_touched": auth,
234
- "relevant_findings": findings,
235
- "recurring_pattern_hits": recurring,
236
- "repo_auth_sites": repo_hits,
237
- }
238
- summary = (
239
- f"auth_touched={auth}; {len(findings)} audit finding(s) relevant; "
240
- f"{len(recurring)} recurring pattern hit(s)"
241
- )
242
- # NOTE: data IS the artifact — DAGExecutor assigns the whole data
243
- # dict to this node's single output key ("security_report").
244
- return SkillResponse(
245
- content=summary, success=True,
246
- data=report,
247
- requires_consensus=auth,
248
- consensus_action="auth_change_review" if auth else None,
249
- )