hara-native 0.1.13

HAL-free native host runtime and package launcher for Hara
Documentation
#![cfg(not(target_arch = "wasm32"))]

use hara_wasm::package_hta_loader::load_hta_require_package;
use hara_wasm::package_manifest::{PackageManifest, PackageRuntimeRequirements};
use sha2::{Digest, Sha256};
use std::collections::BTreeSet;
use std::fs;
use std::path::PathBuf;

const EXTENSION: &str = r#"
{:namespace "fixture.package"
 :identity "hara:example/provider"
 :version "1.0.0"
 :provider :wasm
 :module "provider.wasm"
 :abi :hta.v1
 :exports {"eval" {:args [:value] :returns :value :async true}}
 :capabilities []}
"#;

fn requirements() -> PackageRuntimeRequirements {
    PackageRuntimeRequirements {
        supported_targets: BTreeSet::from(["wasm32-wasi-preview1".to_owned()]),
        supported_abis: BTreeSet::from(["hta.v1".to_owned()]),
        ..PackageRuntimeRequirements::default()
    }
}

fn manifest(bytes: &[u8], artifact_type: &str) -> PackageManifest {
    let digest = format!("sha256:{:x}", Sha256::digest(bytes));
    PackageManifest::parse(&format!(
        r#"{{:harp/format "0.0.0-alpha"
 :package {{:identity "hara:example/provider"
           :version "1.0.0"
           :provenance {{:repository "https://github.com/example/provider"
                         :commit "0123456789abcdef0123456789abcdef01234567"}}}}
 :files {{"artifacts/provider.wasm" {{:sha256 "{digest}" :size {}}}}}
 :wasm-imports {{:provider {{:variant/artifact
   {{:artifact/type :{artifact_type}
    :artifact/path "artifacts/provider.wasm"
    :artifact/sha256 "{digest}"
    :artifact/target "wasm32-wasi-preview1"
    :artifact/abi "hta.v1"
    :artifact/entry-point "hta_start"}}
   :variant/required-capabilities #{{}}
   :variant/host-calls #{{}}
   :variant/exports #{{"eval"}}}}}}}}"#,
        bytes.len()
    ))
    .unwrap()
}

fn root(name: &str, bytes: &[u8]) -> PathBuf {
    let root = std::env::temp_dir().join(format!(
        "hara-package-hta-loader-{name}-{}",
        std::process::id()
    ));
    if root.exists() {
        fs::remove_dir_all(&root).unwrap();
    }
    fs::create_dir_all(root.join("artifacts")).unwrap();
    fs::write(root.join("artifacts/provider.wasm"), bytes).unwrap();
    root
}

#[test]
fn rejects_tampered_hta_artifact_before_wasmtime() {
    let manifest = manifest(b"trusted", "hta");
    let root = root("tampered", b"changed");
    let result = load_hta_require_package(
        &manifest,
        &root,
        "provider",
        &requirements(),
        EXTENSION,
        None,
    );
    let error = match result {
        Ok(_) => panic!("tampered artifact was loaded"),
        Err(error) => error,
    };
    assert!(error.starts_with("package/digest-mismatch:"));
    fs::remove_dir_all(root).unwrap();
}

#[test]
fn rejects_cross_route_wasm_artifacts() {
    let bytes = b"not wasm";
    let manifest = manifest(bytes, "wasm");
    let root = root("route", bytes);
    let result = load_hta_require_package(
        &manifest,
        &root,
        "provider",
        &requirements(),
        EXTENSION,
        None,
    );
    let error = match result {
        Ok(_) => panic!("cross-route artifact was loaded"),
        Err(error) => error,
    };
    assert!(error.starts_with("package/artifact-type-mismatch:"));
    fs::remove_dir_all(root).unwrap();
}