\n");
for (idx, line) in text.lines().enumerate() {
let line_num = idx + 1;
let location_key = format!("md:{}:0", line_num);
let mut line_html = html_escape(line);
if let Some(findings) = location_findings.get(&location_key) {
for finding in findings {
let class = classification_class(&finding.classification);
line_html = format!(
"{} {}",
class, line_html, classification_label(&finding.classification)
);
}
}
out.push_str(&format!(
"{line_num:>4}{}\n",
line_html
));
}
out.push_str("\n");
out
}
/// Build study HTML for non-Markdown formats (PDF, EPUB, DOCX).
///
/// Since we can't reliably reconstruct the original rendering, we show
/// the findings in a structured list with their payload previews.
///
/// The `raw` bytes and `format` are accepted for symmetry with
/// [`build_markdown_study`] and for future per-format rendering hooks,
/// but are not currently consumed by this implementation.
#[allow(unused_variables)]
fn build_generic_study(
raw: &[u8],
format: DocumentFormat,
location_findings: &std::collections::HashMapNo findings. Document passed all checks.
\n"); } out } fn classification_class(c: &ThreatClassification) -> &'static str { match c { ThreatClassification::Benign => "benign", ThreatClassification::Suspicious => "suspicious", ThreatClassification::EducationalContent => "educational", ThreatClassification::Malicious(_) => "malicious", } } fn classification_label(c: &ThreatClassification) -> &'static str { match c { ThreatClassification::Benign => "benign", ThreatClassification::Suspicious => "suspicious", ThreatClassification::EducationalContent => "educational", ThreatClassification::Malicious(_) => "malicious", } } fn html_escape(s: &str) -> String { s.replace('&', "&") .replace('<', "<") .replace('>', ">") .replace('"', """) } #[cfg(test)] mod tests { use super::*; #[test] fn html_escape_basic() { assert_eq!(html_escape(""), "<script>alert('xss')</script>"); } #[test] fn build_study_html_has_structure() { use crate::core::types::*; let report = ScanReport { source_sha256: "abc".to_string(), format: DocumentFormat::Markdown, scanned_at: "x".to_string(), findings: vec![Finding { classification: ThreatClassification::Malicious(MaliciousType::ActiveJavaScriptInjection), location: Location::MarkdownLine { line: 3, col: 0 }, vector_type: None, payload_preview: "eval(1)".to_string(), context_notes: "found evil".to_string(), recommendation: Recommendation::QuarantineAndCleanse, }], text_nodes_scanned: 5, vectors_scanned: 1, }; let html = build_study_html(b"hello\nworld\nevil", &report, "abc123", std::path::Path::new("test.md")); assert!(html.contains("")); assert!(html.contains(" 3")); assert!(html.contains("CorbelPurge Study Mode")); assert!(html.contains("Malicious:")); } }