github.com/google/osv-scalibr@v0.4.1/veles/secrets/common/pair/pair_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 pair_test
    16  
    17  import (
    18  	"regexp"
    19  	"testing"
    20  
    21  	"github.com/google/go-cmp/cmp"
    22  	"github.com/google/osv-scalibr/veles"
    23  	"github.com/google/osv-scalibr/veles/secrets/common/pair"
    24  )
    25  
    26  type mockSecret struct {
    27  	Value string
    28  }
    29  
    30  // mock function to convert Pair to veles.Secret
    31  func mockSecretFromPair(p pair.Pair) (veles.Secret, bool) {
    32  	return mockSecret{Value: string(p.A.Value) + "-" + string(p.B.Value)}, true
    33  }
    34  
    35  // mock function to convert Pair to veles.Secret
    36  func mockSecretFromPartialPair(p pair.Pair) (veles.Secret, bool) {
    37  	if p.B == nil {
    38  		return mockSecret{Value: string(p.A.Value)}, true
    39  	}
    40  	return mockSecret{Value: string(p.B.Value)}, true
    41  }
    42  
    43  func TestFindOptimalPairs(t *testing.T) {
    44  	var (
    45  		aPattern = regexp.MustCompile(`a[a-z]*[1-9]`)
    46  		bPattern = regexp.MustCompile(`b[a-z]*[1-9]`)
    47  	)
    48  
    49  	const distanceUpperBound = 1000
    50  
    51  	tests := []struct {
    52  		name            string
    53  		input           string
    54  		wantSecrets     []veles.Secret
    55  		maxDistance     uint32
    56  		wantPos         []int
    57  		fromPartialPair func(pair.Pair) (veles.Secret, bool)
    58  	}{
    59  		{
    60  			name:        "simple match",
    61  			input:       "a1 b1",
    62  			maxDistance: distanceUpperBound,
    63  			wantSecrets: []veles.Secret{
    64  				mockSecret{Value: "a1-b1"},
    65  			},
    66  			wantPos: []int{0},
    67  		},
    68  		{
    69  			name:        "multiple matches, greedy selection",
    70  			input:       "a1 b1 a2 b2",
    71  			maxDistance: distanceUpperBound,
    72  			wantSecrets: []veles.Secret{
    73  				mockSecret{Value: "a1-b1"},
    74  				mockSecret{Value: "a2-b2"},
    75  			},
    76  			wantPos: []int{0, 6},
    77  		},
    78  		{
    79  			name:  "no matches",
    80  			input: "a1 xxxxx",
    81  		},
    82  		{
    83  			name:        "more bs than as - deduplication",
    84  			input:       "a1 b1 b2",
    85  			maxDistance: distanceUpperBound,
    86  			wantSecrets: []veles.Secret{
    87  				mockSecret{Value: "a1-b1"},
    88  			},
    89  			wantPos: []int{0},
    90  		},
    91  		{
    92  			name:        "far apart - no match",
    93  			input:       "a1           b2",
    94  			maxDistance: uint32(5),
    95  		},
    96  		{
    97  			name:        "overlapping pairs",
    98  			input:       " b2 ab1",
    99  			maxDistance: distanceUpperBound,
   100  			wantSecrets: []veles.Secret{
   101  				mockSecret{Value: "ab1-b2"},
   102  			},
   103  			wantPos: []int{1},
   104  		},
   105  		{
   106  			name:            "partial pair",
   107  			input:           "a1",
   108  			maxDistance:     distanceUpperBound,
   109  			fromPartialPair: mockSecretFromPartialPair,
   110  			wantSecrets: []veles.Secret{
   111  				mockSecret{Value: "a1"},
   112  			},
   113  			wantPos: []int{0},
   114  		},
   115  	}
   116  
   117  	for _, tt := range tests {
   118  		t.Run(tt.name, func(t *testing.T) {
   119  			d := &pair.Detector{
   120  				// include the whole payload using an upper bound as MaxElementLen
   121  				MaxElementLen: 10, MaxDistance: tt.maxDistance,
   122  				FindA:           pair.FindAllMatches(aPattern),
   123  				FindB:           pair.FindAllMatches(bPattern),
   124  				FromPair:        mockSecretFromPair,
   125  				FromPartialPair: tt.fromPartialPair,
   126  			}
   127  
   128  			gotSecrets, gotPos := d.Detect([]byte(tt.input))
   129  			if diff := cmp.Diff(tt.wantSecrets, gotSecrets); diff != "" {
   130  				t.Errorf("Secrets mismatch (-want +got):\n%s", diff)
   131  			}
   132  			if diff := cmp.Diff(tt.wantPos, gotPos); diff != "" {
   133  				t.Errorf("Positions mismatch (-want +got):\n%s", diff)
   134  			}
   135  		})
   136  	}
   137  }