github.com/google/osv-scalibr@v0.4.1/veles/secrets/gitlabpat/validator_test.go (about) 1 // Copyright 2025 Google LLC 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package gitlabpat_test 16 17 import ( 18 "context" 19 "net/http" 20 "net/http/httptest" 21 "net/url" 22 "testing" 23 24 "github.com/google/go-cmp/cmp" 25 "github.com/google/go-cmp/cmp/cmpopts" 26 "github.com/google/osv-scalibr/veles" 27 "github.com/google/osv-scalibr/veles/secrets/gitlabpat" 28 ) 29 30 const validatorTestPat = "glpat-bzox79Of-KE9FD2LjoXXF4CvyxA.01.0r0l8l6ir" 31 32 type redirectTransport struct { 33 redirectTo string 34 hostToRedirect string 35 } 36 37 func (t *redirectTransport) RoundTrip(req *http.Request) (*http.Response, error) { 38 if req.URL.Host == t.hostToRedirect { 39 newURL, err := url.Parse(t.redirectTo) 40 if err != nil { 41 return nil, err 42 } 43 req.URL.Scheme = newURL.Scheme 44 req.URL.Host = newURL.Host 45 } 46 return http.DefaultTransport.RoundTrip(req) 47 } 48 49 func mockGitlabHandler(t *testing.T, expectedPAT string, status int) http.HandlerFunc { 50 t.Helper() 51 return func(w http.ResponseWriter, r *http.Request) { 52 if got, want := r.Header.Get("PRIVATE-TOKEN"), expectedPAT; got != want { //nolint:canonicalheader 53 t.Errorf("r.Header.Get('PRIVATE-TOKEN') = %s, want %s", got, want) 54 } 55 if r.Method != http.MethodGet { 56 t.Errorf("r.Method = %s, want %s", r.Method, http.MethodGet) 57 } 58 if r.URL.Path != "/api/v4/personal_access_tokens/self" { 59 t.Errorf("r.URL.Path = %s, want /api/v4/personal_access_tokens/self", r.URL.Path) 60 } 61 w.WriteHeader(status) 62 } 63 } 64 65 func TestValidator(t *testing.T) { 66 cases := []struct { 67 name string 68 pat string 69 httpStatus int 70 want veles.ValidationStatus 71 wantErr bool 72 }{ 73 { 74 name: "valid pat", 75 pat: validatorTestPat, 76 httpStatus: http.StatusOK, 77 want: veles.ValidationValid, 78 }, 79 { 80 name: "invalid pat", 81 pat: "glpat-invalid", 82 httpStatus: http.StatusUnauthorized, 83 want: veles.ValidationInvalid, 84 }, 85 { 86 name: "unexpected status code", 87 pat: validatorTestPat, 88 httpStatus: http.StatusNotFound, 89 want: veles.ValidationFailed, 90 wantErr: true, 91 }, 92 { 93 name: "empty pat", 94 pat: "", 95 httpStatus: http.StatusUnauthorized, 96 want: veles.ValidationInvalid, 97 }, 98 } 99 100 for _, tc := range cases { 101 t.Run(tc.name, func(t *testing.T) { 102 server := httptest.NewServer(mockGitlabHandler(t, tc.pat, tc.httpStatus)) 103 defer server.Close() 104 105 client := &http.Client{ 106 Transport: &redirectTransport{ 107 redirectTo: server.URL, 108 hostToRedirect: "gitlab.com", 109 }, 110 } 111 112 v := gitlabpat.NewValidator() 113 v.HTTPC = client 114 115 ctx := t.Context() 116 pat := gitlabpat.GitlabPAT{Pat: tc.pat} 117 got, err := v.Validate(ctx, pat) 118 119 if (err != nil) != tc.wantErr { 120 t.Fatalf("v.Validate() error = %v, wantErr %v", err, tc.wantErr) 121 } 122 123 if diff := cmp.Diff(tc.want, got); diff != "" { 124 t.Errorf("v.Validate() returned diff (-want +got):\n%s", diff) 125 } 126 }) 127 } 128 } 129 func TestValidator_ContextCancellation(t *testing.T) { 130 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 131 w.WriteHeader(http.StatusOK) 132 })) 133 defer server.Close() 134 135 client := &http.Client{ 136 Transport: &redirectTransport{ 137 redirectTo: server.URL, 138 hostToRedirect: "gitlab.com", 139 }, 140 } 141 validator := gitlabpat.NewValidator() 142 validator.HTTPC = client 143 144 usernamePat := gitlabpat.GitlabPAT{Pat: validatorTestPat} 145 146 ctx, cancel := context.WithCancel(t.Context()) 147 cancel() 148 149 got, err := validator.Validate(ctx, usernamePat) 150 151 if !cmp.Equal(err, context.Canceled, cmpopts.EquateErrors()) { 152 t.Errorf("Validate() error = %v, want %v", err, context.Canceled) 153 } 154 if got != veles.ValidationFailed { 155 t.Errorf("Validate() = %v, want %v", got, veles.ValidationFailed) 156 } 157 }