@@ -10,37 +10,38 @@ import (
10
10
"strings"
11
11
)
12
12
13
+ // Hook describes an inbound github webhook
13
14
type Hook struct {
14
15
Signature string
15
16
Event string
16
17
Id string
17
18
Payload []byte
18
19
}
19
20
21
+ const signaturePrefix = "sha1="
22
+ const signatureLength = 45 // len(SignaturePrefix) + len(hex(sha1))
23
+
20
24
func signBody (secret , body []byte ) []byte {
21
25
computed := hmac .New (sha1 .New , secret )
22
26
computed .Write (body )
23
27
return []byte (computed .Sum (nil ))
24
28
}
25
29
26
- func verifySignature (secret []byte , signature string , body []byte ) bool {
27
-
28
- const signaturePrefix = "sha1="
29
- const signatureLength = 45 // len(SignaturePrefix) + len(hex(sha1))
30
-
31
- if len (signature ) != signatureLength || ! strings .HasPrefix (signature , signaturePrefix ) {
30
+ // SignedBy checks that the provided secret matches the hook Signature
31
+ func (h * Hook ) SignedBy (secret []byte ) bool {
32
+ if len (h .Signature ) != signatureLength || ! strings .HasPrefix (h .Signature , signaturePrefix ) {
32
33
return false
33
34
}
34
35
35
36
actual := make ([]byte , 20 )
36
- hex .Decode (actual , []byte (signature [5 :]))
37
+ hex .Decode (actual , []byte (h . Signature [5 :]))
37
38
38
- return hmac .Equal (signBody (secret , body ), actual )
39
+ return hmac .Equal (signBody (secret , h . Payload ), actual )
39
40
}
40
41
41
- func Parse ( secret [] byte , req * http. Request ) ( * Hook , error ) {
42
- hook := Hook {}
43
-
42
+ // New extracts a Hook from an incoming http.Request
43
+ func New ( req * http. Request ) ( hook * Hook , err error ) {
44
+ hook = new ( Hook )
44
45
if ! strings .EqualFold (req .Method , "POST" ) {
45
46
return nil , errors .New ("Unknown method!" )
46
47
}
@@ -57,17 +58,15 @@ func Parse(secret []byte, req *http.Request) (*Hook, error) {
57
58
return nil , errors .New ("No event Id!" )
58
59
}
59
60
60
- body , err := ioutil .ReadAll (req .Body )
61
-
62
- if err != nil {
63
- return nil , err
64
- }
61
+ hook .Payload , err = ioutil .ReadAll (req .Body )
62
+ return
63
+ }
65
64
66
- if ! verifySignature (secret , hook .Signature , body ) {
67
- return nil , errors .New ("Invalid signature" )
65
+ // Parse extracts and verifies a hook against a secret
66
+ func Parse (secret []byte , req * http.Request ) (hook * Hook , err error ) {
67
+ hook , err = New (req )
68
+ if err == nil && ! hook .SignedBy (secret ) {
69
+ err = errors .New ("Invalid signature" )
68
70
}
69
-
70
- hook .Payload = body
71
-
72
- return & hook , nil
71
+ return
73
72
}
0 commit comments