UNPKG

detect-secrets-js

Version:

A JavaScript implementation of Yelp's detect-secrets tool - no Python required

153 lines (124 loc) 4.94 kB
#!/usr/bin/env node const { program } = require('commander'); const chalk = require('chalk'); const ora = require('ora'); const path = require('path'); const fs = require('fs'); const detectSecrets = require('../dist/index'); // Set up the CLI program .name('detect-secrets-js') .description('JavaScript implementation of Yelp\'s detect-secrets - no Python required') .version(require('../package.json').version); program .option('-d, --directory <path>', 'Directory to scan (default: current directory)') .option('-r, --root', 'Scan from project root') .option('-e, --exclude-files <patterns>', 'File patterns to exclude (comma-separated)') .option('-x, --exclude-dirs <patterns>', 'Directory patterns to exclude (comma-separated)') .option('-m, --check-missed', 'Check for potentially missed secrets') .option('-v, --verbose', 'Include additional information') .option('-o, --output <file>', 'Output file path') .option('-l, --limit-file-size', 'Enable file size limits to prevent memory issues') .option('--max-file-size <size>', 'Maximum file size to scan in KB (default: no limit)', parseInt); program.parse(process.argv); const options = program.opts(); // Format results for display function formatResults(results) { const { secrets, missed_secrets, truncated } = results; if (secrets.length === 0 && missed_secrets.length === 0) { return chalk.green('No secrets found!'); } let output = ''; // Note if any files were truncated if (truncated) { output += chalk.yellow('Note: Some files were truncated due to size limits.\n'); output += chalk.yellow('Use --max-file-size to increase the limit or remove --limit-file-size to scan without limits.\n\n'); } // Group secrets by file const fileGroups = {}; for (const secret of secrets) { if (!fileGroups[secret.file]) { fileGroups[secret.file] = []; } fileGroups[secret.file].push(secret); } // Format detected secrets if (secrets.length > 0) { output += chalk.bold.yellow(`\n${secrets.length} secret(s) detected:\n`); for (const [file, fileSecrets] of Object.entries(fileGroups)) { output += chalk.cyan(`\n${file}:\n`); for (const secret of fileSecrets) { const status = secret.is_false_positive ? chalk.gray('[Likely False Positive]') : chalk.red('[Secret]'); output += ` ${status} Line ${secret.line}: ${secret.types.join(', ')}\n`; } } } // Format missed secrets if (missed_secrets.length > 0) { output += chalk.bold.yellow(`\n${missed_secrets.length} potentially missed secret(s):\n`); const missedFileGroups = {}; for (const secret of missed_secrets) { if (!missedFileGroups[secret.file]) { missedFileGroups[secret.file] = []; } missedFileGroups[secret.file].push(secret); } for (const [file, fileSecrets] of Object.entries(missedFileGroups)) { output += chalk.cyan(`\n${file}:\n`); for (const secret of fileSecrets) { output += ` ${chalk.yellow('[Potential]')} Line ${secret.line}: ${secret.type}\n`; } } } return output; } // Save results to file if requested function saveResults(results, outputPath) { try { fs.writeFileSync(outputPath, JSON.stringify(results, null, 2)); console.log(chalk.green(`\nResults saved to ${outputPath}`)); } catch (error) { console.error(chalk.red(`\nError saving results: ${error.message}`)); } } // Main function async function main() { const spinner = ora('Initializing WebAssembly module...').start(); try { // Initialize the WebAssembly module await detectSecrets.initialize(); // Prepare scan options const scanOptions = { directory: options.directory || process.cwd(), root: options.root || false, excludeFiles: options.excludeFiles ? options.excludeFiles.split(',') : [], excludeDirs: options.excludeDirs ? options.excludeDirs.split(',') : [], checkMissed: options.checkMissed || false, verbose: options.verbose || false, limitFileSize: options.limitFileSize || false, maxFileSize: options.maxFileSize ? options.maxFileSize * 1024 : undefined }; // Update spinner text spinner.text = 'Scanning for secrets...'; // Scan the directory const results = await detectSecrets.scanDirectory(scanOptions.directory, scanOptions); // Stop spinner and display results spinner.stop(); console.log(formatResults(results)); // Save results if output file is specified if (options.output) { saveResults(results, options.output); } // Exit with error code if secrets were found if (results.secrets.length > 0) { process.exit(1); } } catch (error) { spinner.fail(chalk.red(`Error: ${error.message}`)); process.exit(1); } } // Run the main function main();