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  }