tops-bmad
Version:
CLI tool to install BMAD workflow files into any project with integrated Shai-Hulud 2.0 security scanning
204 lines (166 loc) • 6.01 kB
JavaScript
/**
* TOPS BMAD Security Scanner - Security Dashboard Generator
*
* Generates a security dashboard report from scan results
*
* Usage:
* node security-dashboard.js [--output <file>]
*/
import { readFileSync, writeFileSync, existsSync, statSync } from 'fs';
import { join, dirname, resolve } from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const workspaceRoot = resolve(__dirname, '..', '..');
const configPath = join(__dirname, '..', 'config', 'organization-config.json');
// Load organization config
let orgConfig = { projects: [] };
if (existsSync(configPath)) {
try {
orgConfig = JSON.parse(readFileSync(configPath, 'utf8'));
} catch (error) {
console.warn('⚠️ Could not load organization config:', error.message);
}
}
function findScanResults() {
const results = [];
const projects = orgConfig.projects || [];
// Search for SARIF files
for (const project of projects) {
const projectPath = resolve(workspaceRoot, project.path || project);
const sarifPath = join(projectPath, 'shai-hulud-results.sarif');
if (existsSync(sarifPath)) {
try {
const sarifData = JSON.parse(readFileSync(sarifPath, 'utf8'));
const stats = statSync(sarifPath);
results.push({
project: project.name || project.path || project,
path: projectPath,
sarifPath,
timestamp: stats.mtime,
results: sarifData.runs?.[0]?.results || [],
rules: sarifData.runs?.[0]?.tool?.driver?.rules || []
});
} catch (error) {
console.warn(`⚠️ Could not read SARIF file: ${sarifPath}`);
}
}
}
return results;
}
function generateDashboard(scanResults, outputPath) {
const lines = [];
lines.push('# TOPS BMAD Security Dashboard');
lines.push('');
lines.push(`Generated: ${new Date().toISOString()}`);
lines.push('');
lines.push('---');
lines.push('');
if (scanResults.length === 0) {
lines.push('## No Scan Results Found');
lines.push('');
lines.push('Run security scans to generate results:');
lines.push('```bash');
lines.push('npm run security:scan:all');
lines.push('```');
lines.push('');
return lines.join('\n');
}
lines.push('## Summary');
lines.push('');
let totalIssues = 0;
let totalCritical = 0;
let totalHigh = 0;
for (const result of scanResults) {
const critical = result.results.filter(r => r.level === 'error').length;
const high = result.results.filter(r => r.level === 'warning').length;
const total = result.results.length;
totalIssues += total;
totalCritical += critical;
totalHigh += high;
}
lines.push(`- **Total Projects Scanned:** ${scanResults.length}`);
lines.push(`- **Total Issues Found:** ${totalIssues}`);
lines.push(`- **Critical Issues:** ${totalCritical}`);
lines.push(`- **High Issues:** ${totalHigh}`);
lines.push('');
lines.push('---');
lines.push('');
lines.push('## Project Details');
lines.push('');
for (const result of scanResults) {
const critical = result.results.filter(r => r.level === 'error').length;
const high = result.results.filter(r => r.level === 'warning').length;
const total = result.results.length;
lines.push(`### ${result.project}`);
lines.push('');
lines.push(`- **Path:** \`${result.path}\``);
lines.push(`- **Last Scanned:** ${result.timestamp.toISOString()}`);
lines.push(`- **Total Issues:** ${total}`);
lines.push(` - Critical: ${critical}`);
lines.push(` - High: ${high}`);
lines.push('');
if (total > 0) {
lines.push('#### Issues:');
lines.push('');
for (const issue of result.results.slice(0, 10)) { // Show first 10
const level = issue.level === 'error' ? '🔴' : issue.level === 'warning' ? '🟡' : '🔵';
lines.push(`- ${level} **${issue.level.toUpperCase()}**: ${issue.message.text.substring(0, 100)}${issue.message.text.length > 100 ? '...' : ''}`);
}
if (total > 10) {
lines.push(`- ... and ${total - 10} more issues`);
}
lines.push('');
} else {
lines.push('✅ No issues found');
lines.push('');
}
lines.push('---');
lines.push('');
}
lines.push('## Recommendations');
lines.push('');
if (totalCritical > 0) {
lines.push('🔴 **CRITICAL**: Immediate action required');
lines.push('');
lines.push('1. Review all critical findings');
lines.push('2. Rotate all credentials');
lines.push('3. Update compromised packages');
lines.push('4. Review CI/CD configurations');
lines.push('');
} else if (totalHigh > 0) {
lines.push('🟡 **HIGH**: Review and address high-priority issues');
lines.push('');
lines.push('1. Review high-severity findings');
lines.push('2. Update packages as needed');
lines.push('3. Review security configurations');
lines.push('');
} else {
lines.push('✅ **CLEAN**: No critical or high-severity issues found');
lines.push('');
lines.push('Continue regular security scans to maintain security posture.');
lines.push('');
}
return lines.join('\n');
}
function main() {
const args = process.argv.slice(2);
let outputPath = null;
for (let i = 0; i < args.length; i++) {
if (args[i] === '--output' && i + 1 < args.length) {
outputPath = resolve(args[++i]);
}
}
console.log('📊 Generating security dashboard...\n');
const scanResults = findScanResults();
console.log(`📦 Found scan results for ${scanResults.length} project(s)\n`);
const dashboard = generateDashboard(scanResults, outputPath);
if (outputPath) {
writeFileSync(outputPath, dashboard, 'utf8');
console.log(`✅ Dashboard written to: ${outputPath}`);
} else {
console.log(dashboard);
}
}
main();