Skip to content

module.toml component hash field is parsed but never verified against the loaded .wasm bytes #64

Description

@lgahdl

Summary

module.toml's [module].component field is documented/intended to carry a content hash the runtime should validate against the loaded .wasm bytes before instantiation. It's parsed, but never read again anywhere in the codebase — a tampered, corrupted, or hand-edited-without-recomputing artifact loads and runs exactly as if it matched.

Affected code

  • crates/nexum-runtime/src/manifest/types.rsModuleSection (component: String)
  • crates/nexum-runtime/src/supervisor.rs — component load path (Component::from_file)

Finding

ModuleSection carries #[allow(dead_code)] // version + component parsed for future 0.3 hash-verification. — an explicit admission the field is currently inert.

A grep of the entire crate for any read of this field, and for sha256/Sha256 anywhere in the source, turns up zero hits outside the field declaration itself. The actual load path — Component::from_file(engine, &entry.path) — loads the .wasm bytes straight from disk and proceeds directly into enforce_capabilities() (WIT-import gating); there is no comparison against module.component anywhere before or after.

A malformed hash string (wrong length, non-hex, wrong scheme prefix) has no effect at all, since nothing ever inspects the field's contents.

There is currently only one content-loading backend (local filesystem path from engine.toml) — no remote/content-addressed backend exists yet to test unevenly.

Impact

A .wasm artifact that doesn't match its manifest's declared hash — a corrupted download, a tampered artifact, or a hand-edited manifest with a stale hash — loads and runs identically to a valid one. This covers both accidental drift (operator mistake) and a more adversarial scenario (a compromised artifact store or MITM'd download swapping the .wasm bytes).

Suggested fix

Compute sha256 of the loaded component bytes and compare against module.component before instantiation; reject on mismatch. (Already flagged as planned for 0.3 per the existing code comment — this issue is to make sure it's tracked as real, current-state exposure rather than staying implicit in a dead_code annotation.)

Found via

Internal red-team exercise (operator mistake persona).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions