UNPKG

supamend

Version:

Pluggable DevSecOps Security Scanner with 10+ scanners and multiple reporting channels

165 lines 6.95 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.GitleaksScanner = void 0; const child_process_1 = require("child_process"); const fs = __importStar(require("fs-extra")); const path = __importStar(require("path")); class GitleaksScanner { constructor() { this.name = 'gitleaks'; this.description = 'Detect hardcoded secrets and credentials using Gitleaks'; this.version = '1.0.0'; } async init(config) { // Check if gitleaks is available const isAvailable = await this.isAvailable(); if (!isAvailable) { throw new Error('Gitleaks is not installed. Please install it from https://github.com/zricethezav/gitleaks'); } } async scan(repoPath, options) { return new Promise((resolve, reject) => { const results = []; // Prepare gitleaks command const args = [ 'detect', '--source', repoPath, '--report-format', 'json', '--report-path', path.join(repoPath, 'gitleaks-report.json'), '--no-git' ]; // Add custom rules if provided if (options?.rules) { args.push('--config', options.rules); } const gitleaks = (0, child_process_1.spawn)('gitleaks', args); let stdout = ''; let stderr = ''; gitleaks.stdout.on('data', (data) => { stdout += data.toString(); }); gitleaks.stderr.on('data', (data) => { stderr += data.toString(); }); gitleaks.on('close', async (code) => { try { if (code === 0 || code === 1) { // Gitleaks returns 1 when secrets are found // Try to read the JSON report const reportPath = path.join(repoPath, 'gitleaks-report.json'); if (await fs.pathExists(reportPath)) { const reportContent = await fs.readFile(reportPath, 'utf-8'); const report = JSON.parse(reportContent); for (const finding of report) { const result = { id: `${this.name}-${finding.RuleID}-${finding.StartLine}`, type: 'secret', severity: this.mapSeverity(finding.RuleID), title: `Hardcoded Secret: ${finding.RuleID}`, description: finding.Description || `Found ${finding.RuleID} in ${finding.File}`, file: finding.File, line: finding.StartLine, column: finding.StartColumn, rule: finding.RuleID, scanner: this.name, timestamp: new Date(), metadata: { match: finding.Match, secret: finding.Secret, commit: finding.Commit } }; results.push(result); } // Clean up report file await fs.remove(reportPath); } } resolve(results); } catch (error) { reject(new Error(`Failed to parse gitleaks results: ${error}`)); } }); gitleaks.on('error', (error) => { reject(new Error(`Gitleaks execution failed: ${error.message}`)); }); }); } async isAvailable() { return new Promise((resolve) => { const gitleaks = (0, child_process_1.spawn)('gitleaks', ['version'], { stdio: 'pipe' }); let hasOutput = false; gitleaks.stdout.on('data', () => { hasOutput = true; }); gitleaks.stderr.on('data', () => { hasOutput = true; }); gitleaks.on('close', (code) => { resolve(code === 0 || hasOutput); }); gitleaks.on('error', (err) => { console.error('Gitleaks spawn error:', err); resolve(false); }); // Timeout after 5 seconds setTimeout(() => { gitleaks.kill(); resolve(false); }, 5000); }); } mapSeverity(ruleId) { const criticalRules = ['aws-access-key', 'aws-secret-key', 'private-key', 'password']; const highRules = ['api-key', 'token', 'secret']; const mediumRules = ['email', 'url']; if (criticalRules.some(rule => ruleId.toLowerCase().includes(rule))) { return 'critical'; } else if (highRules.some(rule => ruleId.toLowerCase().includes(rule))) { return 'high'; } else if (mediumRules.some(rule => ruleId.toLowerCase().includes(rule))) { return 'medium'; } else { return 'low'; } } } exports.GitleaksScanner = GitleaksScanner; exports.default = new GitleaksScanner(); //# sourceMappingURL=gitleaks.js.map