diff --git a/server/src/bridge/mod.rs b/server/src/bridge/mod.rs index 6a17eabd9..e9d276944 100644 --- a/server/src/bridge/mod.rs +++ b/server/src/bridge/mod.rs @@ -1316,5 +1316,51 @@ mod inbound_tests { decode_inbound(&frame).is_err(), "atlas+star_map union frame must be rejected" ); + + // T-1131 (PR #184 review): the FIFTH shape's discriminator (`browse`) + // must participate in the same union rejection — a well-formed + // BrowseRequest smuggling another shape's discriminator alongside it + // is rejected, not routed to whichever probe wins. + #[derive(serde::Serialize)] + struct BrowseAndStar { + browse: bool, + kind: crate::atlas::browse_proxy::BrowseEntityKind, + query: crate::atlas::browse_proxy::BrowseQuery, + star_map: bool, + } + let frame = rmp_serde::to_vec_named(&BrowseAndStar { + browse: true, + kind: crate::atlas::browse_proxy::BrowseEntityKind::StarSystem, + query: crate::atlas::browse_proxy::BrowseQuery::Index { + filter_system_id: None, + }, + star_map: true, + }) + .unwrap(); + assert!( + decode_inbound(&frame).is_err(), + "browse+star_map union frame must be rejected" + ); + + #[derive(serde::Serialize)] + struct BrowseAndCity { + browse: bool, + kind: crate::atlas::browse_proxy::BrowseEntityKind, + query: crate::atlas::browse_proxy::BrowseQuery, + city_names: bool, + body_id: String, + } + let frame = rmp_serde::to_vec_named(&BrowseAndCity { + browse: true, + kind: crate::atlas::browse_proxy::BrowseEntityKind::Body, + query: crate::atlas::browse_proxy::BrowseQuery::Detail { id: "GJ1c".into() }, + city_names: true, + body_id: "GJ1c".into(), + }) + .unwrap(); + assert!( + decode_inbound(&frame).is_err(), + "browse+city_names union frame must be rejected" + ); } }