From 5b1d1b17211e5dc9905c2148dbbe32f74958428b Mon Sep 17 00:00:00 2001 From: Paulo Gomes Date: Wed, 7 Sep 2022 10:46:29 +0100 Subject: [PATCH] fuzz: Refactor Fuzzers based on Go native fuzzing The existing fuzzers were converted into the Go native format. Based on how the code was structured on this project, the fuzzers can be quite effective, allowing for entire E2E fuzzing in some cases, but with very low execution cost. Signed-off-by: Paulo Gomes --- Makefile | 14 ++- go.mod | 2 + go.sum | 6 ++ internal/notifier/alertmanager_test.go | 44 ++++++++ internal/notifier/azure_devops.go | 9 ++ internal/notifier/azure_devops_test.go | 57 ++++++++++ internal/notifier/bitbucket_test.go | 55 ++++++++++ internal/notifier/discord_test.go | 37 +++++++ internal/notifier/forwarder_test.go | 33 ++++++ internal/notifier/github.go | 8 ++ internal/notifier/github_dispatch_test.go | 44 ++++++++ internal/notifier/github_test.go | 49 +++++++++ internal/notifier/gitlab_test.go | 47 ++++++++ internal/notifier/google_chat_test.go | 36 +++++++ internal/notifier/grafana_test.go | 39 +++++++ internal/notifier/lark_test.go | 35 ++++++ internal/notifier/matrix_test.go | 38 +++++++ internal/notifier/opsgenie_test.go | 39 +++++++ internal/notifier/rocket_test.go | 40 +++++++ internal/notifier/slack_test.go | 41 +++++++ internal/notifier/teams_test.go | 41 +++++++ .../notifier/telegram_test.go | 26 ++--- internal/notifier/util_test.go | 22 ++++ internal/notifier/webex_test.go | 40 +++++++ tests/fuzz/alertmanager_fuzzer.go | 55 ---------- tests/fuzz/azure_devops_fuzzer.go | 59 ---------- tests/fuzz/bitbucket_fuzzer.go | 59 ---------- tests/fuzz/discord_fuzzer.go | 64 ----------- tests/fuzz/forwarder_fuzzer.go | 56 ---------- tests/fuzz/github_fuzzer.go | 59 ---------- tests/fuzz/gitlab_fuzzer.go | 59 ---------- tests/fuzz/go.mod | 11 -- tests/fuzz/google_chat_fuzzer.go | 55 ---------- tests/fuzz/lark_fuzzer.go | 55 ---------- tests/fuzz/matrix_fuzzer.go | 59 ---------- tests/fuzz/native_go_run.sh | 36 +++++++ tests/fuzz/opsgenie_fuzzer.go | 55 ---------- tests/fuzz/oss_fuzz_build.sh | 101 ++++++++++-------- tests/fuzz/rocket_fuzzer.go | 64 ----------- tests/fuzz/slack_fuzzer.go | 69 ------------ tests/fuzz/teams_fuzzer.go | 55 ---------- tests/fuzz/webex_fuzzer.go | 55 ---------- 42 files changed, 874 insertions(+), 954 deletions(-) rename tests/fuzz/util_fuzzer.go => internal/notifier/telegram_test.go (51%) delete mode 100644 tests/fuzz/alertmanager_fuzzer.go delete mode 100644 tests/fuzz/azure_devops_fuzzer.go delete mode 100644 tests/fuzz/bitbucket_fuzzer.go delete mode 100644 tests/fuzz/discord_fuzzer.go delete mode 100644 tests/fuzz/forwarder_fuzzer.go delete mode 100644 tests/fuzz/github_fuzzer.go delete mode 100644 tests/fuzz/gitlab_fuzzer.go delete mode 100644 tests/fuzz/go.mod delete mode 100644 tests/fuzz/google_chat_fuzzer.go delete mode 100644 tests/fuzz/lark_fuzzer.go delete mode 100644 tests/fuzz/matrix_fuzzer.go create mode 100755 tests/fuzz/native_go_run.sh delete mode 100644 tests/fuzz/opsgenie_fuzzer.go delete mode 100644 tests/fuzz/rocket_fuzzer.go delete mode 100644 tests/fuzz/slack_fuzzer.go delete mode 100644 tests/fuzz/teams_fuzzer.go delete mode 100644 tests/fuzz/webex_fuzzer.go diff --git a/Makefile b/Makefile index 5459fa1ba..fc31a4d96 100644 --- a/Makefile +++ b/Makefile @@ -19,6 +19,10 @@ BUILD_PLATFORMS ?= linux/amd64 # Architecture to use envtest with ENVTEST_ARCH ?= amd64 +# FUZZ_TIME defines the max amount of time, in Go Duration, +# each fuzzer should run for. +FUZZ_TIME ?= 1m + all: manager # Run tests @@ -109,7 +113,7 @@ docker-push: docker-deploy: kubectl -n flux-system set image deployment/notification-controller manager=${IMG} -# Build fuzzers +# Build fuzzers used by oss-fuzz. fuzz-build: rm -rf $(shell pwd)/build/fuzz/ mkdir -p $(shell pwd)/build/fuzz/out/ @@ -122,7 +126,7 @@ fuzz-build: -v "$(shell pwd)/build/fuzz/out":/out \ local-fuzzing:latest -# Run each fuzzer once to ensure they are working +# Run each fuzzer once to ensure they will work when executed by oss-fuzz. fuzz-smoketest: fuzz-build docker run --rm \ -v "$(shell pwd)/build/fuzz/out":/out \ @@ -130,6 +134,12 @@ fuzz-smoketest: fuzz-build local-fuzzing:latest \ bash -c "/runner.sh" +# Run fuzz tests for the duration set in FUZZ_TIME. +fuzz-native: + KUBEBUILDER_ASSETS=$(KUBEBUILDER_ASSETS) \ + FUZZ_TIME=$(FUZZ_TIME) \ + ./tests/fuzz/native_go_run.sh + # Find or download controller-gen CONTROLLER_GEN = $(shell pwd)/bin/controller-gen .PHONY: controller-gen diff --git a/go.mod b/go.mod index fe794d15d..e4e9ce48b 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.18 replace github.com/fluxcd/notification-controller/api => ./api require ( + github.com/AdaLogics/go-fuzz-headers v0.0.0-20220903154154-e8044f6e4c72 github.com/Azure/azure-amqp-common-go/v3 v3.2.3 github.com/Azure/azure-event-hubs-go/v3 v3.3.18 github.com/containrrr/shoutrrr v0.6.1 @@ -68,6 +69,7 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 // indirect + github.com/cyphar/filepath-securejoin v0.2.3 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/devigned/tab v0.1.1 // indirect github.com/emicklei/go-restful/v3 v3.8.0 // indirect diff --git a/go.sum b/go.sum index 701649898..eaf963c21 100644 --- a/go.sum +++ b/go.sum @@ -46,6 +46,8 @@ cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohl cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20220903154154-e8044f6e4c72 h1:1sCHCT0xRr7UArrI1WJxsl9S8QeYdf0fmuGIl2xb7YI= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20220903154154-e8044f6e4c72/go.mod h1:i9fr2JpcEcY/IHEvzCM3qXUZYOQHgR89dt4es1CgMhc= github.com/Azure/azure-amqp-common-go/v3 v3.2.3 h1:uDF62mbd9bypXWi19V1bN5NZEO84JqgmI5G73ibAmrk= github.com/Azure/azure-amqp-common-go/v3 v3.2.3/go.mod h1:7rPmbSfszeovxGfc5fSAXE4ehlXQZHpMja2OtxC2Tas= github.com/Azure/azure-event-hubs-go/v3 v3.3.18 h1:jgWDk2qmknA0UsfyzjHiW5yciOw3aBY0Oq9p/M9lz2Q= @@ -169,6 +171,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI= +github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -450,6 +454,7 @@ github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -1151,6 +1156,7 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= diff --git a/internal/notifier/alertmanager_test.go b/internal/notifier/alertmanager_test.go index 432ecd323..62041ac10 100644 --- a/internal/notifier/alertmanager_test.go +++ b/internal/notifier/alertmanager_test.go @@ -18,13 +18,18 @@ package notifier import ( "context" + "crypto/x509" "encoding/json" + "fmt" "io" "net/http" "net/http/httptest" "testing" + "github.com/fluxcd/pkg/runtime/events" "github.com/stretchr/testify/require" + + fuzz "github.com/AdaLogics/go-fuzz-headers" ) func TestAlertmanager_Post(t *testing.T) { @@ -44,3 +49,42 @@ func TestAlertmanager_Post(t *testing.T) { err = alertmanager.Post(context.TODO(), testEvent()) require.NoError(t, err) } + +func Fuzz_AlertManager(f *testing.F) { + f.Add("update", "", "", []byte{}, []byte("{}")) + f.Add("something", "", "else", []byte{}, []byte("")) + + f.Fuzz(func(t *testing.T, + commitStatus, urlSuffix, summary string, seed, response []byte) { + + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write(response) + io.Copy(io.Discard, r.Body) + r.Body.Close() + })) + defer ts.Close() + + var cert x509.CertPool + _ = fuzz.NewConsumer(seed).GenerateStruct(&cert) + + alertmanager, err := NewAlertmanager(fmt.Sprintf("%s/%s", ts.URL, urlSuffix), "", &cert) + if err != nil { + return + } + + event := events.Event{} + + // Try to fuzz the event object, but if it fails (not enough seed), + // ignore it, as other inputs are also being used in this test. + _ = fuzz.NewConsumer(seed).GenerateStruct(&event) + + if event.Metadata == nil && (commitStatus != "" || summary != "") { + event.Metadata = map[string]string{ + "commit_status": commitStatus, + "summary": summary, + } + } + + _ = alertmanager.Post(context.TODO(), event) + }) +} diff --git a/internal/notifier/azure_devops.go b/internal/notifier/azure_devops.go index 93313d757..8bb8e6c3f 100644 --- a/internal/notifier/azure_devops.go +++ b/internal/notifier/azure_devops.go @@ -147,7 +147,16 @@ func toAzureDevOpsState(severity string) (git.GitStatusState, error) { // duplicateStatus return true if the latest status // with a matching context has the same state and description func duplicateAzureDevOpsStatus(statuses *[]git.GitStatus, status *git.GitStatus) bool { + if status == nil || status.Context == nil || statuses == nil { + return false + } + for _, s := range *statuses { + if s.Context == nil || s.Context.Name == nil || s.Context.Genre == nil || + s.State == nil || s.Description == nil { + continue + } + if *s.Context.Name == *status.Context.Name && *s.Context.Genre == *status.Context.Genre { if *s.State == *status.State && *s.Description == *status.Description { return true diff --git a/internal/notifier/azure_devops_test.go b/internal/notifier/azure_devops_test.go index 75c49f0e3..f6ad56a68 100644 --- a/internal/notifier/azure_devops_test.go +++ b/internal/notifier/azure_devops_test.go @@ -17,10 +17,20 @@ limitations under the License. package notifier import ( + "context" + "crypto/x509" + "fmt" + "io" + "net/http" + "net/http/httptest" + "strings" "testing" + "github.com/fluxcd/pkg/runtime/events" "github.com/microsoft/azure-devops-go-api/azuredevops/v6/git" "github.com/stretchr/testify/assert" + + fuzz "github.com/AdaLogics/go-fuzz-headers" ) func TestNewAzureDevOpsBasic(t *testing.T) { @@ -60,6 +70,53 @@ func TestDuplicateAzureDevOpsStatus(t *testing.T) { } } +const apiLocations = `{"count":0,"value":[{"area":"","id":"428dd4fb-fda5-4722-af02-9313b80305da","routeTemplate":"","resourceName":"","maxVersion":"6.0","minVersion":"5.0","releasedVersion":"6.0"}]}` + +func Fuzz_AzureDevOps(f *testing.F) { + f.Add("alakazam", "org/proj/_git/repo", "revision/dsa123a", "error", "", []byte{}, []byte(`{"count":1,"value":[{"state":"error","description":"","context":{"genre":"fluxcd","name":"/"}}]}`)) + f.Add("alakazam", "org/proj/_git/repo", "revision/dsa123a", "info", "", []byte{}, []byte(`{"count":1,"value":[{"state":"info","description":"","context":{"genre":"fluxcd","name":"/"}}]}`)) + f.Add("alakazam", "org/proj/_git/repo", "revision/dsa123a", "info", "", []byte{}, []byte(`{"count":0,"value":[]}`)) + f.Add("alakazam", "org/proj/_git/repo", "", "", "Progressing", []byte{}, []byte{}) + + f.Fuzz(func(t *testing.T, + token, urlSuffix, revision, severity, reason string, seed, response []byte) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + if strings.HasSuffix(r.URL.Path, "_apis") { + w.Write([]byte(apiLocations)) + } else { + w.Write(response) + } + + io.Copy(io.Discard, r.Body) + r.Body.Close() + })) + defer ts.Close() + + var cert x509.CertPool + _ = fuzz.NewConsumer(seed).GenerateStruct(&cert) + + azureDevOps, err := NewAzureDevOps(fmt.Sprintf("%s/%s", ts.URL, urlSuffix), token, &cert) + if err != nil { + return + } + + event := events.Event{} + + // Try to fuzz the event object, but if it fails (not enough seed), + // ignore it, as other inputs are also being used in this test. + _ = fuzz.NewConsumer(seed).GenerateStruct(&event) + + if event.Metadata == nil && (revision != "") { + event.Metadata = map[string]string{ + "revision": revision, + } + } + event.Severity = severity + + _ = azureDevOps.Post(context.TODO(), event) + }) +} + func azStatus(state git.GitStatusState, context string, description string) *git.GitStatus { genre := "fluxcd" return &git.GitStatus{ diff --git a/internal/notifier/bitbucket_test.go b/internal/notifier/bitbucket_test.go index 519506eed..69d947076 100644 --- a/internal/notifier/bitbucket_test.go +++ b/internal/notifier/bitbucket_test.go @@ -17,8 +17,17 @@ limitations under the License. package notifier import ( + "context" + "crypto/x509" + "fmt" + "io" + "net/http" + "net/http/httptest" + "net/url" "testing" + fuzz "github.com/AdaLogics/go-fuzz-headers" + "github.com/fluxcd/pkg/runtime/events" "github.com/stretchr/testify/assert" ) @@ -38,3 +47,49 @@ func TestNewBitbucketInvalidToken(t *testing.T) { _, err := NewBitbucket("https://bitbucket.org/foo/bar", "bar", nil) assert.NotNil(t, err) } + +func Fuzz_Bitbucket(f *testing.F) { + f.Add("user:pass", "org/repo", "revision/dsa123a", "info", []byte{}, []byte(`{"state":"SUCCESSFUL","description":"","key":"","name":"","url":""}`)) + f.Add("user:pass", "org/repo", "revision/dsa123a", "error", []byte{}, []byte(`{}`)) + + f.Fuzz(func(t *testing.T, + token, urlSuffix, revision, severity string, seed, response []byte) { + + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + io.Copy(io.Discard, r.Body) + w.Write(response) + r.Body.Close() + })) + defer ts.Close() + + var cert x509.CertPool + _ = fuzz.NewConsumer(seed).GenerateStruct(&cert) + + bitbucket, err := NewBitbucket(fmt.Sprintf("%s/%s", ts.URL, urlSuffix), token, &cert) + if err != nil { + return + } + + apiUrl, err := url.Parse(ts.URL) + if err != nil { + t.Fatalf("cannot parse api base URL: %v", err) + } + // Ensure the call does not go to bitbucket and fuzzes the response. + bitbucket.Client.SetApiBaseURL(*apiUrl) + + event := events.Event{} + + // Try to fuzz the event object, but if it fails (not enough seed), + // ignore it, as other inputs are also being used in this test. + _ = fuzz.NewConsumer(seed).GenerateStruct(&event) + + if event.Metadata == nil && (revision != "") { + event.Metadata = map[string]string{ + "revision": revision, + } + } + event.Severity = severity + + _ = bitbucket.Post(context.TODO(), event) + }) +} diff --git a/internal/notifier/discord_test.go b/internal/notifier/discord_test.go index db15eaa45..a407e638c 100644 --- a/internal/notifier/discord_test.go +++ b/internal/notifier/discord_test.go @@ -19,12 +19,15 @@ package notifier import ( "context" "encoding/json" + "fmt" "io" "net/http" "net/http/httptest" "strings" "testing" + fuzz "github.com/AdaLogics/go-fuzz-headers" + "github.com/fluxcd/pkg/runtime/events" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -49,3 +52,37 @@ func TestDiscord_Post(t *testing.T) { err = discord.Post(context.TODO(), testEvent()) require.NoError(t, err) } + +func Fuzz_Discord(f *testing.F) { + f.Add("username", "channel", "/slack", "info", "update", []byte{}, []byte("{}")) + f.Add("", "channel", "", "error", "", []byte{}, []byte("")) + + f.Fuzz(func(t *testing.T, + username, channel, urlSuffix, severity, commitStatus string, seed, response []byte) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + io.Copy(io.Discard, r.Body) + w.Write(response) + r.Body.Close() + })) + defer ts.Close() + + discord, err := NewDiscord(fmt.Sprintf("%s/%s", ts.URL, urlSuffix), "", username, channel) + if err != nil { + return + } + + event := events.Event{} + // Try to fuzz the event object, but if it fails (not enough seed), + // ignore it, as other inputs are also being used in this test. + _ = fuzz.NewConsumer(seed).GenerateStruct(&event) + + if event.Metadata == nil { + event.Metadata = map[string]string{} + } + + event.Metadata["commit_status"] = commitStatus + event.Severity = severity + + _ = discord.Post(context.TODO(), event) + }) +} diff --git a/internal/notifier/forwarder_test.go b/internal/notifier/forwarder_test.go index c3d97ccca..eb4dd6621 100644 --- a/internal/notifier/forwarder_test.go +++ b/internal/notifier/forwarder_test.go @@ -18,12 +18,15 @@ package notifier import ( "context" + "crypto/x509" "encoding/json" + "fmt" "io" "net/http" "net/http/httptest" "testing" + fuzz "github.com/AdaLogics/go-fuzz-headers" "github.com/fluxcd/pkg/runtime/events" "github.com/stretchr/testify/require" @@ -52,3 +55,33 @@ func TestForwarder_Post(t *testing.T) { err = forwarder.Post(context.TODO(), testEvent()) require.NoError(t, err) } + +func Fuzz_Forwarder(f *testing.F) { + f.Add("", []byte{}, []byte{}) + + f.Fuzz(func(t *testing.T, + urlSuffix string, seed, response []byte) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write(response) + io.Copy(io.Discard, r.Body) + r.Body.Close() + })) + defer ts.Close() + + var cert x509.CertPool + _ = fuzz.NewConsumer(seed).GenerateStruct(&cert) + + header := make(map[string]string) + _ = fuzz.NewConsumer(seed).FuzzMap(&header) + + forwarder, err := NewForwarder(fmt.Sprintf("%s/%s", ts.URL, urlSuffix), "", header, &cert) + if err != nil { + return + } + + event := events.Event{} + _ = fuzz.NewConsumer(seed).GenerateStruct(&event) + + _ = forwarder.Post(context.TODO(), event) + }) +} diff --git a/internal/notifier/github.go b/internal/notifier/github.go index 58b3fe6db..57adb4184 100644 --- a/internal/notifier/github.go +++ b/internal/notifier/github.go @@ -144,7 +144,15 @@ func toGitHubState(severity string) (string, error) { // duplicateStatus return true if the latest status // with a matching context has the same state and description func duplicateGithubStatus(statuses []*github.RepoStatus, status *github.RepoStatus) bool { + if status == nil || statuses == nil { + return false + } + for _, s := range statuses { + if s.Context == nil || s.State == nil || s.Description == nil { + continue + } + if *s.Context == *status.Context { if *s.State == *status.State && *s.Description == *status.Description { return true diff --git a/internal/notifier/github_dispatch_test.go b/internal/notifier/github_dispatch_test.go index 0841f7d16..5cdd633d0 100644 --- a/internal/notifier/github_dispatch_test.go +++ b/internal/notifier/github_dispatch_test.go @@ -18,8 +18,15 @@ package notifier import ( "context" + "crypto/x509" + "fmt" + "io" + "net/http" + "net/http/httptest" "testing" + fuzz "github.com/AdaLogics/go-fuzz-headers" + "github.com/fluxcd/pkg/runtime/events" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -59,3 +66,40 @@ func TestGitHubDispatch_PostUpdate(t *testing.T) { err = githubDispatch.Post(context.TODO(), event) require.NoError(t, err) } + +func Fuzz_GitHub_Dispatch(f *testing.F) { + f.Add("token", "org/repo", "", []byte{}, []byte{}) + f.Add("token", "org/repo", "update", []byte{}, []byte{}) + f.Add("", "", "", []byte{}, []byte{}) + + f.Fuzz(func(t *testing.T, + token, urlSuffix, commitStatus string, seed, response []byte) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write(response) + io.Copy(io.Discard, r.Body) + r.Body.Close() + })) + defer ts.Close() + + var cert x509.CertPool + _ = fuzz.NewConsumer(seed).GenerateStruct(&cert) + + dispatch, err := NewGitHubDispatch(fmt.Sprintf("%s/%s", ts.URL, urlSuffix), token, &cert) + if err != nil { + return + } + + event := events.Event{} + // Try to fuzz the event object, but if it fails (not enough seed), + // ignore it, as other inputs are also being used in this test. + _ = fuzz.NewConsumer(seed).GenerateStruct(&event) + + if event.Metadata == nil { + event.Metadata = map[string]string{} + } + + event.Metadata["commit_status"] = commitStatus + + _ = dispatch.Post(context.TODO(), event) + }) +} diff --git a/internal/notifier/github_test.go b/internal/notifier/github_test.go index c320df17d..3ded5a084 100644 --- a/internal/notifier/github_test.go +++ b/internal/notifier/github_test.go @@ -17,8 +17,16 @@ limitations under the License. package notifier import ( + "context" + "crypto/x509" + "fmt" + "io" + "net/http" + "net/http/httptest" "testing" + fuzz "github.com/AdaLogics/go-fuzz-headers" + "github.com/fluxcd/pkg/runtime/events" "github.com/google/go-github/v41/github" "github.com/stretchr/testify/assert" ) @@ -69,6 +77,47 @@ func TestDuplicateGithubStatus(t *testing.T) { } } +func Fuzz_GitHub(f *testing.F) { + f.Add("token", "org/repo", "revision/abce1", "error", "", []byte{}, []byte(`[{"context":"/","state":"failure","description":""}]`)) + f.Add("token", "org/repo", "revision/abce1", "info", "", []byte{}, []byte(`[{"context":"/","state":"success","description":""}]`)) + f.Add("token", "org/repo", "revision/abce1", "info", "", []byte{}, []byte(`[{"context":"/","state":"failure","description":""}]`)) + f.Add("token", "org/repo", "revision/abce1", "info", "", []byte{}, []byte(`[{"context":"/"}]`)) + f.Add("token", "org/repo", "revision/abce1", "info", "", []byte{}, []byte(`[{}]`)) + f.Add("token", "org/repo", "revision/abce1", "info", "Progressing", []byte{}, []byte{}) + + f.Fuzz(func(t *testing.T, + token, urlSuffix, revision, severity, reason string, seed, response []byte) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write(response) + io.Copy(io.Discard, r.Body) + r.Body.Close() + })) + defer ts.Close() + + var cert x509.CertPool + _ = fuzz.NewConsumer(seed).GenerateStruct(&cert) + + github, err := NewGitHub(fmt.Sprintf("%s/%s", ts.URL, urlSuffix), token, &cert) + if err != nil { + return + } + + event := events.Event{} + _ = fuzz.NewConsumer(seed).GenerateStruct(&event) + + if event.Metadata == nil && (revision != "") { + event.Metadata = map[string]string{ + "revision": revision, + } + } + + event.Severity = severity + event.Reason = reason + + _ = github.Post(context.TODO(), event) + }) +} + func ghStatus(state string, context string, description string) *github.RepoStatus { return &github.RepoStatus{ State: &state, diff --git a/internal/notifier/gitlab_test.go b/internal/notifier/gitlab_test.go index 32a50bdf9..5277a5346 100644 --- a/internal/notifier/gitlab_test.go +++ b/internal/notifier/gitlab_test.go @@ -17,8 +17,16 @@ limitations under the License. package notifier import ( + "context" + "crypto/x509" + "fmt" + "io" + "net/http" + "net/http/httptest" "testing" + fuzz "github.com/AdaLogics/go-fuzz-headers" + "github.com/fluxcd/pkg/runtime/events" "github.com/stretchr/testify/assert" ) @@ -45,3 +53,42 @@ func TestNewGitLabEmptyToken(t *testing.T) { _, err := NewGitLab("https://gitlab.com/foo/bar", "", nil) assert.NotNil(t, err) } + +func Fuzz_GitLab(f *testing.F) { + f.Add("token", "org/repo", "revision/abce1", "error", "", []byte{}, []byte(`[{"sha":"abce1","status":"failed","name":"/","description":""}]`)) + f.Add("token", "org/repo", "revision/abce1", "info", "", []byte{}, []byte(`[{"sha":"abce1","status":"failed","name":"/","description":""}]`)) + f.Add("token", "org/repo", "revision/abce1", "info", "Progressing", []byte{}, []byte{}) + f.Add("token", "org/repo", "revision/abce1", "info", "", []byte{}, []byte(`[]`)) + + f.Fuzz(func(t *testing.T, + token, urlSuffix, revision, severity, reason string, seed, response []byte) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write(response) + io.Copy(io.Discard, r.Body) + r.Body.Close() + })) + defer ts.Close() + + var cert x509.CertPool + _ = fuzz.NewConsumer(seed).GenerateStruct(&cert) + + gitLab, err := NewGitLab(fmt.Sprintf("%s/%s", ts.URL, urlSuffix), token, &cert) + if err != nil { + return + } + + event := events.Event{} + _ = fuzz.NewConsumer(seed).GenerateStruct(&event) + + if event.Metadata == nil && (revision != "") { + event.Metadata = map[string]string{ + "revision": revision, + } + } + + event.Severity = severity + event.Reason = reason + + _ = gitLab.Post(context.TODO(), event) + }) +} diff --git a/internal/notifier/google_chat_test.go b/internal/notifier/google_chat_test.go index 8ca3074fc..0060d6a74 100644 --- a/internal/notifier/google_chat_test.go +++ b/internal/notifier/google_chat_test.go @@ -19,11 +19,14 @@ package notifier import ( "context" "encoding/json" + "fmt" "io" "net/http" "net/http/httptest" "testing" + fuzz "github.com/AdaLogics/go-fuzz-headers" + "github.com/fluxcd/pkg/runtime/events" "github.com/stretchr/testify/require" ) @@ -49,3 +52,36 @@ func TestGoogleChat_Post(t *testing.T) { err = google_chat.Post(context.TODO(), testEvent()) require.NoError(t, err) } + +func Fuzz_GoogleChat(f *testing.F) { + f.Add("", "error", "", "", []byte{}, []byte{}) + f.Add("", "info", "", "update", []byte{}, []byte{}) + + f.Fuzz(func(t *testing.T, + urlSuffix, severity, message, commitStatus string, seed, response []byte) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write(response) + io.Copy(io.Discard, r.Body) + r.Body.Close() + })) + defer ts.Close() + + googlechat, err := NewGoogleChat(fmt.Sprintf("%s/%s", ts.URL, urlSuffix), "") + if err != nil { + return + } + + event := events.Event{} + _ = fuzz.NewConsumer(seed).GenerateStruct(&event) + + if event.Metadata == nil { + event.Metadata = map[string]string{} + } + + event.Metadata["commit_status"] = commitStatus + event.Message = message + event.Severity = severity + + _ = googlechat.Post(context.TODO(), event) + }) +} diff --git a/internal/notifier/grafana_test.go b/internal/notifier/grafana_test.go index 6675cd907..d4765f4c9 100644 --- a/internal/notifier/grafana_test.go +++ b/internal/notifier/grafana_test.go @@ -18,12 +18,16 @@ package notifier import ( "context" + "crypto/x509" "encoding/json" + "fmt" "io" "net/http" "net/http/httptest" "testing" + fuzz "github.com/AdaLogics/go-fuzz-headers" + "github.com/fluxcd/pkg/runtime/events" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -51,3 +55,38 @@ func TestGrafana_Post(t *testing.T) { assert.NoError(t, err) }) } + +func Fuzz_Grafana(f *testing.F) { + f.Add("token", "user", "pass", "", "", []byte{}, []byte{}) + f.Add("", "user", "pass", "", "", []byte{}, []byte{}) + f.Add("token", "user", "pass", "", "update", []byte{}, []byte{}) + + f.Fuzz(func(t *testing.T, + token, username, password, urlSuffix, commitStatus string, seed, response []byte) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write(response) + io.Copy(io.Discard, r.Body) + r.Body.Close() + })) + defer ts.Close() + + var cert x509.CertPool + _ = fuzz.NewConsumer(seed).GenerateStruct(&cert) + + grafana, err := NewGrafana(fmt.Sprintf("%s/%s", ts.URL, urlSuffix), "", token, &cert, username, password) + if err != nil { + return + } + + event := events.Event{} + _ = fuzz.NewConsumer(seed).GenerateStruct(&event) + + if event.Metadata == nil { + event.Metadata = map[string]string{} + } + + event.Metadata["commit_status"] = commitStatus + + _ = grafana.Post(context.TODO(), event) + }) +} diff --git a/internal/notifier/lark_test.go b/internal/notifier/lark_test.go index 50f07ac4a..83ae6d4b0 100644 --- a/internal/notifier/lark_test.go +++ b/internal/notifier/lark_test.go @@ -3,11 +3,14 @@ package notifier import ( "context" "encoding/json" + "fmt" "io" "net/http" "net/http/httptest" "testing" + fuzz "github.com/AdaLogics/go-fuzz-headers" + "github.com/fluxcd/pkg/runtime/events" "github.com/stretchr/testify/require" ) @@ -30,3 +33,35 @@ func TestLark_Post(t *testing.T) { err = lark.Post(context.TODO(), testEvent()) require.NoError(t, err) } + +func Fuzz_Lark(f *testing.F) { + f.Add("", "", "error", []byte{}, []byte{}) + f.Add("", "update", "error", []byte{}, []byte{}) + + f.Fuzz(func(t *testing.T, + urlSuffix, commitStatus, severity string, seed, response []byte) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write(response) + io.Copy(io.Discard, r.Body) + r.Body.Close() + })) + defer ts.Close() + + lark, err := NewLark(fmt.Sprintf("%s/%s", ts.URL, urlSuffix)) + if err != nil { + return + } + + event := events.Event{} + _ = fuzz.NewConsumer(seed).GenerateStruct(&event) + + if event.Metadata == nil { + event.Metadata = map[string]string{} + } + + event.Metadata["commit_status"] = commitStatus + event.Severity = severity + + _ = lark.Post(context.TODO(), event) + }) +} diff --git a/internal/notifier/matrix_test.go b/internal/notifier/matrix_test.go index ad8468521..cba7f6d9f 100644 --- a/internal/notifier/matrix_test.go +++ b/internal/notifier/matrix_test.go @@ -1,9 +1,16 @@ package notifier import ( + "context" + "crypto/x509" + "fmt" + "io" + "net/http" + "net/http/httptest" "testing" "time" + fuzz "github.com/AdaLogics/go-fuzz-headers" "github.com/fluxcd/pkg/runtime/events" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -48,3 +55,34 @@ func TestSha1Sum(t *testing.T) { } } } + +func Fuzz_Matrix(f *testing.F) { + f.Add("token", "room1", "", "error", []byte{}, []byte{}) + f.Add("token", "room1", "", "info", []byte{}, []byte{}) + + f.Fuzz(func(t *testing.T, + token, roomId, urlSuffix, severity string, seed, response []byte) { + + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write(response) + io.Copy(io.Discard, r.Body) + r.Body.Close() + })) + defer ts.Close() + + var cert x509.CertPool + _ = fuzz.NewConsumer(seed).GenerateStruct(&cert) + + matrix, err := NewMatrix(fmt.Sprintf("%s/%s", ts.URL, urlSuffix), token, roomId, &cert) + if err != nil { + return + } + + event := events.Event{} + _ = fuzz.NewConsumer(seed).GenerateStruct(&event) + + event.Severity = severity + + _ = matrix.Post(context.TODO(), event) + }) +} diff --git a/internal/notifier/opsgenie_test.go b/internal/notifier/opsgenie_test.go index 9e5690f91..f03fa5724 100644 --- a/internal/notifier/opsgenie_test.go +++ b/internal/notifier/opsgenie_test.go @@ -18,12 +18,16 @@ package notifier import ( "context" + "crypto/x509" "encoding/json" + "fmt" "io" "net/http" "net/http/httptest" "testing" + fuzz "github.com/AdaLogics/go-fuzz-headers" + "github.com/fluxcd/pkg/runtime/events" "github.com/stretchr/testify/require" ) @@ -44,3 +48,38 @@ func TestOpsgenie_Post(t *testing.T) { err = opsgenie.Post(context.TODO(), testEvent()) require.NoError(t, err) } + +func Fuzz_OpsGenie(f *testing.F) { + f.Add("token", "", "", "error", "", []byte{}, []byte{}) + f.Add("token", "", "update", "info", "", []byte{}, []byte{}) + + f.Fuzz(func(t *testing.T, + token, urlSuffix, commitStatus, severity, message string, seed, response []byte) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write(response) + io.Copy(io.Discard, r.Body) + r.Body.Close() + })) + defer ts.Close() + + var cert x509.CertPool + _ = fuzz.NewConsumer(seed).GenerateStruct(&cert) + + opsgenie, err := NewOpsgenie(fmt.Sprintf("%s/%s", ts.URL, urlSuffix), "", &cert, token) + if err != nil { + return + } + + event := events.Event{} + _ = fuzz.NewConsumer(seed).GenerateStruct(&event) + + if event.Metadata == nil { + event.Metadata = map[string]string{} + } + + event.Metadata["commit_status"] = commitStatus + event.Message = message + + _ = opsgenie.Post(context.TODO(), event) + }) +} diff --git a/internal/notifier/rocket_test.go b/internal/notifier/rocket_test.go index bfddcfc36..b0de335ac 100644 --- a/internal/notifier/rocket_test.go +++ b/internal/notifier/rocket_test.go @@ -18,12 +18,16 @@ package notifier import ( "context" + "crypto/x509" "encoding/json" + "fmt" "io" "net/http" "net/http/httptest" "testing" + fuzz "github.com/AdaLogics/go-fuzz-headers" + "github.com/fluxcd/pkg/runtime/events" "github.com/stretchr/testify/require" ) @@ -46,3 +50,39 @@ func TestRocket_Post(t *testing.T) { err = rocket.Post(context.TODO(), testEvent()) require.NoError(t, err) } + +func Fuzz_Rocket(f *testing.F) { + f.Add("user", "channel", "", "error", "", "", []byte{}, []byte{}) + f.Add("user", "channel", "", "error", "update", "", []byte{}, []byte{}) + + f.Fuzz(func(t *testing.T, + username, channel, urlSuffix, severity, commitStatus, message string, seed, response []byte) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write(response) + io.Copy(io.Discard, r.Body) + r.Body.Close() + })) + defer ts.Close() + + var cert x509.CertPool + _ = fuzz.NewConsumer(seed).GenerateStruct(&cert) + + rocket, err := NewRocket(fmt.Sprintf("%s/%s", ts.URL, urlSuffix), "", &cert, username, channel) + if err != nil { + return + } + + event := events.Event{} + _ = fuzz.NewConsumer(seed).GenerateStruct(&event) + + if event.Metadata == nil { + event.Metadata = map[string]string{} + } + + event.Metadata["commit_status"] = commitStatus + event.Severity = severity + event.Message = message + + _ = rocket.Post(context.TODO(), event) + }) +} diff --git a/internal/notifier/slack_test.go b/internal/notifier/slack_test.go index e9bcc5a72..6b5b50ec9 100644 --- a/internal/notifier/slack_test.go +++ b/internal/notifier/slack_test.go @@ -18,12 +18,16 @@ package notifier import ( "context" + "crypto/x509" "encoding/json" + "fmt" "io" "net/http" "net/http/httptest" "testing" + fuzz "github.com/AdaLogics/go-fuzz-headers" + "github.com/fluxcd/pkg/runtime/events" "github.com/stretchr/testify/require" ) @@ -56,3 +60,40 @@ func TestSlack_PostUpdate(t *testing.T) { err = slack.Post(context.TODO(), event) require.NoError(t, err) } + +func Fuzz_Slack(f *testing.F) { + f.Add("token", "user", "channel", "", "error", "", "", []byte{}, []byte{}) + f.Add("token", "", "channel", "", "info", "", "", []byte{}, []byte{}) + f.Add("token", "", "channel", "", "info", "update", "", []byte{}, []byte{}) + + f.Fuzz(func(t *testing.T, + token, username, channel, urlSuffix, severity, commitStatus, message string, seed, response []byte) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write(response) + io.Copy(io.Discard, r.Body) + r.Body.Close() + })) + defer ts.Close() + + var cert x509.CertPool + _ = fuzz.NewConsumer(seed).GenerateStruct(&cert) + + slack, err := NewSlack(fmt.Sprintf("%s/%s", ts.URL, urlSuffix), "", token, &cert, username, channel) + if err != nil { + return + } + + event := events.Event{} + _ = fuzz.NewConsumer(seed).GenerateStruct(&event) + + if event.Metadata == nil { + event.Metadata = map[string]string{} + } + + event.Metadata["commit_status"] = commitStatus + event.Severity = severity + event.Message = message + + _ = slack.Post(context.TODO(), event) + }) +} diff --git a/internal/notifier/teams_test.go b/internal/notifier/teams_test.go index 2d5111db8..ccd5de14d 100644 --- a/internal/notifier/teams_test.go +++ b/internal/notifier/teams_test.go @@ -18,12 +18,16 @@ package notifier import ( "context" + "crypto/x509" "encoding/json" + "fmt" "io" "net/http" "net/http/httptest" "testing" + fuzz "github.com/AdaLogics/go-fuzz-headers" + "github.com/fluxcd/pkg/runtime/events" "github.com/stretchr/testify/require" ) @@ -46,3 +50,40 @@ func TestTeams_Post(t *testing.T) { err = teams.Post(context.TODO(), testEvent()) require.NoError(t, err) } + +func Fuzz_MSTeams(f *testing.F) { + f.Add("token", "", "error", "", "", []byte{}, []byte{}) + f.Add("token", "", "info", "", "", []byte{}, []byte{}) + f.Add("token", "", "info", "", "update", []byte{}, []byte{}) + + f.Fuzz(func(t *testing.T, + token, urlSuffix, severity, message, commitStatus string, seed, response []byte) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write(response) + io.Copy(io.Discard, r.Body) + r.Body.Close() + })) + defer ts.Close() + + var cert x509.CertPool + _ = fuzz.NewConsumer(seed).GenerateStruct(&cert) + + teams, err := NewMSTeams(fmt.Sprintf("%s/%s", ts.URL, urlSuffix), "", &cert) + if err != nil { + return + } + + event := events.Event{} + _ = fuzz.NewConsumer(seed).GenerateStruct(&event) + + if event.Metadata == nil { + event.Metadata = map[string]string{} + } + + event.Metadata["commit_status"] = commitStatus + event.Message = message + event.Severity = severity + + _ = teams.Post(context.TODO(), event) + }) +} diff --git a/tests/fuzz/util_fuzzer.go b/internal/notifier/telegram_test.go similarity index 51% rename from tests/fuzz/util_fuzzer.go rename to internal/notifier/telegram_test.go index 2552c0f9c..d5ccc8f8c 100644 --- a/tests/fuzz/util_fuzzer.go +++ b/internal/notifier/telegram_test.go @@ -1,8 +1,5 @@ -//go:build gofuzz -// +build gofuzz - /* -Copyright 2021 The Flux authors +Copyright 2022 The Flux authors Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -20,22 +17,13 @@ limitations under the License. package notifier import ( - fuzz "github.com/AdaLogics/go-fuzz-headers" - "github.com/fluxcd/pkg/runtime/events" + "testing" ) -// FuzzNotifierUtil implements a fuzzer that targets -// notifier.formatNameAndDescription() and notifier.parseGitAddress(). -func FuzzNotifierUtil(data []byte) int { - f := fuzz.NewConsumer(data) - event := events.Event{} - - if err := f.GenerateStruct(&event); err != nil { - return 0 - } - - _, _ = formatNameAndDescription(event) - _, _, _ = parseGitAddress(string(data)) +func Fuzz_Telegram_escapeString(f *testing.F) { + f.Add("a-") - return 1 + f.Fuzz(func(t *testing.T, str string) { + _ = escapeString(str) + }) } diff --git a/internal/notifier/util_test.go b/internal/notifier/util_test.go index 397d0f8a6..b229f0683 100644 --- a/internal/notifier/util_test.go +++ b/internal/notifier/util_test.go @@ -19,6 +19,7 @@ package notifier import ( "testing" + fuzz "github.com/AdaLogics/go-fuzz-headers" "github.com/fluxcd/pkg/runtime/events" "github.com/stretchr/testify/require" v1 "k8s.io/api/core/v1" @@ -123,3 +124,24 @@ func TestUtil_BasicAuth(t *testing.T) { s := basicAuth(username, password) require.Equal(t, "dXNlcjpwYXNzd29yZA==", s) } + +func Fuzz_Util_ParseGitAddress(f *testing.F) { + f.Add("ssh://git@abc.com") + + f.Fuzz(func(t *testing.T, gitAddress string) { + _, _, _ = parseGitAddress(gitAddress) + }) +} + +func Fuzz_Util_FormatNameAndDescription(f *testing.F) { + f.Add("aA1-", []byte{}) + + f.Fuzz(func(t *testing.T, reason string, seed []byte) { + event := events.Event{} + _ = fuzz.NewConsumer(seed).GenerateStruct(&event) + + event.Reason = reason + + _, _ = formatNameAndDescription(event) + }) +} diff --git a/internal/notifier/webex_test.go b/internal/notifier/webex_test.go index e8991e2d5..5b9428baf 100644 --- a/internal/notifier/webex_test.go +++ b/internal/notifier/webex_test.go @@ -18,12 +18,16 @@ package notifier import ( "context" + "crypto/x509" "encoding/json" + "fmt" "io" "net/http" "net/http/httptest" "testing" + fuzz "github.com/AdaLogics/go-fuzz-headers" + "github.com/fluxcd/pkg/runtime/events" "github.com/stretchr/testify/require" ) @@ -54,3 +58,39 @@ func TestWebex_PostUpdate(t *testing.T) { err = webex.Post(context.TODO(), event) require.NoError(t, err) } + +func Fuzz_Webex(f *testing.F) { + f.Add("token", "channel", "", "error", "", "", []byte{}, []byte{}) + f.Add("token", "channel", "", "info", "", "update", []byte{}, []byte{}) + + f.Fuzz(func(t *testing.T, + token, channel, urlSuffix, severity, message, commitStatus string, seed, response []byte) { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Write(response) + io.Copy(io.Discard, r.Body) + r.Body.Close() + })) + defer ts.Close() + + var cert x509.CertPool + _ = fuzz.NewConsumer(seed).GenerateStruct(&cert) + + webex, err := NewWebex(fmt.Sprintf("%s/%s", ts.URL, urlSuffix), "", &cert, channel, token) + if err != nil { + return + } + + event := events.Event{} + _ = fuzz.NewConsumer(seed).GenerateStruct(&event) + + if event.Metadata == nil { + event.Metadata = map[string]string{} + } + + event.Metadata["commit_status"] = commitStatus + event.Message = message + event.Severity = severity + + _ = webex.Post(context.TODO(), event) + }) +} diff --git a/tests/fuzz/alertmanager_fuzzer.go b/tests/fuzz/alertmanager_fuzzer.go deleted file mode 100644 index 81cc42e0d..000000000 --- a/tests/fuzz/alertmanager_fuzzer.go +++ /dev/null @@ -1,55 +0,0 @@ -//go:build gofuzz -// +build gofuzz - -/* -Copyright 2022 The Flux authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package notifier - -import ( - "context" - "io" - "net/http" - "net/http/httptest" - - fuzz "github.com/AdaLogics/go-fuzz-headers" - "github.com/fluxcd/pkg/runtime/events" -) - -// FuzzAlertmanager implements a fuzzer that targets Alertmanager.Post(). -func FuzzAlertmanager(data []byte) int { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - io.ReadAll(r.Body) - r.Body.Close() - })) - defer ts.Close() - - alertmanager, err := NewAlertmanager(ts.URL, "", nil) - if err != nil { - return 0 - } - - f := fuzz.NewConsumer(data) - event := events.Event{} - - if err := f.GenerateStruct(&event); err != nil { - return 0 - } - - _ = alertmanager.Post(context.TODO(), event) - - return 1 -} diff --git a/tests/fuzz/azure_devops_fuzzer.go b/tests/fuzz/azure_devops_fuzzer.go deleted file mode 100644 index 621e0bff1..000000000 --- a/tests/fuzz/azure_devops_fuzzer.go +++ /dev/null @@ -1,59 +0,0 @@ -//go:build gofuzz -// +build gofuzz - -/* -Copyright 2022 The Flux authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package notifier - -import ( - "context" - "io" - "net/http" - "net/http/httptest" - - fuzz "github.com/AdaLogics/go-fuzz-headers" - "github.com/fluxcd/pkg/runtime/events" -) - -// FuzzAzureDevOps implements a fuzzer that targets AzureDevOps.Post(). -func FuzzAzureDevOps(data []byte) int { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - io.ReadAll(r.Body) - r.Body.Close() - })) - defer ts.Close() - - f := fuzz.NewConsumer(data) - token, err := f.GetString() - if err != nil { - return 0 - } - - azureDevOps, err := NewAzureDevOps(ts.URL, token, nil) - if err != nil { - return 0 - } - - event := events.Event{} - if err := f.GenerateStruct(&event); err != nil { - return 0 - } - - _ = azureDevOps.Post(context.TODO(), event) - - return 1 -} diff --git a/tests/fuzz/bitbucket_fuzzer.go b/tests/fuzz/bitbucket_fuzzer.go deleted file mode 100644 index d0e8e1cac..000000000 --- a/tests/fuzz/bitbucket_fuzzer.go +++ /dev/null @@ -1,59 +0,0 @@ -//go:build gofuzz -// +build gofuzz - -/* -Copyright 2022 The Flux authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package notifier - -import ( - "context" - "io" - "net/http" - "net/http/httptest" - - fuzz "github.com/AdaLogics/go-fuzz-headers" - "github.com/fluxcd/pkg/runtime/events" -) - -// FuzzBitbucket implements a fuzzer that targets Bitbucket.Post(). -func FuzzBitbucket(data []byte) int { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - io.ReadAll(r.Body) - r.Body.Close() - })) - defer ts.Close() - - f := fuzz.NewConsumer(data) - token, err := f.GetString() - if err != nil { - return 0 - } - - bitbucket, err := NewBitbucket(ts.URL, token, nil) - if err != nil { - return 0 - } - - event := events.Event{} - if err := f.GenerateStruct(&event); err != nil { - return 0 - } - - _ = bitbucket.Post(context.TODO(), event) - - return 1 -} diff --git a/tests/fuzz/discord_fuzzer.go b/tests/fuzz/discord_fuzzer.go deleted file mode 100644 index 9cebf5715..000000000 --- a/tests/fuzz/discord_fuzzer.go +++ /dev/null @@ -1,64 +0,0 @@ -//go:build gofuzz -// +build gofuzz - -/* -Copyright 2022 The Flux authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package notifier - -import ( - "context" - "io" - "net/http" - "net/http/httptest" - - fuzz "github.com/AdaLogics/go-fuzz-headers" - "github.com/fluxcd/pkg/runtime/events" -) - -// FuzzDiscord implements a fuzzer that targets Discord.Post(). -func FuzzDiscord(data []byte) int { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - io.ReadAll(r.Body) - r.Body.Close() - })) - defer ts.Close() - - f := fuzz.NewConsumer(data) - username, err := f.GetString() - if err != nil { - return 0 - } - - channel, err := f.GetString() - if err != nil { - return 0 - } - - discord, err := NewDiscord(ts.URL, "", username, channel) - if err != nil { - return 0 - } - - event := events.Event{} - if err := f.GenerateStruct(&event); err != nil { - return 0 - } - - _ = discord.Post(context.TODO(), event) - - return 1 -} diff --git a/tests/fuzz/forwarder_fuzzer.go b/tests/fuzz/forwarder_fuzzer.go deleted file mode 100644 index b045fa321..000000000 --- a/tests/fuzz/forwarder_fuzzer.go +++ /dev/null @@ -1,56 +0,0 @@ -//go:build gofuzz -// +build gofuzz - -/* -Copyright 2022 The Flux authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package notifier - -import ( - "context" - "io" - "net/http" - "net/http/httptest" - - fuzz "github.com/AdaLogics/go-fuzz-headers" - "github.com/fluxcd/pkg/runtime/events" -) - -// FuzzForwarder implements a fuzzer that targets Forwarder.Post(). -func FuzzForwarder(data []byte) int { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - io.ReadAll(r.Body) - r.Body.Close() - })) - defer ts.Close() - - header := make(map[string]string) - forwarder, err := NewForwarder(ts.URL, "", header, nil) - if err != nil { - return 0 - } - - f := fuzz.NewConsumer(data) - event := events.Event{} - - if err := f.GenerateStruct(&event); err != nil { - return 0 - } - - _ = forwarder.Post(context.TODO(), event) - - return 1 -} diff --git a/tests/fuzz/github_fuzzer.go b/tests/fuzz/github_fuzzer.go deleted file mode 100644 index 56917c36c..000000000 --- a/tests/fuzz/github_fuzzer.go +++ /dev/null @@ -1,59 +0,0 @@ -//go:build gofuzz -// +build gofuzz - -/* -Copyright 2022 The Flux authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package notifier - -import ( - "context" - "io" - "net/http" - "net/http/httptest" - - fuzz "github.com/AdaLogics/go-fuzz-headers" - "github.com/fluxcd/pkg/runtime/events" -) - -// FuzzGitHub implements a fuzzer that targets GitHub.Post(). -func FuzzGitHub(data []byte) int { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - io.ReadAll(r.Body) - r.Body.Close() - })) - defer ts.Close() - - f := fuzz.NewConsumer(data) - token, err := f.GetString() - if err != nil { - return 0 - } - - github, err := NewGitHub(ts.URL, token, nil) - if err != nil { - return 0 - } - - event := events.Event{} - if err := f.GenerateStruct(&event); err != nil { - return 0 - } - - _ = github.Post(context.TODO(), event) - - return 1 -} diff --git a/tests/fuzz/gitlab_fuzzer.go b/tests/fuzz/gitlab_fuzzer.go deleted file mode 100644 index 03fc6eb48..000000000 --- a/tests/fuzz/gitlab_fuzzer.go +++ /dev/null @@ -1,59 +0,0 @@ -//go:build gofuzz -// +build gofuzz - -/* -Copyright 2022 The Flux authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package notifier - -import ( - "context" - "io" - "net/http" - "net/http/httptest" - - fuzz "github.com/AdaLogics/go-fuzz-headers" - "github.com/fluxcd/pkg/runtime/events" -) - -// FuzzGitLab implements a fuzzer that targets GitLab.Post(). -func FuzzGitLab(data []byte) int { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - io.ReadAll(r.Body) - r.Body.Close() - })) - defer ts.Close() - - f := fuzz.NewConsumer(data) - token, err := f.GetString() - if err != nil { - return 0 - } - - gitLab, err := NewGitLab(ts.URL, token, nil) - if err != nil { - return 0 - } - - event := events.Event{} - if err := f.GenerateStruct(&event); err != nil { - return 0 - } - - _ = gitLab.Post(context.TODO(), event) - - return 1 -} diff --git a/tests/fuzz/go.mod b/tests/fuzz/go.mod deleted file mode 100644 index a517047c0..000000000 --- a/tests/fuzz/go.mod +++ /dev/null @@ -1,11 +0,0 @@ -module github.com/fluxcd/notification-controller/tests/fuzz - -// This module is used only to avoid polluting the main module -// with fuzz dependencies. - -go 1.17 - -replace ( - github.com/fluxcd/notification-controller/api => ../../api - github.com/fluxcd/notification-controller => ../../ -) diff --git a/tests/fuzz/google_chat_fuzzer.go b/tests/fuzz/google_chat_fuzzer.go deleted file mode 100644 index e4fc68e65..000000000 --- a/tests/fuzz/google_chat_fuzzer.go +++ /dev/null @@ -1,55 +0,0 @@ -//go:build gofuzz -// +build gofuzz - -/* -Copyright 2022 The Flux authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package notifier - -import ( - "context" - "io" - "net/http" - "net/http/httptest" - - fuzz "github.com/AdaLogics/go-fuzz-headers" - "github.com/fluxcd/pkg/runtime/events" -) - -// FuzzGoogleChat implements a fuzzer that targets GoogleChat.Post(). -func FuzzGoogleChat(data []byte) int { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - io.ReadAll(r.Body) - r.Body.Close() - })) - defer ts.Close() - - googlechat, err := NewGoogleChat(ts.URL, "") - if err != nil { - return 0 - } - - f := fuzz.NewConsumer(data) - event := events.Event{} - - if err := f.GenerateStruct(&event); err != nil { - return 0 - } - - _ = googlechat.Post(context.TODO(), event) - - return 1 -} diff --git a/tests/fuzz/lark_fuzzer.go b/tests/fuzz/lark_fuzzer.go deleted file mode 100644 index 2e055492c..000000000 --- a/tests/fuzz/lark_fuzzer.go +++ /dev/null @@ -1,55 +0,0 @@ -//go:build gofuzz -// +build gofuzz - -/* -Copyright 2022 The Flux authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package notifier - -import ( - "context" - "io" - "net/http" - "net/http/httptest" - - fuzz "github.com/AdaLogics/go-fuzz-headers" - "github.com/fluxcd/pkg/runtime/events" -) - -// FuzzLark implements a fuzzer that targets Lark.Post(). -func FuzzLark(data []byte) int { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - io.ReadAll(r.Body) - r.Body.Close() - })) - defer ts.Close() - - lark, err := NewLark(ts.URL) - if err != nil { - return 0 - } - - f := fuzz.NewConsumer(data) - event := events.Event{} - - if err := f.GenerateStruct(&event); err != nil { - return 0 - } - - _ = lark.Post(context.TODO(), event) - - return 1 -} diff --git a/tests/fuzz/matrix_fuzzer.go b/tests/fuzz/matrix_fuzzer.go deleted file mode 100644 index 4bcff2cae..000000000 --- a/tests/fuzz/matrix_fuzzer.go +++ /dev/null @@ -1,59 +0,0 @@ -//go:build gofuzz -// +build gofuzz - -/* -Copyright 2022 The Flux authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package notifier - -import ( - "context" - "io" - "net/http" - "net/http/httptest" - - fuzz "github.com/AdaLogics/go-fuzz-headers" - "github.com/fluxcd/pkg/runtime/events" -) - -// FuzzMatrix implements a fuzzer that targets Matrix.Post(). -func FuzzMatrix(data []byte) int { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - io.ReadAll(r.Body) - r.Body.Close() - })) - defer ts.Close() - - f := fuzz.NewConsumer(data) - token, err := f.GetString() - if err != nil { - return 0 - } - - matrix, err := NewMatrix(ts.URL, "", token, nil) - if err != nil { - return 0 - } - - event := events.Event{} - if err := f.GenerateStruct(&event); err != nil { - return 0 - } - - _ = matrix.Post(context.TODO(), event) - - return 1 -} diff --git a/tests/fuzz/native_go_run.sh b/tests/fuzz/native_go_run.sh new file mode 100755 index 000000000..afd9af615 --- /dev/null +++ b/tests/fuzz/native_go_run.sh @@ -0,0 +1,36 @@ +#!/usr/bin/env bash + +# Copyright 2022 The Flux authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +set -euxo pipefail + +# This script iterates through all go fuzzing targets, running each one +# through the period of time established by FUZZ_TIME. + +FUZZ_TIME=${FUZZ_TIME:-"5s"} + +test_files=$(grep -r --include='**_test.go' --files-with-matches 'func Fuzz' .) + +for file in ${test_files} +do + targets=$(grep -oP 'func \K(Fuzz\w*)' "${file}") + for target_name in ${targets} + do + echo "Running ${file}.${target_name} for ${FUZZ_TIME}." + file_dir=$(dirname "${file}") + + go test -fuzz="^${target_name}\$" -fuzztime "${FUZZ_TIME}" "${file_dir}" + done +done diff --git a/tests/fuzz/opsgenie_fuzzer.go b/tests/fuzz/opsgenie_fuzzer.go deleted file mode 100644 index db6c6c979..000000000 --- a/tests/fuzz/opsgenie_fuzzer.go +++ /dev/null @@ -1,55 +0,0 @@ -//go:build gofuzz -// +build gofuzz - -/* -Copyright 2022 The Flux authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package notifier - -import ( - "context" - "io" - "net/http" - "net/http/httptest" - - fuzz "github.com/AdaLogics/go-fuzz-headers" - "github.com/fluxcd/pkg/runtime/events" -) - -// FuzzOpsGenie implements a fuzzer that targets Opsgenie.Post(). -func FuzzOpsGenie(data []byte) int { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - io.ReadAll(r.Body) - r.Body.Close() - })) - defer ts.Close() - - opsgenie, err := NewOpsgenie(ts.URL, "", nil, "token") - if err != nil { - return 0 - } - - f := fuzz.NewConsumer(data) - event := events.Event{} - - if err := f.GenerateStruct(&event); err != nil { - return 0 - } - - _ = opsgenie.Post(context.TODO(), event) - - return 1 -} diff --git a/tests/fuzz/oss_fuzz_build.sh b/tests/fuzz/oss_fuzz_build.sh index 771236179..247d9b1fb 100755 --- a/tests/fuzz/oss_fuzz_build.sh +++ b/tests/fuzz/oss_fuzz_build.sh @@ -19,48 +19,61 @@ set -euxo pipefail GOPATH="${GOPATH:-/root/go}" GO_SRC="${GOPATH}/src" PROJECT_PATH="github.com/fluxcd/notification-controller" +TMP_DIR=$(mktemp -d /tmp/oss_fuzz-XXXXXX) -cd "${GO_SRC}" - -# Move fuzzer to their respective directories. -# This removes dependency noises from the modules' go.mod and go.sum files. -cp "${PROJECT_PATH}/tests/fuzz/util_fuzzer.go" "${PROJECT_PATH}/internal/notifier/" -cp "${PROJECT_PATH}/tests/fuzz/alertmanager_fuzzer.go" "${PROJECT_PATH}/internal/notifier/" -cp "${PROJECT_PATH}/tests/fuzz/opsgenie_fuzzer.go" "${PROJECT_PATH}/internal/notifier/" -cp "${PROJECT_PATH}/tests/fuzz/webex_fuzzer.go" "${PROJECT_PATH}/internal/notifier/" -cp "${PROJECT_PATH}/tests/fuzz/discord_fuzzer.go" "${PROJECT_PATH}/internal/notifier/" -cp "${PROJECT_PATH}/tests/fuzz/forwarder_fuzzer.go" "${PROJECT_PATH}/internal/notifier/" -cp "${PROJECT_PATH}/tests/fuzz/lark_fuzzer.go" "${PROJECT_PATH}/internal/notifier/" -cp "${PROJECT_PATH}/tests/fuzz/matrix_fuzzer.go" "${PROJECT_PATH}/internal/notifier/" -cp "${PROJECT_PATH}/tests/fuzz/rocket_fuzzer.go" "${PROJECT_PATH}/internal/notifier/" -cp "${PROJECT_PATH}/tests/fuzz/slack_fuzzer.go" "${PROJECT_PATH}/internal/notifier/" -cp "${PROJECT_PATH}/tests/fuzz/teams_fuzzer.go" "${PROJECT_PATH}/internal/notifier/" -cp "${PROJECT_PATH}/tests/fuzz/google_chat_fuzzer.go" "${PROJECT_PATH}/internal/notifier/" -cp "${PROJECT_PATH}/tests/fuzz/azure_devops_fuzzer.go" "${PROJECT_PATH}/internal/notifier/" -cp "${PROJECT_PATH}/tests/fuzz/bitbucket_fuzzer.go" "${PROJECT_PATH}/internal/notifier/" -cp "${PROJECT_PATH}/tests/fuzz/github_fuzzer.go" "${PROJECT_PATH}/internal/notifier/" -cp "${PROJECT_PATH}/tests/fuzz/gitlab_fuzzer.go" "${PROJECT_PATH}/internal/notifier/" - - -# compile fuzz tests for the runtime module -pushd "${PROJECT_PATH}" - -go get -d github.com/AdaLogics/go-fuzz-headers -compile_go_fuzzer "${PROJECT_PATH}/internal/notifier/" FuzzNotifierUtil fuzz_notifier_util -compile_go_fuzzer "${PROJECT_PATH}/internal/notifier/" FuzzAlertmanager fuzz_alert_manager -compile_go_fuzzer "${PROJECT_PATH}/internal/notifier/" FuzzOpsGenie fuzz_opsgenie -compile_go_fuzzer "${PROJECT_PATH}/internal/notifier/" FuzzWebex fuzz_webex -compile_go_fuzzer "${PROJECT_PATH}/internal/notifier/" FuzzDiscord fuzz_discord -compile_go_fuzzer "${PROJECT_PATH}/internal/notifier/" FuzzForwarder fuzz_forwarder -compile_go_fuzzer "${PROJECT_PATH}/internal/notifier/" FuzzLark fuzz_lark -compile_go_fuzzer "${PROJECT_PATH}/internal/notifier/" FuzzMatrix fuzz_matrix -compile_go_fuzzer "${PROJECT_PATH}/internal/notifier/" FuzzRocket fuzz_rocket -compile_go_fuzzer "${PROJECT_PATH}/internal/notifier/" FuzzSlack fuzz_slack -compile_go_fuzzer "${PROJECT_PATH}/internal/notifier/" FuzzMSTeams fuzz_msteams -compile_go_fuzzer "${PROJECT_PATH}/internal/notifier/" FuzzGoogleChat fuzz_google_chat -compile_go_fuzzer "${PROJECT_PATH}/internal/notifier/" FuzzAzureDevOps fuzz_azure_devops -compile_go_fuzzer "${PROJECT_PATH}/internal/notifier/" FuzzBitbucket fuzz_bitbucket -compile_go_fuzzer "${PROJECT_PATH}/internal/notifier/" FuzzGitHub fuzz_github -compile_go_fuzzer "${PROJECT_PATH}/internal/notifier/" FuzzGitLab fuzz_gitlab - -popd +cleanup(){ + rm -rf "${TMP_DIR}" +} +trap cleanup EXIT + +install_deps(){ + if ! command -v go-118-fuzz-build &> /dev/null || ! command -v addimport &> /dev/null; then + mkdir -p "${TMP_DIR}/go-118-fuzz-build" + + git clone https://github.com/AdamKorcz/go-118-fuzz-build "${TMP_DIR}/go-118-fuzz-build" + cd "${TMP_DIR}/go-118-fuzz-build" + go build -o "${GOPATH}/bin/go-118-fuzz-build" + + cd addimport + go build -o "${GOPATH}/bin/addimport" + fi + + if ! command -v goimports &> /dev/null; then + go install golang.org/x/tools/cmd/goimports@latest + fi +} + +# Removes the content of test funcs which could cause the Fuzz +# tests to break. +remove_test_funcs(){ + filename=$1 + + echo "removing co-located *testing.T" + sed -i -e '/func Test.*testing.T) {$/ {:r;/\n}/!{N;br}; s/\n.*\n/\n/}' "${filename}" + + # After removing the body of the go testing funcs, consolidate the imports. + goimports -w "${filename}" +} + +install_deps + +cd "${GO_SRC}/${PROJECT_PATH}" + +go get github.com/AdamKorcz/go-118-fuzz-build/utils + +# Iterate through all Go Fuzz targets, compiling each into a fuzzer. +test_files=$(grep -r --include='**_test.go' --files-with-matches 'func Fuzz' .) +for file in ${test_files} +do + remove_test_funcs "${file}" + + targets=$(grep -oP 'func \K(Fuzz\w*)' "${file}") + for target_name in ${targets} + do + fuzzer_name=$(echo "${target_name}" | tr '[:upper:]' '[:lower:]') + target_dir=$(dirname "${file}") + + echo "Building ${file}.${target_name} into ${fuzzer_name}" + compile_native_go_fuzzer "${target_dir}" "${target_name}" "${fuzzer_name}" + done +done diff --git a/tests/fuzz/rocket_fuzzer.go b/tests/fuzz/rocket_fuzzer.go deleted file mode 100644 index 8035f991b..000000000 --- a/tests/fuzz/rocket_fuzzer.go +++ /dev/null @@ -1,64 +0,0 @@ -//go:build gofuzz -// +build gofuzz - -/* -Copyright 2022 The Flux authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package notifier - -import ( - "context" - "io" - "net/http" - "net/http/httptest" - - fuzz "github.com/AdaLogics/go-fuzz-headers" - "github.com/fluxcd/pkg/runtime/events" -) - -// FuzzRocket implements a fuzzer that targets Rocket.Post(). -func FuzzRocket(data []byte) int { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - io.ReadAll(r.Body) - r.Body.Close() - })) - defer ts.Close() - - f := fuzz.NewConsumer(data) - username, err := f.GetString() - if err != nil { - return 0 - } - - channel, err := f.GetString() - if err != nil { - return 0 - } - - rocket, err := NewRocket(ts.URL, "", nil, username, channel) - if err != nil { - return 0 - } - - event := events.Event{} - if err := f.GenerateStruct(&event); err != nil { - return 0 - } - - _ = rocket.Post(context.TODO(), event) - - return 1 -} diff --git a/tests/fuzz/slack_fuzzer.go b/tests/fuzz/slack_fuzzer.go deleted file mode 100644 index a165b2961..000000000 --- a/tests/fuzz/slack_fuzzer.go +++ /dev/null @@ -1,69 +0,0 @@ -//go:build gofuzz -// +build gofuzz - -/* -Copyright 2022 The Flux authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package notifier - -import ( - "context" - "io" - "net/http" - "net/http/httptest" - - fuzz "github.com/AdaLogics/go-fuzz-headers" - "github.com/fluxcd/pkg/runtime/events" -) - -// FuzzSlack implements a fuzzer that targets Slack.Post(). -func FuzzSlack(data []byte) int { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - io.ReadAll(r.Body) - r.Body.Close() - })) - defer ts.Close() - - f := fuzz.NewConsumer(data) - username, err := f.GetString() - if err != nil { - return 0 - } - - channel, err := f.GetString() - if err != nil { - return 0 - } - - token, err := f.GetString() - if err != nil { - return 0 - } - - slack, err := NewSlack(ts.URL, "", token, nil, username, channel) - if err != nil { - return 0 - } - - event := events.Event{} - if err := f.GenerateStruct(&event); err != nil { - return 0 - } - - _ = slack.Post(context.TODO(), event) - - return 1 -} diff --git a/tests/fuzz/teams_fuzzer.go b/tests/fuzz/teams_fuzzer.go deleted file mode 100644 index 4e90e25ed..000000000 --- a/tests/fuzz/teams_fuzzer.go +++ /dev/null @@ -1,55 +0,0 @@ -//go:build gofuzz -// +build gofuzz - -/* -Copyright 2022 The Flux authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package notifier - -import ( - "context" - "io" - "net/http" - "net/http/httptest" - - fuzz "github.com/AdaLogics/go-fuzz-headers" - "github.com/fluxcd/pkg/runtime/events" -) - -// FuzzMSTeams implements a fuzzer that targets MSTeams.Post(). -func FuzzMSTeams(data []byte) int { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - io.ReadAll(r.Body) - r.Body.Close() - })) - defer ts.Close() - - teams, err := NewMSTeams(ts.URL, "", nil) - if err != nil { - return 0 - } - - f := fuzz.NewConsumer(data) - event := events.Event{} - - if err := f.GenerateStruct(&event); err != nil { - return 0 - } - - _ = teams.Post(context.TODO(), event) - - return 1 -} diff --git a/tests/fuzz/webex_fuzzer.go b/tests/fuzz/webex_fuzzer.go deleted file mode 100644 index 6fbbde1f4..000000000 --- a/tests/fuzz/webex_fuzzer.go +++ /dev/null @@ -1,55 +0,0 @@ -//go:build gofuzz -// +build gofuzz - -/* -Copyright 2022 The Flux authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package notifier - -import ( - "context" - "io" - "net/http" - "net/http/httptest" - - fuzz "github.com/AdaLogics/go-fuzz-headers" - "github.com/fluxcd/pkg/runtime/events" -) - -// FuzzWebex implements a fuzzer that targets Webex.Post(). -func FuzzWebex(data []byte) int { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - io.ReadAll(r.Body) - r.Body.Close() - })) - defer ts.Close() - - webex, err := NewWebex(ts.URL, "", nil, "", "") - if err != nil { - return 0 - } - - f := fuzz.NewConsumer(data) - event := events.Event{} - - if err := f.GenerateStruct(&event); err != nil { - return 0 - } - - _ = webex.Post(context.TODO(), event) - - return 1 -}