@@ -2,12 +2,15 @@ package ipquality
2
2
3
3
import (
4
4
"context"
5
+ "encoding/json"
6
+ "errors"
5
7
"fmt"
6
- regexp "github.com/wasilibs/go-re2"
7
8
"io"
8
9
"net/http"
9
10
"strings"
10
11
12
+ regexp "github.com/wasilibs/go-re2"
13
+
11
14
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
12
15
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
13
16
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
22
25
client = common .SaneHttpClient ()
23
26
24
27
// Make sure that your group is surrounded in boundary characters such as below to reduce false positives.
25
- keyPat = regexp .MustCompile (detectors .PrefixRegex ([]string {"ipquality" }) + `\b([0-9a-z ]{32})\b` )
28
+ keyPat = regexp .MustCompile (detectors .PrefixRegex ([]string {"ipquality" }) + `\b([0-9a-zA-Z ]{32})\b` )
26
29
)
27
30
31
+ const (
32
+ // response messages
33
+ invalidKeyMessage = "Invalid or unauthorized key"
34
+ insufficientCreditMessage = "insufficient credits"
35
+ )
36
+
37
+ type apiResponse struct {
38
+ Success bool `json:"success"`
39
+ Message string `json:"message"`
40
+ }
41
+
28
42
// Keywords are used for efficiently pre-filtering chunks.
29
43
// Use identifiers in the secret preferably, or the provider name.
30
44
func (s Scanner ) Keywords () []string {
@@ -46,24 +60,10 @@ func (s Scanner) FromData(ctx context.Context, verify bool, data []byte) (result
46
60
}
47
61
48
62
if verify {
49
- req , err := http .NewRequestWithContext (ctx , "GET" , fmt .Sprintf ("https://www.ipqualityscore.com/api/json/account/%s" , resMatch ), nil )
50
- if err != nil {
51
- continue
52
- }
53
- req .Header .Add ("Content-Type" , "application/json" )
54
- res , err := client .Do (req )
55
- if err == nil {
56
- defer res .Body .Close ()
57
- bodyBytes , err := io .ReadAll (res .Body )
58
- if err != nil {
59
- continue
60
- }
61
- body := string (bodyBytes )
62
- validResponse := strings .Contains (body , "insufficient credits" ) || strings .Contains (body , `"success":true` )
63
- if (res .StatusCode >= 200 && res .StatusCode < 300 ) && validResponse {
64
- s1 .Verified = true
65
- }
66
- }
63
+ isVerified , verificationErr := verifyIPQualityAPIKey (ctx , client , resMatch )
64
+
65
+ s1 .Verified = isVerified
66
+ s1 .SetVerificationError (verificationErr )
67
67
}
68
68
69
69
results = append (results , s1 )
@@ -79,3 +79,57 @@ func (s Scanner) Type() detectorspb.DetectorType {
79
79
func (s Scanner ) Description () string {
80
80
return "IPQualityScore provides tools to detect and prevent fraudulent activity. IPQualityScore API keys can be used to access their fraud prevention services."
81
81
}
82
+
83
+ func verifyIPQualityAPIKey (ctx context.Context , client * http.Client , apiKey string ) (bool , error ) {
84
+ req , err := http .NewRequestWithContext (ctx , "GET" , fmt .Sprintf ("https://www.ipqualityscore.com/api/json/account/%s" , apiKey ), nil )
85
+ if err != nil {
86
+ return false , err
87
+ }
88
+
89
+ req .Header .Add ("Content-Type" , "application/json" )
90
+
91
+ resp , err := client .Do (req )
92
+ if err != nil {
93
+ return false , err
94
+ }
95
+
96
+ defer func () {
97
+ _ , _ = io .Copy (io .Discard , resp .Body )
98
+ _ = resp .Body .Close ()
99
+ }()
100
+
101
+ switch resp .StatusCode {
102
+ case http .StatusOK :
103
+ var response apiResponse
104
+
105
+ bodyBytes , err := io .ReadAll (resp .Body )
106
+ if err != nil {
107
+ return false , err
108
+ }
109
+
110
+ if err = json .Unmarshal (bodyBytes , & response ); err != nil {
111
+ return false , err
112
+ }
113
+
114
+ switch response .Success {
115
+ case true :
116
+ return true , nil
117
+ case false :
118
+ /*
119
+ for invalid api key and for a key which has insufficient credit the API returns the same response.
120
+ The scenario where we have correct API key but it has insufficient credit is rare than a scenario that we capture
121
+ an invalid api key as the pattern is too common. Hence in case we get insufficient credit error message we mark the
122
+ API Key as inactive and send back a verification error as well.
123
+ */
124
+ if strings .Contains (response .Message , insufficientCreditMessage ) {
125
+ return false , errors .New ("couldn't verify; API Key has " + insufficientCreditMessage )
126
+ } else if strings .Contains (response .Message , invalidKeyMessage ) {
127
+ return false , nil
128
+ }
129
+ }
130
+
131
+ return false , nil
132
+ default :
133
+ return false , fmt .Errorf ("unexpected status code: %d" , resp .StatusCode )
134
+ }
135
+ }
0 commit comments