-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgui_text.rs
More file actions
107 lines (96 loc) · 3.24 KB
/
Copy pathgui_text.rs
File metadata and controls
107 lines (96 loc) · 3.24 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
// SPDX-License-Identifier: MPL-2.0
// SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>
//! Headless GUI report renderer — text-only output, no display server.
//!
//! This is the `gui --headless` path. It is always compiled (independent of the
//! `gui` feature flag) so the readiness test `readiness_d_gui_headless_runs`
//! and CI integrations keep working in MSRV-clean default builds. The
//! windowed renderer in [`crate::report::gui`] is feature-gated because eframe
//! raises MSRV above 1.85.0.
use crate::report::formatter::ReportFormatter;
use crate::types::AssaultReport;
use anyhow::Result;
/// Print a structured text summary of every GUI panel.
///
/// Safe to call in CI or headless environments with no Wayland/X11 display.
/// Promotes the `gui` subcommand from Grade E to Grade D.
pub fn run_headless(report: AssaultReport) -> Result<()> {
let assail = &report.assail_report;
let formatter = ReportFormatter::new();
println!("PANIC-ATTACK GUI REPORT (headless)");
println!();
println!("=== Summary ===");
println!("Language: {:?}", assail.language);
println!(
"Score: {:.1}/100",
report.overall_assessment.robustness_score
);
println!("Crashes: {}", report.total_crashes);
println!("Signatures: {}", report.total_signatures);
println!("Weak points: {}", assail.weak_points.len());
println!("Frameworks: {:?}", assail.frameworks);
println!();
println!("=== Assail ===");
println!("Program: {}", assail.program_path.display());
println!(
"Stats: lines={} unsafe={} panics={} unwraps={}",
assail.statistics.total_lines,
assail.statistics.unsafe_blocks,
assail.statistics.panic_sites,
assail.statistics.unwrap_calls
);
for wp in &assail.weak_points {
let loc = wp
.location
.as_deref()
.or(wp.file.as_deref())
.unwrap_or("<unknown>");
println!(
" [{:?}] {:?} @ {} — {}",
wp.severity, wp.category, loc, wp.description
);
}
println!();
println!("=== File Risk ===");
for detail in formatter.file_risk_details(assail) {
println!(" {}", detail);
}
println!();
println!("=== Matrix ===");
println!(
"Dependency edges: {} Taint rows: {}",
assail.dependency_graph.edges.len(),
assail.taint_matrix.rows.len()
);
for detail in formatter.taint_matrix_details(assail) {
println!(" {}", detail);
}
println!();
println!("=== Attacks ===");
for result in &report.attack_results {
let status = if result.skipped {
"skipped"
} else if result.success {
"passed"
} else {
"failed"
};
println!(
" {:?}: {} crashes={} duration={:.2}s",
result.axis,
status,
result.crashes.len(),
result.duration.as_secs_f64()
);
}
println!();
println!("=== Assessment ===");
for issue in &report.overall_assessment.critical_issues {
println!(" CRITICAL: {}", issue);
}
for rec in &report.overall_assessment.recommendations {
println!(" REC: {}", rec);
}
println!();
Ok(())
}