update-bundle/tests/w13_trusted_time.rs
//! **W13's contract with everything already built against this crate.**
//!
//! The seam W13 needed was already here — `verify_member_map` has taken
//! `now: DateTime<Utc>` since the crate was written, and `verify_control_plane_at`
//! and `verify_detached_by_trusted_signer_at` were already the `_at` pattern.
//! W13 fed that seam. It was allowed to ADD, and forbidden to CHANGE: a consumer
//! that compiled against this crate before W13 must compile against it after.
//!
//! This file is that guard, and it is a COMPILE-TIME one where it can be: each
//! published function is coerced to a function pointer of its exact pre-W13
//! signature. A parameter added, removed or retyped stops this file compiling,
//! which is louder than an assertion nobody reads.
#![allow(clippy::type_complexity)] // the signatures are spelled out on purpose: the test pins them
use chrono::{DateTime, Utc};
use std::path::Path;
use holger_update_bundle::policy::TrustPolicy;
use holger_update_bundle::rollback::AcceptedState;
use holger_update_bundle::time::{HostRtc, TrustedTime, HOST_RTC_NAME};
use holger_update_bundle::verify::{TargetSpec, VerifyReport};
use holger_update_bundle::{BundleError, MemberMap};
/// The published signatures still exist, unchanged, and delegate to the named
/// host RTC rather than to a bare `Utc::now()` nobody can name.
///
/// The coercions below are the test. If `verify_member_map` ever grew a
/// `&dyn TrustedTime` parameter instead of gaining a sibling, this file would not
/// compile — which is the point: `holger-traits` is published and so is this
/// crate's shape, and W13 may not break a caller to gain a feature.
#[test]
fn the_published_signature_still_exists_and_delegates() {
// ── The three pre-W13 public shapes, pinned as function pointers. ────────
let _verify_member_map: fn(
&MemberMap,
&TrustPolicy,
&TargetSpec,
Option<&AcceptedState>,
DateTime<Utc>,
) -> Result<VerifyReport, BundleError> = holger_update_bundle::verify_member_map;
let _verify_bundle_file: fn(
&Path,
&TrustPolicy,
&TargetSpec,
Option<&AcceptedState>,
) -> Result<VerifyReport, BundleError> = holger_update_bundle::verify_bundle_file;
let _extract_bundle_file: fn(
&Path,
&TrustPolicy,
&TargetSpec,
&Path,
) -> Result<VerifyReport, BundleError> = holger_update_bundle::extract_bundle_file;
// The `_at` pattern that predates W13 is untouched too — W13 followed it
// rather than inventing a second convention.
let _verify_control_plane: fn(
&MemberMap,
&TrustPolicy,
) -> Result<
holger_update_bundle::verify::ControlVerdict,
BundleError,
> = holger_update_bundle::verify::verify_control_plane;
// ── And the additions, which are siblings and not replacements. ──────────
let _verify_member_map_with_time: fn(
&MemberMap,
&TrustPolicy,
&TargetSpec,
Option<&AcceptedState>,
&dyn TrustedTime,
) -> Result<VerifyReport, BundleError> =
holger_update_bundle::verify::verify_member_map_with_time;
let _verify_bundle_file_at: fn(
&Path,
&TrustPolicy,
&TargetSpec,
Option<&AcceptedState>,
&dyn TrustedTime,
) -> Result<VerifyReport, BundleError> = holger_update_bundle::verify_bundle_file_at;
let _extract_bundle_file_at: fn(
&Path,
&TrustPolicy,
&TargetSpec,
&Path,
&dyn TrustedTime,
) -> Result<VerifyReport, BundleError> = holger_update_bundle::extract_bundle_file_at;
// ── The delegation is to a NAMED source, and the name is recorded. ───────
//
// A delegation to `Utc::now()` would leave the verdict unable to say what
// clock it rested on, which is the defect W13 exists to close. The source
// code is the evidence: `verify_bundle_file` hands `&HostRtc` on, and
// `HostRtc` declares itself untrusted.
let api = include_str!("../src/api.rs");
assert!(
api.contains("verify_bundle_file_at(path, policy, target, local_state, &HostRtc)"),
"verify_bundle_file must delegate to the `_at` form with the host RTC NAMED"
);
assert!(
api.contains("extract_bundle_file_at(path, policy, target, dest, &HostRtc)"),
"extract_bundle_file must delegate to the `_at` form with the host RTC NAMED"
);
assert!(
!api.contains("Utc::now()"),
"no api.rs door may still reach for the wall clock directly — that is the \
unnamed source W13 replaced"
);
assert_eq!(HostRtc.source_name(), HOST_RTC_NAME);
assert!(
!HostRtc.is_trusted(),
"the delegation target must declare itself UNTRUSTED, or the recorded name \
is a reassurance instead of a warning"
);
}
/// A caller that still hands a bare instant gets the honest answer in its own
/// verdict: the instant came from somewhere this crate cannot name.
///
/// This is what keeps the old signature from being a quiet way to launder an
/// unverified clock into a report that looks the same as a verified one.
#[test]
fn a_bare_instant_is_recorded_as_unnamed_not_as_trusted() {
let verify = include_str!("../src/verify.rs");
assert!(
verify.contains("time source: unnamed"),
"verify_member_map's bare-instant path must record `time source: unnamed`"
);
assert!(
verify.contains("(UNTRUSTED — this box has no verified time"),
"the untrusted host RTC must be recorded as UNTRUSTED, in those words"
);
assert!(
verify.contains("format!(\"time source: {} (verified)\", t.source_name())"),
"only a source that says it is trusted may be recorded as verified"
);
}