github.com/google/go-github/v33@v33.0.0/github/messages_test.go (about) 1 // Copyright 2016 The go-github AUTHORS. All rights reserved. 2 // 3 // Use of this source code is governed by a BSD-style 4 // license that can be found in the LICENSE file. 5 6 package github 7 8 import ( 9 "bytes" 10 "encoding/json" 11 "net/http" 12 "net/url" 13 "reflect" 14 "strings" 15 "testing" 16 ) 17 18 func TestValidatePayload(t *testing.T) { 19 const defaultBody = `{"yo":true}` // All tests below use the default request body and signature. 20 const defaultSignature = "sha1=126f2c800419c60137ce748d7672e77b65cf16d6" 21 secretKey := []byte("0123456789abcdef") 22 tests := []struct { 23 signature string 24 eventID string 25 event string 26 wantEventID string 27 wantEvent string 28 wantPayload string 29 }{ 30 // The following tests generate expected errors: 31 {}, // Missing signature 32 {signature: "yo"}, // Missing signature prefix 33 {signature: "sha1=yo"}, // Signature not hex string 34 {signature: "sha1=012345"}, // Invalid signature 35 // The following tests expect err=nil: 36 { 37 signature: defaultSignature, 38 eventID: "dead-beef", 39 event: "ping", 40 wantEventID: "dead-beef", 41 wantEvent: "ping", 42 wantPayload: defaultBody, 43 }, 44 { 45 signature: defaultSignature, 46 event: "ping", 47 wantEvent: "ping", 48 wantPayload: defaultBody, 49 }, 50 { 51 signature: "sha256=b1f8020f5b4cd42042f807dd939015c4a418bc1ff7f604dd55b0a19b5d953d9b", 52 event: "ping", 53 wantEvent: "ping", 54 wantPayload: defaultBody, 55 }, 56 { 57 signature: "sha512=8456767023c1195682e182a23b3f5d19150ecea598fde8cb85918f7281b16079471b1329f92b912c4d8bd7455cb159777db8f29608b20c7c87323ba65ae62e1f", 58 event: "ping", 59 wantEvent: "ping", 60 wantPayload: defaultBody, 61 }, 62 } 63 64 for _, test := range tests { 65 buf := bytes.NewBufferString(defaultBody) 66 req, err := http.NewRequest("GET", "http://localhost/event", buf) 67 if err != nil { 68 t.Fatalf("NewRequest: %v", err) 69 } 70 if test.signature != "" { 71 req.Header.Set(signatureHeader, test.signature) 72 } 73 req.Header.Set("Content-Type", "application/json") 74 75 got, err := ValidatePayload(req, secretKey) 76 if err != nil { 77 if test.wantPayload != "" { 78 t.Errorf("ValidatePayload(%#v): err = %v, want nil", test, err) 79 } 80 continue 81 } 82 if string(got) != test.wantPayload { 83 t.Errorf("ValidatePayload = %q, want %q", got, test.wantPayload) 84 } 85 } 86 } 87 88 func TestValidatePayload_FormGet(t *testing.T) { 89 payload := `{"yo":true}` 90 signature := "sha1=3374ef144403e8035423b23b02e2c9d7a4c50368" 91 secretKey := []byte("0123456789abcdef") 92 93 form := url.Values{} 94 form.Add("payload", payload) 95 req, err := http.NewRequest("POST", "http://localhost/event", strings.NewReader(form.Encode())) 96 if err != nil { 97 t.Fatalf("NewRequest: %v", err) 98 } 99 req.PostForm = form 100 req.Header.Set("Content-Type", "application/x-www-form-urlencoded") 101 req.Header.Set(signatureHeader, signature) 102 103 got, err := ValidatePayload(req, secretKey) 104 if err != nil { 105 t.Errorf("ValidatePayload(%#v): err = %v, want nil", payload, err) 106 } 107 if string(got) != payload { 108 t.Errorf("ValidatePayload = %q, want %q", got, payload) 109 } 110 111 // check that if payload is invalid we get error 112 req.Header.Set(signatureHeader, "invalid signature") 113 if _, err = ValidatePayload(req, []byte{0}); err == nil { 114 t.Error("ValidatePayload = nil, want err") 115 } 116 } 117 118 func TestValidatePayload_FormPost(t *testing.T) { 119 payload := `{"yo":true}` 120 signature := "sha1=3374ef144403e8035423b23b02e2c9d7a4c50368" 121 secretKey := []byte("0123456789abcdef") 122 123 form := url.Values{} 124 form.Set("payload", payload) 125 buf := bytes.NewBufferString(form.Encode()) 126 req, err := http.NewRequest("POST", "http://localhost/event", buf) 127 if err != nil { 128 t.Fatalf("NewRequest: %v", err) 129 } 130 req.Header.Set("Content-Type", "application/x-www-form-urlencoded") 131 req.Header.Set(signatureHeader, signature) 132 133 got, err := ValidatePayload(req, secretKey) 134 if err != nil { 135 t.Errorf("ValidatePayload(%#v): err = %v, want nil", payload, err) 136 } 137 if string(got) != payload { 138 t.Errorf("ValidatePayload = %q, want %q", got, payload) 139 } 140 141 // check that if payload is invalid we get error 142 req.Header.Set(signatureHeader, "invalid signature") 143 if _, err = ValidatePayload(req, []byte{0}); err == nil { 144 t.Error("ValidatePayload = nil, want err") 145 } 146 } 147 148 func TestValidatePayload_InvalidContentType(t *testing.T) { 149 req, err := http.NewRequest("POST", "http://localhost/event", nil) 150 if err != nil { 151 t.Fatalf("NewRequest: %v", err) 152 } 153 req.Header.Set("Content-Type", "invalid content type") 154 if _, err = ValidatePayload(req, nil); err == nil { 155 t.Error("ValidatePayload = nil, want err") 156 } 157 } 158 159 func TestValidatePayload_NoSecretKey(t *testing.T) { 160 payload := `{"yo":true}` 161 162 form := url.Values{} 163 form.Set("payload", payload) 164 buf := bytes.NewBufferString(form.Encode()) 165 req, err := http.NewRequest("POST", "http://localhost/event", buf) 166 if err != nil { 167 t.Fatalf("NewRequest: %v", err) 168 } 169 req.Header.Set("Content-Type", "application/x-www-form-urlencoded") 170 171 got, err := ValidatePayload(req, nil) 172 if err != nil { 173 t.Errorf("ValidatePayload(%#v): err = %v, want nil", payload, err) 174 } 175 if string(got) != payload { 176 t.Errorf("ValidatePayload = %q, want %q", got, payload) 177 } 178 } 179 180 func TestParseWebHook(t *testing.T) { 181 tests := []struct { 182 payload interface{} 183 messageType string 184 }{ 185 { 186 payload: &CheckRunEvent{}, 187 messageType: "check_run", 188 }, 189 { 190 payload: &CheckSuiteEvent{}, 191 messageType: "check_suite", 192 }, 193 { 194 payload: &CommitCommentEvent{}, 195 messageType: "commit_comment", 196 }, 197 { 198 payload: &ContentReferenceEvent{}, 199 messageType: "content_reference", 200 }, 201 { 202 payload: &CreateEvent{}, 203 messageType: "create", 204 }, 205 { 206 payload: &DeleteEvent{}, 207 messageType: "delete", 208 }, 209 { 210 payload: &DeployKeyEvent{}, 211 messageType: "deploy_key", 212 }, 213 { 214 payload: &DeploymentEvent{}, 215 messageType: "deployment", 216 }, 217 218 { 219 payload: &DeploymentStatusEvent{}, 220 messageType: "deployment_status", 221 }, 222 { 223 payload: &ForkEvent{}, 224 messageType: "fork", 225 }, 226 { 227 payload: &GitHubAppAuthorizationEvent{}, 228 messageType: "github_app_authorization", 229 }, 230 { 231 payload: &GollumEvent{}, 232 messageType: "gollum", 233 }, 234 { 235 payload: &InstallationEvent{}, 236 messageType: "installation", 237 }, 238 { 239 payload: &InstallationRepositoriesEvent{}, 240 messageType: "installation_repositories", 241 }, 242 { 243 payload: &IssueCommentEvent{}, 244 messageType: "issue_comment", 245 }, 246 { 247 payload: &IssuesEvent{}, 248 messageType: "issues", 249 }, 250 { 251 payload: &LabelEvent{}, 252 messageType: "label", 253 }, 254 { 255 payload: &MarketplacePurchaseEvent{}, 256 messageType: "marketplace_purchase", 257 }, 258 { 259 payload: &MemberEvent{}, 260 messageType: "member", 261 }, 262 { 263 payload: &MembershipEvent{}, 264 messageType: "membership", 265 }, 266 { 267 payload: &MetaEvent{}, 268 messageType: "meta", 269 }, 270 { 271 payload: &MilestoneEvent{}, 272 messageType: "milestone", 273 }, 274 { 275 payload: &OrganizationEvent{}, 276 messageType: "organization", 277 }, 278 { 279 payload: &OrgBlockEvent{}, 280 messageType: "org_block", 281 }, 282 { 283 payload: &PackageEvent{}, 284 messageType: "package", 285 }, 286 { 287 payload: &PageBuildEvent{}, 288 messageType: "page_build", 289 }, 290 { 291 payload: &PingEvent{}, 292 messageType: "ping", 293 }, 294 { 295 payload: &ProjectEvent{}, 296 messageType: "project", 297 }, 298 { 299 payload: &ProjectCardEvent{}, 300 messageType: "project_card", 301 }, 302 { 303 payload: &ProjectColumnEvent{}, 304 messageType: "project_column", 305 }, 306 { 307 payload: &PublicEvent{}, 308 messageType: "public", 309 }, 310 { 311 payload: &PullRequestEvent{}, 312 messageType: "pull_request", 313 }, 314 { 315 payload: &PullRequestReviewEvent{}, 316 messageType: "pull_request_review", 317 }, 318 { 319 payload: &PullRequestReviewCommentEvent{}, 320 messageType: "pull_request_review_comment", 321 }, 322 { 323 payload: &PushEvent{}, 324 messageType: "push", 325 }, 326 { 327 payload: &ReleaseEvent{}, 328 messageType: "release", 329 }, 330 { 331 payload: &RepositoryEvent{}, 332 messageType: "repository", 333 }, 334 { 335 payload: &RepositoryVulnerabilityAlertEvent{}, 336 messageType: "repository_vulnerability_alert", 337 }, 338 { 339 payload: &StarEvent{}, 340 messageType: "star", 341 }, 342 { 343 payload: &StatusEvent{}, 344 messageType: "status", 345 }, 346 { 347 payload: &TeamEvent{}, 348 messageType: "team", 349 }, 350 { 351 payload: &TeamAddEvent{}, 352 messageType: "team_add", 353 }, 354 { 355 payload: &UserEvent{}, 356 messageType: "user", 357 }, 358 { 359 payload: &WatchEvent{}, 360 messageType: "watch", 361 }, 362 { 363 payload: &RepositoryDispatchEvent{}, 364 messageType: "repository_dispatch", 365 }, 366 { 367 payload: &WorkflowDispatchEvent{}, 368 messageType: "workflow_dispatch", 369 }, 370 { 371 payload: &WorkflowRunEvent{}, 372 messageType: "workflow_run", 373 }, 374 } 375 376 for _, test := range tests { 377 p, err := json.Marshal(test.payload) 378 if err != nil { 379 t.Fatalf("Marshal(%#v): %v", test.payload, err) 380 } 381 got, err := ParseWebHook(test.messageType, p) 382 if err != nil { 383 t.Fatalf("ParseWebHook: %v", err) 384 } 385 if want := test.payload; !reflect.DeepEqual(got, want) { 386 t.Errorf("ParseWebHook(%#v, %#v) = %#v, want %#v", test.messageType, p, got, want) 387 } 388 } 389 } 390 391 func TestDeliveryID(t *testing.T) { 392 id := "8970a780-244e-11e7-91ca-da3aabcb9793" 393 req, err := http.NewRequest("POST", "http://localhost", nil) 394 if err != nil { 395 t.Fatalf("DeliveryID: %v", err) 396 } 397 req.Header.Set("X-Github-Delivery", id) 398 399 got := DeliveryID(req) 400 if got != id { 401 t.Errorf("DeliveryID(%#v) = %q, want %q", req, got, id) 402 } 403 }