pury
Version:
đĄī¸ AI-powered security scanner with advanced threat detection, dual reporting system (detailed & summary), and comprehensive code analysis
316 lines âĸ 15.6 kB
JavaScript
import { Severity } from '../types/index.js';
export class MarkdownReporter {
async output(report) {
return this.generateMarkdownReport(report);
}
async writeToFile(report, filePath) {
const fs = await import('fs/promises');
const path = await import('path');
// Ensure directory exists
await fs.mkdir(path.dirname(filePath), { recursive: true });
const markdown = this.generateMarkdownReport(report);
await fs.writeFile(filePath, markdown, 'utf8');
}
generateMarkdownReport(report) {
const lines = [];
// Header with enhanced scientific presentation
lines.push('# đĄī¸ PuryAI Security Analysis Report');
lines.push('');
lines.push('> **Comprehensive Security Assessment & Risk Analysis**');
lines.push('> ');
lines.push('> *Advanced AI-Powered Static Code Analysis with Multi-Layer Detection*');
lines.push('');
lines.push('---');
lines.push('');
// Executive Summary
lines.push('## đ Executive Summary');
lines.push('');
const riskLevel = this.calculateOverallRiskLevel(report.summary.severityCount);
const riskScore = this.calculateRiskScore(report.summary.severityCount, report.summary.filesScanned);
lines.push(`**Overall Risk Assessment:** ${riskLevel} (Score: ${riskScore}/100)`);
lines.push(`**Analysis Scope:** ${report.summary.filesScanned} files analyzed`);
lines.push(`**Total Findings:** ${report.summary.threatsFound} security and quality issues identified`);
lines.push('');
// Detailed Metrics
lines.push('## đ Quantitative Analysis');
lines.push('');
// Enhanced metrics table
const scanRate = report.summary.filesScanned > 0
? (report.summary.filesScanned / (report.summary.scanDuration / 1000)).toFixed(2)
: '0';
const avgIssuesPerFile = report.summary.filesScanned > 0
? (report.summary.threatsFound / report.summary.filesScanned).toFixed(2)
: '0';
lines.push('| Metric | Value | Analysis |');
lines.push('|--------|-------|----------|');
lines.push(`| **Files Analyzed** | ${report.summary.filesScanned} | Target scope coverage |`);
lines.push(`| **Security Issues** | ${report.summary.threatsFound} | Total findings across all categories |`);
lines.push(`| **Scan Duration** | ${(report.summary.scanDuration / 1000).toFixed(2)}s | Processing efficiency |`);
lines.push(`| **Analysis Rate** | ${scanRate} files/sec | Performance metric |`);
lines.push(`| **Issue Density** | ${avgIssuesPerFile} issues/file | Code quality indicator |`);
lines.push(`| **Risk Score** | ${riskScore}/100 | Composite risk assessment |`);
lines.push(`| **Scan Timestamp** | ${new Date(report.metadata.timestamp).toLocaleString()} | Analysis execution time |`);
if (report.metadata.aiProvider) {
lines.push(`| **AI Analysis Engine** | ${report.metadata.aiProvider} | Detection methodology |`);
}
lines.push('');
// Enhanced severity analysis
if (report.summary.threatsFound > 0) {
lines.push('### đ¯ Risk Distribution Analysis');
lines.push('');
// Calculate percentages and risk weights
const totalIssues = report.summary.threatsFound;
lines.push('| Severity Level | Count | Percentage | Risk Weight | Priority |');
lines.push('|----------------|-------|------------|-------------|----------|');
const severityData = [
{ level: 'Critical', key: 'critical', weight: 10, priority: 'Immediate Action Required' },
{ level: 'High', key: 'high', weight: 7, priority: 'Address within 24h' },
{ level: 'Medium', key: 'medium', weight: 4, priority: 'Plan remediation' },
{ level: 'Low', key: 'low', weight: 1, priority: 'Monitor & review' }
];
for (const sev of severityData) {
const count = report.summary.severityCount[sev.key] || 0;
if (count > 0) {
const percentage = ((count / totalIssues) * 100).toFixed(1);
const icon = this.getSeverityIcon(sev.key);
const badge = this.getSeverityBadge(sev.key);
lines.push(`| ${icon} **${sev.level}** ${badge} | ${count} | ${percentage}% | ${sev.weight}/10 | ${sev.priority} |`);
}
}
lines.push('');
}
// Enhanced findings analysis
if (report.findings.length > 0) {
lines.push('## đŦ Detailed Security Findings Analysis');
lines.push('');
lines.push('> **Methodology:** Static analysis with pattern recognition, entropy analysis, and AI-powered threat detection');
lines.push('');
const groupedByFile = this.groupFindingsByFile(report.findings);
const basePath = process.cwd();
let fileIndex = 1;
for (const [filePath, findings] of groupedByFile.entries()) {
const relativePath = this.getRelativePath(filePath, basePath);
const fileRiskScore = this.calculateFileRiskScore(findings);
const issueCount = findings.length;
lines.push(`### ${fileIndex}. đ \`${relativePath}\``);
lines.push('');
lines.push(`**File Risk Assessment:** ${fileRiskScore}/100 | **Issues Found:** ${issueCount}`);
lines.push('');
// Group by severity for this file
const severityGroups = this.groupFindingsBySeverity(findings);
for (const severity of ['critical', 'high', 'medium', 'low']) {
const severityFindings = severityGroups[severity];
if (severityFindings && severityFindings.length > 0) {
const icon = this.getSeverityIcon(severity);
const badge = this.getSeverityBadge(severity);
lines.push(`#### ${icon} ${severity.toUpperCase()} SEVERITY ${badge}`);
lines.push('');
// Group by type for scientific analysis
const typeGroups = this.groupFindingsByType(severityFindings);
for (const [issueType, typeFindings] of typeGroups.entries()) {
const frequency = typeFindings.length;
const confidenceLevel = this.calculateConfidenceLevel(typeFindings);
lines.push(`**đ Issue Pattern:** ${issueType}`);
lines.push(`**đ Detection Frequency:** ${frequency} occurrence${frequency > 1 ? 's' : ''}`);
lines.push(`**đ¯ Confidence Level:** ${confidenceLevel}%`);
lines.push('');
if (typeFindings.length === 1) {
const finding = typeFindings[0];
lines.push(`**đ Location:** ${finding.line ? `Line ${finding.line}` : 'File-level'}`);
if (finding.evidence && finding.evidence.length <= 100) {
lines.push(`**đŦ Evidence:** \`${finding.evidence}\``);
}
if (finding.description) {
lines.push(`**đ Analysis:** ${finding.description}`);
}
if (finding.suggestion) {
lines.push(`**đĄ Remediation:** ${finding.suggestion}`);
}
}
else {
lines.push(`**đ Locations:** Lines ${typeFindings.map(f => f.line || '?').join(', ')}`);
if (typeFindings[0]?.suggestion) {
lines.push(`**đĄ Remediation:** ${typeFindings[0].suggestion}`);
}
}
lines.push('');
}
}
}
lines.push('---');
lines.push('');
fileIndex++;
}
}
else {
lines.push('## â
No Security Issues Found!');
lines.push('');
lines.push('đ **Congratulations!** Your code appears clean and secure.');
lines.push('');
}
// Recommendations
if (report.summary.threatsFound > 0) {
lines.push('## đĄ Recommendations');
lines.push('');
lines.push('### Immediate Actions');
lines.push('');
const criticalCount = report.summary.severityCount.critical || 0;
const highCount = report.summary.severityCount.high || 0;
if (criticalCount > 0) {
lines.push(`- đ¨ **URGENT**: Address all ${criticalCount} critical severity issues immediately`);
}
if (highCount > 0) {
lines.push(`- â ī¸ **HIGH PRIORITY**: Review and fix ${highCount} high severity issues`);
}
lines.push('- đ Review medium and low priority issues for code quality improvements');
lines.push('- đ Run scans regularly to catch issues early');
lines.push('- đ Use `--format json` for detailed programmatic analysis');
lines.push('- đ¯ Consider running additional analyzers with `--analyzers` flag');
lines.push('');
lines.push('### Security Best Practices');
lines.push('');
lines.push('- đ Never commit secrets, API keys, or credentials to version control');
lines.push('- đĄī¸ Keep dependencies updated and scan for vulnerabilities regularly');
lines.push('- đ Implement code review processes for all changes');
lines.push('- đ Use static analysis tools in your CI/CD pipeline');
lines.push('- đ Follow secure coding guidelines for your technology stack');
}
else {
lines.push('## đ Excellent Security Posture!');
lines.push('');
lines.push('Keep up the great work! Continue following security best practices:');
lines.push('');
lines.push('- đ Run regular security scans');
lines.push('- đ Stay updated on security vulnerabilities');
lines.push('- đ ī¸ Keep dependencies updated');
lines.push('- đĨ Maintain security awareness in your team');
}
lines.push('');
// Footer
lines.push('---');
lines.push('');
lines.push(`**Generated by PuryAI v${report.metadata.version}** | ${new Date().toISOString()}`);
lines.push('');
lines.push('> đ Advanced AI-Powered Code Security Analysis');
return lines.join('\n');
}
groupFindingsByFile(findings) {
const grouped = new Map();
for (const finding of findings) {
if (!grouped.has(finding.file)) {
grouped.set(finding.file, []);
}
grouped.get(finding.file).push(finding);
}
return grouped;
}
groupFindingsByType(findings) {
const grouped = new Map();
for (const finding of findings) {
const key = finding.title;
if (!grouped.has(key)) {
grouped.set(key, []);
}
grouped.get(key).push(finding);
}
return grouped;
}
getRelativePath(filePath, basePath) {
if (filePath.startsWith(basePath)) {
const relativePath = filePath.substring(basePath.length);
return relativePath.startsWith('/') ? relativePath.substring(1) : relativePath;
}
return filePath;
}
groupFindingsBySeverity(findings) {
const grouped = {
[Severity.CRITICAL]: [],
[Severity.HIGH]: [],
[Severity.MEDIUM]: [],
[Severity.LOW]: []
};
for (const finding of findings) {
grouped[finding.severity].push(finding);
}
return grouped;
}
getSeverityIcon(severity) {
switch (severity) {
case Severity.CRITICAL:
return 'đ¨';
case Severity.HIGH:
return 'â ī¸';
case Severity.MEDIUM:
return 'đ';
case Severity.LOW:
return 'âšī¸';
default:
return 'đ';
}
}
getSeverityBadge(severity) {
switch (severity) {
case Severity.CRITICAL:
return '';
case Severity.HIGH:
return '';
case Severity.MEDIUM:
return '';
case Severity.LOW:
return '';
default:
return '';
}
}
calculateOverallRiskLevel(severityCount) {
const critical = severityCount.critical || 0;
const high = severityCount.high || 0;
const medium = severityCount.medium || 0;
if (critical > 0)
return 'đ¨ **CRITICAL RISK**';
if (high > 5)
return 'â ī¸ **HIGH RISK**';
if (high > 0 || medium > 10)
return 'đ **MEDIUM RISK**';
if (medium > 0)
return 'âšī¸ **LOW RISK**';
return 'â
**MINIMAL RISK**';
}
calculateRiskScore(severityCount, filesScanned) {
const critical = severityCount.critical || 0;
const high = severityCount.high || 0;
const medium = severityCount.medium || 0;
const low = severityCount.low || 0;
// Risk scoring algorithm: weighted severity * frequency / file count
const rawScore = critical * 10 + high * 7 + medium * 4 + low * 1;
const normalizedScore = Math.min(100, Math.round((rawScore / Math.max(1, filesScanned)) * 2));
return normalizedScore;
}
calculateFileRiskScore(findings) {
let score = 0;
for (const finding of findings) {
switch (finding.severity) {
case Severity.CRITICAL:
score += 25;
break;
case Severity.HIGH:
score += 15;
break;
case Severity.MEDIUM:
score += 8;
break;
case Severity.LOW:
score += 3;
break;
}
}
return Math.min(100, score);
}
calculateConfidenceLevel(findings) {
// Simple confidence calculation based on pattern consistency
const hasEvidence = findings.filter(f => f.evidence).length;
const totalFindings = findings.length;
return Math.round((hasEvidence / totalFindings) * 100);
}
}
//# sourceMappingURL=markdown.js.map