1
1
package pipelineascode
2
2
3
3
import (
4
+ "crypto/hmac"
5
+ "crypto/sha256"
6
+ "encoding/hex"
7
+ "fmt"
8
+ "net/http"
4
9
"strings"
5
10
"testing"
6
11
@@ -9,10 +14,12 @@ import (
9
14
"github.com/openshift-pipelines/pipelines-as-code/pkg/apis/pipelinesascode/v1alpha1"
10
15
"github.com/openshift-pipelines/pipelines-as-code/pkg/consoleui"
11
16
"github.com/openshift-pipelines/pipelines-as-code/pkg/events"
17
+ "github.com/openshift-pipelines/pipelines-as-code/pkg/opscomments"
12
18
"github.com/openshift-pipelines/pipelines-as-code/pkg/params"
13
19
"github.com/openshift-pipelines/pipelines-as-code/pkg/params/clients"
14
20
"github.com/openshift-pipelines/pipelines-as-code/pkg/params/info"
15
21
"github.com/openshift-pipelines/pipelines-as-code/pkg/params/settings"
22
+ "github.com/openshift-pipelines/pipelines-as-code/pkg/params/triggertype"
16
23
ghprovider "github.com/openshift-pipelines/pipelines-as-code/pkg/provider/github"
17
24
testclient "github.com/openshift-pipelines/pipelines-as-code/pkg/test/clients"
18
25
ghtesthelper "github.com/openshift-pipelines/pipelines-as-code/pkg/test/github"
@@ -281,3 +288,263 @@ func TestGetPipelineRunsFromRepo(t *testing.T) {
281
288
})
282
289
}
283
290
}
291
+
292
+ func TestVerifyRepoAndUser (t * testing.T ) {
293
+ observerCore , _ := zapobserver .New (zap .InfoLevel )
294
+ logger := zap .New (observerCore ).Sugar ()
295
+
296
+ payload := []byte (`{"key": "value"}` )
297
+ mac := hmac .New (sha256 .New , []byte ("secret" ))
298
+ mac .Write (payload )
299
+ sha256secret := hex .EncodeToString (mac .Sum (nil ))
300
+
301
+ header := make (http.Header )
302
+ header .Set (github .SHA256SignatureHeader , fmt .Sprintf ("sha256=%s" , sha256secret ))
303
+
304
+ request := & info.Request {
305
+ Header : header ,
306
+ Payload : payload ,
307
+ }
308
+
309
+ tests := []struct {
310
+ name string
311
+ runevent info.Event
312
+ repositories []* v1alpha1.Repository
313
+ webhookSecret string
314
+ wantRepoNil bool
315
+ wantErr bool
316
+ wantErrMsg string
317
+ }{
318
+ {
319
+ name : "no repository match" ,
320
+ runevent : info.Event {
321
+ Organization : "owner" ,
322
+ Repository : "repo" ,
323
+ URL : "https://example.com/owner/repo" ,
324
+ SHA : "123abc" ,
325
+ EventType : triggertype .PullRequest .String (),
326
+ TriggerTarget : triggertype .PullRequest ,
327
+ },
328
+ wantRepoNil : true ,
329
+ wantErr : false ,
330
+ },
331
+ {
332
+ name : "missing git_provider section" ,
333
+ runevent : info.Event {
334
+ Organization : "owner" ,
335
+ Repository : "repo" ,
336
+ URL : "https://example.com/owner/repo" ,
337
+ SHA : "123abc" ,
338
+ EventType : triggertype .PullRequest .String (),
339
+ TriggerTarget : triggertype .PullRequest ,
340
+ },
341
+ repositories : []* v1alpha1.Repository {{
342
+ ObjectMeta : metav1.ObjectMeta {Name : "repo" , Namespace : "ns" },
343
+ Spec : v1alpha1.RepositorySpec {URL : "https://example.com/owner/repo" },
344
+ }},
345
+ wantRepoNil : false ,
346
+ wantErr : true ,
347
+ wantErrMsg : "cannot get secret from repository: failed to find git_provider details in repository spec: ns/repo" ,
348
+ },
349
+ {
350
+ name : "webhook validation failure" ,
351
+ runevent : info.Event {
352
+ Organization : "owner" ,
353
+ Repository : "repo" ,
354
+ URL : "https://example.com/owner/repo" ,
355
+ SHA : "123abc" ,
356
+ EventType : triggertype .PullRequest .String (),
357
+ TriggerTarget : triggertype .PullRequest ,
358
+ InstallationID : 1 ,
359
+ Request : & info.Request {Header : http.Header {}},
360
+ },
361
+ repositories : []* v1alpha1.Repository {{
362
+ ObjectMeta : metav1.ObjectMeta {Name : "repo" , Namespace : "ns" },
363
+ Spec : v1alpha1.RepositorySpec {URL : "https://example.com/owner/repo" },
364
+ }},
365
+ wantRepoNil : false ,
366
+ wantErr : true ,
367
+ wantErrMsg : "could not validate payload, check your webhook secret?: no signature has been detected, for security reason we are not allowing webhooks that has no secret" ,
368
+ },
369
+ {
370
+ name : "webhook secret is not set" ,
371
+ runevent : info.Event {
372
+ Organization : "owner" ,
373
+ Repository : "repo" ,
374
+ URL : "https://example.com/owner/repo" ,
375
+ SHA : "123abc" ,
376
+ EventType : triggertype .PullRequest .String (),
377
+ TriggerTarget : triggertype .PullRequest ,
378
+ InstallationID : 1 ,
379
+ Request : request ,
380
+ },
381
+ repositories : []* v1alpha1.Repository {{
382
+ ObjectMeta : metav1.ObjectMeta {Name : "repo" , Namespace : "ns" },
383
+ Spec : v1alpha1.RepositorySpec {URL : "https://example.com/owner/repo" },
384
+ }},
385
+ wantRepoNil : false ,
386
+ wantErr : true ,
387
+ wantErrMsg : "could not validate payload, check your webhook secret?: no webhook secret has been set, in repository CR or secret" ,
388
+ },
389
+ {
390
+ name : "permission denied push comment" ,
391
+ runevent : info.Event {
392
+ Organization : "owner" ,
393
+ Repository : "repo" ,
394
+ URL : "https://example.com/owner/repo" ,
395
+ SHA : "123abc" ,
396
+ EventType : opscomments .TestAllCommentEventType .String (),
397
+ TriggerTarget : triggertype .Push ,
398
+ Sender : "intruder" ,
399
+ InstallationID : 1 ,
400
+ Request : request ,
401
+ },
402
+ repositories : []* v1alpha1.Repository {{
403
+ ObjectMeta : metav1.ObjectMeta {Name : "repo" , Namespace : "ns" },
404
+ Spec : v1alpha1.RepositorySpec {URL : "https://example.com/owner/repo" },
405
+ }},
406
+ webhookSecret : "secret" ,
407
+ wantRepoNil : true ,
408
+ wantErr : true ,
409
+ wantErrMsg : "failed to run create status, user is not allowed to run the CI" ,
410
+ },
411
+ {
412
+ name : "permission denied pull_request comment pending approval" ,
413
+ runevent : info.Event {
414
+ Organization : "owner" ,
415
+ Repository : "repo" ,
416
+ URL : "https://example.com/owner/repo" ,
417
+ SHA : "123abc" ,
418
+ EventType : triggertype .PullRequest .String (),
419
+ TriggerTarget : triggertype .PullRequest ,
420
+ Sender : "outsider" ,
421
+ InstallationID : 1 ,
422
+ Request : request ,
423
+ },
424
+ repositories : []* v1alpha1.Repository {{
425
+ ObjectMeta : metav1.ObjectMeta {Name : "repo" , Namespace : "ns" },
426
+ Spec : v1alpha1.RepositorySpec {URL : "https://example.com/owner/repo" },
427
+ }},
428
+ webhookSecret : "secret" ,
429
+ wantRepoNil : true ,
430
+ wantErr : true ,
431
+ wantErrMsg : "failed to run create status, user is not allowed to run the CI" ,
432
+ },
433
+ {
434
+ name : "commit not found" ,
435
+ runevent : info.Event {
436
+ Organization : "owner" ,
437
+ Repository : "repo" ,
438
+ URL : "https://example.com/owner/repo" ,
439
+ SHA : "" ,
440
+ EventType : triggertype .PullRequest .String (),
441
+ TriggerTarget : triggertype .PullRequest ,
442
+ InstallationID : 1 ,
443
+ Sender : "owner" ,
444
+ Request : request ,
445
+ },
446
+ repositories : []* v1alpha1.Repository {{
447
+ ObjectMeta : metav1.ObjectMeta {Name : "repo" , Namespace : "ns" },
448
+ Spec : v1alpha1.RepositorySpec {URL : "https://example.com/owner/repo" },
449
+ }},
450
+ webhookSecret : "secret" ,
451
+ wantRepoNil : false ,
452
+ wantErr : true ,
453
+ wantErrMsg : "could not find commit info" ,
454
+ },
455
+ {
456
+ name : "happy path" ,
457
+ runevent : info.Event {
458
+ Organization : "owner" ,
459
+ Repository : "repo" ,
460
+ URL : "https://example.com/owner/repo" ,
461
+ SHA : "123abc" ,
462
+ EventType : triggertype .PullRequest .String (),
463
+ TriggerTarget : triggertype .PullRequest ,
464
+ InstallationID : 1 ,
465
+ Sender : "owner" ,
466
+ Request : request ,
467
+ },
468
+ repositories : []* v1alpha1.Repository {{
469
+ ObjectMeta : metav1.ObjectMeta {Name : "repo" , Namespace : "ns" },
470
+ Spec : v1alpha1.RepositorySpec {URL : "https://example.com/owner/repo" },
471
+ }},
472
+ webhookSecret : "secret" ,
473
+ wantRepoNil : false ,
474
+ wantErr : false ,
475
+ },
476
+ }
477
+
478
+ pacInfo := & info.PacOpts {Settings : settings .DefaultSettings ()}
479
+
480
+ for _ , tt := range tests {
481
+ t .Run (tt .name , func (t * testing.T ) {
482
+ baseCtx , _ := rtesting .SetupFakeContext (t )
483
+ ctx := info .StoreNS (baseCtx , "pac" )
484
+
485
+ ghClient , mux , _ , teardown := ghtesthelper .SetupGH ()
486
+ defer teardown ()
487
+
488
+ // commit endpoint
489
+ commitPath := fmt .Sprintf ("/repos/%s/%s/git/commits/%s" , tt .runevent .Organization , tt .runevent .Repository , tt .runevent .SHA )
490
+ mux .HandleFunc (commitPath , func (rw http.ResponseWriter , _ * http.Request ) {
491
+ if tt .runevent .SHA == "" {
492
+ rw .WriteHeader (http .StatusNotFound )
493
+ return
494
+ }
495
+ fmt .Fprint (rw , `{"sha":"123abc","html_url":"https://example.com/commit/123abc","message":"msg"}` )
496
+ })
497
+
498
+ // org members empty
499
+ mux .HandleFunc (fmt .Sprintf ("/orgs/%s/members" , tt .runevent .Organization ), func (rw http.ResponseWriter , _ * http.Request ) { fmt .Fprint (rw , `[]` ) })
500
+ // collaborator check – return 404 for non‐collaborator sender when defined
501
+ if tt .runevent .Sender != "" && tt .runevent .Sender != tt .runevent .Organization {
502
+ mux .HandleFunc (
503
+ fmt .Sprintf ("/repos/%s/%s/collaborators/%s" , tt .runevent .Organization , tt .runevent .Repository , tt .runevent .Sender ),
504
+ func (rw http.ResponseWriter , _ * http.Request ) { rw .WriteHeader (http .StatusNotFound ) },
505
+ )
506
+ }
507
+ // status endpoint stub (used when CreateStatus is called)
508
+ mux .HandleFunc (
509
+ fmt .Sprintf ("/repos/%s/%s/statuses/%s" , tt .runevent .Organization , tt .runevent .Repository , tt .runevent .SHA ),
510
+ func (rw http.ResponseWriter , _ * http.Request ) { fmt .Fprint (rw , `{}` ) },
511
+ )
512
+
513
+ vcx := & ghprovider.Provider {Token : github .Ptr ("token" ), Logger : logger }
514
+ vcx .SetGithubClient (ghClient )
515
+ vcx .SetPacInfo (pacInfo )
516
+
517
+ k8int := & kitesthelper.KinterfaceTest {GetSecretResult : map [string ]string {"pipelines-as-code-secret" : tt .webhookSecret }}
518
+
519
+ stdata , _ := testclient .SeedTestData (t , ctx , testclient.Data {Repositories : tt .repositories /*Secret: []*corev1.Secret{secret}*/ })
520
+ in := info .NewInfo ()
521
+ cs := & params.Run {
522
+ Info : in ,
523
+ Clients : clients.Clients {
524
+ PipelineAsCode : stdata .PipelineAsCode ,
525
+ Kube : stdata .Kube ,
526
+ Tekton : stdata .Pipeline ,
527
+ Log : logger ,
528
+ },
529
+ }
530
+ cs .Clients .SetConsoleUI (consoleui.FallBackConsole {})
531
+
532
+ ev := tt .runevent
533
+ ev .Provider = & info.Provider {Token : "token" , WebhookSecret : tt .webhookSecret }
534
+
535
+ p := NewPacs (& ev , vcx , cs , pacInfo , k8int , logger , nil )
536
+ repo , err := p .verifyRepoAndUser (ctx )
537
+ assert .Assert (t , (err != nil ) == tt .wantErr )
538
+
539
+ if tt .wantErr {
540
+ assert .ErrorContains (t , err , tt .wantErrMsg )
541
+ }
542
+
543
+ if tt .wantRepoNil {
544
+ assert .Assert (t , repo == nil )
545
+ } else {
546
+ assert .Assert (t , repo != nil )
547
+ }
548
+ })
549
+ }
550
+ }
0 commit comments