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 }