#![allow(clippy::expect_used)]
use veracode_platform::{
FindingsQuery, GetBuildInfoRequest, VeracodeClient, VeracodeConfig, validation::AppGuid,
};
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = VeracodeConfig::new(
&std::env::var("VERACODE_API_ID").expect("VERACODE_API_ID required"),
&std::env::var("VERACODE_API_KEY").expect("VERACODE_API_KEY required"),
);
let app_name_or_guid = std::env::var("APP_NAME")
.or_else(|_| std::env::var("APP_GUID"))
.expect("APP_NAME or APP_GUID environment variable required");
let client = VeracodeClient::new(config.clone())?;
let _policy_api = client.policy_api();
let build_api = client.build_api()?;
println!("🔍 Policy Scan Findings & Summary Report with GUID Resolution");
let (app_guid, policy_guid) = if app_name_or_guid.contains('-') && app_name_or_guid.len() == 36
{
println!("🔍 Fetching application details for GUID: {app_name_or_guid}");
match client
.get_application(&AppGuid::new(&app_name_or_guid)?)
.await
{
Ok(app) => {
let app_name = app
.profile
.as_ref()
.map(|p| p.name.as_str())
.unwrap_or(&app_name_or_guid);
println!("✅ Found application: {} (GUID: {})", app_name, app.guid);
let policy_guid = match &app.profile {
Some(profile) => match &profile.policies {
Some(policies) if !policies.is_empty() => {
let policy = policies.first().expect("should have first policy"); println!("✅ Found policy: {} (GUID: {})", policy.name, policy.guid);
policy.guid.clone()
}
_ => {
println!("⚠️ No policy found for application");
String::from("no-policy-found")
}
},
None => {
println!("⚠️ No application profile found");
String::from("no-profile-found")
}
};
(app.guid.clone(), policy_guid)
}
Err(e) => {
eprintln!("❌ Failed to fetch application by GUID: {e}");
return Ok(());
}
}
} else {
println!("🔍 Resolving application name to GUID: {app_name_or_guid}");
match client.get_application_by_name(&app_name_or_guid).await {
Ok(Some(app)) => {
println!(
"✅ Found application: {} (GUID: {})",
app.profile
.as_ref()
.map(|p| p.name.as_str())
.unwrap_or(&app_name_or_guid),
app.guid
);
let policy_guid = match &app.profile {
Some(profile) => match &profile.policies {
Some(policies) if !policies.is_empty() => {
let policy = policies.first().expect("should have first policy"); println!("✅ Found policy: {} (GUID: {})", policy.name, policy.guid);
policy.guid.clone()
}
_ => {
println!("⚠️ No policy found for application");
String::from("no-policy-found")
}
},
None => {
println!("⚠️ No application profile found");
String::from("no-profile-found")
}
};
(app.guid.clone(), policy_guid)
}
Ok(None) => {
eprintln!("❌ Application not found: {app_name_or_guid}");
return Ok(());
}
Err(e) => {
eprintln!("❌ Error searching for application: {e}");
return Ok(());
}
}
};
println!("\n📊 Application: {app_guid} | Policy: {policy_guid}");
let app_id = match client.get_app_id_from_guid(&AppGuid::new(&app_guid)?).await {
Ok(id) => id,
Err(e) => {
eprintln!("❌ Failed to get app ID from GUID: {e}");
return Ok(());
}
};
println!("\n📡 Getting latest policy scan build information...");
let build_info_request = GetBuildInfoRequest {
app_id: app_id.clone(),
build_id: None, sandbox_id: None, };
match build_api.get_build_info(&build_info_request).await {
Ok(build_info) => {
println!("✅ Policy Scan Build Information:");
println!(" Build ID: {}", build_info.build_id);
println!(" App ID: {}", build_info.app_id);
println!(
" Version: {}",
build_info.version.as_deref().unwrap_or("N/A")
);
println!(
" App Name: {}",
build_info.app_name.as_deref().unwrap_or("N/A")
);
println!(
" Platform: {}",
build_info.platform.as_deref().unwrap_or("N/A")
);
println!(
" Policy Compliance: {}",
build_info
.policy_compliance_status
.as_deref()
.unwrap_or("N/A")
);
println!(
" Rules Status: {}",
build_info.rules_status.as_deref().unwrap_or("N/A")
);
println!("\n🧪 Testing new structured Findings API for policy scan...");
match client
.findings_api()
.get_findings(
&FindingsQuery::new(&app_guid) .with_pagination(0, 10) .with_severity(vec![3, 4, 5]),
) .await
{
Ok(findings_response) => {
println!("✅ Structured Findings API Response:");
println!(" Total elements: {}", findings_response.total_elements());
println!(
" Current page: {} of {}",
findings_response.current_page().saturating_add(1),
findings_response.total_pages()
);
println!(
" Findings on this page: {}",
findings_response.findings().len()
);
println!(" Has next page: {}", findings_response.has_next_page());
for (i, finding) in findings_response.findings().iter().enumerate() {
let i: usize = i;
println!(
"\n 📋 Finding #{} (Issue ID: {})",
i.saturating_add(1),
finding.issue_id
);
println!(
" CWE-{}: {}",
finding.finding_details.cwe.id, finding.finding_details.cwe.name
);
println!(
" Severity: {} | File: {} (line {})",
finding.finding_details.severity,
finding.finding_details.file_name,
finding.finding_details.file_line_number
);
println!(
" Status: {} | Violates Policy: {}",
finding.finding_status.status, finding.violates_policy
);
if finding.violates_policy {
println!(" ⚠️ POLICY VIOLATION!");
}
}
}
Err(e) => {
eprintln!("❌ Structured Findings API call failed: {e}");
}
}
println!("\n🔄 Testing auto-paginated findings collection...");
match client
.findings_api() .get_all_policy_findings(&app_guid)
.await
{
Ok(all_findings) => {
println!(
"✅ Retrieved all {} findings across all pages",
all_findings.len()
);
let mut severity_counts: std::collections::HashMap<u32, usize> =
std::collections::HashMap::new();
let mut policy_violations: usize = 0;
for finding in &all_findings {
*severity_counts
.entry(finding.finding_details.severity)
.or_insert(0) = severity_counts
.get(&finding.finding_details.severity)
.unwrap_or(&0)
.saturating_add(1);
if finding.violates_policy {
policy_violations = policy_violations.saturating_add(1);
}
}
println!(" 📊 Findings by Severity:");
for severity in [5, 4, 3, 2, 1, 0] {
if let Some(count) = severity_counts.get(&severity) {
let severity_name = match severity {
5 => "Very High",
4 => "High",
3 => "Medium",
2 => "Low",
1 => "Very Low",
0 => "Informational",
_ => "Unknown",
};
println!(
" Severity {severity} ({severity_name}): {count} findings"
);
}
}
println!(" ⚠️ Policy Violations: {policy_violations} findings");
}
Err(e) => {
eprintln!("❌ Auto-paginated findings collection failed: {e}");
}
}
println!("\n📡 Legacy raw API call for comparison...");
let findings_endpoint =
format!("/appsec/v2/applications/{app_guid}/findings?page=0&size=5");
println!("📡 Calling findings endpoint for policy scan: {findings_endpoint}");
match client.get(&findings_endpoint, None).await {
Ok(response) => {
let status = response.status();
println!("✅ Legacy Findings Response Status: {status}");
let body = response.text().await?;
println!("\n📄 Legacy Raw JSON Response (truncated):");
let truncated = if body.chars().count() > 1000 {
format!(
"{}...\n[Response truncated - {} total characters]",
body.chars().take(1000).collect::<String>(),
body.chars().count()
)
} else {
body
};
println!("{truncated}");
}
Err(e) => {
eprintln!("❌ Legacy Findings API call failed: {e}");
}
}
let summary_endpoint = format!("/appsec/v2/applications/{app_guid}/summary_report");
println!("\n📡 Calling summary report endpoint for policy scan: {summary_endpoint}");
match client.get(&summary_endpoint, None).await {
Ok(response) => {
let status = response.status();
println!("✅ Summary Report Response Status: {status}");
let body = response.text().await?;
println!("\n📄 Policy Scan Summary Report JSON (truncated):");
let truncated = if body.chars().count() > 1500 {
format!(
"{}...\n[Response truncated - {} total characters]",
body.chars().take(1500).collect::<String>(),
body.chars().count()
)
} else {
body
};
println!("{truncated}");
}
Err(e) => {
eprintln!("❌ Summary Report API call failed: {e}");
}
}
}
Err(e) => {
eprintln!("❌ Failed to get build info: {e}");
}
}
println!("\n🎯 Policy Scan Findings API Testing Results:");
println!(
"This example demonstrated the new structured Findings API features for policy scans:"
);
println!(" 1. Paginated findings retrieval with filtering (severity, etc.)");
println!(" 2. Auto-paginated collection to get all findings across pages");
println!(" 3. Rich structured data access (CWE details, file locations, severity)");
println!(" 4. Policy scan context (no sandbox context required)");
println!(" 5. Policy violation detection and analysis");
println!("\n📋 Benefits:");
println!(" • Type-safe access to finding details instead of raw JSON parsing");
println!(" • Automatic pagination handling to collect all results");
println!(" • Memory-efficient with Cow<> patterns for string handling");
println!(" • Rich filtering capabilities (severity, CWE, scan type, etc.)");
println!("\n📝 Usage:");
println!(" export VERACODE_API_ID=\"your_api_id\"");
println!(" export VERACODE_API_KEY=\"your_api_key\"");
println!(" export APP_NAME=\"your_app_name\" # or APP_GUID");
println!(" cargo run --example simple_policy_json");
Ok(())
}