From a91bd54636bc8ac5cb36f73f626dffca0ed852ad Mon Sep 17 00:00:00 2001 From: Easton Crupper <65553218+ecrupper@users.noreply.github.com> Date: Fri, 5 Jan 2024 13:25:35 -0500 Subject: [PATCH] enhance(secret): use allow event system (#341) * enhance(secret): use allow event system * fix some typos and linter changes * remove repo version of 'Allowed' --- constants/allow_events.go | 1 + database/secret.go | 8 ++ database/secret_test.go | 26 ++-- library/actions/comment.go | 3 +- library/actions/push.go | 3 +- library/actions/push_test.go | 3 +- library/actions/schedule.go | 53 ++++++++ library/actions/schedule_test.go | 98 ++++++++++++++ library/events.go | 71 +++++++++- library/events_test.go | 39 ++++++ library/repo.go | 32 ----- library/repo_test.go | 35 ----- library/secret.go | 65 ++++----- library/secret_test.go | 222 ++++++++++++++++++++++++++----- 14 files changed, 512 insertions(+), 147 deletions(-) create mode 100644 library/actions/schedule.go create mode 100644 library/actions/schedule_test.go diff --git a/constants/allow_events.go b/constants/allow_events.go index 60d6d88c..f5d2cdd9 100644 --- a/constants/allow_events.go +++ b/constants/allow_events.go @@ -20,4 +20,5 @@ const ( AllowDeployCreate AllowCommentCreate AllowCommentEdit + AllowSchedule ) diff --git a/database/secret.go b/database/secret.go index c955fd96..f9dae86d 100644 --- a/database/secret.go +++ b/database/secret.go @@ -51,6 +51,7 @@ type Secret struct { Type sql.NullString `sql:"type"` Images pq.StringArray `sql:"images" gorm:"type:varchar(1000)"` Events pq.StringArray `sql:"events" gorm:"type:varchar(1000)"` + AllowEvents sql.NullInt64 `sql:"allow_events"` AllowCommand sql.NullBool `sql:"allow_command"` CreatedAt sql.NullInt64 `sql:"created_at"` CreatedBy sql.NullString `sql:"created_by"` @@ -151,6 +152,11 @@ func (s *Secret) Nullify() *Secret { s.Type.Valid = false } + // check if the AllowEvents field should be false + if s.AllowEvents.Int64 == 0 { + s.AllowEvents.Valid = false + } + // check if the CreatedAt field should be false if s.CreatedAt.Int64 == 0 { s.CreatedAt.Valid = false @@ -188,6 +194,7 @@ func (s *Secret) ToLibrary() *library.Secret { secret.SetType(s.Type.String) secret.SetImages(s.Images) secret.SetEvents(s.Events) + secret.SetAllowEvents(library.NewEventsFromMask(s.AllowEvents.Int64)) secret.SetAllowCommand(s.AllowCommand.Bool) secret.SetCreatedAt(s.CreatedAt.Int64) secret.SetCreatedBy(s.CreatedBy.String) @@ -274,6 +281,7 @@ func SecretFromLibrary(s *library.Secret) *Secret { Type: sql.NullString{String: s.GetType(), Valid: true}, Images: pq.StringArray(s.GetImages()), Events: pq.StringArray(s.GetEvents()), + AllowEvents: sql.NullInt64{Int64: s.GetAllowEvents().ToDatabase(), Valid: true}, AllowCommand: sql.NullBool{Bool: s.GetAllowCommand(), Valid: true}, CreatedAt: sql.NullInt64{Int64: s.GetCreatedAt(), Valid: true}, CreatedBy: sql.NullString{String: s.GetCreatedBy(), Valid: true}, diff --git a/database/secret_test.go b/database/secret_test.go index 584faa80..f4c6d23c 100644 --- a/database/secret_test.go +++ b/database/secret_test.go @@ -113,17 +113,18 @@ func TestDatabase_Secret_Nullify(t *testing.T) { var s *Secret want := &Secret{ - ID: sql.NullInt64{Int64: 0, Valid: false}, - Org: sql.NullString{String: "", Valid: false}, - Repo: sql.NullString{String: "", Valid: false}, - Team: sql.NullString{String: "", Valid: false}, - Name: sql.NullString{String: "", Valid: false}, - Value: sql.NullString{String: "", Valid: false}, - Type: sql.NullString{String: "", Valid: false}, - CreatedAt: sql.NullInt64{Int64: 0, Valid: false}, - CreatedBy: sql.NullString{String: "", Valid: false}, - UpdatedAt: sql.NullInt64{Int64: 0, Valid: false}, - UpdatedBy: sql.NullString{String: "", Valid: false}, + ID: sql.NullInt64{Int64: 0, Valid: false}, + Org: sql.NullString{String: "", Valid: false}, + Repo: sql.NullString{String: "", Valid: false}, + Team: sql.NullString{String: "", Valid: false}, + Name: sql.NullString{String: "", Valid: false}, + Value: sql.NullString{String: "", Valid: false}, + Type: sql.NullString{String: "", Valid: false}, + AllowEvents: sql.NullInt64{Int64: 0, Valid: false}, + CreatedAt: sql.NullInt64{Int64: 0, Valid: false}, + CreatedBy: sql.NullString{String: "", Valid: false}, + UpdatedAt: sql.NullInt64{Int64: 0, Valid: false}, + UpdatedBy: sql.NullString{String: "", Valid: false}, } // setup tests @@ -168,6 +169,7 @@ func TestDatabase_Secret_ToLibrary(t *testing.T) { want.SetType("repo") want.SetImages([]string{"alpine"}) want.SetEvents([]string{"push", "tag", "deployment"}) + want.SetAllowEvents(library.NewEventsFromMask(1)) want.SetAllowCommand(true) want.SetCreatedAt(tsCreate) want.SetCreatedBy("octocat") @@ -291,6 +293,7 @@ func TestDatabase_SecretFromLibrary(t *testing.T) { s.SetType("repo") s.SetImages([]string{"alpine"}) s.SetEvents([]string{"push", "tag", "deployment"}) + s.SetAllowEvents(library.NewEventsFromMask(1)) s.SetAllowCommand(true) s.SetCreatedAt(tsCreate) s.SetCreatedBy("octocat") @@ -320,6 +323,7 @@ func testSecret() *Secret { Type: sql.NullString{String: "repo", Valid: true}, Images: []string{"alpine"}, Events: []string{"push", "tag", "deployment"}, + AllowEvents: sql.NullInt64{Int64: 1, Valid: true}, AllowCommand: sql.NullBool{Bool: true, Valid: true}, CreatedAt: sql.NullInt64{Int64: tsCreate, Valid: true}, CreatedBy: sql.NullString{String: "octocat", Valid: true}, diff --git a/library/actions/comment.go b/library/actions/comment.go index b221a132..499cfed6 100644 --- a/library/actions/comment.go +++ b/library/actions/comment.go @@ -1,6 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -// -//nolint:dupl // ignore dup code + package actions import "github.com/go-vela/types/constants" diff --git a/library/actions/push.go b/library/actions/push.go index 38b41866..5472af94 100644 --- a/library/actions/push.go +++ b/library/actions/push.go @@ -1,6 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -// -//nolint:dupl // ignore dup code + package actions import "github.com/go-vela/types/constants" diff --git a/library/actions/push_test.go b/library/actions/push_test.go index 181d0112..fbdb3cb7 100644 --- a/library/actions/push_test.go +++ b/library/actions/push_test.go @@ -115,6 +115,7 @@ func testMask() int64 { constants.AllowPullSync | constants.AllowPullReopen | constants.AllowDeployCreate | - constants.AllowCommentCreate, + constants.AllowCommentCreate | + constants.AllowSchedule, ) } diff --git a/library/actions/schedule.go b/library/actions/schedule.go new file mode 100644 index 00000000..a3ca9d36 --- /dev/null +++ b/library/actions/schedule.go @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: Apache-2.0 + +package actions + +import "github.com/go-vela/types/constants" + +// Schedule is the library representation of the various actions associated +// with the schedule event. +type Schedule struct { + Run *bool `json:"run"` +} + +// FromMask returns the Schedule type resulting from the provided integer mask. +func (a *Schedule) FromMask(mask int64) *Schedule { + a.SetRun(mask&constants.AllowSchedule > 0) + + return a +} + +// ToMask returns the integer mask of the values for the Schedule set. +func (a *Schedule) ToMask() int64 { + mask := int64(0) + + if a.GetRun() { + mask = mask | constants.AllowSchedule + } + + return mask +} + +// GetRun returns the Run field from the provided Schedule. If the object is nil, +// or the field within the object is nil, it returns the zero value instead. +func (a *Schedule) GetRun() bool { + // return zero value if Schedule type or Run field is nil + if a == nil || a.Run == nil { + return false + } + + return *a.Run +} + +// SetRun sets the Schedule Run field. +// +// When the provided Schedule type is nil, it +// will set nothing and immediately return. +func (a *Schedule) SetRun(v bool) { + // return if Schedule type is nil + if a == nil { + return + } + + a.Run = &v +} diff --git a/library/actions/schedule_test.go b/library/actions/schedule_test.go new file mode 100644 index 00000000..52826788 --- /dev/null +++ b/library/actions/schedule_test.go @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: Apache-2.0 + +package actions + +import ( + "reflect" + "testing" + + "github.com/go-vela/types/constants" +) + +func TestLibrary_Schedule_Getters(t *testing.T) { + // setup tests + tests := []struct { + actions *Schedule + want *Schedule + }{ + { + actions: testSchedule(), + want: testSchedule(), + }, + { + actions: new(Schedule), + want: new(Schedule), + }, + } + + // run tests + for _, test := range tests { + if test.actions.GetRun() != test.want.GetRun() { + t.Errorf("GetRun is %v, want %v", test.actions.GetRun(), test.want.GetRun()) + } + } +} + +func TestLibrary_Schedule_Setters(t *testing.T) { + // setup types + var a *Schedule + + // setup tests + tests := []struct { + actions *Schedule + want *Schedule + }{ + { + actions: testSchedule(), + want: testSchedule(), + }, + { + actions: a, + want: new(Schedule), + }, + } + + // run tests + for _, test := range tests { + test.actions.SetRun(test.want.GetRun()) + + if test.actions.GetRun() != test.want.GetRun() { + t.Errorf("SetRun is %v, want %v", test.actions.GetRun(), test.want.GetRun()) + } + } +} + +func TestLibrary_Schedule_FromMask(t *testing.T) { + // setup types + mask := testMask() + + want := testSchedule() + + // run test + got := new(Schedule).FromMask(mask) + + if !reflect.DeepEqual(got, want) { + t.Errorf("FromMask is %v, want %v", got, want) + } +} + +func TestLibrary_Schedule_ToMask(t *testing.T) { + // setup types + actions := testSchedule() + + want := int64(constants.AllowSchedule) + + // run test + got := actions.ToMask() + + if want != got { + t.Errorf("ToMask is %v, want %v", got, want) + } +} + +func testSchedule() *Schedule { + schedule := new(Schedule) + schedule.SetRun(true) + + return schedule +} diff --git a/library/events.go b/library/events.go index ca84fed3..78ce5d8e 100644 --- a/library/events.go +++ b/library/events.go @@ -10,10 +10,11 @@ import ( // Events is the library representation of the various events that generate a // webhook from the SCM. type Events struct { - Push *actions.Push `json:"push"` - PullRequest *actions.Pull `json:"pull_request"` - Deployment *actions.Deploy `json:"deployment"` - Comment *actions.Comment `json:"comment"` + Push *actions.Push `json:"push"` + PullRequest *actions.Pull `json:"pull_request"` + Deployment *actions.Deploy `json:"deployment"` + Comment *actions.Comment `json:"comment"` + Schedule *actions.Schedule `json:"schedule"` } // NewEventsFromMask is an instatiation function for the Events type that @@ -23,6 +24,7 @@ func NewEventsFromMask(mask int64) *Events { pullActions := new(actions.Pull).FromMask(mask) deployActions := new(actions.Deploy).FromMask(mask) commentActions := new(actions.Comment).FromMask(mask) + scheduleActions := new(actions.Schedule).FromMask(mask) e := new(Events) @@ -30,10 +32,43 @@ func NewEventsFromMask(mask int64) *Events { e.SetPullRequest(pullActions) e.SetDeployment(deployActions) e.SetComment(commentActions) + e.SetSchedule(scheduleActions) return e } +// EventAllowed determines whether or not an event is allowed based on the repository settings. +func (e *Events) Allowed(event, action string) bool { + allowed := false + + if len(action) > 0 { + event = event + ":" + action + } + + switch event { + case constants.EventPush: + allowed = e.GetPush().GetBranch() + case constants.EventPull + ":" + constants.ActionOpened: + allowed = e.GetPullRequest().GetOpened() + case constants.EventPull + ":" + constants.ActionSynchronize: + allowed = e.GetPullRequest().GetSynchronize() + case constants.EventPull + ":" + constants.ActionEdited: + allowed = e.GetPullRequest().GetEdited() + case constants.EventTag: + allowed = e.GetPush().GetTag() + case constants.EventComment + ":" + constants.ActionCreated: + allowed = e.GetComment().GetCreated() + case constants.EventComment + ":" + constants.ActionEdited: + allowed = e.GetComment().GetEdited() + case constants.EventDeploy: + allowed = e.GetDeployment().GetCreated() + case constants.EventSchedule: + allowed = e.GetSchedule().GetRun() + } + + return allowed +} + // List is an Events method that generates a comma-separated list of event:action // combinations that are allowed for the repo. func (e *Events) List() []string { @@ -71,6 +106,10 @@ func (e *Events) List() []string { eventSlice = append(eventSlice, constants.EventComment+":"+constants.ActionEdited) } + if e.GetSchedule().GetRun() { + eventSlice = append(eventSlice, constants.EventSchedule) + } + return eventSlice } @@ -123,6 +162,17 @@ func (e *Events) GetComment() *actions.Comment { return e.Comment } +// GetSchedule returns the Schedule field from the provided Events. If the object is nil, +// or the field within the object is nil, it returns the zero value instead. +func (e *Events) GetSchedule() *actions.Schedule { + // return zero value if Events type or Schedule field is nil + if e == nil || e.Schedule == nil { + return new(actions.Schedule) + } + + return e.Schedule +} + // SetPush sets the Events Push field. // // When the provided Events type is nil, it @@ -174,3 +224,16 @@ func (e *Events) SetComment(v *actions.Comment) { e.Comment = v } + +// SetSchedule sets the Events Schedule field. +// +// When the provided Events type is nil, it +// will set nothing and immediately return. +func (e *Events) SetSchedule(v *actions.Schedule) { + // return if Events type is nil + if e == nil { + return + } + + e.Schedule = v +} diff --git a/library/events_test.go b/library/events_test.go index 2023c46b..30ffb963 100644 --- a/library/events_test.go +++ b/library/events_test.go @@ -124,6 +124,41 @@ func TestLibrary_Events_NewEventsFromMask(t *testing.T) { } } +func TestLibrary_Events_Allowed(t *testing.T) { + // setup tests + tests := []struct { + events *Events + event string + action string + want bool + }{ + { + events: testEvents(), + event: "pull_request", + action: "opened", + want: true, + }, + { + events: testEvents(), + event: "deployment", + want: false, + }, + { + events: testEvents(), + event: "push", + want: true, + }, + } + + for _, test := range tests { + got := test.events.Allowed(test.event, test.action) + + if got != test.want { + t.Errorf("Allowed is %v, want %v", got, test.want) + } + } +} + func testEvents() *Events { e := new(Events) @@ -144,10 +179,14 @@ func testEvents() *Events { comment.SetCreated(false) comment.SetEdited(false) + schedule := new(actions.Schedule) + schedule.SetRun(false) + e.SetPush(push) e.SetPullRequest(pr) e.SetDeployment(deploy) e.SetComment(comment) + e.SetSchedule(schedule) return e } diff --git a/library/repo.go b/library/repo.go index b5bd13c1..2b1bdb51 100644 --- a/library/repo.go +++ b/library/repo.go @@ -5,8 +5,6 @@ package library import ( "fmt" "strings" - - "github.com/go-vela/types/constants" ) // Repo is the library representation of a repo. @@ -764,36 +762,6 @@ func (r *Repo) SetApproveBuild(v string) { r.ApproveBuild = &v } -// EventAllowed determines whether or not an event is allowed based on the repository settings. -func (r *Repo) EventAllowed(event, action string) (allowed bool) { - allowed = false - - if len(action) > 0 { - event = event + ":" + action - } - - switch event { - case constants.EventPush: - allowed = r.GetAllowEvents().GetPush().GetBranch() - case constants.EventPull + ":" + constants.ActionOpened: - allowed = r.GetAllowEvents().GetPullRequest().GetOpened() - case constants.EventPull + ":" + constants.ActionSynchronize: - allowed = r.GetAllowEvents().GetPullRequest().GetSynchronize() - case constants.EventPull + ":" + constants.ActionEdited: - allowed = r.GetAllowEvents().GetPullRequest().GetEdited() - case constants.EventTag: - allowed = r.GetAllowEvents().GetPush().GetTag() - case constants.EventComment + ":" + constants.ActionCreated: - allowed = r.GetAllowEvents().GetComment().GetCreated() - case constants.EventComment + ":" + constants.ActionEdited: - allowed = r.GetAllowEvents().GetComment().GetEdited() - case constants.EventDeploy: - allowed = r.GetAllowEvents().GetDeployment().GetCreated() - } - - return -} - // String implements the Stringer interface for the Repo type. // //nolint:dupl // ignore duplicate with test func diff --git a/library/repo_test.go b/library/repo_test.go index 1da2db4a..e536a135 100644 --- a/library/repo_test.go +++ b/library/repo_test.go @@ -331,41 +331,6 @@ func TestLibrary_Repo_Setters(t *testing.T) { } } -func TestLibrary_Repo_EventAllowed(t *testing.T) { - // setup tests - tests := []struct { - repo *Repo - event string - action string - want bool - }{ - { - repo: testRepo(), - event: "pull_request", - action: "opened", - want: true, - }, - { - repo: testRepo(), - event: "deployment", - want: false, - }, - { - repo: new(Repo), - event: "push", - want: false, - }, - } - - for _, test := range tests { - got := test.repo.EventAllowed(test.event, test.action) - - if got != test.want { - t.Errorf("EventAllowed is %v, want %v", got, test.want) - } - } -} - func TestLibrary_Repo_String(t *testing.T) { // setup types r := testRepo() diff --git a/library/secret.go b/library/secret.go index 62dd5e43..2566b4cd 100644 --- a/library/secret.go +++ b/library/secret.go @@ -23,6 +23,7 @@ type Secret struct { Type *string `json:"type,omitempty"` Images *[]string `json:"images,omitempty"` Events *[]string `json:"events,omitempty"` + AllowEvents *Events `json:"allow_events,omitempty"` AllowCommand *bool `json:"allow_command,omitempty"` CreatedAt *int64 `json:"created_at,omitempty"` CreatedBy *string `json:"created_by,omitempty"` @@ -47,6 +48,7 @@ func (s *Secret) Sanitize() *Secret { Type: s.Type, Images: s.Images, Events: s.Events, + AllowEvents: s.AllowEvents, AllowCommand: s.AllowCommand, CreatedAt: s.CreatedAt, CreatedBy: s.CreatedBy, @@ -60,28 +62,17 @@ func (s *Secret) Sanitize() *Secret { // resource. func (s *Secret) Match(from *pipeline.Container) bool { eACL, iACL := false, false - events, images, commands := s.GetEvents(), s.GetImages(), s.GetAllowCommand() + images, commands := s.GetImages(), s.GetAllowCommand() // check if commands are utilized when not allowed if !commands && len(from.Commands) > 0 { return false } - // check incoming events - switch from.Environment["VELA_BUILD_EVENT"] { - case constants.EventPush: - eACL = checkEvent(events, constants.EventPush) - case constants.EventPull: - eACL = checkEvent(events, constants.EventPull) - case constants.EventTag: - eACL = checkEvent(events, constants.EventTag) - case constants.EventDeploy: - eACL = checkEvent(events, constants.EventDeploy) - case constants.EventComment: - eACL = checkEvent(events, constants.EventComment) - case constants.EventSchedule: - eACL = checkEvent(events, constants.EventSchedule) - } + eACL = s.GetAllowEvents().Allowed( + from.Environment["VELA_BUILD_EVENT"], + from.Environment["VELA_BUILD_EVENT_ACTION"], + ) // check images whitelist for _, i := range images { @@ -93,8 +84,6 @@ func (s *Secret) Match(from *pipeline.Container) bool { // inject secrets into environment switch { - case iACL && (len(events) == 0): - return true case eACL && (len(images) == 0): return true case eACL && iACL: @@ -222,6 +211,19 @@ func (s *Secret) GetEvents() []string { return *s.Events } +// GetAllowEvents returns the AllowEvents field. +// +// When the provided Secret type is nil, or the field within +// the type is nil, it returns the zero value for the field. +func (s *Secret) GetAllowEvents() *Events { + // return zero value if Secret type or AllowEvents field is nil + if s == nil || s.AllowEvents == nil { + return new(Events) + } + + return s.AllowEvents +} + // GetAllowCommand returns the AllowCommand field. // // When the provided Secret type is nil, or the field within @@ -404,6 +406,19 @@ func (s *Secret) SetEvents(v []string) { s.Events = &v } +// SetAllowEvents sets the AllowEvents field. +// +// When the provided Secret type is nil, it +// will set nothing and immediately return. +func (s *Secret) SetAllowEvents(v *Events) { + // return if Secret type is nil + if s == nil { + return + } + + s.AllowEvents = v +} + // SetAllowCommand sets the AllowCommand field. // // When the provided Secret type is nil, it @@ -473,6 +488,7 @@ func (s *Secret) SetUpdatedBy(v string) { func (s *Secret) String() string { return fmt.Sprintf(`{ AllowCommand: %t, + AllowEvents: %s, Events: %s, ID: %d, Images: %s, @@ -488,6 +504,7 @@ func (s *Secret) String() string { UpdatedBy: %s, }`, s.GetAllowCommand(), + s.GetAllowEvents().List(), s.GetEvents(), s.GetID(), s.GetImages(), @@ -503,15 +520,3 @@ func (s *Secret) String() string { s.GetUpdatedBy(), ) } - -// checkEvent implements a function that iterates through -// a list to check the event is a member of the list. -func checkEvent(events []string, event string) bool { - for _, e := range events { - if e == event { - return true - } - } - - return false -} diff --git a/library/secret_test.go b/library/secret_test.go index 4fe2986f..591c1ac5 100644 --- a/library/secret_test.go +++ b/library/secret_test.go @@ -9,7 +9,9 @@ import ( "time" "github.com/go-vela/types/constants" + "github.com/go-vela/types/library/actions" "github.com/go-vela/types/pipeline" + "github.com/google/go-cmp/cmp" ) func TestLibrary_Secret_Sanitize(t *testing.T) { @@ -30,154 +32,290 @@ func TestLibrary_Secret_Sanitize(t *testing.T) { func TestLibrary_Secret_Match(t *testing.T) { // setup types v := "foo" - booL := false + fBool := false + tBool := true + + testEvents := &Events{ + Push: &actions.Push{ + Branch: &tBool, + Tag: &tBool, + }, + PullRequest: &actions.Pull{ + Opened: &fBool, + Synchronize: &tBool, + Edited: &fBool, + }, + Deployment: &actions.Deploy{ + Created: &tBool, + }, + Comment: &actions.Comment{ + Created: &tBool, + Edited: &tBool, + }, + Schedule: &actions.Schedule{ + Run: &tBool, + }, + } // setup tests tests := []struct { + name string step *pipeline.Container sec *Secret want bool }{ { // test matching secret events + name: "push", step: &pipeline.Container{ Image: "alpine:latest", Environment: map[string]string{"VELA_BUILD_EVENT": "push"}, }, - sec: &Secret{Name: &v, Value: &v, Images: &[]string{}, Events: &[]string{"push"}}, + sec: &Secret{ + Name: &v, + Value: &v, + Images: &[]string{}, + AllowEvents: testEvents, + }, want: true, }, { + name: "pull request opened fail", step: &pipeline.Container{ - Image: "alpine:latest", - Environment: map[string]string{"VELA_BUILD_EVENT": "pull_request"}, + Image: "alpine:latest", + Environment: map[string]string{ + "VELA_BUILD_EVENT": "pull_request", + "VELA_BUILD_EVENT_ACTION": "opened", + }, }, - sec: &Secret{Name: &v, Value: &v, Images: &[]string{}, Events: &[]string{"pull_request"}}, - want: true, + sec: &Secret{ + Name: &v, + Value: &v, + Images: &[]string{}, + AllowEvents: testEvents, + }, + want: false, }, { + name: "tag", step: &pipeline.Container{ Image: "alpine:latest", Environment: map[string]string{"VELA_BUILD_EVENT": "tag"}, }, - sec: &Secret{Name: &v, Value: &v, Images: &[]string{}, Events: &[]string{"tag"}}, + sec: &Secret{ + Name: &v, + Value: &v, + Images: &[]string{}, + AllowEvents: testEvents, + }, want: true, }, { + name: "deployment", step: &pipeline.Container{ Image: "alpine:latest", Environment: map[string]string{"VELA_BUILD_EVENT": "deployment"}, }, - sec: &Secret{Name: &v, Value: &v, Images: &[]string{}, Events: &[]string{"deployment"}}, + sec: &Secret{ + Name: &v, + Value: &v, + Images: &[]string{}, + AllowEvents: testEvents, + }, want: true, }, { + name: "comment created", step: &pipeline.Container{ - Image: "alpine:latest", - Environment: map[string]string{"VELA_BUILD_EVENT": "comment"}, + Image: "alpine:latest", + Environment: map[string]string{ + "VELA_BUILD_EVENT": "comment", + "VELA_BUILD_EVENT_ACTION": "created", + }, + }, + sec: &Secret{ + Name: &v, + Value: &v, + Images: &[]string{}, + AllowEvents: testEvents, }, - sec: &Secret{Name: &v, Value: &v, Images: &[]string{}, Events: &[]string{"comment"}}, want: true, }, { + name: "fake event", step: &pipeline.Container{ Image: "alpine:latest", Environment: map[string]string{"VELA_BUILD_EVENT": "fake_event"}, }, - sec: &Secret{Name: &v, Value: &v, Images: &[]string{}, Events: &[]string{"push"}}, + sec: &Secret{ + Name: &v, + Value: &v, + Images: &[]string{}, + AllowEvents: testEvents, + }, want: false, }, { + name: "push with empty image constraint", step: &pipeline.Container{ Image: "alpine:latest", Environment: map[string]string{"VELA_BUILD_EVENT": "push"}, }, - sec: &Secret{Name: &v, Value: &v, Images: &[]string{}, Events: &[]string{"push", "pull_request"}}, + sec: &Secret{ + Name: &v, + Value: &v, + Images: &[]string{}, + AllowEvents: testEvents, + }, want: true, }, { + name: "schedule", step: &pipeline.Container{ Image: "alpine:latest", Environment: map[string]string{"VELA_BUILD_EVENT": "schedule"}, }, - sec: &Secret{Name: &v, Value: &v, Images: &[]string{}, Events: &[]string{"push", "pull_request", "schedule"}}, + sec: &Secret{ + Name: &v, + Value: &v, + Images: &[]string{}, + AllowEvents: testEvents, + }, want: true, }, { // test matching secret images + name: "image basic", step: &pipeline.Container{ Image: "alpine:latest", Environment: map[string]string{"VELA_BUILD_EVENT": "push"}, }, - sec: &Secret{Name: &v, Value: &v, Images: &[]string{"alpine"}, Events: &[]string{}}, + sec: &Secret{ + Name: &v, + Value: &v, + Images: &[]string{"alpine"}, + AllowEvents: testEvents, + }, want: true, }, { + name: "image and tag", step: &pipeline.Container{ Image: "alpine:latest", Environment: map[string]string{"VELA_BUILD_EVENT": "push"}, }, - sec: &Secret{Name: &v, Value: &v, Images: &[]string{"alpine:latest"}, Events: &[]string{}}, + sec: &Secret{ + Name: &v, + Value: &v, + Images: &[]string{"alpine:latest"}, + AllowEvents: testEvents, + }, want: true, }, { + name: "mismatch tag with same image", step: &pipeline.Container{ Image: "alpine:latest", Environment: map[string]string{"VELA_BUILD_EVENT": "push"}, }, - sec: &Secret{Name: &v, Value: &v, Images: &[]string{"alpine:1"}, Events: &[]string{}}, + sec: &Secret{ + Name: &v, + Value: &v, + Images: &[]string{"alpine:1"}, + AllowEvents: testEvents, + }, want: false, }, { + name: "multiple allowed images", step: &pipeline.Container{ Image: "alpine:latest", Environment: map[string]string{"VELA_BUILD_EVENT": "push"}, }, - sec: &Secret{Name: &v, Value: &v, Images: &[]string{"alpine", "centos"}, Events: &[]string{}}, + sec: &Secret{ + Name: &v, + Value: &v, + Images: &[]string{"alpine", "centos"}, + AllowEvents: testEvents, + }, want: true, }, { // test matching secret events and images + name: "push and image pass", step: &pipeline.Container{ Image: "alpine:latest", Environment: map[string]string{"VELA_BUILD_EVENT": "push"}, }, - sec: &Secret{Name: &v, Value: &v, Images: &[]string{"alpine"}, Events: &[]string{"push"}}, + sec: &Secret{ + Name: &v, + Value: &v, + Images: &[]string{"alpine"}, + AllowEvents: testEvents, + }, want: true, }, { + name: "push and image tag pass", step: &pipeline.Container{ Image: "alpine:latest", Environment: map[string]string{"VELA_BUILD_EVENT": "push"}, }, - sec: &Secret{Name: &v, Value: &v, Images: &[]string{"alpine:latest"}, Events: &[]string{"push"}}, + sec: &Secret{ + Name: &v, + Value: &v, + Images: &[]string{"alpine:latest"}, + AllowEvents: testEvents, + }, want: true, }, { + name: "push and bad image tag", step: &pipeline.Container{ Image: "alpine:latest", Environment: map[string]string{"VELA_BUILD_EVENT": "push"}, }, - sec: &Secret{Name: &v, Value: &v, Images: &[]string{"alpine:1"}, Events: &[]string{"push"}}, + sec: &Secret{ + Name: &v, + Value: &v, + Images: &[]string{"alpine:1"}, + AllowEvents: testEvents, + }, want: false, }, { + name: "mismatch event and match image", step: &pipeline.Container{ - Image: "alpine:latest", - Environment: map[string]string{"VELA_BUILD_EVENT": "pull_request"}, + Image: "alpine:latest", + Environment: map[string]string{ + "VELA_BUILD_EVENT": "pull_request", + "VELA_BUILD_EVENT_ACTION": "edited", + }, + }, + sec: &Secret{ + Name: &v, + Value: &v, + Images: &[]string{"alpine:latest"}, + AllowEvents: testEvents, }, - sec: &Secret{Name: &v, Value: &v, Images: &[]string{"alpine:latest"}, Events: &[]string{"push"}}, want: false, }, { + name: "event and multi image allowed pass", step: &pipeline.Container{ Image: "alpine:latest", Environment: map[string]string{"VELA_BUILD_EVENT": "push"}, }, - sec: &Secret{Name: &v, Value: &v, Images: &[]string{"alpine", "centos"}, Events: &[]string{"push"}}, + sec: &Secret{ + Name: &v, + Value: &v, + Images: &[]string{"alpine", "centos"}, + AllowEvents: testEvents, + }, want: true, }, { // test build events with image ACLs and rulesets + name: "rulesets and push pass", step: &pipeline.Container{ Image: "alpine:latest", Environment: map[string]string{"VELA_BUILD_EVENT": "push"}, @@ -187,10 +325,16 @@ func TestLibrary_Secret_Match(t *testing.T) { }, }, }, - sec: &Secret{Name: &v, Value: &v, Images: &[]string{"alpine"}, Events: &[]string{"push"}}, + sec: &Secret{ + Name: &v, + Value: &v, + Images: &[]string{"alpine"}, + AllowEvents: testEvents, + }, want: true, }, { + name: "no commands allowed", step: &pipeline.Container{ Image: "alpine:latest", Environment: map[string]string{"VELA_BUILD_EVENT": "push"}, @@ -201,7 +345,13 @@ func TestLibrary_Secret_Match(t *testing.T) { }, Commands: []string{"echo hi"}, }, - sec: &Secret{Name: &v, Value: &v, Images: &[]string{"alpine"}, Events: &[]string{"push"}, AllowCommand: &booL}, + sec: &Secret{ + Name: &v, + Value: &v, + Images: &[]string{"alpine"}, + AllowEvents: testEvents, + AllowCommand: &fBool, + }, want: false, }, } @@ -211,7 +361,7 @@ func TestLibrary_Secret_Match(t *testing.T) { got := test.sec.Match(test.step) if got != test.want { - t.Errorf("Match is %v, want %v", got, test.want) + t.Errorf("Match for %s is %v, want %v", test.name, got, test.want) } } } @@ -270,6 +420,10 @@ func TestLibrary_Secret_Getters(t *testing.T) { t.Errorf("GetEvents is %v, want %v", test.secret.GetEvents(), test.want.GetEvents()) } + if !reflect.DeepEqual(test.secret.GetAllowEvents(), test.want.GetAllowEvents()) { + t.Errorf("GetAllowEvents is %v, want %v", test.secret.GetAllowEvents(), test.want.GetAllowEvents()) + } + if test.secret.GetAllowCommand() != test.want.GetAllowCommand() { t.Errorf("GetAllowCommand is %v, want %v", test.secret.GetAllowCommand(), test.want.GetAllowCommand()) } @@ -322,6 +476,7 @@ func TestLibrary_Secret_Setters(t *testing.T) { test.secret.SetType(test.want.GetType()) test.secret.SetImages(test.want.GetImages()) test.secret.SetEvents(test.want.GetEvents()) + test.secret.SetAllowEvents(test.want.GetAllowEvents()) test.secret.SetAllowCommand(test.want.GetAllowCommand()) test.secret.SetCreatedAt(test.want.GetCreatedAt()) test.secret.SetCreatedBy(test.want.GetCreatedBy()) @@ -364,6 +519,10 @@ func TestLibrary_Secret_Setters(t *testing.T) { t.Errorf("SetEvents is %v, want %v", test.secret.GetEvents(), test.want.GetEvents()) } + if !reflect.DeepEqual(test.secret.GetAllowEvents(), test.want.GetAllowEvents()) { + t.Errorf("SetAllowEvents is %v, want %v", test.secret.GetAllowEvents(), test.want.GetAllowEvents()) + } + if test.secret.GetAllowCommand() != test.want.GetAllowCommand() { t.Errorf("SetAllowCommand is %v, want %v", test.secret.GetAllowCommand(), test.want.GetAllowCommand()) } @@ -392,6 +551,7 @@ func TestLibrary_Secret_String(t *testing.T) { want := fmt.Sprintf(`{ AllowCommand: %t, + AllowEvents: %v, Events: %s, ID: %d, Images: %s, @@ -407,6 +567,7 @@ func TestLibrary_Secret_String(t *testing.T) { UpdatedBy: %s, }`, s.GetAllowCommand(), + s.GetAllowEvents().List(), s.GetEvents(), s.GetID(), s.GetImages(), @@ -425,8 +586,8 @@ func TestLibrary_Secret_String(t *testing.T) { // run test got := s.String() - if !reflect.DeepEqual(got, want) { - t.Errorf("String is %v, want %v", got, want) + if diff := cmp.Diff(got, want); diff != "" { + t.Errorf("String Mismatch: -want +got):\n%s", diff) } } @@ -447,6 +608,7 @@ func testSecret() *Secret { s.SetType("repo") s.SetImages([]string{"alpine"}) s.SetEvents([]string{"push", "tag", "deployment"}) + s.SetAllowEvents(NewEventsFromMask(1)) s.SetAllowCommand(true) s.SetCreatedAt(tsCreate) s.SetCreatedBy("octocat")