github.com/google/osv-scalibr@v0.4.1/veles/secrets/gcpsak/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 gcpsak_test 16 17 import ( 18 "context" 19 "encoding/json" 20 "errors" 21 "fmt" 22 "net/http" 23 "net/http/httptest" 24 "testing" 25 26 "github.com/google/osv-scalibr/veles" 27 "github.com/google/osv-scalibr/veles/secrets/gcpsak" 28 ) 29 30 const ( 31 pathPrefix = "/robot/v1/metadata/x509/" 32 ) 33 34 func serveCerts(t *testing.T, certs map[string]string) http.Handler { 35 t.Helper() 36 data, err := json.MarshalIndent(certs, "", " ") 37 if err != nil { 38 t.Errorf("json.Marshal(certs) error: %v", err) 39 } 40 mux := http.NewServeMux() 41 mux.HandleFunc(pathPrefix+exampleServiceAccount, func(w http.ResponseWriter, _ *http.Request) { 42 w.Header().Set("Content-Type", "application/json") 43 if _, err := w.Write(data); err != nil { 44 t.Errorf("unable to write HTTP response: %v", err) 45 } 46 }) 47 return mux 48 } 49 50 func TestValidator(t *testing.T) { 51 exampleCerts := map[string]string{ 52 exampleKeyID: exampleCertificate, 53 } 54 // Set up fake "GCP metadata" HTTP server. 55 srv := httptest.NewTLSServer(serveCerts(t, exampleCerts)) 56 t.Cleanup(func() { 57 srv.Close() 58 }) 59 validator := gcpsak.NewValidator( 60 gcpsak.WithClient(srv.Client()), 61 gcpsak.WithDefaultUniverse(srv.Listener.Addr().String()), 62 ) 63 64 cases := []struct { 65 name string 66 sak gcpsak.GCPSAK 67 want veles.ValidationStatus 68 }{ 69 { 70 name: "example_valid", 71 sak: gcpsak.GCPSAK{ 72 PrivateKeyID: exampleKeyID, 73 ServiceAccount: exampleServiceAccount, 74 Signature: exampleSignature, 75 }, 76 want: veles.ValidationValid, 77 }, 78 { 79 name: "unknown_private_key_ID_invalid", 80 sak: gcpsak.GCPSAK{ 81 PrivateKeyID: "foobar", 82 ServiceAccount: exampleServiceAccount, 83 Signature: exampleSignature, 84 }, 85 want: veles.ValidationInvalid, 86 }, 87 { 88 name: "unknown_service_account_invalid", 89 sak: gcpsak.GCPSAK{ 90 PrivateKeyID: exampleKeyID, 91 ServiceAccount: "unknown-account@asasdasd", 92 Signature: exampleSignature, 93 }, 94 want: veles.ValidationInvalid, 95 }, 96 { 97 name: "invalid_signature_invalid", 98 sak: gcpsak.GCPSAK{ 99 PrivateKeyID: exampleKeyID, 100 ServiceAccount: exampleServiceAccount, 101 Signature: make([]byte, 256), 102 }, 103 want: veles.ValidationInvalid, 104 }, 105 } 106 for _, tc := range cases { 107 t.Run(tc.name, func(t *testing.T) { 108 t.Parallel() 109 got, err := validator.Validate(t.Context(), tc.sak) 110 if err != nil { 111 t.Errorf("Validate() error: %v, want nil", err) 112 } 113 if got != tc.want { 114 t.Errorf("Validate() = %q, want %q", got, tc.want) 115 } 116 }) 117 } 118 } 119 120 func TestValidator_errors(t *testing.T) { 121 sak := gcpsak.GCPSAK{ 122 PrivateKeyID: exampleKeyID, 123 ServiceAccount: exampleServiceAccount, 124 Signature: exampleSignature, 125 } 126 cases := []struct { 127 name string 128 handler http.Handler 129 }{ 130 { 131 name: "other_HTTP_status", 132 handler: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { 133 http.Error(w, "internal server error", http.StatusInternalServerError) 134 }), 135 }, 136 { 137 // This should never happen with the actual GCP metadata server. 138 name: "response_is_not_JSON", 139 handler: http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { 140 fmt.Fprintln(w, "JSON machine broke - understandable have a nice day") 141 }), 142 }, 143 { 144 // This should never happen with the actual GCP metadata server. 145 name: "certificate_is_not_PEM", 146 handler: serveCerts(t, map[string]string{ 147 exampleKeyID: "This doesn't even parse as a PEM block.", 148 }), 149 }, 150 { 151 // This should never happen with the actual GCP metadata server. 152 name: "certificate_is_not_DER", 153 handler: serveCerts(t, map[string]string{ 154 exampleKeyID: "-----BEGIN CERTIFICATE-----\nThis is not a real certificate.\n-----END CERTIFICATE-----\n", 155 }), 156 }, 157 } 158 for _, tc := range cases { 159 t.Run(tc.name, func(t *testing.T) { 160 t.Parallel() 161 srv := httptest.NewTLSServer(tc.handler) 162 t.Cleanup(func() { 163 srv.Close() 164 }) 165 validator := gcpsak.NewValidator( 166 gcpsak.WithClient(srv.Client()), 167 gcpsak.WithDefaultUniverse(srv.Listener.Addr().String()), 168 ) 169 status, err := validator.Validate(t.Context(), sak) 170 if err == nil { 171 t.Error("Validate() error = nil, want err") 172 } 173 if status != veles.ValidationFailed { 174 t.Errorf("Validate() = %q, want %q", status, veles.ValidationFailed) 175 } 176 }) 177 } 178 } 179 180 func TestValidator_respectsContext(t *testing.T) { 181 srv := httptest.NewTLSServer(nil) 182 t.Cleanup(func() { 183 srv.Close() 184 }) 185 validator := gcpsak.NewValidator( 186 gcpsak.WithClient(srv.Client()), 187 gcpsak.WithDefaultUniverse(srv.Listener.Addr().String()), 188 ) 189 ctx, cancel := context.WithCancel(t.Context()) 190 cancel() 191 sak := gcpsak.GCPSAK{ 192 PrivateKeyID: exampleKeyID, 193 ServiceAccount: exampleServiceAccount, 194 Signature: exampleSignature, 195 } 196 if _, err := validator.Validate(ctx, sak); !errors.Is(err, context.Canceled) { 197 t.Errorf("Validate() error: %v, want context.Canceled", err) 198 } 199 }