8000 Breaks up public API (#6) · rjz/githubhook@4242805 · GitHub
[go: up one dir, main page]

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)
0