55 "fmt"
66 "io"
77 "net/http"
8- "strings"
98
109 regexp "github.com/wasilibs/go-re2"
1110
@@ -14,57 +13,54 @@ import (
1413 "github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
1514)
1615
17- type Scanner struct {}
16+ type Scanner struct {
17+ client * http.Client
18+ }
1819
1920// Ensure the Scanner satisfies the interface at compile time
2021var _ detectors.Detector = (* Scanner )(nil )
2122
2223var (
23- client = common .SaneHttpClient ()
24+ defaultClient = common .SaneHttpClient ()
2425
2526 // Make sure that your group is surrounded in boundary characters such as below to reduce false positives
26- keyPat = regexp .MustCompile (detectors . PrefixRegex ([] string { "flightlabs" }) + `\b(ey[a-zA-Z0-9]{34}.ey [a-zA-Z0-9._ -]{300,350 })\b` )
27+ keyPat = regexp .MustCompile (`\b(eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9\. ey[a-zA-Z0-9_-]+\. [a-zA-Z0-9_ -]{86 })\b` )
2728)
2829
2930// Keywords are used for efficiently pre-filtering chunks.
3031// Use identifiers in the secret preferably, or the provider name.
3132func (s Scanner ) Keywords () []string {
32- return []string {"flightlabs" }
33+ return []string {"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9" }
34+ }
35+
36+ func (s Scanner ) getClient () * http.Client {
37+ client := s .client
38+ if client == nil {
39+ client = defaultClient
40+ }
41+
42+ return client
3343}
3444
3545// FromData will find and optionally verify FlightLabs secrets in a given set of bytes.
3646func (s Scanner ) FromData (ctx context.Context , verify bool , data []byte ) (results []detectors.Result , err error ) {
3747 dataStr := string (data )
3848
39- matches := keyPat . FindAllStringSubmatch ( dataStr , - 1 )
40-
41- for _ , match := range matches {
42- resMatch := strings . TrimSpace ( match [ 1 ])
49+ uniqueKeys := make ( map [ string ] struct {} )
50+ for _ , match := range keyPat . FindAllStringSubmatch ( dataStr , - 1 ) {
51+ uniqueKeys [ match [ 1 ]] = struct {}{}
52+ }
4353
54+ for key := range uniqueKeys {
4455 s1 := detectors.Result {
4556 DetectorType : detectorspb .DetectorType_FlightLabs ,
46- Raw : []byte (resMatch ),
57+ Raw : []byte (key ),
4758 }
4859
4960 if verify {
50- req , err := http .NewRequestWithContext (ctx , "GET" , fmt .Sprintf ("https://app.goflightlabs.com/airports?access_key=%s" , resMatch ), nil )
51- if err != nil {
52- continue
53- }
54-
55- res , err := client .Do (req )
56- if err == nil {
57- bodyBytes , err := io .ReadAll (res .Body )
58- if err != nil {
59- continue
60- }
61- bodyString := string (bodyBytes )
62- validResponse := strings .Contains (bodyString , `"id"` )
63- defer res .Body .Close ()
64- if res .StatusCode >= 200 && res .StatusCode < 300 && validResponse {
65- s1 .Verified = true
66- }
67- }
61+ isVerified , verificationErr := verifyMatch (ctx , s .getClient (), key )
62+ s1 .Verified = isVerified
63+ s1 .SetVerificationError (verificationErr , key )
6864 }
6965
7066 results = append (results , s1 )
@@ -73,6 +69,34 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
7369 return results , nil
7470}
7571
72+ func verifyMatch (ctx context.Context , client * http.Client , secret string ) (bool , error ) {
73+ // API Reference: https://www.goflightlabs.com/airports-by-filters
74+
75+ url := fmt .Sprintf ("https://www.goflightlabs.com/airports-by-filter?access_key=%s&iata_code=JFK" , secret )
76+ req , err := http .NewRequestWithContext (ctx , http .MethodGet , url , http .NoBody )
77+ if err != nil {
78+ return false , err
79+ }
80+
81+ res , err := client .Do (req )
82+ if err != nil {
83+ return false , err
84+ }
85+ defer func () {
86+ _ , _ = io .Copy (io .Discard , res .Body )
87+ _ = res .Body .Close ()
88+ }()
89+
90+ switch res .StatusCode {
91+ case http .StatusOK :
92+ return true , nil
93+ case http .StatusUnauthorized :
94+ return false , nil
95+ default :
96+ return false , fmt .Errorf ("unexpected status code: %d" , res .StatusCode )
97+ }
98+ }
99+
76100func (s Scanner ) Type () detectorspb.DetectorType {
77101 return detectorspb .DetectorType_FlightLabs
78102}
0 commit comments