github.com/go-playground/webhooks/v6@v6.3.0/github/github.go (about)

     1  package github
     2  
     3  import (
     4  	"crypto/hmac"
     5  	"crypto/sha1"
     6  	"encoding/hex"
     7  	"encoding/json"
     8  	"errors"
     9  	"fmt"
    10  	"io"
    11  	"io/ioutil"
    12  	"net/http"
    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 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  	StatusEvent                              Event = "status"
    70  	TeamEvent                                Event = "team"
    71  	TeamAddEvent                             Event = "team_add"
    72  	WatchEvent                               Event = "watch"
    73  	WorkflowDispatchEvent                    Event = "workflow_dispatch"
    74  	WorkflowJobEvent                         Event = "workflow_job"
    75  	WorkflowRunEvent                         Event = "workflow_run"
    76  	GitHubAppAuthorizationEvent              Event = "github_app_authorization"
    77  )
    78  
    79  // EventSubtype defines a GitHub Hook Event subtype
    80  type EventSubtype string
    81  
    82  // GitHub hook event subtypes
    83  const (
    84  	NoSubtype     EventSubtype = ""
    85  	BranchSubtype EventSubtype = "branch"
    86  	TagSubtype    EventSubtype = "tag"
    87  	PullSubtype   EventSubtype = "pull"
    88  	IssueSubtype  EventSubtype = "issues"
    89  )
    90  
    91  // Option is a configuration option for the webhook
    92  type Option func(*Webhook) error
    93  
    94  // Options is a namespace var for configuration options
    95  var Options = WebhookOptions{}
    96  
    97  // WebhookOptions is a namespace for configuration option methods
    98  type WebhookOptions struct{}
    99  
   100  // Secret registers the GitHub secret
   101  func (WebhookOptions) Secret(secret string) Option {
   102  	return func(hook *Webhook) error {
   103  		hook.secret = secret
   104  		return nil
   105  	}
   106  }
   107  
   108  // Webhook instance contains all methods needed to process events
   109  type Webhook struct {
   110  	secret string
   111  }
   112  
   113  // New creates and returns a WebHook instance denoted by the Provider type
   114  func New(options ...Option) (*Webhook, error) {
   115  	hook := new(Webhook)
   116  	for _, opt := range options {
   117  		if err := opt(hook); err != nil {
   118  			return nil, errors.New("Error applying Option")
   119  		}
   120  	}
   121  	return hook, nil
   122  }
   123  
   124  // Parse verifies and parses the events specified and returns the payload object or an error
   125  func (hook Webhook) Parse(r *http.Request, events ...Event) (interface{}, error) {
   126  	defer func() {
   127  		_, _ = io.Copy(ioutil.Discard, r.Body)
   128  		_ = r.Body.Close()
   129  	}()
   130  
   131  	if len(events) == 0 {
   132  		return nil, ErrEventNotSpecifiedToParse
   133  	}
   134  	if r.Method != http.MethodPost {
   135  		return nil, ErrInvalidHTTPMethod
   136  	}
   137  
   138  	event := r.Header.Get("X-GitHub-Event")
   139  	if event == "" {
   140  		return nil, ErrMissingGithubEventHeader
   141  	}
   142  	gitHubEvent := Event(event)
   143  
   144  	var found bool
   145  	for _, evt := range events {
   146  		if evt == gitHubEvent {
   147  			found = true
   148  			break
   149  		}
   150  	}
   151  	// event not defined to be parsed
   152  	if !found {
   153  		return nil, ErrEventNotFound
   154  	}
   155  
   156  	payload, err := ioutil.ReadAll(r.Body)
   157  	if err != nil || len(payload) == 0 {
   158  		return nil, ErrParsingPayload
   159  	}
   160  
   161  	// If we have a Secret set, we should check the MAC
   162  	if len(hook.secret) > 0 {
   163  		signature := r.Header.Get("X-Hub-Signature")
   164  		if len(signature) == 0 {
   165  			return nil, ErrMissingHubSignatureHeader
   166  		}
   167  		mac := hmac.New(sha1.New, []byte(hook.secret))
   168  		_, _ = mac.Write(payload)
   169  		expectedMAC := hex.EncodeToString(mac.Sum(nil))
   170  
   171  		if !hmac.Equal([]byte(signature[5:]), []byte(expectedMAC)) {
   172  			return nil, ErrHMACVerificationFailed
   173  		}
   174  	}
   175  
   176  	switch gitHubEvent {
   177  	case CheckRunEvent:
   178  		var pl CheckRunPayload
   179  		err = json.Unmarshal([]byte(payload), &pl)
   180  		return pl, err
   181  	case CheckSuiteEvent:
   182  		var pl CheckSuitePayload
   183  		err = json.Unmarshal([]byte(payload), &pl)
   184  		return pl, err
   185  	case CommitCommentEvent:
   186  		var pl CommitCommentPayload
   187  		err = json.Unmarshal([]byte(payload), &pl)
   188  		return pl, err
   189  	case CreateEvent:
   190  		var pl CreatePayload
   191  		err = json.Unmarshal([]byte(payload), &pl)
   192  		return pl, err
   193  	case DeployKeyEvent:
   194  		var pl DeployKeyPayload
   195  		err = json.Unmarshal([]byte(payload), &pl)
   196  		return pl, err
   197  	case DeleteEvent:
   198  		var pl DeletePayload
   199  		err = json.Unmarshal([]byte(payload), &pl)
   200  		return pl, err
   201  	case DependabotAlertEvent:
   202  		var pl DependabotAlertPayload
   203  		err = json.Unmarshal([]byte(payload), &pl)
   204  		return pl, err
   205  	case DeploymentEvent:
   206  		var pl DeploymentPayload
   207  		err = json.Unmarshal([]byte(payload), &pl)
   208  		return pl, err
   209  	case DeploymentStatusEvent:
   210  		var pl DeploymentStatusPayload
   211  		err = json.Unmarshal([]byte(payload), &pl)
   212  		return pl, err
   213  	case ForkEvent:
   214  		var pl ForkPayload
   215  		err = json.Unmarshal([]byte(payload), &pl)
   216  		return pl, err
   217  	case GollumEvent:
   218  		var pl GollumPayload
   219  		err = json.Unmarshal([]byte(payload), &pl)
   220  		return pl, err
   221  	case InstallationEvent, IntegrationInstallationEvent:
   222  		var pl InstallationPayload
   223  		err = json.Unmarshal([]byte(payload), &pl)
   224  		return pl, err
   225  	case InstallationRepositoriesEvent, IntegrationInstallationRepositoriesEvent:
   226  		var pl InstallationRepositoriesPayload
   227  		err = json.Unmarshal([]byte(payload), &pl)
   228  		return pl, err
   229  	case IssueCommentEvent:
   230  		var pl IssueCommentPayload
   231  		err = json.Unmarshal([]byte(payload), &pl)
   232  		return pl, err
   233  	case IssuesEvent:
   234  		var pl IssuesPayload
   235  		err = json.Unmarshal([]byte(payload), &pl)
   236  		return pl, err
   237  	case LabelEvent:
   238  		var pl LabelPayload
   239  		err = json.Unmarshal([]byte(payload), &pl)
   240  		return pl, err
   241  	case MemberEvent:
   242  		var pl MemberPayload
   243  		err = json.Unmarshal([]byte(payload), &pl)
   244  		return pl, err
   245  	case MembershipEvent:
   246  		var pl MembershipPayload
   247  		err = json.Unmarshal([]byte(payload), &pl)
   248  		return pl, err
   249  	case MetaEvent:
   250  		var pl MetaPayload
   251  		err = json.Unmarshal([]byte(payload), &pl)
   252  		return pl, err
   253  	case MilestoneEvent:
   254  		var pl MilestonePayload
   255  		err = json.Unmarshal([]byte(payload), &pl)
   256  		return pl, err
   257  	case OrganizationEvent:
   258  		var pl OrganizationPayload
   259  		err = json.Unmarshal([]byte(payload), &pl)
   260  		return pl, err
   261  	case OrgBlockEvent:
   262  		var pl OrgBlockPayload
   263  		err = json.Unmarshal([]byte(payload), &pl)
   264  		return pl, err
   265  	case PageBuildEvent:
   266  		var pl PageBuildPayload
   267  		err = json.Unmarshal([]byte(payload), &pl)
   268  		return pl, err
   269  	case PingEvent:
   270  		var pl PingPayload
   271  		err = json.Unmarshal([]byte(payload), &pl)
   272  		return pl, err
   273  	case ProjectCardEvent:
   274  		var pl ProjectCardPayload
   275  		err = json.Unmarshal([]byte(payload), &pl)
   276  		return pl, err
   277  	case ProjectColumnEvent:
   278  		var pl ProjectColumnPayload
   279  		err = json.Unmarshal([]byte(payload), &pl)
   280  		return pl, err
   281  	case ProjectEvent:
   282  		var pl ProjectPayload
   283  		err = json.Unmarshal([]byte(payload), &pl)
   284  		return pl, err
   285  	case PublicEvent:
   286  		var pl PublicPayload
   287  		err = json.Unmarshal([]byte(payload), &pl)
   288  		return pl, err
   289  	case PullRequestEvent:
   290  		var pl PullRequestPayload
   291  		err = json.Unmarshal([]byte(payload), &pl)
   292  		return pl, err
   293  	case PullRequestReviewEvent:
   294  		var pl PullRequestReviewPayload
   295  		err = json.Unmarshal([]byte(payload), &pl)
   296  		return pl, err
   297  	case PullRequestReviewCommentEvent:
   298  		var pl PullRequestReviewCommentPayload
   299  		err = json.Unmarshal([]byte(payload), &pl)
   300  		return pl, err
   301  	case PushEvent:
   302  		var pl PushPayload
   303  		err = json.Unmarshal([]byte(payload), &pl)
   304  		return pl, err
   305  	case ReleaseEvent:
   306  		var pl ReleasePayload
   307  		err = json.Unmarshal([]byte(payload), &pl)
   308  		return pl, err
   309  	case RepositoryEvent:
   310  		var pl RepositoryPayload
   311  		err = json.Unmarshal([]byte(payload), &pl)
   312  		return pl, err
   313  	case RepositoryVulnerabilityAlertEvent:
   314  		var pl RepositoryVulnerabilityAlertPayload
   315  		err = json.Unmarshal([]byte(payload), &pl)
   316  		return pl, err
   317  	case SecurityAdvisoryEvent:
   318  		var pl SecurityAdvisoryPayload
   319  		err = json.Unmarshal([]byte(payload), &pl)
   320  		return pl, err
   321  	case StatusEvent:
   322  		var pl StatusPayload
   323  		err = json.Unmarshal([]byte(payload), &pl)
   324  		return pl, err
   325  	case TeamEvent:
   326  		var pl TeamPayload
   327  		err = json.Unmarshal([]byte(payload), &pl)
   328  		return pl, err
   329  	case TeamAddEvent:
   330  		var pl TeamAddPayload
   331  		err = json.Unmarshal([]byte(payload), &pl)
   332  		return pl, err
   333  	case WatchEvent:
   334  		var pl WatchPayload
   335  		err = json.Unmarshal([]byte(payload), &pl)
   336  		return pl, err
   337  	case WorkflowDispatchEvent:
   338  		var pl WorkflowDispatchPayload
   339  		err = json.Unmarshal([]byte(payload), &pl)
   340  		return pl, err
   341  	case WorkflowJobEvent:
   342  		var pl WorkflowJobPayload
   343  		err = json.Unmarshal([]byte(payload), &pl)
   344  		return pl, err
   345  	case WorkflowRunEvent:
   346  		var pl WorkflowRunPayload
   347  		err = json.Unmarshal([]byte(payload), &pl)
   348  		return pl, err
   349  	case GitHubAppAuthorizationEvent:
   350  		var pl GitHubAppAuthorizationPayload
   351  		err = json.Unmarshal([]byte(payload), &pl)
   352  		return pl, err
   353  	default:
   354  		return nil, fmt.Errorf("unknown event %s", gitHubEvent)
   355  	}
   356  }