use sarif_rust::builder::*;
use sarif_rust::types::Level;
use sarif_rust::utils::conversion::*;
use std::fs;
use std::path::Path;
fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("🔄 SARIF Conversion Demo - All Formats");
println!("=====================================\n");
let sarif_log = create_sample_sarif_log()?;
println!("📊 Created SARIF log with:");
println!(" - {} runs", sarif_log.runs.len());
let total_results: usize = sarif_log
.runs
.iter()
.map(|run| run.results.as_ref().map_or(0, |r| r.len()))
.sum();
println!(" - {} total results", total_results);
println!();
let output_dir = "sarif_outputs";
fs::create_dir_all(output_dir)?;
println!("📁 Created output directory: {}", output_dir);
println!();
output_sarif_json(&sarif_log, output_dir)?;
output_csv_format(&sarif_log, output_dir)?;
output_html_format(&sarif_log, output_dir)?;
output_jsonl_format(&sarif_log, output_dir)?;
output_github_format(&sarif_log, output_dir)?;
output_with_custom_config(&sarif_log, output_dir)?;
println!("\n✅ All conversions completed successfully!");
println!("\nGenerated files in '{}':", output_dir);
println!(" 📄 results.sarif - Original SARIF JSON");
println!(" 📊 results.csv - CSV format");
println!(" 🌐 results.html - HTML report");
println!(" 📋 results.jsonl - JSON Lines format");
println!(" 🔒 results_github.json - GitHub Security format");
println!(" ⚙️ results_filtered.csv - Custom filtered CSV");
println!();
println!("💡 Open results.html in a web browser to see the interactive report!");
Ok(())
}
fn create_sample_sarif_log() -> Result<sarif_rust::types::SarifLog, Box<dyn std::error::Error>> {
let sarif_log = SarifLogBuilder::new()
.with_schema("https://raw.githubusercontent.com/oasis-tcs/sarif-spec/master/Schemata/sarif-schema-2.1.0.json")
.add_run(
RunBuilder::with_tool("security-scanner", Some("2.1.0"))
.add_result(
ResultBuilder::with_text_message("SQL injection vulnerability detected in user input validation")
.with_rule_id("CWE-89")
.with_level(Level::Error)
.add_file_location("src/auth/login.rs", 42, 15)
.build()
)
.add_result(
ResultBuilder::with_text_message("Hardcoded password found in configuration")
.with_rule_id("CWE-798")
.with_level(Level::Error)
.add_file_location("src/config/database.rs", 28, 1)
.build()
)
.add_result(
ResultBuilder::with_text_message("Unescaped user input in HTML template")
.with_rule_id("CWE-79")
.with_level(Level::Warning)
.add_file_location("src/web/templates.rs", 156, 8)
.build()
)
.add_result(
ResultBuilder::with_text_message("Missing error handling for file operations")
.with_rule_id("ERR-001")
.with_level(Level::Warning)
.add_file_location("src/utils/file_manager.rs", 89, 12)
.build()
)
.build()
)
.add_run(
RunBuilder::with_tool("style-checker", Some("1.5.2"))
.add_result(
ResultBuilder::with_text_message("Consider using const instead of let for immutable values")
.with_rule_id("STYLE-001")
.with_level(Level::Note)
.add_file_location("src/main.rs", 15, 5)
.build()
)
.add_result(
ResultBuilder::with_text_message("Function exceeds recommended length (>50 lines)")
.with_rule_id("STYLE-002")
.with_level(Level::Note)
.add_file_location("src/business/calculator.rs", 78, 1)
.build()
)
.add_result(
ResultBuilder::with_text_message("Unused import detected")
.with_rule_id("STYLE-003")
.with_level(Level::Note)
.add_file_location("src/helpers/utils.rs", 3, 1)
.build()
)
.build()
)
.build()?;
Ok(sarif_log)
}
fn output_sarif_json(
sarif: &sarif_rust::types::SarifLog,
output_dir: &str,
) -> Result<(), Box<dyn std::error::Error>> {
println!("1. 📄 Converting to SARIF JSON");
let json = sarif_rust::to_string_pretty(sarif)?;
let file_path = Path::new(output_dir).join("results.sarif");
fs::write(&file_path, &json)?;
println!(
" ✓ Generated: {} ({} bytes)",
file_path.display(),
json.len()
);
println!(" 📋 Pretty-printed SARIF v2.1.0 JSON format");
println!();
Ok(())
}
fn output_csv_format(
sarif: &sarif_rust::types::SarifLog,
output_dir: &str,
) -> Result<(), Box<dyn std::error::Error>> {
println!("2. 📊 Converting to CSV");
let csv_converter = CsvConverter::new();
let csv_output = csv_converter.convert_to_csv(sarif)?;
let file_path = Path::new(output_dir).join("results.csv");
fs::write(&file_path, &csv_output)?;
println!(
" ✓ Generated: {} ({} bytes)",
file_path.display(),
csv_output.len()
);
println!(
" 📋 Comma-separated values with columns: tool, rule_id, level, message, file, line"
);
let lines: Vec<&str> = csv_output.lines().take(3).collect();
if lines.len() > 1 {
println!(" 📄 Preview:");
for (i, line) in lines.iter().enumerate() {
if i == 0 {
println!(" Header: {}", line);
} else {
println!(" Row {}: {}", i, line);
}
}
}
println!();
Ok(())
}
fn output_html_format(
sarif: &sarif_rust::types::SarifLog,
output_dir: &str,
) -> Result<(), Box<dyn std::error::Error>> {
println!("3. 🌐 Converting to HTML");
let html_converter = HtmlConverter::new();
let html_output = html_converter.convert_to_html(sarif)?;
let file_path = Path::new(output_dir).join("results.html");
fs::write(&file_path, &html_output)?;
println!(
" ✓ Generated: {} ({} bytes)",
file_path.display(),
html_output.len()
);
println!(" 📋 Interactive HTML report with styling and navigation");
println!(" 🌐 Open in web browser for best viewing experience");
println!();
Ok(())
}
fn output_jsonl_format(
sarif: &sarif_rust::types::SarifLog,
output_dir: &str,
) -> Result<(), Box<dyn std::error::Error>> {
println!("4. 📋 Converting to JSON Lines (JSONL)");
let jsonl_converter = JsonLinesConverter::new();
let jsonl_output = jsonl_converter.convert_to_jsonl(sarif)?;
let file_path = Path::new(output_dir).join("results.jsonl");
fs::write(&file_path, &jsonl_output)?;
println!(
" ✓ Generated: {} ({} bytes)",
file_path.display(),
jsonl_output.len()
);
println!(" 📋 One JSON object per line, ideal for streaming and log processing");
let line_count = jsonl_output.lines().count();
println!(" 📊 Contains {} lines (one per result)", line_count);
println!();
Ok(())
}
fn output_github_format(
sarif: &sarif_rust::types::SarifLog,
output_dir: &str,
) -> Result<(), Box<dyn std::error::Error>> {
println!("5. 🔒 Converting to GitHub Security format");
let github_converter = GitHubSecurityConverter::new();
let github_output = github_converter.convert_to_github_format(sarif)?;
let file_path = Path::new(output_dir).join("results_github.json");
fs::write(&file_path, &github_output)?;
println!(
" ✓ Generated: {} ({} bytes)",
file_path.display(),
github_output.len()
);
println!(" 📋 GitHub-compatible security alert format");
println!(" 🔒 Ready for GitHub Security Advisory integration");
println!();
Ok(())
}
fn output_with_custom_config(
sarif: &sarif_rust::types::SarifLog,
output_dir: &str,
) -> Result<(), Box<dyn std::error::Error>> {
println!("6. ⚙️ Converting with custom configuration");
let config = ConversionConfig {
include_full_paths: true,
max_message_length: Some(100), min_level: Some(Level::Warning), include_snippets: false,
field_mappings: std::collections::HashMap::new(),
};
let csv_converter = CsvConverter::with_config(config);
let csv_output = csv_converter.convert_to_csv(sarif)?;
let file_path = Path::new(output_dir).join("results_filtered.csv");
fs::write(&file_path, &csv_output)?;
println!(
" ✓ Generated: {} ({} bytes)",
file_path.display(),
csv_output.len()
);
println!(" 📋 Filtered CSV (warnings/errors only, max 100 char messages)");
let filtered_lines = csv_output.lines().count();
println!(
" 📊 Filtered to {} results (excluding notes)",
filtered_lines.saturating_sub(1)
); println!();
Ok(())
}