Skip to content

Commit 1b8eeab

Browse files
Copilot0xrinegade
andcommitted
Add audit command with Typst report generation
Co-authored-by: 0xrinegade <[email protected]>
1 parent d557566 commit 1b8eeab

File tree

9 files changed

+899
-509
lines changed

9 files changed

+899
-509
lines changed

.gitignore

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,12 @@ node_modules/
1111
.DS_Store
1212

1313
package-lock.json
14-
~*.*
14+
~*.*
15+
16+
# Typst installation files
17+
typst-*
18+
*.tar.xz
19+
20+
# Generated audit reports
21+
audit_reports/
22+
*.pdf

src/clparse.rs

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -794,10 +794,6 @@ pub fn parse_command_line() -> clap::ArgMatches {
794794
.help("Require confirmation for deploying large binaries (>1MB)")
795795
)
796796
)
797-
.subcommand(
798-
Command::new("new_feature_command")
799-
.about("New feature for testing")
800-
)
801797
.subcommand(
802798
Command::new("solana")
803799
.about("Deploy and manage Solana validators")
@@ -950,6 +946,39 @@ pub fn parse_command_line() -> clap::ArgMatches {
950946
.help("Detailed diagnostic output")
951947
)
952948
)
949+
.subcommand(
950+
Command::new("audit")
951+
.about("Generate comprehensive security audit report")
952+
.arg(
953+
Arg::new("output")
954+
.long("output")
955+
.short('o')
956+
.value_name("PATH")
957+
.help("Output directory for audit report files")
958+
.default_value("audit_reports")
959+
)
960+
.arg(
961+
Arg::new("format")
962+
.long("format")
963+
.value_name("FORMAT")
964+
.value_parser(clap::builder::PossibleValuesParser::new(["typst", "pdf", "both"]))
965+
.default_value("both")
966+
.help("Output format: typst source, PDF, or both")
967+
)
968+
.arg(
969+
Arg::new("verbose")
970+
.long("verbose")
971+
.short('v')
972+
.action(ArgAction::Count)
973+
.help("Verbose audit output")
974+
)
975+
.arg(
976+
Arg::new("test")
977+
.long("test")
978+
.action(ArgAction::SetTrue)
979+
.help("Generate test audit report with sample data")
980+
)
981+
)
953982
.subcommand(
954983
Command::new("new_feature_command")
955984
.about("New feature for testing")

src/main.rs

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1699,6 +1699,131 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
16991699
}
17001700
}
17011701
}
1702+
"audit" => {
1703+
use crate::utils::audit::AuditCoordinator;
1704+
use std::fs;
1705+
use std::path::Path;
1706+
1707+
println!("🔍 OSVM Security Audit");
1708+
println!("======================");
1709+
1710+
let output_dir = matches.get_one::<String>("output").unwrap();
1711+
let format = matches.get_one::<String>("format").unwrap();
1712+
let verbose = matches.get_count("verbose");
1713+
let test_mode = matches.get_flag("test");
1714+
1715+
if verbose > 0 {
1716+
println!("📁 Output directory: {}", output_dir);
1717+
println!("📄 Format: {}", format);
1718+
if test_mode {
1719+
println!("🧪 Test mode: generating sample audit report");
1720+
}
1721+
}
1722+
1723+
// Create output directory
1724+
if let Err(e) = fs::create_dir_all(output_dir) {
1725+
eprintln!("❌ Failed to create output directory: {}", e);
1726+
exit(1);
1727+
}
1728+
1729+
// Initialize audit coordinator
1730+
let audit_coordinator = AuditCoordinator::new();
1731+
1732+
// Generate audit report
1733+
let report = if test_mode {
1734+
println!("🧪 Generating test audit report...");
1735+
audit_coordinator.create_test_audit_report()
1736+
} else {
1737+
// Run security audit
1738+
match audit_coordinator.run_security_audit().await {
1739+
Ok(report) => report,
1740+
Err(e) => {
1741+
eprintln!("❌ Failed to run security audit: {}", e);
1742+
exit(1);
1743+
}
1744+
}
1745+
};
1746+
1747+
println!("✅ Security audit completed successfully");
1748+
println!("📊 Security Score: {:.1}/100", report.summary.security_score);
1749+
println!("🔍 Total Findings: {}", report.summary.total_findings);
1750+
1751+
if report.summary.critical_findings > 0 {
1752+
println!("🔴 Critical: {}", report.summary.critical_findings);
1753+
}
1754+
if report.summary.high_findings > 0 {
1755+
println!("🟠 High: {}", report.summary.high_findings);
1756+
}
1757+
if report.summary.medium_findings > 0 {
1758+
println!("🟡 Medium: {}", report.summary.medium_findings);
1759+
}
1760+
if report.summary.low_findings > 0 {
1761+
println!("🔵 Low: {}", report.summary.low_findings);
1762+
}
1763+
1764+
// Generate timestamp for unique filenames
1765+
let timestamp = chrono::Utc::now().format("%Y%m%d_%H%M%S");
1766+
1767+
// Generate outputs based on requested format
1768+
let typst_path = Path::new(output_dir).join(format!("osvm_audit_report_{}.typ", timestamp));
1769+
let pdf_path = Path::new(output_dir).join(format!("osvm_audit_report_{}.pdf", timestamp));
1770+
1771+
match format.as_str() {
1772+
"typst" | "both" => {
1773+
if let Err(e) = audit_coordinator.generate_typst_document(&report, &typst_path) {
1774+
eprintln!("❌ Failed to generate Typst document: {}", e);
1775+
exit(1);
1776+
}
1777+
println!("📄 Typst document generated: {}", typst_path.display());
1778+
1779+
if format == "both" {
1780+
if let Err(e) = audit_coordinator.compile_to_pdf(&typst_path, &pdf_path) {
1781+
eprintln!("❌ Failed to compile PDF: {}", e);
1782+
eprintln!(" Typst document is available at: {}", typst_path.display());
1783+
} else {
1784+
println!("📋 PDF report generated: {}", pdf_path.display());
1785+
}
1786+
}
1787+
}
1788+
"pdf" => {
1789+
// Generate Typst document first (temporary)
1790+
if let Err(e) = audit_coordinator.generate_typst_document(&report, &typst_path) {
1791+
eprintln!("❌ Failed to generate Typst document: {}", e);
1792+
exit(1);
1793+
}
1794+
1795+
if let Err(e) = audit_coordinator.compile_to_pdf(&typst_path, &pdf_path) {
1796+
eprintln!("❌ Failed to compile PDF: {}", e);
1797+
exit(1);
1798+
}
1799+
1800+
// Remove temporary Typst file
1801+
let _ = fs::remove_file(&typst_path);
1802+
println!("📋 PDF report generated: {}", pdf_path.display());
1803+
}
1804+
_ => {
1805+
eprintln!("❌ Invalid format specified");
1806+
exit(1);
1807+
}
1808+
}
1809+
1810+
if verbose > 0 {
1811+
println!("\n📋 Audit Summary:");
1812+
println!(" Compliance Level: {}", report.summary.compliance_level);
1813+
println!(" System: {} {}", report.system_info.os_info, report.system_info.architecture);
1814+
println!(" Rust Version: {}", report.system_info.rust_version);
1815+
if let Some(ref solana_version) = report.system_info.solana_version {
1816+
println!(" Solana Version: {}", solana_version);
1817+
}
1818+
}
1819+
1820+
println!("\n💡 To view the full report, open the generated file.");
1821+
1822+
if !test_mode && (report.summary.critical_findings > 0 || report.summary.high_findings > 0) {
1823+
println!("⚠️ Critical or high-severity findings detected. Please review and address them promptly.");
1824+
exit(1);
1825+
}
1826+
}
17021827
"new_feature_command" => {
17031828
println!("Expected output for new feature");
17041829
}

0 commit comments

Comments
 (0)