ferret-scan 1.0.1 → 1.0.3

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.
@@ -11,6 +11,7 @@ import { analyzeCorrelations, shouldAnalyzeCorrelations } from '../analyzers/Cor
11
11
  import { loadThreatDatabase } from '../intelligence/ThreatFeed.js';
12
12
  import { matchIndicators, shouldMatchIndicators } from '../intelligence/IndicatorMatcher.js';
13
13
  import logger from '../utils/logger.js';
14
+ import ora from 'ora';
14
15
  /**
15
16
  * Create an empty scan summary
16
17
  */
@@ -167,6 +168,12 @@ function scanFile(file, config) {
167
168
  return { findings: [], error: message };
168
169
  }
169
170
  }
171
+ /**
172
+ * Yield to event loop to allow spinner updates
173
+ */
174
+ function yieldToEventLoop() {
175
+ return new Promise(resolve => setImmediate(resolve));
176
+ }
170
177
  /**
171
178
  * Main scan function
172
179
  */
@@ -174,12 +181,20 @@ export async function scan(config) {
174
181
  const startTime = new Date();
175
182
  const allFindings = [];
176
183
  const errors = [];
184
+ const showProgress = !config.ci && process.stdout.isTTY;
177
185
  logger.info(`Starting scan of ${config.paths.length} path(s)`);
178
- // Discover files
186
+ // Discover files with spinner
187
+ let spinner = null;
188
+ if (showProgress) {
189
+ spinner = ora('Discovering files...').start();
190
+ }
179
191
  const discovery = discoverFiles(config.paths, {
180
192
  maxFileSize: config.maxFileSize,
181
193
  ignore: config.ignore,
182
194
  });
195
+ if (spinner) {
196
+ spinner.succeed(`Discovered ${discovery.files.length} files to scan (${discovery.skipped} skipped)`);
197
+ }
183
198
  // Add discovery errors
184
199
  for (const error of discovery.errors) {
185
200
  errors.push({
@@ -191,9 +206,19 @@ export async function scan(config) {
191
206
  if (discovery.files.length === 0) {
192
207
  logger.warn('No files found to scan');
193
208
  }
194
- // Scan each file
209
+ // Scan each file with progress
210
+ const totalFiles = discovery.files.length;
211
+ let scannedCount = 0;
212
+ let findingsCount = 0;
213
+ if (showProgress && totalFiles > 0) {
214
+ spinner = ora(`Scanning files... 0/${totalFiles}`).start();
215
+ }
195
216
  for (const file of discovery.files) {
196
217
  logger.debug(`Scanning: ${file.relativePath}`);
218
+ if (spinner && totalFiles > 10) {
219
+ // Only update spinner text for larger scans to avoid flicker
220
+ spinner.text = `Scanning ${scannedCount + 1}/${totalFiles}: ${file.relativePath.slice(-50)}${findingsCount > 0 ? ` (${findingsCount} findings)` : ''}`;
221
+ }
197
222
  const result = scanFile(file, config);
198
223
  if (result.error) {
199
224
  errors.push({
@@ -203,6 +228,15 @@ export async function scan(config) {
203
228
  });
204
229
  }
205
230
  allFindings.push(...result.findings);
231
+ scannedCount++;
232
+ findingsCount = allFindings.length;
233
+ // Yield to event loop every 100 files to allow spinner to update
234
+ if (showProgress && scannedCount % 100 === 0) {
235
+ await yieldToEventLoop();
236
+ }
237
+ }
238
+ if (spinner) {
239
+ spinner.succeed(`Scanned ${totalFiles} files${findingsCount > 0 ? ` - found ${findingsCount} issues` : ' - no issues found'}`);
206
240
  }
207
241
  // Cross-file correlation analysis if enabled
208
242
  if (shouldAnalyzeCorrelations(discovery.files, config)) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ferret-scan",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "Security scanner for AI CLI configurations - detect prompt injections, credential leaks, and malicious patterns in AI agent configs",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",