@@ -5,7 +5,11 @@ package cmd
5
5
6
6
import (
7
7
"bytes"
8
+ "crypto/hmac"
9
+ "crypto/rand"
10
+ "crypto/sha256"
8
11
"crypto/tls"
12
+ "encoding/base64"
9
13
"encoding/json"
10
14
"fmt"
11
15
"reflect"
@@ -78,6 +82,42 @@ func convertLogsToNDJSON(logs interface{}) (string, error) {
78
82
return strings .Join (ndjsonLines , "\n " ), nil
79
83
}
80
84
85
+ func GenerateWebhookSecret () (string , error ) {
86
+ // Generate 32 bytes of random data
87
+ randomBytes := make ([]byte , 32 )
88
+ _ , err := rand .Read (randomBytes )
89
+ if err != nil {
90
+ return "" , fmt .Errorf ("failed to generate random bytes: %w" , err )
91
+ }
92
+
93
+ // Encode the random bytes to base64
94
+ secret := base64 .URLEncoding .EncodeToString (randomBytes )
95
+
96
+ // Prefix the secret with "whsec_" to match the format of the example
97
+ return "whsec_" + secret , nil
98
+ }
99
+
100
+ func calculateWebhookSignature (payload []byte , secret string ) (string , error ) {
101
+ // Split the secret and decode the base64 part
102
+ parts := strings .SplitN (secret , "_" , 2 )
103
+ if len (parts ) != 2 {
104
+ return "" , fmt .Errorf ("invalid secret format" )
105
+ }
106
+ secretBytes , err := base64 .URLEncoding .DecodeString (parts [1 ])
107
+ if err != nil {
108
+ return "" , fmt .Errorf ("failed to decode secret: %w" , err )
109
+ }
110
+
111
+ // Create the HMAC
112
+ h := hmac .New (sha256 .New , secretBytes )
113
+ h .Write (payload )
114
+
115
+ // Get the result and encode to base64
116
+ signature := base64 .StdEncoding .EncodeToString (h .Sum (nil ))
117
+
118
+ return signature , nil
119
+ }
120
+
81
121
// event types same as log types : minimal, results, policy, report
82
122
// custom modifiers : policy+bulk, report+bulk,
83
123
@@ -168,6 +208,22 @@ func PostResultsToWebhooks(sarifReport SARIFReport) error {
168
208
// Prepare the request
169
209
req := client .R ()
170
210
req .SetHeaders (hook .Headers )
211
+
212
+ // Set custom User-Agent
213
+ userAgent := fmt .Sprintf ("intercept/%s" , buildVersion )
214
+ req .SetHeader ("User-Agent" , userAgent )
215
+
216
+ // Convert payload to []byte for signature calculation
217
+ var payloadBytes []byte
218
+ if payloadType == "x-ndjson" {
219
+ payloadBytes = []byte (payload .(string ))
220
+ } else {
221
+ payloadBytes , _ = json .Marshal (payload )
222
+ }
223
+ // Calculate and set the signature
224
+ signature , _ := calculateWebhookSignature (payloadBytes , webhookSecret )
225
+ req .SetHeader ("X-Signature" , signature )
226
+
171
227
req .SetBody (payload )
172
228
173
229
// Set content type based on payloadType
@@ -196,13 +252,15 @@ func PostResultsToWebhooks(sarifReport SARIFReport) error {
196
252
time .Sleep (retryDelay )
197
253
}
198
254
}
255
+ // Flatten hook.EventTypes to a single string
256
+ flatEventTypes := strings .Join (hook .EventTypes , "," )
199
257
200
258
if err != nil {
201
- log .Error ().Err (err ).Str ("hook" , hook .Name ).Msg ("Failed to post to webhook" )
259
+ log .Error ().Err (err ).Str ("hook" , hook .Name ).Str ( "event_type" , flatEventTypes ). Msg ("Failed to post to webhook" )
202
260
} else if ! resp .IsSuccess () {
203
- log .Error ().Str ("hook" , hook .Name ).Int ("status" , resp .StatusCode ()).Msg ("Webhook request failed" )
261
+ log .Error ().Str ("hook" , hook .Name ).Str ( "event_type" , flatEventTypes ). Int ("status" , resp .StatusCode ()).Msg ("Webhook request failed" )
204
262
} else {
205
- log .Info ().Str ("hook" , hook .Name ).Msg ("Successfully posted to webhook" )
263
+ log .Info ().Str ("hook" , hook .Name ).Str ( "event_type" , flatEventTypes ). Str ( "payload_type" , payloadType ). Msg ("Successfully posted to webhook" )
206
264
}
207
265
}
208
266
@@ -330,6 +388,22 @@ func PostReportToWebhooks(sarifReport SARIFReport) error {
330
388
// Prepare the request
331
389
req := client .R ()
332
390
req .SetHeaders (hook .Headers )
391
+
392
+ // Set custom User-Agent
393
+ userAgent := fmt .Sprintf ("intercept/%s" , buildVersion )
394
+ req .SetHeader ("User-Agent" , userAgent )
395
+
396
+ // Convert payload to []byte for signature calculation
397
+ var payloadBytes []byte
398
+ if payloadType == "x-ndjson" {
399
+ payloadBytes = []byte (payload .(string ))
400
+ } else {
401
+ payloadBytes , _ = json .Marshal (payload )
402
+ }
403
+ // Calculate and set the signature
404
+ signature , _ := calculateWebhookSignature (payloadBytes , webhookSecret )
405
+ req .SetHeader ("X-Signature" , signature )
406
+
333
407
req .SetBody (payload )
334
408
335
409
// if containsString(hook.EventTypes, "bulk") {
@@ -361,12 +435,15 @@ func PostReportToWebhooks(sarifReport SARIFReport) error {
361
435
}
362
436
}
363
437
438
+ // Flatten hook.EventTypes to a single string
439
+ flatEventTypes := strings .Join (hook .EventTypes , "," )
440
+
364
441
if err != nil {
365
- log .Error ().Err (err ).Str ("hook" , hook .Name ).Msg ("Failed to post to webhook" )
442
+ log .Error ().Err (err ).Str ("hook" , hook .Name ).Str ( "event_type" , flatEventTypes ). Msg ("Failed to post to webhook" )
366
443
} else if ! resp .IsSuccess () {
367
- log .Error ().Str ("hook" , hook .Name ).Int ("status" , resp .StatusCode ()).Msg ("Webhook request failed" )
444
+ log .Error ().Str ("hook" , hook .Name ).Str ( "event_type" , flatEventTypes ). Int ("status" , resp .StatusCode ()).Msg ("Webhook request failed" )
368
445
} else {
369
- log .Info ().Str ("hook" , hook .Name ).Msg ("Successfully posted to webhook" )
446
+ log .Info ().Str ("hook" , hook .Name ).Str ( "event_type" , flatEventTypes ). Str ( "payload_type" , payloadType ). Msg ("Successfully posted to webhook" )
370
447
}
371
448
}
372
449
0 commit comments