archicore 0.1.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/README.md +530 -0
- package/dist/analyzers/dead-code.d.ts +95 -0
- package/dist/analyzers/dead-code.js +327 -0
- package/dist/analyzers/duplication.d.ts +90 -0
- package/dist/analyzers/duplication.js +344 -0
- package/dist/analyzers/security.d.ts +79 -0
- package/dist/analyzers/security.js +484 -0
- package/dist/architecture/index.d.ts +35 -0
- package/dist/architecture/index.js +249 -0
- package/dist/cli/commands/analyzers.d.ts +6 -0
- package/dist/cli/commands/analyzers.js +431 -0
- package/dist/cli/commands/export.d.ts +6 -0
- package/dist/cli/commands/export.js +78 -0
- package/dist/cli/commands/index.d.ts +8 -0
- package/dist/cli/commands/index.js +8 -0
- package/dist/cli/commands/init.d.ts +26 -0
- package/dist/cli/commands/init.js +140 -0
- package/dist/cli/commands/interactive.d.ts +7 -0
- package/dist/cli/commands/interactive.js +522 -0
- package/dist/cli/commands/projects.d.ts +6 -0
- package/dist/cli/commands/projects.js +249 -0
- package/dist/cli/index.d.ts +7 -0
- package/dist/cli/index.js +7 -0
- package/dist/cli/ui/box.d.ts +17 -0
- package/dist/cli/ui/box.js +62 -0
- package/dist/cli/ui/colors.d.ts +49 -0
- package/dist/cli/ui/colors.js +86 -0
- package/dist/cli/ui/index.d.ts +9 -0
- package/dist/cli/ui/index.js +9 -0
- package/dist/cli/ui/prompt.d.ts +34 -0
- package/dist/cli/ui/prompt.js +122 -0
- package/dist/cli/ui/spinner.d.ts +29 -0
- package/dist/cli/ui/spinner.js +80 -0
- package/dist/cli/ui/table.d.ts +33 -0
- package/dist/cli/ui/table.js +84 -0
- package/dist/cli/utils/config.d.ts +23 -0
- package/dist/cli/utils/config.js +73 -0
- package/dist/cli/utils/index.d.ts +6 -0
- package/dist/cli/utils/index.js +6 -0
- package/dist/cli/utils/session.d.ts +27 -0
- package/dist/cli/utils/session.js +117 -0
- package/dist/cli.d.ts +8 -0
- package/dist/cli.js +295 -0
- package/dist/code-index/ast-parser.d.ts +16 -0
- package/dist/code-index/ast-parser.js +330 -0
- package/dist/code-index/dependency-graph.d.ts +16 -0
- package/dist/code-index/dependency-graph.js +161 -0
- package/dist/code-index/index.d.ts +44 -0
- package/dist/code-index/index.js +124 -0
- package/dist/code-index/symbol-extractor.d.ts +13 -0
- package/dist/code-index/symbol-extractor.js +150 -0
- package/dist/export/index.d.ts +92 -0
- package/dist/export/index.js +676 -0
- package/dist/github/github-service.d.ts +146 -0
- package/dist/github/github-service.js +609 -0
- package/dist/impact-engine/index.d.ts +25 -0
- package/dist/impact-engine/index.js +284 -0
- package/dist/index.d.ts +60 -0
- package/dist/index.js +149 -0
- package/dist/metrics/index.d.ts +136 -0
- package/dist/metrics/index.js +525 -0
- package/dist/orchestrator/deepseek-optimizer.d.ts +67 -0
- package/dist/orchestrator/deepseek-optimizer.js +320 -0
- package/dist/orchestrator/index.d.ts +34 -0
- package/dist/orchestrator/index.js +305 -0
- package/dist/pr-guardian/index.d.ts +143 -0
- package/dist/pr-guardian/index.js +553 -0
- package/dist/refactoring/index.d.ts +108 -0
- package/dist/refactoring/index.js +580 -0
- package/dist/rules-engine/index.d.ts +129 -0
- package/dist/rules-engine/index.js +482 -0
- package/dist/semantic-memory/embedding-service.d.ts +24 -0
- package/dist/semantic-memory/embedding-service.js +120 -0
- package/dist/semantic-memory/index.d.ts +45 -0
- package/dist/semantic-memory/index.js +206 -0
- package/dist/semantic-memory/vector-store.d.ts +27 -0
- package/dist/semantic-memory/vector-store.js +166 -0
- package/dist/server/index.d.ts +28 -0
- package/dist/server/index.js +141 -0
- package/dist/server/middleware/api-auth.d.ts +43 -0
- package/dist/server/middleware/api-auth.js +256 -0
- package/dist/server/routes/admin.d.ts +5 -0
- package/dist/server/routes/admin.js +123 -0
- package/dist/server/routes/api.d.ts +7 -0
- package/dist/server/routes/api.js +362 -0
- package/dist/server/routes/auth.d.ts +16 -0
- package/dist/server/routes/auth.js +191 -0
- package/dist/server/routes/developer.d.ts +8 -0
- package/dist/server/routes/developer.js +439 -0
- package/dist/server/routes/github.d.ts +7 -0
- package/dist/server/routes/github.js +495 -0
- package/dist/server/routes/upload.d.ts +7 -0
- package/dist/server/routes/upload.js +196 -0
- package/dist/server/services/api-key-service.d.ts +81 -0
- package/dist/server/services/api-key-service.js +281 -0
- package/dist/server/services/auth-service.d.ts +40 -0
- package/dist/server/services/auth-service.js +315 -0
- package/dist/server/services/project-service.d.ts +123 -0
- package/dist/server/services/project-service.js +533 -0
- package/dist/server/services/token-service.d.ts +107 -0
- package/dist/server/services/token-service.js +416 -0
- package/dist/server/services/upload-service.d.ts +93 -0
- package/dist/server/services/upload-service.js +464 -0
- package/dist/types/api.d.ts +188 -0
- package/dist/types/api.js +86 -0
- package/dist/types/github.d.ts +335 -0
- package/dist/types/github.js +5 -0
- package/dist/types/index.d.ts +265 -0
- package/dist/types/index.js +32 -0
- package/dist/types/user.d.ts +69 -0
- package/dist/types/user.js +42 -0
- package/dist/utils/file-utils.d.ts +20 -0
- package/dist/utils/file-utils.js +163 -0
- package/dist/utils/logger.d.ts +17 -0
- package/dist/utils/logger.js +41 -0
- package/dist/watcher/index.d.ts +125 -0
- package/dist/watcher/index.js +397 -0
- package/package.json +71 -0
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Code Duplication Detector
|
|
3
|
+
*
|
|
4
|
+
* Оптимизированное обнаружение дублирования кода:
|
|
5
|
+
* - Использует хеширование строк (Rabin-Karp подход)
|
|
6
|
+
* - Ограничивает размер анализа для больших проектов
|
|
7
|
+
* - Не хранит весь код в памяти
|
|
8
|
+
*/
|
|
9
|
+
import { Logger } from '../utils/logger.js';
|
|
10
|
+
export class DuplicationDetector {
|
|
11
|
+
MIN_LINES = 6; // Минимум строк для детекции
|
|
12
|
+
WINDOW_SIZES = [6, 10, 15, 25]; // Фиксированные размеры окон
|
|
13
|
+
MAX_CLONES = 50; // Максимум клонов в результате
|
|
14
|
+
MAX_FILES = 500; // Максимум файлов для анализа
|
|
15
|
+
MAX_LINES_PER_FILE = 5000; // Максимум строк на файл
|
|
16
|
+
/**
|
|
17
|
+
* Анализ дублирования
|
|
18
|
+
*/
|
|
19
|
+
async analyze(fileContents) {
|
|
20
|
+
Logger.progress('Analyzing code duplication...');
|
|
21
|
+
// Ограничиваем размер для больших проектов
|
|
22
|
+
const files = this.limitFiles(fileContents);
|
|
23
|
+
// Считаем общее количество строк
|
|
24
|
+
let totalLines = 0;
|
|
25
|
+
for (const content of files.values()) {
|
|
26
|
+
totalLines += content.split('\n').length;
|
|
27
|
+
}
|
|
28
|
+
// Для очень больших проектов используем быстрый алгоритм
|
|
29
|
+
const clones = totalLines > 50000
|
|
30
|
+
? await this.findClonesQuick(files)
|
|
31
|
+
: await this.findClonesNormal(files);
|
|
32
|
+
const summary = this.calculateSummary(clones, totalLines, files.size);
|
|
33
|
+
Logger.success(`Found ${clones.length} code clones (${summary.duplicationPercentage}% duplication)`);
|
|
34
|
+
return { clones, summary };
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Ограничение файлов для анализа
|
|
38
|
+
*/
|
|
39
|
+
limitFiles(fileContents) {
|
|
40
|
+
if (fileContents.size <= this.MAX_FILES) {
|
|
41
|
+
return fileContents;
|
|
42
|
+
}
|
|
43
|
+
Logger.warn(`Too many files (${fileContents.size}), analyzing first ${this.MAX_FILES}`);
|
|
44
|
+
const limited = new Map();
|
|
45
|
+
let count = 0;
|
|
46
|
+
for (const [path, content] of fileContents) {
|
|
47
|
+
if (count >= this.MAX_FILES)
|
|
48
|
+
break;
|
|
49
|
+
limited.set(path, content);
|
|
50
|
+
count++;
|
|
51
|
+
}
|
|
52
|
+
return limited;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Быстрый алгоритм для больших проектов
|
|
56
|
+
* Использует только хеширование строк
|
|
57
|
+
*/
|
|
58
|
+
async findClonesQuick(fileContents) {
|
|
59
|
+
const lineHashes = new Map();
|
|
60
|
+
// Хешируем каждую строку
|
|
61
|
+
for (const [filePath, content] of fileContents) {
|
|
62
|
+
const lines = content.split('\n').slice(0, this.MAX_LINES_PER_FILE);
|
|
63
|
+
for (let i = 0; i < lines.length; i++) {
|
|
64
|
+
const normalized = this.normalizeLine(lines[i]);
|
|
65
|
+
if (normalized.length < 10)
|
|
66
|
+
continue; // Слишком короткая строка
|
|
67
|
+
const hash = this.quickHash(normalized);
|
|
68
|
+
const existing = lineHashes.get(hash) || [];
|
|
69
|
+
existing.push({ file: filePath, lineNum: i + 1, hash });
|
|
70
|
+
lineHashes.set(hash, existing);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
// Находим последовательности одинаковых строк
|
|
74
|
+
const clones = [];
|
|
75
|
+
const processed = new Set();
|
|
76
|
+
for (const [hash, locations] of lineHashes) {
|
|
77
|
+
if (locations.length < 2)
|
|
78
|
+
continue;
|
|
79
|
+
// Группируем по файлам
|
|
80
|
+
const byFile = new Map();
|
|
81
|
+
for (const loc of locations) {
|
|
82
|
+
const existing = byFile.get(loc.file) || [];
|
|
83
|
+
existing.push(loc.lineNum);
|
|
84
|
+
byFile.set(loc.file, existing);
|
|
85
|
+
}
|
|
86
|
+
// Ищем блоки в разных файлах
|
|
87
|
+
const files = Array.from(byFile.keys());
|
|
88
|
+
if (files.length < 2)
|
|
89
|
+
continue;
|
|
90
|
+
for (let i = 0; i < files.length && clones.length < this.MAX_CLONES; i++) {
|
|
91
|
+
for (let j = i + 1; j < files.length && clones.length < this.MAX_CLONES; j++) {
|
|
92
|
+
const file1 = files[i];
|
|
93
|
+
const file2 = files[j];
|
|
94
|
+
const lines1 = byFile.get(file1);
|
|
95
|
+
const lines2 = byFile.get(file2);
|
|
96
|
+
for (const line1 of lines1) {
|
|
97
|
+
for (const line2 of lines2) {
|
|
98
|
+
const key = `${file1}:${line1}-${file2}:${line2}`;
|
|
99
|
+
if (processed.has(key))
|
|
100
|
+
continue;
|
|
101
|
+
// Расширяем блок вниз
|
|
102
|
+
const blockSize = this.expandBlock(fileContents.get(file1), fileContents.get(file2), line1, line2);
|
|
103
|
+
if (blockSize >= this.MIN_LINES) {
|
|
104
|
+
processed.add(key);
|
|
105
|
+
const code1 = this.getCodeBlock(fileContents.get(file1), line1, line1 + blockSize - 1);
|
|
106
|
+
clones.push({
|
|
107
|
+
id: `clone-${hash.substring(0, 8)}-${clones.length}`,
|
|
108
|
+
type: 'exact',
|
|
109
|
+
instances: [
|
|
110
|
+
{ file: file1, startLine: line1, endLine: line1 + blockSize - 1, code: code1 },
|
|
111
|
+
{ file: file2, startLine: line2, endLine: line2 + blockSize - 1, code: code1 }
|
|
112
|
+
],
|
|
113
|
+
linesCount: blockSize,
|
|
114
|
+
similarity: 100,
|
|
115
|
+
suggestion: 'Создайте общий модуль/утилиту и импортируйте его'
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return this.deduplicateClones(clones);
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Обычный алгоритм для средних проектов
|
|
127
|
+
*/
|
|
128
|
+
async findClonesNormal(fileContents) {
|
|
129
|
+
const clones = [];
|
|
130
|
+
for (const windowSize of this.WINDOW_SIZES) {
|
|
131
|
+
if (clones.length >= this.MAX_CLONES)
|
|
132
|
+
break;
|
|
133
|
+
const blockHashes = new Map();
|
|
134
|
+
// Собираем хеши блоков
|
|
135
|
+
for (const [filePath, content] of fileContents) {
|
|
136
|
+
const lines = content.split('\n').slice(0, this.MAX_LINES_PER_FILE);
|
|
137
|
+
for (let start = 0; start <= lines.length - windowSize; start++) {
|
|
138
|
+
const blockLines = lines.slice(start, start + windowSize);
|
|
139
|
+
const normalized = this.normalizeBlock(blockLines);
|
|
140
|
+
if (normalized.length < 20)
|
|
141
|
+
continue;
|
|
142
|
+
const hash = this.quickHash(normalized);
|
|
143
|
+
const existing = blockHashes.get(hash) || [];
|
|
144
|
+
existing.push({
|
|
145
|
+
file: filePath,
|
|
146
|
+
startLine: start + 1,
|
|
147
|
+
code: blockLines.join('\n')
|
|
148
|
+
});
|
|
149
|
+
blockHashes.set(hash, existing);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
// Находим дубликаты
|
|
153
|
+
for (const [hash, blocks] of blockHashes) {
|
|
154
|
+
if (blocks.length < 2)
|
|
155
|
+
continue;
|
|
156
|
+
if (clones.length >= this.MAX_CLONES)
|
|
157
|
+
break;
|
|
158
|
+
// Фильтруем перекрывающиеся блоки
|
|
159
|
+
const filtered = this.filterOverlapping(blocks, windowSize);
|
|
160
|
+
if (filtered.length < 2)
|
|
161
|
+
continue;
|
|
162
|
+
// Проверяем, что это не все в одном файле рядом
|
|
163
|
+
const uniqueFiles = new Set(filtered.map(b => b.file));
|
|
164
|
+
const inSameFile = uniqueFiles.size === 1;
|
|
165
|
+
if (inSameFile) {
|
|
166
|
+
// Проверяем что блоки не рядом
|
|
167
|
+
const sorted = [...filtered].sort((a, b) => a.startLine - b.startLine);
|
|
168
|
+
let hasDistant = false;
|
|
169
|
+
for (let i = 1; i < sorted.length; i++) {
|
|
170
|
+
if (sorted[i].startLine - sorted[i - 1].startLine > windowSize * 2) {
|
|
171
|
+
hasDistant = true;
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
if (!hasDistant)
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
clones.push({
|
|
179
|
+
id: `clone-${hash.substring(0, 8)}`,
|
|
180
|
+
type: 'exact',
|
|
181
|
+
instances: filtered.slice(0, 5).map(b => ({
|
|
182
|
+
file: b.file,
|
|
183
|
+
startLine: b.startLine,
|
|
184
|
+
endLine: b.startLine + windowSize - 1,
|
|
185
|
+
code: b.code
|
|
186
|
+
})),
|
|
187
|
+
linesCount: windowSize,
|
|
188
|
+
similarity: 100,
|
|
189
|
+
suggestion: inSameFile
|
|
190
|
+
? 'Извлеките дублированный код в отдельную функцию'
|
|
191
|
+
: 'Создайте общий модуль/утилиту и импортируйте его'
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return this.deduplicateClones(clones);
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Расширение блока вниз пока строки совпадают
|
|
199
|
+
*/
|
|
200
|
+
expandBlock(content1, content2, line1, line2) {
|
|
201
|
+
const lines1 = content1.split('\n');
|
|
202
|
+
const lines2 = content2.split('\n');
|
|
203
|
+
let size = 1;
|
|
204
|
+
const maxExpand = Math.min(50, lines1.length - line1 + 1, lines2.length - line2 + 1);
|
|
205
|
+
while (size < maxExpand) {
|
|
206
|
+
const idx1 = line1 - 1 + size;
|
|
207
|
+
const idx2 = line2 - 1 + size;
|
|
208
|
+
if (idx1 >= lines1.length || idx2 >= lines2.length)
|
|
209
|
+
break;
|
|
210
|
+
const norm1 = this.normalizeLine(lines1[idx1]);
|
|
211
|
+
const norm2 = this.normalizeLine(lines2[idx2]);
|
|
212
|
+
if (norm1 !== norm2 || norm1.length < 3)
|
|
213
|
+
break;
|
|
214
|
+
size++;
|
|
215
|
+
}
|
|
216
|
+
return size;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Получить блок кода по номерам строк
|
|
220
|
+
*/
|
|
221
|
+
getCodeBlock(content, startLine, endLine) {
|
|
222
|
+
const lines = content.split('\n');
|
|
223
|
+
return lines.slice(startLine - 1, endLine).join('\n');
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Нормализация одной строки
|
|
227
|
+
*/
|
|
228
|
+
normalizeLine(line) {
|
|
229
|
+
return line
|
|
230
|
+
.replace(/\/\/.*$/, '') // Удаляем однострочные комментарии
|
|
231
|
+
.replace(/['"`][^'"`]*['"`]/g, '""') // Нормализуем строки
|
|
232
|
+
.replace(/\s+/g, ' ') // Нормализуем пробелы
|
|
233
|
+
.trim();
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Нормализация блока строк
|
|
237
|
+
*/
|
|
238
|
+
normalizeBlock(lines) {
|
|
239
|
+
return lines
|
|
240
|
+
.map(l => this.normalizeLine(l))
|
|
241
|
+
.filter(l => l.length > 0)
|
|
242
|
+
.join('\n');
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Быстрое хеширование
|
|
246
|
+
*/
|
|
247
|
+
quickHash(str) {
|
|
248
|
+
// Используем короткий хеш для скорости
|
|
249
|
+
let hash = 0;
|
|
250
|
+
for (let i = 0; i < str.length; i++) {
|
|
251
|
+
const char = str.charCodeAt(i);
|
|
252
|
+
hash = ((hash << 5) - hash) + char;
|
|
253
|
+
hash = hash & hash;
|
|
254
|
+
}
|
|
255
|
+
return hash.toString(36);
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Фильтрация перекрывающихся блоков
|
|
259
|
+
*/
|
|
260
|
+
filterOverlapping(blocks, windowSize) {
|
|
261
|
+
const byFile = new Map();
|
|
262
|
+
for (const block of blocks) {
|
|
263
|
+
const existing = byFile.get(block.file) || [];
|
|
264
|
+
existing.push(block);
|
|
265
|
+
byFile.set(block.file, existing);
|
|
266
|
+
}
|
|
267
|
+
const result = [];
|
|
268
|
+
for (const fileBlocks of byFile.values()) {
|
|
269
|
+
fileBlocks.sort((a, b) => a.startLine - b.startLine);
|
|
270
|
+
let lastEnd = -windowSize;
|
|
271
|
+
for (const block of fileBlocks) {
|
|
272
|
+
if (block.startLine > lastEnd) {
|
|
273
|
+
result.push(block);
|
|
274
|
+
lastEnd = block.startLine + windowSize;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
return result;
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Удаление дубликатов клонов (перекрывающихся)
|
|
282
|
+
*/
|
|
283
|
+
deduplicateClones(clones) {
|
|
284
|
+
// Сортируем по размеру (больше лучше)
|
|
285
|
+
clones.sort((a, b) => b.linesCount - a.linesCount);
|
|
286
|
+
const result = [];
|
|
287
|
+
const covered = new Set();
|
|
288
|
+
for (const clone of clones) {
|
|
289
|
+
let isDuplicate = false;
|
|
290
|
+
for (const instance of clone.instances) {
|
|
291
|
+
for (let line = instance.startLine; line <= instance.endLine; line++) {
|
|
292
|
+
const key = `${instance.file}:${line}`;
|
|
293
|
+
if (covered.has(key)) {
|
|
294
|
+
isDuplicate = true;
|
|
295
|
+
break;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
if (isDuplicate)
|
|
299
|
+
break;
|
|
300
|
+
}
|
|
301
|
+
if (!isDuplicate) {
|
|
302
|
+
result.push(clone);
|
|
303
|
+
// Отмечаем строки как покрытые
|
|
304
|
+
for (const instance of clone.instances) {
|
|
305
|
+
for (let line = instance.startLine; line <= instance.endLine; line++) {
|
|
306
|
+
covered.add(`${instance.file}:${line}`);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
if (result.length >= this.MAX_CLONES)
|
|
311
|
+
break;
|
|
312
|
+
}
|
|
313
|
+
return result;
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* Подсчёт статистики
|
|
317
|
+
*/
|
|
318
|
+
calculateSummary(clones, totalLines, _totalFiles) {
|
|
319
|
+
const duplicatedLines = new Set();
|
|
320
|
+
for (const clone of clones) {
|
|
321
|
+
for (const instance of clone.instances) {
|
|
322
|
+
for (let line = instance.startLine; line <= instance.endLine; line++) {
|
|
323
|
+
duplicatedLines.add(`${instance.file}:${line}`);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
const totalDuplicatedLines = duplicatedLines.size;
|
|
328
|
+
const duplicationPercentage = totalLines > 0
|
|
329
|
+
? Math.round((totalDuplicatedLines / totalLines) * 100)
|
|
330
|
+
: 0;
|
|
331
|
+
const filesAffected = new Set(clones.flatMap(c => c.instances.map(i => i.file))).size;
|
|
332
|
+
const estimatedRefactorHours = clones.reduce((sum, clone) => {
|
|
333
|
+
return sum + 0.25 + clone.instances.length * 0.08;
|
|
334
|
+
}, 0);
|
|
335
|
+
return {
|
|
336
|
+
totalClones: clones.length,
|
|
337
|
+
totalDuplicatedLines,
|
|
338
|
+
duplicationPercentage,
|
|
339
|
+
filesAffected,
|
|
340
|
+
estimatedRefactorHours: Math.round(estimatedRefactorHours * 10) / 10
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
//# sourceMappingURL=duplication.js.map
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Security Analyzer
|
|
3
|
+
*
|
|
4
|
+
* Обнаружение уязвимостей безопасности:
|
|
5
|
+
* - SQL Injection
|
|
6
|
+
* - XSS (Cross-Site Scripting)
|
|
7
|
+
* - Command Injection
|
|
8
|
+
* - Path Traversal
|
|
9
|
+
* - Hardcoded Secrets
|
|
10
|
+
* - Insecure Dependencies
|
|
11
|
+
* - OWASP Top 10
|
|
12
|
+
*/
|
|
13
|
+
export type VulnerabilitySeverity = 'critical' | 'high' | 'medium' | 'low' | 'info';
|
|
14
|
+
export interface SecurityResult {
|
|
15
|
+
vulnerabilities: Vulnerability[];
|
|
16
|
+
secrets: HardcodedSecret[];
|
|
17
|
+
summary: SecuritySummary;
|
|
18
|
+
}
|
|
19
|
+
export interface Vulnerability {
|
|
20
|
+
id: string;
|
|
21
|
+
type: VulnerabilityType;
|
|
22
|
+
severity: VulnerabilitySeverity;
|
|
23
|
+
title: string;
|
|
24
|
+
description: string;
|
|
25
|
+
file: string;
|
|
26
|
+
line: number;
|
|
27
|
+
code: string;
|
|
28
|
+
cwe?: string;
|
|
29
|
+
owasp?: string;
|
|
30
|
+
remediation: string;
|
|
31
|
+
}
|
|
32
|
+
export type VulnerabilityType = 'sql-injection' | 'xss' | 'command-injection' | 'path-traversal' | 'hardcoded-secret' | 'insecure-random' | 'weak-crypto' | 'open-redirect' | 'ssrf' | 'xxe' | 'insecure-deserialization' | 'sensitive-data-exposure' | 'missing-auth' | 'broken-access' | 'security-misconfiguration' | 'prototype-pollution' | 'regex-dos';
|
|
33
|
+
export interface HardcodedSecret {
|
|
34
|
+
type: SecretType;
|
|
35
|
+
file: string;
|
|
36
|
+
line: number;
|
|
37
|
+
preview: string;
|
|
38
|
+
confidence: 'high' | 'medium' | 'low';
|
|
39
|
+
}
|
|
40
|
+
export type SecretType = 'api-key' | 'password' | 'private-key' | 'jwt-secret' | 'aws-key' | 'database-url' | 'oauth-secret' | 'generic-secret';
|
|
41
|
+
export interface SecuritySummary {
|
|
42
|
+
totalVulnerabilities: number;
|
|
43
|
+
critical: number;
|
|
44
|
+
high: number;
|
|
45
|
+
medium: number;
|
|
46
|
+
low: number;
|
|
47
|
+
secretsFound: number;
|
|
48
|
+
riskScore: number;
|
|
49
|
+
grade: 'A' | 'B' | 'C' | 'D' | 'F';
|
|
50
|
+
}
|
|
51
|
+
export declare class SecurityAnalyzer {
|
|
52
|
+
private readonly vulnerabilityPatterns;
|
|
53
|
+
private readonly secretPatterns;
|
|
54
|
+
/**
|
|
55
|
+
* Анализ безопасности
|
|
56
|
+
*/
|
|
57
|
+
analyze(fileContents: Map<string, string>): Promise<SecurityResult>;
|
|
58
|
+
/**
|
|
59
|
+
* Получить номер строки по индексу
|
|
60
|
+
*/
|
|
61
|
+
private getLineNumber;
|
|
62
|
+
/**
|
|
63
|
+
* Маскировка секрета
|
|
64
|
+
*/
|
|
65
|
+
private maskSecret;
|
|
66
|
+
/**
|
|
67
|
+
* Удаление дубликатов уязвимостей
|
|
68
|
+
*/
|
|
69
|
+
private deduplicateVulnerabilities;
|
|
70
|
+
/**
|
|
71
|
+
* Удаление дубликатов секретов
|
|
72
|
+
*/
|
|
73
|
+
private deduplicateSecrets;
|
|
74
|
+
/**
|
|
75
|
+
* Подсчёт статистики
|
|
76
|
+
*/
|
|
77
|
+
private calculateSummary;
|
|
78
|
+
}
|
|
79
|
+
//# sourceMappingURL=security.d.ts.map
|