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

nordisk/heimdall

guests/static-scan/tests/common/guests.rs

// SPDX-License-Identifier: AGPL-3.0-only
//! The guest as Heimdall runs it: built for wasm32-unknown-unknown, turned
//! into a component against heimdall-wasm's WIT, signed like a Holger plugin
//! (`.wasm.p7s` + `.wasm.blake3`) and loaded by the real host (no WASI).
//! Heimdall is a defensive sandbox.

#![allow(dead_code)]

use std::path::{Path, PathBuf};
use std::process::Command;
use std::sync::OnceLock;

use guest_host::verify::sign::{dev, sign_module};
use heimdall_wasm::{Guest, Host, HostConfig};

/// Build the core module for wasm32 (its own target dir, `--locked`).
fn build_core() -> Vec<u8> {
    let crate_dir = Path::new(env!("CARGO_MANIFEST_DIR"));
    let target = Path::new(env!("CARGO_TARGET_TMPDIR")).join("guest-wasm");
    let st = Command::new(std::env::var("CARGO").unwrap_or_else(|_| "cargo".into()))
        .args([
            "build",
            "--release",
            "--locked",
            "--lib",
            "--target",
            "wasm32-unknown-unknown",
            "--manifest-path",
        ])
        .arg(crate_dir.join("Cargo.toml"))
        .arg("--target-dir")
        .arg(&target)
        .env_remove("RUSTFLAGS")
        .env_remove("CARGO_ENCODED_RUSTFLAGS")
        .status()
        .expect("cargo could not be started");
    assert!(st.success(), "building the guest for wasm32 failed");
    std::fs::read(target.join("wasm32-unknown-unknown/release/heimdall_guest_static_scan.wasm"))
        .unwrap()
}

pub fn core_module() -> &'static [u8] {
    static M: OnceLock<Vec<u8>> = OnceLock::new();
    M.get_or_init(build_core)
}

pub fn component() -> &'static [u8] {
    static C: OnceLock<Vec<u8>> = OnceLock::new();
    C.get_or_init(|| {
        wit_component::ComponentEncoder::default()
            .module(core_module())
            .unwrap()
            .validate(true)
            .encode()
            .unwrap()
    })
}

/// The names a core module imports, as `module::name`.
pub fn imports(core: &[u8]) -> Vec<String> {
    let mut out = Vec::new();
    for payload in wasmparser::Parser::new(0).parse_all(core) {
        if let wasmparser::Payload::ImportSection(r) = payload.unwrap() {
            for imp in r.into_imports() {
                let imp = imp.unwrap();
                out.push(format!("{}::{}", imp.module, imp.name));
            }
        }
    }
    out
}

/// A signing CA, a release signer and a host that trusts the CA.
pub struct Lab {
    pub dir: tempfile::TempDir,
    pub host: Host,
    signer: guest_host::verify::sign::Ed25519Signer,
}

impl Lab {
    pub fn new() -> Lab {
        let dir = tempfile::tempdir().unwrap();
        let (ca_key, ca_der) = dev::mint_ca("heimdall-guest-test-ca").unwrap();
        let root = dir.path().join("root.der");
        std::fs::write(&root, &ca_der).unwrap();
        let signer = dev::new_ed25519_signer(&ca_key, &ca_der, "heimdall-guest-release").unwrap();
        let host = Host::new(HostConfig::new([root])).unwrap();
        Lab { dir, host, signer }
    }

    /// Write `bytes` as `<name>.wasm`, signed and pinned; returns its path.
    pub fn signed(&self, name: &str, bytes: &[u8]) -> PathBuf {
        let module = self.dir.path().join(format!("{name}.wasm"));
        std::fs::write(&module, bytes).unwrap();
        std::fs::write(
            self.dir.path().join(format!("{name}.wasm.p7s")),
            sign_module(bytes, &self.signer).unwrap(),
        )
        .unwrap();
        std::fs::write(
            self.dir.path().join(format!("{name}.wasm.blake3")),
            format!("{}\n", blake3::hash(bytes).to_hex()),
        )
        .unwrap();
        module
    }
}

pub struct Loaded {
    pub lab: Lab,
    pub guest: Guest,
}

pub fn loaded() -> &'static Loaded {
    static L: OnceLock<Loaded> = OnceLock::new();
    L.get_or_init(|| {
        let lab = Lab::new();
        let p = lab.signed("static-scan", component());
        let guest = lab
            .host
            .load(&p)
            .unwrap_or_else(|e| panic!("the host refused the guest: {e}"));
        Loaded { lab, guest }
    })
}