@@ -28,12 +28,15 @@ vi.mock('./utils.js', async () => {
28
28
return result ;
29
29
} ) ,
30
30
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 )
32
32
if ( typeof token === 'string' ) {
33
- return token ;
33
+ throw new Error ( 'Invalid token configuration' ) ;
34
34
}
35
35
// 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' ) ;
37
40
} ) ,
38
41
} ;
39
42
} ) ;
@@ -947,4 +950,62 @@ test('getGerritReposFromConfig handles concurrent authentication requests', asyn
947
950
// Verify getTokenFromConfig was called for each request
948
951
const { getTokenFromConfig } = await import ( './utils.js' ) ;
949
952
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' ) ;
950
1011
} ) ;
0 commit comments