github.com/google/osv-scalibr@v0.4.1/veles/secrets/common/simpletoken/simpletoken_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 simpletoken_test
    16  
    17  import (
    18  	"regexp"
    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/common/simpletoken"
    25  )
    26  
    27  // TestDetect_truePositives tests for cases where we know Detect will return a
    28  // match/matches.
    29  func TestDetect_truePositives(t *testing.T) {
    30  	cases := []struct {
    31  		name         string
    32  		regexp       string
    33  		maxSecretLen uint32
    34  		in           []byte
    35  		want         []veles.Secret
    36  		wantPos      []int
    37  		fromMatch    func([]byte) (veles.Secret, bool)
    38  	}{{
    39  		name:         "match only",
    40  		regexp:       "FOO",
    41  		maxSecretLen: 3,
    42  		in:           []byte("FOO"),
    43  		want:         []veles.Secret{"FOO"},
    44  		wantPos:      []int{0},
    45  	}, {
    46  		name:         "match at beginning",
    47  		regexp:       "FOO",
    48  		maxSecretLen: 3,
    49  		in:           []byte("FOOa"),
    50  		want:         []veles.Secret{"FOO"},
    51  		wantPos:      []int{0},
    52  	}, {
    53  		name:         "match in middle",
    54  		regexp:       "FOO",
    55  		maxSecretLen: 3,
    56  		in:           []byte("aFOOa"),
    57  		want:         []veles.Secret{"FOO"},
    58  		wantPos:      []int{1},
    59  	}, {
    60  		name:         "match at end",
    61  		regexp:       "FOO",
    62  		maxSecretLen: 3,
    63  		in:           []byte("aFOO"),
    64  		want:         []veles.Secret{"FOO"},
    65  		wantPos:      []int{1},
    66  	}, {
    67  		name:         "match at end",
    68  		regexp:       "FOO",
    69  		maxSecretLen: 3,
    70  		in:           []byte("aFOO"),
    71  		want:         []veles.Secret{"FOO"},
    72  		wantPos:      []int{1},
    73  	}, {
    74  		name:         "multiple matches",
    75  		regexp:       "FOO",
    76  		maxSecretLen: 3,
    77  		in:           []byte("FOO FOO"), //nolint:dupword
    78  		want:         []veles.Secret{"FOO", "FOO"},
    79  		wantPos:      []int{0, 4},
    80  	}, {
    81  		name:         "multi-line input",
    82  		regexp:       "FOO",
    83  		maxSecretLen: 3,
    84  		in: []byte(`
    85  FOO
    86  BAR
    87  BAZ
    88  		`),
    89  		want:    []veles.Secret{"FOO"},
    90  		wantPos: []int{1},
    91  	}, {
    92  		name:         "multiple distinct matches",
    93  		regexp:       "[A-Z]{3}",
    94  		maxSecretLen: 3,
    95  		in:           []byte("FOO BAR"),
    96  		want:         []veles.Secret{"FOO", "BAR"},
    97  		wantPos:      []int{0, 4},
    98  	}, {
    99  		name:         "not ok",
   100  		regexp:       "[A-Z]{3}",
   101  		maxSecretLen: 3,
   102  		in:           []byte("FOO BAR"),
   103  		want:         []veles.Secret{"BAR"},
   104  		wantPos:      []int{4},
   105  		fromMatch: func(b []byte) (veles.Secret, bool) {
   106  			if string(b) == "FOO" {
   107  				return nil, false
   108  			}
   109  			return string(b), true
   110  		},
   111  	}, {
   112  		// See https://pkg.go.dev/regexp and
   113  		// https://github.com/google/re2/wiki/syntax.
   114  		name:         "matches do not overlap",
   115  		regexp:       "[A-Z]{3}",
   116  		maxSecretLen: 3,
   117  		in:           []byte("FOOBAR"),
   118  		want:         []veles.Secret{"FOO", "BAR"},
   119  		wantPos:      []int{0, 3},
   120  	}}
   121  	for _, tc := range cases {
   122  		t.Run(tc.name, func(t *testing.T) {
   123  			if tc.fromMatch == nil {
   124  				tc.fromMatch = func(b []byte) (veles.Secret, bool) {
   125  					return string(b), true
   126  				}
   127  			}
   128  			d := simpletoken.Detector{
   129  				MaxLen:    tc.maxSecretLen,
   130  				Re:        regexp.MustCompile(tc.regexp),
   131  				FromMatch: tc.fromMatch,
   132  			}
   133  			got, gotPos := d.Detect(tc.in)
   134  			if diff := cmp.Diff(tc.want, got, cmpopts.EquateEmpty()); diff != "" {
   135  				t.Errorf("Detect() diff (-want +got):\n%s", diff)
   136  			}
   137  			if diff := cmp.Diff(tc.wantPos, gotPos, cmpopts.EquateEmpty()); diff != "" {
   138  				t.Errorf("Detect() diff (-want +got):\n%s", diff)
   139  			}
   140  		})
   141  	}
   142  }
   143  
   144  // TestDetect_trueNegative tests for cases where we know Detect will not return
   145  // a match.
   146  func TestDetect_trueNegatives(t *testing.T) {
   147  	cases := []struct {
   148  		name         string
   149  		regexp       string
   150  		maxSecretLen uint32
   151  		in           []byte
   152  		want         []veles.Secret
   153  		wantPos      []int
   154  	}{{
   155  		name:         "no match",
   156  		regexp:       "FOO",
   157  		maxSecretLen: 3,
   158  		in:           []byte("BAR"),
   159  	}}
   160  	for _, tc := range cases {
   161  		t.Run(tc.name, func(t *testing.T) {
   162  			d := simpletoken.Detector{
   163  				MaxLen: tc.maxSecretLen,
   164  				Re:     regexp.MustCompile(tc.regexp),
   165  				FromMatch: func(b []byte) (veles.Secret, bool) {
   166  					return string(b), true
   167  				},
   168  			}
   169  			got, gotPos := d.Detect(tc.in)
   170  			if diff := cmp.Diff(tc.want, got, cmpopts.EquateEmpty()); diff != "" {
   171  				t.Errorf("Detect() diff (-want +got):\n%s", diff)
   172  			}
   173  			if diff := cmp.Diff(tc.wantPos, gotPos, cmpopts.EquateEmpty()); diff != "" {
   174  				t.Errorf("Detect() diff (-want +got):\n%s", diff)
   175  			}
   176  		})
   177  	}
   178  }