From e2bd6330a17072dafb08b810a94289ceedf075ae Mon Sep 17 00:00:00 2001 From: archipelago Date: Mon, 31 Aug 2026 17:08:37 -0400 Subject: [PATCH] test(app-catalog): pin the signed-catalog body gate --- core/archipelago/src/container/app_catalog.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/core/archipelago/src/container/app_catalog.rs b/core/archipelago/src/container/app_catalog.rs index fd960871..a2b45630 100644 --- a/core/archipelago/src/container/app_catalog.rs +++ b/core/archipelago/src/container/app_catalog.rs @@ -661,4 +661,23 @@ mod tests { ] ); } + + // The signed-catalog body served to the browser must be the anchored, + // release-root-verified bytes — and nothing else. Unsigned caches (the + // migration-window form) and self-consistent-but-unanchored signatures + // must both be refused so a tampered mirror can never become an install + // button (same posture as the OTA manifest supply-chain gate). + #[tokio::test] + async fn verified_catalog_body_rejects_unsigned_cache() { + let dir = tempfile::tempdir().unwrap(); + write_cache(dir.path(), r#"{"schema":1,"apps":{"demo":{"version":"1"}}}"#).unwrap(); + let err = verified_catalog_body(dir.path()).await.unwrap_err(); + assert!(err.to_string().contains("unsigned")); + } + + #[tokio::test] + async fn verified_catalog_body_rejects_missing_cache() { + let dir = tempfile::tempdir().unwrap(); + assert!(verified_catalog_body(dir.path()).await.is_err()); + } }