github.com/google/osv-scalibr@v0.4.1/binary/proto/secret_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 proto_test 16 17 import ( 18 "errors" 19 "testing" 20 "time" 21 22 "github.com/google/go-cmp/cmp" 23 "github.com/google/go-cpy/cpy" 24 "github.com/google/osv-scalibr/binary/proto" 25 "github.com/google/osv-scalibr/inventory" 26 "github.com/google/osv-scalibr/veles" 27 "github.com/google/osv-scalibr/veles/secrets/gcpapikey" 28 "github.com/google/osv-scalibr/veles/secrets/gcpoauth2client" 29 "github.com/google/osv-scalibr/veles/secrets/gcpsak" 30 "google.golang.org/protobuf/testing/protocmp" 31 32 spb "github.com/google/osv-scalibr/binary/proto/scan_result_go_proto" 33 "google.golang.org/protobuf/types/known/timestamppb" 34 ) 35 36 var ( 37 secretAt1 = time.Now() 38 39 secretGCPSAKStruct1 = &inventory.Secret{ 40 Secret: gcpsak.GCPSAK{ 41 PrivateKeyID: "some-private-key-id", 42 ServiceAccount: "some-service-account@gserviceaccount.iam.google.com", 43 Signature: make([]byte, 256), 44 }, 45 Location: "/foo/bar/baz.json", 46 Validation: inventory.SecretValidationResult{ 47 At: secretAt1, 48 Status: veles.ValidationInvalid, 49 }, 50 } 51 secretGCPSAKProto1 = &spb.Secret{ 52 Secret: &spb.SecretData{ 53 Secret: &spb.SecretData_Gcpsak{ 54 Gcpsak: &spb.SecretData_GCPSAK{ 55 PrivateKeyId: "some-private-key-id", 56 ClientEmail: "some-service-account@gserviceaccount.iam.google.com", 57 Signature: make([]byte, 256), 58 }, 59 }, 60 }, 61 Status: &spb.SecretStatus{ 62 Status: spb.SecretStatus_INVALID, 63 LastUpdated: timestamppb.New(secretAt1), 64 }, 65 Locations: []*spb.Location{ 66 &spb.Location{ 67 Location: &spb.Location_Filepath{ 68 Filepath: &spb.Filepath{ 69 Path: "/foo/bar/baz.json", 70 }, 71 }, 72 }, 73 }, 74 } 75 secretGCPAPIKeyStruct = &inventory.Secret{ 76 Secret: gcpapikey.GCPAPIKey{ 77 Key: "AIzatestestestestestestestestestesttest", 78 }, 79 Location: "/foo/bar/baz.json", 80 } 81 secretGCPAPIKeyProto = &spb.Secret{ 82 Secret: &spb.SecretData{ 83 Secret: &spb.SecretData_GcpApiKey{ 84 GcpApiKey: &spb.SecretData_GCPAPIKey{ 85 Key: "AIzatestestestestestestestestestesttest", 86 }, 87 }, 88 }, 89 Locations: []*spb.Location{ 90 &spb.Location{ 91 Location: &spb.Location_Filepath{ 92 Filepath: &spb.Filepath{ 93 Path: "/foo/bar/baz.json", 94 }, 95 }, 96 }, 97 }, 98 } 99 100 secretGCPOAuth2ClientCredentialsStruct = &inventory.Secret{ 101 Secret: gcpoauth2client.Credentials{ 102 ID: "12345678901-abcdefghijklmnopqrstuvwxyz.apps.googleusercontent.com", 103 Secret: "GOCSPX-1mVwFTjGIXgs2BC2uHzksQi0HAK", 104 }, 105 Location: "/foo/bar/baz.json", 106 } 107 secretGCPOAuth2ClientCredentialsProto = &spb.Secret{ 108 Secret: &spb.SecretData{ 109 Secret: &spb.SecretData_GcpOauth2ClientCredentials{ 110 GcpOauth2ClientCredentials: &spb.SecretData_GCPOAuth2ClientCredentials{ 111 Id: "12345678901-abcdefghijklmnopqrstuvwxyz.apps.googleusercontent.com", 112 Secret: "GOCSPX-1mVwFTjGIXgs2BC2uHzksQi0HAK", 113 }, 114 }, 115 }, 116 Locations: []*spb.Location{ 117 &spb.Location{ 118 Location: &spb.Location_Filepath{ 119 Filepath: &spb.Filepath{ 120 Path: "/foo/bar/baz.json", 121 }, 122 }, 123 }, 124 }, 125 } 126 ) 127 128 // --- Struct to Proto 129 130 func TestSecretToProto(t *testing.T) { 131 copier := cpy.New( 132 cpy.IgnoreAllUnexported(), 133 ) 134 135 testCases := []struct { 136 desc string 137 s *inventory.Secret 138 want *spb.Secret 139 wantErr error 140 }{ 141 { 142 desc: "nil", 143 s: nil, 144 want: nil, 145 }, 146 { 147 desc: "success", 148 s: secretGCPSAKStruct1, 149 want: secretGCPSAKProto1, 150 }, 151 { 152 desc: "empty_validation", 153 s: func(s *inventory.Secret) *inventory.Secret { 154 s = copier.Copy(s).(*inventory.Secret) 155 s.Validation = inventory.SecretValidationResult{} 156 return s 157 }(secretGCPSAKStruct1), 158 want: func(s *spb.Secret) *spb.Secret { 159 s = copier.Copy(s).(*spb.Secret) 160 s.Status = nil 161 return s 162 }(secretGCPSAKProto1), 163 }, 164 { 165 desc: "success_GCP_API_key", 166 s: secretGCPAPIKeyStruct, 167 want: secretGCPAPIKeyProto, 168 }, 169 { 170 desc: "GCP_OAuth2_client_credentials", 171 s: secretGCPOAuth2ClientCredentialsStruct, 172 want: secretGCPOAuth2ClientCredentialsProto, 173 }, 174 } 175 176 for _, tc := range testCases { 177 t.Run(tc.desc, func(t *testing.T) { 178 got, err := proto.SecretToProto(tc.s) 179 if !errors.Is(err, tc.wantErr) { 180 t.Errorf("SecretToProto(%v) returned error %v, want error %v", tc.s, err, tc.wantErr) 181 } 182 if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" { 183 t.Fatalf("SecretToProto(%v) returned diff (-want +got):\n%s", tc.s, diff) 184 } 185 186 // No need to test the reverse conversion if the result is nil. 187 if got == nil && tc.s != nil { 188 return 189 } 190 191 // Test the reverse conversion for completeness. 192 gotPB, err := proto.SecretToStruct(got) 193 if err != nil { 194 t.Fatalf("SecretToStruct(%v) returned error %v, want nil", got, err) 195 } 196 if diff := cmp.Diff(tc.s, gotPB, protocmp.Transform()); diff != "" { 197 t.Fatalf("SecretToStruct(%v) returned diff (-want +got):\n%s", got, diff) 198 } 199 }) 200 } 201 } 202 203 // --- Proto to Struct 204 205 func TestSecretToStruct(t *testing.T) { 206 copier := cpy.New( 207 cpy.IgnoreAllUnexported(), 208 ) 209 210 testCases := []struct { 211 desc string 212 s *spb.Secret 213 want *inventory.Secret 214 wantErr error 215 }{ 216 { 217 desc: "nil", 218 s: nil, 219 want: nil, 220 }, 221 { 222 desc: "success", 223 s: secretGCPSAKProto1, 224 want: secretGCPSAKStruct1, 225 }, 226 { 227 desc: "empty_validation", 228 s: func(s *spb.Secret) *spb.Secret { 229 s = copier.Copy(s).(*spb.Secret) 230 s.Status = nil 231 return s 232 }(secretGCPSAKProto1), 233 want: func(s *inventory.Secret) *inventory.Secret { 234 s = copier.Copy(s).(*inventory.Secret) 235 s.Validation = inventory.SecretValidationResult{} 236 return s 237 }(secretGCPSAKStruct1), 238 }, 239 { 240 desc: "success_GCP_API_key", 241 s: secretGCPAPIKeyProto, 242 want: secretGCPAPIKeyStruct, 243 }, 244 { 245 desc: "GCP_OAuth2_client_credentials", 246 s: secretGCPOAuth2ClientCredentialsProto, 247 want: secretGCPOAuth2ClientCredentialsStruct, 248 }, 249 } 250 251 for _, tc := range testCases { 252 t.Run(tc.desc, func(t *testing.T) { 253 got, err := proto.SecretToStruct(tc.s) 254 if !errors.Is(err, tc.wantErr) { 255 t.Errorf("SecretToStruct(%v) returned error %v, want error %v", tc.s, err, tc.wantErr) 256 } 257 if diff := cmp.Diff(tc.want, got, protocmp.Transform()); diff != "" { 258 t.Fatalf("SecretToStruct(%v) returned diff (-want +got):\n%s", tc.s, diff) 259 } 260 261 // No need to test the reverse conversion if the result is nil. 262 if got == nil && tc.s != nil { 263 return 264 } 265 266 // Test the reverse conversion for completeness. 267 gotPB, err := proto.SecretToProto(got) 268 if err != nil { 269 t.Fatalf("SecretToProto(%v) returned error %v, want nil", got, err) 270 } 271 if diff := cmp.Diff(tc.s, gotPB, protocmp.Transform()); diff != "" { 272 t.Fatalf("SecretToProto(%v) returned diff (-want +got):\n%s", got, diff) 273 } 274 }) 275 } 276 }