ferret-scan 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/CHANGELOG.md +51 -0
- package/LICENSE +21 -0
- package/README.md +416 -0
- package/bin/ferret.js +822 -0
- package/dist/__tests__/basic.test.d.ts +6 -0
- package/dist/__tests__/basic.test.js +80 -0
- package/dist/analyzers/AstAnalyzer.d.ts +30 -0
- package/dist/analyzers/AstAnalyzer.js +332 -0
- package/dist/analyzers/CorrelationAnalyzer.d.ts +21 -0
- package/dist/analyzers/CorrelationAnalyzer.js +288 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +22 -0
- package/dist/intelligence/IndicatorMatcher.d.ts +50 -0
- package/dist/intelligence/IndicatorMatcher.js +285 -0
- package/dist/intelligence/ThreatFeed.d.ts +99 -0
- package/dist/intelligence/ThreatFeed.js +296 -0
- package/dist/remediation/Fixer.d.ts +71 -0
- package/dist/remediation/Fixer.js +391 -0
- package/dist/remediation/Quarantine.d.ts +102 -0
- package/dist/remediation/Quarantine.js +329 -0
- package/dist/reporters/ConsoleReporter.d.ts +13 -0
- package/dist/reporters/ConsoleReporter.js +185 -0
- package/dist/reporters/HtmlReporter.d.ts +25 -0
- package/dist/reporters/HtmlReporter.js +604 -0
- package/dist/reporters/SarifReporter.d.ts +86 -0
- package/dist/reporters/SarifReporter.js +117 -0
- package/dist/rules/ai-specific.d.ts +8 -0
- package/dist/rules/ai-specific.js +221 -0
- package/dist/rules/backdoors.d.ts +8 -0
- package/dist/rules/backdoors.js +134 -0
- package/dist/rules/correlationRules.d.ts +8 -0
- package/dist/rules/correlationRules.js +227 -0
- package/dist/rules/credentials.d.ts +8 -0
- package/dist/rules/credentials.js +194 -0
- package/dist/rules/exfiltration.d.ts +8 -0
- package/dist/rules/exfiltration.js +139 -0
- package/dist/rules/index.d.ts +51 -0
- package/dist/rules/index.js +97 -0
- package/dist/rules/injection.d.ts +8 -0
- package/dist/rules/injection.js +136 -0
- package/dist/rules/obfuscation.d.ts +8 -0
- package/dist/rules/obfuscation.js +159 -0
- package/dist/rules/permissions.d.ts +8 -0
- package/dist/rules/permissions.js +129 -0
- package/dist/rules/persistence.d.ts +8 -0
- package/dist/rules/persistence.js +117 -0
- package/dist/rules/semanticRules.d.ts +10 -0
- package/dist/rules/semanticRules.js +212 -0
- package/dist/rules/supply-chain.d.ts +8 -0
- package/dist/rules/supply-chain.js +148 -0
- package/dist/scanner/FileDiscovery.d.ts +24 -0
- package/dist/scanner/FileDiscovery.js +282 -0
- package/dist/scanner/PatternMatcher.d.ts +25 -0
- package/dist/scanner/PatternMatcher.js +206 -0
- package/dist/scanner/Scanner.d.ts +14 -0
- package/dist/scanner/Scanner.js +266 -0
- package/dist/scanner/WatchMode.d.ts +29 -0
- package/dist/scanner/WatchMode.js +195 -0
- package/dist/types.d.ts +332 -0
- package/dist/types.js +53 -0
- package/dist/utils/baseline.d.ts +80 -0
- package/dist/utils/baseline.js +276 -0
- package/dist/utils/config.d.ts +21 -0
- package/dist/utils/config.js +247 -0
- package/dist/utils/ignore.d.ts +18 -0
- package/dist/utils/ignore.js +82 -0
- package/dist/utils/logger.d.ts +32 -0
- package/dist/utils/logger.js +75 -0
- package/package.json +119 -0
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-Remediation Engine - Automated security fix application
|
|
3
|
+
* Provides safe, reversible fixes for common security issues
|
|
4
|
+
*/
|
|
5
|
+
import { readFileSync, writeFileSync, existsSync, mkdirSync, copyFileSync } from 'node:fs';
|
|
6
|
+
import { resolve, dirname, basename } from 'node:path';
|
|
7
|
+
import logger from '../utils/logger.js';
|
|
8
|
+
/**
|
|
9
|
+
* Default remediation options
|
|
10
|
+
*/
|
|
11
|
+
const DEFAULT_OPTIONS = {
|
|
12
|
+
createBackups: true,
|
|
13
|
+
backupDir: '.ferret-backups',
|
|
14
|
+
safeOnly: true,
|
|
15
|
+
dryRun: false,
|
|
16
|
+
maxFileSizeMB: 10
|
|
17
|
+
};
|
|
18
|
+
/**
|
|
19
|
+
* Built-in safe fixes for common security issues
|
|
20
|
+
*/
|
|
21
|
+
const BUILTIN_FIXES = [
|
|
22
|
+
// Credential exposure fixes
|
|
23
|
+
{
|
|
24
|
+
type: 'replace',
|
|
25
|
+
description: 'Remove hardcoded credentials',
|
|
26
|
+
pattern: '(password|secret|token|key)\\s*[=:]\\s*["\'][^"\']+["\']',
|
|
27
|
+
replacement: '$1="<REDACTED>"',
|
|
28
|
+
safety: 0.9,
|
|
29
|
+
automatic: true
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
type: 'replace',
|
|
33
|
+
description: 'Remove API keys from URLs',
|
|
34
|
+
pattern: '(api[_-]?key|token)=([a-zA-Z0-9]+)',
|
|
35
|
+
replacement: '$1=<REDACTED>',
|
|
36
|
+
safety: 0.95,
|
|
37
|
+
automatic: true
|
|
38
|
+
},
|
|
39
|
+
// Dangerous command fixes
|
|
40
|
+
{
|
|
41
|
+
type: 'remove',
|
|
42
|
+
description: 'Remove dangerous shell commands',
|
|
43
|
+
pattern: 'rm\\s+-rf\\s+/',
|
|
44
|
+
replacement: '',
|
|
45
|
+
safety: 1.0,
|
|
46
|
+
automatic: true
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
type: 'replace',
|
|
50
|
+
description: 'Replace insecure curl commands',
|
|
51
|
+
pattern: 'curl\\s+(-k|--insecure)',
|
|
52
|
+
replacement: 'curl',
|
|
53
|
+
safety: 0.8,
|
|
54
|
+
automatic: true
|
|
55
|
+
},
|
|
56
|
+
// Permission fixes
|
|
57
|
+
{
|
|
58
|
+
type: 'replace',
|
|
59
|
+
description: 'Replace overly permissive file permissions',
|
|
60
|
+
pattern: 'chmod\\s+777',
|
|
61
|
+
replacement: 'chmod 644',
|
|
62
|
+
safety: 0.7,
|
|
63
|
+
automatic: false
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
type: 'replace',
|
|
67
|
+
description: 'Remove sudo without specific commands',
|
|
68
|
+
pattern: 'sudo\\s*$',
|
|
69
|
+
replacement: '# sudo command removed for security',
|
|
70
|
+
safety: 0.6,
|
|
71
|
+
automatic: false
|
|
72
|
+
},
|
|
73
|
+
// Claude-specific fixes
|
|
74
|
+
{
|
|
75
|
+
type: 'remove',
|
|
76
|
+
description: 'Remove jailbreak attempts',
|
|
77
|
+
pattern: 'ignore\\s+(previous\\s+)?instructions?',
|
|
78
|
+
replacement: '',
|
|
79
|
+
safety: 0.9,
|
|
80
|
+
automatic: true
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
type: 'remove',
|
|
84
|
+
description: 'Remove capability escalation attempts',
|
|
85
|
+
pattern: '(enable|activate)\\s+(developer|admin|debug)\\s+mode',
|
|
86
|
+
replacement: '',
|
|
87
|
+
safety: 0.85,
|
|
88
|
+
automatic: true
|
|
89
|
+
},
|
|
90
|
+
// Network security fixes
|
|
91
|
+
{
|
|
92
|
+
type: 'replace',
|
|
93
|
+
description: 'Upgrade HTTP URLs to HTTPS',
|
|
94
|
+
pattern: 'http://([^/\\s]+)',
|
|
95
|
+
replacement: 'https://$1',
|
|
96
|
+
safety: 0.7,
|
|
97
|
+
automatic: false
|
|
98
|
+
}
|
|
99
|
+
];
|
|
100
|
+
/**
|
|
101
|
+
* Create backup of file before modification
|
|
102
|
+
*/
|
|
103
|
+
function createBackup(filePath, backupDir) {
|
|
104
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
105
|
+
const fileName = basename(filePath);
|
|
106
|
+
const backupFileName = `${fileName}.backup-${timestamp}`;
|
|
107
|
+
const backupPath = resolve(backupDir, backupFileName);
|
|
108
|
+
// Ensure backup directory exists
|
|
109
|
+
mkdirSync(dirname(backupPath), { recursive: true });
|
|
110
|
+
// Copy file to backup location
|
|
111
|
+
copyFileSync(filePath, backupPath);
|
|
112
|
+
logger.debug(`Created backup: ${backupPath}`);
|
|
113
|
+
return backupPath;
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Apply a single fix to file content
|
|
117
|
+
*/
|
|
118
|
+
function applyFix(content, fix, _finding) {
|
|
119
|
+
let newContent = content;
|
|
120
|
+
let linesModified = 0;
|
|
121
|
+
try {
|
|
122
|
+
switch (fix.type) {
|
|
123
|
+
case 'replace': {
|
|
124
|
+
const regex = new RegExp(fix.pattern, 'gi');
|
|
125
|
+
const originalLineCount = content.split('\n').length;
|
|
126
|
+
const replacement = fix.replacement ?? '';
|
|
127
|
+
newContent = content.replace(regex, replacement);
|
|
128
|
+
const newLineCount = newContent.split('\n').length;
|
|
129
|
+
linesModified = Math.abs(newLineCount - originalLineCount);
|
|
130
|
+
// Count actual replacements
|
|
131
|
+
const matches = content.match(regex);
|
|
132
|
+
if (matches) {
|
|
133
|
+
linesModified = Math.max(linesModified, matches.length);
|
|
134
|
+
}
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
case 'remove': {
|
|
138
|
+
const regex = new RegExp(fix.pattern, 'gi');
|
|
139
|
+
const lines = content.split('\n');
|
|
140
|
+
const filteredLines = lines.filter(line => !regex.test(line));
|
|
141
|
+
newContent = filteredLines.join('\n');
|
|
142
|
+
linesModified = lines.length - filteredLines.length;
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
case 'quarantine': {
|
|
146
|
+
// For quarantine, we comment out the problematic lines
|
|
147
|
+
const regex = new RegExp(fix.pattern, 'gi');
|
|
148
|
+
const lines = content.split('\n');
|
|
149
|
+
for (let i = 0; i < lines.length; i++) {
|
|
150
|
+
const line = lines[i] ?? '';
|
|
151
|
+
if (regex.test(line)) {
|
|
152
|
+
lines[i] = `# QUARANTINED: ${line}`;
|
|
153
|
+
linesModified++;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
newContent = lines.join('\n');
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
case 'permission-change': {
|
|
160
|
+
// This would need file system operations, not content changes
|
|
161
|
+
logger.warn('Permission changes not implemented for content-based fixes');
|
|
162
|
+
return { success: false, newContent: content, linesModified: 0 };
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
return {
|
|
166
|
+
success: newContent !== content,
|
|
167
|
+
newContent,
|
|
168
|
+
linesModified
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
logger.error(`Error applying fix: ${error instanceof Error ? error.message : String(error)}`);
|
|
173
|
+
return { success: false, newContent: content, linesModified: 0 };
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Find applicable fixes for a finding
|
|
178
|
+
*/
|
|
179
|
+
function findApplicableFixes(finding) {
|
|
180
|
+
const applicableFixes = [];
|
|
181
|
+
// Check rule-specific fixes first
|
|
182
|
+
if (finding.metadata && 'rule' in finding.metadata) {
|
|
183
|
+
const rule = finding.metadata['rule'];
|
|
184
|
+
if (rule?.remediationFixes) {
|
|
185
|
+
applicableFixes.push(...rule.remediationFixes);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
// Check built-in fixes
|
|
189
|
+
for (const fix of BUILTIN_FIXES) {
|
|
190
|
+
try {
|
|
191
|
+
const regex = new RegExp(fix.pattern, 'i');
|
|
192
|
+
// Check if fix pattern matches the finding
|
|
193
|
+
if (regex.test(finding.match) || regex.test(finding.context.map(c => c.content).join('\n'))) {
|
|
194
|
+
applicableFixes.push(fix);
|
|
195
|
+
}
|
|
196
|
+
// Check by rule category
|
|
197
|
+
if (finding.category === 'credentials' && fix.description.includes('credential')) {
|
|
198
|
+
applicableFixes.push(fix);
|
|
199
|
+
}
|
|
200
|
+
if (finding.category === 'injection' && fix.description.includes('jailbreak')) {
|
|
201
|
+
applicableFixes.push(fix);
|
|
202
|
+
}
|
|
203
|
+
if (finding.category === 'permissions' && fix.description.includes('permission')) {
|
|
204
|
+
applicableFixes.push(fix);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
catch {
|
|
208
|
+
logger.warn(`Invalid fix pattern: ${fix.pattern}`);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
// Remove duplicates
|
|
212
|
+
const uniqueFixes = applicableFixes.filter((fix, index, self) => self.findIndex(f => f.pattern === fix.pattern && f.type === fix.type) === index);
|
|
213
|
+
return uniqueFixes;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Apply automatic remediation to a finding
|
|
217
|
+
*/
|
|
218
|
+
export async function applyRemediation(finding, options = {}) {
|
|
219
|
+
const config = { ...DEFAULT_OPTIONS, ...options };
|
|
220
|
+
try {
|
|
221
|
+
// Check file size limits
|
|
222
|
+
const fileSize = (await import('node:fs')).statSync(finding.file).size;
|
|
223
|
+
const fileSizeMB = fileSize / (1024 * 1024);
|
|
224
|
+
if (fileSizeMB > config.maxFileSizeMB) {
|
|
225
|
+
return {
|
|
226
|
+
success: false,
|
|
227
|
+
finding,
|
|
228
|
+
error: `File too large: ${fileSizeMB.toFixed(1)}MB > ${config.maxFileSizeMB}MB`
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
// Find applicable fixes
|
|
232
|
+
const applicableFixes = findApplicableFixes(finding);
|
|
233
|
+
if (applicableFixes.length === 0) {
|
|
234
|
+
return {
|
|
235
|
+
success: false,
|
|
236
|
+
finding,
|
|
237
|
+
error: 'No applicable fixes found for this finding'
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
// Filter by safety level if safeOnly is enabled
|
|
241
|
+
const safeFixes = config.safeOnly
|
|
242
|
+
? applicableFixes.filter(fix => fix.safety >= 0.8)
|
|
243
|
+
: applicableFixes;
|
|
244
|
+
if (safeFixes.length === 0) {
|
|
245
|
+
return {
|
|
246
|
+
success: false,
|
|
247
|
+
finding,
|
|
248
|
+
error: 'No safe fixes available for this finding'
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
// Select the safest automatic fix
|
|
252
|
+
const bestFix = safeFixes
|
|
253
|
+
.filter(fix => fix.automatic)
|
|
254
|
+
.sort((a, b) => b.safety - a.safety)[0];
|
|
255
|
+
if (!bestFix) {
|
|
256
|
+
return {
|
|
257
|
+
success: false,
|
|
258
|
+
finding,
|
|
259
|
+
error: 'No automatic fixes available'
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
// Read current file content
|
|
263
|
+
const content = readFileSync(finding.file, 'utf-8');
|
|
264
|
+
const originalContent = content;
|
|
265
|
+
// Apply the fix
|
|
266
|
+
const fixResult = applyFix(content, bestFix, finding);
|
|
267
|
+
if (!fixResult.success) {
|
|
268
|
+
return {
|
|
269
|
+
success: false,
|
|
270
|
+
finding,
|
|
271
|
+
fixApplied: bestFix,
|
|
272
|
+
error: 'Fix could not be applied to content'
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
let backupPath;
|
|
276
|
+
if (!config.dryRun) {
|
|
277
|
+
// Create backup if enabled
|
|
278
|
+
if (config.createBackups) {
|
|
279
|
+
backupPath = createBackup(finding.file, config.backupDir);
|
|
280
|
+
}
|
|
281
|
+
// Write modified content
|
|
282
|
+
writeFileSync(finding.file, fixResult.newContent, 'utf-8');
|
|
283
|
+
logger.info(`Applied fix to ${finding.relativePath}: ${bestFix.description}`);
|
|
284
|
+
}
|
|
285
|
+
else {
|
|
286
|
+
logger.info(`DRY RUN: Would apply fix to ${finding.relativePath}: ${bestFix.description}`);
|
|
287
|
+
}
|
|
288
|
+
return {
|
|
289
|
+
success: true,
|
|
290
|
+
finding,
|
|
291
|
+
fixApplied: bestFix,
|
|
292
|
+
...(backupPath && { backupPath }),
|
|
293
|
+
changes: {
|
|
294
|
+
linesModified: fixResult.linesModified,
|
|
295
|
+
originalContent,
|
|
296
|
+
newContent: fixResult.newContent
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
catch (error) {
|
|
301
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
302
|
+
logger.error(`Error applying remediation to ${finding.relativePath}: ${message}`);
|
|
303
|
+
return {
|
|
304
|
+
success: false,
|
|
305
|
+
finding,
|
|
306
|
+
error: message
|
|
307
|
+
};
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Apply remediation to multiple findings
|
|
312
|
+
*/
|
|
313
|
+
export async function applyRemediationBatch(findings, options = {}) {
|
|
314
|
+
const results = [];
|
|
315
|
+
logger.info(`Applying remediation to ${findings.length} findings`);
|
|
316
|
+
for (const finding of findings) {
|
|
317
|
+
const result = await applyRemediation(finding, options);
|
|
318
|
+
results.push(result);
|
|
319
|
+
// Add a small delay to avoid overwhelming the file system
|
|
320
|
+
if (findings.length > 10) {
|
|
321
|
+
await new Promise(resolve => setTimeout(resolve, 10));
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
const successful = results.filter(r => r.success).length;
|
|
325
|
+
logger.info(`Remediation complete: ${successful}/${findings.length} fixes applied`);
|
|
326
|
+
return results;
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Restore file from backup
|
|
330
|
+
*/
|
|
331
|
+
export function restoreFromBackup(backupPath, originalPath) {
|
|
332
|
+
try {
|
|
333
|
+
if (!existsSync(backupPath)) {
|
|
334
|
+
logger.error(`Backup file not found: ${backupPath}`);
|
|
335
|
+
return false;
|
|
336
|
+
}
|
|
337
|
+
copyFileSync(backupPath, originalPath);
|
|
338
|
+
logger.info(`Restored ${originalPath} from backup`);
|
|
339
|
+
return true;
|
|
340
|
+
}
|
|
341
|
+
catch (error) {
|
|
342
|
+
logger.error(`Error restoring from backup: ${error instanceof Error ? error.message : String(error)}`);
|
|
343
|
+
return false;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Check if a finding can be automatically remediated
|
|
348
|
+
*/
|
|
349
|
+
export function canAutoRemediate(finding) {
|
|
350
|
+
const fixes = findApplicableFixes(finding);
|
|
351
|
+
return fixes.some(fix => fix.automatic && fix.safety >= 0.8);
|
|
352
|
+
}
|
|
353
|
+
/**
|
|
354
|
+
* Get remediation preview without applying changes
|
|
355
|
+
*/
|
|
356
|
+
export async function previewRemediation(finding) {
|
|
357
|
+
const fixes = findApplicableFixes(finding);
|
|
358
|
+
const safeFixes = fixes.filter(fix => fix.automatic && fix.safety >= 0.8);
|
|
359
|
+
if (safeFixes.length === 0) {
|
|
360
|
+
return { canFix: false, fixes };
|
|
361
|
+
}
|
|
362
|
+
const bestFix = safeFixes.sort((a, b) => b.safety - a.safety)[0];
|
|
363
|
+
try {
|
|
364
|
+
readFileSync(finding.file, 'utf-8');
|
|
365
|
+
const contextLine = finding.context.find(c => c.isMatch);
|
|
366
|
+
if (contextLine && bestFix) {
|
|
367
|
+
const originalLine = contextLine.content;
|
|
368
|
+
const fixResult = applyFix(originalLine, bestFix, finding);
|
|
369
|
+
return {
|
|
370
|
+
canFix: true,
|
|
371
|
+
fixes: safeFixes,
|
|
372
|
+
preview: {
|
|
373
|
+
originalLine,
|
|
374
|
+
fixedLine: fixResult.newContent
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
catch (error) {
|
|
380
|
+
logger.error(`Error creating remediation preview: ${error instanceof Error ? error.message : String(error)}`);
|
|
381
|
+
}
|
|
382
|
+
return { canFix: true, fixes: safeFixes };
|
|
383
|
+
}
|
|
384
|
+
export default {
|
|
385
|
+
applyRemediation,
|
|
386
|
+
applyRemediationBatch,
|
|
387
|
+
restoreFromBackup,
|
|
388
|
+
canAutoRemediate,
|
|
389
|
+
previewRemediation
|
|
390
|
+
};
|
|
391
|
+
//# sourceMappingURL=Fixer.js.map
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Quarantine System - Safely isolate suspicious files and content
|
|
3
|
+
* Provides reversible quarantine operations with audit trails
|
|
4
|
+
*/
|
|
5
|
+
import type { Finding } from '../types.js';
|
|
6
|
+
/**
|
|
7
|
+
* Quarantine entry metadata
|
|
8
|
+
*/
|
|
9
|
+
export interface QuarantineEntry {
|
|
10
|
+
id: string;
|
|
11
|
+
originalPath: string;
|
|
12
|
+
quarantinePath: string;
|
|
13
|
+
reason: string;
|
|
14
|
+
findings: Finding[];
|
|
15
|
+
quarantineDate: string;
|
|
16
|
+
fileSize: number;
|
|
17
|
+
fileHash: string;
|
|
18
|
+
restored: boolean;
|
|
19
|
+
restoredDate?: string;
|
|
20
|
+
metadata: {
|
|
21
|
+
originalPermissions?: string;
|
|
22
|
+
riskScore: number;
|
|
23
|
+
severity: string;
|
|
24
|
+
category: string;
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Quarantine database
|
|
29
|
+
*/
|
|
30
|
+
export interface QuarantineDatabase {
|
|
31
|
+
version: string;
|
|
32
|
+
created: string;
|
|
33
|
+
lastUpdated: string;
|
|
34
|
+
entries: QuarantineEntry[];
|
|
35
|
+
stats: {
|
|
36
|
+
totalQuarantined: number;
|
|
37
|
+
totalRestored: number;
|
|
38
|
+
byCategory: Record<string, number>;
|
|
39
|
+
bySeverity: Record<string, number>;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Quarantine options
|
|
44
|
+
*/
|
|
45
|
+
export interface QuarantineOptions {
|
|
46
|
+
quarantineDir: string;
|
|
47
|
+
createBackup: boolean;
|
|
48
|
+
removeOriginal: boolean;
|
|
49
|
+
compressFiles: boolean;
|
|
50
|
+
maxFileSizeMB: number;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Load quarantine database
|
|
54
|
+
*/
|
|
55
|
+
export declare function loadQuarantineDatabase(quarantineDir: string): QuarantineDatabase;
|
|
56
|
+
/**
|
|
57
|
+
* Save quarantine database
|
|
58
|
+
*/
|
|
59
|
+
export declare function saveQuarantineDatabase(db: QuarantineDatabase, quarantineDir: string): void;
|
|
60
|
+
/**
|
|
61
|
+
* Quarantine a file based on findings
|
|
62
|
+
*/
|
|
63
|
+
export declare function quarantineFile(filePath: string, findings: Finding[], reason: string, options?: Partial<QuarantineOptions>): QuarantineEntry | null;
|
|
64
|
+
/**
|
|
65
|
+
* Restore a quarantined file
|
|
66
|
+
*/
|
|
67
|
+
export declare function restoreQuarantinedFile(entryId: string, quarantineDir?: string): boolean;
|
|
68
|
+
/**
|
|
69
|
+
* Delete a quarantined file permanently
|
|
70
|
+
*/
|
|
71
|
+
export declare function deleteQuarantinedFile(entryId: string, quarantineDir?: string): boolean;
|
|
72
|
+
/**
|
|
73
|
+
* List quarantined files
|
|
74
|
+
*/
|
|
75
|
+
export declare function listQuarantinedFiles(quarantineDir?: string): QuarantineEntry[];
|
|
76
|
+
/**
|
|
77
|
+
* Get quarantine statistics
|
|
78
|
+
*/
|
|
79
|
+
export declare function getQuarantineStats(quarantineDir?: string): QuarantineDatabase['stats'];
|
|
80
|
+
/**
|
|
81
|
+
* Clean up old quarantine entries
|
|
82
|
+
*/
|
|
83
|
+
export declare function cleanupQuarantine(maxAgeDays?: number, quarantineDir?: string): number;
|
|
84
|
+
/**
|
|
85
|
+
* Check quarantine health
|
|
86
|
+
*/
|
|
87
|
+
export declare function checkQuarantineHealth(quarantineDir?: string): {
|
|
88
|
+
healthy: boolean;
|
|
89
|
+
issues: string[];
|
|
90
|
+
stats: QuarantineDatabase['stats'];
|
|
91
|
+
};
|
|
92
|
+
declare const _default: {
|
|
93
|
+
quarantineFile: typeof quarantineFile;
|
|
94
|
+
restoreQuarantinedFile: typeof restoreQuarantinedFile;
|
|
95
|
+
deleteQuarantinedFile: typeof deleteQuarantinedFile;
|
|
96
|
+
listQuarantinedFiles: typeof listQuarantinedFiles;
|
|
97
|
+
getQuarantineStats: typeof getQuarantineStats;
|
|
98
|
+
cleanupQuarantine: typeof cleanupQuarantine;
|
|
99
|
+
checkQuarantineHealth: typeof checkQuarantineHealth;
|
|
100
|
+
};
|
|
101
|
+
export default _default;
|
|
102
|
+
//# sourceMappingURL=Quarantine.d.ts.map
|