5
5
"context"
6
6
"encoding/json"
7
7
"fmt"
8
+ "io"
8
9
"net/http"
10
+ "strings"
9
11
10
12
regexp "github.com/wasilibs/go-re2"
11
13
@@ -14,18 +16,25 @@ import (
14
16
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
15
17
)
16
18
17
- type Scanner struct {
19
+ type Scanner struct {
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
- // possibly always `sq0csp` for secret
26
- // and `sq0idb` for app
27
- keyPat = regexp .MustCompile (`[\w\-]*sq0i[a-z]{2}-[0-9A-Za-z\-_]{22,43}` )
28
- secPat = regexp .MustCompile (`[\w\-]*sq0c[a-z]{2}-[0-9A-Za-z\-_]{40,50}` )
27
+ client = common .SaneHttpClient ()
28
+ /*
29
+ The sandbox id and secret has word `sandbox-` as prefix
30
+ possibly always `sq0csp` for secret and `sq0idb` for app
31
+ */
32
+ keyPat = regexp .MustCompile (`(?:sandbox-)?sq0i[a-z]{2}-[0-9A-Za-z_-]{22,43}` )
33
+ secPat = regexp .MustCompile (`(?:sandbox-)?sq0c[a-z]{2}-[0-9A-Za-z_-]{40,50}` )
34
+
35
+ // api endpoints
36
+ sandboxEndpoint = "https://connect.squareupsandbox.com/oauth2/revoke"
37
+ prodEndpoint = "https://connect.squareup.com/oauth2/revoke"
29
38
)
30
39
31
40
// Keywords are used for efficiently pre-filtering chunks.
@@ -34,61 +43,114 @@ func (s Scanner) Keywords() []string {
34
43
return []string {"sq0i" }
35
44
}
36
45
46
+ func (s Scanner ) Type () detectorspb.DetectorType {
47
+ return detectorspb .DetectorType_SquareApp
48
+ }
49
+
50
+ func (s Scanner ) Description () string {
51
+ return "Square is a financial services and mobile payment company. Square credentials can be used to access and manage payment processing and other financial services."
52
+ }
53
+
37
54
// FromData will find and optionally verify SquareApp secrets in a given set of bytes.
38
55
func (s Scanner ) FromData (ctx context.Context , verify bool , data []byte ) (results []detectors.Result , err error ) {
39
56
dataStr := string (data )
40
57
41
- matches := keyPat .FindAllString (dataStr , - 1 )
42
- secMatches := secPat .FindAllString (dataStr , - 1 )
43
- for _ , match := range matches {
44
- for _ , secMatch := range secMatches {
58
+ var uniqueIDMatches , uniqueSecretMatches = make (map [string ]struct {}), make (map [string ]struct {})
59
+
60
+ for _ , match := range keyPat .FindAllString (dataStr , - 1 ) {
61
+ uniqueIDMatches [match ] = struct {}{}
62
+ }
63
+
64
+ for _ , match := range secPat .FindAllString (dataStr , - 1 ) {
65
+ uniqueSecretMatches [match ] = struct {}{}
66
+ }
67
+
68
+ for id := range uniqueIDMatches {
69
+ for secret := range uniqueSecretMatches {
70
+ // if both are not from same env, continue
71
+ if ! hasSamePrefix (id , secret ) {
72
+ continue
73
+ }
45
74
46
75
result := detectors.Result {
47
76
DetectorType : detectorspb .DetectorType_SquareApp ,
48
- Raw : []byte (match ),
49
- Redacted : match ,
77
+ Raw : []byte (id ),
78
+ Redacted : id ,
79
+ ExtraData : map [string ]string {},
50
80
}
51
81
52
- if verify {
53
- baseURL := "https://connect.squareupsandbox.com/oauth2/revoke"
54
-
55
- client := common .SaneHttpClient ()
56
- reqData , err := json .Marshal (map [string ]string {
57
- "client_id" : match ,
58
- "access_token" : "fakeTruffleHogAccessTokenForVerification" ,
59
- })
60
- if err != nil {
61
- return results , err
62
- }
63
-
64
- req , err := http .NewRequestWithContext (ctx , "POST" , baseURL , bytes .NewReader (reqData ))
65
- if err != nil {
66
- continue
67
- }
68
- req .Header .Add ("Authorization" , fmt .Sprintf ("Client %s" , secMatch ))
69
- req .Header .Add ("Content-Type" , "application/json" )
70
-
71
- res , err := client .Do (req )
72
- if err == nil {
73
- res .Body .Close () // The request body is unused.
74
-
75
- // 404 = Correct credentials. The fake access token should not be found.
76
- if res .StatusCode == http .StatusNotFound {
77
- result .Verified = true
78
- }
79
- }
82
+ var isVerified bool
83
+ var verificationErr error
84
+
85
+ // verify against sandbox endpoint
86
+ if verify && isSandbox (id ) {
87
+ isVerified , verificationErr = verifySquareApp (ctx , client , sandboxEndpoint , id , secret )
88
+ result .ExtraData ["Env" ] = "Sandbox"
89
+ }
90
+
91
+ // verify against prod endpoint
92
+ if verify && ! isSandbox (id ) {
93
+ isVerified , verificationErr = verifySquareApp (ctx , client , prodEndpoint , id , secret )
94
+ result .ExtraData ["Env" ] = "Production"
80
95
}
81
96
97
+ result .Verified = isVerified
98
+ result .SetVerificationError (verificationErr )
99
+
82
100
results = append (results , result )
101
+
102
+ // once a secret is verified with id, remove it from the list
103
+ if isVerified {
104
+ delete (uniqueSecretMatches , secret )
105
+ }
83
106
}
84
107
}
85
- return
108
+
109
+ return results , nil
86
110
}
87
111
88
- func (s Scanner ) Type () detectorspb.DetectorType {
89
- return detectorspb .DetectorType_SquareApp
112
+ func verifySquareApp (ctx context.Context , client * http.Client , endpoint , id , secret string ) (bool , error ) {
113
+ reqData , err := json .Marshal (map [string ]string {
114
+ "client_id" : id ,
115
+ "access_token" : "fakeTruffleHogAccessTokenForVerification" ,
116
+ })
117
+ if err != nil {
118
+ return false , err
119
+ }
120
+
121
+ req , err := http .NewRequestWithContext (ctx , "POST" , endpoint , bytes .NewReader (reqData ))
122
+ if err != nil {
123
+ return false , err
124
+ }
125
+ req .Header .Add ("Authorization" , fmt .Sprintf ("Client %s" , secret ))
126
+ req .Header .Add ("Content-Type" , "application/json" )
127
+
128
+ resp , err := client .Do (req )
129
+ if err != nil {
130
+ return false , err
131
+ }
132
+
133
+ defer func () {
134
+ _ , _ = io .Copy (io .Discard , resp .Body )
135
+ _ = resp .Body .Close ()
136
+ }()
137
+
138
+ switch resp .StatusCode {
139
+ case http .StatusNotFound :
140
+ return true , nil
141
+ default :
142
+ return false , fmt .Errorf ("unexpected status code: %d" , resp .StatusCode )
143
+ }
90
144
}
91
145
92
- func (s Scanner ) Description () string {
93
- return "Square is a financial services and mobile payment company. Square credentials can be used to access and manage payment processing and other financial services."
146
+ func hasSamePrefix (id , secret string ) bool {
147
+ idHasPrefix := strings .HasPrefix (id , "sandbox-" )
148
+ secretHasPrefix := strings .HasPrefix (secret , "sandbox-" )
149
+
150
+ return idHasPrefix == secretHasPrefix
151
+ }
152
+
153
+ // isSandbox check if provided key(id or secret) is of sandbox env
154
+ func isSandbox (key string ) bool {
155
+ return strings .HasPrefix (key , "sandbox-" )
94
156
}
0 commit comments