@@ -6,19 +6,22 @@ import (
6
6
"crypto/md5"
7
7
"crypto/sha256"
8
8
"encoding/hex"
9
- regexp "github.com/wasilibs/go-re2"
9
+ "fmt"
10
+ "io"
10
11
"net/http"
11
12
"net/url"
12
13
"strconv"
13
14
"strings"
14
15
"time"
15
16
17
+ regexp "github.com/wasilibs/go-re2"
18
+
16
19
"github.com/trufflesecurity/trufflehog/v3/pkg/common"
17
20
"github.com/trufflesecurity/trufflehog/v3/pkg/detectors"
18
21
"github.com/trufflesecurity/trufflehog/v3/pkg/pb/detectorspb"
19
22
)
20
23
21
- type Scanner struct {
24
+ type Scanner struct {
22
25
detectors.DefaultMultiPartCredentialProvider
23
26
}
24
27
@@ -49,72 +52,33 @@ const (
49
52
func (s Scanner ) FromData (ctx context.Context , verify bool , data []byte ) (results []detectors.Result , err error ) {
50
53
dataStr := string (data )
51
54
52
- keyMatches := keyPat .FindAllStringSubmatch (dataStr , - 1 )
53
- appMatches := appIdPat .FindAllStringSubmatch (dataStr , - 1 )
54
- secretMatches := secretPat .FindAllStringSubmatch (dataStr , - 1 )
55
+ uniqueKeyMatches , uniqueAppMatches , uniqueSecretMatches := make (map [string ]struct {}), make (map [string ]struct {}), make (map [string ]struct {})
55
56
56
- for _ , appMatch := range appMatches {
57
- if len (appMatch ) != 2 {
58
- continue
59
- }
60
- resappMatch := strings .TrimSpace (appMatch [1 ])
57
+ for _ , keyMatch := range keyPat .FindAllStringSubmatch (dataStr , - 1 ) {
58
+ uniqueKeyMatches [keyMatch [1 ]] = struct {}{}
59
+ }
61
60
62
- for _ , keyMatch := range keyMatches {
63
- if len (keyMatch ) != 2 {
64
- continue
65
- }
66
- reskeyMatch := strings .TrimSpace (keyMatch [1 ])
61
+ for _ , appMatch := range appIdPat .FindAllStringSubmatch (dataStr , - 1 ) {
62
+ uniqueAppMatches [appMatch [1 ]] = struct {}{}
63
+ }
67
64
68
- for _ , secretMatch := range secretMatches {
69
- if len (secretMatch ) != 2 {
70
- continue
71
- }
72
- ressecretMatch := strings .TrimSpace (secretMatch [1 ])
65
+ for _ , secretMatch := range secretPat .FindAllStringSubmatch (dataStr , - 1 ) {
66
+ uniqueSecretMatches [secretMatch [1 ]] = struct {}{}
67
+ }
73
68
69
+ for app := range uniqueAppMatches {
70
+ for key := range uniqueKeyMatches {
71
+ for secret := range uniqueSecretMatches {
74
72
s1 := detectors.Result {
75
73
DetectorType : detectorspb .DetectorType_PusherChannelKey ,
76
- Raw : []byte (resappMatch ),
77
- RawV2 : []byte (resappMatch + reskeyMatch ),
74
+ Raw : []byte (app ),
75
+ RawV2 : []byte (app + key ),
78
76
}
79
77
80
78
if verify {
81
-
82
- method := "POST"
83
- path := "/apps/" + resappMatch + "/events"
84
-
85
- stringPayload := `{"channels":["my-channel"],"data":"{\"message\":\"hello world\"}","name":"my_event"}`
86
- payload := strings .NewReader (stringPayload )
87
- _bodyMD5 := md5 .New ()
88
- _bodyMD5 .Write ([]byte (stringPayload ))
89
- hash := hex .EncodeToString (_bodyMD5 .Sum (nil ))
90
-
91
- timestamp := strconv .FormatInt (time .Now ().Unix (), 10 )
92
- params := url.Values {
93
- "auth_key" : {reskeyMatch },
94
- "auth_timestamp" : {timestamp },
95
- "auth_version" : {auth_version },
96
- "body_md5" : {hash },
97
- }
98
-
99
- usecd , _ := url .QueryUnescape (params .Encode ())
100
-
101
- stringToSign := strings .Join ([]string {method , path , usecd }, "\n " )
102
- signature := hex .EncodeToString (hmacBytes ([]byte (stringToSign ), []byte (ressecretMatch )))
103
-
104
- md5Str := "https://api-ap1.pusher.com/apps/" + resappMatch + "/events?auth_key=" + reskeyMatch + "&auth_signature=" + signature + "&auth_timestamp=" + timestamp + "&auth_version=1.0&body_md5=" + hash
105
-
106
- req , err := http .NewRequestWithContext (ctx , method , md5Str , payload )
107
- if err != nil {
108
- continue
109
- }
110
- req .Header .Add ("Content-Type" , "application/json" )
111
- res , err := client .Do (req )
112
- if err == nil {
113
- defer res .Body .Close ()
114
- if res .StatusCode >= 200 && res .StatusCode < 300 {
115
- s1 .Verified = true
116
- }
117
- }
79
+ isVerified , verificationErr := verifyPusherChannelKey (ctx , client , app , key , secret )
80
+ s1 .Verified = isVerified
81
+ s1 .SetVerificationError (verificationErr )
118
82
}
119
83
120
84
results = append (results , s1 )
@@ -139,3 +103,55 @@ func (s Scanner) Type() detectorspb.DetectorType {
139
103
func (s Scanner ) Description () string {
140
104
return "Pusher is a service for adding real-time functionality to web and mobile apps. Pusher Channel keys can be used to authenticate and send messages to channels."
141
105
}
106
+
107
+ func verifyPusherChannelKey (ctx context.Context , client * http.Client , app , key , secret string ) (bool , error ) {
108
+ method := "POST"
109
+ path := "/apps/" + app + "/events"
110
+
111
+ stringPayload := `{"channels":["my-channel"],"data":"{\"message\":\"hello world\"}","name":"my_event"}`
112
+ payload := strings .NewReader (stringPayload )
113
+ _bodyMD5 := md5 .New ()
114
+ _bodyMD5 .Write ([]byte (stringPayload ))
115
+ hash := hex .EncodeToString (_bodyMD5 .Sum (nil ))
116
+
117
+ timestamp := strconv .FormatInt (time .Now ().Unix (), 10 )
118
+ params := url.Values {
119
+ "auth_key" : {key },
120
+ "auth_timestamp" : {timestamp },
121
+ "auth_version" : {auth_version },
122
+ "body_md5" : {hash },
123
+ }
124
+
125
+ usecd , _ := url .QueryUnescape (params .Encode ())
126
+
127
+ stringToSign := strings .Join ([]string {method , path , usecd }, "\n " )
128
+ signature := hex .EncodeToString (hmacBytes ([]byte (stringToSign ), []byte (secret )))
129
+
130
+ md5Str := "https://api-ap1.pusher.com/apps/" + app + "/events?auth_key=" + key + "&auth_signature=" + signature + "&auth_timestamp=" + timestamp + "&auth_version=1.0&body_md5=" + hash
131
+
132
+ req , err := http .NewRequestWithContext (ctx , method , md5Str , payload )
133
+ if err != nil {
134
+ return false , err
135
+ }
136
+
137
+ req .Header .Add ("Content-Type" , "application/json" )
138
+ resp , err := client .Do (req )
139
+ if err != nil {
140
+ return false , nil
141
+ }
142
+
143
+ defer func () {
144
+ _ , _ = io .Copy (io .Discard , resp .Body )
145
+ _ = resp .Body .Close ()
146
+
147
+ }()
148
+
149
+ switch resp .StatusCode {
150
+ case http .StatusOK :
151
+ return true , nil
152
+ case http .StatusUnauthorized , http .StatusForbidden :
153
+ return false , nil
154
+ default :
155
+ return false , fmt .Errorf ("unexpected status code: %d" , resp .StatusCode )
156
+ }
157
+ }
0 commit comments