fix(detectors/ngrok): broaden valid bearer tokens matching (#5152)
* fix(detectors/ngrok): match API keys that do not start with 2 The ngrok bearer token pattern is 27 alphanumerics, an underscore, and 21 alphanumerics. Requiring a leading 2 missed valid API keys and ak_ resource IDs were never secrets. Co-authored-by: Cursor <[email protected]> * fix(detectors/ngrok): restore digit-leading suffix constraint The prefix broadening accidentally allowed any alphanumeric suffix start, which would increase false positives. Co-authored-by: Cursor <[email protected]> * fix(detectors/ngrok): accept 20-21 char suffixes with any leading char Real authtokens verified against the ngrok API can have 20-char and letter-leading suffixes, so the digit-leading 21-char suffix constraint drops valid secrets. Co-authored-by: Cursor <[email protected]> --------- Co-authored-by: Cursor <[email protected]>
This commit is contained in:
@@ -38,7 +38,13 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"ngrok"}) + `\b(2[a-zA-Z0-9]{26}_\d[a-zA-Z0-9]{20})\b`)
|
||||
// ngrok API keys and authtokens are {prefix}_{suffix}, both alphanumeric with
|
||||
// no fixed leading character. The prefix is 27 chars. Suffixes are usually 21
|
||||
// chars with a leading digit, but real authtokens verified against the ngrok
|
||||
// API (via ERR_NGROK_206) have been observed with 20-char and letter-leading
|
||||
// suffixes, so the pattern must accept 20-21 chars starting with any
|
||||
// alphanumeric character.
|
||||
keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"ngrok"}) + `\b([a-zA-Z0-9]{27}_[a-zA-Z0-9]{20,21})\b`)
|
||||
)
|
||||
|
||||
func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (results []detectors.Result, err error) {
|
||||
|
||||
@@ -12,9 +12,15 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
validPattern = "2WIRSIHOQyHSVklZnoz2k6bTYdH_0E7z0Ta9QEyR1fZvQ0KU9"
|
||||
invalidPattern = "2WIRSIHOQyHSVklZnoz2k6bT?dH_0E7z0Ta9QEyR1fZvQ0KU9"
|
||||
keyword = "ngrok"
|
||||
validPattern = "2WIRSIHOQyHSVklZnoz2k6bTYdH_0E7z0Ta9QEyR1fZvQ0KU9"
|
||||
validPatternAlt = "3GuTI5JXfaLtYILkc6N0xopP2V0_89zkDfefiuMv6Hk1z6oYi"
|
||||
// Authtokens exist in the wild with letter-leading and 20-char suffixes
|
||||
// (confirmed live against the ngrok API), so both must be detected.
|
||||
validPatternLetterSuffix = "3GuTI5JXfaLtYILkc6N0xopP2V0_a9zkDfefiuMv6Hk1z6oYi"
|
||||
validPatternShortSuffix = "4HvUJ6KYgbMuZJMld7O1yqqQ3W1_b8ylEgfgjvNw7Il2z7pZ"
|
||||
invalidPattern = "2WIRSIHOQyHSVklZnoz2k6bT?dH_0E7z0Ta9QEyR1fZvQ0KU9"
|
||||
invalidIDPattern = "ak_3GuTI5JXfaLtYILkc6N0xopP2V0"
|
||||
keyword = "ngrok"
|
||||
)
|
||||
|
||||
func TestNgrok_Pattern(t *testing.T) {
|
||||
@@ -30,11 +36,26 @@ func TestNgrok_Pattern(t *testing.T) {
|
||||
input: fmt.Sprintf("%s token = '%s'", keyword, validPattern),
|
||||
want: []string{"2WIRSIHOQyHSVklZnoz2k6bTYdH_0E7z0Ta9QEyR1fZvQ0KU9"},
|
||||
},
|
||||
{
|
||||
name: "valid pattern - api key not starting with 2",
|
||||
input: fmt.Sprintf("%s token = '%s'", keyword, validPatternAlt),
|
||||
want: []string{validPatternAlt},
|
||||
},
|
||||
{
|
||||
name: "valid pattern - ignore duplicate",
|
||||
input: fmt.Sprintf("%s token = '%s' | '%s'", keyword, validPattern, validPattern),
|
||||
want: []string{"2WIRSIHOQyHSVklZnoz2k6bTYdH_0E7z0Ta9QEyR1fZvQ0KU9"},
|
||||
},
|
||||
{
|
||||
name: "valid pattern - authtoken suffix not starting with digit",
|
||||
input: fmt.Sprintf("%s token = '%s'", keyword, validPatternLetterSuffix),
|
||||
want: []string{validPatternLetterSuffix},
|
||||
},
|
||||
{
|
||||
name: "valid pattern - authtoken with 20 char suffix",
|
||||
input: fmt.Sprintf("%s token = '%s'", keyword, validPatternShortSuffix),
|
||||
want: []string{validPatternShortSuffix},
|
||||
},
|
||||
{
|
||||
name: "valid pattern - key out of prefix range",
|
||||
input: fmt.Sprintf("%s keyword is not close to the real key in the data\n = '%s'", keyword, validPattern),
|
||||
@@ -45,6 +66,11 @@ func TestNgrok_Pattern(t *testing.T) {
|
||||
input: fmt.Sprintf("%s = '%s'", keyword, invalidPattern),
|
||||
want: []string{},
|
||||
},
|
||||
{
|
||||
name: "invalid pattern - api key resource id not bearer token",
|
||||
input: fmt.Sprintf("%s api_key = '%s'", keyword, invalidIDPattern),
|
||||
want: []string{},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
||||
Reference in New Issue
Block a user