secret-scan-cli
Version:
A tool to scan codebases for potential secrets and sensitive information
110 lines (99 loc) • 3.64 kB
JavaScript
// src/main.js
import { Worker } from 'worker_threads';
import fs from 'fs';
import path from 'path';
import chalk from 'chalk';
import { execSync } from 'child_process';
import { patterns, ignorePaths } from './config.js';
// Function to scan files in parallel
function scanFilesInParallel(files) {
return new Promise((resolve, reject) => {
const worker = new Worker(new URL('./worker.js', import.meta.url), {
workerData: files,
});
worker.on('message', resolve);
worker.on('error', reject);
worker.on('exit', (code) => {
if (code !== 0) reject(new Error(`Worker stopped with exit code ${code}`));
});
});
}
// Function to recursively get all files in a folder
function getAllFiles(dirPath, fileList = []) {
const files = fs.readdirSync(dirPath);
files.forEach((file) => {
const fullPath = path.join(dirPath, file);
if (ignorePaths.includes(file)) {
console.log(chalk.gray(`Skipping ignored path: ${fullPath}`));
return; // Skip ignored paths
}
if (fs.statSync(fullPath).isDirectory()) {
getAllFiles(fullPath, fileList);
} else {
fileList.push(fullPath);
}
});
return fileList;
}
// Function to scan a specific folder
export async function scanFolder(folderPath) {
try {
const files = getAllFiles(folderPath);
const results = await scanFilesInParallel(files);
let foundSecrets = false;
results.forEach((result) => {
if (result.secrets.length > 0) {
console.log(chalk.red.bold(`Secrets found in ${result.file}:`));
result.secrets.forEach((secret) => {
console.log(chalk.yellow(`- ${secret.type}: ${secret.matches.join(', ')}`));
// Show the affected lines
const content = fs.readFileSync(result.file, 'utf8').split('\n');
secret.lineNumbers.forEach(lineNum => {
console.log(chalk.gray(` Line ${lineNum}: ${content[lineNum - 1].trim()}`));
});
});
foundSecrets = true;
}
});
if (foundSecrets) {
console.log(chalk.red.bold('Secrets detected. Scan failed.'));
process.exit(1);
} else {
console.log(chalk.green('No secrets detected.'));
}
} catch (error) {
console.error(chalk.red.bold(`Error: ${error.message}`));
process.exit(1);
}
}
// Function to scan all tracked files in the repository
export async function scanRepository() {
try {
const files = execSync('git ls-files').toString().trim().split('\n').filter(Boolean);
const results = await scanFilesInParallel(files);
let foundSecrets = false;
results.forEach((result) => {
if (result.secrets.length > 0) {
console.log(chalk.red.bold(`Secrets found in ${result.file}:`));
result.secrets.forEach((secret) => {
console.log(chalk.yellow(`- ${secret.type}: ${secret.matches.join(', ')}`));
// Show the affected lines
const content = fs.readFileSync(result.file, 'utf8').split('\n');
secret.lineNumbers.forEach(lineNum => {
console.log(chalk.gray(` Line ${lineNum}: ${content[lineNum - 1].trim()}`));
});
});
foundSecrets = true;
}
});
if (foundSecrets) {
console.log(chalk.red.bold('Secrets detected. Commit blocked.'));
process.exit(1);
} else {
console.log(chalk.green('No secrets detected. Commit allowed.'));
}
} catch (error) {
console.error(chalk.red.bold(`Error scanning repository: ${error.message}`));
process.exit(1);
}
}