Skip to content

Commit 4242805

Browse files
committed
Breaks up public API (#6)
Separating `Parse` into separate `New` and `SignedBy` methods will allow consumers to provisionally extract hook content from an HTTP request (via `New`) before verifyingt that the hook signature meets expectations.
1 parent 6ab7c67 commit 4242805

File tree

2 files changed

+30
-26
lines changed

2 files changed

+30
-26
lines changed

githubhook.go

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -10,37 +10,38 @@ import (
1010
"strings"
1111
)
1212

13+
// Hook describes an inbound github webhook
1314
type Hook struct {
1415
Signature string
1516
Event string
1617
Id string
1718
Payload []byte
1819
}
1920

21+
const signaturePrefix = "sha1="
22+
const signatureLength = 45 // len(SignaturePrefix) + len(hex(sha1))
23+
2024
func signBody(secret, body []byte) []byte {
2125
computed := hmac.New(sha1.New, secret)
2226
computed.Write(body)
2327
return []byte(computed.Sum(nil))
2428
}
2529

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) {
3233
return false
3334
}
3435

3536
actual := make([]byte, 20)
36-
hex.Decode(actual, []byte(signature[5:]))
37+
hex.Decode(actual, []byte(h.Signature[5:]))
3738

38-
return hmac.Equal(signBody(secret, body), actual)
39+
return hmac.Equal(signBody(secret, h.Payload), actual)
3940
}
4041

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)
4445
if !strings.EqualFold(req.Method, "POST") {
4546
return nil, errors.New("Unknown method!")
4647
}
@@ -57,17 +58,15 @@ func Parse(secret []byte, req *http.Request) (*Hook, error) {
5758
return nil, errors.New("No event Id!")
5859
}
5960

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+
}
6564

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")
6870
}
69-
70-
hook.Payload = body
71-
72-
return &hook, nil
71+
return
7372
}

githubhook_test.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,11 @@ func expectErrorMessage(t *testing.T, msg string, err error) {
1818
}
1919
}
2020

21+
func expectNewError(t *testing.T, msg string, r *http.Request) {
22+
_, err := New(r)
23+
expectErrorMessage(t, msg, err)
24+
}
25+
2126
func expectParseError(t *testing.T, msg string, r *http.Request) {
2227
_, err := Parse([]byte(testSecret), r)
2328
expectErrorMessage(t, msg, err)
@@ -33,25 +38,25 @@ func signature(body string) string {
3338

3439
func TestNonPost(t *testing.T) {
3540
r, _ := http.NewRequest("GET", "/path", nil)
36-
expectParseError(t, "Unknown method!", r)
41+
expectNewError(t, "Unknown method!", r)
3742
}
3843

3944
func TestMissingSignature(t *testing.T) {
4045
r, _ := http.NewRequest("POST", "/path", nil)
41-
expectParseError(t, "No signature!", r)
46+
expectNewError(t, "No signature!", r)
4247
}
4348

4449
func TestMissingEvent(t *testing.T) {
4550
r, _ := http.NewRequest("POST", "/path", nil)
4651
r.Header.Add("x-hub-signature", "bogus signature")
47-
expectParseError(t, "No event!", r)
52+
expectNewError(t, "No event!", r)
4853
}
4954

5055
func TestMissingEventId(t *testing.T) {
5156
r, _ := http.NewRequest("POST", "/path", nil)
5257
r.Header.Add("x-hub-signature", "bogus signature")
5358
r.Header.Add("x-github-event", "bogus event")
54-
expectParseError(t, "No event Id!", r)
59+
expectNewError(t, "No event Id!", r)
5560
}
5661

5762
func TestInvalidSignature(t *testing.T) {

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy