• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

go-playground / webhooks / 12715633619

10 Jan 2025 07:02PM CUT coverage: 88.976% (+0.04%) from 88.933%
12715633619

Pull #203

github

vigo
add tests
Pull Request #203: feat: add GitHub star webhook

4 of 4 new or added lines in 1 file covered. (100.0%)

904 of 1016 relevant lines covered (88.98%)

8.84 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.04
/github/github.go
1
package github
2

3
import (
4
        "crypto/hmac"
5
        "crypto/sha256"
6
        "encoding/hex"
7
        "encoding/json"
8
        "errors"
9
        "fmt"
10
        "io"
11
        "net/http"
12
        "strings"
13
)
14

15
// parse errors
16
var (
17
        ErrEventNotSpecifiedToParse  = errors.New("no Event specified to parse")
18
        ErrInvalidHTTPMethod         = errors.New("invalid HTTP Method")
19
        ErrMissingGithubEventHeader  = errors.New("missing X-GitHub-Event Header")
20
        ErrMissingHubSignatureHeader = errors.New("missing X-Hub-Signature-256 Header")
21
        ErrEventNotFound             = errors.New("event not defined to be parsed")
22
        ErrParsingPayload            = errors.New("error parsing payload")
23
        ErrHMACVerificationFailed    = errors.New("HMAC verification failed")
24
)
25

26
// Event defines a GitHub hook event type
27
type Event string
28

29
// GitHub hook types
30
const (
31
        CheckRunEvent                            Event = "check_run"
32
        CheckSuiteEvent                          Event = "check_suite"
33
        CommitCommentEvent                       Event = "commit_comment"
34
        CreateEvent                              Event = "create"
35
        DeleteEvent                              Event = "delete"
36
        DependabotAlertEvent                     Event = "dependabot_alert"
37
        DeployKeyEvent                           Event = "deploy_key"
38
        DeploymentEvent                          Event = "deployment"
39
        DeploymentStatusEvent                    Event = "deployment_status"
40
        ForkEvent                                Event = "fork"
41
        GollumEvent                              Event = "gollum"
42
        InstallationEvent                        Event = "installation"
43
        InstallationRepositoriesEvent            Event = "installation_repositories"
44
        IntegrationInstallationEvent             Event = "integration_installation"
45
        IntegrationInstallationRepositoriesEvent Event = "integration_installation_repositories"
46
        IssueCommentEvent                        Event = "issue_comment"
47
        IssuesEvent                              Event = "issues"
48
        LabelEvent                               Event = "label"
49
        MemberEvent                              Event = "member"
50
        MembershipEvent                          Event = "membership"
51
        MilestoneEvent                           Event = "milestone"
52
        MetaEvent                                Event = "meta"
53
        OrganizationEvent                        Event = "organization"
54
        OrgBlockEvent                            Event = "org_block"
55
        PageBuildEvent                           Event = "page_build"
56
        PingEvent                                Event = "ping"
57
        ProjectCardEvent                         Event = "project_card"
58
        ProjectColumnEvent                       Event = "project_column"
59
        ProjectEvent                             Event = "project"
60
        PublicEvent                              Event = "public"
61
        PullRequestEvent                         Event = "pull_request"
62
        PullRequestReviewEvent                   Event = "pull_request_review"
63
        PullRequestReviewCommentEvent            Event = "pull_request_review_comment"
64
        PushEvent                                Event = "push"
65
        ReleaseEvent                             Event = "release"
66
        RepositoryEvent                          Event = "repository"
67
        RepositoryVulnerabilityAlertEvent        Event = "repository_vulnerability_alert"
68
        SecurityAdvisoryEvent                    Event = "security_advisory"
69
        StarEvent                                Event = "star"
70
        StatusEvent                              Event = "status"
71
        TeamEvent                                Event = "team"
72
        TeamAddEvent                             Event = "team_add"
73
        WatchEvent                               Event = "watch"
74
        WorkflowDispatchEvent                    Event = "workflow_dispatch"
75
        WorkflowJobEvent                         Event = "workflow_job"
76
        WorkflowRunEvent                         Event = "workflow_run"
77
        GitHubAppAuthorizationEvent              Event = "github_app_authorization"
78
        CodeScanningAlertEvent                   Event = "code_scanning_alert"
79
)
80

81
// EventSubtype defines a GitHub Hook Event subtype
82
type EventSubtype string
83

84
// GitHub hook event subtypes
85
const (
86
        NoSubtype     EventSubtype = ""
87
        BranchSubtype EventSubtype = "branch"
88
        TagSubtype    EventSubtype = "tag"
89
        PullSubtype   EventSubtype = "pull"
90
        IssueSubtype  EventSubtype = "issues"
91
)
92

93
// Option is a configuration option for the webhook
94
type Option func(*Webhook) error
95

96
// Options is a namespace var for configuration options
97
var Options = WebhookOptions{}
98

99
// WebhookOptions is a namespace for configuration option methods
100
type WebhookOptions struct{}
101

102
// Secret registers the GitHub secret
103
func (WebhookOptions) Secret(secret string) Option {
1✔
104
        return func(hook *Webhook) error {
2✔
105
                hook.secret = secret
1✔
106
                return nil
1✔
107
        }
1✔
108
}
109

110
// Webhook instance contains all methods needed to process events
111
type Webhook struct {
112
        secret string
113
}
114

115
// New creates and returns a WebHook instance denoted by the Provider type
116
func New(options ...Option) (*Webhook, error) {
1✔
117
        hook := new(Webhook)
1✔
118
        for _, opt := range options {
2✔
119
                if err := opt(hook); err != nil {
1✔
120
                        return nil, errors.New("Error applying Option")
×
121
                }
×
122
        }
123
        return hook, nil
1✔
124
}
125

126
// Parse verifies and parses the events specified and returns the payload object or an error
127
func (hook Webhook) Parse(r *http.Request, events ...Event) (interface{}, error) {
55✔
128
        defer func() {
110✔
129
                _, _ = io.Copy(io.Discard, r.Body)
55✔
130
                _ = r.Body.Close()
55✔
131
        }()
55✔
132

133
        if len(events) == 0 {
55✔
134
                return nil, ErrEventNotSpecifiedToParse
×
135
        }
×
136
        if r.Method != http.MethodPost {
55✔
137
                return nil, ErrInvalidHTTPMethod
×
138
        }
×
139

140
        event := r.Header.Get("X-GitHub-Event")
55✔
141
        if event == "" {
56✔
142
                return nil, ErrMissingGithubEventHeader
1✔
143
        }
1✔
144
        gitHubEvent := Event(event)
54✔
145

54✔
146
        var found bool
54✔
147
        for _, evt := range events {
108✔
148
                if evt == gitHubEvent {
107✔
149
                        found = true
53✔
150
                        break
53✔
151
                }
152
        }
153
        // event not defined to be parsed
154
        if !found {
55✔
155
                return nil, ErrEventNotFound
1✔
156
        }
1✔
157

158
        payload, err := io.ReadAll(r.Body)
53✔
159
        if err != nil || len(payload) == 0 {
54✔
160
                return nil, ErrParsingPayload
1✔
161
        }
1✔
162

163
        // If we have a Secret set, we should check the MAC
164
        if len(hook.secret) > 0 {
104✔
165
                signature := r.Header.Get("X-Hub-Signature-256")
52✔
166
                if len(signature) == 0 {
53✔
167
                        return nil, ErrMissingHubSignatureHeader
1✔
168
                }
1✔
169

170
                signature = strings.TrimPrefix(signature, "sha256=")
51✔
171

51✔
172
                mac := hmac.New(sha256.New, []byte(hook.secret))
51✔
173
                _, _ = mac.Write(payload)
51✔
174
                expectedMAC := hex.EncodeToString(mac.Sum(nil))
51✔
175

51✔
176
                if !hmac.Equal([]byte(signature), []byte(expectedMAC)) {
52✔
177
                        return nil, ErrHMACVerificationFailed
1✔
178
                }
1✔
179
        }
180

181
        switch gitHubEvent {
50✔
182
        case CheckRunEvent:
1✔
183
                var pl CheckRunPayload
1✔
184
                err = json.Unmarshal([]byte(payload), &pl)
1✔
185
                return pl, err
1✔
186
        case CheckSuiteEvent:
1✔
187
                var pl CheckSuitePayload
1✔
188
                err = json.Unmarshal([]byte(payload), &pl)
1✔
189
                return pl, err
1✔
190
        case CommitCommentEvent:
1✔
191
                var pl CommitCommentPayload
1✔
192
                err = json.Unmarshal([]byte(payload), &pl)
1✔
193
                return pl, err
1✔
194
        case CreateEvent:
1✔
195
                var pl CreatePayload
1✔
196
                err = json.Unmarshal([]byte(payload), &pl)
1✔
197
                return pl, err
1✔
198
        case DeployKeyEvent:
1✔
199
                var pl DeployKeyPayload
1✔
200
                err = json.Unmarshal([]byte(payload), &pl)
1✔
201
                return pl, err
1✔
202
        case DeleteEvent:
1✔
203
                var pl DeletePayload
1✔
204
                err = json.Unmarshal([]byte(payload), &pl)
1✔
205
                return pl, err
1✔
206
        case DependabotAlertEvent:
1✔
207
                var pl DependabotAlertPayload
1✔
208
                err = json.Unmarshal([]byte(payload), &pl)
1✔
209
                return pl, err
1✔
210
        case DeploymentEvent:
1✔
211
                var pl DeploymentPayload
1✔
212
                err = json.Unmarshal([]byte(payload), &pl)
1✔
213
                return pl, err
1✔
214
        case DeploymentStatusEvent:
1✔
215
                var pl DeploymentStatusPayload
1✔
216
                err = json.Unmarshal([]byte(payload), &pl)
1✔
217
                return pl, err
1✔
218
        case ForkEvent:
1✔
219
                var pl ForkPayload
1✔
220
                err = json.Unmarshal([]byte(payload), &pl)
1✔
221
                return pl, err
1✔
222
        case GollumEvent:
1✔
223
                var pl GollumPayload
1✔
224
                err = json.Unmarshal([]byte(payload), &pl)
1✔
225
                return pl, err
1✔
226
        case InstallationEvent, IntegrationInstallationEvent:
2✔
227
                var pl InstallationPayload
2✔
228
                err = json.Unmarshal([]byte(payload), &pl)
2✔
229
                return pl, err
2✔
230
        case InstallationRepositoriesEvent, IntegrationInstallationRepositoriesEvent:
2✔
231
                var pl InstallationRepositoriesPayload
2✔
232
                err = json.Unmarshal([]byte(payload), &pl)
2✔
233
                return pl, err
2✔
234
        case IssueCommentEvent:
2✔
235
                var pl IssueCommentPayload
2✔
236
                err = json.Unmarshal([]byte(payload), &pl)
2✔
237
                return pl, err
2✔
238
        case IssuesEvent:
1✔
239
                var pl IssuesPayload
1✔
240
                err = json.Unmarshal([]byte(payload), &pl)
1✔
241
                return pl, err
1✔
242
        case LabelEvent:
1✔
243
                var pl LabelPayload
1✔
244
                err = json.Unmarshal([]byte(payload), &pl)
1✔
245
                return pl, err
1✔
246
        case MemberEvent:
1✔
247
                var pl MemberPayload
1✔
248
                err = json.Unmarshal([]byte(payload), &pl)
1✔
249
                return pl, err
1✔
250
        case MembershipEvent:
1✔
251
                var pl MembershipPayload
1✔
252
                err = json.Unmarshal([]byte(payload), &pl)
1✔
253
                return pl, err
1✔
254
        case MetaEvent:
×
255
                var pl MetaPayload
×
256
                err = json.Unmarshal([]byte(payload), &pl)
×
257
                return pl, err
×
258
        case MilestoneEvent:
1✔
259
                var pl MilestonePayload
1✔
260
                err = json.Unmarshal([]byte(payload), &pl)
1✔
261
                return pl, err
1✔
262
        case OrganizationEvent:
1✔
263
                var pl OrganizationPayload
1✔
264
                err = json.Unmarshal([]byte(payload), &pl)
1✔
265
                return pl, err
1✔
266
        case OrgBlockEvent:
1✔
267
                var pl OrgBlockPayload
1✔
268
                err = json.Unmarshal([]byte(payload), &pl)
1✔
269
                return pl, err
1✔
270
        case PageBuildEvent:
1✔
271
                var pl PageBuildPayload
1✔
272
                err = json.Unmarshal([]byte(payload), &pl)
1✔
273
                return pl, err
1✔
274
        case PingEvent:
1✔
275
                var pl PingPayload
1✔
276
                err = json.Unmarshal([]byte(payload), &pl)
1✔
277
                return pl, err
1✔
278
        case ProjectCardEvent:
1✔
279
                var pl ProjectCardPayload
1✔
280
                err = json.Unmarshal([]byte(payload), &pl)
1✔
281
                return pl, err
1✔
282
        case ProjectColumnEvent:
1✔
283
                var pl ProjectColumnPayload
1✔
284
                err = json.Unmarshal([]byte(payload), &pl)
1✔
285
                return pl, err
1✔
286
        case ProjectEvent:
1✔
287
                var pl ProjectPayload
1✔
288
                err = json.Unmarshal([]byte(payload), &pl)
1✔
289
                return pl, err
1✔
290
        case PublicEvent:
1✔
291
                var pl PublicPayload
1✔
292
                err = json.Unmarshal([]byte(payload), &pl)
1✔
293
                return pl, err
1✔
294
        case PullRequestEvent:
1✔
295
                var pl PullRequestPayload
1✔
296
                err = json.Unmarshal([]byte(payload), &pl)
1✔
297
                return pl, err
1✔
298
        case PullRequestReviewEvent:
1✔
299
                var pl PullRequestReviewPayload
1✔
300
                err = json.Unmarshal([]byte(payload), &pl)
1✔
301
                return pl, err
1✔
302
        case PullRequestReviewCommentEvent:
1✔
303
                var pl PullRequestReviewCommentPayload
1✔
304
                err = json.Unmarshal([]byte(payload), &pl)
1✔
305
                return pl, err
1✔
306
        case PushEvent:
1✔
307
                var pl PushPayload
1✔
308
                err = json.Unmarshal([]byte(payload), &pl)
1✔
309
                return pl, err
1✔
310
        case ReleaseEvent:
1✔
311
                var pl ReleasePayload
1✔
312
                err = json.Unmarshal([]byte(payload), &pl)
1✔
313
                return pl, err
1✔
314
        case RepositoryEvent:
2✔
315
                var pl RepositoryPayload
2✔
316
                err = json.Unmarshal([]byte(payload), &pl)
2✔
317
                return pl, err
2✔
318
        case RepositoryVulnerabilityAlertEvent:
1✔
319
                var pl RepositoryVulnerabilityAlertPayload
1✔
320
                err = json.Unmarshal([]byte(payload), &pl)
1✔
321
                return pl, err
1✔
322
        case SecurityAdvisoryEvent:
1✔
323
                var pl SecurityAdvisoryPayload
1✔
324
                err = json.Unmarshal([]byte(payload), &pl)
1✔
325
                return pl, err
1✔
326
        case StarEvent:
2✔
327
                var pl StarPayload
2✔
328
                err = json.Unmarshal([]byte(payload), &pl)
2✔
329
                return pl, err
2✔
330
        case StatusEvent:
1✔
331
                var pl StatusPayload
1✔
332
                err = json.Unmarshal([]byte(payload), &pl)
1✔
333
                return pl, err
1✔
334
        case TeamEvent:
1✔
335
                var pl TeamPayload
1✔
336
                err = json.Unmarshal([]byte(payload), &pl)
1✔
337
                return pl, err
1✔
338
        case TeamAddEvent:
1✔
339
                var pl TeamAddPayload
1✔
340
                err = json.Unmarshal([]byte(payload), &pl)
1✔
341
                return pl, err
1✔
342
        case WatchEvent:
1✔
343
                var pl WatchPayload
1✔
344
                err = json.Unmarshal([]byte(payload), &pl)
1✔
345
                return pl, err
1✔
346
        case WorkflowDispatchEvent:
1✔
347
                var pl WorkflowDispatchPayload
1✔
348
                err = json.Unmarshal([]byte(payload), &pl)
1✔
349
                return pl, err
1✔
350
        case WorkflowJobEvent:
1✔
351
                var pl WorkflowJobPayload
1✔
352
                err = json.Unmarshal([]byte(payload), &pl)
1✔
353
                return pl, err
1✔
354
        case WorkflowRunEvent:
1✔
355
                var pl WorkflowRunPayload
1✔
356
                err = json.Unmarshal([]byte(payload), &pl)
1✔
357
                return pl, err
1✔
358
        case GitHubAppAuthorizationEvent:
1✔
359
                var pl GitHubAppAuthorizationPayload
1✔
360
                err = json.Unmarshal([]byte(payload), &pl)
1✔
361
                return pl, err
1✔
362
        case CodeScanningAlertEvent:
1✔
363
                var pl CodeScanningAlertPayload
1✔
364
                err = json.Unmarshal([]byte(payload), &pl)
1✔
365
                return pl, err
1✔
366
        default:
×
367
                return nil, fmt.Errorf("unknown event %s", gitHubEvent)
×
368
        }
369
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc