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  }