Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 34 additions & 1 deletion pkg/detectors/chatbot/chatbot.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package chatbot
import (
"context"
"fmt"
"io"
"net/http"
"strings"

Expand All @@ -23,8 +24,32 @@ var (

// Make sure that your group is surrounded in boundary characters such as below to reduce false positives.
keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"chatbot"}) + `\b([a-zA-Z0-9_]{32})\b`)

// False positive filters
onlyLetters = regexp.MustCompile(`^[a-zA-Z]+$`)
snakeCaseOnly = regexp.MustCompile(`^[a-z]+(_[a-z]+)+$`)
camelCaseName = regexp.MustCompile(`^[a-z]+([A-Z][a-z]+)+$`) // camelCase
pascalCaseName = regexp.MustCompile(`^([A-Z][a-z]+)+$`) // PascalCase
)

// isLikelyFalsePositive checks if a matched string is likely a false positive
// (e.g., variable names, class names, identifiers) rather than an actual API key.
func isLikelyFalsePositive(key string) bool {
// Only letters (no digits) - likely variable/class name
if onlyLetters.MatchString(key) {
return true
}
// Snake_case pattern - likely variable name
if snakeCaseOnly.MatchString(key) {
return true
}
// camelCase or PascalCase - likely code identifier
if camelCaseName.MatchString(key) || pascalCaseName.MatchString(key) {
return true
}
return false
}

// Keywords are used for efficiently pre-filtering chunks.
// Use identifiers in the secret preferably, or the provider name.
func (s Scanner) Keywords() []string {
Expand All @@ -40,6 +65,11 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
for _, match := range matches {
resMatch := strings.TrimSpace(match[1])

// Filter out likely false positives
if isLikelyFalsePositive(resMatch) {
continue
}

s1 := detectors.Result{
DetectorType: detectorspb.DetectorType_Chatbot,
Raw: []byte(resMatch),
Expand All @@ -53,7 +83,10 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch))
res, err := client.Do(req)
if err == nil {
defer res.Body.Close()
defer func() {
_, _ = io.Copy(io.Discard, res.Body)
_ = res.Body.Close()
}()
if res.StatusCode >= 200 && res.StatusCode < 300 {
s1.Verified = true
}
Expand Down
Loading