@@ -2,8 +2,9 @@ package cloudflarecakey
2
2
3
3
import (
4
4
"context"
5
+ "fmt"
6
+ "io"
5
7
"net/http"
6
- "strings"
7
8
8
9
regexp "github.com/wasilibs/go-re2"
9
10
@@ -20,7 +21,8 @@ var _ detectors.Detector = (*Scanner)(nil)
20
21
var (
21
22
client = common .SaneHttpClient ()
22
23
23
- keyPat = regexp .MustCompile (detectors .PrefixRegex ([]string {"cloudflare" }) + `\b(v[A-Za-z0-9._-]{173,})\b` )
24
+ // origin ca keys documentation: https://developers.cloudflare.com/fundamentals/api/get-started/ca-keys/
25
+ keyPat = regexp .MustCompile (`\b(v1\.0-[A-Za-z0-9-]{171})\b` )
24
26
)
25
27
26
28
// Keywords are used for efficiently pre-filtering chunks.
@@ -33,35 +35,22 @@ func (s Scanner) Keywords() []string {
33
35
func (s Scanner ) FromData (ctx context.Context , verify bool , data []byte ) (results []detectors.Result , err error ) {
34
36
dataStr := string (data )
35
37
36
- matches := keyPat . FindAllStringSubmatch ( dataStr , - 1 )
38
+ uniqueMatches := make ( map [ string ] struct {} )
37
39
38
- for _ , match := range matches {
39
- if len (match ) != 2 {
40
- continue
41
- }
42
- resMatch := strings .TrimSpace (match [1 ])
40
+ for _ , matches := range keyPat .FindAllStringSubmatch (dataStr , - 1 ) {
41
+ uniqueMatches [matches [1 ]] = struct {}{}
42
+ }
43
43
44
+ for caKey := range uniqueMatches {
44
45
s1 := detectors.Result {
45
46
DetectorType : detectorspb .DetectorType_CloudflareCaKey ,
46
- Raw : []byte (resMatch ),
47
+ Raw : []byte (caKey ),
47
48
}
48
49
49
50
if verify {
50
- req , err := http .NewRequestWithContext (ctx , "GET" , "https://api.cloudflare.com/client/v4/certificates?zone_id=a" , nil )
51
- if err != nil {
52
- continue
53
- }
54
- req .Header .Add ("Content-Type" , "application/json" )
55
- req .Header .Add ("user-agent" , "curl/7.68.0" ) // pretend to be from curl so we do not wait 100+ seconds -> nice try did not work
56
-
57
- req .Header .Add ("X-Auth-User-Service-Key" , resMatch )
58
- res , err := client .Do (req )
59
- if err == nil {
60
- defer res .Body .Close ()
61
- if res .StatusCode >= 200 && res .StatusCode < 300 {
62
- s1 .Verified = true
63
- }
64
- }
51
+ isVerified , verificationErr := verifyCloudFlareCAKey (ctx , client , caKey )
52
+ s1 .Verified = isVerified
53
+ s1 .SetVerificationError (verificationErr , caKey )
65
54
}
66
55
67
56
results = append (results , s1 )
@@ -77,3 +66,33 @@ func (s Scanner) Type() detectorspb.DetectorType {
77
66
func (s Scanner ) Description () string {
78
67
return "Cloudflare is a web infrastructure and website security company. Cloudflare CA keys can be used to manage SSL/TLS certificates and other security settings."
79
68
}
69
+
70
+ func verifyCloudFlareCAKey (ctx context.Context , client * http.Client , caKey string ) (bool , error ) {
71
+ req , err := http .NewRequestWithContext (ctx , "GET" , "https://api.cloudflare.com/client/v4/certificates?zone_id=a" , nil )
72
+ if err != nil {
73
+ return false , nil
74
+ }
75
+
76
+ req .Header .Add ("Content-Type" , "application/json" )
77
+ req .Header .Add ("user-agent" , "curl/7.68.0" ) // pretend to be from curl so we do not wait 100+ seconds -> nice try did not work
78
+
79
+ req .Header .Add ("X-Auth-User-Service-Key" , caKey )
80
+ resp , err := client .Do (req )
81
+ if err != nil {
82
+ return false , err
83
+ }
84
+
85
+ defer func () {
86
+ _ , _ = io .Copy (io .Discard , resp .Body )
87
+ _ = resp .Body .Close ()
88
+ }()
89
+
90
+ switch resp .StatusCode {
91
+ case http .StatusOK :
92
+ return true , nil
93
+ case http .StatusUnauthorized , http .StatusForbidden :
94
+ return false , nil
95
+ default :
96
+ return false , fmt .Errorf ("unexpected status code: %d" , resp .StatusCode )
97
+ }
98
+ }
0 commit comments