Harden HCM matcher: artifact verification, tier ladder consistency, dedup - #12
Merged
Conversation
Reorder the JSON schema `tier` enum to `blocked, community, reference, supported, certified` so it matches the `SupportTier` declaration order in `model.rs` (JSON Schema enums are sets, so this is a consistency fix, not a validation change). State the ladder authoritatively in the hardware docs: `model.rs` is the single source of truth, the schema mirrors it, and a CI test locks the two together. Note that the strategic-doc "OEM Reference Design" product-line label is unrelated to the `SupportTier::Reference` enum value (which is the second-lowest, virtual-evidence-only tier). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Move `strip_hex_prefix`/`id_matches` into `model.rs` as the single shared
implementation and have `diagnosis.rs` use it. `diagnosis::parse_hex`
previously used `trim_start_matches("0x")`, which strips repeated prefixes;
it now shares the single-strip semantics so `0x0x...` is treated
consistently everywhere. Add a unit test for the `0x0x10de` / `0X10DE`
edge cases.
Add `#[serde(deny_unknown_fields)]` to the untrusted HCM structs
(`HcmManifest`, `HardwareSelector`, `CapabilityRequirement`, `ArtifactPin`,
`CapabilityEvidence`) so a mistyped manifest field is rejected instead of
silently dropped (fail-closed). The attribute works on the internally
tagged `CapabilityRequirement` because the `type` tag is consumed by the
enum deserializer.
Reconcile the `#[serde(default)]` inconsistency on `DeviceInfo`: every
`Option` field already deserializes to `None` when absent, so drop the
redundant annotations and document why `DeviceInfo` intentionally stays
forward-compatible (no `deny_unknown_fields`) unlike the manifest structs.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Introduce an optional `ArtifactVerifier` hook so the matcher can verify the real bytes behind each pinned artifact instead of trusting the declared `sha256`. New entry points `evaluate_manifest_with_verifier` / `evaluate_manifest_at_with_verifier` take `Option<&dyn ArtifactVerifier>`; the existing `evaluate_manifest`/`evaluate_manifest_at` delegate with `None`, so today's callers (the CLI) are unchanged and the historical "trust declared hashes" behavior is preserved but now explicit in the API. When a verifier is supplied, every pinned artifact must resolve and its computed SHA-256 must match the pin, or `effective_tier` is driven to `Blocked`; unresolved-when-verifying is likewise fail-closed. `DirectoryArtifactVerifier` resolves artifacts from a base directory, hashes them with `sha2`, and wires `signing_key_id` into the contract via an optional trusted-key set (untrusted/unsigned = Blocked). Actual signature cryptography is a documented TODO stub that today checks key-id membership, not a real detached signature. Rewrite the `evaluate_device` "id matched but no bound driver" decision as an explicit two-step (collect id-matches, then check driver presence) instead of an `inspect`-driven side effect that relied on `any`'s lazy short-circuit, so a future iterator refactor cannot silently break it. Add tests: verified artifacts retain the declared tier; mismatched hash and unresolvable-when-required both Block; the default (no-verifier) path still trusts declared hashes; a schema drift guard locks the JSON schema `tier` enum to the `SupportTier` declaration order; and unknown fields in the untrusted manifest structs are rejected. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This was referenced Aug 1, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Hardening pass on
andromeda-hardware(HCM matcher + model). All changes are confined tocrates/andromeda-hardware/**,schemas/**, and the two hardware dev docs (plus generatedCargo.lock).1. Fail-closed artifact verification (security review #1)
evaluate_manifest/matcher previously trusted the declaredartifacts[].sha256and never usedArtifactPin.signing_key_id. Added an optionalArtifactVerifierhook:evaluate_manifest_with_verifier/evaluate_manifest_at_with_verifiertakeOption<&dyn ArtifactVerifier>. The existingevaluate_manifest/evaluate_manifest_atdelegate withNone, so the CLI and today's behavior are unchanged — but the trust boundary is now explicit in the API.effective_tieris driven toBlocked; unresolvable-when-verifying is likewise fail-closed.DirectoryArtifactVerifierresolves artifacts from a base directory, hashes withsha2, and wiressigning_key_idinto the contract via an optional trusted-key set (untrusted/unsigned =Blocked). Real signature cryptography is a documented TODO stub (checks key-id membership, not a detached signature).2. SupportTier single source of truth (documentation review #2/#3)
tierenum toblocked, community, reference, supported, certifiedto match themodel.rsdeclaration order.schema_tier_enum_matches_model_declaration_order) that reads the schema file and asserts its enum order equals theSupportTierdeclaration order, so future drift fails CI.SupportTier::Referencevalue.3. Dedup hex-ID helpers (code-quality review #2)
strip_hex_prefix/id_matcheswere duplicated inmatcher.rsanddiagnosis.rs, anddiagnosis::parse_hexusedtrim_start_matches("0x")(strips repeated prefixes). Extracted one shared helper inmodel.rs, made both callers use it with consistent single-strip semantics, and added a test for0x0x10de/0X10DE.4. evaluate_device refactor (code-quality review #4)
Rewrote the "id matched but no bound driver" decision from an
inspect-driven side effect (relying onany's lazy short-circuit) to an explicit two-step (collect id-matches, then check driver presence). Behavior identical.5. deny_unknown_fields on HCM (code-quality review #1)
Added
#[serde(deny_unknown_fields)]toHcmManifest,HardwareSelector,CapabilityRequirement(internally tagged — verified compatible),ArtifactPin, andCapabilityEvidence, so a mistyped manifest field is rejected instead of silently dropped. Added a test covering top-level, selector, requirement, and artifact structs (plus a valid-doc control).6. serde(default) reconciliation (code-quality review #3)
Removed the redundant
#[serde(default)]onDeviceInfoOptionfields (absentOptionalready deserializes toNone) and documented whyDeviceInfointentionally stays forward-compatible, unlike the untrusted manifest structs.Validation
cargo fmt --all --check— cleancargo clippy --workspace --all-targets --locked -- -D warnings— cleancargo test --workspace --locked— 120 passing (hardware 32 → 43)jsonschemaDraft 2020-12)Shape changes
andromeda-hardware:ArtifactVerifier,ArtifactVerdict,DirectoryArtifactVerifier,evaluate_manifest_with_verifier,evaluate_manifest_at_with_verifier. No existing signature changed; no CLI/JSON output shape change (the CLI still callsevaluate_manifest).sha2added as a dependency of theandromeda-hardwarecrate only.🤖 Generated with Claude Code