-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathwebhook_test.go
More file actions
83 lines (73 loc) · 2.73 KB
/
Copy pathwebhook_test.go
File metadata and controls
83 lines (73 loc) · 2.73 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
package cboxid_test
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"fmt"
"strconv"
"testing"
"time"
cboxid "github.com/cboxdk/id-go"
)
const (
webhookSecret = "whsec_test"
webhookPayload = `{"event":"user.updated","id":"user-1"}`
)
// signWebhook builds a "t={ts},v1={hmac}" header exactly as Cbox ID does — an
// HMAC-SHA256 over "{ts}.{body}".
func signWebhook(t *testing.T, timestamp int64, body, secret string) string {
t.Helper()
mac := hmac.New(sha256.New, []byte(secret))
mac.Write([]byte(strconv.FormatInt(timestamp, 10) + "." + body))
return fmt.Sprintf("t=%d,v1=%s", timestamp, hex.EncodeToString(mac.Sum(nil)))
}
func TestVerifyWebhookAcceptsFreshSignature(t *testing.T) {
header := signWebhook(t, time.Now().Unix(), webhookPayload, webhookSecret)
if !cboxid.VerifyWebhook(webhookPayload, header, webhookSecret, 300) {
t.Error("a fresh, correctly-signed payload should verify")
}
}
func TestVerifyWebhookRejectsWrongSecret(t *testing.T) {
header := signWebhook(t, time.Now().Unix(), webhookPayload, webhookSecret)
if cboxid.VerifyWebhook(webhookPayload, header, "whsec_other", 300) {
t.Error("a signature made with a different secret must be rejected")
}
}
func TestVerifyWebhookRejectsTamperedBody(t *testing.T) {
header := signWebhook(t, time.Now().Unix(), webhookPayload, webhookSecret)
if cboxid.VerifyWebhook(webhookPayload+"x", header, webhookSecret, 300) {
t.Error("a body that no longer matches the signature must be rejected")
}
}
func TestVerifyWebhookRejectsStaleTimestamp(t *testing.T) {
// A replayed/old event outside the tolerance window must be rejected.
header := signWebhook(t, time.Now().Unix()-10_000, webhookPayload, webhookSecret)
if cboxid.VerifyWebhook(webhookPayload, header, webhookSecret, 300) {
t.Error("a timestamp outside the tolerance must be rejected")
}
}
func TestVerifyWebhookTimestampTolerance(t *testing.T) {
// 100s old is inside a 300s tolerance…
header := signWebhook(t, time.Now().Unix()-100, webhookPayload, webhookSecret)
if !cboxid.VerifyWebhook(webhookPayload, header, webhookSecret, 300) {
t.Error("a timestamp within the tolerance should verify")
}
// …but outside a 50s tolerance.
if cboxid.VerifyWebhook(webhookPayload, header, webhookSecret, 50) {
t.Error("a timestamp outside the tolerance must be rejected")
}
}
func TestVerifyWebhookRejectsMissingOrMalformedHeader(t *testing.T) {
cases := []string{
"", // missing
"garbage", // no key=value segments
"t=abc,v1=xx", // non-numeric timestamp
"v1=deadbeef", // no timestamp
"t=1700000000", // no signature
}
for _, header := range cases {
if cboxid.VerifyWebhook(webhookPayload, header, webhookSecret, 300) {
t.Errorf("malformed header %q must be rejected", header)
}
}
}