Refactored caflou detector (#4372)

This commit is contained in:
Kashif Khan
2025-08-08 12:34:26 +05:00
committed by GitHub
parent eb908bd433
commit d28724608b
2 changed files with 56 additions and 42 deletions
+37 -18
View File
@@ -3,6 +3,7 @@ package caflou
import (
"context"
"fmt"
"io"
"net/http"
"strings"
"time"
@@ -25,7 +26,7 @@ var (
defaultClient = common.SaneHttpClientTimeOut(time.Second * 10)
// Make sure that your group is surrounded in boundary characters such as below to reduce false positives.
keyPat = regexp.MustCompile(detectors.PrefixRegex([]string{"caflou"}) + `\b([a-bA-Z0-9\S]{155})\b`)
keyPat = regexp.MustCompile(`\b(eyJhbGciOiJIUzI1NiJ9[a-zA-Z0-9._-]{135})\b`)
)
// Keywords are used for efficiently pre-filtering chunks.
@@ -34,6 +35,14 @@ func (s Scanner) Keywords() []string {
return []string{"caflou"}
}
func (s Scanner) Type() detectorspb.DetectorType {
return detectorspb.DetectorType_Caflou
}
func (s Scanner) Description() string {
return "Caflou is a business management software used for managing projects, tasks, and finances. Caflou API keys can be used to access and modify this data."
}
// FromData will find and optionally verify Caflou secrets in a given set of bytes.
func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (results []detectors.Result, err error) {
dataStr := string(data)
@@ -54,18 +63,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
client = defaultClient
}
req, err := http.NewRequestWithContext(ctx, "GET", "https://app.caflou.com/api/v1/accounts", nil)
if err != nil {
continue
}
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch))
res, err := client.Do(req)
if err == nil {
defer res.Body.Close()
if res.StatusCode >= 200 && res.StatusCode < 300 {
s1.Verified = true
}
}
isVerified, verificationErr := verifyCaflou(ctx, client, resMatch)
s1.Verified = isVerified
s1.SetVerificationError(verificationErr, resMatch)
}
results = append(results, s1)
@@ -74,10 +74,29 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
return results, nil
}
func (s Scanner) Type() detectorspb.DetectorType {
return detectorspb.DetectorType_Caflou
}
func verifyCaflou(ctx context.Context, client *http.Client, token string) (bool, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://app.caflou.com/api/v1/accounts", http.NoBody)
if err != nil {
return false, err
}
func (s Scanner) Description() string {
return "Caflou is a business management software used for managing projects, tasks, and finances. Caflou API keys can be used to access and modify this data."
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token))
resp, err := client.Do(req)
if err != nil {
return false, err
}
defer func() {
_, _ = io.Copy(io.Discard, resp.Body)
_ = resp.Body.Close()
}()
switch resp.StatusCode {
case http.StatusOK:
return true, nil
case http.StatusUnauthorized:
return false, nil
default:
return false, fmt.Errorf("unexpected status code: %d", resp.StatusCode)
}
}
+19 -24
View File
@@ -10,27 +10,6 @@ import (
"github.com/trufflesecurity/trufflehog/v3/pkg/engine/ahocorasick"
)
var (
validPattern = `
# Configuration File: config.yaml
database:
host: $DB_HOST
port: $DB_PORT
username: $DB_USERNAME
password: $DB_PASS # IMPORTANT: Do not share this password publicly
api:
base_url: "https://api.example.com/instances"
api_key: $API_KEY
caflou_auth_token: "Bearer b8SQoPKLMCBbwIm0XDzbZiDydUk9qNqqBlKnR5Nouwbjs9cv3D1azAXpiFq9WrfNlwxbCwDL2FWCheXmdYKZkMRZklahJh5NQZZY7Zf220hjGJOtKgFbWxy9xQ9hodQqsOOx9Of30qtTrnRxFPa9wxYkSBn"
# Notes:
# - Remember to rotate the secret every 90 days.
# - The above credentials should only be used in a secure environment.
`
secret = "b8SQoPKLMCBbwIm0XDzbZiDydUk9qNqqBlKnR5Nouwbjs9cv3D1azAXpiFq9WrfNlwxbCwDL2FWCheXmdYKZkMRZklahJh5NQZZY7Zf220hjGJOtKgFbWxy9xQ9hodQqsOOx9Of30qtTrnRxFPa9wxYkSBn"
)
func TestCaflou_Pattern(t *testing.T) {
d := Scanner{}
ahoCorasickCore := ahocorasick.NewAhoCorasickCore([]detectors.Detector{d})
@@ -41,9 +20,25 @@ func TestCaflou_Pattern(t *testing.T) {
want []string
}{
{
name: "valid pattern",
input: validPattern,
want: []string{secret},
name: "valid pattern",
input: `
# Configuration File: config.yaml
database:
host: $DB_HOST
port: $DB_PORT
username: $DB_USERNAME
password: $DB_PASS # IMPORTANT: Do not share this password publicly
api:
base_url: "https://api.example.com/instances"
api_key: $API_KEY
caflou_auth_token: "Bearer eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX9lkIjo1OTQ5MCwianCpIjoiOTQwZjBlODkxNPhhZjM4OTQ1OGQwMDIxIiziZXhwIjoxGzU1MTk4MDAwfQ.EMNGCPX7aNIvriX360oLFAgMwHeXxKD7N4kdcJtPqTI"
# Notes:
# - Remember to rotate the secret every 90 days.
# - The above credentials should only be used in a secure environment.
`,
want: []string{"eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX9lkIjo1OTQ5MCwianCpIjoiOTQwZjBlODkxNPhhZjM4OTQ1OGQwMDIxIiziZXhwIjoxGzU1MTk4MDAwfQ.EMNGCPX7aNIvriX360oLFAgMwHeXxKD7N4kdcJtPqTI"},
},
}