github.com/google/osv-scalibr@v0.4.1/veles/secrets/dockerhubpat/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 dockerhubpat_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/dockerhubpat"
    26  )
    27  
    28  const testKey = `dckr_pat_7awgM4jG5SQvxcvmNzhKj8PQjxo`
    29  
    30  // TestDetector_truePositives tests for cases where we know the Detector
    31  // will find a Docker Hub PAT/s.
    32  func TestDetector_truePositives(t *testing.T) {
    33  	engine, err := veles.NewDetectionEngine([]veles.Detector{dockerhubpat.NewDetector()})
    34  	if err != nil {
    35  		t.Fatal(err)
    36  	}
    37  	cases := []struct {
    38  		name  string
    39  		input string
    40  		want  []veles.Secret
    41  	}{
    42  		{
    43  			name:  "simple matching string",
    44  			input: testKey,
    45  			want: []veles.Secret{
    46  				dockerhubpat.DockerHubPAT{Pat: testKey},
    47  			},
    48  		},
    49  		{
    50  			name:  "match of docker login command 1",
    51  			input: `docker login -u username -p ` + testKey,
    52  			want: []veles.Secret{
    53  				dockerhubpat.DockerHubPAT{Pat: testKey, Username: "username"},
    54  			},
    55  		},
    56  		{
    57  			name:  "match of docker login command with duplicate 2",
    58  			input: `docker login -u username -p ` + testKey + "\n\n" + testKey,
    59  			want: []veles.Secret{
    60  				dockerhubpat.DockerHubPAT{Pat: testKey, Username: "username"},
    61  			},
    62  		},
    63  		{
    64  			name:  "match of docker login command 2",
    65  			input: `docker login -p ` + testKey + ` -u username `,
    66  			want: []veles.Secret{
    67  				dockerhubpat.DockerHubPAT{Pat: testKey, Username: "username"},
    68  			},
    69  		},
    70  		{
    71  			name:  "match in middle of string",
    72  			input: `DOCKERHUBPAT="` + testKey + `"`,
    73  			want: []veles.Secret{
    74  				dockerhubpat.DockerHubPAT{Pat: testKey},
    75  			},
    76  		},
    77  		{
    78  			name:  "multiple matches",
    79  			input: testKey + testKey + testKey,
    80  			want: []veles.Secret{
    81  				dockerhubpat.DockerHubPAT{Pat: testKey},
    82  				dockerhubpat.DockerHubPAT{Pat: testKey},
    83  				dockerhubpat.DockerHubPAT{Pat: testKey},
    84  			},
    85  		},
    86  		{
    87  			name:  "multiple distinct matches",
    88  			input: testKey + "\n" + testKey[:len(testKey)-1] + "a",
    89  			want: []veles.Secret{
    90  				dockerhubpat.DockerHubPAT{Pat: testKey},
    91  				dockerhubpat.DockerHubPAT{Pat: testKey[:len(testKey)-1] + "a"},
    92  			},
    93  		},
    94  		{
    95  			name: "larger_input_containing_key",
    96  			input: fmt.Sprintf(`
    97  		:test_api_key: pat-test
    98  		:dockerhub_pat: %s
    99  				`, testKey),
   100  			want: []veles.Secret{
   101  				dockerhubpat.DockerHubPAT{Pat: testKey},
   102  			},
   103  		},
   104  		{
   105  			name:  "potential match longer than max key length",
   106  			input: testKey + `extra`,
   107  			want: []veles.Secret{
   108  				dockerhubpat.DockerHubPAT{Pat: testKey},
   109  			},
   110  		},
   111  	}
   112  	for _, tc := range cases {
   113  		t.Run(tc.name, func(t *testing.T) {
   114  			got, err := engine.Detect(t.Context(), strings.NewReader(tc.input))
   115  			if err != nil {
   116  				t.Errorf("Detect() error: %v, want nil", err)
   117  			}
   118  			fmt.Printf("got = %+v\n", got)
   119  			if diff := cmp.Diff(tc.want, got, cmpopts.EquateEmpty()); diff != "" {
   120  				t.Errorf("Detect() diff (-want +got):\n%s", diff)
   121  			}
   122  		})
   123  	}
   124  }
   125  
   126  // TestDetector_trueNegatives tests for cases where we know the Detector
   127  // will not find a Docker Hub PAT.
   128  func TestDetector_trueNegatives(t *testing.T) {
   129  	engine, err := veles.NewDetectionEngine([]veles.Detector{dockerhubpat.NewDetector()})
   130  	if err != nil {
   131  		t.Fatal(err)
   132  	}
   133  	cases := []struct {
   134  		name  string
   135  		input string
   136  		want  []veles.Secret
   137  	}{{
   138  		name:  "empty input",
   139  		input: "",
   140  	}, {
   141  		name:  "short key should not match",
   142  		input: testKey[:len(testKey)-1],
   143  	}, {
   144  		name:  "invalid character in key should not match",
   145  		input: `dckr_pat_7a@wgM4jG5SQvxcvmNzhKj8PQjxoQ`,
   146  	}, {
   147  		name:  "incorrect prefix should not match",
   148  		input: `aaar_pat_7awgM4jG5SQvxcvmNzhKj8PQjxo`,
   149  	}, {
   150  		name:  "prefix missing dash should not match",
   151  		input: `7awgM4jG5SQvxcvmNzhKj8PQjxo`,
   152  	}}
   153  	for _, tc := range cases {
   154  		t.Run(tc.name, func(t *testing.T) {
   155  			got, err := engine.Detect(t.Context(), strings.NewReader(tc.input))
   156  			if err != nil {
   157  				t.Errorf("Detect() error: %v, want nil", err)
   158  			}
   159  			if diff := cmp.Diff(tc.want, got, cmpopts.EquateEmpty()); diff != "" {
   160  				t.Errorf("Detect() diff (-want +got):\n%s", diff)
   161  			}
   162  		})
   163  	}
   164  }