@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.
- package/PLANNING.md +231 -0
- package/README.md +50 -0
- package/dist/anchoring/ethereum.d.ts +135 -0
- package/dist/anchoring/ethereum.d.ts.map +1 -0
- package/dist/anchoring/ethereum.js +474 -0
- package/dist/anchoring/ethereum.js.map +1 -0
- package/dist/anchoring/index.d.ts +93 -0
- package/dist/anchoring/index.d.ts.map +1 -0
- package/dist/anchoring/index.js +184 -0
- package/dist/anchoring/index.js.map +1 -0
- package/dist/anchoring/merkle.d.ts +91 -0
- package/dist/anchoring/merkle.d.ts.map +1 -0
- package/dist/anchoring/merkle.js +203 -0
- package/dist/anchoring/merkle.js.map +1 -0
- package/dist/anchoring/solana.d.ts +85 -0
- package/dist/anchoring/solana.d.ts.map +1 -0
- package/dist/anchoring/solana.js +301 -0
- package/dist/anchoring/solana.js.map +1 -0
- package/dist/constants.d.ts +130 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/constants.js +536 -0
- package/dist/constants.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -0
- package/dist/language/code-analyzer.d.ts +80 -0
- package/dist/language/code-analyzer.d.ts.map +1 -0
- package/dist/language/code-analyzer.js +489 -0
- package/dist/language/code-analyzer.js.map +1 -0
- package/dist/language/detector.d.ts +53 -0
- package/dist/language/detector.d.ts.map +1 -0
- package/dist/language/detector.js +248 -0
- package/dist/language/detector.js.map +1 -0
- package/dist/language/index.d.ts +61 -0
- package/dist/language/index.d.ts.map +1 -0
- package/dist/language/index.js +109 -0
- package/dist/language/index.js.map +1 -0
- package/dist/language/nl-analyzer.d.ts +59 -0
- package/dist/language/nl-analyzer.d.ts.map +1 -0
- package/dist/language/nl-analyzer.js +350 -0
- package/dist/language/nl-analyzer.js.map +1 -0
- package/dist/language/semantic.d.ts +48 -0
- package/dist/language/semantic.d.ts.map +1 -0
- package/dist/language/semantic.js +329 -0
- package/dist/language/semantic.js.map +1 -0
- package/dist/storage/index.d.ts +6 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +6 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/memory.d.ts +48 -0
- package/dist/storage/memory.d.ts.map +1 -0
- package/dist/storage/memory.js +211 -0
- package/dist/storage/memory.js.map +1 -0
- package/dist/thread/drift.d.ts +43 -0
- package/dist/thread/drift.d.ts.map +1 -0
- package/dist/thread/drift.js +248 -0
- package/dist/thread/drift.js.map +1 -0
- package/dist/thread/index.d.ts +9 -0
- package/dist/thread/index.d.ts.map +1 -0
- package/dist/thread/index.js +9 -0
- package/dist/thread/index.js.map +1 -0
- package/dist/thread/intent.d.ts +68 -0
- package/dist/thread/intent.d.ts.map +1 -0
- package/dist/thread/intent.js +333 -0
- package/dist/thread/intent.js.map +1 -0
- package/dist/thread/manager.d.ts +85 -0
- package/dist/thread/manager.d.ts.map +1 -0
- package/dist/thread/manager.js +305 -0
- package/dist/thread/manager.js.map +1 -0
- package/dist/thread/weave.d.ts +61 -0
- package/dist/thread/weave.d.ts.map +1 -0
- package/dist/thread/weave.js +158 -0
- package/dist/thread/weave.js.map +1 -0
- package/dist/tools/index.d.ts +18 -0
- package/dist/tools/index.d.ts.map +1 -0
- package/dist/tools/index.js +102 -0
- package/dist/tools/index.js.map +1 -0
- package/dist/types.d.ts +466 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +48 -0
- package/dist/types.js.map +1 -0
- package/package.json +24 -0
- package/src/anchoring/ethereum.ts +568 -0
- package/src/anchoring/index.ts +236 -0
- package/src/anchoring/merkle.ts +256 -0
- package/src/anchoring/solana.ts +370 -0
- package/src/constants.ts +566 -0
- package/src/index.ts +43 -0
- package/src/language/code-analyzer.ts +564 -0
- package/src/language/detector.ts +297 -0
- package/src/language/index.ts +129 -0
- package/src/language/nl-analyzer.ts +411 -0
- package/src/language/semantic.ts +385 -0
- package/src/storage/index.ts +6 -0
- package/src/storage/memory.ts +271 -0
- package/src/thread/drift.ts +319 -0
- package/src/thread/index.ts +9 -0
- package/src/thread/intent.ts +409 -0
- package/src/thread/manager.ts +414 -0
- package/src/thread/weave.ts +205 -0
- package/src/tools/index.ts +107 -0
- package/src/types.ts +736 -0
- 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
|
+
}
|