signalscreen-checker 0.2.1

Windows code-signing hygiene checker. Reads the Authenticode signature in a PE file and grades it A-F. Pure Rust, no Windows dependency.
Documentation
//! The report: canonical JSON (the contract every surface consumes) + human render.
use crate::checks::CheckResult;
use serde::Serialize;

#[derive(Debug, Serialize)]
pub struct Report {
    pub grade: char,
    pub score: u32,
    pub file: FileInfo,
    pub signature: SigSummary,
    pub certificate: Option<CertSummary>,
    pub checks: Vec<CheckResult>,
}

#[derive(Debug, Serialize)]
pub struct FileInfo {
    pub name: String,
    pub sha256: String,
}

#[derive(Debug, Serialize)]
pub struct SigSummary {
    pub present: bool,
    pub valid: bool,
    pub digest_algo: String,
    pub timestamp_present: bool,
    /// Present only when the token parsed. `timestamp_present`
    /// without this means one is there and could not be read.
    #[serde(skip_serializing_if = "Option::is_none")]
    pub timestamp: Option<TimestampSummary>,
}

#[derive(Debug, Serialize)]
pub struct TimestampSummary {
    pub signed_at_unix: i64,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub authority: Option<String>,
    /// `rfc3161` or `countersignature`.
    pub kind: &'static str,
}

#[derive(Debug, Serialize)]
pub struct CertSummary {
    pub subject_o: Option<String>,
    pub issuer: String,
    pub not_after_unix: i64,
    pub ev_hint: bool,
    pub self_signed: bool,
}

impl Report {
    pub fn to_json(&self) -> String {
        serde_json::to_string_pretty(self).unwrap()
    }

    pub fn to_human(&self) -> String {
        let mut s = format!(
            "Signing hygiene: {} ({}/100)\n  file: {}\n",
            self.grade, self.score, self.file.name
        );
        for c in &self.checks {
            s.push_str(&format!("  [{:?}] {}: {}\n", c.status, c.id, c.detail));
        }
        s
    }
}