@codebakers/mcp 5.2.0
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.
- package/INSTALL.md +221 -0
- package/LICENSE +21 -0
- package/README.md +412 -0
- package/dist/cli.d.ts +9 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +236 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +526 -0
- package/dist/index.js.map +1 -0
- package/dist/tools/analyze-mockups.d.ts +18 -0
- package/dist/tools/analyze-mockups.d.ts.map +1 -0
- package/dist/tools/analyze-mockups.js +497 -0
- package/dist/tools/analyze-mockups.js.map +1 -0
- package/dist/tools/autonomous-build.d.ts +19 -0
- package/dist/tools/autonomous-build.d.ts.map +1 -0
- package/dist/tools/autonomous-build.js +287 -0
- package/dist/tools/autonomous-build.js.map +1 -0
- package/dist/tools/check-gate.d.ts +21 -0
- package/dist/tools/check-gate.d.ts.map +1 -0
- package/dist/tools/check-gate.js +446 -0
- package/dist/tools/check-gate.js.map +1 -0
- package/dist/tools/check-scope.d.ts +22 -0
- package/dist/tools/check-scope.d.ts.map +1 -0
- package/dist/tools/check-scope.js +251 -0
- package/dist/tools/check-scope.js.map +1 -0
- package/dist/tools/deploy-vercel.d.ts +18 -0
- package/dist/tools/deploy-vercel.d.ts.map +1 -0
- package/dist/tools/deploy-vercel.js +178 -0
- package/dist/tools/deploy-vercel.js.map +1 -0
- package/dist/tools/diagnose-error.d.ts +20 -0
- package/dist/tools/diagnose-error.d.ts.map +1 -0
- package/dist/tools/diagnose-error.js +351 -0
- package/dist/tools/diagnose-error.js.map +1 -0
- package/dist/tools/enforce-feature.d.ts +25 -0
- package/dist/tools/enforce-feature.d.ts.map +1 -0
- package/dist/tools/enforce-feature.js +387 -0
- package/dist/tools/enforce-feature.js.map +1 -0
- package/dist/tools/execute-atomic-unit.d.ts +23 -0
- package/dist/tools/execute-atomic-unit.d.ts.map +1 -0
- package/dist/tools/execute-atomic-unit.js +107 -0
- package/dist/tools/execute-atomic-unit.js.map +1 -0
- package/dist/tools/fix-commit.d.ts +23 -0
- package/dist/tools/fix-commit.d.ts.map +1 -0
- package/dist/tools/fix-commit.js +213 -0
- package/dist/tools/fix-commit.js.map +1 -0
- package/dist/tools/fix-mockups.d.ts +21 -0
- package/dist/tools/fix-mockups.d.ts.map +1 -0
- package/dist/tools/fix-mockups.js +595 -0
- package/dist/tools/fix-mockups.js.map +1 -0
- package/dist/tools/generate-api-route.d.ts +18 -0
- package/dist/tools/generate-api-route.d.ts.map +1 -0
- package/dist/tools/generate-api-route.js +212 -0
- package/dist/tools/generate-api-route.js.map +1 -0
- package/dist/tools/generate-chatbot.d.ts +20 -0
- package/dist/tools/generate-chatbot.d.ts.map +1 -0
- package/dist/tools/generate-chatbot.js +555 -0
- package/dist/tools/generate-chatbot.js.map +1 -0
- package/dist/tools/generate-component.d.ts +18 -0
- package/dist/tools/generate-component.d.ts.map +1 -0
- package/dist/tools/generate-component.js +159 -0
- package/dist/tools/generate-component.js.map +1 -0
- package/dist/tools/generate-docs.d.ts +21 -0
- package/dist/tools/generate-docs.d.ts.map +1 -0
- package/dist/tools/generate-docs.js +782 -0
- package/dist/tools/generate-docs.js.map +1 -0
- package/dist/tools/generate-e2e-tests.d.ts +12 -0
- package/dist/tools/generate-e2e-tests.d.ts.map +1 -0
- package/dist/tools/generate-e2e-tests.js +37 -0
- package/dist/tools/generate-e2e-tests.js.map +1 -0
- package/dist/tools/generate-migration.d.ts +21 -0
- package/dist/tools/generate-migration.d.ts.map +1 -0
- package/dist/tools/generate-migration.js +94 -0
- package/dist/tools/generate-migration.js.map +1 -0
- package/dist/tools/generate-schema.d.ts +18 -0
- package/dist/tools/generate-schema.d.ts.map +1 -0
- package/dist/tools/generate-schema.js +422 -0
- package/dist/tools/generate-schema.js.map +1 -0
- package/dist/tools/generate-spec.d.ts +18 -0
- package/dist/tools/generate-spec.d.ts.map +1 -0
- package/dist/tools/generate-spec.js +446 -0
- package/dist/tools/generate-spec.js.map +1 -0
- package/dist/tools/generate-store-contracts.d.ts +17 -0
- package/dist/tools/generate-store-contracts.d.ts.map +1 -0
- package/dist/tools/generate-store-contracts.js +356 -0
- package/dist/tools/generate-store-contracts.js.map +1 -0
- package/dist/tools/generate-store.d.ts +16 -0
- package/dist/tools/generate-store.d.ts.map +1 -0
- package/dist/tools/generate-store.js +166 -0
- package/dist/tools/generate-store.js.map +1 -0
- package/dist/tools/generate-unit-tests.d.ts +14 -0
- package/dist/tools/generate-unit-tests.d.ts.map +1 -0
- package/dist/tools/generate-unit-tests.js +85 -0
- package/dist/tools/generate-unit-tests.js.map +1 -0
- package/dist/tools/get-context.d.ts +35 -0
- package/dist/tools/get-context.d.ts.map +1 -0
- package/dist/tools/get-context.js +367 -0
- package/dist/tools/get-context.js.map +1 -0
- package/dist/tools/init-session.d.ts +22 -0
- package/dist/tools/init-session.d.ts.map +1 -0
- package/dist/tools/init-session.js +232 -0
- package/dist/tools/init-session.js.map +1 -0
- package/dist/tools/map-dependencies.d.ts +25 -0
- package/dist/tools/map-dependencies.d.ts.map +1 -0
- package/dist/tools/map-dependencies.js +480 -0
- package/dist/tools/map-dependencies.js.map +1 -0
- package/dist/tools/optimize-performance.d.ts +18 -0
- package/dist/tools/optimize-performance.d.ts.map +1 -0
- package/dist/tools/optimize-performance.js +285 -0
- package/dist/tools/optimize-performance.js.map +1 -0
- package/dist/tools/run-interview.d.ts +23 -0
- package/dist/tools/run-interview.d.ts.map +1 -0
- package/dist/tools/run-interview.js +371 -0
- package/dist/tools/run-interview.js.map +1 -0
- package/dist/tools/run-tests.d.ts +12 -0
- package/dist/tools/run-tests.d.ts.map +1 -0
- package/dist/tools/run-tests.js +30 -0
- package/dist/tools/run-tests.js.map +1 -0
- package/dist/tools/scan-security.d.ts +19 -0
- package/dist/tools/scan-security.d.ts.map +1 -0
- package/dist/tools/scan-security.js +358 -0
- package/dist/tools/scan-security.js.map +1 -0
- package/dist/tools/validate-accessibility.d.ts +18 -0
- package/dist/tools/validate-accessibility.d.ts.map +1 -0
- package/dist/tools/validate-accessibility.js +251 -0
- package/dist/tools/validate-accessibility.js.map +1 -0
- package/dist/tools/validate-mockups.d.ts +21 -0
- package/dist/tools/validate-mockups.d.ts.map +1 -0
- package/dist/tools/validate-mockups.js +433 -0
- package/dist/tools/validate-mockups.js.map +1 -0
- package/dist/tools/verify-completeness.d.ts +13 -0
- package/dist/tools/verify-completeness.d.ts.map +1 -0
- package/dist/tools/verify-completeness.js +68 -0
- package/dist/tools/verify-completeness.js.map +1 -0
- package/dist/tools/verify-mockups.d.ts +14 -0
- package/dist/tools/verify-mockups.d.ts.map +1 -0
- package/dist/tools/verify-mockups.js +85 -0
- package/dist/tools/verify-mockups.js.map +1 -0
- package/package.json +50 -0
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* codebakers_scan_security
|
|
3
|
+
*
|
|
4
|
+
* Security Vulnerability Scanner
|
|
5
|
+
*
|
|
6
|
+
* Scans for:
|
|
7
|
+
* - Dependency vulnerabilities (npm audit)
|
|
8
|
+
* - XSS vulnerabilities
|
|
9
|
+
* - SQL injection risks
|
|
10
|
+
* - Exposed secrets
|
|
11
|
+
* - Insecure authentication patterns
|
|
12
|
+
* - Missing security headers
|
|
13
|
+
*/
|
|
14
|
+
import * as fs from 'fs/promises';
|
|
15
|
+
import * as path from 'path';
|
|
16
|
+
import { execSync } from 'child_process';
|
|
17
|
+
export async function scanSecurity(args = {}) {
|
|
18
|
+
const cwd = process.cwd();
|
|
19
|
+
const { block_on_critical = true } = args;
|
|
20
|
+
console.error('🍞 CodeBakers: Scanning Security');
|
|
21
|
+
try {
|
|
22
|
+
const issues = [];
|
|
23
|
+
// Scan 1: Dependency vulnerabilities
|
|
24
|
+
const depIssues = await scanDependencies(cwd);
|
|
25
|
+
issues.push(...depIssues);
|
|
26
|
+
// Scan 2: Code vulnerabilities
|
|
27
|
+
const codeIssues = await scanCodeSecurity(cwd);
|
|
28
|
+
issues.push(...codeIssues);
|
|
29
|
+
// Scan 3: Environment variables
|
|
30
|
+
const envIssues = await scanEnvSecurity(cwd);
|
|
31
|
+
issues.push(...envIssues);
|
|
32
|
+
// Scan 4: API routes security
|
|
33
|
+
const apiIssues = await scanApiSecurity(cwd);
|
|
34
|
+
issues.push(...apiIssues);
|
|
35
|
+
// Calculate risk score
|
|
36
|
+
const criticalCount = issues.filter(i => i.severity === 'critical').length;
|
|
37
|
+
const highCount = issues.filter(i => i.severity === 'high').length;
|
|
38
|
+
// Generate report
|
|
39
|
+
const report = generateSecurityReport(issues, block_on_critical);
|
|
40
|
+
// Save detailed report
|
|
41
|
+
const reportPath = path.join(cwd, '.codebakers', 'SECURITY-REPORT.md');
|
|
42
|
+
await fs.mkdir(path.dirname(reportPath), { recursive: true });
|
|
43
|
+
await fs.writeFile(reportPath, generateDetailedSecurityReport(issues), 'utf-8');
|
|
44
|
+
return report;
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
return `🍞 CodeBakers: Security Scan Failed\n\nError: ${error instanceof Error ? error.message : String(error)}`;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
async function scanDependencies(cwd) {
|
|
51
|
+
const issues = [];
|
|
52
|
+
try {
|
|
53
|
+
// Run npm audit
|
|
54
|
+
const output = execSync('npm audit --json', {
|
|
55
|
+
cwd,
|
|
56
|
+
encoding: 'utf-8',
|
|
57
|
+
stdio: ['pipe', 'pipe', 'ignore']
|
|
58
|
+
});
|
|
59
|
+
const audit = JSON.parse(output);
|
|
60
|
+
if (audit.vulnerabilities) {
|
|
61
|
+
for (const [pkg, vulnData] of Object.entries(audit.vulnerabilities)) {
|
|
62
|
+
const data = vulnData;
|
|
63
|
+
const severity = data.severity;
|
|
64
|
+
issues.push({
|
|
65
|
+
severity,
|
|
66
|
+
category: 'Dependencies',
|
|
67
|
+
title: `Vulnerable dependency: ${pkg}`,
|
|
68
|
+
description: data.via?.[0]?.title || `${pkg} has known vulnerabilities`,
|
|
69
|
+
fix: `Run: npm audit fix${severity === 'critical' || severity === 'high' ? ' --force' : ''}`
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
// npm audit failed or no vulnerabilities
|
|
76
|
+
}
|
|
77
|
+
return issues;
|
|
78
|
+
}
|
|
79
|
+
async function scanCodeSecurity(cwd) {
|
|
80
|
+
const issues = [];
|
|
81
|
+
const srcDir = path.join(cwd, 'src');
|
|
82
|
+
try {
|
|
83
|
+
await scanDirectorySecurity(srcDir, issues);
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
// src/ doesn't exist
|
|
87
|
+
}
|
|
88
|
+
return issues;
|
|
89
|
+
}
|
|
90
|
+
async function scanDirectorySecurity(dir, issues) {
|
|
91
|
+
try {
|
|
92
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
93
|
+
for (const entry of entries) {
|
|
94
|
+
const fullPath = path.join(dir, entry.name);
|
|
95
|
+
if (entry.isDirectory()) {
|
|
96
|
+
await scanDirectorySecurity(fullPath, issues);
|
|
97
|
+
}
|
|
98
|
+
else if (entry.name.match(/\.(ts|tsx|js|jsx)$/)) {
|
|
99
|
+
await scanFileSecurity(fullPath, issues);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
// Directory doesn't exist
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
async function scanFileSecurity(file, issues) {
|
|
108
|
+
const content = await fs.readFile(file, 'utf-8');
|
|
109
|
+
const lines = content.split('\n');
|
|
110
|
+
for (let i = 0; i < lines.length; i++) {
|
|
111
|
+
const line = lines[i];
|
|
112
|
+
const lineNumber = i + 1;
|
|
113
|
+
// Check 1: Dangerous innerHTML usage
|
|
114
|
+
if (line.includes('dangerouslySetInnerHTML') && !line.includes('DOMPurify')) {
|
|
115
|
+
issues.push({
|
|
116
|
+
severity: 'high',
|
|
117
|
+
category: 'XSS',
|
|
118
|
+
title: 'Potential XSS vulnerability',
|
|
119
|
+
description: 'dangerouslySetInnerHTML without sanitization',
|
|
120
|
+
file,
|
|
121
|
+
line: lineNumber,
|
|
122
|
+
fix: 'Use DOMPurify to sanitize HTML before rendering'
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
// Check 2: eval() usage
|
|
126
|
+
if (line.includes('eval(')) {
|
|
127
|
+
issues.push({
|
|
128
|
+
severity: 'critical',
|
|
129
|
+
category: 'Code Injection',
|
|
130
|
+
title: 'eval() usage detected',
|
|
131
|
+
description: 'eval() can execute arbitrary code',
|
|
132
|
+
file,
|
|
133
|
+
line: lineNumber,
|
|
134
|
+
fix: 'Avoid eval() - use safe alternatives'
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
// Check 3: Weak password regex
|
|
138
|
+
if (line.match(/password.*length.*[<>]\s*[1-5]/) || line.match(/password.*minLength.*[1-5]/)) {
|
|
139
|
+
issues.push({
|
|
140
|
+
severity: 'medium',
|
|
141
|
+
category: 'Authentication',
|
|
142
|
+
title: 'Weak password requirements',
|
|
143
|
+
description: 'Password minimum length is too short',
|
|
144
|
+
file,
|
|
145
|
+
line: lineNumber,
|
|
146
|
+
fix: 'Require passwords to be at least 8 characters'
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
// Check 4: Hardcoded secrets
|
|
150
|
+
const secretPatterns = [
|
|
151
|
+
/api[_-]?key\s*=\s*['"][a-zA-Z0-9]{20,}['"]/i,
|
|
152
|
+
/secret\s*=\s*['"][a-zA-Z0-9]{20,}['"]/i,
|
|
153
|
+
/password\s*=\s*['"][^'"]{5,}['"]/i,
|
|
154
|
+
/token\s*=\s*['"][a-zA-Z0-9]{20,}['"]/i
|
|
155
|
+
];
|
|
156
|
+
for (const pattern of secretPatterns) {
|
|
157
|
+
if (pattern.test(line) && !line.includes('process.env')) {
|
|
158
|
+
issues.push({
|
|
159
|
+
severity: 'critical',
|
|
160
|
+
category: 'Secrets',
|
|
161
|
+
title: 'Hardcoded secret detected',
|
|
162
|
+
description: 'Secret value found in code',
|
|
163
|
+
file,
|
|
164
|
+
line: lineNumber,
|
|
165
|
+
fix: 'Move secrets to environment variables'
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
// Check 5: SQL injection risk (if using raw SQL)
|
|
170
|
+
if (line.includes('queryRawUnsafe') || line.includes('executeRawUnsafe')) {
|
|
171
|
+
issues.push({
|
|
172
|
+
severity: 'critical',
|
|
173
|
+
category: 'SQL Injection',
|
|
174
|
+
title: 'SQL injection risk',
|
|
175
|
+
description: 'Using raw SQL queries without parameterization',
|
|
176
|
+
file,
|
|
177
|
+
line: lineNumber,
|
|
178
|
+
fix: 'Use parameterized queries or query builder'
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
// Check 6: Missing authentication check
|
|
182
|
+
if ((line.includes('async function POST') || line.includes('async function DELETE')) && !content.includes('auth') && !content.includes('session')) {
|
|
183
|
+
issues.push({
|
|
184
|
+
severity: 'high',
|
|
185
|
+
category: 'Authentication',
|
|
186
|
+
title: 'Missing authentication check',
|
|
187
|
+
description: 'API route might be missing auth verification',
|
|
188
|
+
file,
|
|
189
|
+
line: lineNumber,
|
|
190
|
+
fix: 'Add authentication check before processing request'
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
async function scanEnvSecurity(cwd) {
|
|
196
|
+
const issues = [];
|
|
197
|
+
// Check if .env is in .gitignore
|
|
198
|
+
const gitignorePath = path.join(cwd, '.gitignore');
|
|
199
|
+
try {
|
|
200
|
+
const gitignore = await fs.readFile(gitignorePath, 'utf-8');
|
|
201
|
+
if (!gitignore.includes('.env')) {
|
|
202
|
+
issues.push({
|
|
203
|
+
severity: 'high',
|
|
204
|
+
category: 'Secrets',
|
|
205
|
+
title: '.env not in .gitignore',
|
|
206
|
+
description: 'Environment variables might be committed to git',
|
|
207
|
+
file: '.gitignore',
|
|
208
|
+
fix: 'Add .env to .gitignore'
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
// .gitignore doesn't exist
|
|
214
|
+
}
|
|
215
|
+
// Check for .env.example
|
|
216
|
+
const envExamplePath = path.join(cwd, '.env.example');
|
|
217
|
+
try {
|
|
218
|
+
await fs.access(envExamplePath);
|
|
219
|
+
}
|
|
220
|
+
catch {
|
|
221
|
+
issues.push({
|
|
222
|
+
severity: 'low',
|
|
223
|
+
category: 'Configuration',
|
|
224
|
+
title: 'Missing .env.example',
|
|
225
|
+
description: 'No example environment file for developers',
|
|
226
|
+
fix: 'Create .env.example with placeholder values'
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
return issues;
|
|
230
|
+
}
|
|
231
|
+
async function scanApiSecurity(cwd) {
|
|
232
|
+
const issues = [];
|
|
233
|
+
const apiDir = path.join(cwd, 'src', 'app', 'api');
|
|
234
|
+
try {
|
|
235
|
+
await scanApiRoutes(apiDir, issues);
|
|
236
|
+
}
|
|
237
|
+
catch {
|
|
238
|
+
// API directory doesn't exist
|
|
239
|
+
}
|
|
240
|
+
return issues;
|
|
241
|
+
}
|
|
242
|
+
async function scanApiRoutes(dir, issues) {
|
|
243
|
+
try {
|
|
244
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
245
|
+
for (const entry of entries) {
|
|
246
|
+
const fullPath = path.join(dir, entry.name);
|
|
247
|
+
if (entry.isDirectory()) {
|
|
248
|
+
await scanApiRoutes(fullPath, issues);
|
|
249
|
+
}
|
|
250
|
+
else if (entry.name === 'route.ts' || entry.name === 'route.js') {
|
|
251
|
+
const content = await fs.readFile(fullPath, 'utf-8');
|
|
252
|
+
// Check for CORS misconfiguration
|
|
253
|
+
if (content.includes("'Access-Control-Allow-Origin', '*'")) {
|
|
254
|
+
issues.push({
|
|
255
|
+
severity: 'medium',
|
|
256
|
+
category: 'CORS',
|
|
257
|
+
title: 'Permissive CORS policy',
|
|
258
|
+
description: 'Allow-Origin set to * (allows all domains)',
|
|
259
|
+
file: fullPath,
|
|
260
|
+
fix: 'Restrict CORS to specific domains'
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
// Check for rate limiting
|
|
264
|
+
if (!content.includes('ratelimit') && !content.includes('rate-limit')) {
|
|
265
|
+
issues.push({
|
|
266
|
+
severity: 'medium',
|
|
267
|
+
category: 'Rate Limiting',
|
|
268
|
+
title: 'Missing rate limiting',
|
|
269
|
+
description: 'API route might be vulnerable to abuse',
|
|
270
|
+
file: fullPath,
|
|
271
|
+
fix: 'Add rate limiting middleware'
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
catch {
|
|
278
|
+
// Directory doesn't exist
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
function generateSecurityReport(issues, blockOnCritical) {
|
|
282
|
+
const critical = issues.filter(i => i.severity === 'critical').length;
|
|
283
|
+
const high = issues.filter(i => i.severity === 'high').length;
|
|
284
|
+
const medium = issues.filter(i => i.severity === 'medium').length;
|
|
285
|
+
const low = issues.filter(i => i.severity === 'low').length;
|
|
286
|
+
const blocked = blockOnCritical && critical > 0;
|
|
287
|
+
let report = `🍞 CodeBakers: Security Scan\n\n`;
|
|
288
|
+
report += `**Status:** ${blocked ? '❌ BLOCKED' : critical === 0 && high === 0 ? '✅ PASS' : '⚠️ WARNINGS'}\n\n`;
|
|
289
|
+
report += `## Vulnerabilities\n\n`;
|
|
290
|
+
report += `- Critical: ${critical} ${critical === 0 ? '✅' : '❌'}\n`;
|
|
291
|
+
report += `- High: ${high} ${high === 0 ? '✅' : '⚠️'}\n`;
|
|
292
|
+
report += `- Medium: ${medium}\n`;
|
|
293
|
+
report += `- Low: ${low}\n\n`;
|
|
294
|
+
if (critical > 0) {
|
|
295
|
+
report += `## 🚨 Critical Issues (Must Fix)\n\n`;
|
|
296
|
+
for (const issue of issues.filter(i => i.severity === 'critical').slice(0, 5)) {
|
|
297
|
+
report += `**${issue.title}**\n`;
|
|
298
|
+
report += `- Category: ${issue.category}\n`;
|
|
299
|
+
report += `- ${issue.description}\n`;
|
|
300
|
+
if (issue.file) {
|
|
301
|
+
report += `- File: ${path.basename(issue.file)}${issue.line ? `:${issue.line}` : ''}\n`;
|
|
302
|
+
}
|
|
303
|
+
report += `- Fix: ${issue.fix}\n\n`;
|
|
304
|
+
}
|
|
305
|
+
if (critical > 5) {
|
|
306
|
+
report += `... and ${critical - 5} more critical issues\n\n`;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
if (high > 0 && critical === 0) {
|
|
310
|
+
report += `## ⚠️ High Severity Issues\n\n`;
|
|
311
|
+
for (const issue of issues.filter(i => i.severity === 'high').slice(0, 3)) {
|
|
312
|
+
report += `- ${issue.title}: ${issue.description}\n`;
|
|
313
|
+
}
|
|
314
|
+
report += `\n`;
|
|
315
|
+
}
|
|
316
|
+
if (blocked) {
|
|
317
|
+
report += `## ❌ Deployment Blocked\n\n`;
|
|
318
|
+
report += `Critical security issues must be fixed before deployment.\n\n`;
|
|
319
|
+
report += `**Next steps:**\n`;
|
|
320
|
+
report += `1. Fix critical issues above\n`;
|
|
321
|
+
report += `2. Re-run: codebakers_scan_security\n`;
|
|
322
|
+
report += `3. Proceed to deployment\n\n`;
|
|
323
|
+
}
|
|
324
|
+
else if (critical === 0 && high === 0) {
|
|
325
|
+
report += `## ✅ Security Check Passed\n\n`;
|
|
326
|
+
report += `No critical or high severity issues found.\n\n`;
|
|
327
|
+
}
|
|
328
|
+
report += `**Full report:** .codebakers/SECURITY-REPORT.md\n`;
|
|
329
|
+
return report;
|
|
330
|
+
}
|
|
331
|
+
function generateDetailedSecurityReport(issues) {
|
|
332
|
+
let report = `# Security Scan Report\n\n`;
|
|
333
|
+
report += `**Generated:** ${new Date().toISOString()}\n\n`;
|
|
334
|
+
report += `## Summary\n\n`;
|
|
335
|
+
report += `| Severity | Count |\n`;
|
|
336
|
+
report += `|----------|-------|\n`;
|
|
337
|
+
report += `| Critical | ${issues.filter(i => i.severity === 'critical').length} |\n`;
|
|
338
|
+
report += `| High | ${issues.filter(i => i.severity === 'high').length} |\n`;
|
|
339
|
+
report += `| Medium | ${issues.filter(i => i.severity === 'medium').length} |\n`;
|
|
340
|
+
report += `| Low | ${issues.filter(i => i.severity === 'low').length} |\n\n`;
|
|
341
|
+
for (const severity of ['critical', 'high', 'medium', 'low']) {
|
|
342
|
+
const filtered = issues.filter(i => i.severity === severity);
|
|
343
|
+
if (filtered.length === 0)
|
|
344
|
+
continue;
|
|
345
|
+
report += `## ${severity.charAt(0).toUpperCase() + severity.slice(1)} Issues\n\n`;
|
|
346
|
+
for (const issue of filtered) {
|
|
347
|
+
report += `### ${issue.title}\n\n`;
|
|
348
|
+
report += `- **Category:** ${issue.category}\n`;
|
|
349
|
+
report += `- **Description:** ${issue.description}\n`;
|
|
350
|
+
if (issue.file) {
|
|
351
|
+
report += `- **File:** ${issue.file}${issue.line ? `:${issue.line}` : ''}\n`;
|
|
352
|
+
}
|
|
353
|
+
report += `- **Fix:** ${issue.fix}\n\n`;
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
return report;
|
|
357
|
+
}
|
|
358
|
+
//# sourceMappingURL=scan-security.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scan-security.js","sourceRoot":"","sources":["../../src/tools/scan-security.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* codebakers_validate_accessibility
|
|
3
|
+
*
|
|
4
|
+
* Accessibility Validation - WCAG Compliance
|
|
5
|
+
*
|
|
6
|
+
* Scans components for accessibility violations:
|
|
7
|
+
* - Missing ARIA labels
|
|
8
|
+
* - Low contrast ratios
|
|
9
|
+
* - Missing alt text
|
|
10
|
+
* - Keyboard navigation issues
|
|
11
|
+
* - Screen reader compatibility
|
|
12
|
+
*/
|
|
13
|
+
interface A11yArgs {
|
|
14
|
+
threshold?: number;
|
|
15
|
+
}
|
|
16
|
+
export declare function validateAccessibility(args?: A11yArgs): Promise<string>;
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=validate-accessibility.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validate-accessibility.d.ts","sourceRoot":"","sources":["../../src/tools/validate-accessibility.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAKH,UAAU,QAAQ;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAWD,wBAAsB,qBAAqB,CAAC,IAAI,GAAE,QAAa,GAAG,OAAO,CAAC,MAAM,CAAC,CAmChF"}
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* codebakers_validate_accessibility
|
|
3
|
+
*
|
|
4
|
+
* Accessibility Validation - WCAG Compliance
|
|
5
|
+
*
|
|
6
|
+
* Scans components for accessibility violations:
|
|
7
|
+
* - Missing ARIA labels
|
|
8
|
+
* - Low contrast ratios
|
|
9
|
+
* - Missing alt text
|
|
10
|
+
* - Keyboard navigation issues
|
|
11
|
+
* - Screen reader compatibility
|
|
12
|
+
*/
|
|
13
|
+
import * as fs from 'fs/promises';
|
|
14
|
+
import * as path from 'path';
|
|
15
|
+
export async function validateAccessibility(args = {}) {
|
|
16
|
+
const cwd = process.cwd();
|
|
17
|
+
const { threshold = 90 } = args;
|
|
18
|
+
console.error('🍞 CodeBakers: Validating Accessibility');
|
|
19
|
+
try {
|
|
20
|
+
const issues = [];
|
|
21
|
+
// Scan all component files
|
|
22
|
+
const components = await findComponents(cwd);
|
|
23
|
+
for (const file of components) {
|
|
24
|
+
const content = await fs.readFile(file, 'utf-8');
|
|
25
|
+
const fileIssues = await scanFile(file, content);
|
|
26
|
+
issues.push(...fileIssues);
|
|
27
|
+
}
|
|
28
|
+
// Calculate score
|
|
29
|
+
const criticalCount = issues.filter(i => i.severity === 'critical').length;
|
|
30
|
+
const warningCount = issues.filter(i => i.severity === 'warning').length;
|
|
31
|
+
const score = calculateA11yScore(components.length, criticalCount, warningCount);
|
|
32
|
+
// Generate report
|
|
33
|
+
const report = generateA11yReport(score, threshold, issues);
|
|
34
|
+
// Save detailed report
|
|
35
|
+
const reportPath = path.join(cwd, '.codebakers', 'ACCESSIBILITY-REPORT.md');
|
|
36
|
+
await fs.mkdir(path.dirname(reportPath), { recursive: true });
|
|
37
|
+
await fs.writeFile(reportPath, generateDetailedReport(score, issues), 'utf-8');
|
|
38
|
+
return report;
|
|
39
|
+
}
|
|
40
|
+
catch (error) {
|
|
41
|
+
return `🍞 CodeBakers: Accessibility Validation Failed\n\nError: ${error instanceof Error ? error.message : String(error)}`;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
async function findComponents(cwd) {
|
|
45
|
+
const components = [];
|
|
46
|
+
const srcDir = path.join(cwd, 'src');
|
|
47
|
+
try {
|
|
48
|
+
await scanDirectory(srcDir, components);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
// src/ might not exist yet
|
|
52
|
+
}
|
|
53
|
+
return components;
|
|
54
|
+
}
|
|
55
|
+
async function scanDirectory(dir, results) {
|
|
56
|
+
try {
|
|
57
|
+
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
58
|
+
for (const entry of entries) {
|
|
59
|
+
const fullPath = path.join(dir, entry.name);
|
|
60
|
+
if (entry.isDirectory()) {
|
|
61
|
+
await scanDirectory(fullPath, results);
|
|
62
|
+
}
|
|
63
|
+
else if (entry.name.match(/\.(tsx|jsx)$/)) {
|
|
64
|
+
results.push(fullPath);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
catch {
|
|
69
|
+
// Directory doesn't exist or permission denied
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
async function scanFile(file, content) {
|
|
73
|
+
const issues = [];
|
|
74
|
+
const lines = content.split('\n');
|
|
75
|
+
for (let i = 0; i < lines.length; i++) {
|
|
76
|
+
const line = lines[i];
|
|
77
|
+
const lineNumber = i + 1;
|
|
78
|
+
// Check 1: Images without alt text
|
|
79
|
+
if (line.includes('<img') && !line.includes('alt=')) {
|
|
80
|
+
issues.push({
|
|
81
|
+
file,
|
|
82
|
+
line: lineNumber,
|
|
83
|
+
severity: 'critical',
|
|
84
|
+
rule: 'WCAG 1.1.1',
|
|
85
|
+
message: 'Image missing alt attribute',
|
|
86
|
+
fix: 'Add alt="descriptive text" to <img> tag'
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
// Check 2: Buttons without accessible labels
|
|
90
|
+
if (line.match(/<button[^>]*>[\s]*<[^>]*\/>/)) {
|
|
91
|
+
if (!line.includes('aria-label') && !line.includes('title')) {
|
|
92
|
+
issues.push({
|
|
93
|
+
file,
|
|
94
|
+
line: lineNumber,
|
|
95
|
+
severity: 'critical',
|
|
96
|
+
rule: 'WCAG 4.1.2',
|
|
97
|
+
message: 'Button with icon only - missing accessible label',
|
|
98
|
+
fix: 'Add aria-label="action description" to button'
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// Check 3: Input fields without labels
|
|
103
|
+
if (line.includes('<input') && !line.includes('aria-label') && !line.includes('placeholder')) {
|
|
104
|
+
const hasLabel = lines.slice(Math.max(0, i - 3), i).some(l => l.includes('<label'));
|
|
105
|
+
if (!hasLabel) {
|
|
106
|
+
issues.push({
|
|
107
|
+
file,
|
|
108
|
+
line: lineNumber,
|
|
109
|
+
severity: 'critical',
|
|
110
|
+
rule: 'WCAG 3.3.2',
|
|
111
|
+
message: 'Input field missing label',
|
|
112
|
+
fix: 'Add <label> or aria-label attribute'
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// Check 4: Low contrast color combinations
|
|
117
|
+
const lowContrastPatterns = [
|
|
118
|
+
/text-gray-400.*bg-gray-300/,
|
|
119
|
+
/text-gray-300.*bg-white/,
|
|
120
|
+
/text-yellow-200.*bg-yellow-100/
|
|
121
|
+
];
|
|
122
|
+
for (const pattern of lowContrastPatterns) {
|
|
123
|
+
if (pattern.test(line)) {
|
|
124
|
+
issues.push({
|
|
125
|
+
file,
|
|
126
|
+
line: lineNumber,
|
|
127
|
+
severity: 'warning',
|
|
128
|
+
rule: 'WCAG 1.4.3',
|
|
129
|
+
message: 'Potentially low contrast color combination',
|
|
130
|
+
fix: 'Ensure contrast ratio is at least 4.5:1 for normal text'
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
// Check 5: Missing heading hierarchy
|
|
135
|
+
if (line.match(/<h[1-6]/)) {
|
|
136
|
+
const headingLevel = parseInt(line.match(/<h([1-6])/)?.[1] || '1');
|
|
137
|
+
const previousHeadings = lines.slice(0, i).filter(l => l.match(/<h[1-6]/));
|
|
138
|
+
if (previousHeadings.length > 0) {
|
|
139
|
+
const lastHeading = previousHeadings[previousHeadings.length - 1];
|
|
140
|
+
const lastLevel = parseInt(lastHeading.match(/<h([1-6])/)?.[1] || '1');
|
|
141
|
+
if (headingLevel > lastLevel + 1) {
|
|
142
|
+
issues.push({
|
|
143
|
+
file,
|
|
144
|
+
line: lineNumber,
|
|
145
|
+
severity: 'warning',
|
|
146
|
+
rule: 'WCAG 1.3.1',
|
|
147
|
+
message: `Heading hierarchy skipped (h${lastLevel} to h${headingLevel})`,
|
|
148
|
+
fix: `Use sequential heading levels (h${lastLevel} to h${lastLevel + 1})`
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
// Check 6: Click handlers on non-interactive elements
|
|
154
|
+
if (line.match(/onClick.*<div|onClick.*<span/) && !line.includes('role=') && !line.includes('tabIndex')) {
|
|
155
|
+
issues.push({
|
|
156
|
+
file,
|
|
157
|
+
line: lineNumber,
|
|
158
|
+
severity: 'warning',
|
|
159
|
+
rule: 'WCAG 2.1.1',
|
|
160
|
+
message: 'Click handler on non-interactive element',
|
|
161
|
+
fix: 'Add role="button" and tabIndex={0} or use <button> instead'
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
// Check 7: Form submission without accessible feedback
|
|
165
|
+
if (line.includes('onSubmit') || line.includes('type="submit"')) {
|
|
166
|
+
const hasAriaLive = content.includes('aria-live') || content.includes('role="status"');
|
|
167
|
+
if (!hasAriaLive) {
|
|
168
|
+
issues.push({
|
|
169
|
+
file,
|
|
170
|
+
line: lineNumber,
|
|
171
|
+
severity: 'info',
|
|
172
|
+
rule: 'WCAG 4.1.3',
|
|
173
|
+
message: 'Form submission - consider adding status feedback',
|
|
174
|
+
fix: 'Add aria-live="polite" region for submission status'
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return issues;
|
|
180
|
+
}
|
|
181
|
+
function calculateA11yScore(totalComponents, critical, warnings) {
|
|
182
|
+
if (totalComponents === 0)
|
|
183
|
+
return 100;
|
|
184
|
+
const criticalPenalty = critical * 10;
|
|
185
|
+
const warningPenalty = warnings * 2;
|
|
186
|
+
const totalPenalty = criticalPenalty + warningPenalty;
|
|
187
|
+
const score = Math.max(0, 100 - totalPenalty);
|
|
188
|
+
return Math.round(score);
|
|
189
|
+
}
|
|
190
|
+
function generateA11yReport(score, threshold, issues) {
|
|
191
|
+
const critical = issues.filter(i => i.severity === 'critical').length;
|
|
192
|
+
const warnings = issues.filter(i => i.severity === 'warning').length;
|
|
193
|
+
const passed = score >= threshold;
|
|
194
|
+
let report = `🍞 CodeBakers: Accessibility Validation\n\n`;
|
|
195
|
+
report += `**Score:** ${score}/100 ${passed ? '✅' : '❌'}\n`;
|
|
196
|
+
report += `**Threshold:** ${threshold}/100\n`;
|
|
197
|
+
report += `**Status:** ${passed ? 'PASS' : 'FAIL'}\n\n`;
|
|
198
|
+
report += `## Issues Found\n\n`;
|
|
199
|
+
report += `- Critical: ${critical} ${critical === 0 ? '✅' : '❌'}\n`;
|
|
200
|
+
report += `- Warnings: ${warnings}\n`;
|
|
201
|
+
report += `- Info: ${issues.filter(i => i.severity === 'info').length}\n\n`;
|
|
202
|
+
if (critical > 0) {
|
|
203
|
+
report += `## Critical Issues (Must Fix)\n\n`;
|
|
204
|
+
for (const issue of issues.filter(i => i.severity === 'critical').slice(0, 5)) {
|
|
205
|
+
report += `**${path.basename(issue.file)}:${issue.line}**\n`;
|
|
206
|
+
report += `- Rule: ${issue.rule}\n`;
|
|
207
|
+
report += `- Issue: ${issue.message}\n`;
|
|
208
|
+
report += `- Fix: ${issue.fix}\n\n`;
|
|
209
|
+
}
|
|
210
|
+
if (critical > 5) {
|
|
211
|
+
report += `... and ${critical - 5} more critical issues\n\n`;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
if (!passed) {
|
|
215
|
+
report += `## Next Steps\n\n`;
|
|
216
|
+
report += `1. Fix critical issues above\n`;
|
|
217
|
+
report += `2. Run: codebakers_fix_accessibility (auto-fix)\n`;
|
|
218
|
+
report += `3. Re-validate: codebakers_validate_accessibility\n\n`;
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
report += `## ✅ Accessibility Compliant\n\n`;
|
|
222
|
+
report += `Your app meets WCAG ${threshold >= 95 ? 'AAA' : 'AA'} standards.\n\n`;
|
|
223
|
+
}
|
|
224
|
+
report += `**Full report:** .codebakers/ACCESSIBILITY-REPORT.md\n`;
|
|
225
|
+
return report;
|
|
226
|
+
}
|
|
227
|
+
function generateDetailedReport(score, issues) {
|
|
228
|
+
let report = `# Accessibility Report\n\n`;
|
|
229
|
+
report += `**Generated:** ${new Date().toISOString()}\n`;
|
|
230
|
+
report += `**Score:** ${score}/100\n\n`;
|
|
231
|
+
report += `## Summary\n\n`;
|
|
232
|
+
report += `| Severity | Count |\n`;
|
|
233
|
+
report += `|----------|-------|\n`;
|
|
234
|
+
report += `| Critical | ${issues.filter(i => i.severity === 'critical').length} |\n`;
|
|
235
|
+
report += `| Warning | ${issues.filter(i => i.severity === 'warning').length} |\n`;
|
|
236
|
+
report += `| Info | ${issues.filter(i => i.severity === 'info').length} |\n\n`;
|
|
237
|
+
for (const severity of ['critical', 'warning', 'info']) {
|
|
238
|
+
const filtered = issues.filter(i => i.severity === severity);
|
|
239
|
+
if (filtered.length === 0)
|
|
240
|
+
continue;
|
|
241
|
+
report += `## ${severity.charAt(0).toUpperCase() + severity.slice(1)} Issues\n\n`;
|
|
242
|
+
for (const issue of filtered) {
|
|
243
|
+
report += `### ${path.basename(issue.file)}:${issue.line}\n\n`;
|
|
244
|
+
report += `- **Rule:** ${issue.rule}\n`;
|
|
245
|
+
report += `- **Issue:** ${issue.message}\n`;
|
|
246
|
+
report += `- **Fix:** ${issue.fix}\n\n`;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return report;
|
|
250
|
+
}
|
|
251
|
+
//# sourceMappingURL=validate-accessibility.js.map
|