Skip to content

id(world-manifest): an empty module_hash passes validation — parse accepts "" and plan/apply install it, contradicting validate's own '1 to 64 hex digits' rule #1517

Closed

Description

@dezren39

Problem

WorldManifest::validate accepts an empty module_hash, even though its own error message says the hash "must be 1 to 64 hex digits". A manifest with module_hash = "" parses cleanly, plan() then emits an InstallModule step carrying the empty hash, and the pure apply() installs it into the state — the bad value is only ever caught (if at all) downstream at the server, far from the typo that caused it.

Evidence (verified this run, main 279c973)

pkgs/id/src/world_manifest.rs:186-192:

if let Some(hash) = &self.module_hash {
    let valid = hash.len() <= MAX_NAME_BYTES && hash.bytes().all(|b| b.is_ascii_hexdigit());
    ensure!(valid, "module hash must be 1 to {MAX_NAME_BYTES} hex digits");
}

"".len() <= 64 is true and all() on an empty iterator is true, so the lower bound in the message is never enforced. Every sibling validator in pkgs/id/src/world_caps.rs does enforce it — e.g. :248 !spec.is_empty() && spec.len() <= MAX_NAME_BYTES, :274, :278 — the manifest hash check is the outlier.

Probe (crate on the laptop compiling the verbatim world_manifest.rs sha256 234f1dfc… + world_caps.rs sha256 679a2c25…, identical sandbox↔laptop; log hidden_files/probe-2026-10-27-laneB-manifest/out.log):

  • parse("module_hash = \"\"") → OK, module_hash=Some("")
  • control parse("module_hash = \"not-hex!\"") → Err("module hash must be 1 to 64 hex digits")
  • control parse("module_hash = \"abc123\"") → OK
  • plan() for the empty-hash manifest against a default state → [InstallModule { hash: Some(""), source: None, seed: 0 }], and apply() sets state.module_hash = Some("")

Why it matters

world.toml is the declarative admin interface for a world (id world apply / diff). Validation at parse() is the one place every bad manifest is supposed to die with a precise, field-naming error. An empty hash slipping through turns a one-character typo into a confusing downstream install failure (or a state whose module hash is the empty string), and the error text actively misleads: it claims a 1-digit minimum that the code does not check.

Acceptance criteria

  • parse("module_hash = \"\"") fails with the existing "module hash must be 1 to {MAX_NAME_BYTES} hex digits" error
  • Non-empty hex hashes (1..=64 digits) still parse; non-hex and >64-digit hashes still fail
  • A regression test joins validation_rejects_unknown_names_and_bad_hashes in world_manifest.rs covering the empty hash
  • cargo test for the world_manifest module passes; no other validation behaviour changes

Activity

  1. added
    kind/bugSomething isn't working (prefixed form of the classic bug label)
    source/creatorFiled by the new-issue-creator job
    priority/lowNice to have; do when convenient
    app/idPackage/app: id (pkgs/ or apps/ in this repo)
    on Oct 27, 2026
  2. developing-today commented on Oct 27, 2026

    @developing-today
    Contributor
  3. added
    work/self-assignedAn agent grabbed this item directly, outside the queue
    effort/xsTrivial: expected under 30 minutes of work
    on Oct 27, 2026
  4. dezren39 commented on Oct 27, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · picked up

  5. added a commit that references this issue on Oct 27, 2026
  6. dezren39 commented on Oct 27, 2026

    @dezren39
    MemberAuthor

    🤖 Agent note · done

    • Agent: new-issue-creator job (lane B, new-issue-creator-code-b)
    • Session / ID: new-issue-creator-code-b run, 2026-10-27 15:00 CDT
    • Found via: filed this run
    • Task: Self-solve complete. PR fix(id): reject an empty module_hash in world manifests (#1517) #1527 merged (merge commit 357feb2; fix commit 8b5c4bc): WorldManifest::validate now rejects an empty module_hash with the existing "1 to 64 hex digits" message, plus a regression assertion in validation_rejects_unknown_names_and_bad_hashes.
    • Expectation: met — verified on the laptop: probe crate with the patched verbatim file flips CASE1 to Err with controls unchanged, and the real crate's cargo test --lib world_manifest passes 11/11; rustfmt clean. PR CI checks were still pending (not started) at merge time; merged under the standing directive on the strength of the local verification.
    • Links: PR fix(id): reject an empty module_hash in world manifests (#1517) #1527
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

app/idPackage/app: id (pkgs/ or apps/ in this repo)effort/xsTrivial: expected under 30 minutes of workkind/bugSomething isn't working (prefixed form of the classic bug label)priority/lowNice to have; do when convenientsource/creatorFiled by the new-issue-creator jobstatus:done

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions