Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/gunnar

installtest/src/medium.rs

//! **What is actually INSIDE a composed medium** — the ESP's boot PEs, who
//! signed them, and every name in the initramfs.
//!
//! # Why a reader and not a build assertion
//!
//! The 2026-09-05 fixture-key leak was not found by a test. The appliance ISO
//! built green, booted, and served — carrying `data/control.git/secrets.kek`,
//! a test-minted key, in its initramfs. Every check in the chain looked at
//! what the build INTENDED. The only thing that found it was opening the
//! artifact and listing what came out, and that was a shell pipeline in a
//! scratch directory, which is debt rather than a guard.
//!
//! So this module opens the medium. It is the second, independent point in the
//! chain: `korp_installer::forge` refuses a seeded staging TREE, and this
//! reads the produced MEDIUM. A defect that defeats one has to defeat the
//! other separately.
//!
//! # Reused, never twinned
//!
//! Every piece already existed and none of it is re-implemented here:
//! [`crate::firmware::iso_root_file`] finds the ESP in the ISO-9660 volume,
//! `kvmproof::read_esp_image_file` reads a file out of the FAT,
//! `korp_installer::forge::initramfs_entries` picks the framing (an
//! appliance's `\initrd` is a raw zstd frame, an installer's a plain cpio),
//! and `korp_installer::secureboot::signer_names` says WHO signed a PE rather
//! than merely that somebody did.

use std::path::Path;

use korp_installer::kvmproof;

/// The ESP path of the first-stage bootloader an enforcing firmware verifies.
pub const ESP_BOOTX64: &str = "EFI/BOOT/BOOTX64.EFI";
/// The ESP path of the image the first stage chainloads.
pub const ESP_VMLINUZ: &str = "vmlinuz";
/// The ESP path of the initramfs.
pub const ESP_INITRD: &str = "initrd";

/// What one PE on the ESP is, and who vouched for it.
#[derive(Debug, Clone)]
pub struct PeReport {
    /// The ESP path it was read from.
    pub path: String,
    /// Does it carry an attribute certificate table at all?
    pub signed: bool,
    /// Every certificate's `subject` — empty when unsigned, and ALSO empty
    /// when a table is present but unparseable, which is why `signed` is kept
    /// beside it rather than derived from this.
    pub signers: Vec<korp_installer::secureboot::SignerName>,
}

impl PeReport {
    /// A one-line summary that names the signer rather than counting bytes.
    pub fn line(&self) -> String {
        match (self.signed, self.signers.first()) {
            (false, _) => format!("{:22} UNSIGNED", self.path),
            (true, None) => format!("{:22} SIGNED, but no name could be read off it", self.path),
            (true, Some(s)) => format!(
                "{:22} SIGNED by {}{}",
                self.path,
                s.subject,
                if s.is_self_signed() {
                    " (self-signed)"
                } else {
                    ""
                }
            ),
        }
    }
}

/// Everything this reader can say about one composed medium.
#[derive(Debug, Clone)]
pub struct MediumCensus {
    /// The medium's file name.
    pub name: String,
    /// Its size on disk.
    pub bytes: u64,
    /// The boot PEs found on the ESP, in the order asked for.
    pub pes: Vec<PeReport>,
    /// Every name in the initramfs, in stream order.
    pub initramfs: Vec<String>,
}

impl MediumCensus {
    /// The initramfs names under `data/` — the appliance's persistent state,
    /// which a RELEASE medium ships EMPTY. `.keep` and `.gitignore` are the
    /// two files that spell "this directory ships empty" and are not content;
    /// the same rule, and the same two exceptions, as
    /// `korp_installer::forge::seeded_data_files`, which refuses them at the
    /// staging tree.
    pub fn seeded_data(&self) -> Vec<&str> {
        self.initramfs
            .iter()
            .map(String::as_str)
            .filter(|n| {
                n.starts_with("data/")
                    && *n != "data/.keep"
                    && *n != "data/.gitignore"
                    && !n.ends_with('/')
            })
            .collect()
    }

    /// Does the medium say, from the INSIDE, that it is a fixture?
    pub fn marked_fixture(&self) -> bool {
        self.initramfs
            .iter()
            .any(|n| n == korp_installer::forge::FIXTURE_SEED_MARK)
    }
}

/// **Open a composed ISO and report what is in it.**
///
/// `Err` carries the reason as a sentence. Nothing here degrades to a partial
/// answer: a medium whose ESP cannot be found, or whose initramfs no framing
/// parses, is an error rather than a census with empty fields — an empty
/// field is shaped exactly like a clean medium.
pub fn census(iso: &Path) -> Result<MediumCensus, String> {
    let bytes = std::fs::metadata(iso)
        .map_err(|e| format!("{}: {e}", iso.display()))?
        .len();
    let (lba, len) = crate::firmware::iso_root_file(iso, crate::firmware::ESP_IMAGE_NAME)
        .ok_or_else(|| {
            format!(
                "{}: no root-level `{}` in the ISO-9660 volume, so there is no ESP to read",
                iso.display(),
                crate::firmware::ESP_IMAGE_NAME
            )
        })?;
    let esp = crate::firmware::read_range(
        iso,
        lba * crate::firmware::ISO_BLOCK,
        usize::try_from(len).map_err(|_| format!("{}: absurd ESP length {len}", iso.display()))?,
    )
    .ok_or_else(|| format!("{}: the ESP extent is not readable", iso.display()))?;

    let mut pes = Vec::new();
    for path in [ESP_BOOTX64, ESP_VMLINUZ] {
        let Some(pe) = kvmproof::read_esp_image_file(&esp, path) else {
            continue;
        };
        pes.push(PeReport {
            path: path.to_string(),
            signed: korp_installer::secureboot::is_signed(&pe),
            signers: korp_installer::secureboot::signer_names(&pe),
        });
    }

    let raw = kvmproof::read_esp_image_file(&esp, ESP_INITRD)
        .ok_or_else(|| format!("{}: no `{ESP_INITRD}` on the ESP", iso.display()))?;
    let initramfs = korp_installer::forge::initramfs_entries(&raw)
        .map_err(|e| format!("{}: {e:#}", iso.display()))?
        .into_iter()
        .map(|(n, _, _)| n)
        .collect();

    Ok(MediumCensus {
        name: iso
            .file_name()
            .unwrap_or_default()
            .to_string_lossy()
            .into_owned(),
        bytes,
        pes,
        initramfs,
    })
}