Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -54,3 +54,7 @@ plugins/plugin-schema-types.js
plugins/host-modules.d.ts
output-hyperagent-*/
scripts/bash-bundle/_tmp_bundle.js

# Generated NAPI bindings (emitted by `napi build`)
src/code-validator/guest/host/index.d.ts
src/code-validator/guest/host/index.js
4 changes: 4 additions & 0 deletions .prettierignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
# Generated NAPI binding type declarations (emitted by `napi build`).
# napi-derive >= 3 writes JSDoc into this file, which is not Prettier-formatted.
# The canonical, hand-written types live in src/code-validator/guest/index.d.ts.
src/code-validator/guest/host/index.d.ts
163 changes: 130 additions & 33 deletions src/code-validator/guest/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions src/code-validator/guest/host/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ crate-type = ["cdylib", "rlib"]

[dependencies]
hyperlight-host = { workspace = true }
napi = { version = "2", features = ["async", "napi6"] }
napi-derive = "2"
napi = { version = "3", features = ["async", "napi6"] }
napi-derive = "3"
sha2 = "0.11"
hex = "0.4"
base64 = "0.22"
Expand Down
29 changes: 26 additions & 3 deletions src/code-validator/guest/host/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,17 +166,40 @@ fn bundle_runtime() {
let runtime_resource = build_runtime();
let runtime_bytes = fs::read(&runtime_resource).expect("Failed to read runtime binary");

// Compute SHA256 hash
assert!(
!runtime_bytes.is_empty(),
"analysis runtime binary is empty: {runtime_resource:?}"
);

let out_dir = env::var_os("OUT_DIR").unwrap();

// Snapshot the runtime binary into this crate's OUT_DIR and embed THAT copy.
//
// The hash below is computed from `runtime_bytes`, while the embedded bytes
// come from `include_bytes!` evaluated when this crate is compiled — two
// reads at different times. The build path of `runtime_resource` lives in a
// shared target directory that other cargo invocations (clippy, the napi
// typedef pass, repeated `just build`/`just test` runs) can rebuild, and the
// guest binary is not bit-for-bit reproducible. If that file changed between
// hashing and the `include_bytes!` compile, the embedded bytes would no
// longer match the stored hash and the runtime integrity check would fail.
//
// Writing a private snapshot into OUT_DIR — which nothing else ever touches —
// and pointing `include_bytes!` at it guarantees the embedded bytes are
// exactly the bytes we hashed, as a single atomic pair.
let embedded_path = Path::new(&out_dir).join("analysis-runtime.bin");
fs::write(&embedded_path, &runtime_bytes).expect("Failed to write runtime snapshot");

// Compute SHA256 hash of the same in-memory buffer we just embedded.
use sha2::{Digest, Sha256};
let mut hasher = Sha256::new();
hasher.update(&runtime_bytes);
let hash = hasher.finalize();
let hash_hex = hex::encode(hash);

let out_dir = env::var_os("OUT_DIR").unwrap();
let dest_path = Path::new(&out_dir).join("host_resource.rs");
let contents = format!(
r#"pub(crate) static ANALYSIS_RUNTIME: &[u8] = include_bytes!({runtime_resource:?});
r#"pub(crate) static ANALYSIS_RUNTIME: &[u8] = include_bytes!({embedded_path:?});
pub(crate) const ANALYSIS_RUNTIME_SHA256: &str = "{hash_hex}";"#
);

Expand Down
Empty file.
Loading