@@ -720,4 +720,45 @@ mod tests {
720720
721721 assert ! ( failures. is_empty( ) , "\n {}" , failures. join( "\n " ) ) ;
722722 }
723+
724+ #[ test]
725+ fn new_normalizes_empty_api_key_to_none_on_trusted_origin ( ) {
726+ let guard = ICAPTCHA_ENV_LOCK . lock ( ) . unwrap ( ) ;
727+ let prev_key = std:: env:: var_os ( "GITLAWB_ICAPTCHA_API_KEY" ) ;
728+ let prev_op = std:: env:: var_os ( "GITLAWB_ICAPTCHA_URL" ) ;
729+
730+ // Operator origin configured and resolved as trusted (no advert), so
731+ // api_key reaches the env read and the emptiness filter is the only
732+ // thing standing between a blank var and an empty bearer token.
733+ std:: env:: set_var ( "GITLAWB_ICAPTCHA_URL" , "https://icap.mynode.example" ) ;
734+
735+ // "Operator set the var but left it blank" must stay keyless: a
736+ // Some("") would attach `Authorization: Bearer ` (empty token) to the
737+ // challenge and answer requests instead of omitting the header.
738+ std:: env:: set_var ( "GITLAWB_ICAPTCHA_API_KEY" , "" ) ;
739+ let empty = IcaptchaCfg :: new ( "did:key:zTEST" , None , None ) ;
740+
741+ // Contrast arm: a real key on the same trusted path must survive the
742+ // filter, so the filter can't regress to dropping every key.
743+ std:: env:: set_var ( "GITLAWB_ICAPTCHA_API_KEY" , "secret-bearer" ) ;
744+ let non_empty = IcaptchaCfg :: new ( "did:key:zTEST" , None , None ) ;
745+
746+ // Restore env and release the lock BEFORE asserting, so a failing
747+ // assertion can't poison the shared lock or cascade into a sibling.
748+ match prev_key {
749+ Some ( v) => std:: env:: set_var ( "GITLAWB_ICAPTCHA_API_KEY" , v) ,
750+ None => std:: env:: remove_var ( "GITLAWB_ICAPTCHA_API_KEY" ) ,
751+ }
752+ match prev_op {
753+ Some ( v) => std:: env:: set_var ( "GITLAWB_ICAPTCHA_URL" , v) ,
754+ None => std:: env:: remove_var ( "GITLAWB_ICAPTCHA_URL" ) ,
755+ }
756+ drop ( guard) ;
757+
758+ assert_eq ! (
759+ empty. api_key, None ,
760+ "empty GITLAWB_ICAPTCHA_API_KEY must normalize to None"
761+ ) ;
762+ assert_eq ! ( non_empty. api_key. as_deref( ) , Some ( "secret-bearer" ) ) ;
763+ }
723764}
0 commit comments