@@ -872,6 +872,10 @@ fn endpoint_attributes_cover(loaded: &NetworkEndpoint, proposed: &NetworkEndpoin
872872 loaded. request_body_credential_rewrite ,
873873 proposed. request_body_credential_rewrite ,
874874 )
875+ && flag_covers (
876+ loaded. allow_uninspected_credentials ,
877+ proposed. allow_uninspected_credentials ,
878+ )
875879 // Fields the merge neither widens nor retains: it drops them entirely.
876880 // An unset proposal value asks for nothing and is satisfied by whatever
877881 // is loaded; a set value that differs was dropped, so the proposal is
@@ -5070,6 +5074,74 @@ mod tests {
50705074 ) ) ;
50715075 }
50725076
5077+ /// An endpoint flag applies to every binary on the rule, so an update that
5078+ /// turns one on must declare the rule's whole binary scope.
5079+ #[ test]
5080+ fn enabling_an_endpoint_flag_requires_the_whole_binary_scope ( ) {
5081+ let base = endpoint ( "api.example.com" , 443 ) ;
5082+ for flag in [
5083+ "allow_encoded_slash" ,
5084+ "websocket_credential_rewrite" ,
5085+ "request_body_credential_rewrite" ,
5086+ "allow_uninspected_credentials" ,
5087+ ] {
5088+ let mut widened = base. clone ( ) ;
5089+ match flag {
5090+ "allow_encoded_slash" => widened. allow_encoded_slash = true ,
5091+ "websocket_credential_rewrite" => widened. websocket_credential_rewrite = true ,
5092+ "request_body_credential_rewrite" => widened. request_body_credential_rewrite = true ,
5093+ _ => widened. allow_uninspected_credentials = true ,
5094+ }
5095+ let existing = rule_with_authorizations (
5096+ "realtime" ,
5097+ vec ! [ base. clone( ) ] ,
5098+ & [ "/usr/bin/tool-a" , "/usr/bin/tool-b" ] ,
5099+ ) ;
5100+ let merge = |binaries : & [ & str ] | {
5101+ merge_policy (
5102+ policy_with_rule ( "realtime" , existing. clone ( ) ) ,
5103+ & [ PolicyMergeOp :: AddRule {
5104+ rule_name : "realtime" . to_string ( ) ,
5105+ rule : rule_with_authorizations ( "realtime" , vec ! [ widened. clone( ) ] , binaries) ,
5106+ } ] ,
5107+ )
5108+ } ;
5109+
5110+ assert ! (
5111+ matches!(
5112+ merge( & [ "/usr/bin/tool-a" ] ) ,
5113+ Err ( PolicyMergeError :: ExistingBinariesWouldInheritAuthorization {
5114+ undeclared_binaries,
5115+ ..
5116+ } ) if undeclared_binaries == [ "/usr/bin/tool-b" ]
5117+ ) ,
5118+ "{flag}: tool-b would inherit the flag undeclared"
5119+ ) ;
5120+ assert ! (
5121+ merge( & [ "/usr/bin/tool-a" , "/usr/bin/tool-b" ] ) . is_ok( ) ,
5122+ "{flag}: naming every binary must succeed"
5123+ ) ;
5124+ }
5125+ }
5126+
5127+ #[ test]
5128+ fn coverage_requires_a_proposed_uninspected_credentials_exception ( ) {
5129+ let loaded = policy_with_rule (
5130+ "realtime" ,
5131+ rule_with_authorizations (
5132+ "realtime" ,
5133+ vec ! [ endpoint( "api.example.com" , 443 ) ] ,
5134+ & [ "/usr/bin/client" ] ,
5135+ ) ,
5136+ ) ;
5137+ let mut proposed_endpoint = endpoint ( "api.example.com" , 443 ) ;
5138+ proposed_endpoint. allow_uninspected_credentials = true ;
5139+ let proposed =
5140+ rule_with_authorizations ( "realtime" , vec ! [ proposed_endpoint] , & [ "/usr/bin/client" ] ) ;
5141+
5142+ assert ! ( !policy_covers_rule( & loaded, & proposed) ) ;
5143+ }
5144+
50735145 fn endpoint_with_ports ( host : & str , ports : & [ u32 ] ) -> NetworkEndpoint {
50745146 NetworkEndpoint {
50755147 host : host. to_string ( ) ,
0 commit comments