@@ -806,6 +806,106 @@ TEST_F(KeycacheTest, RefreshExpiredTest) {
806806 EXPECT_EQ (jwks_str, " {\" keys\" : []}" );
807807}
808808
809+ class IssuerSecurityTest : public ::testing::Test {
810+ protected:
811+ void SetUp () override {
812+ char *err_msg = nullptr ;
813+ m_key = KeyPtr (
814+ scitoken_key_create (" 1" , " ES256" , ec_public, ec_private, &err_msg),
815+ scitoken_key_destroy);
816+ ASSERT_TRUE (m_key.get () != nullptr ) << err_msg;
817+
818+ m_token = TokenPtr (scitoken_create (m_key.get ()), scitoken_destroy);
819+ ASSERT_TRUE (m_token.get () != nullptr );
820+
821+ // Store public key for verification
822+ auto rv = scitoken_store_public_ec_key (" https://demo.scitokens.org/gtest" ,
823+ " 1" , ec_public, &err_msg);
824+ ASSERT_TRUE (rv == 0 ) << err_msg;
825+
826+ scitoken_set_lifetime (m_token.get (), 60 );
827+
828+ m_read_token.reset (scitoken_create (nullptr ));
829+ ASSERT_TRUE (m_read_token.get () != nullptr );
830+ }
831+
832+ using KeyPtr = std::unique_ptr<void , decltype (&scitoken_key_destroy)>;
833+ KeyPtr m_key{nullptr , scitoken_key_destroy};
834+
835+ using TokenPtr = std::unique_ptr<void , decltype (&scitoken_destroy)>;
836+ TokenPtr m_token{nullptr , scitoken_destroy};
837+
838+ TokenPtr m_read_token{nullptr , scitoken_destroy};
839+ };
840+
841+ TEST_F (IssuerSecurityTest, LongIssuerTruncation) {
842+ char *err_msg = nullptr ;
843+
844+ // Create a very long issuer (1000 characters)
845+ std::string very_long_issuer (1000 , ' A' );
846+ auto rv = scitoken_set_claim_string (m_token.get (), " iss" , very_long_issuer.c_str (), &err_msg);
847+ ASSERT_TRUE (rv == 0 ) << err_msg;
848+
849+ char *token_value = nullptr ;
850+ rv = scitoken_serialize (m_token.get (), &token_value, &err_msg);
851+ ASSERT_TRUE (rv == 0 ) << err_msg;
852+ std::unique_ptr<char , decltype (&free)> token_value_ptr (token_value, free);
853+
854+ // Try to verify with a restricted issuer list to trigger error
855+ const char * allowed_issuers[] = {" https://good.issuer.com" , nullptr };
856+ rv = scitoken_deserialize_v2 (token_value, m_read_token.get (), allowed_issuers, &err_msg);
857+
858+ // Should fail
859+ ASSERT_FALSE (rv == 0 );
860+ ASSERT_TRUE (err_msg != nullptr );
861+
862+ std::string error_message (err_msg);
863+ std::unique_ptr<char , decltype (&free)> err_msg_ptr (err_msg, free);
864+
865+ // Error message should be reasonable length (< 400 chars)
866+ EXPECT_LT (error_message.length (), 400 );
867+
868+ // Should contain expected error text
869+ EXPECT_NE (error_message.find (" is not in list of allowed issuers" ), std::string::npos);
870+
871+ // Should contain truncated issuer with ellipsis
872+ EXPECT_NE (error_message.find (" ..." ), std::string::npos);
873+ }
874+
875+ TEST_F (IssuerSecurityTest, SpecialCharacterIssuer) {
876+ char *err_msg = nullptr ;
877+
878+ // Create an issuer with special characters and control chars
879+ std::string special_issuer = " https://bad.com/\"\n\t\r\x01\x1f " ;
880+ auto rv = scitoken_set_claim_string (m_token.get (), " iss" , special_issuer.c_str (), &err_msg);
881+ ASSERT_TRUE (rv == 0 ) << err_msg;
882+
883+ char *token_value = nullptr ;
884+ rv = scitoken_serialize (m_token.get (), &token_value, &err_msg);
885+ ASSERT_TRUE (rv == 0 ) << err_msg;
886+ std::unique_ptr<char , decltype (&free)> token_value_ptr (token_value, free);
887+
888+ // Try to verify with a restricted issuer list to trigger error
889+ const char * allowed_issuers[] = {" https://good.issuer.com" , nullptr };
890+ rv = scitoken_deserialize_v2 (token_value, m_read_token.get (), allowed_issuers, &err_msg);
891+
892+ // Should fail
893+ ASSERT_FALSE (rv == 0 );
894+ ASSERT_TRUE (err_msg != nullptr );
895+
896+ std::string error_message (err_msg);
897+ std::unique_ptr<char , decltype (&free)> err_msg_ptr (err_msg, free);
898+
899+ // Error message should be reasonable length
900+ EXPECT_LT (error_message.length (), 300 );
901+
902+ // Should contain expected error text
903+ EXPECT_NE (error_message.find (" is not in list of allowed issuers" ), std::string::npos);
904+
905+ // Should contain properly escaped JSON (with quotes)
906+ EXPECT_NE (error_message.find (" \" " ), std::string::npos);
907+ }
908+
809909int main (int argc, char **argv) {
810910 ::testing::InitGoogleTest (&argc, argv);
811911 return RUN_ALL_TESTS ();
0 commit comments