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.
Files changed (69) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/LICENSE +21 -0
  3. package/README.md +416 -0
  4. package/bin/ferret.js +822 -0
  5. package/dist/__tests__/basic.test.d.ts +6 -0
  6. package/dist/__tests__/basic.test.js +80 -0
  7. package/dist/analyzers/AstAnalyzer.d.ts +30 -0
  8. package/dist/analyzers/AstAnalyzer.js +332 -0
  9. package/dist/analyzers/CorrelationAnalyzer.d.ts +21 -0
  10. package/dist/analyzers/CorrelationAnalyzer.js +288 -0
  11. package/dist/index.d.ts +17 -0
  12. package/dist/index.js +22 -0
  13. package/dist/intelligence/IndicatorMatcher.d.ts +50 -0
  14. package/dist/intelligence/IndicatorMatcher.js +285 -0
  15. package/dist/intelligence/ThreatFeed.d.ts +99 -0
  16. package/dist/intelligence/ThreatFeed.js +296 -0
  17. package/dist/remediation/Fixer.d.ts +71 -0
  18. package/dist/remediation/Fixer.js +391 -0
  19. package/dist/remediation/Quarantine.d.ts +102 -0
  20. package/dist/remediation/Quarantine.js +329 -0
  21. package/dist/reporters/ConsoleReporter.d.ts +13 -0
  22. package/dist/reporters/ConsoleReporter.js +185 -0
  23. package/dist/reporters/HtmlReporter.d.ts +25 -0
  24. package/dist/reporters/HtmlReporter.js +604 -0
  25. package/dist/reporters/SarifReporter.d.ts +86 -0
  26. package/dist/reporters/SarifReporter.js +117 -0
  27. package/dist/rules/ai-specific.d.ts +8 -0
  28. package/dist/rules/ai-specific.js +221 -0
  29. package/dist/rules/backdoors.d.ts +8 -0
  30. package/dist/rules/backdoors.js +134 -0
  31. package/dist/rules/correlationRules.d.ts +8 -0
  32. package/dist/rules/correlationRules.js +227 -0
  33. package/dist/rules/credentials.d.ts +8 -0
  34. package/dist/rules/credentials.js +194 -0
  35. package/dist/rules/exfiltration.d.ts +8 -0
  36. package/dist/rules/exfiltration.js +139 -0
  37. package/dist/rules/index.d.ts +51 -0
  38. package/dist/rules/index.js +97 -0
  39. package/dist/rules/injection.d.ts +8 -0
  40. package/dist/rules/injection.js +136 -0
  41. package/dist/rules/obfuscation.d.ts +8 -0
  42. package/dist/rules/obfuscation.js +159 -0
  43. package/dist/rules/permissions.d.ts +8 -0
  44. package/dist/rules/permissions.js +129 -0
  45. package/dist/rules/persistence.d.ts +8 -0
  46. package/dist/rules/persistence.js +117 -0
  47. package/dist/rules/semanticRules.d.ts +10 -0
  48. package/dist/rules/semanticRules.js +212 -0
  49. package/dist/rules/supply-chain.d.ts +8 -0
  50. package/dist/rules/supply-chain.js +148 -0
  51. package/dist/scanner/FileDiscovery.d.ts +24 -0
  52. package/dist/scanner/FileDiscovery.js +282 -0
  53. package/dist/scanner/PatternMatcher.d.ts +25 -0
  54. package/dist/scanner/PatternMatcher.js +206 -0
  55. package/dist/scanner/Scanner.d.ts +14 -0
  56. package/dist/scanner/Scanner.js +266 -0
  57. package/dist/scanner/WatchMode.d.ts +29 -0
  58. package/dist/scanner/WatchMode.js +195 -0
  59. package/dist/types.d.ts +332 -0
  60. package/dist/types.js +53 -0
  61. package/dist/utils/baseline.d.ts +80 -0
  62. package/dist/utils/baseline.js +276 -0
  63. package/dist/utils/config.d.ts +21 -0
  64. package/dist/utils/config.js +247 -0
  65. package/dist/utils/ignore.d.ts +18 -0
  66. package/dist/utils/ignore.js +82 -0
  67. package/dist/utils/logger.d.ts +32 -0
  68. package/dist/utils/logger.js +75 -0
  69. package/package.json +119 -0
@@ -0,0 +1,195 @@
1
+ /**
2
+ * WatchMode - Real-time file watching and scanning
3
+ * Monitors files for changes and automatically rescans
4
+ */
5
+ import chokidar from 'chokidar';
6
+ import { scan } from './Scanner.js';
7
+ import { generateConsoleReport } from '../reporters/ConsoleReporter.js';
8
+ import logger from '../utils/logger.js';
9
+ const DEFAULT_WATCH_OPTIONS = {
10
+ debounceMs: 1000, // Wait 1 second after last change
11
+ batchChanges: true, // Batch multiple changes together
12
+ ignored: [
13
+ '**/node_modules/**',
14
+ '**/.git/**',
15
+ '**/dist/**',
16
+ '**/build/**',
17
+ '**/*.log',
18
+ '**/tmp/**',
19
+ '**/.DS_Store',
20
+ ],
21
+ };
22
+ /**
23
+ * Debounce function calls
24
+ */
25
+ function debounce(func, wait) {
26
+ let timeout;
27
+ return (...args) => {
28
+ clearTimeout(timeout);
29
+ timeout = setTimeout(() => { func(...args); }, wait);
30
+ };
31
+ }
32
+ /**
33
+ * Start watching files and scanning on changes
34
+ */
35
+ export async function startWatchMode(config, options = {}) {
36
+ const watchOptions = { ...DEFAULT_WATCH_OPTIONS, ...options };
37
+ const events = [];
38
+ let isScanning = false;
39
+ logger.info(`🔍 Starting watch mode for: ${config.paths.join(', ')}`);
40
+ logger.info(`⏱️ Debounce: ${watchOptions.debounceMs}ms`);
41
+ // Initial scan
42
+ /* eslint-disable no-console */
43
+ console.log('🚀 Running initial scan...\n');
44
+ const initialResult = await scan(config);
45
+ const initialReport = generateConsoleReport(initialResult, {
46
+ verbose: config.verbose,
47
+ ci: config.ci,
48
+ });
49
+ console.log(initialReport);
50
+ console.log('\n👀 Watching for changes...\n');
51
+ /* eslint-enable no-console */
52
+ // Debounced scan function
53
+ const performScan = async () => {
54
+ if (isScanning) {
55
+ logger.debug('Scan already in progress, skipping');
56
+ return;
57
+ }
58
+ isScanning = true;
59
+ const changedFiles = events.splice(0); // Clear events
60
+ try {
61
+ logger.info(`📝 File changes detected: ${changedFiles.length} event(s)`);
62
+ if (config.verbose) {
63
+ for (const event of changedFiles.slice(0, 5)) { // Show max 5 files
64
+ const icon = event.type === 'add' ? '➕' : event.type === 'change' ? '📝' : '➖';
65
+ logger.info(` ${icon} ${event.path}`);
66
+ }
67
+ if (changedFiles.length > 5) {
68
+ logger.info(` ... and ${changedFiles.length - 5} more`);
69
+ }
70
+ }
71
+ /* eslint-disable no-console */
72
+ console.log('🔄 Rescanning...\n');
73
+ const result = await scan(config);
74
+ // Clear previous output and show new results
75
+ if (!config.verbose && !config.ci) {
76
+ process.stdout.write('\x1Bc'); // Clear screen
77
+ }
78
+ const report = generateConsoleReport(result, {
79
+ verbose: config.verbose,
80
+ ci: config.ci,
81
+ });
82
+ console.log(report);
83
+ const timestamp = new Date().toLocaleTimeString();
84
+ console.log(`\n✅ Scan completed at ${timestamp}`);
85
+ console.log('👀 Watching for changes...\n');
86
+ /* eslint-enable no-console */
87
+ }
88
+ catch (error) {
89
+ // eslint-disable-next-line no-console
90
+ console.error('❌ Scan failed:', error instanceof Error ? error.message : String(error));
91
+ }
92
+ finally {
93
+ isScanning = false;
94
+ }
95
+ };
96
+ const debouncedScan = debounce(() => {
97
+ void performScan();
98
+ }, watchOptions.debounceMs);
99
+ // Set up file watcher
100
+ const watcher = chokidar.watch(config.paths, {
101
+ ignored: [
102
+ ...watchOptions.ignored,
103
+ ...config.ignore.map(pattern => `**/${pattern}`),
104
+ ],
105
+ persistent: true,
106
+ ignoreInitial: true, // Don't trigger on initial scan
107
+ followSymlinks: false,
108
+ depth: 10, // Reasonable recursion depth
109
+ awaitWriteFinish: {
110
+ stabilityThreshold: 100,
111
+ pollInterval: 50,
112
+ },
113
+ });
114
+ // Watch event handlers
115
+ watcher.on('add', (path) => {
116
+ events.push({ type: 'add', path, timestamp: new Date() });
117
+ if (config.verbose) {
118
+ logger.debug(`File added: ${path}`);
119
+ }
120
+ debouncedScan();
121
+ });
122
+ watcher.on('change', (path) => {
123
+ events.push({ type: 'change', path, timestamp: new Date() });
124
+ if (config.verbose) {
125
+ logger.debug(`File changed: ${path}`);
126
+ }
127
+ debouncedScan();
128
+ });
129
+ watcher.on('unlink', (path) => {
130
+ events.push({ type: 'unlink', path, timestamp: new Date() });
131
+ if (config.verbose) {
132
+ logger.debug(`File removed: ${path}`);
133
+ }
134
+ debouncedScan();
135
+ });
136
+ watcher.on('error', (error) => {
137
+ // eslint-disable-next-line no-console
138
+ console.error('❌ Watch error:', error);
139
+ });
140
+ watcher.on('ready', () => {
141
+ const watched = watcher.getWatched();
142
+ const watchedCount = Object.keys(watched).length;
143
+ logger.info(`👁️ Watching ${watchedCount || 'multiple'} directories`);
144
+ });
145
+ // Handle graceful shutdown
146
+ const cleanup = () => {
147
+ logger.info('🛑 Stopping watch mode...');
148
+ void watcher.close();
149
+ };
150
+ process.on('SIGINT', cleanup);
151
+ process.on('SIGTERM', cleanup);
152
+ // Return cleanup function
153
+ return cleanup;
154
+ }
155
+ /**
156
+ * Watch mode with enhanced console output
157
+ */
158
+ export async function startEnhancedWatchMode(config, options = {}) {
159
+ // Enhanced version with better UX
160
+ const watchOptions = { ...DEFAULT_WATCH_OPTIONS, ...options };
161
+ /* eslint-disable no-console */
162
+ console.log('🚀 Ferret Watch Mode Starting...\n');
163
+ console.log(`📂 Watching: ${config.paths.join(', ')}`);
164
+ console.log(`⚙️ Debounce: ${watchOptions.debounceMs}ms`);
165
+ console.log(`🔍 Severities: ${config.severities.join(', ')}`);
166
+ console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n');
167
+ /* eslint-enable no-console */
168
+ return startWatchMode(config, options);
169
+ }
170
+ /**
171
+ * Create a simple file change notifier
172
+ */
173
+ export function createChangeNotifier(paths, callback, options = {}) {
174
+ const watchOptions = { ...DEFAULT_WATCH_OPTIONS, ...options };
175
+ const changedFiles = [];
176
+ const debouncedCallback = debounce(() => {
177
+ const files = [...changedFiles];
178
+ changedFiles.length = 0; // Clear array
179
+ callback(files);
180
+ }, watchOptions.debounceMs);
181
+ const watcher = chokidar.watch(paths, {
182
+ ignored: watchOptions.ignored,
183
+ persistent: true,
184
+ ignoreInitial: true,
185
+ });
186
+ watcher.on('all', (event, path) => {
187
+ if (['add', 'change', 'unlink'].includes(event)) {
188
+ changedFiles.push(path);
189
+ debouncedCallback();
190
+ }
191
+ });
192
+ return () => { void watcher.close(); };
193
+ }
194
+ export default { startWatchMode, startEnhancedWatchMode, createChangeNotifier };
195
+ //# sourceMappingURL=WatchMode.js.map
@@ -0,0 +1,332 @@
1
+ /**
2
+ * Ferret-Scan Type Definitions
3
+ * Security scanner for AI CLI configurations
4
+ */
5
+ /** Severity levels for security findings */
6
+ export type Severity = 'CRITICAL' | 'HIGH' | 'MEDIUM' | 'LOW' | 'INFO';
7
+ /** Threat categories detected by the scanner */
8
+ export type ThreatCategory = 'exfiltration' | 'credentials' | 'injection' | 'backdoors' | 'supply-chain' | 'permissions' | 'persistence' | 'obfuscation' | 'ai-specific' | 'advanced-hiding' | 'behavioral';
9
+ /** Component types that can be analyzed */
10
+ export type ComponentType = 'skill' | 'agent' | 'hook' | 'plugin' | 'mcp' | 'settings' | 'ai-config-md' | 'rules-file';
11
+ /** File types supported for analysis */
12
+ export type FileType = 'md' | 'sh' | 'bash' | 'zsh' | 'json' | 'yaml' | 'yml' | 'ts' | 'js' | 'tsx' | 'jsx';
13
+ /** Semantic pattern for AST-based analysis */
14
+ export interface SemanticPattern {
15
+ /** Pattern type */
16
+ type: 'function-call' | 'property-access' | 'dynamic-import' | 'eval-chain' | 'object-structure';
17
+ /** Pattern identifier */
18
+ pattern: string;
19
+ /** Required context */
20
+ context?: string[];
21
+ /** Minimum confidence level (0-1) */
22
+ confidence?: number;
23
+ }
24
+ /** Correlation rule for multi-file analysis */
25
+ export interface CorrelationRule {
26
+ /** Rule identifier */
27
+ id: string;
28
+ /** Description of the correlation pattern */
29
+ description: string;
30
+ /** File patterns that must be present */
31
+ filePatterns: string[];
32
+ /** Content patterns that must exist across files */
33
+ contentPatterns: string[];
34
+ /** Maximum distance between related files (directory levels) */
35
+ maxDistance?: number;
36
+ }
37
+ /** Remediation fix definition */
38
+ export interface RemediationFix {
39
+ /** Fix type */
40
+ type: 'replace' | 'remove' | 'quarantine' | 'permission-change';
41
+ /** Fix description */
42
+ description: string;
43
+ /** Pattern to match for fix */
44
+ pattern: string;
45
+ /** Replacement content (for replace type) */
46
+ replacement?: string;
47
+ /** Safety level (0=dangerous, 1=safe) */
48
+ safety: number;
49
+ /** Whether fix can be applied automatically */
50
+ automatic: boolean;
51
+ }
52
+ /** A single security rule definition */
53
+ export interface Rule {
54
+ /** Unique rule identifier (e.g., "EXFIL-001") */
55
+ id: string;
56
+ /** Human-readable rule name */
57
+ name: string;
58
+ /** Category of threat this rule detects */
59
+ category: ThreatCategory;
60
+ /** Severity level of findings from this rule */
61
+ severity: Severity;
62
+ /** Detailed description of what this rule detects */
63
+ description: string;
64
+ /** Regex patterns to match against content */
65
+ patterns: RegExp[];
66
+ /** File types this rule applies to */
67
+ fileTypes: FileType[];
68
+ /** Component types this rule applies to */
69
+ components: ComponentType[];
70
+ /** Recommended remediation steps */
71
+ remediation: string;
72
+ /** Reference URLs for more information */
73
+ references: string[];
74
+ /** Whether this rule is enabled by default */
75
+ enabled: boolean;
76
+ /** Patterns that exclude a match (false positive filters) */
77
+ excludePatterns?: RegExp[];
78
+ /** Context patterns that must also be present for a match */
79
+ requireContext?: RegExp[];
80
+ /** Context patterns that invalidate a match (documentation indicators) */
81
+ excludeContext?: RegExp[];
82
+ /** Minimum match length to trigger (filters short matches) */
83
+ minMatchLength?: number;
84
+ /** Semantic patterns for AST-based detection */
85
+ semanticPatterns?: SemanticPattern[];
86
+ /** Correlation rules for multi-file analysis */
87
+ correlationRules?: CorrelationRule[];
88
+ /** Available fixes for this rule */
89
+ remediationFixes?: RemediationFix[];
90
+ }
91
+ /** AST node information for semantic findings */
92
+ export interface ASTNodeInfo {
93
+ /** Node type (function, property, etc.) */
94
+ nodeType: string;
95
+ /** Node name/identifier */
96
+ name?: string;
97
+ /** Parent context */
98
+ parent?: string;
99
+ /** Child nodes */
100
+ children?: string[];
101
+ }
102
+ /** Semantic context for advanced analysis */
103
+ export interface SemanticContext {
104
+ /** Function/method name */
105
+ functionName?: string;
106
+ /** Variable names in scope */
107
+ variables?: string[];
108
+ /** Import statements */
109
+ imports?: string[];
110
+ /** Call chain */
111
+ callChain?: string[];
112
+ }
113
+ /** A security finding from the scanner */
114
+ export interface Finding {
115
+ /** Rule ID that triggered this finding */
116
+ ruleId: string;
117
+ /** Rule name */
118
+ ruleName: string;
119
+ /** Severity level */
120
+ severity: Severity;
121
+ /** Category of threat */
122
+ category: ThreatCategory;
123
+ /** Full path to the file */
124
+ file: string;
125
+ /** Relative path for display */
126
+ relativePath: string;
127
+ /** Line number where the finding occurred */
128
+ line: number;
129
+ /** Column number (if available) */
130
+ column?: number;
131
+ /** The matched text */
132
+ match: string;
133
+ /** Context lines around the finding */
134
+ context: ContextLine[];
135
+ /** Remediation recommendation */
136
+ remediation: string;
137
+ /** Additional metadata */
138
+ metadata?: Record<string, unknown>;
139
+ /** Timestamp when finding was detected */
140
+ timestamp: Date;
141
+ /** Risk score (0-100) */
142
+ riskScore: number;
143
+ }
144
+ /** Semantic finding with AST information */
145
+ export interface SemanticFinding extends Finding {
146
+ /** AST node information */
147
+ astNode?: ASTNodeInfo;
148
+ /** Semantic context */
149
+ semanticContext?: SemanticContext;
150
+ /** Confidence level (0-1) */
151
+ confidence: number;
152
+ }
153
+ /** Correlation finding across multiple files */
154
+ export interface CorrelationFinding extends Finding {
155
+ /** Related files in the attack pattern */
156
+ relatedFiles: string[];
157
+ /** Attack pattern name */
158
+ attackPattern: string;
159
+ /** Risk vectors identified */
160
+ riskVectors: string[];
161
+ /** Correlation strength (0-1) */
162
+ correlationStrength: number;
163
+ }
164
+ /** A line of context around a finding */
165
+ export interface ContextLine {
166
+ /** Line number */
167
+ lineNumber: number;
168
+ /** Line content */
169
+ content: string;
170
+ /** Whether this is the matched line */
171
+ isMatch: boolean;
172
+ }
173
+ /** A discovered file to be analyzed */
174
+ export interface DiscoveredFile {
175
+ /** Full absolute path */
176
+ path: string;
177
+ /** Relative path from scan root */
178
+ relativePath: string;
179
+ /** File extension/type */
180
+ type: FileType;
181
+ /** Detected component type */
182
+ component: ComponentType;
183
+ /** File size in bytes */
184
+ size: number;
185
+ /** Last modified timestamp */
186
+ modified: Date;
187
+ }
188
+ /** Results from a complete scan */
189
+ export interface ScanResult {
190
+ /** Whether the scan completed successfully */
191
+ success: boolean;
192
+ /** Timestamp when scan started */
193
+ startTime: Date;
194
+ /** Timestamp when scan completed */
195
+ endTime: Date;
196
+ /** Duration in milliseconds */
197
+ duration: number;
198
+ /** Paths that were scanned */
199
+ scannedPaths: string[];
200
+ /** Total files discovered */
201
+ totalFiles: number;
202
+ /** Files that were actually analyzed */
203
+ analyzedFiles: number;
204
+ /** Files that were skipped (ignored) */
205
+ skippedFiles: number;
206
+ /** All findings from the scan */
207
+ findings: Finding[];
208
+ /** Findings grouped by severity */
209
+ findingsBySeverity: Record<Severity, Finding[]>;
210
+ /** Findings grouped by category */
211
+ findingsByCategory: Record<ThreatCategory, Finding[]>;
212
+ /** Overall risk score (0-100) */
213
+ overallRiskScore: number;
214
+ /** Summary statistics */
215
+ summary: ScanSummary;
216
+ /** Any errors encountered during scanning */
217
+ errors: ScanError[];
218
+ }
219
+ /** Summary statistics for a scan */
220
+ export interface ScanSummary {
221
+ critical: number;
222
+ high: number;
223
+ medium: number;
224
+ low: number;
225
+ info: number;
226
+ total: number;
227
+ }
228
+ /** An error encountered during scanning */
229
+ export interface ScanError {
230
+ /** File path where error occurred */
231
+ file?: string;
232
+ /** Error message */
233
+ message: string;
234
+ /** Error code */
235
+ code?: string;
236
+ /** Whether the error was fatal */
237
+ fatal: boolean;
238
+ }
239
+ /** Scanner configuration options */
240
+ export interface ScannerConfig {
241
+ /** Paths to scan */
242
+ paths: string[];
243
+ /** Severity levels to report */
244
+ severities: Severity[];
245
+ /** Categories to scan for */
246
+ categories: ThreatCategory[];
247
+ /** Patterns to ignore (glob) */
248
+ ignore: string[];
249
+ /** Path to custom rules directory */
250
+ customRules?: string;
251
+ /** Minimum severity to fail on */
252
+ failOn: Severity;
253
+ /** Enable watch mode */
254
+ watch: boolean;
255
+ /** Enable AI-powered detection */
256
+ aiDetection: boolean;
257
+ /** Enable threat intelligence */
258
+ threatIntel: boolean;
259
+ /** Enable behavioral analysis */
260
+ behaviorAnalysis: boolean;
261
+ /** Enable semantic analysis */
262
+ semanticAnalysis: boolean;
263
+ /** Enable cross-file correlation */
264
+ correlationAnalysis: boolean;
265
+ /** Enable auto-remediation */
266
+ autoRemediation: boolean;
267
+ /** Context lines to show around findings */
268
+ contextLines: number;
269
+ /** Maximum file size to scan (bytes) */
270
+ maxFileSize: number;
271
+ /** Output format */
272
+ format: OutputFormat;
273
+ /** Output file path */
274
+ outputFile?: string;
275
+ /** Verbose output */
276
+ verbose: boolean;
277
+ /** CI mode (simplified output) */
278
+ ci: boolean;
279
+ }
280
+ /** Supported output formats */
281
+ export type OutputFormat = 'console' | 'json' | 'sarif' | 'html' | 'csv';
282
+ /** CLI options passed from command line */
283
+ export interface CliOptions {
284
+ path?: string;
285
+ format?: OutputFormat;
286
+ severity?: string;
287
+ categories?: string;
288
+ failOn?: string;
289
+ output?: string;
290
+ watch?: boolean;
291
+ ci?: boolean;
292
+ verbose?: boolean;
293
+ aiDetection?: boolean;
294
+ threatIntel?: boolean;
295
+ semanticAnalysis?: boolean;
296
+ correlationAnalysis?: boolean;
297
+ autoRemediation?: boolean;
298
+ config?: string;
299
+ }
300
+ /** Configuration file structure (.ferretrc.json) */
301
+ export interface ConfigFile {
302
+ severity?: Severity[];
303
+ categories?: ThreatCategory[];
304
+ ignore?: string[];
305
+ customRules?: string;
306
+ failOn?: Severity;
307
+ aiDetection?: {
308
+ enabled: boolean;
309
+ confidence?: number;
310
+ };
311
+ threatIntelligence?: {
312
+ enabled: boolean;
313
+ feeds?: string[];
314
+ updateInterval?: string;
315
+ };
316
+ behaviorAnalysis?: {
317
+ enabled: boolean;
318
+ patterns?: string[];
319
+ };
320
+ remediation?: {
321
+ autoFix?: boolean;
322
+ quarantineDir?: string;
323
+ backupOriginals?: boolean;
324
+ };
325
+ }
326
+ /** Default scanner configuration */
327
+ export declare const DEFAULT_CONFIG: ScannerConfig;
328
+ /** Severity weights for risk scoring */
329
+ export declare const SEVERITY_WEIGHTS: Record<Severity, number>;
330
+ /** Severity order for sorting */
331
+ export declare const SEVERITY_ORDER: Severity[];
332
+ //# sourceMappingURL=types.d.ts.map
package/dist/types.js ADDED
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Ferret-Scan Type Definitions
3
+ * Security scanner for AI CLI configurations
4
+ */
5
+ /** Default scanner configuration */
6
+ export const DEFAULT_CONFIG = {
7
+ paths: [],
8
+ severities: ['CRITICAL', 'HIGH', 'MEDIUM', 'LOW', 'INFO'],
9
+ categories: [
10
+ 'exfiltration',
11
+ 'credentials',
12
+ 'injection',
13
+ 'backdoors',
14
+ 'supply-chain',
15
+ 'permissions',
16
+ 'persistence',
17
+ 'obfuscation',
18
+ 'ai-specific',
19
+ 'advanced-hiding',
20
+ 'behavioral',
21
+ ],
22
+ ignore: ['**/node_modules/**', '**/.git/**'],
23
+ failOn: 'HIGH',
24
+ watch: false,
25
+ aiDetection: false,
26
+ threatIntel: false,
27
+ behaviorAnalysis: false,
28
+ semanticAnalysis: false,
29
+ correlationAnalysis: false,
30
+ autoRemediation: false,
31
+ contextLines: 3,
32
+ maxFileSize: 10 * 1024 * 1024, // 10MB
33
+ format: 'console',
34
+ verbose: false,
35
+ ci: false,
36
+ };
37
+ /** Severity weights for risk scoring */
38
+ export const SEVERITY_WEIGHTS = {
39
+ CRITICAL: 100,
40
+ HIGH: 75,
41
+ MEDIUM: 50,
42
+ LOW: 25,
43
+ INFO: 10,
44
+ };
45
+ /** Severity order for sorting */
46
+ export const SEVERITY_ORDER = [
47
+ 'CRITICAL',
48
+ 'HIGH',
49
+ 'MEDIUM',
50
+ 'LOW',
51
+ 'INFO',
52
+ ];
53
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Baseline Management - Track and ignore accepted findings
3
+ * Allows users to create baselines of known/accepted security findings
4
+ */
5
+ import type { Finding, ScanResult } from '../types.js';
6
+ export interface BaselineFinding {
7
+ ruleId: string;
8
+ file: string;
9
+ line: number;
10
+ match: string;
11
+ hash: string;
12
+ acceptedDate: string;
13
+ reason?: string;
14
+ expiresDate?: string;
15
+ }
16
+ export interface Baseline {
17
+ version: string;
18
+ createdDate: string;
19
+ lastUpdated: string;
20
+ description?: string;
21
+ findings: BaselineFinding[];
22
+ }
23
+ /**
24
+ * Load baseline from file
25
+ */
26
+ export declare function loadBaseline(baselinePath: string): Baseline | null;
27
+ /**
28
+ * Save baseline to file
29
+ */
30
+ export declare function saveBaseline(baseline: Baseline, baselinePath: string): void;
31
+ /**
32
+ * Create a new baseline from scan results
33
+ */
34
+ export declare function createBaseline(result: ScanResult, description?: string): Baseline;
35
+ /**
36
+ * Add findings to an existing baseline
37
+ */
38
+ export declare function addToBaseline(baseline: Baseline, findings: Finding[], reason?: string): Baseline;
39
+ /**
40
+ * Remove findings from baseline
41
+ */
42
+ export declare function removeFromBaseline(baseline: Baseline, findingHashes: string[]): Baseline;
43
+ /**
44
+ * Filter scan results against baseline
45
+ */
46
+ export declare function filterAgainstBaseline(result: ScanResult, baseline: Baseline | null): ScanResult;
47
+ /**
48
+ * Check if baseline findings are still valid
49
+ */
50
+ export declare function validateBaseline(baseline: Baseline, currentResult: ScanResult): {
51
+ valid: BaselineFinding[];
52
+ invalid: BaselineFinding[];
53
+ };
54
+ /**
55
+ * Get default baseline path for a project
56
+ */
57
+ export declare function getDefaultBaselinePath(scanPaths: string[]): string;
58
+ /**
59
+ * Baseline statistics
60
+ */
61
+ export declare function getBaselineStats(baseline: Baseline): {
62
+ totalFindings: number;
63
+ byRule: Record<string, number>;
64
+ bySeverity: Record<string, number>;
65
+ oldestFinding: string;
66
+ newestFinding: string;
67
+ };
68
+ declare const _default: {
69
+ loadBaseline: typeof loadBaseline;
70
+ saveBaseline: typeof saveBaseline;
71
+ createBaseline: typeof createBaseline;
72
+ addToBaseline: typeof addToBaseline;
73
+ removeFromBaseline: typeof removeFromBaseline;
74
+ filterAgainstBaseline: typeof filterAgainstBaseline;
75
+ validateBaseline: typeof validateBaseline;
76
+ getDefaultBaselinePath: typeof getDefaultBaselinePath;
77
+ getBaselineStats: typeof getBaselineStats;
78
+ };
79
+ export default _default;
80
+ //# sourceMappingURL=baseline.d.ts.map