github.com/google/osv-scalibr@v0.4.1/veles/secrets/gcpoauth2client/detector_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 gcpoauth2client_test 16 17 import ( 18 "fmt" 19 "strings" 20 "testing" 21 22 "github.com/google/go-cmp/cmp" 23 "github.com/google/go-cmp/cmp/cmpopts" 24 "github.com/google/osv-scalibr/veles" 25 "github.com/google/osv-scalibr/veles/secrets/gcpoauth2client" 26 ) 27 28 func TestDetector_Detect(t *testing.T) { 29 engine, err := veles.NewDetectionEngine([]veles.Detector{gcpoauth2client.NewDetector()}) 30 if err != nil { 31 t.Fatal(err) 32 } 33 34 tests := []struct { 35 name string 36 input string 37 want []veles.Secret 38 }{ 39 // --- Empty or invalid input --- 40 { 41 name: "empty input", 42 input: "", 43 want: nil, 44 }, 45 { 46 name: "non-credential input", 47 input: "Some random text", 48 want: nil, 49 }, 50 { 51 name: "invalid client ID format - too short prefix", 52 input: "123456789-tooshort.apps.googleusercontent.com", 53 want: nil, 54 }, 55 { 56 name: "invalid client ID format - wrong domain", 57 input: "123456789012-valid.example.com", 58 want: nil, 59 }, 60 { 61 name: "invalid client secret format - wrong prefix", 62 input: "WRONG-1mVwFTjGIXgs2BC2uHzksQi0HAK", 63 want: nil, 64 }, 65 { 66 name: "invalid client secret format - too short", 67 input: "GOCSPX-short", 68 want: nil, 69 }, 70 // --- Only client ID or Secret --- 71 { 72 name: "client ID but no client secret", 73 input: `app_id: 333333333333-standalone.apps.googleusercontent.com`, 74 want: nil, 75 }, 76 { 77 name: "client secret but no client ID", 78 input: `app_secret: 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`, 79 want: nil, 80 }, 81 // -- Single Client ID and Secret in close proximity (happy path) --- 82 { 83 name: "client_ID_and_secret_in_close_proximity", 84 input: `123456789012-abcdefghijklmnopqrstuvwxyz.apps.googleusercontent.com 85 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`, 86 want: []veles.Secret{ 87 gcpoauth2client.Credentials{ 88 ID: "123456789012-abcdefghijklmnopqrstuvwxyz.apps.googleusercontent.com", 89 Secret: "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", 90 }, 91 }, 92 }, 93 { 94 name: "client_secret_in_with_invalid_prefix", 95 input: `123456789012-abcdefghijklmnopqrstuvwxyz.apps.googleusercontent.com 96 abcGOCSPX-1mVwFTjGIXgs2BC2uHzksQi0HAK1`, 97 want: nil, 98 }, 99 { 100 name: "client_ID_and_secret_in_close_proximity_in_json_format", 101 input: `{ 102 "client_id": "717762328687-iludtf96g1hinl76e4lc1b9a82g457nn.apps.googleusercontent.com", 103 "client_secret": "GOCSPX-WebAppSecret9876543210ABCDEF" 104 }`, 105 want: []veles.Secret{ 106 gcpoauth2client.Credentials{ 107 ID: "717762328687-iludtf96g1hinl76e4lc1b9a82g457nn.apps.googleusercontent.com", 108 Secret: "GOCSPX-WebAppSecret9876543210ABCDEF", 109 }, 110 }, 111 }, 112 { 113 name: "valid_formats_mixed_with_invalid", 114 input: `valid_id: 444444444444-valid.apps.googleusercontent.com 115 invalid_id: 123-invalid.apps.googleusercontent.com 116 valid_secret: GOCSPX-ValidSecret12345678901234567 117 invalid_secret: WRONG-InvalidSecret123456789012345`, 118 want: []veles.Secret{ 119 gcpoauth2client.Credentials{ 120 ID: "444444444444-valid.apps.googleusercontent.com", 121 Secret: "GOCSPX-ValidSecret12345678901234567", 122 }, 123 }, 124 }, 125 // -- Multiple Client ID and Secret in close proximity --- 126 { 127 name: "complex_file_with_multiple_client_ID_and_secret_-_test_proximity", 128 input: `config_app1: 129 111111111111-first.apps.googleusercontent.com 130 GOCSPX-FirstSecret12345678901234567 131 132 config_app2: 133 222222222222-second.apps.googleusercontent.com 134 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`, 135 want: []veles.Secret{ 136 gcpoauth2client.Credentials{ 137 ID: "111111111111-first.apps.googleusercontent.com", 138 Secret: "GOCSPX-FirstSecret12345678901234567", 139 }, 140 gcpoauth2client.Credentials{ 141 ID: "222222222222-second.apps.googleusercontent.com", 142 Secret: "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", 143 }, 144 }, 145 }, 146 { 147 name: "real_world_client_secrets.json_example", 148 input: `{ 149 "web": { 150 "client_id": "555666777888-webappclient.apps.googleusercontent.com", 151 "project_id": "my-project-123", 152 "auth_uri": "https://accounts.google.com/o/oauth2/auth", 153 "token_uri": "https://oauth2.googleapis.com/token", 154 "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", 155 "client_secret": "GOCSPX-RealWorldExample123456789012", 156 "redirect_uris": ["http://localhost:8080/callback"] 157 } 158 }`, 159 want: []veles.Secret{ 160 gcpoauth2client.Credentials{ 161 ID: "555666777888-webappclient.apps.googleusercontent.com", 162 Secret: "GOCSPX-RealWorldExample123456789012", 163 }, 164 }, 165 }, 166 // -- Multiple Client ID and Secret in with varied proximity --- 167 { 168 name: "complex_file_with_multiple_client_ID_and_secret_-_far_apart_(no_pairing)", 169 input: `config_app1: 170 111111111111-first.apps.googleusercontent.com` + strings.Repeat("\nfiller line with random data", 500) + ` 171 config_app2: 172 GOCSPX-FarAwaySecret123456789012345`, 173 want: nil, 174 }, 175 { 176 name: "multiple_client_IDs_with_one_secret_-_closest_pairing", 177 input: `first_id: 111111111111-first.apps.googleusercontent.com 178 second_id: 222222222222-second.apps.googleusercontent.com 179 shared_secret: GOCSPX-SharedSecret1234567890123456`, 180 want: []veles.Secret{ 181 gcpoauth2client.Credentials{ 182 ID: "222222222222-second.apps.googleusercontent.com", 183 Secret: "GOCSPX-SharedSecret1234567890123456", 184 }, 185 }, 186 }, 187 { 188 name: "one_client_ID_with_multiple_secrets_-_closest_pairing", 189 input: `first_secret: GOCSPX-FirstSecret12345678901234567 190 shared_id: 333333333333-shared.apps.googleusercontent.com 191 second_secret: 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`, 192 want: []veles.Secret{ 193 gcpoauth2client.Credentials{ 194 ID: "333333333333-shared.apps.googleusercontent.com", 195 Secret: "GOCSPX-FirstSecret12345678901234567", 196 }, 197 }, 198 }, 199 // --- Duplicate client ID or secret --- 200 { 201 name: "deduplication_test_-_same_client_ID_appears_multiple_times", 202 input: `first_occurrence: 123456789012-duplicate.apps.googleusercontent.com 203 some_other_data: random_value 204 second_occurrence: 123456789012-duplicate.apps.googleusercontent.com 205 secret: GOCSPX-DuplicateTest123456789012345`, 206 want: []veles.Secret{ 207 gcpoauth2client.Credentials{ 208 ID: "123456789012-duplicate.apps.googleusercontent.com", 209 Secret: "GOCSPX-DuplicateTest123456789012345", 210 }, 211 }, 212 }, 213 { 214 name: "deduplication_test_-_same_client_secret_appears_multiple_times", 215 input: `id: 111111111111-unique.apps.googleusercontent.com 216 first_secret: GOCSPX-DuplicateSecret1234567890123 217 some_other_data: random_value 218 second_secret: GOCSPX-DuplicateSecret1234567890123`, 219 want: []veles.Secret{ 220 gcpoauth2client.Credentials{ 221 ID: "111111111111-unique.apps.googleusercontent.com", 222 Secret: "GOCSPX-DuplicateSecret1234567890123", 223 }, 224 }, 225 }, 226 { 227 name: "deduplication_test_-_multiple_pairs_with_overlapping_credentials", 228 input: `shared_id: 123456789012-shared.apps.googleusercontent.com 229 first_secret: GOCSPX-FirstSecret12345678901234567 230 another_id: 987654321098-another.apps.googleusercontent.com 231 shared_secret: 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 232 shared_id_again: 123456789012-shared.apps.googleusercontent.com 233 shared_secret_again: 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`, 234 want: []veles.Secret{ 235 gcpoauth2client.Credentials{ 236 ID: "987654321098-another.apps.googleusercontent.com", 237 Secret: "GOCSPX-FirstSecret12345678901234567", 238 }, 239 gcpoauth2client.Credentials{ 240 ID: "123456789012-shared.apps.googleusercontent.com", 241 Secret: "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", 242 }, 243 gcpoauth2client.Credentials{ 244 ID: "123456789012-shared.apps.googleusercontent.com", 245 Secret: "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", 246 }, 247 }, 248 }, 249 { 250 name: "deduplication_test_-_ensures_no_double_pairing_of_same_credentials", 251 input: `first_id: 111111111111-first.apps.googleusercontent.com 252 unique_secret: GOCSPX-UniqueSecret1234567890123456 253 second_id: 222222222222-second.apps.googleusercontent.com 254 first_id_again: 111111111111-first.apps.googleusercontent.com`, 255 want: []veles.Secret{ 256 gcpoauth2client.Credentials{ 257 ID: "222222222222-second.apps.googleusercontent.com", 258 Secret: "GOCSPX-UniqueSecret1234567890123456", 259 }, 260 }, 261 }, 262 } 263 264 for _, tc := range tests { 265 t.Run(tc.name, func(t *testing.T) { 266 got, err := engine.Detect(t.Context(), strings.NewReader(tc.input)) 267 if err != nil { 268 t.Errorf("Detect() error: %v, want nil", err) 269 } 270 fmt.Printf("got = %+v\n", got) 271 if diff := cmp.Diff(tc.want, got, cmpopts.EquateEmpty()); diff != "" { 272 t.Errorf("Detect() diff (-want +got):\n%s", diff) 273 } 274 }) 275 } 276 }