github.com/google/osv-scalibr@v0.4.1/veles/secrets/github/apprefreshtoken_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 github_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/github"
    26  )
    27  
    28  const (
    29  	refreshTestKey        = `ghr_OWOCPzqKuy3J4w53QpkLfffjBUJSh5yLnFHj7wiyR0NDadVOcykNkoqhoYYXM1yy2sOpAu0lG8fw`
    30  	refreshAnotherTestKey = `ghr_Exma21WpQt8vgSQNpEiZtETooAnNLM3rnXRAPnCQYKiuWdmPRnVF0I6cW0zCgA14u7HQzD1Zebn0`
    31  )
    32  
    33  // TestAppRefreshTokenDetector_truePositives tests for cases where we know the Detector
    34  // will find a Github app refresh tokens.
    35  func TestAppRefreshTokenDetector_truePositives(t *testing.T) {
    36  	engine, err := veles.NewDetectionEngine([]veles.Detector{github.NewAppRefreshTokenDetector()})
    37  	if err != nil {
    38  		t.Fatal(err)
    39  	}
    40  	cases := []struct {
    41  		name  string
    42  		input string
    43  		want  []veles.Secret
    44  	}{{
    45  		name:  "simple matching string",
    46  		input: refreshTestKey,
    47  		want: []veles.Secret{
    48  			github.AppRefreshToken{Token: refreshTestKey},
    49  		},
    50  	}, {
    51  		name:  "simple matching string another key",
    52  		input: refreshAnotherTestKey,
    53  		want: []veles.Secret{
    54  			github.AppRefreshToken{Token: refreshAnotherTestKey},
    55  		},
    56  	}, {
    57  		name:  "match at end of string",
    58  		input: `API_TOKEN=` + refreshTestKey,
    59  		want: []veles.Secret{
    60  			github.AppRefreshToken{Token: refreshTestKey},
    61  		},
    62  	}, {
    63  		name:  "match in middle of string",
    64  		input: `API_TOKEN="` + refreshTestKey + `"`,
    65  		want: []veles.Secret{
    66  			github.AppRefreshToken{Token: refreshTestKey},
    67  		},
    68  	}, {
    69  		name:  "multiple matches",
    70  		input: refreshTestKey + refreshTestKey + refreshTestKey,
    71  		want: []veles.Secret{
    72  			github.AppRefreshToken{Token: refreshTestKey},
    73  			github.AppRefreshToken{Token: refreshTestKey},
    74  			github.AppRefreshToken{Token: refreshTestKey},
    75  		},
    76  	}, {
    77  		name:  "bad checksum",
    78  		input: refreshTestKey[:len(refreshTestKey)-1] + "a",
    79  		want:  []veles.Secret{},
    80  	}, {
    81  		name:  "multiple distinct matches",
    82  		input: refreshTestKey + "\n" + refreshAnotherTestKey,
    83  		want: []veles.Secret{
    84  			github.AppRefreshToken{Token: refreshTestKey},
    85  			github.AppRefreshToken{Token: refreshAnotherTestKey},
    86  		},
    87  	}, {
    88  		name: "larger_input_containing_key",
    89  		input: fmt.Sprintf(`
    90  :test_api_key: do-test
    91  :API_TOKEN: %s
    92  		`, refreshTestKey),
    93  		want: []veles.Secret{
    94  			github.AppRefreshToken{Token: refreshTestKey},
    95  		},
    96  	}, {
    97  		name:  "potential match longer than max key length",
    98  		input: refreshTestKey + `extra`,
    99  		want: []veles.Secret{
   100  			github.AppRefreshToken{Token: refreshTestKey},
   101  		},
   102  	}}
   103  	for _, tc := range cases {
   104  		t.Run(tc.name, func(t *testing.T) {
   105  			got, err := engine.Detect(t.Context(), strings.NewReader(tc.input))
   106  			if err != nil {
   107  				t.Errorf("Detect() error: %v, want nil", err)
   108  			}
   109  			if diff := cmp.Diff(tc.want, got, cmpopts.EquateEmpty()); diff != "" {
   110  				t.Errorf("Detect() diff (-want +got):\n%s", diff)
   111  			}
   112  		})
   113  	}
   114  }
   115  
   116  // TestAppRefreshTokenDetector_trueNegatives tests for cases where we know the Detector
   117  // will not find a Github app refresh tokens.
   118  func TestAppRefreshTokenDetector_trueNegatives(t *testing.T) {
   119  	engine, err := veles.NewDetectionEngine([]veles.Detector{github.NewAppRefreshTokenDetector()})
   120  	if err != nil {
   121  		t.Fatal(err)
   122  	}
   123  	cases := []struct {
   124  		name  string
   125  		input string
   126  		want  []veles.Secret
   127  	}{{
   128  		name:  "empty input",
   129  		input: "",
   130  	}, {
   131  		name:  "short key should not match",
   132  		input: refreshTestKey[:len(refreshTestKey)-1],
   133  	}, {
   134  		name:  "invalid character in key should not match",
   135  		input: `gh` + `r_OWOCPzqKuy3J4w53QpkLfff+BUJSh5yLnFHj7wiyR0NDadVOcykNkoqhoYYXM1yy2sOpAu0lG8fw`,
   136  	}, {
   137  		name:  "incorrect prefix should not match",
   138  		input: `Eop_v1_OWOCPzqKuy3J4w53QpkLfffjBUJSh5yLnFHj7wiyR0NDadVOcykNkoqhoYYXM1yy2sOpAu0lG8fw`,
   139  	}, {
   140  		name:  "bad checksum should not match",
   141  		input: `gh` + `r_OWOCPzqKuy3J4w53QpkLfff+BUJSh5yLnFHj7ziyR0NDadVOcykNkoqhoYYXM1yy2sOpAu0lG8fw`,
   142  	}, {
   143  		name:  "prefix missing dash should not match",
   144  		input: `gh` + `rOWOCPzqKuy3J4w53QpkLfffjBUJSh5yLnFHj7wiyR0NDadVOcykNkoqhoYYXM1yy2sOpAu0lG8fw`,
   145  	}}
   146  	for _, tc := range cases {
   147  		t.Run(tc.name, func(t *testing.T) {
   148  			got, err := engine.Detect(t.Context(), strings.NewReader(tc.input))
   149  			if err != nil {
   150  				t.Errorf("Detect() error: %v, want nil", err)
   151  			}
   152  			if diff := cmp.Diff(tc.want, got, cmpopts.EquateEmpty()); diff != "" {
   153  				t.Errorf("Detect() diff (-want +got):\n%s", diff)
   154  			}
   155  		})
   156  	}
   157  }