diff --git a/pkg/detectors/appcues/appcues.go b/pkg/detectors/appcues/appcues.go
index f03984c9a..5f378d95e 100644
--- a/pkg/detectors/appcues/appcues.go
+++ b/pkg/detectors/appcues/appcues.go
@@ -3,6 +3,7 @@ package appcues
import (
"context"
"fmt"
+ "io"
"net/http"
"strings"
@@ -13,7 +14,7 @@ import (
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
)
-type Scanner struct{
+type Scanner struct {
detectors.DefaultMultiPartCredentialProvider
}
@@ -60,18 +61,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
RawV2: []byte(resMatch + resUserMatch),
}
if verify {
- req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://api.appcues.com/v2/accounts/%s/flows", resIdMatch), nil)
- if err != nil {
- continue
- }
- req.SetBasicAuth(resUserMatch, resMatch)
- res, err := client.Do(req)
- if err == nil {
- defer res.Body.Close()
- if res.StatusCode >= 200 && res.StatusCode < 300 {
- s1.Verified = true
- }
- }
+ isVerified, err := verifyMatch(ctx, client, resUserMatch, resMatch, resIdMatch)
+ s1.Verified = isVerified
+ s1.SetVerificationError(err, resUserMatch, resMatch, resIdMatch)
}
results = append(results, s1)
@@ -82,6 +74,31 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
return results, nil
}
+func verifyMatch(ctx context.Context, client *http.Client, resUserMatch, resMatch, resIdMatch string) (bool, error) {
+ // Reference: https://api.appcues.com/v2/docs?_gl=1#responses
+ req, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf("https://api.appcues.com/v2/accounts/%s/flows", resIdMatch), http.NoBody)
+ if err != nil {
+ return false, err
+ }
+ req.SetBasicAuth(resUserMatch, resMatch)
+ res, err := client.Do(req)
+ if err != nil {
+ return false, err
+ }
+ defer func() {
+ _, _ = io.Copy(io.Discard, res.Body)
+ _ = res.Body.Close()
+ }()
+ switch res.StatusCode {
+ case http.StatusOK:
+ return true, nil
+ case http.StatusUnauthorized, http.StatusForbidden, http.StatusBadRequest:
+ return false, nil
+ default:
+ return false, fmt.Errorf("unexpected status code: %d", res.StatusCode)
+ }
+}
+
func (s Scanner) Type() detectorspb.DetectorType {
return detectorspb.DetectorType_Appcues
}
diff --git a/pkg/detectors/appfollow/appfollow.go b/pkg/detectors/appfollow/appfollow.go
index 5f2765526..c3e439adc 100644
--- a/pkg/detectors/appfollow/appfollow.go
+++ b/pkg/detectors/appfollow/appfollow.go
@@ -2,6 +2,8 @@ package appfollow
import (
"context"
+ "fmt"
+ "io"
"net/http"
"strings"
@@ -45,18 +47,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
}
if verify {
- req, err := http.NewRequestWithContext(ctx, "GET", "https://api.appfollow.io/api/v2/account/users", nil)
- if err != nil {
- continue
- }
- req.Header.Add("X-AppFollow-API-Token", resMatch)
- res, err := client.Do(req)
- if err == nil {
- defer res.Body.Close()
- if res.StatusCode >= 200 && res.StatusCode < 300 {
- s1.Verified = true
- }
- }
+ isVerified, err := verifyMatch(ctx, client, resMatch)
+ s1.Verified = isVerified
+ s1.SetVerificationError(err, resMatch)
}
results = append(results, s1)
@@ -65,6 +58,31 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
return results, nil
}
+func verifyMatch(ctx context.Context, client *http.Client, token string) (bool, error) {
+ // Reference: https://docs.api.appfollow.io/reference/users_list_api_v2_account_users_get-1
+ req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://api.appfollow.io/api/v2/account/users", http.NoBody)
+ if err != nil {
+ return false, err
+ }
+ req.Header.Add("X-AppFollow-API-Token", token)
+ res, err := client.Do(req)
+ if err != nil {
+ return false, err
+ }
+ defer func() {
+ _, _ = io.Copy(io.Discard, res.Body)
+ _ = res.Body.Close()
+ }()
+ switch res.StatusCode {
+ case http.StatusOK, http.StatusPaymentRequired, http.StatusUnprocessableEntity:
+ return true, nil
+ case http.StatusUnauthorized:
+ return false, nil
+ default:
+ return false, fmt.Errorf("unexpected status code: %d", res.StatusCode)
+ }
+}
+
func (s Scanner) Type() detectorspb.DetectorType {
return detectorspb.DetectorType_Appfollow
}
diff --git a/pkg/detectors/appointedd/appointedd.go b/pkg/detectors/appointedd/appointedd.go
index 9c240ca6d..aed88100f 100644
--- a/pkg/detectors/appointedd/appointedd.go
+++ b/pkg/detectors/appointedd/appointedd.go
@@ -2,11 +2,13 @@ package appointedd
import (
"context"
- regexp "github.com/wasilibs/go-re2"
+ "fmt"
"io"
"net/http"
"strings"
+ regexp "github.com/wasilibs/go-re2"
+
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
@@ -44,24 +46,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
Raw: []byte(resMatch),
}
if verify {
- req, err := http.NewRequestWithContext(ctx, "GET", "https://api.appointedd.com/v1/availability/slots", nil)
- if err != nil {
- continue
- }
- req.Header.Add("X-API-KEY", resMatch)
- res, err := client.Do(req)
- if err == nil {
- defer res.Body.Close()
- bodyBytes, err := io.ReadAll(res.Body)
- if err != nil {
- continue
- }
- body := string(bodyBytes)
-
- if strings.Contains(body, "total") {
- s1.Verified = true
- }
- }
+ isVerified, err := verifyMatch(ctx, client, resMatch)
+ s1.Verified = isVerified
+ s1.SetVerificationError(err, resMatch)
}
results = append(results, s1)
@@ -70,6 +57,35 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
return results, nil
}
+func verifyMatch(ctx context.Context, client *http.Client, secret string) (bool, error) {
+ req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://api.appointedd.com/v1/availability/slots", http.NoBody)
+ if err != nil {
+ return false, err
+ }
+ req.Header.Add("X-API-KEY", secret)
+ res, err := client.Do(req)
+ if err != nil {
+ return false, err
+ }
+ defer func() {
+ _, _ = io.Copy(io.Discard, res.Body)
+ _ = res.Body.Close()
+ }()
+
+ switch res.StatusCode {
+ case http.StatusOK:
+ bodyBytes, err := io.ReadAll(res.Body)
+ if err != nil {
+ return false, err
+ }
+ return strings.Contains(string(bodyBytes), "total"), nil
+ case http.StatusUnauthorized, http.StatusForbidden:
+ return false, nil
+ default:
+ return false, fmt.Errorf("unexpected status code: %d", res.StatusCode)
+ }
+}
+
func (s Scanner) Type() detectorspb.DetectorType {
return detectorspb.DetectorType_Appointedd
}
diff --git a/pkg/detectors/appsynergy/appsynergy.go b/pkg/detectors/appsynergy/appsynergy.go
index c5014de6a..79bcb67e8 100644
--- a/pkg/detectors/appsynergy/appsynergy.go
+++ b/pkg/detectors/appsynergy/appsynergy.go
@@ -3,10 +3,12 @@ package appsynergy
import (
"context"
"fmt"
- regexp "github.com/wasilibs/go-re2"
+ "io"
"net/http"
"strings"
+ regexp "github.com/wasilibs/go-re2"
+
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
@@ -45,18 +47,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
}
if verify {
- payload := strings.NewReader(`{"html":"
Hello World
","filename":"HelloWorld.pdf"}`)
- req, err := http.NewRequestWithContext(ctx, "POST", fmt.Sprintf("https://www.appsynergy.com/api?action=HTML2PDF&apiKey=%s", resMatch), payload)
- if err != nil {
- continue
- }
- res, err := client.Do(req)
- if err == nil {
- defer res.Body.Close()
- if res.StatusCode >= 200 && res.StatusCode < 300 {
- s1.Verified = true
- }
- }
+ isVerified, err := verifyMatch(ctx, client, resMatch)
+ s1.Verified = isVerified
+ s1.SetVerificationError(err, resMatch)
}
results = append(results, s1)
@@ -65,6 +58,41 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
return results, nil
}
+func verifyMatch(ctx context.Context, client *http.Client, secret string) (bool, error) {
+ payload := strings.NewReader(`{"html":"Hello World
","filename":"HelloWorld.pdf"}`)
+ req, err := http.NewRequestWithContext(ctx, http.MethodPost, "https://www.appsynergy.com/api?action=HTML2PDF&apiKey="+secret, payload)
+ if err != nil {
+ return false, err
+ }
+ res, err := client.Do(req)
+ if err != nil {
+ return false, err
+ }
+ defer func() {
+ _, _ = io.Copy(io.Discard, res.Body)
+ _ = res.Body.Close()
+ }()
+
+ switch res.StatusCode {
+ case http.StatusOK:
+ return true, nil
+ case http.StatusUnauthorized:
+ return false, nil
+ case http.StatusBadRequest:
+ bodyBytes, err := io.ReadAll(res.Body)
+ if err != nil {
+ return false, err
+ }
+ body := string(bodyBytes)
+ if strings.Contains(body, "Invalid API Key") {
+ return false, nil
+ }
+ return false, fmt.Errorf("status bad request invalid api key message not found: %d", res.StatusCode)
+ default:
+ return false, fmt.Errorf("unexpected status code: %d", res.StatusCode)
+ }
+}
+
func (s Scanner) Type() detectorspb.DetectorType {
return detectorspb.DetectorType_AppSynergy
}
diff --git a/pkg/detectors/apptivo/apptivo.go b/pkg/detectors/apptivo/apptivo.go
index bd68fc5c1..eb7882ecb 100644
--- a/pkg/detectors/apptivo/apptivo.go
+++ b/pkg/detectors/apptivo/apptivo.go
@@ -14,7 +14,7 @@ import (
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
)
-type Scanner struct{
+type Scanner struct {
detectors.DefaultMultiPartCredentialProvider
}
@@ -54,27 +54,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
}
if verify {
- req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://api.apptivo.com/app/dao/v6/leads?a=getConfigData&apiKey=%s&accessKey=%s", resMatch, resIdMatch), nil)
- if err != nil {
- continue
- }
- res, err := client.Do(req)
- if err == nil {
- bodyBytes, err := io.ReadAll(res.Body)
- if err != nil {
- continue
- }
- bodyString := string(bodyBytes)
- validResponse := strings.Contains(bodyString, `displayName`)
- defer res.Body.Close()
- if res.StatusCode >= 200 && res.StatusCode < 300 {
- if validResponse {
- s1.Verified = true
- } else {
- s1.Verified = false
- }
- }
- }
+ isVerified, err := verifyMatch(ctx, client, resMatch, resIdMatch)
+ s1.Verified = isVerified
+ s1.SetVerificationError(err, resMatch, resIdMatch)
}
results = append(results, s1)
@@ -84,6 +66,32 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
return results, nil
}
+func verifyMatch(ctx context.Context, client *http.Client, apiKey, accessKey string) (bool, error) {
+ req, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf("https://api.apptivo.com/app/dao/v6/leads?a=getConfigData&apiKey=%s&accessKey=%s", apiKey, accessKey), http.NoBody)
+ if err != nil {
+ return false, err
+ }
+ res, err := client.Do(req)
+ if err != nil {
+ return false, err
+ }
+ defer func() {
+ _, _ = io.Copy(io.Discard, res.Body)
+ _ = res.Body.Close()
+ }()
+
+ switch res.StatusCode {
+ case http.StatusOK:
+ bodyBytes, err := io.ReadAll(res.Body)
+ if err != nil {
+ return false, err
+ }
+ return strings.Contains(string(bodyBytes), `displayName`), nil
+ default:
+ return false, fmt.Errorf("unexpected status code %d", res.StatusCode)
+ }
+}
+
func (s Scanner) Type() detectorspb.DetectorType {
return detectorspb.DetectorType_Apptivo
}
diff --git a/pkg/detectors/artsy/artsy.go b/pkg/detectors/artsy/artsy.go
index 6da207de1..89b6f8eaf 100644
--- a/pkg/detectors/artsy/artsy.go
+++ b/pkg/detectors/artsy/artsy.go
@@ -2,6 +2,8 @@ package artsy
import (
"context"
+ "fmt"
+ "io"
"net/http"
"strings"
@@ -54,17 +56,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
}
if verify {
- req, err := http.NewRequestWithContext(ctx, "POST", "https://api.artsy.net/api/tokens/xapp_token?client_id="+resIdMatch+"&client_secret="+resMatch, nil)
- if err != nil {
- continue
- }
- res, err := client.Do(req)
- if err == nil {
- defer res.Body.Close()
- if res.StatusCode >= 200 && res.StatusCode < 300 {
- s1.Verified = true
- }
- }
+ isVerified, err := verifyMatch(ctx, client, resIdMatch, resMatch)
+ s1.Verified = isVerified
+ s1.SetVerificationError(err, resMatch)
}
results = append(results, s1)
@@ -75,6 +69,30 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
return results, nil
}
+func verifyMatch(ctx context.Context, client *http.Client, id, secret string) (bool, error) {
+ // Reference: https://developers.artsy.net/v2/docs/authentication
+ req, err := http.NewRequestWithContext(ctx, http.MethodPost, "https://api.artsy.net/api/tokens/xapp_token?client_id="+id+"&client_secret="+secret, http.NoBody)
+ if err != nil {
+ return false, err
+ }
+ res, err := client.Do(req)
+ if err != nil {
+ return false, err
+ }
+ defer func() {
+ _, _ = io.Copy(io.Discard, res.Body)
+ _ = res.Body.Close()
+ }()
+ switch res.StatusCode {
+ case http.StatusCreated:
+ return true, nil
+ case http.StatusUnauthorized:
+ return false, nil
+ default:
+ return false, fmt.Errorf("unexpected status code %d", res.StatusCode)
+ }
+}
+
func (s Scanner) Type() detectorspb.DetectorType {
return detectorspb.DetectorType_Artsy
}
diff --git a/pkg/detectors/asanaoauth/asanaoauth.go b/pkg/detectors/asanaoauth/asanaoauth.go
index 85bcfa31f..83de2aef1 100644
--- a/pkg/detectors/asanaoauth/asanaoauth.go
+++ b/pkg/detectors/asanaoauth/asanaoauth.go
@@ -3,6 +3,7 @@ package asanaoauth
import (
"context"
"fmt"
+ "io"
"net/http"
"strings"
@@ -46,19 +47,10 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
}
if verify {
- req, err := http.NewRequestWithContext(ctx, "GET", "https://app.asana.com/api/1.0/users/me", 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
- s1.AnalysisInfo = map[string]string{"key": resMatch}
- }
- }
+ isVerified, err := verifyMatch(ctx, client, resMatch)
+ s1.Verified = isVerified
+ s1.SetVerificationError(err, resMatch)
+ s1.AnalysisInfo = map[string]string{"key": resMatch}
}
results = append(results, s1)
@@ -67,6 +59,30 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
return results, nil
}
+func verifyMatch(ctx context.Context, client *http.Client, token string) (bool, error) {
+ req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://app.asana.com/api/1.0/users/me", http.NoBody)
+ if err != nil {
+ return false, err
+ }
+ req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token))
+ res, err := client.Do(req)
+ if err != nil {
+ return false, err
+ }
+ defer func() {
+ _, _ = io.Copy(io.Discard, res.Body)
+ _ = res.Body.Close()
+ }()
+ switch res.StatusCode {
+ case http.StatusOK:
+ return true, nil
+ case http.StatusUnauthorized:
+ return false, nil
+ default:
+ return false, fmt.Errorf("unexpected status code %d", res.StatusCode)
+ }
+}
+
func (s Scanner) Type() detectorspb.DetectorType {
return detectorspb.DetectorType_AsanaOauth
}
diff --git a/pkg/detectors/asanapersonalaccesstoken/asanapersonalaccesstoken.go b/pkg/detectors/asanapersonalaccesstoken/asanapersonalaccesstoken.go
index 050ea1b6f..abd1ed75e 100644
--- a/pkg/detectors/asanapersonalaccesstoken/asanapersonalaccesstoken.go
+++ b/pkg/detectors/asanapersonalaccesstoken/asanapersonalaccesstoken.go
@@ -3,10 +3,12 @@ package asanapersonalaccesstoken
import (
"context"
"fmt"
- regexp "github.com/wasilibs/go-re2"
+ "io"
"net/http"
"strings"
+ regexp "github.com/wasilibs/go-re2"
+
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
@@ -44,18 +46,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
}
if verify {
- req, err := http.NewRequestWithContext(ctx, "GET", "https://app.asana.com/api/1.0/users/me", 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, err := verifyMatch(ctx, client, resMatch)
+ s1.Verified = isVerified
+ s1.SetVerificationError(err, resMatch)
}
results = append(results, s1)
@@ -64,6 +57,30 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
return results, nil
}
+func verifyMatch(ctx context.Context, client *http.Client, token string) (bool, error) {
+ req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://app.asana.com/api/1.0/users/me", http.NoBody)
+ if err != nil {
+ return false, err
+ }
+ req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token))
+ res, err := client.Do(req)
+ if err != nil {
+ return false, err
+ }
+ defer func() {
+ _, _ = io.Copy(io.Discard, res.Body)
+ _ = res.Body.Close()
+ }()
+ switch res.StatusCode {
+ case http.StatusOK:
+ return true, nil
+ case http.StatusUnauthorized:
+ return false, nil
+ default:
+ return false, fmt.Errorf("unexpected status code %d", res.StatusCode)
+ }
+}
+
func (s Scanner) Type() detectorspb.DetectorType {
return detectorspb.DetectorType_AsanaPersonalAccessToken
}
diff --git a/pkg/detectors/assemblyai/assemblyai.go b/pkg/detectors/assemblyai/assemblyai.go
index 47ef5db0d..73441ccbb 100644
--- a/pkg/detectors/assemblyai/assemblyai.go
+++ b/pkg/detectors/assemblyai/assemblyai.go
@@ -2,10 +2,13 @@ package assemblyai
import (
"context"
- regexp "github.com/wasilibs/go-re2"
+ "fmt"
+ "io"
"net/http"
"strings"
+ regexp "github.com/wasilibs/go-re2"
+
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
@@ -44,19 +47,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
}
if verify {
- req, err := http.NewRequestWithContext(ctx, "GET", "https://api.assemblyai.com/v2/transcript", nil)
- if err != nil {
- continue
- }
- req.Header.Add("Content-Type", "application/json")
- req.Header.Add("Authorization", resMatch)
- res, err := client.Do(req)
- if err == nil {
- defer res.Body.Close()
- if res.StatusCode >= 200 && res.StatusCode < 300 {
- s1.Verified = true
- }
- }
+ isVerified, err := verifyMatch(ctx, client, resMatch)
+ s1.Verified = isVerified
+ s1.SetVerificationError(err, resMatch)
}
results = append(results, s1)
@@ -65,6 +58,31 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
return results, nil
}
+func verifyMatch(ctx context.Context, client *http.Client, token string) (bool, error) {
+ req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://api.assemblyai.com/v2/transcript", http.NoBody)
+ if err != nil {
+ return false, err
+ }
+ req.Header.Add("Authorization", token)
+ res, err := client.Do(req)
+ if err != nil {
+ return false, err
+ }
+ defer func() {
+ _, _ = io.Copy(io.Discard, res.Body)
+ _ = res.Body.Close()
+ }()
+
+ switch res.StatusCode {
+ case http.StatusOK:
+ return true, nil
+ case http.StatusUnauthorized:
+ return false, nil
+ default:
+ return false, fmt.Errorf("unexpected status code %d", res.StatusCode)
+ }
+}
+
func (s Scanner) Type() detectorspb.DetectorType {
return detectorspb.DetectorType_AssemblyAI
}
diff --git a/pkg/detectors/atera/atera.go b/pkg/detectors/atera/atera.go
index cc7eba7a4..d18650490 100644
--- a/pkg/detectors/atera/atera.go
+++ b/pkg/detectors/atera/atera.go
@@ -2,10 +2,13 @@ package atera
import (
"context"
- regexp "github.com/wasilibs/go-re2"
+ "fmt"
+ "io"
"net/http"
"strings"
+ regexp "github.com/wasilibs/go-re2"
+
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
@@ -44,19 +47,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
}
if verify {
- req, err := http.NewRequestWithContext(ctx, "GET", "https://app.atera.com/api/v3/alerts", nil)
- if err != nil {
- continue
- }
- req.Header.Add("Accept", "application/json")
- req.Header.Add("X-API-KEY", resMatch)
- res, err := client.Do(req)
- if err == nil {
- defer res.Body.Close()
- if res.StatusCode >= 200 && res.StatusCode < 300 {
- s1.Verified = true
- }
- }
+ isVerified, err := verifyMatch(ctx, client, resMatch)
+ s1.Verified = isVerified
+ s1.SetVerificationError(err, resMatch)
}
results = append(results, s1)
@@ -65,6 +58,31 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
return results, nil
}
+func verifyMatch(ctx context.Context, client *http.Client, token string) (bool, error) {
+ req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://app.atera.com/api/v3/alerts", http.NoBody)
+ if err != nil {
+ return false, err
+ }
+ req.Header.Add("Accept", "application/json")
+ req.Header.Add("X-API-KEY", token)
+ res, err := client.Do(req)
+ if err != nil {
+ return false, err
+ }
+ defer func() {
+ _, _ = io.Copy(io.Discard, res.Body)
+ _ = res.Body.Close()
+ }()
+ switch res.StatusCode {
+ case http.StatusOK:
+ return true, nil
+ case http.StatusUnauthorized:
+ return false, nil
+ default:
+ return false, fmt.Errorf("unexpected status code %d", res.StatusCode)
+ }
+}
+
func (s Scanner) Type() detectorspb.DetectorType {
return detectorspb.DetectorType_Atera
}
diff --git a/pkg/detectors/auth0managementapitoken/auth0managementapitoken.go b/pkg/detectors/auth0managementapitoken/auth0managementapitoken.go
index d7058f384..bffa672e9 100644
--- a/pkg/detectors/auth0managementapitoken/auth0managementapitoken.go
+++ b/pkg/detectors/auth0managementapitoken/auth0managementapitoken.go
@@ -3,6 +3,7 @@ package auth0managementapitoken
import (
"context"
"fmt"
+ "io"
"net/http"
"strings"
@@ -61,22 +62,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
}
if verify {
- /*
- curl -H "Authorization: Bearer $token" https://domain/api/v2/users
- */
-
- req, err := http.NewRequestWithContext(ctx, "GET", "https://"+domainRes+"/api/v2/users", nil)
- if err != nil {
- continue
- }
- req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", managementAPITokenRes))
- res, err := client.Do(req)
- if err == nil {
- defer res.Body.Close()
- if res.StatusCode >= 200 && res.StatusCode < 300 {
- s1.Verified = true
- }
- }
+ isVerified, err := verifyMatch(ctx, client, managementAPITokenRes, domainRes)
+ s1.Verified = isVerified
+ s1.SetVerificationError(err, managementAPITokenRes)
}
results = append(results, s1)
@@ -86,6 +74,34 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
return results, nil
}
+func verifyMatch(ctx context.Context, client *http.Client, token, domain string) (bool, error) {
+ /*
+ curl -H "Authorization: Bearer $token" https://domain/api/v2/users
+ Reference: https://auth0.com/docs/api/management/v2/users/get-users
+ */
+ req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://"+domain+"/api/v2/users", http.NoBody)
+ if err != nil {
+ return false, err
+ }
+ req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token))
+ res, err := client.Do(req)
+ if err != nil {
+ return false, err
+ }
+ defer func() {
+ _, _ = io.Copy(io.Discard, res.Body)
+ _ = res.Body.Close()
+ }()
+ switch res.StatusCode {
+ case http.StatusOK, http.StatusForbidden:
+ return true, nil
+ case http.StatusUnauthorized:
+ return false, nil
+ default:
+ return false, fmt.Errorf("unexpected status code %d", res.StatusCode)
+ }
+}
+
func (s Scanner) Type() detectorspb.DetectorType {
return detectorspb.DetectorType_Auth0ManagementApiToken
}
diff --git a/pkg/detectors/autodesk/autodesk.go b/pkg/detectors/autodesk/autodesk.go
index e30f6ae99..43f391abf 100644
--- a/pkg/detectors/autodesk/autodesk.go
+++ b/pkg/detectors/autodesk/autodesk.go
@@ -13,7 +13,7 @@ import (
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
)
-type Scanner struct{
+type Scanner struct {
detectors.DefaultMultiPartCredentialProvider
}
diff --git a/pkg/detectors/autoklose/autoklose.go b/pkg/detectors/autoklose/autoklose.go
index a34982cc6..e675257af 100644
--- a/pkg/detectors/autoklose/autoklose.go
+++ b/pkg/detectors/autoklose/autoklose.go
@@ -48,38 +48,10 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
}
if verify {
- // API Documentation: https://api.aklab.xyz/#auth-info-fd71acd1-2e41-4991-8789-3edfd258479a
- req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://api.autoklose.com/api/me/?api_token=%s", resMatch), nil)
- if err != nil {
- continue
- }
- req.Header.Add("Accept", "application/json")
- res, err := client.Do(req)
- if err == nil {
- defer func() {
- _, _ = io.Copy(io.Discard, res.Body)
- _ = res.Body.Close()
- }()
-
- if res.StatusCode == http.StatusOK {
- s1.Verified = true
- bodyBytes, err := io.ReadAll(res.Body)
- if err != nil {
- continue
- }
-
- var responseBody map[string]interface{}
- if err := json.Unmarshal(bodyBytes, &responseBody); err == nil {
- if email, ok := responseBody["email"].(string); ok {
- s1.ExtraData = map[string]string{
- "email": email,
- }
- }
- }
- }
- } else {
- s1.SetVerificationError(err, resMatch)
- }
+ isVerified, extraData, err := verifyMatch(ctx, client, resMatch)
+ s1.Verified = isVerified
+ s1.ExtraData = extraData
+ s1.SetVerificationError(err, resMatch)
}
results = append(results, s1)
@@ -88,6 +60,46 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
return results, nil
}
+func verifyMatch(ctx context.Context, client *http.Client, token string) (bool, map[string]string, error) {
+ // API Documentation: https://api.aklab.xyz/#auth-info-fd71acd1-2e41-4991-8789-3edfd258479a
+ url := fmt.Sprintf("https://api.autoklose.com/api/me/?api_token=%s", token)
+ req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, http.NoBody)
+ if err != nil {
+ return false, nil, err
+ }
+ req.Header.Add("Accept", "application/json")
+ res, err := client.Do(req)
+ if err != nil {
+ return false, nil, err
+ }
+ defer func() {
+ _, _ = io.Copy(io.Discard, res.Body)
+ _ = res.Body.Close()
+ }()
+
+ switch res.StatusCode {
+ case http.StatusOK:
+ bodyBytes, err := io.ReadAll(res.Body)
+ if err != nil {
+ return false, nil, err
+ }
+
+ var responseBody map[string]interface{}
+ if err := json.Unmarshal(bodyBytes, &responseBody); err != nil {
+ return false, nil, err
+ }
+
+ if email, ok := responseBody["email"].(string); ok {
+ return true, map[string]string{"email": email}, nil
+ }
+ return true, nil, nil
+ case http.StatusUnauthorized:
+ return false, nil, nil
+ default:
+ return false, nil, fmt.Errorf("unexpected status code: %d", res.StatusCode)
+ }
+}
+
func (s Scanner) Type() detectorspb.DetectorType {
return detectorspb.DetectorType_Autoklose
}
diff --git a/pkg/detectors/avazapersonalaccesstoken/avazapersonalaccesstoken.go b/pkg/detectors/avazapersonalaccesstoken/avazapersonalaccesstoken.go
index 942742190..d9fcbadf8 100644
--- a/pkg/detectors/avazapersonalaccesstoken/avazapersonalaccesstoken.go
+++ b/pkg/detectors/avazapersonalaccesstoken/avazapersonalaccesstoken.go
@@ -3,10 +3,12 @@ package avazapersonalaccesstoken
import (
"context"
"fmt"
- regexp "github.com/wasilibs/go-re2"
+ "io"
"net/http"
"strings"
+ regexp "github.com/wasilibs/go-re2"
+
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
@@ -46,18 +48,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
}
if verify {
- req, err := http.NewRequestWithContext(ctx, "GET", "https://api.avaza.com/api/Account", 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, err := verifyMatch(ctx, client, resMatch)
+ s1.Verified = isVerified
+ s1.SetVerificationError(err, resMatch)
}
results = append(results, s1)
@@ -66,6 +59,32 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
return results, nil
}
+func verifyMatch(ctx context.Context, client *http.Client, token string) (bool, error) {
+ // API Documentation: https://api.avaza.com/swagger/ui/index#!/Account/Account_Get
+ req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://api.avaza.com/api/Account", http.NoBody)
+ if err != nil {
+ return false, err
+ }
+ req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", token))
+ res, err := client.Do(req)
+ if err != nil {
+ return false, err
+ }
+ defer func() {
+ _, _ = io.Copy(io.Discard, res.Body)
+ _ = res.Body.Close()
+ }()
+
+ switch res.StatusCode {
+ case http.StatusOK:
+ return true, nil
+ case http.StatusUnauthorized:
+ return false, nil
+ default:
+ return false, fmt.Errorf("unexpected status code: %d", res.StatusCode)
+ }
+}
+
func (s Scanner) Type() detectorspb.DetectorType {
return detectorspb.DetectorType_AvazaPersonalAccessToken
}
diff --git a/pkg/detectors/aviationstack/aviationstack.go b/pkg/detectors/aviationstack/aviationstack.go
index 805ac3c77..8626e9ac1 100644
--- a/pkg/detectors/aviationstack/aviationstack.go
+++ b/pkg/detectors/aviationstack/aviationstack.go
@@ -3,11 +3,13 @@ package aviationstack
import (
"context"
"fmt"
- regexp "github.com/wasilibs/go-re2"
+ "io"
"net/http"
"strings"
"time"
+ regexp "github.com/wasilibs/go-re2"
+
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
@@ -46,19 +48,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
}
if verify {
- timeout := 10 * time.Second
- client.Timeout = timeout
- req, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("https://api.aviationstack.com/v1/flights?access_key=%s", resMatch), nil)
- if err != nil {
- continue
- }
- res, err := client.Do(req)
- if err == nil {
- defer res.Body.Close()
- if res.StatusCode >= 200 && res.StatusCode < 300 {
- s1.Verified = true
- }
- }
+ isVerified, err := verifyMatch(ctx, client, resMatch)
+ s1.Verified = isVerified
+ s1.SetVerificationError(err, resMatch)
}
results = append(results, s1)
@@ -67,6 +59,32 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
return results, nil
}
+func verifyMatch(ctx context.Context, client *http.Client, token string) (bool, error) {
+ client.Timeout = 10 * time.Second
+ url := fmt.Sprintf("https://api.aviationstack.com/v1/flights?access_key=%s", token)
+ req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, http.NoBody)
+ if err != nil {
+ return false, err
+ }
+ res, err := client.Do(req)
+ if err != nil {
+ return false, err
+ }
+ defer func() {
+ _, _ = io.Copy(io.Discard, res.Body)
+ _ = res.Body.Close()
+ }()
+
+ switch res.StatusCode {
+ case http.StatusOK:
+ return true, nil
+ case http.StatusUnauthorized:
+ return false, nil
+ default:
+ return false, fmt.Errorf("unexpected status code: %d", res.StatusCode)
+ }
+}
+
func (s Scanner) Type() detectorspb.DetectorType {
return detectorspb.DetectorType_AviationStack
}
diff --git a/pkg/detectors/axonaut/axonaut.go b/pkg/detectors/axonaut/axonaut.go
index 194c0329d..04a4549f4 100644
--- a/pkg/detectors/axonaut/axonaut.go
+++ b/pkg/detectors/axonaut/axonaut.go
@@ -2,10 +2,13 @@ package axonaut
import (
"context"
- regexp "github.com/wasilibs/go-re2"
+ "fmt"
+ "io"
"net/http"
"strings"
+ regexp "github.com/wasilibs/go-re2"
+
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
@@ -44,18 +47,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
}
if verify {
- req, err := http.NewRequestWithContext(ctx, "GET", "https://axonaut.com/api/v2/companies?type=all&sort=id", nil)
- if err != nil {
- continue
- }
- req.Header.Add("userApiKey", resMatch)
- res, err := client.Do(req)
- if err == nil {
- defer res.Body.Close()
- if res.StatusCode >= 200 && res.StatusCode < 300 {
- s1.Verified = true
- }
- }
+ isVerified, err := verifyMatch(ctx, client, resMatch)
+ s1.Verified = isVerified
+ s1.SetVerificationError(err, resMatch)
}
results = append(results, s1)
@@ -64,6 +58,31 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
return results, nil
}
+func verifyMatch(ctx context.Context, client *http.Client, key string) (bool, error) {
+ req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://axonaut.com/api/v2/companies?type=all&sort=id", http.NoBody)
+ if err != nil {
+ return false, err
+ }
+ req.Header.Add("userApiKey", key)
+ res, err := client.Do(req)
+ if err != nil {
+ return false, err
+ }
+ defer func() {
+ _, _ = io.Copy(io.Discard, res.Body)
+ _ = res.Body.Close()
+ }()
+
+ switch res.StatusCode {
+ case http.StatusOK:
+ return true, nil
+ case http.StatusForbidden:
+ return false, nil
+ default:
+ return false, fmt.Errorf("unexpected status code: %d", res.StatusCode)
+ }
+}
+
func (s Scanner) Type() detectorspb.DetectorType {
return detectorspb.DetectorType_Axonaut
}
diff --git a/pkg/detectors/aylien/aylien.go b/pkg/detectors/aylien/aylien.go
index f40c7424f..1006b7f59 100644
--- a/pkg/detectors/aylien/aylien.go
+++ b/pkg/detectors/aylien/aylien.go
@@ -2,6 +2,8 @@ package aylien
import (
"context"
+ "fmt"
+ "io"
"net/http"
"strings"
@@ -12,7 +14,7 @@ import (
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
)
-type Scanner struct{
+type Scanner struct {
detectors.DefaultMultiPartCredentialProvider
}
@@ -52,19 +54,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
RawV2: []byte(resMatch + resIdMatch),
}
if verify {
- req, err := http.NewRequestWithContext(ctx, "GET", "https://api.aylien.com/news/stories", nil)
- if err != nil {
- continue
- }
- req.Header.Add("X-AYLIEN-NewsAPI-Application-ID", resIdMatch)
- req.Header.Add("X-AYLIEN-NewsAPI-Application-Key", resMatch)
- res, err := client.Do(req)
- if err == nil {
- defer res.Body.Close()
- if res.StatusCode >= 200 && res.StatusCode < 300 {
- s1.Verified = true
- }
- }
+ isVerified, err := verifyMatch(ctx, client, resIdMatch, resMatch)
+ s1.Verified = isVerified
+ s1.SetVerificationError(err, resMatch)
}
results = append(results, s1)
@@ -74,6 +66,32 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
return results, nil
}
+func verifyMatch(ctx context.Context, client *http.Client, id, key string) (bool, error) {
+ req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://api.aylien.com/news/stories", http.NoBody)
+ if err != nil {
+ return false, err
+ }
+ req.Header.Add("X-AYLIEN-NewsAPI-Application-ID", id)
+ req.Header.Add("X-AYLIEN-NewsAPI-Application-Key", key)
+ res, err := client.Do(req)
+ if err != nil {
+ return false, err
+ }
+ defer func() {
+ _, _ = io.Copy(io.Discard, res.Body)
+ _ = res.Body.Close()
+ }()
+
+ switch res.StatusCode {
+ case http.StatusOK:
+ return true, nil
+ case http.StatusUnauthorized:
+ return false, nil
+ default:
+ return false, fmt.Errorf("unexpected status code: %d", res.StatusCode)
+ }
+}
+
func (s Scanner) Type() detectorspb.DetectorType {
return detectorspb.DetectorType_Aylien
}
diff --git a/pkg/detectors/ayrshare/ayrshare.go b/pkg/detectors/ayrshare/ayrshare.go
index df60114f5..6ac4e3ae0 100644
--- a/pkg/detectors/ayrshare/ayrshare.go
+++ b/pkg/detectors/ayrshare/ayrshare.go
@@ -48,37 +48,10 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
}
if verify {
- req, err := http.NewRequestWithContext(ctx, "GET", "https://app.ayrshare.com/api/user", nil)
- if err != nil {
- continue
- }
- req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", resMatch))
- res, err := client.Do(req)
- if err == nil {
- defer func() {
- _, _ = io.Copy(io.Discard, res.Body)
- _ = res.Body.Close()
- }()
-
- if res.StatusCode == http.StatusOK {
- s1.Verified = true
- bodyBytes, err := io.ReadAll(res.Body)
- if err != nil {
- continue
- }
-
- var responseBody map[string]interface{}
- if err := json.Unmarshal(bodyBytes, &responseBody); err == nil {
- if email, ok := responseBody["email"].(string); ok {
- s1.ExtraData = map[string]string{
- "email": email,
- }
- }
- }
- }
- } else {
- s1.SetVerificationError(err, resMatch)
- }
+ isVerified, extraData, err := verifyMatch(ctx, client, resMatch)
+ s1.Verified = isVerified
+ s1.ExtraData = extraData
+ s1.SetVerificationError(err, resMatch)
}
results = append(results, s1)
@@ -87,6 +60,51 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
return results, nil
}
+func verifyMatch(ctx context.Context, client *http.Client, key string) (bool, map[string]string, error) {
+ // Reference: https://www.ayrshare.com/docs/apis/user/profile-details
+ req, err := http.NewRequestWithContext(ctx, http.MethodGet, "https://app.ayrshare.com/api/user", http.NoBody)
+ if err != nil {
+ return false, nil, err
+ }
+ req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", key))
+ res, err := client.Do(req)
+ if err != nil {
+ return false, nil, err
+ }
+ defer func() {
+ _, _ = io.Copy(io.Discard, res.Body)
+ _ = res.Body.Close()
+ }()
+
+ switch res.StatusCode {
+ case http.StatusOK:
+ bodyBytes, err := io.ReadAll(res.Body)
+ if err != nil {
+ return false, nil, err
+ }
+ var responseBody map[string]any
+ if err := json.Unmarshal(bodyBytes, &responseBody); err == nil {
+ if email, ok := responseBody["email"].(string); ok {
+ return true, map[string]string{"email": email}, nil
+ }
+ }
+ return true, nil, nil
+ case http.StatusUnauthorized:
+ return false, nil, nil
+ case http.StatusForbidden:
+ // Invalid Bearer tokens get a 403 Forbidden response despite what is stated in the docs.
+ // Documentation: https://www.ayrshare.com/docs/errors/errors-http
+ bodyBytes, err := io.ReadAll(res.Body)
+ if err != nil {
+ return false, nil, err
+ }
+ if strings.Contains(string(bodyBytes), "API Key not valid") {
+ return false, nil, nil
+ }
+ }
+ return false, nil, fmt.Errorf("unexpected status code: %d", res.StatusCode)
+}
+
func (s Scanner) Type() detectorspb.DetectorType {
return detectorspb.DetectorType_Ayrshare
}
diff --git a/pkg/detectors/azure_batch/azurebatch.go b/pkg/detectors/azure_batch/azurebatch.go
index 8165dfb44..ad2110da2 100644
--- a/pkg/detectors/azure_batch/azurebatch.go
+++ b/pkg/detectors/azure_batch/azurebatch.go
@@ -65,35 +65,9 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
if client == nil {
client = defaultClient
}
- url := fmt.Sprintf("%s/applications?api-version=2020-09-01.12.0", endpoint)
- date := time.Now().UTC().Format(http.TimeFormat)
- stringToSign := fmt.Sprintf(
- "GET\n\n\n\n\napplication/json\n%s\n\n\n\n\n\n%s\napi-version:%s",
- date,
- strings.ToLower(fmt.Sprintf("/%s/applications", accountName)),
- "2020-09-01.12.0",
- )
- key, _ := base64.StdEncoding.DecodeString(accountKey)
- h := hmac.New(sha256.New, key)
- h.Write([]byte(stringToSign))
- signature := base64.StdEncoding.EncodeToString(h.Sum(nil))
- req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
- if err != nil {
- continue
- }
- req.Header.Set("Content-Type", "application/json")
- req.Header.Set("Authorization", fmt.Sprintf("SharedKey %s:%s", accountName, signature))
- req.Header.Set("Date", date)
- resp, err := client.Do(req)
- if err != nil {
- continue
- }
- defer resp.Body.Close()
-
- if resp.StatusCode == http.StatusOK {
- s1.Verified = true
- }
-
+ isVerified, err := verifyMatch(ctx, client, endpoint, accountName, accountKey)
+ s1.Verified = isVerified
+ s1.SetVerificationError(err)
}
results = append(results, s1)
@@ -106,6 +80,51 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
return results, nil
}
+func verifyMatch(ctx context.Context, client *http.Client, endpoint, accountName, accountKey string) (bool, error) {
+ // Reference: https://learn.microsoft.com/en-us/rest/api/batchservice/application/list
+ url := fmt.Sprintf("%s/applications?api-version=2020-09-01.12.0", endpoint)
+
+ date := time.Now().UTC().Format(http.TimeFormat)
+ stringToSign := fmt.Sprintf(
+ "GET\n\n\n\n\napplication/json\n%s\n\n\n\n\n\n%s\napi-version:%s",
+ date,
+ strings.ToLower(fmt.Sprintf("/%s/applications", accountName)),
+ "2020-09-01.12.0",
+ )
+ key, _ := base64.StdEncoding.DecodeString(accountKey)
+ h := hmac.New(sha256.New, key)
+ h.Write([]byte(stringToSign))
+ signature := base64.StdEncoding.EncodeToString(h.Sum(nil))
+
+ req, err := http.NewRequestWithContext(ctx, http.MethodGet, url, http.NoBody)
+ if err != nil {
+ return false, err
+ }
+
+ req.Header.Set("Content-Type", "application/json")
+ req.Header.Set("Authorization", fmt.Sprintf("SharedKey %s:%s", accountName, signature))
+ req.Header.Set("Date", date)
+ resp, err := client.Do(req)
+ if err != nil {
+ // If the host is not found, we can assume that the endpoint is invalid
+ if strings.Contains(err.Error(), "no such host") {
+ return false, nil
+ }
+ return false, err
+ }
+ defer resp.Body.Close()
+
+ switch resp.StatusCode {
+ case http.StatusOK:
+ return true, nil
+ case http.StatusForbidden:
+ // Key is either invalid or the account is disabled.
+ return false, nil
+ default:
+ return false, fmt.Errorf("unexpected status code %d for %s", resp.StatusCode, url)
+ }
+}
+
func (s Scanner) IsFalsePositive(_ detectors.Result) (bool, string) {
return false, ""
}