@@ -28,12 +28,15 @@ vi.mock('./utils.js', async () => {
2828 return result ;
2929 } ) ,
3030 getTokenFromConfig : vi . fn ( ) . mockImplementation ( async ( token ) => {
31- // If token is a string, return it directly (mimicking actual behavior )
31+ // String tokens are no longer supported (security measure )
3232 if ( typeof token === 'string' ) {
33- return token ;
33+ throw new Error ( 'Invalid token configuration' ) ;
3434 }
3535 // For objects (env/secret), return mock value
36- return 'mock-password' ;
36+ if ( token && typeof token === 'object' && ( 'secret' in token || 'env' in token ) ) {
37+ return 'mock-password' ;
38+ }
39+ throw new Error ( 'Invalid token configuration' ) ;
3740 } ) ,
3841 } ;
3942} ) ;
@@ -947,4 +950,62 @@ test('getGerritReposFromConfig handles concurrent authentication requests', asyn
947950 // Verify getTokenFromConfig was called for each request
948951 const { getTokenFromConfig } = await import ( './utils.js' ) ;
949952 expect ( getTokenFromConfig ) . toHaveBeenCalledTimes ( 5 ) ;
953+ } ) ;
954+
955+ test ( 'getGerritReposFromConfig rejects invalid token formats (security)' , async ( ) => {
956+ const configWithStringToken : any = {
957+ type : 'gerrit' ,
958+ url : 'https://gerrit.example.com' ,
959+ projects : [ 'test-project' ] ,
960+ auth : {
961+ username : 'testuser' ,
962+ password : 'direct-string-password' // This should be rejected
963+ }
964+ } ;
965+
966+ await expect ( getGerritReposFromConfig ( configWithStringToken , 1 , mockDb ) )
967+ . rejects . toThrow ( 'CONNECTION_SYNC_FAILED_TO_FETCH_GERRIT_PROJECTS' ) ;
968+
969+ const configWithMalformedToken : any = {
970+ type : 'gerrit' ,
971+ url : 'https://gerrit.example.com' ,
972+ projects : [ 'test-project' ] ,
973+ auth : {
974+ username : 'testuser' ,
975+ password : { invalid : 'format' } // This should be rejected
976+ }
977+ } ;
978+
979+ await expect ( getGerritReposFromConfig ( configWithMalformedToken , 1 , mockDb ) )
980+ . rejects . toThrow ( 'CONNECTION_SYNC_FAILED_TO_FETCH_GERRIT_PROJECTS' ) ;
981+ } ) ;
982+
983+ test ( 'getGerritReposFromConfig handles responses with and without XSSI prefix' , async ( ) => {
984+ const config : GerritConnectionConfig = {
985+ type : 'gerrit' ,
986+ url : 'https://gerrit.example.com' ,
987+ projects : [ 'test-project' ]
988+ } ;
989+
990+ // Test with XSSI prefix
991+ const responseWithXSSI = {
992+ ok : true ,
993+ text : ( ) => Promise . resolve ( ')]}\'\n{"test-project": {"id": "test%2Dproject"}}' ) ,
994+ } ;
995+ mockFetch . mockResolvedValueOnce ( responseWithXSSI as any ) ;
996+
997+ const result1 = await getGerritReposFromConfig ( config , 1 , mockDb ) ;
998+ expect ( result1 ) . toHaveLength ( 1 ) ;
999+ expect ( result1 [ 0 ] . name ) . toBe ( 'test-project' ) ;
1000+
1001+ // Test without XSSI prefix
1002+ const responseWithoutXSSI = {
1003+ ok : true ,
1004+ text : ( ) => Promise . resolve ( '{"test-project": {"id": "test%2Dproject"}}' ) ,
1005+ } ;
1006+ mockFetch . mockResolvedValueOnce ( responseWithoutXSSI as any ) ;
1007+
1008+ const result2 = await getGerritReposFromConfig ( config , 1 , mockDb ) ;
1009+ expect ( result2 ) . toHaveLength ( 1 ) ;
1010+ expect ( result2 [ 0 ] . name ) . toBe ( 'test-project' ) ;
9501011} ) ;
0 commit comments