claudify-agent 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/.claude/QUICK-REFERENCE.md +213 -213
  2. package/.claude/cost-dashboard.md +162 -162
  3. package/.claude/docs/COST-OPTIMIZATION.md +402 -403
  4. package/.claude/docs/MCP-INTEGRATION-GUIDE.md +348 -348
  5. package/.claude/docs/PLAN-MODE-OPTIMIZATION.md +835 -836
  6. package/.claude/hooks/auto-learning-review.ts +200 -195
  7. package/.claude/hooks/auto-lint-fix.ts +60 -104
  8. package/.claude/hooks/auto-security-review.ts +314 -354
  9. package/.claude/hooks/auto-validate.ts +249 -348
  10. package/.claude/hooks/lib/claude-hook.ts +168 -0
  11. package/.claude/hooks/plan-quality-tracker.ts +598 -719
  12. package/.claude/rules/anti-patterns.md +22 -22
  13. package/.claude/rules/context-tier-1.md +41 -41
  14. package/.claude/rules/context-tier-2.md +285 -285
  15. package/.claude/rules/general.md +25 -25
  16. package/.claude/rules/mcp-auto-use.md +46 -46
  17. package/.claude/rules/parallel-exploration-auto.md +30 -31
  18. package/.claude/rules/plan-mode-execution.md +289 -289
  19. package/.claude/rules/plan-mode-research.md +34 -34
  20. package/.claude/rules/plan-mode-structure.md +166 -166
  21. package/.claude/rules/plan-mode-validation.md +129 -130
  22. package/.claude/rules/security-critical.md +19 -19
  23. package/.claude/rules/tool-auto-selection.md +45 -45
  24. package/.claude/rules/tool-strategy.md +22 -22
  25. package/.claude/scripts/security-scan.js +1 -1
  26. package/.claude/security-dashboard.md +142 -142
  27. package/.claude/settings.json +50 -0
  28. package/.claude/skills/debug-optimizer/SKILL.md +420 -399
  29. package/.claude/skills/learning-loop/SKILL.md +313 -308
  30. package/.claude/skills/mcp-mastery/SKILL.md +218 -214
  31. package/.claude/skills/nextjs-guide/SKILL.md +687 -687
  32. package/.claude/skills/parallel-exploration/SKILL.md +197 -193
  33. package/.claude/skills/plan-mode-mastery/SKILL.md +268 -263
  34. package/.claude/skills/python-guide/SKILL.md +822 -822
  35. package/.claude/skills/react-guide/SKILL.md +762 -762
  36. package/.claude/skills/{security.md → security-guide/SKILL.md} +10 -0
  37. package/.claude/skills/solidity-guide/SKILL.md +5 -0
  38. package/.claude/skills/tool-selection/SKILL.md +433 -428
  39. package/.claude/skills/vite-guide/SKILL.md +445 -445
  40. package/CLAUDE.md +1 -0
  41. package/README.md +74 -35
  42. package/README.vi.md +106 -0
  43. package/bin/cli.js +13 -4
  44. package/package.json +9 -1
  45. package/.claude/skills/debug-optimizer.md +0 -12
  46. package/.claude/skills/nextjs.md +0 -13
  47. package/.claude/skills/react.md +0 -12
@@ -1,354 +1,314 @@
1
- // .cursor/hooks/auto-security-review.ts
2
- // Automatic security vulnerability scanning for AI-generated code
3
-
4
- import { readFileSync, writeFileSync, existsSync } from "fs";
5
- import { join } from "path";
6
-
7
- export {};
8
-
9
- interface SecurityHookInput {
10
- conversation_id: string;
11
- status: "completed" | "aborted" | "error";
12
- loop_count: number;
13
- }
14
-
15
- interface SecurityFinding {
16
- severity: "critical" | "high" | "medium" | "low";
17
- type: string;
18
- description: string;
19
- location?: string;
20
- suggestion: string;
21
- }
22
-
23
- interface SecurityConfig {
24
- enableSecurityReview?: boolean;
25
- severityThreshold?: "critical" | "high" | "medium" | "low";
26
- scanPatterns?: {
27
- hardcodedCredentials?: boolean;
28
- sqlInjection?: boolean;
29
- xss?: boolean;
30
- insecureCrypto?: boolean;
31
- pathTraversal?: boolean;
32
- commandInjection?: boolean;
33
- };
34
- }
35
-
36
- // Security pattern definitions (OWASP Top 10 inspired)
37
- const SECURITY_PATTERNS = {
38
- hardcodedCredentials: [
39
- {
40
- pattern: /(password|passwd|pwd)\s*[=:]\s*["'][^"']+["']/gi,
41
- type: "Hardcoded Password",
42
- severity: "critical" as const,
43
- suggestion: "Use environment variables or secure secret management (e.g., AWS Secrets Manager, Azure Key Vault)"
44
- },
45
- {
46
- pattern: /(api[_-]?key|apikey)\s*[=:]\s*["'][^"']+["']/gi,
47
- type: "Hardcoded API Key",
48
- severity: "critical" as const,
49
- suggestion: "Store API keys in environment variables or secure vault"
50
- },
51
- {
52
- pattern: /(secret|token|auth)\s*[=:]\s*["'][^"']+["']/gi,
53
- type: "Hardcoded Secret/Token",
54
- severity: "critical" as const,
55
- suggestion: "Use environment variables for secrets"
56
- },
57
- {
58
- pattern: /AWS[_A-Z]*KEY|AWS[_A-Z]*SECRET/gi,
59
- type: "AWS Credentials",
60
- severity: "critical" as const,
61
- suggestion: "Use IAM roles or AWS Secrets Manager"
62
- }
63
- ],
64
- sqlInjection: [
65
- {
66
- pattern: /(execute|query|raw)\s*\(\s*`[^`]*\$\{[^}]+\}/gi,
67
- type: "Potential SQL Injection",
68
- severity: "critical" as const,
69
- suggestion: "Use parameterized queries or ORM methods instead of string interpolation"
70
- },
71
- {
72
- pattern: /execute\s*\(\s*["'][^"']*["']\s*\+/gi,
73
- type: "SQL String Concatenation",
74
- severity: "critical" as const,
75
- suggestion: "Use parameterized queries"
76
- }
77
- ],
78
- xss: [
79
- {
80
- pattern: /dangerouslySetInnerHTML\s*=/gi,
81
- type: "Dangerous HTML Rendering",
82
- severity: "high" as const,
83
- suggestion: "Avoid dangerouslySetInnerHTML or sanitize input with DOMPurify"
84
- },
85
- {
86
- pattern: /innerHTML\s*=/gi,
87
- type: "Inner HTML Assignment",
88
- severity: "high" as const,
89
- suggestion: "Use textContent or sanitize HTML before assignment"
90
- },
91
- {
92
- pattern: /eval\s*\(/gi,
93
- type: "Eval Usage",
94
- severity: "high" as const,
95
- suggestion: "Avoid eval() - use safer alternatives"
96
- }
97
- ],
98
- insecureCrypto: [
99
- {
100
- pattern: /crypto\s*\.\s*createHash\s*\(\s*["']md5["']/gi,
101
- type: "Weak Hash Algorithm (MD5)",
102
- severity: "high" as const,
103
- suggestion: "Use SHA-256 or stronger (crypto.createHash('sha256'))"
104
- },
105
- {
106
- pattern: /crypto\s*\.\s*createHash\s*\(\s*["']sha1["']/gi,
107
- type: "Weak Hash Algorithm (SHA1)",
108
- severity: "medium" as const,
109
- suggestion: "Use SHA-256 or stronger for security-critical applications"
110
- },
111
- {
112
- pattern: /Math\.random\s*\(/gi,
113
- type: "Insecure Random Number Generator",
114
- severity: "medium" as const,
115
- suggestion: "Use crypto.randomBytes() for security-sensitive randomness"
116
- }
117
- ],
118
- pathTraversal: [
119
- {
120
- pattern: /fs\s*\.\s*(readFile|writeFile|appendFile)\s*\([^)]*\+/gi,
121
- type: "Potential Path Traversal",
122
- severity: "high" as const,
123
- suggestion: "Validate and sanitize file paths, use path.resolve() and path.normalize()"
124
- },
125
- {
126
- pattern: /require\s*\(\s*[^)]*\+/gi,
127
- type: "Dynamic Require with User Input",
128
- severity: "high" as const,
129
- suggestion: "Avoid dynamic require with user-controlled paths"
130
- }
131
- ],
132
- commandInjection: [
133
- {
134
- pattern: /exec\s*\(\s*`[^`]*\$\{[^}]+\}/gi,
135
- type: "Command Injection via Template Literal",
136
- severity: "critical" as const,
137
- suggestion: "Avoid shell commands with user input, use execFile with arguments array"
138
- },
139
- {
140
- pattern: /spawn\s*\(\s*[^)]*\+/gi,
141
- type: "Command Injection via String Concatenation",
142
- severity: "critical" as const,
143
- suggestion: "Use spawn with arguments array instead of string concatenation"
144
- },
145
- {
146
- pattern: /child_process\s*\.\s*exec\s*\(/gi,
147
- type: "Unsafe exec Usage",
148
- severity: "medium" as const,
149
- suggestion: "Prefer execFile or spawn with argument separation"
150
- }
151
- ]
152
- };
153
-
154
- // Read input from stdin
155
- const stdin = await new Promise<string>((resolve) => {
156
- let data = "";
157
- process.stdin.on("data", (chunk) => (data += chunk));
158
- process.stdin.on("end", () => resolve(data));
159
- });
160
-
161
- const input: SecurityHookInput = JSON.parse(stdin);
162
-
163
- // Load configuration
164
- const configPath = join(process.cwd(), '.cursor/hooks.config.json');
165
- const config: SecurityConfig = existsSync(configPath)
166
- ? JSON.parse(readFileSync(configPath, 'utf-8'))
167
- : {};
168
-
169
- const securityConfig = {
170
- enableSecurityReview: (config as any).validation?.enableSecurityReview !== false,
171
- severityThreshold: (config as any).validation?.securitySeverityThreshold || "high",
172
- scanPatterns: (config as any).validation?.securityScanPatterns || {
173
- hardcodedCredentials: true,
174
- sqlInjection: true,
175
- xss: true,
176
- insecureCrypto: true,
177
- pathTraversal: true,
178
- commandInjection: true
179
- }
180
- };
181
-
182
- // Only run on completion
183
- if (input.status !== "completed") {
184
- console.log(JSON.stringify({}));
185
- process.exit(0);
186
- }
187
-
188
- if (!securityConfig.enableSecurityReview) {
189
- console.error("[Security Review] ⚠ Security review disabled in config");
190
- console.log(JSON.stringify({}));
191
- process.exit(0);
192
- }
193
-
194
- console.error("[Security Review] Running automatic security scan...");
195
-
196
- const findings: SecurityFinding[] = [];
197
-
198
- // Try to scan changed files
199
- const conversationFile = join(process.cwd(), `.cursor/conversations/${input.conversation_id}.json`);
200
- let conversation;
201
- try {
202
- if (existsSync(conversationFile)) {
203
- conversation = JSON.parse(readFileSync(conversationFile, 'utf-8'));
204
- }
205
- } catch {
206
- console.error("[Security Review] Could not read conversation file, proceeding with scan");
207
- conversation = null;
208
- }
209
-
210
- // Extract code from conversation messages
211
- function extractCodeFromConversation(conv: any): string[] {
212
- const codeBlocks: string[] = [];
213
-
214
- if (!conv?.messages) return codeBlocks;
215
-
216
- for (const message of conv.messages) {
217
- if (message.content) {
218
- // Extract code blocks
219
- const codeBlockRegex = /```(?:typescript|javascript|tsx|ts|js)?\n([\s\S]*?)```/g;
220
- let match;
221
- while ((match = codeBlockRegex.exec(message.content)) !== null) {
222
- codeBlocks.push(match[1]);
223
- }
224
- }
225
- }
226
-
227
- return codeBlocks;
228
- }
229
-
230
- // Scan code for security issues
231
- function scanCodeForSecurity(code: string, patternCategory: string): SecurityFinding[] {
232
- const categoryFindings: SecurityFinding[] = [];
233
- const patterns = SECURITY_PATTERNS[patternCategory as keyof typeof SECURITY_PATTERNS];
234
-
235
- if (!patterns || !securityConfig.scanPatterns[patternCategory as keyof typeof securityConfig.scanPatterns]) {
236
- return categoryFindings;
237
- }
238
-
239
- for (const { pattern, type, severity, suggestion } of patterns) {
240
- const matches = code.match(pattern);
241
- if (matches) {
242
- categoryFindings.push({
243
- severity,
244
- type,
245
- description: `Found ${matches.length} occurrence(s) of potentially insecure pattern`,
246
- suggestion
247
- });
248
- }
249
- }
250
-
251
- return categoryFindings;
252
- }
253
-
254
- // Scan all pattern categories
255
- const codeToScan = conversation ? extractCodeFromConversation(conversation) : [];
256
-
257
- // If no code extracted, skip scan
258
- if (codeToScan.length === 0) {
259
- console.error("[Security Review] ⚠ No code blocks found to scan");
260
- console.log(JSON.stringify({}));
261
- process.exit(0);
262
- }
263
-
264
- const combinedCode = codeToScan.join('\n');
265
-
266
- // Run security scans
267
- const patternCategories = Object.keys(SECURITY_PATTERNS);
268
- for (const category of patternCategories) {
269
- const categoryFindings = scanCodeForSecurity(combinedCode, category);
270
- findings.push(...categoryFindings);
271
- }
272
-
273
- // Filter by severity threshold
274
- const severityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
275
- const thresholdLevel = severityOrder[securityConfig.severityThreshold];
276
-
277
- const filteredFindings = findings.filter(
278
- finding => severityOrder[finding.severity] <= thresholdLevel
279
- );
280
-
281
- // Generate security report
282
- const criticalCount = filteredFindings.filter(f => f.severity === "critical").length;
283
- const highCount = filteredFindings.filter(f => f.severity === "high").length;
284
- const mediumCount = filteredFindings.filter(f => f.severity === "medium").length;
285
- const lowCount = filteredFindings.filter(f => f.severity === "low").length;
286
-
287
- const reportPath = join(process.cwd(), '.cursor/security-report.md');
288
-
289
- const securityReport = `## Security Scan Report
290
-
291
- Generated: ${new Date().toISOString()}
292
- Conversation: ${input.conversation_id}
293
- Severity Threshold: ${securityConfig.severityThreshold}
294
-
295
- ## Summary
296
-
297
- - 🔴 Critical: ${criticalCount}
298
- - 🟠 High: ${highCount}
299
- - 🟡 Medium: ${mediumCount}
300
- - 🟢 Low: ${lowCount}
301
- - **Total Findings**: ${filteredFindings.length}
302
-
303
- ## Findings
304
-
305
- ${filteredFindings.length === 0 ? '✅ No security issues found!' : filteredFindings.map((finding, i) => `
306
- ### ${i + 1}. ${finding.type}
307
-
308
- **Severity**: ${finding.severity.toUpperCase()}
309
-
310
- **Description**: ${finding.description}
311
-
312
- **Recommendation**: ${finding.suggestion}
313
-
314
- ---
315
- `).join('\n')}
316
-
317
- ## Security Checklist (OWASP Top 10)
318
-
319
- - [ ] All user input validated and sanitized
320
- - [ ] SQL injection prevented (parameterized queries)
321
- - [ ] XSS prevented (escaping/sanitization)
322
- - [ ] Authentication/authorization verified
323
- - [ ] No hardcoded credentials or secrets
324
- - [ ] Error handling doesn't leak sensitive info
325
- - [ ] Rate limiting on auth endpoints
326
- - [ ] Secure random number generation used
327
- - [ ] Strong cryptographic algorithms used
328
- - [ ] File paths validated and normalized
329
-
330
- ## Next Steps
331
-
332
- ${criticalCount > 0 ? '🚨 CRITICAL issues found! Address immediately before deployment.' :
333
- highCount > 0 ? '⚠️ HIGH severity issues found. Review and fix before production.' :
334
- mediumCount > 0 ? '📋 Medium severity issues found. Schedule for next sprint.' :
335
- lowCount > 0 ? '💡 Low severity issues found. Consider fixing for best practices.' :
336
- '✅ No security issues detected at current threshold.'}
337
- `;
338
-
339
- writeFileSync(reportPath, securityReport);
340
- console.error(`[Security Review] Report saved to ${reportPath}`);
341
-
342
- // Provide feedback to user
343
- if (criticalCount > 0 || highCount > 0) {
344
- const output = {
345
- followup_message: `🚨 Security scan found ${criticalCount} critical and ${highCount} high severity issues. See \`.cursor/security-report.md\` for details.`,
346
- };
347
- console.log(JSON.stringify(output));
348
- } else if (filteredFindings.length > 0) {
349
- console.error(`[Security Review] ⚠️ Found ${filteredFindings.length} security finding(s). Report saved.`);
350
- console.log(JSON.stringify({}));
351
- } else {
352
- console.error("[Security Review] ✓ No security issues found");
353
- console.log(JSON.stringify({}));
354
- }
1
+ // .claude/hooks/auto-security-review.ts
2
+ // Stop hook — scans code Claude wrote this turn for security vulnerabilities
3
+
4
+ import { readFileSync, writeFileSync, existsSync } from "fs";
5
+ import { join } from "path";
6
+ import {
7
+ readHookInput,
8
+ loadCurrentTurn,
9
+ writtenCode,
10
+ stateDir,
11
+ blockOrNotify,
12
+ done,
13
+ } from "./lib/claude-hook.ts";
14
+
15
+ interface SecurityFinding {
16
+ severity: "critical" | "high" | "medium" | "low";
17
+ type: string;
18
+ description: string;
19
+ location?: string;
20
+ suggestion: string;
21
+ }
22
+
23
+ // Security settings live under "validation" in .claude/hooks.config.json
24
+ interface SecurityConfig {
25
+ validation?: {
26
+ enableSecurityReview?: boolean;
27
+ securitySeverityThreshold?: "critical" | "high" | "medium" | "low";
28
+ securityScanPatterns?: {
29
+ hardcodedCredentials?: boolean;
30
+ sqlInjection?: boolean;
31
+ xss?: boolean;
32
+ insecureCrypto?: boolean;
33
+ pathTraversal?: boolean;
34
+ commandInjection?: boolean;
35
+ };
36
+ };
37
+ }
38
+
39
+ // Security pattern definitions (OWASP Top 10 inspired)
40
+ const SECURITY_PATTERNS = {
41
+ hardcodedCredentials: [
42
+ {
43
+ pattern: /(password|passwd|pwd)\s*[=:]\s*["'][^"']+["']/gi,
44
+ type: "Hardcoded Password",
45
+ severity: "critical" as const,
46
+ suggestion: "Use environment variables or secure secret management (e.g., AWS Secrets Manager, Azure Key Vault)"
47
+ },
48
+ {
49
+ pattern: /(api[_-]?key|apikey)\s*[=:]\s*["'][^"']+["']/gi,
50
+ type: "Hardcoded API Key",
51
+ severity: "critical" as const,
52
+ suggestion: "Store API keys in environment variables or secure vault"
53
+ },
54
+ {
55
+ pattern: /(secret|token|auth)\s*[=:]\s*["'][^"']+["']/gi,
56
+ type: "Hardcoded Secret/Token",
57
+ severity: "critical" as const,
58
+ suggestion: "Use environment variables for secrets"
59
+ },
60
+ {
61
+ pattern: /AWS[_A-Z]*KEY|AWS[_A-Z]*SECRET/gi,
62
+ type: "AWS Credentials",
63
+ severity: "critical" as const,
64
+ suggestion: "Use IAM roles or AWS Secrets Manager"
65
+ }
66
+ ],
67
+ sqlInjection: [
68
+ {
69
+ pattern: /(execute|query|raw)\s*\(\s*`[^`]*\$\{[^}]+\}/gi,
70
+ type: "Potential SQL Injection",
71
+ severity: "critical" as const,
72
+ suggestion: "Use parameterized queries or ORM methods instead of string interpolation"
73
+ },
74
+ {
75
+ pattern: /execute\s*\(\s*["'][^"']*["']\s*\+/gi,
76
+ type: "SQL String Concatenation",
77
+ severity: "critical" as const,
78
+ suggestion: "Use parameterized queries"
79
+ }
80
+ ],
81
+ xss: [
82
+ {
83
+ pattern: /dangerouslySetInnerHTML\s*=/gi,
84
+ type: "Dangerous HTML Rendering",
85
+ severity: "high" as const,
86
+ suggestion: "Avoid dangerouslySetInnerHTML or sanitize input with DOMPurify"
87
+ },
88
+ {
89
+ pattern: /innerHTML\s*=/gi,
90
+ type: "Inner HTML Assignment",
91
+ severity: "high" as const,
92
+ suggestion: "Use textContent or sanitize HTML before assignment"
93
+ },
94
+ {
95
+ pattern: /eval\s*\(/gi,
96
+ type: "Eval Usage",
97
+ severity: "high" as const,
98
+ suggestion: "Avoid eval() - use safer alternatives"
99
+ }
100
+ ],
101
+ insecureCrypto: [
102
+ {
103
+ pattern: /crypto\s*\.\s*createHash\s*\(\s*["']md5["']/gi,
104
+ type: "Weak Hash Algorithm (MD5)",
105
+ severity: "high" as const,
106
+ suggestion: "Use SHA-256 or stronger (crypto.createHash('sha256'))"
107
+ },
108
+ {
109
+ pattern: /crypto\s*\.\s*createHash\s*\(\s*["']sha1["']/gi,
110
+ type: "Weak Hash Algorithm (SHA1)",
111
+ severity: "medium" as const,
112
+ suggestion: "Use SHA-256 or stronger for security-critical applications"
113
+ },
114
+ {
115
+ pattern: /Math\.random\s*\(/gi,
116
+ type: "Insecure Random Number Generator",
117
+ severity: "medium" as const,
118
+ suggestion: "Use crypto.randomBytes() for security-sensitive randomness"
119
+ }
120
+ ],
121
+ pathTraversal: [
122
+ {
123
+ pattern: /fs\s*\.\s*(readFile|writeFile|appendFile)\s*\([^)]*\+/gi,
124
+ type: "Potential Path Traversal",
125
+ severity: "high" as const,
126
+ suggestion: "Validate and sanitize file paths, use path.resolve() and path.normalize()"
127
+ },
128
+ {
129
+ pattern: /require\s*\(\s*[^)]*\+/gi,
130
+ type: "Dynamic Require with User Input",
131
+ severity: "high" as const,
132
+ suggestion: "Avoid dynamic require with user-controlled paths"
133
+ }
134
+ ],
135
+ commandInjection: [
136
+ {
137
+ pattern: /exec\s*\(\s*`[^`]*\$\{[^}]+\}/gi,
138
+ type: "Command Injection via Template Literal",
139
+ severity: "critical" as const,
140
+ suggestion: "Avoid shell commands with user input, use execFile with arguments array"
141
+ },
142
+ {
143
+ pattern: /spawn\s*\(\s*[^)]*\+/gi,
144
+ type: "Command Injection via String Concatenation",
145
+ severity: "critical" as const,
146
+ suggestion: "Use spawn with arguments array instead of string concatenation"
147
+ },
148
+ {
149
+ pattern: /child_process\s*\.\s*exec\s*\(/gi,
150
+ type: "Unsafe exec Usage",
151
+ severity: "medium" as const,
152
+ suggestion: "Prefer execFile or spawn with argument separation"
153
+ }
154
+ ]
155
+ };
156
+
157
+ const input = await readHookInput();
158
+
159
+ // Load configuration
160
+ const configPath = join(stateDir(input), 'hooks.config.json');
161
+ const config: SecurityConfig = existsSync(configPath)
162
+ ? JSON.parse(readFileSync(configPath, 'utf-8'))
163
+ : {};
164
+
165
+ const securityConfig = {
166
+ enableSecurityReview: config.validation?.enableSecurityReview !== false,
167
+ severityThreshold: config.validation?.securitySeverityThreshold || "high",
168
+ scanPatterns: config.validation?.securityScanPatterns || {
169
+ hardcodedCredentials: true,
170
+ sqlInjection: true,
171
+ xss: true,
172
+ insecureCrypto: true,
173
+ pathTraversal: true,
174
+ commandInjection: true
175
+ }
176
+ };
177
+
178
+ if (!securityConfig.enableSecurityReview) {
179
+ console.error("[Security Review] ⚠ Security review disabled in config");
180
+ done();
181
+ }
182
+
183
+ console.error("[Security Review] Running automatic security scan...");
184
+
185
+ const findings: SecurityFinding[] = [];
186
+
187
+ // Scan the code Claude wrote (Write/Edit/MultiEdit) in this turn
188
+ const turn = loadCurrentTurn(input.transcript_path);
189
+ const codeToScan = turn ? writtenCode(turn.toolUses) : [];
190
+
191
+ // Scan code for security issues
192
+ function scanCodeForSecurity(code: string, file: string, patternCategory: string): SecurityFinding[] {
193
+ const categoryFindings: SecurityFinding[] = [];
194
+ const patterns = SECURITY_PATTERNS[patternCategory as keyof typeof SECURITY_PATTERNS];
195
+
196
+ if (!patterns || !securityConfig.scanPatterns[patternCategory as keyof typeof securityConfig.scanPatterns]) {
197
+ return categoryFindings;
198
+ }
199
+
200
+ for (const { pattern, type, severity, suggestion } of patterns) {
201
+ const matches = code.match(pattern);
202
+ if (matches) {
203
+ categoryFindings.push({
204
+ severity,
205
+ type,
206
+ description: `Found ${matches.length} occurrence(s) of potentially insecure pattern`,
207
+ location: file,
208
+ suggestion
209
+ });
210
+ }
211
+ }
212
+
213
+ return categoryFindings;
214
+ }
215
+
216
+ // If no code was written, skip scan
217
+ if (codeToScan.length === 0) {
218
+ console.error("[Security Review] ⚠ No code changes found to scan");
219
+ done();
220
+ }
221
+
222
+ // Run security scans on each written chunk
223
+ const patternCategories = Object.keys(SECURITY_PATTERNS);
224
+ for (const { file, code } of codeToScan) {
225
+ for (const category of patternCategories) {
226
+ findings.push(...scanCodeForSecurity(code, file, category));
227
+ }
228
+ }
229
+
230
+ // Filter by severity threshold
231
+ const severityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
232
+ const thresholdLevel = severityOrder[securityConfig.severityThreshold];
233
+
234
+ const filteredFindings = findings.filter(
235
+ finding => severityOrder[finding.severity] <= thresholdLevel
236
+ );
237
+
238
+ // Generate security report
239
+ const criticalCount = filteredFindings.filter(f => f.severity === "critical").length;
240
+ const highCount = filteredFindings.filter(f => f.severity === "high").length;
241
+ const mediumCount = filteredFindings.filter(f => f.severity === "medium").length;
242
+ const lowCount = filteredFindings.filter(f => f.severity === "low").length;
243
+
244
+ const reportPath = join(stateDir(input), 'security-report.md');
245
+
246
+ const securityReport = `## Security Scan Report
247
+
248
+ Generated: ${new Date().toISOString()}
249
+ Session: ${input.session_id}
250
+ Severity Threshold: ${securityConfig.severityThreshold}
251
+
252
+ ## Summary
253
+
254
+ - 🔴 Critical: ${criticalCount}
255
+ - 🟠 High: ${highCount}
256
+ - 🟡 Medium: ${mediumCount}
257
+ - 🟢 Low: ${lowCount}
258
+ - **Total Findings**: ${filteredFindings.length}
259
+
260
+ ## Findings
261
+
262
+ ${filteredFindings.length === 0 ? '✅ No security issues found!' : filteredFindings.map((finding, i) => `
263
+ ### ${i + 1}. ${finding.type}
264
+
265
+ **Severity**: ${finding.severity.toUpperCase()}
266
+
267
+ **File**: ${finding.location || 'unknown'}
268
+
269
+ **Description**: ${finding.description}
270
+
271
+ **Recommendation**: ${finding.suggestion}
272
+
273
+ ---
274
+ `).join('\n')}
275
+
276
+ ## Security Checklist (OWASP Top 10)
277
+
278
+ - [ ] All user input validated and sanitized
279
+ - [ ] SQL injection prevented (parameterized queries)
280
+ - [ ] XSS prevented (escaping/sanitization)
281
+ - [ ] Authentication/authorization verified
282
+ - [ ] No hardcoded credentials or secrets
283
+ - [ ] Error handling doesn't leak sensitive info
284
+ - [ ] Rate limiting on auth endpoints
285
+ - [ ] Secure random number generation used
286
+ - [ ] Strong cryptographic algorithms used
287
+ - [ ] File paths validated and normalized
288
+
289
+ ## Next Steps
290
+
291
+ ${criticalCount > 0 ? '🚨 CRITICAL issues found! Address immediately before deployment.' :
292
+ highCount > 0 ? '⚠️ HIGH severity issues found. Review and fix before production.' :
293
+ mediumCount > 0 ? '📋 Medium severity issues found. Schedule for next sprint.' :
294
+ lowCount > 0 ? '💡 Low severity issues found. Consider fixing for best practices.' :
295
+ '✅ No security issues detected at current threshold.'}
296
+ `;
297
+
298
+ writeFileSync(reportPath, securityReport);
299
+ console.error(`[Security Review] Report saved to ${reportPath}`);
300
+
301
+ // Provide feedback to user
302
+ if (criticalCount > 0 || highCount > 0) {
303
+ const summary = filteredFindings
304
+ .filter(f => f.severity === "critical" || f.severity === "high")
305
+ .map(f => `- [${f.severity.toUpperCase()}] ${f.type} in ${f.location}: ${f.suggestion}`)
306
+ .join('\n');
307
+ blockOrNotify(input, `🚨 Security scan found ${criticalCount} critical and ${highCount} high severity issues. Review and fix them (or confirm they are false positives). Full report: ${reportPath}\n\n${summary}`);
308
+ } else if (filteredFindings.length > 0) {
309
+ console.error(`[Security Review] ⚠️ Found ${filteredFindings.length} security finding(s). Report saved.`);
310
+ done();
311
+ } else {
312
+ console.error("[Security Review] ✓ No security issues found");
313
+ done();
314
+ }