github.com/google/osv-scalibr@v0.4.1/veles/secrets/gcpexpressmode/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 gcpexpressmode_test
    16  
    17  import (
    18  	"strings"
    19  	"testing"
    20  
    21  	"github.com/google/go-cmp/cmp"
    22  	"github.com/google/go-cmp/cmp/cmpopts"
    23  	"github.com/google/osv-scalibr/veles"
    24  	"github.com/google/osv-scalibr/veles/secrets/gcpexpressmode"
    25  )
    26  
    27  const (
    28  	testKey = "AQ.Ab8Rthat-is-1-very-nice-KeyYouGotThere_ShameIfLost"
    29  )
    30  
    31  func TestDetector_truePositives(t *testing.T) {
    32  	engine, err := veles.NewDetectionEngine([]veles.Detector{gcpexpressmode.NewDetector()})
    33  	if err != nil {
    34  		t.Fatalf("veles.NewDetectionEngine() error: %v", err)
    35  	}
    36  	cases := []struct {
    37  		name  string
    38  		input string
    39  		want  []veles.Secret
    40  	}{
    41  		{
    42  			name:  "simple matching string",
    43  			input: testKey,
    44  			want: []veles.Secret{
    45  				gcpexpressmode.APIKey{Key: testKey},
    46  			},
    47  		},
    48  		{
    49  			name:  "match at end of string",
    50  			input: "API_KEY=" + testKey,
    51  			want: []veles.Secret{
    52  				gcpexpressmode.APIKey{Key: testKey},
    53  			},
    54  		},
    55  		{
    56  			name:  "match in middle of string",
    57  			input: `API_KEY="` + testKey + `"`,
    58  			want: []veles.Secret{
    59  				gcpexpressmode.APIKey{Key: testKey},
    60  			},
    61  		},
    62  		{
    63  			name:  "multiple matches",
    64  			input: testKey + "\n" + testKey + testKey,
    65  			want: []veles.Secret{
    66  				gcpexpressmode.APIKey{Key: testKey},
    67  				gcpexpressmode.APIKey{Key: testKey},
    68  				gcpexpressmode.APIKey{Key: testKey},
    69  			},
    70  		},
    71  		{
    72  			name:  "multiple distinct matches",
    73  			input: testKey + "\n" + testKey[:len(testKey)-1] + "1\n",
    74  			want: []veles.Secret{
    75  				gcpexpressmode.APIKey{Key: testKey},
    76  				gcpexpressmode.APIKey{Key: testKey[:len(testKey)-1] + "1"},
    77  			},
    78  		},
    79  	}
    80  	for _, tc := range cases {
    81  		t.Run(tc.name, func(t *testing.T) {
    82  			got, err := engine.Detect(t.Context(), strings.NewReader(tc.input))
    83  			if err != nil {
    84  				t.Errorf("Detect() error: %v, want nil", err)
    85  			}
    86  			if diff := cmp.Diff(tc.want, got, cmpopts.EquateEmpty()); diff != "" {
    87  				t.Errorf("Detect() diff (-want +got):\n%s", diff)
    88  			}
    89  		})
    90  	}
    91  }
    92  
    93  func TestDetector_trueNegatives(t *testing.T) {
    94  	engine, err := veles.NewDetectionEngine([]veles.Detector{gcpexpressmode.NewDetector()})
    95  	if err != nil {
    96  		t.Fatal(err)
    97  	}
    98  	cases := []struct {
    99  		name  string
   100  		input string
   101  		want  []veles.Secret
   102  	}{
   103  		{
   104  			name:  "empty input",
   105  			input: "",
   106  		},
   107  		{
   108  			name:  "short key should not match",
   109  			input: testKey[:len(testKey)-1],
   110  		},
   111  		{
   112  			name:  "incorrect casing of prefix should not match",
   113  			input: strings.ToLower(testKey),
   114  		},
   115  		{
   116  			name:  "special character in key should not match",
   117  			input: "AQ.Ab8Rthat-is-1-very-n.ce-KeyYouGotThere_ShameIfLost",
   118  		},
   119  		{
   120  			name:  "wrong prefix should not match",
   121  			input: "AQ-Ab8Rthat-is-1-very-n.ce-KeyYouGotThere_ShameIfLost",
   122  		},
   123  	}
   124  	for _, tc := range cases {
   125  		t.Run(tc.name, func(t *testing.T) {
   126  			got, err := engine.Detect(t.Context(), strings.NewReader(tc.input))
   127  			if err != nil {
   128  				t.Errorf("Detect() error: %v, want nil", err)
   129  			}
   130  			if diff := cmp.Diff(tc.want, got, cmpopts.EquateEmpty()); diff != "" {
   131  				t.Errorf("Detect() diff (-want +got):\n%s", diff)
   132  			}
   133  		})
   134  	}
   135  }