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