@nahisaho/musubix-core 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.
- package/dist/cli/commands/codegen.d.ts +125 -0
- package/dist/cli/commands/codegen.d.ts.map +1 -0
- package/dist/cli/commands/codegen.js +684 -0
- package/dist/cli/commands/codegen.js.map +1 -0
- package/dist/cli/commands/design.d.ts +158 -0
- package/dist/cli/commands/design.d.ts.map +1 -0
- package/dist/cli/commands/design.js +562 -0
- package/dist/cli/commands/design.js.map +1 -0
- package/dist/cli/commands/explain.d.ts +116 -0
- package/dist/cli/commands/explain.d.ts.map +1 -0
- package/dist/cli/commands/explain.js +419 -0
- package/dist/cli/commands/explain.js.map +1 -0
- package/dist/cli/commands/index.d.ts +13 -0
- package/dist/cli/commands/index.d.ts.map +1 -1
- package/dist/cli/commands/index.js +31 -7
- package/dist/cli/commands/index.js.map +1 -1
- package/dist/cli/commands/requirements.d.ts +103 -0
- package/dist/cli/commands/requirements.d.ts.map +1 -0
- package/dist/cli/commands/requirements.js +403 -0
- package/dist/cli/commands/requirements.js.map +1 -0
- package/dist/cli/commands/skills.d.ts +99 -0
- package/dist/cli/commands/skills.d.ts.map +1 -0
- package/dist/cli/commands/skills.js +363 -0
- package/dist/cli/commands/skills.js.map +1 -0
- package/dist/cli/commands/test.d.ts +113 -0
- package/dist/cli/commands/test.d.ts.map +1 -0
- package/dist/cli/commands/test.js +532 -0
- package/dist/cli/commands/test.js.map +1 -0
- package/dist/cli/commands/trace.d.ts +132 -0
- package/dist/cli/commands/trace.d.ts.map +1 -0
- package/dist/cli/commands/trace.js +553 -0
- package/dist/cli/commands/trace.js.map +1 -0
- package/package.json +5 -3
|
@@ -0,0 +1,684 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Codegen Command
|
|
3
|
+
*
|
|
4
|
+
* CLI commands for code generation and analysis
|
|
5
|
+
*
|
|
6
|
+
* @packageDocumentation
|
|
7
|
+
* @module cli/commands/codegen
|
|
8
|
+
*
|
|
9
|
+
* @see REQ-CLI-003 - Codegen CLI
|
|
10
|
+
* @see REQ-CG-001 - Code Generation
|
|
11
|
+
* @see REQ-CG-002 - Static Analysis
|
|
12
|
+
* @see DES-MUSUBIX-001 Section 16-C.3 - codegenコマンド設計
|
|
13
|
+
* @see TSK-071〜073 - Codegen CLI実装
|
|
14
|
+
*/
|
|
15
|
+
import { readFile, writeFile, mkdir, readdir, stat } from 'fs/promises';
|
|
16
|
+
import { resolve, dirname, extname, join } from 'path';
|
|
17
|
+
import { ExitCode, getGlobalOptions, outputResult } from '../base.js';
|
|
18
|
+
/**
|
|
19
|
+
* Security patterns to check
|
|
20
|
+
*/
|
|
21
|
+
const SECURITY_PATTERNS = [
|
|
22
|
+
{
|
|
23
|
+
pattern: /eval\s*\(/g,
|
|
24
|
+
type: 'Code Injection',
|
|
25
|
+
severity: 'critical',
|
|
26
|
+
cwe: 'CWE-94',
|
|
27
|
+
description: 'Use of eval() can lead to code injection vulnerabilities',
|
|
28
|
+
recommendation: 'Avoid eval(). Use safer alternatives like JSON.parse() for data parsing.',
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
pattern: /new\s+Function\s*\(/g,
|
|
32
|
+
type: 'Code Injection',
|
|
33
|
+
severity: 'high',
|
|
34
|
+
cwe: 'CWE-94',
|
|
35
|
+
description: 'Dynamic function creation can lead to code injection',
|
|
36
|
+
recommendation: 'Avoid dynamic function creation. Use predefined functions instead.',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
pattern: /innerHTML\s*=/g,
|
|
40
|
+
type: 'XSS',
|
|
41
|
+
severity: 'high',
|
|
42
|
+
cwe: 'CWE-79',
|
|
43
|
+
description: 'Direct innerHTML assignment can lead to XSS vulnerabilities',
|
|
44
|
+
recommendation: 'Use textContent or sanitize HTML before assignment.',
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
pattern: /document\.write\s*\(/g,
|
|
48
|
+
type: 'XSS',
|
|
49
|
+
severity: 'high',
|
|
50
|
+
cwe: 'CWE-79',
|
|
51
|
+
description: 'document.write can be exploited for XSS attacks',
|
|
52
|
+
recommendation: 'Use DOM manipulation methods instead of document.write.',
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
pattern: /password\s*[:=]\s*['"][^'"]+['"]/gi,
|
|
56
|
+
type: 'Hardcoded Credentials',
|
|
57
|
+
severity: 'critical',
|
|
58
|
+
cwe: 'CWE-798',
|
|
59
|
+
description: 'Hardcoded passwords detected',
|
|
60
|
+
recommendation: 'Use environment variables or secure credential stores.',
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
pattern: /api[_-]?key\s*[:=]\s*['"][^'"]+['"]/gi,
|
|
64
|
+
type: 'Hardcoded Credentials',
|
|
65
|
+
severity: 'high',
|
|
66
|
+
cwe: 'CWE-798',
|
|
67
|
+
description: 'Hardcoded API key detected',
|
|
68
|
+
recommendation: 'Use environment variables for API keys.',
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
pattern: /exec\s*\(/g,
|
|
72
|
+
type: 'Command Injection',
|
|
73
|
+
severity: 'high',
|
|
74
|
+
cwe: 'CWE-78',
|
|
75
|
+
description: 'Use of exec() can lead to command injection',
|
|
76
|
+
recommendation: 'Use execFile() or spawn() with explicit arguments.',
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
pattern: /dangerouslySetInnerHTML/g,
|
|
80
|
+
type: 'XSS',
|
|
81
|
+
severity: 'medium',
|
|
82
|
+
cwe: 'CWE-79',
|
|
83
|
+
description: 'React dangerouslySetInnerHTML can lead to XSS',
|
|
84
|
+
recommendation: 'Sanitize HTML content before using dangerouslySetInnerHTML.',
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
pattern: /\bhttp:\/\//g,
|
|
88
|
+
type: 'Insecure Communication',
|
|
89
|
+
severity: 'medium',
|
|
90
|
+
cwe: 'CWE-319',
|
|
91
|
+
description: 'Use of HTTP instead of HTTPS',
|
|
92
|
+
recommendation: 'Use HTTPS for all external communications.',
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
pattern: /Math\.random\(\)/g,
|
|
96
|
+
type: 'Weak Randomness',
|
|
97
|
+
severity: 'low',
|
|
98
|
+
cwe: 'CWE-338',
|
|
99
|
+
description: 'Math.random() is not cryptographically secure',
|
|
100
|
+
recommendation: 'Use crypto.getRandomValues() for security-sensitive randomness.',
|
|
101
|
+
},
|
|
102
|
+
];
|
|
103
|
+
/**
|
|
104
|
+
* Code analysis rules
|
|
105
|
+
*/
|
|
106
|
+
const ANALYSIS_RULES = [
|
|
107
|
+
{
|
|
108
|
+
name: 'no-any',
|
|
109
|
+
pattern: /:\s*any\b/g,
|
|
110
|
+
severity: 'warning',
|
|
111
|
+
message: 'Avoid using "any" type. Use proper types for better type safety.',
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
name: 'no-console',
|
|
115
|
+
pattern: /console\.(log|warn|error|info|debug)\s*\(/g,
|
|
116
|
+
severity: 'info',
|
|
117
|
+
message: 'Console statements should be removed in production code.',
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
name: 'max-line-length',
|
|
121
|
+
check: (line) => line.length > 120,
|
|
122
|
+
severity: 'info',
|
|
123
|
+
message: 'Line exceeds 120 characters.',
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
name: 'no-magic-numbers',
|
|
127
|
+
pattern: /[^0-9a-z_]([2-9]|[1-9]\d+)(?=[^0-9]|$)/gi,
|
|
128
|
+
severity: 'info',
|
|
129
|
+
message: 'Avoid magic numbers. Use named constants instead.',
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
name: 'prefer-const',
|
|
133
|
+
pattern: /\blet\s+\w+\s*=/g,
|
|
134
|
+
severity: 'info',
|
|
135
|
+
message: 'Consider using const if variable is not reassigned.',
|
|
136
|
+
},
|
|
137
|
+
];
|
|
138
|
+
/**
|
|
139
|
+
* Register codegen command
|
|
140
|
+
*/
|
|
141
|
+
export function registerCodegenCommand(program) {
|
|
142
|
+
const codegen = program
|
|
143
|
+
.command('codegen')
|
|
144
|
+
.description('Code generation and analysis');
|
|
145
|
+
// codegen generate
|
|
146
|
+
codegen
|
|
147
|
+
.command('generate <design>')
|
|
148
|
+
.description('Generate code from design')
|
|
149
|
+
.option('-o, --output <dir>', 'Output directory', 'src/generated')
|
|
150
|
+
.option('-l, --language <lang>', 'Target language', 'typescript')
|
|
151
|
+
.option('-t, --template <name>', 'Code template to use')
|
|
152
|
+
.action(async (design, options) => {
|
|
153
|
+
const globalOpts = getGlobalOptions(program);
|
|
154
|
+
try {
|
|
155
|
+
const designPath = resolve(process.cwd(), design);
|
|
156
|
+
const content = await readFile(designPath, 'utf-8');
|
|
157
|
+
// Parse design and generate code
|
|
158
|
+
const files = generateCodeFromDesign(content, options);
|
|
159
|
+
const outputDir = resolve(process.cwd(), options.output ?? 'src/generated');
|
|
160
|
+
await mkdir(outputDir, { recursive: true });
|
|
161
|
+
let totalLines = 0;
|
|
162
|
+
const languages = new Set();
|
|
163
|
+
for (const file of files) {
|
|
164
|
+
const filePath = resolve(outputDir, file.filename);
|
|
165
|
+
await mkdir(dirname(filePath), { recursive: true });
|
|
166
|
+
await writeFile(filePath, file.content, 'utf-8');
|
|
167
|
+
totalLines += file.content.split('\n').length;
|
|
168
|
+
languages.add(file.language);
|
|
169
|
+
}
|
|
170
|
+
const result = {
|
|
171
|
+
success: true,
|
|
172
|
+
files,
|
|
173
|
+
summary: {
|
|
174
|
+
totalFiles: files.length,
|
|
175
|
+
totalLines,
|
|
176
|
+
languages: Array.from(languages),
|
|
177
|
+
},
|
|
178
|
+
message: `Generated ${files.length} files (${totalLines} lines)`,
|
|
179
|
+
};
|
|
180
|
+
if (!globalOpts.quiet) {
|
|
181
|
+
console.log(`✅ Code generated in ${outputDir}`);
|
|
182
|
+
for (const file of files) {
|
|
183
|
+
console.log(` - ${file.filename}`);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
if (globalOpts.json) {
|
|
187
|
+
outputResult(result, globalOpts);
|
|
188
|
+
}
|
|
189
|
+
process.exit(ExitCode.SUCCESS);
|
|
190
|
+
}
|
|
191
|
+
catch (error) {
|
|
192
|
+
if (!globalOpts.quiet) {
|
|
193
|
+
console.error(`❌ Code generation failed: ${error.message}`);
|
|
194
|
+
}
|
|
195
|
+
process.exit(ExitCode.GENERAL_ERROR);
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
// codegen analyze
|
|
199
|
+
codegen
|
|
200
|
+
.command('analyze <path>')
|
|
201
|
+
.description('Static code analysis')
|
|
202
|
+
.option('--strict', 'Enable strict mode', false)
|
|
203
|
+
.action(async (path, _options) => {
|
|
204
|
+
const globalOpts = getGlobalOptions(program);
|
|
205
|
+
try {
|
|
206
|
+
const targetPath = resolve(process.cwd(), path);
|
|
207
|
+
const issues = [];
|
|
208
|
+
let fileCount = 0;
|
|
209
|
+
let totalLines = 0;
|
|
210
|
+
// Check if path is file or directory
|
|
211
|
+
const pathStat = await stat(targetPath);
|
|
212
|
+
if (pathStat.isDirectory()) {
|
|
213
|
+
await analyzeDirectory(targetPath, issues, (lines) => {
|
|
214
|
+
fileCount++;
|
|
215
|
+
totalLines += lines;
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
const content = await readFile(targetPath, 'utf-8');
|
|
220
|
+
const lines = content.split('\n');
|
|
221
|
+
analyzeFile(targetPath, lines, issues);
|
|
222
|
+
fileCount = 1;
|
|
223
|
+
totalLines = lines.length;
|
|
224
|
+
}
|
|
225
|
+
const errors = issues.filter(i => i.severity === 'error').length;
|
|
226
|
+
const warnings = issues.filter(i => i.severity === 'warning').length;
|
|
227
|
+
const info = issues.filter(i => i.severity === 'info').length;
|
|
228
|
+
// Calculate metrics
|
|
229
|
+
const complexity = Math.round(totalLines / 10); // Simplified
|
|
230
|
+
const maintainabilityIndex = Math.max(0, 100 - errors * 10 - warnings * 2);
|
|
231
|
+
const result = {
|
|
232
|
+
success: true,
|
|
233
|
+
issues,
|
|
234
|
+
metrics: {
|
|
235
|
+
files: fileCount,
|
|
236
|
+
lines: totalLines,
|
|
237
|
+
complexity,
|
|
238
|
+
maintainabilityIndex,
|
|
239
|
+
},
|
|
240
|
+
summary: { errors, warnings, info },
|
|
241
|
+
message: errors === 0
|
|
242
|
+
? `✅ Analyzed ${fileCount} files - No errors found`
|
|
243
|
+
: `⚠️ Found ${errors} errors, ${warnings} warnings`,
|
|
244
|
+
};
|
|
245
|
+
outputResult(result, globalOpts);
|
|
246
|
+
process.exit(errors === 0 ? ExitCode.SUCCESS : ExitCode.VALIDATION_ERROR);
|
|
247
|
+
}
|
|
248
|
+
catch (error) {
|
|
249
|
+
if (!globalOpts.quiet) {
|
|
250
|
+
console.error(`❌ Analysis failed: ${error.message}`);
|
|
251
|
+
}
|
|
252
|
+
process.exit(ExitCode.GENERAL_ERROR);
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
// codegen security
|
|
256
|
+
codegen
|
|
257
|
+
.command('security <path>')
|
|
258
|
+
.description('Security scan')
|
|
259
|
+
.option('--severity <level>', 'Minimum severity to report', 'low')
|
|
260
|
+
.action(async (path, options) => {
|
|
261
|
+
const globalOpts = getGlobalOptions(program);
|
|
262
|
+
try {
|
|
263
|
+
const targetPath = resolve(process.cwd(), path);
|
|
264
|
+
const vulnerabilities = [];
|
|
265
|
+
// Check if path is file or directory
|
|
266
|
+
const pathStat = await stat(targetPath);
|
|
267
|
+
if (pathStat.isDirectory()) {
|
|
268
|
+
await scanDirectory(targetPath, vulnerabilities);
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
const content = await readFile(targetPath, 'utf-8');
|
|
272
|
+
scanFile(targetPath, content, vulnerabilities);
|
|
273
|
+
}
|
|
274
|
+
// Filter by severity
|
|
275
|
+
const severityOrder = ['critical', 'high', 'medium', 'low'];
|
|
276
|
+
const minSeverityIndex = severityOrder.indexOf(options.severity ?? 'low');
|
|
277
|
+
const filtered = vulnerabilities.filter(v => severityOrder.indexOf(v.severity) <= minSeverityIndex);
|
|
278
|
+
const critical = filtered.filter(v => v.severity === 'critical').length;
|
|
279
|
+
const high = filtered.filter(v => v.severity === 'high').length;
|
|
280
|
+
const medium = filtered.filter(v => v.severity === 'medium').length;
|
|
281
|
+
const low = filtered.filter(v => v.severity === 'low').length;
|
|
282
|
+
// Calculate security score (0-100)
|
|
283
|
+
const score = Math.max(0, 100 - critical * 30 - high * 15 - medium * 5 - low * 1);
|
|
284
|
+
const result = {
|
|
285
|
+
success: true,
|
|
286
|
+
vulnerabilities: filtered,
|
|
287
|
+
summary: { critical, high, medium, low },
|
|
288
|
+
score,
|
|
289
|
+
message: critical + high === 0
|
|
290
|
+
? `✅ Security score: ${score}/100 - No critical issues`
|
|
291
|
+
: `🔴 Security score: ${score}/100 - ${critical} critical, ${high} high severity issues`,
|
|
292
|
+
};
|
|
293
|
+
outputResult(result, globalOpts);
|
|
294
|
+
process.exit(critical === 0 ? ExitCode.SUCCESS : ExitCode.VALIDATION_ERROR);
|
|
295
|
+
}
|
|
296
|
+
catch (error) {
|
|
297
|
+
if (!globalOpts.quiet) {
|
|
298
|
+
console.error(`❌ Security scan failed: ${error.message}`);
|
|
299
|
+
}
|
|
300
|
+
process.exit(ExitCode.GENERAL_ERROR);
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Generate code from design
|
|
306
|
+
*/
|
|
307
|
+
function generateCodeFromDesign(content, options) {
|
|
308
|
+
const files = [];
|
|
309
|
+
const language = options.language ?? 'typescript';
|
|
310
|
+
const ext = language === 'typescript' ? '.ts' : language === 'javascript' ? '.js' : '.py';
|
|
311
|
+
// Extract components from design
|
|
312
|
+
const componentMatches = content.match(/component\s+["']?([A-Za-z0-9_-]+)["']?/gi) || [];
|
|
313
|
+
const classMatches = content.match(/class\s+["']?([A-Za-z0-9_-]+)["']?/gi) || [];
|
|
314
|
+
const interfaceMatches = content.match(/interface\s+["']?([A-Za-z0-9_-]+)["']?/gi) || [];
|
|
315
|
+
// Extract requirement references
|
|
316
|
+
const reqMatches = content.match(/REQ-[A-Z]+-\d+/g) || [];
|
|
317
|
+
const desMatches = content.match(/DES-[A-Z]+-\d+/g) || [];
|
|
318
|
+
// Generate files for components
|
|
319
|
+
const seen = new Set();
|
|
320
|
+
const allMatches = [...componentMatches, ...classMatches, ...interfaceMatches];
|
|
321
|
+
for (const match of allMatches) {
|
|
322
|
+
const name = match.split(/\s+/).pop()?.replace(/["']/g, '') || 'Unknown';
|
|
323
|
+
if (seen.has(name))
|
|
324
|
+
continue;
|
|
325
|
+
seen.add(name);
|
|
326
|
+
const code = generateComponentCode(name, language);
|
|
327
|
+
files.push({
|
|
328
|
+
filename: `${toKebabCase(name)}${ext}`,
|
|
329
|
+
language,
|
|
330
|
+
content: code,
|
|
331
|
+
metadata: {
|
|
332
|
+
requirements: reqMatches.slice(0, 5),
|
|
333
|
+
designElements: desMatches.slice(0, 5),
|
|
334
|
+
patterns: [],
|
|
335
|
+
},
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
// Generate index file
|
|
339
|
+
if (files.length > 0) {
|
|
340
|
+
const indexContent = files
|
|
341
|
+
.map(f => {
|
|
342
|
+
const baseName = f.filename.replace(ext, '');
|
|
343
|
+
return `export * from './${baseName}';`;
|
|
344
|
+
})
|
|
345
|
+
.join('\n');
|
|
346
|
+
files.push({
|
|
347
|
+
filename: `index${ext}`,
|
|
348
|
+
language,
|
|
349
|
+
content: `/**\n * Generated index file\n * @generated\n */\n\n${indexContent}\n`,
|
|
350
|
+
metadata: {
|
|
351
|
+
requirements: [],
|
|
352
|
+
designElements: [],
|
|
353
|
+
patterns: [],
|
|
354
|
+
},
|
|
355
|
+
});
|
|
356
|
+
}
|
|
357
|
+
return files;
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Generate component code
|
|
361
|
+
*/
|
|
362
|
+
function generateComponentCode(name, language) {
|
|
363
|
+
const className = toPascalCase(name);
|
|
364
|
+
if (language === 'typescript') {
|
|
365
|
+
return `/**
|
|
366
|
+
* ${className}
|
|
367
|
+
*
|
|
368
|
+
* @generated
|
|
369
|
+
* @module ${toKebabCase(name)}
|
|
370
|
+
*/
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* ${className} interface
|
|
374
|
+
*/
|
|
375
|
+
export interface I${className} {
|
|
376
|
+
/**
|
|
377
|
+
* Initialize the component
|
|
378
|
+
*/
|
|
379
|
+
initialize(): Promise<void>;
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* Execute main logic
|
|
383
|
+
*/
|
|
384
|
+
execute(): Promise<void>;
|
|
385
|
+
|
|
386
|
+
/**
|
|
387
|
+
* Cleanup resources
|
|
388
|
+
*/
|
|
389
|
+
dispose(): Promise<void>;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* ${className} implementation
|
|
394
|
+
*/
|
|
395
|
+
export class ${className} implements I${className} {
|
|
396
|
+
private initialized = false;
|
|
397
|
+
|
|
398
|
+
/**
|
|
399
|
+
* Create a new ${className} instance
|
|
400
|
+
*/
|
|
401
|
+
constructor() {
|
|
402
|
+
// Initialize
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Initialize the component
|
|
407
|
+
*/
|
|
408
|
+
async initialize(): Promise<void> {
|
|
409
|
+
if (this.initialized) return;
|
|
410
|
+
// TODO: Add initialization logic
|
|
411
|
+
this.initialized = true;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Execute main logic
|
|
416
|
+
*/
|
|
417
|
+
async execute(): Promise<void> {
|
|
418
|
+
if (!this.initialized) {
|
|
419
|
+
await this.initialize();
|
|
420
|
+
}
|
|
421
|
+
// TODO: Add execution logic
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* Cleanup resources
|
|
426
|
+
*/
|
|
427
|
+
async dispose(): Promise<void> {
|
|
428
|
+
// TODO: Add cleanup logic
|
|
429
|
+
this.initialized = false;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Create a new ${className} instance
|
|
435
|
+
*/
|
|
436
|
+
export function create${className}(): ${className} {
|
|
437
|
+
return new ${className}();
|
|
438
|
+
}
|
|
439
|
+
`;
|
|
440
|
+
}
|
|
441
|
+
else if (language === 'python') {
|
|
442
|
+
return `"""
|
|
443
|
+
${className}
|
|
444
|
+
|
|
445
|
+
Generated module
|
|
446
|
+
"""
|
|
447
|
+
|
|
448
|
+
from abc import ABC, abstractmethod
|
|
449
|
+
from typing import Optional
|
|
450
|
+
|
|
451
|
+
|
|
452
|
+
class I${className}(ABC):
|
|
453
|
+
"""${className} interface."""
|
|
454
|
+
|
|
455
|
+
@abstractmethod
|
|
456
|
+
async def initialize(self) -> None:
|
|
457
|
+
"""Initialize the component."""
|
|
458
|
+
pass
|
|
459
|
+
|
|
460
|
+
@abstractmethod
|
|
461
|
+
async def execute(self) -> None:
|
|
462
|
+
"""Execute main logic."""
|
|
463
|
+
pass
|
|
464
|
+
|
|
465
|
+
@abstractmethod
|
|
466
|
+
async def dispose(self) -> None:
|
|
467
|
+
"""Cleanup resources."""
|
|
468
|
+
pass
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
class ${className}(I${className}):
|
|
472
|
+
"""${className} implementation."""
|
|
473
|
+
|
|
474
|
+
def __init__(self) -> None:
|
|
475
|
+
"""Create a new ${className} instance."""
|
|
476
|
+
self._initialized = False
|
|
477
|
+
|
|
478
|
+
async def initialize(self) -> None:
|
|
479
|
+
"""Initialize the component."""
|
|
480
|
+
if self._initialized:
|
|
481
|
+
return
|
|
482
|
+
# TODO: Add initialization logic
|
|
483
|
+
self._initialized = True
|
|
484
|
+
|
|
485
|
+
async def execute(self) -> None:
|
|
486
|
+
"""Execute main logic."""
|
|
487
|
+
if not self._initialized:
|
|
488
|
+
await self.initialize()
|
|
489
|
+
# TODO: Add execution logic
|
|
490
|
+
|
|
491
|
+
async def dispose(self) -> None:
|
|
492
|
+
"""Cleanup resources."""
|
|
493
|
+
# TODO: Add cleanup logic
|
|
494
|
+
self._initialized = False
|
|
495
|
+
|
|
496
|
+
|
|
497
|
+
def create_${toSnakeCase(name)}() -> ${className}:
|
|
498
|
+
"""Create a new ${className} instance."""
|
|
499
|
+
return ${className}()
|
|
500
|
+
`;
|
|
501
|
+
}
|
|
502
|
+
// JavaScript
|
|
503
|
+
return `/**
|
|
504
|
+
* ${className}
|
|
505
|
+
*
|
|
506
|
+
* @generated
|
|
507
|
+
* @module ${toKebabCase(name)}
|
|
508
|
+
*/
|
|
509
|
+
|
|
510
|
+
/**
|
|
511
|
+
* ${className} implementation
|
|
512
|
+
*/
|
|
513
|
+
export class ${className} {
|
|
514
|
+
#initialized = false;
|
|
515
|
+
|
|
516
|
+
/**
|
|
517
|
+
* Create a new ${className} instance
|
|
518
|
+
*/
|
|
519
|
+
constructor() {
|
|
520
|
+
// Initialize
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* Initialize the component
|
|
525
|
+
*/
|
|
526
|
+
async initialize() {
|
|
527
|
+
if (this.#initialized) return;
|
|
528
|
+
// TODO: Add initialization logic
|
|
529
|
+
this.#initialized = true;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
/**
|
|
533
|
+
* Execute main logic
|
|
534
|
+
*/
|
|
535
|
+
async execute() {
|
|
536
|
+
if (!this.#initialized) {
|
|
537
|
+
await this.initialize();
|
|
538
|
+
}
|
|
539
|
+
// TODO: Add execution logic
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
/**
|
|
543
|
+
* Dispose resources
|
|
544
|
+
*/
|
|
545
|
+
async dispose() {
|
|
546
|
+
// TODO: Add cleanup logic
|
|
547
|
+
this.#initialized = false;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
/**
|
|
552
|
+
* Create a new ${className} instance
|
|
553
|
+
*/
|
|
554
|
+
export function create${className}() {
|
|
555
|
+
return new ${className}();
|
|
556
|
+
}
|
|
557
|
+
`;
|
|
558
|
+
}
|
|
559
|
+
/**
|
|
560
|
+
* Analyze directory
|
|
561
|
+
*/
|
|
562
|
+
async function analyzeDirectory(dir, issues, onFile) {
|
|
563
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
564
|
+
for (const entry of entries) {
|
|
565
|
+
const fullPath = join(dir, entry.name);
|
|
566
|
+
if (entry.isDirectory()) {
|
|
567
|
+
if (!entry.name.startsWith('.') && entry.name !== 'node_modules') {
|
|
568
|
+
await analyzeDirectory(fullPath, issues, onFile);
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
else if (entry.isFile()) {
|
|
572
|
+
const ext = extname(entry.name);
|
|
573
|
+
if (['.ts', '.js', '.tsx', '.jsx', '.py'].includes(ext)) {
|
|
574
|
+
const content = await readFile(fullPath, 'utf-8');
|
|
575
|
+
const lines = content.split('\n');
|
|
576
|
+
analyzeFile(fullPath, lines, issues);
|
|
577
|
+
onFile(lines.length);
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
/**
|
|
583
|
+
* Analyze file
|
|
584
|
+
*/
|
|
585
|
+
function analyzeFile(file, lines, issues) {
|
|
586
|
+
for (let i = 0; i < lines.length; i++) {
|
|
587
|
+
const line = lines[i];
|
|
588
|
+
for (const rule of ANALYSIS_RULES) {
|
|
589
|
+
if ('pattern' in rule && rule.pattern) {
|
|
590
|
+
rule.pattern.lastIndex = 0;
|
|
591
|
+
if (rule.pattern.test(line)) {
|
|
592
|
+
issues.push({
|
|
593
|
+
file,
|
|
594
|
+
line: i + 1,
|
|
595
|
+
severity: rule.severity,
|
|
596
|
+
rule: rule.name,
|
|
597
|
+
message: rule.message,
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
else if ('check' in rule && rule.check?.(line)) {
|
|
602
|
+
issues.push({
|
|
603
|
+
file,
|
|
604
|
+
line: i + 1,
|
|
605
|
+
severity: rule.severity,
|
|
606
|
+
rule: rule.name,
|
|
607
|
+
message: rule.message,
|
|
608
|
+
});
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
/**
|
|
614
|
+
* Scan directory for security issues
|
|
615
|
+
*/
|
|
616
|
+
async function scanDirectory(dir, vulnerabilities) {
|
|
617
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
618
|
+
for (const entry of entries) {
|
|
619
|
+
const fullPath = join(dir, entry.name);
|
|
620
|
+
if (entry.isDirectory()) {
|
|
621
|
+
if (!entry.name.startsWith('.') && entry.name !== 'node_modules') {
|
|
622
|
+
await scanDirectory(fullPath, vulnerabilities);
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
else if (entry.isFile()) {
|
|
626
|
+
const ext = extname(entry.name);
|
|
627
|
+
if (['.ts', '.js', '.tsx', '.jsx', '.py', '.json', '.yml', '.yaml'].includes(ext)) {
|
|
628
|
+
const content = await readFile(fullPath, 'utf-8');
|
|
629
|
+
scanFile(fullPath, content, vulnerabilities);
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
/**
|
|
635
|
+
* Scan file for security issues
|
|
636
|
+
*/
|
|
637
|
+
function scanFile(file, content, vulnerabilities) {
|
|
638
|
+
const lines = content.split('\n');
|
|
639
|
+
for (const pattern of SECURITY_PATTERNS) {
|
|
640
|
+
pattern.pattern.lastIndex = 0;
|
|
641
|
+
for (let i = 0; i < lines.length; i++) {
|
|
642
|
+
pattern.pattern.lastIndex = 0;
|
|
643
|
+
if (pattern.pattern.test(lines[i])) {
|
|
644
|
+
vulnerabilities.push({
|
|
645
|
+
severity: pattern.severity,
|
|
646
|
+
type: pattern.type,
|
|
647
|
+
file,
|
|
648
|
+
line: i + 1,
|
|
649
|
+
description: pattern.description,
|
|
650
|
+
recommendation: pattern.recommendation,
|
|
651
|
+
cwe: pattern.cwe,
|
|
652
|
+
});
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
/**
|
|
658
|
+
* Convert to kebab-case
|
|
659
|
+
*/
|
|
660
|
+
function toKebabCase(str) {
|
|
661
|
+
return str
|
|
662
|
+
.replace(/([a-z])([A-Z])/g, '$1-$2')
|
|
663
|
+
.replace(/[\s_]+/g, '-')
|
|
664
|
+
.toLowerCase();
|
|
665
|
+
}
|
|
666
|
+
/**
|
|
667
|
+
* Convert to PascalCase
|
|
668
|
+
*/
|
|
669
|
+
function toPascalCase(str) {
|
|
670
|
+
return str
|
|
671
|
+
.replace(/[-_\s]+(.)?/g, (_, c) => (c ? c.toUpperCase() : ''))
|
|
672
|
+
.replace(/^./, s => s.toUpperCase());
|
|
673
|
+
}
|
|
674
|
+
/**
|
|
675
|
+
* Convert to snake_case
|
|
676
|
+
*/
|
|
677
|
+
function toSnakeCase(str) {
|
|
678
|
+
return str
|
|
679
|
+
.replace(/([a-z])([A-Z])/g, '$1_$2')
|
|
680
|
+
.replace(/[-\s]+/g, '_')
|
|
681
|
+
.toLowerCase();
|
|
682
|
+
}
|
|
683
|
+
export { generateCodeFromDesign, analyzeFile, scanFile, };
|
|
684
|
+
//# sourceMappingURL=codegen.js.map
|