4
4
"context"
5
5
b64 "encoding/base64"
6
6
"fmt"
7
+ "io"
7
8
"net/http"
8
9
"strings"
9
10
@@ -15,14 +16,15 @@ import (
15
16
)
16
17
17
18
type Scanner struct {
19
+ client * http.Client
18
20
detectors.DefaultMultiPartCredentialProvider
19
21
}
20
22
21
23
// Ensure the Scanner satisfies the interface at compile time.
22
24
var _ detectors.Detector = (* Scanner )(nil )
23
25
24
26
var (
25
- client = common .SaneHttpClient ()
27
+ defaultClient = common .SaneHttpClient ()
26
28
27
29
// Make sure that your group is surrounded in boundary characters such as below to reduce false positives.
28
30
idPat = regexp .MustCompile (detectors .PrefixRegex ([]string {"dwolla" }) + `\b([a-zA-Z-0-9]{50})\b` )
@@ -35,46 +37,44 @@ func (s Scanner) Keywords() []string {
35
37
return []string {"dwolla" }
36
38
}
37
39
40
+ func (s Scanner ) getClient () * http.Client {
41
+ if s .client != nil {
42
+ return s .client
43
+ }
44
+ return defaultClient
45
+ }
46
+
38
47
// FromData will find and optionally verify Dwolla secrets in a given set of bytes.
39
48
func (s Scanner ) FromData (ctx context.Context , verify bool , data []byte ) (results []detectors.Result , err error ) {
40
49
dataStr := string (data )
41
50
42
- idMatches := idPat .FindAllStringSubmatch (dataStr , - 1 )
43
- secretMatches := secretPat .FindAllStringSubmatch (dataStr , - 1 )
44
-
45
- for _ , match := range idMatches {
46
-
47
- idMatch := strings .TrimSpace (match [1 ])
51
+ uniqueIDs := make (map [string ]struct {})
52
+ for _ , matches := range idPat .FindAllStringSubmatch (dataStr , - 1 ) {
53
+ uniqueIDs [matches [1 ]] = struct {}{}
54
+ }
48
55
49
- for _ , secret := range secretMatches {
56
+ uniqueSecrets := make (map [string ]struct {})
57
+ for _ , matches := range secretPat .FindAllStringSubmatch (dataStr , - 1 ) {
58
+ uniqueSecrets [matches [1 ]] = struct {}{}
59
+ }
50
60
51
- secretMatch := strings .TrimSpace (secret [1 ])
61
+ for id := range uniqueIDs {
62
+ for secret := range uniqueSecrets {
63
+ if id == secret {
64
+ continue // Skip if ID and secret are the same.
65
+ }
52
66
53
67
s1 := detectors.Result {
54
68
DetectorType : detectorspb .DetectorType_Dwolla ,
55
- Raw : []byte (idMatch ),
56
- RawV2 : []byte (idMatch + secretMatch ),
69
+ Raw : []byte (id ),
70
+ RawV2 : []byte (id + secret ),
57
71
}
58
72
59
73
if verify {
60
- data := fmt .Sprintf ("%s:%s" , idMatch , secretMatch )
61
- encoded := b64 .StdEncoding .EncodeToString ([]byte (data ))
62
- payload := strings .NewReader ("grant_type=client_credentials" )
63
-
64
- req , err := http .NewRequestWithContext (ctx , "POST" , "https://api-sandbox.dwolla.com/token" , payload )
65
- if err != nil {
66
- continue
67
- }
68
- req .Header .Add ("Content-Type" , "application/x-www-form-urlencoded" )
69
- req .Header .Add ("Authorization" , fmt .Sprintf ("Basic %s" , encoded ))
70
-
71
- res , err := client .Do (req )
72
- if err == nil {
73
- defer res .Body .Close ()
74
- if res .StatusCode >= 200 && res .StatusCode < 300 {
75
- s1 .Verified = true
76
- }
77
- }
74
+ client := s .getClient ()
75
+ isVerified , err := verifyMatch (ctx , client , id , secret )
76
+ s1 .Verified = isVerified
77
+ s1 .SetVerificationError (err , id , secret )
78
78
}
79
79
80
80
results = append (results , s1 )
@@ -84,6 +84,37 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
84
84
return results , nil
85
85
}
86
86
87
+ func verifyMatch (ctx context.Context , client * http.Client , id , secret string ) (bool , error ) {
88
+ data := fmt .Sprintf ("%s:%s" , id , secret )
89
+ encoded := b64 .StdEncoding .EncodeToString ([]byte (data ))
90
+ payload := strings .NewReader ("grant_type=client_credentials" )
91
+
92
+ req , err := http .NewRequestWithContext (ctx , http .MethodPost , "https://api-sandbox.dwolla.com/token" , payload )
93
+ if err != nil {
94
+ return false , err
95
+ }
96
+ req .Header .Add ("Content-Type" , "application/x-www-form-urlencoded" )
97
+ req .Header .Add ("Authorization" , fmt .Sprintf ("Basic %s" , encoded ))
98
+
99
+ res , err := client .Do (req )
100
+ if err != nil {
101
+ return false , err
102
+ }
103
+ defer func () {
104
+ _ , _ = io .Copy (io .Discard , res .Body )
105
+ _ = res .Body .Close ()
106
+ }()
107
+
108
+ switch res .StatusCode {
109
+ case http .StatusOK :
110
+ return true , nil
111
+ case http .StatusUnauthorized :
112
+ return false , nil
113
+ default :
114
+ return false , fmt .Errorf ("unexpected status code %d" , res .StatusCode )
115
+ }
116
+ }
117
+
87
118
func (s Scanner ) Type () detectorspb.DetectorType {
88
119
return detectorspb .DetectorType_Dwolla
89
120
}
0 commit comments