@weave_protocol/domere 1.0.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.
Files changed (104) hide show
  1. package/PLANNING.md +231 -0
  2. package/README.md +50 -0
  3. package/dist/anchoring/ethereum.d.ts +135 -0
  4. package/dist/anchoring/ethereum.d.ts.map +1 -0
  5. package/dist/anchoring/ethereum.js +474 -0
  6. package/dist/anchoring/ethereum.js.map +1 -0
  7. package/dist/anchoring/index.d.ts +93 -0
  8. package/dist/anchoring/index.d.ts.map +1 -0
  9. package/dist/anchoring/index.js +184 -0
  10. package/dist/anchoring/index.js.map +1 -0
  11. package/dist/anchoring/merkle.d.ts +91 -0
  12. package/dist/anchoring/merkle.d.ts.map +1 -0
  13. package/dist/anchoring/merkle.js +203 -0
  14. package/dist/anchoring/merkle.js.map +1 -0
  15. package/dist/anchoring/solana.d.ts +85 -0
  16. package/dist/anchoring/solana.d.ts.map +1 -0
  17. package/dist/anchoring/solana.js +301 -0
  18. package/dist/anchoring/solana.js.map +1 -0
  19. package/dist/constants.d.ts +130 -0
  20. package/dist/constants.d.ts.map +1 -0
  21. package/dist/constants.js +536 -0
  22. package/dist/constants.js.map +1 -0
  23. package/dist/index.d.ts +13 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +37 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/language/code-analyzer.d.ts +80 -0
  28. package/dist/language/code-analyzer.d.ts.map +1 -0
  29. package/dist/language/code-analyzer.js +489 -0
  30. package/dist/language/code-analyzer.js.map +1 -0
  31. package/dist/language/detector.d.ts +53 -0
  32. package/dist/language/detector.d.ts.map +1 -0
  33. package/dist/language/detector.js +248 -0
  34. package/dist/language/detector.js.map +1 -0
  35. package/dist/language/index.d.ts +61 -0
  36. package/dist/language/index.d.ts.map +1 -0
  37. package/dist/language/index.js +109 -0
  38. package/dist/language/index.js.map +1 -0
  39. package/dist/language/nl-analyzer.d.ts +59 -0
  40. package/dist/language/nl-analyzer.d.ts.map +1 -0
  41. package/dist/language/nl-analyzer.js +350 -0
  42. package/dist/language/nl-analyzer.js.map +1 -0
  43. package/dist/language/semantic.d.ts +48 -0
  44. package/dist/language/semantic.d.ts.map +1 -0
  45. package/dist/language/semantic.js +329 -0
  46. package/dist/language/semantic.js.map +1 -0
  47. package/dist/storage/index.d.ts +6 -0
  48. package/dist/storage/index.d.ts.map +1 -0
  49. package/dist/storage/index.js +6 -0
  50. package/dist/storage/index.js.map +1 -0
  51. package/dist/storage/memory.d.ts +48 -0
  52. package/dist/storage/memory.d.ts.map +1 -0
  53. package/dist/storage/memory.js +211 -0
  54. package/dist/storage/memory.js.map +1 -0
  55. package/dist/thread/drift.d.ts +43 -0
  56. package/dist/thread/drift.d.ts.map +1 -0
  57. package/dist/thread/drift.js +248 -0
  58. package/dist/thread/drift.js.map +1 -0
  59. package/dist/thread/index.d.ts +9 -0
  60. package/dist/thread/index.d.ts.map +1 -0
  61. package/dist/thread/index.js +9 -0
  62. package/dist/thread/index.js.map +1 -0
  63. package/dist/thread/intent.d.ts +68 -0
  64. package/dist/thread/intent.d.ts.map +1 -0
  65. package/dist/thread/intent.js +333 -0
  66. package/dist/thread/intent.js.map +1 -0
  67. package/dist/thread/manager.d.ts +85 -0
  68. package/dist/thread/manager.d.ts.map +1 -0
  69. package/dist/thread/manager.js +305 -0
  70. package/dist/thread/manager.js.map +1 -0
  71. package/dist/thread/weave.d.ts +61 -0
  72. package/dist/thread/weave.d.ts.map +1 -0
  73. package/dist/thread/weave.js +158 -0
  74. package/dist/thread/weave.js.map +1 -0
  75. package/dist/tools/index.d.ts +18 -0
  76. package/dist/tools/index.d.ts.map +1 -0
  77. package/dist/tools/index.js +102 -0
  78. package/dist/tools/index.js.map +1 -0
  79. package/dist/types.d.ts +466 -0
  80. package/dist/types.d.ts.map +1 -0
  81. package/dist/types.js +48 -0
  82. package/dist/types.js.map +1 -0
  83. package/package.json +24 -0
  84. package/src/anchoring/ethereum.ts +568 -0
  85. package/src/anchoring/index.ts +236 -0
  86. package/src/anchoring/merkle.ts +256 -0
  87. package/src/anchoring/solana.ts +370 -0
  88. package/src/constants.ts +566 -0
  89. package/src/index.ts +43 -0
  90. package/src/language/code-analyzer.ts +564 -0
  91. package/src/language/detector.ts +297 -0
  92. package/src/language/index.ts +129 -0
  93. package/src/language/nl-analyzer.ts +411 -0
  94. package/src/language/semantic.ts +385 -0
  95. package/src/storage/index.ts +6 -0
  96. package/src/storage/memory.ts +271 -0
  97. package/src/thread/drift.ts +319 -0
  98. package/src/thread/index.ts +9 -0
  99. package/src/thread/intent.ts +409 -0
  100. package/src/thread/manager.ts +414 -0
  101. package/src/thread/weave.ts +205 -0
  102. package/src/tools/index.ts +107 -0
  103. package/src/types.ts +736 -0
  104. package/tsconfig.json +19 -0
@@ -0,0 +1,564 @@
1
+ /**
2
+ * Dōmere - The Judge Protocol
3
+ * Code Analysis
4
+ */
5
+
6
+ import type {
7
+ CodeAnalysis,
8
+ DangerousPattern,
9
+ DataFlow,
10
+ ExternalCall,
11
+ LanguageType,
12
+ } from '../types.js';
13
+ import { DANGEROUS_CODE_PATTERNS } from '../constants.js';
14
+
15
+ // ============================================================================
16
+ // Code Analyzer
17
+ // ============================================================================
18
+
19
+ export class CodeAnalyzer {
20
+ /**
21
+ * Analyze code for security and structure
22
+ */
23
+ analyze(code: string, language: LanguageType): CodeAnalysis {
24
+ const dangerousPatterns = this.detectDangerousPatterns(code, language);
25
+ const dataFlows = this.analyzeDataFlows(code, language);
26
+ const externalCalls = this.detectExternalCalls(code, language);
27
+
28
+ const riskLevel = this.calculateRiskLevel(dangerousPatterns, dataFlows, externalCalls);
29
+
30
+ return {
31
+ language,
32
+ functions: this.extractFunctions(code, language),
33
+ classes: this.extractClasses(code, language),
34
+ imports: this.extractImports(code, language),
35
+ exports: this.extractExports(code, language),
36
+ dangerous_patterns: dangerousPatterns,
37
+ data_flows: dataFlows,
38
+ external_calls: externalCalls,
39
+ complexity_score: this.calculateComplexity(code),
40
+ sandbox_required: riskLevel !== 'low',
41
+ risk_level: riskLevel,
42
+ recommendations: this.generateRecommendations(dangerousPatterns, externalCalls),
43
+ };
44
+ }
45
+
46
+ /**
47
+ * Detect dangerous code patterns
48
+ */
49
+ detectDangerousPatterns(code: string, language: LanguageType): DangerousPattern[] {
50
+ const patterns: DangerousPattern[] = [];
51
+
52
+ // Get language-specific patterns
53
+ const langPatterns = DANGEROUS_CODE_PATTERNS[language as keyof typeof DANGEROUS_CODE_PATTERNS];
54
+
55
+ if (langPatterns) {
56
+ for (const { pattern, description, severity } of langPatterns) {
57
+ const regex = new RegExp(pattern.source, pattern.flags);
58
+ let match;
59
+
60
+ while ((match = regex.exec(code)) !== null) {
61
+ const line = this.getLineNumber(code, match.index);
62
+ const column = this.getColumnNumber(code, match.index);
63
+
64
+ patterns.push({
65
+ pattern: match[0],
66
+ description,
67
+ severity,
68
+ line,
69
+ column,
70
+ recommendation: this.getRecommendation(description, severity),
71
+ });
72
+ }
73
+ }
74
+ }
75
+
76
+ // Common patterns across languages
77
+ const commonPatterns = [
78
+ { pattern: /password\s*=\s*['"][^'"]+['"]/gi, description: 'Hardcoded password', severity: 'critical' as const },
79
+ { pattern: /api[_-]?key\s*=\s*['"][^'"]+['"]/gi, description: 'Hardcoded API key', severity: 'critical' as const },
80
+ { pattern: /secret\s*=\s*['"][^'"]+['"]/gi, description: 'Hardcoded secret', severity: 'critical' as const },
81
+ { pattern: /TODO|FIXME|HACK|XXX/g, description: 'Code annotation suggesting incomplete work', severity: 'low' as const },
82
+ ];
83
+
84
+ for (const { pattern, description, severity } of commonPatterns) {
85
+ let match;
86
+ while ((match = pattern.exec(code)) !== null) {
87
+ const line = this.getLineNumber(code, match.index);
88
+ patterns.push({
89
+ pattern: match[0].slice(0, 50), // Truncate for security
90
+ description,
91
+ severity,
92
+ line,
93
+ recommendation: this.getRecommendation(description, severity),
94
+ });
95
+ }
96
+ }
97
+
98
+ return patterns;
99
+ }
100
+
101
+ /**
102
+ * Analyze data flows in code
103
+ */
104
+ analyzeDataFlows(code: string, language: LanguageType): DataFlow[] {
105
+ const flows: DataFlow[] = [];
106
+
107
+ // Detect input -> output flows
108
+ const inputPatterns: Record<string, RegExp[]> = {
109
+ javascript: [
110
+ /req\.(body|query|params|headers)/g,
111
+ /process\.env/g,
112
+ /fs\.readFile/g,
113
+ /fetch\s*\(/g,
114
+ ],
115
+ python: [
116
+ /request\.(form|args|json|data)/g,
117
+ /os\.environ/g,
118
+ /open\s*\(/g,
119
+ /requests\.(get|post)/g,
120
+ /input\s*\(/g,
121
+ ],
122
+ sql: [
123
+ /SELECT\s+.*\s+FROM/gi,
124
+ ],
125
+ };
126
+
127
+ const outputPatterns: Record<string, RegExp[]> = {
128
+ javascript: [
129
+ /res\.(send|json|write)/g,
130
+ /console\.(log|error)/g,
131
+ /fs\.writeFile/g,
132
+ /fetch\s*\(/g,
133
+ ],
134
+ python: [
135
+ /return\s+/g,
136
+ /print\s*\(/g,
137
+ /\.write\s*\(/g,
138
+ /requests\.(get|post)/g,
139
+ ],
140
+ sql: [
141
+ /INSERT\s+INTO/gi,
142
+ /UPDATE\s+/gi,
143
+ ],
144
+ };
145
+
146
+ const inputs = inputPatterns[language as keyof typeof inputPatterns] || [];
147
+ const outputs = outputPatterns[language as keyof typeof outputPatterns] || [];
148
+
149
+ const inputMatches: string[] = [];
150
+ const outputMatches: string[] = [];
151
+
152
+ for (const pattern of inputs) {
153
+ const matches = code.match(pattern);
154
+ if (matches) inputMatches.push(...matches);
155
+ }
156
+
157
+ for (const pattern of outputs) {
158
+ const matches = code.match(pattern);
159
+ if (matches) outputMatches.push(...matches);
160
+ }
161
+
162
+ // Create flow pairs
163
+ for (const input of inputMatches) {
164
+ for (const output of outputMatches) {
165
+ flows.push({
166
+ source: input,
167
+ destination: output,
168
+ sensitive: this.isSensitiveFlow(input, output),
169
+ });
170
+ }
171
+ }
172
+
173
+ // Detect sensitive data patterns
174
+ const sensitivePatterns = [
175
+ /password/i, /secret/i, /token/i, /key/i, /credential/i,
176
+ /ssn/i, /social.*security/i, /credit.*card/i,
177
+ ];
178
+
179
+ for (const pattern of sensitivePatterns) {
180
+ if (pattern.test(code)) {
181
+ // Look for where this data goes
182
+ const varPattern = new RegExp(`(\\w+)\\s*=.*${pattern.source}`, 'gi');
183
+ const matches = code.match(varPattern);
184
+ if (matches) {
185
+ for (const match of matches) {
186
+ flows.push({
187
+ source: match,
188
+ destination: 'unknown',
189
+ data_type: pattern.source.replace(/[\\^$*+?.()|[\]{}]/g, ''),
190
+ sensitive: true,
191
+ });
192
+ }
193
+ }
194
+ }
195
+ }
196
+
197
+ return flows;
198
+ }
199
+
200
+ /**
201
+ * Detect external calls
202
+ */
203
+ detectExternalCalls(code: string, language: LanguageType): ExternalCall[] {
204
+ const calls: ExternalCall[] = [];
205
+
206
+ // HTTP calls
207
+ const httpPatterns = [
208
+ { pattern: /fetch\s*\(\s*['"`]([^'"`]+)['"`]/g, type: 'http' as const },
209
+ { pattern: /axios\.(get|post|put|delete)\s*\(\s*['"`]([^'"`]+)['"`]/g, type: 'http' as const },
210
+ { pattern: /requests\.(get|post|put|delete)\s*\(\s*['"`]([^'"`]+)['"`]/g, type: 'http' as const },
211
+ { pattern: /http\.(get|post|put|delete)\s*\(\s*['"`]([^'"`]+)['"`]/g, type: 'http' as const },
212
+ { pattern: /curl\s+['"`]?([^\s'"`]+)/g, type: 'http' as const },
213
+ { pattern: /wget\s+['"`]?([^\s'"`]+)/g, type: 'http' as const },
214
+ ];
215
+
216
+ for (const { pattern, type } of httpPatterns) {
217
+ let match;
218
+ while ((match = pattern.exec(code)) !== null) {
219
+ const url = match[2] || match[1];
220
+ calls.push({
221
+ type,
222
+ target: url,
223
+ method: match[1]?.toUpperCase(),
224
+ risk_level: this.assessUrlRisk(url),
225
+ });
226
+ }
227
+ }
228
+
229
+ // Database calls
230
+ const dbPatterns = [
231
+ { pattern: /\.query\s*\(\s*['"`]([^'"`]+)['"`]/g, type: 'database' as const },
232
+ { pattern: /\.execute\s*\(\s*['"`]([^'"`]+)['"`]/g, type: 'database' as const },
233
+ { pattern: /cursor\.execute\s*\(\s*['"`]([^'"`]+)['"`]/g, type: 'database' as const },
234
+ ];
235
+
236
+ for (const { pattern, type } of dbPatterns) {
237
+ let match;
238
+ while ((match = pattern.exec(code)) !== null) {
239
+ calls.push({
240
+ type,
241
+ target: match[1].slice(0, 100), // Truncate long queries
242
+ risk_level: this.assessQueryRisk(match[1]),
243
+ });
244
+ }
245
+ }
246
+
247
+ // File system calls
248
+ const fsPatterns = [
249
+ { pattern: /(?:fs\.|open\s*\()['"`]([^'"`]+)['"`]/g, type: 'file' as const },
250
+ { pattern: /(?:readFile|writeFile|appendFile)\s*\(\s*['"`]([^'"`]+)['"`]/g, type: 'file' as const },
251
+ ];
252
+
253
+ for (const { pattern, type } of fsPatterns) {
254
+ let match;
255
+ while ((match = pattern.exec(code)) !== null) {
256
+ calls.push({
257
+ type,
258
+ target: match[1],
259
+ risk_level: this.assessFileRisk(match[1]),
260
+ });
261
+ }
262
+ }
263
+
264
+ // Process/command execution
265
+ const processPatterns = [
266
+ { pattern: /(?:exec|spawn|system)\s*\(\s*['"`]([^'"`]+)['"`]/g, type: 'process' as const },
267
+ { pattern: /subprocess\.(call|run|Popen)\s*\(\s*\[?['"`]([^'"`]+)['"`]/g, type: 'process' as const },
268
+ { pattern: /os\.system\s*\(\s*['"`]([^'"`]+)['"`]/g, type: 'process' as const },
269
+ ];
270
+
271
+ for (const { pattern, type } of processPatterns) {
272
+ let match;
273
+ while ((match = pattern.exec(code)) !== null) {
274
+ calls.push({
275
+ type,
276
+ target: match[1] || match[2],
277
+ risk_level: 'high',
278
+ });
279
+ }
280
+ }
281
+
282
+ return calls;
283
+ }
284
+
285
+ /**
286
+ * Extract function definitions
287
+ */
288
+ extractFunctions(code: string, language: LanguageType): string[] {
289
+ const functions: string[] = [];
290
+
291
+ const patterns: Record<string, RegExp> = {
292
+ javascript: /(?:function\s+(\w+)|(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s+)?(?:\([^)]*\)|[^=])\s*=>|(\w+)\s*:\s*(?:async\s+)?function)/g,
293
+ typescript: /(?:function\s+(\w+)|(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s+)?(?:\([^)]*\)|[^=])\s*=>|(\w+)\s*:\s*(?:async\s+)?function)/g,
294
+ python: /def\s+(\w+)\s*\(/g,
295
+ java: /(?:public|private|protected)?\s*(?:static\s+)?(?:\w+\s+)+(\w+)\s*\([^)]*\)\s*(?:throws\s+\w+\s*)?{/g,
296
+ go: /func\s+(?:\([^)]+\)\s+)?(\w+)\s*\(/g,
297
+ rust: /fn\s+(\w+)\s*[<(]/g,
298
+ ruby: /def\s+(\w+)/g,
299
+ php: /function\s+(\w+)\s*\(/g,
300
+ };
301
+
302
+ const pattern = patterns[language as keyof typeof patterns];
303
+ if (pattern) {
304
+ let match;
305
+ while ((match = pattern.exec(code)) !== null) {
306
+ const name = match[1] || match[2] || match[3];
307
+ if (name && !functions.includes(name)) {
308
+ functions.push(name);
309
+ }
310
+ }
311
+ }
312
+
313
+ return functions;
314
+ }
315
+
316
+ /**
317
+ * Extract class definitions
318
+ */
319
+ extractClasses(code: string, language: LanguageType): string[] {
320
+ const classes: string[] = [];
321
+
322
+ const patterns: Record<string, RegExp> = {
323
+ javascript: /class\s+(\w+)/g,
324
+ typescript: /class\s+(\w+)/g,
325
+ python: /class\s+(\w+)/g,
326
+ java: /class\s+(\w+)/g,
327
+ csharp: /class\s+(\w+)/g,
328
+ ruby: /class\s+(\w+)/g,
329
+ php: /class\s+(\w+)/g,
330
+ };
331
+
332
+ const pattern = patterns[language as keyof typeof patterns];
333
+ if (pattern) {
334
+ let match;
335
+ while ((match = pattern.exec(code)) !== null) {
336
+ if (!classes.includes(match[1])) {
337
+ classes.push(match[1]);
338
+ }
339
+ }
340
+ }
341
+
342
+ return classes;
343
+ }
344
+
345
+ /**
346
+ * Extract imports
347
+ */
348
+ extractImports(code: string, language: LanguageType): string[] {
349
+ const imports: string[] = [];
350
+
351
+ const patterns: Record<string, RegExp> = {
352
+ javascript: /(?:import\s+.*\s+from\s+['"]([^'"]+)['"]|require\s*\(\s*['"]([^'"]+)['"]\s*\))/g,
353
+ typescript: /(?:import\s+.*\s+from\s+['"]([^'"]+)['"]|require\s*\(\s*['"]([^'"]+)['"]\s*\))/g,
354
+ python: /(?:import\s+(\w+)|from\s+(\w+)\s+import)/g,
355
+ java: /import\s+([\w.]+);/g,
356
+ go: /import\s+(?:\(\s*)?["']([^"']+)["']/g,
357
+ rust: /use\s+([\w:]+)/g,
358
+ };
359
+
360
+ const pattern = patterns[language as keyof typeof patterns];
361
+ if (pattern) {
362
+ let match;
363
+ while ((match = pattern.exec(code)) !== null) {
364
+ const imp = match[1] || match[2];
365
+ if (imp && !imports.includes(imp)) {
366
+ imports.push(imp);
367
+ }
368
+ }
369
+ }
370
+
371
+ return imports;
372
+ }
373
+
374
+ /**
375
+ * Extract exports
376
+ */
377
+ extractExports(code: string, language: LanguageType): string[] {
378
+ const exports: string[] = [];
379
+
380
+ const patterns: Record<string, RegExp> = {
381
+ javascript: /export\s+(?:default\s+)?(?:class|function|const|let|var|interface|type)?\s*(\w+)/g,
382
+ typescript: /export\s+(?:default\s+)?(?:class|function|const|let|var|interface|type)?\s*(\w+)/g,
383
+ };
384
+
385
+ const pattern = patterns[language as keyof typeof patterns];
386
+ if (pattern) {
387
+ let match;
388
+ while ((match = pattern.exec(code)) !== null) {
389
+ if (match[1] && !exports.includes(match[1])) {
390
+ exports.push(match[1]);
391
+ }
392
+ }
393
+ }
394
+
395
+ return exports;
396
+ }
397
+
398
+ /**
399
+ * Calculate code complexity
400
+ */
401
+ calculateComplexity(code: string): number {
402
+ let complexity = 1; // Base complexity
403
+
404
+ // Count decision points
405
+ const decisionPatterns = [
406
+ /\bif\b/g,
407
+ /\belse\s+if\b/g,
408
+ /\bwhile\b/g,
409
+ /\bfor\b/g,
410
+ /\bforeach\b/g,
411
+ /\bswitch\b/g,
412
+ /\bcase\b/g,
413
+ /\bcatch\b/g,
414
+ /\?\s*[^:]+\s*:/g, // Ternary
415
+ /&&/g,
416
+ /\|\|/g,
417
+ ];
418
+
419
+ for (const pattern of decisionPatterns) {
420
+ const matches = code.match(pattern);
421
+ if (matches) {
422
+ complexity += matches.length;
423
+ }
424
+ }
425
+
426
+ // Normalize (0-100 scale)
427
+ const lines = code.split('\n').length;
428
+ const normalizedComplexity = Math.min(100, (complexity / Math.max(1, lines / 10)) * 10);
429
+
430
+ return Math.round(normalizedComplexity);
431
+ }
432
+
433
+ /**
434
+ * Calculate overall risk level
435
+ */
436
+ private calculateRiskLevel(
437
+ patterns: DangerousPattern[],
438
+ flows: DataFlow[],
439
+ calls: ExternalCall[]
440
+ ): 'low' | 'medium' | 'high' | 'critical' {
441
+ const criticalPatterns = patterns.filter(p => p.severity === 'critical').length;
442
+ const highPatterns = patterns.filter(p => p.severity === 'high').length;
443
+ const sensitiveFlows = flows.filter(f => f.sensitive).length;
444
+ const highRiskCalls = calls.filter(c => c.risk_level === 'high').length;
445
+
446
+ if (criticalPatterns > 0) return 'critical';
447
+ if (highPatterns > 2 || sensitiveFlows > 2 || highRiskCalls > 1) return 'high';
448
+ if (highPatterns > 0 || sensitiveFlows > 0 || highRiskCalls > 0) return 'medium';
449
+
450
+ return 'low';
451
+ }
452
+
453
+ /**
454
+ * Generate recommendations
455
+ */
456
+ private generateRecommendations(patterns: DangerousPattern[], calls: ExternalCall[]): string[] {
457
+ const recommendations: string[] = [];
458
+
459
+ for (const pattern of patterns) {
460
+ if (pattern.recommendation && !recommendations.includes(pattern.recommendation)) {
461
+ recommendations.push(pattern.recommendation);
462
+ }
463
+ }
464
+
465
+ if (calls.some(c => c.type === 'process')) {
466
+ recommendations.push('Review process execution calls for potential command injection');
467
+ }
468
+
469
+ if (calls.some(c => c.type === 'database')) {
470
+ recommendations.push('Ensure database queries use parameterized statements');
471
+ }
472
+
473
+ if (calls.some(c => c.type === 'file')) {
474
+ recommendations.push('Validate file paths to prevent directory traversal');
475
+ }
476
+
477
+ return recommendations.slice(0, 5);
478
+ }
479
+
480
+ /**
481
+ * Get line number from character position
482
+ */
483
+ private getLineNumber(code: string, position: number): number {
484
+ return code.slice(0, position).split('\n').length;
485
+ }
486
+
487
+ /**
488
+ * Get column number from character position
489
+ */
490
+ private getColumnNumber(code: string, position: number): number {
491
+ const lines = code.slice(0, position).split('\n');
492
+ return lines[lines.length - 1].length + 1;
493
+ }
494
+
495
+ /**
496
+ * Get recommendation based on issue
497
+ */
498
+ private getRecommendation(description: string, severity: string): string {
499
+ const recommendations: Record<string, string> = {
500
+ 'eval': 'Avoid eval() - use safer alternatives like JSON.parse() or specific parsers',
501
+ 'exec': 'Avoid direct command execution - use libraries with proper escaping',
502
+ 'sql injection': 'Use parameterized queries or an ORM',
503
+ 'password': 'Move credentials to environment variables or a secrets manager',
504
+ 'api key': 'Move API keys to environment variables or a secrets manager',
505
+ };
506
+
507
+ for (const [key, rec] of Object.entries(recommendations)) {
508
+ if (description.toLowerCase().includes(key)) {
509
+ return rec;
510
+ }
511
+ }
512
+
513
+ return severity === 'critical'
514
+ ? 'Review this pattern carefully before deployment'
515
+ : 'Consider reviewing this pattern';
516
+ }
517
+
518
+ /**
519
+ * Check if a data flow is sensitive
520
+ */
521
+ private isSensitiveFlow(source: string, destination: string): boolean {
522
+ const sensitiveTerms = ['password', 'secret', 'token', 'key', 'credential', 'auth'];
523
+ const combined = (source + destination).toLowerCase();
524
+ return sensitiveTerms.some(term => combined.includes(term));
525
+ }
526
+
527
+ /**
528
+ * Assess URL risk
529
+ */
530
+ private assessUrlRisk(url: string): 'low' | 'medium' | 'high' {
531
+ // External URLs are higher risk
532
+ if (url.startsWith('http://')) return 'high'; // Insecure
533
+ if (url.includes('localhost') || url.includes('127.0.0.1')) return 'low';
534
+ if (url.startsWith('/')) return 'low'; // Relative
535
+ return 'medium';
536
+ }
537
+
538
+ /**
539
+ * Assess SQL query risk
540
+ */
541
+ private assessQueryRisk(query: string): 'low' | 'medium' | 'high' {
542
+ const queryUpper = query.toUpperCase();
543
+ if (queryUpper.includes('DROP') || queryUpper.includes('DELETE') || queryUpper.includes('TRUNCATE')) {
544
+ return 'high';
545
+ }
546
+ if (queryUpper.includes('UPDATE') || queryUpper.includes('INSERT')) {
547
+ return 'medium';
548
+ }
549
+ return 'low';
550
+ }
551
+
552
+ /**
553
+ * Assess file path risk
554
+ */
555
+ private assessFileRisk(path: string): 'low' | 'medium' | 'high' {
556
+ if (path.includes('..') || path.startsWith('/etc') || path.startsWith('/var')) {
557
+ return 'high';
558
+ }
559
+ if (path.startsWith('/')) {
560
+ return 'medium';
561
+ }
562
+ return 'low';
563
+ }
564
+ }