From 8d95dc087a8ba6a0f256872399569c0b1d5b823c Mon Sep 17 00:00:00 2001 From: Paulo Gomes Date: Wed, 23 Nov 2022 14:56:32 +0000 Subject: [PATCH] build: Improve fuzz tests' reliability Establish conventions which aligns with what is supported upstream today, whilst expanding on documentation to ensure folks have pointers on how to debug/check for issues going forwards. Signed-off-by: Paulo Gomes --- internal/notifier/alertmanager_fuzz_test.go | 69 ++++++++++++++ internal/notifier/alertmanager_test.go | 44 --------- internal/notifier/azure_devops_fuzz_test.go | 78 ++++++++++++++++ internal/notifier/azure_devops_test.go | 57 ------------ internal/notifier/bitbucket_fuzz_test.go | 77 ++++++++++++++++ internal/notifier/bitbucket_test.go | 55 ------------ internal/notifier/discord_fuzz_test.go | 63 +++++++++++++ internal/notifier/discord_test.go | 37 -------- internal/notifier/forwarder_fuzz_test.go | 60 +++++++++++++ internal/notifier/forwarder_test.go | 35 +------- .../notifier/github_dispatch_fuzz_test.go | 67 ++++++++++++++ internal/notifier/github_dispatch_test.go | 44 --------- internal/notifier/github_fuzz_test.go | 71 +++++++++++++++ internal/notifier/github_test.go | 49 ---------- internal/notifier/gitlab_fuzz_test.go | 69 ++++++++++++++ internal/notifier/gitlab_test.go | 47 ---------- internal/notifier/google_chat_fuzz_test.go | 62 +++++++++++++ internal/notifier/google_chat_test.go | 36 -------- internal/notifier/grafana_fuzz_test.go | 65 ++++++++++++++ internal/notifier/grafana_test.go | 39 -------- internal/notifier/lark_fuzz_test.go | 45 ++++++++++ internal/notifier/lark_test.go | 35 -------- internal/notifier/matrix_fuzz_test.go | 45 ++++++++++ internal/notifier/matrix_test.go | 38 -------- internal/notifier/opsgenie_fuzz_test.go | 65 ++++++++++++++ internal/notifier/opsgenie_test.go | 39 -------- internal/notifier/rocket_fuzz_test.go | 66 ++++++++++++++ internal/notifier/rocket_test.go | 40 --------- internal/notifier/slack_fuzz_test.go | 67 ++++++++++++++ internal/notifier/slack_test.go | 41 --------- internal/notifier/teams_fuzz_test.go | 67 ++++++++++++++ internal/notifier/teams_test.go | 41 --------- ...telegram_test.go => telegram_fuzz_test.go} | 0 internal/notifier/util_fuzz_test.go | 45 ++++++++++ internal/notifier/util_test.go | 22 ----- internal/notifier/webex_fuzz_test.go | 66 ++++++++++++++ internal/notifier/webex_test.go | 40 --------- tests/fuzz/README.md | 82 +++++++++++++++++ tests/fuzz/oss_fuzz_build.sh | 90 ++++++++++--------- 39 files changed, 1276 insertions(+), 782 deletions(-) create mode 100644 internal/notifier/alertmanager_fuzz_test.go create mode 100644 internal/notifier/azure_devops_fuzz_test.go create mode 100644 internal/notifier/bitbucket_fuzz_test.go create mode 100644 internal/notifier/discord_fuzz_test.go create mode 100644 internal/notifier/forwarder_fuzz_test.go create mode 100644 internal/notifier/github_dispatch_fuzz_test.go create mode 100644 internal/notifier/github_fuzz_test.go create mode 100644 internal/notifier/gitlab_fuzz_test.go create mode 100644 internal/notifier/google_chat_fuzz_test.go create mode 100644 internal/notifier/grafana_fuzz_test.go create mode 100644 internal/notifier/lark_fuzz_test.go create mode 100644 internal/notifier/matrix_fuzz_test.go create mode 100644 internal/notifier/opsgenie_fuzz_test.go create mode 100644 internal/notifier/rocket_fuzz_test.go create mode 100644 internal/notifier/slack_fuzz_test.go create mode 100644 internal/notifier/teams_fuzz_test.go rename internal/notifier/{telegram_test.go => telegram_fuzz_test.go} (100%) create mode 100644 internal/notifier/util_fuzz_test.go create mode 100644 internal/notifier/webex_fuzz_test.go create mode 100644 tests/fuzz/README.md diff --git a/internal/notifier/alertmanager_fuzz_test.go b/internal/notifier/alertmanager_fuzz_test.go new file mode 100644 index 000000000..1ccfbac54 --- /dev/null +++ b/internal/notifier/alertmanager_fuzz_test.go @@ -0,0 +1,69 @@ +/* +Copyright 2020 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" + "crypto/x509" + "fmt" + "io" + "net/http" + "net/http/httptest" + "testing" + + fuzz "github.com/AdaLogics/go-fuzz-headers" + eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1" +) + +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 := eventv1.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/alertmanager_test.go b/internal/notifier/alertmanager_test.go index 2ecb4f913..432ecd323 100644 --- a/internal/notifier/alertmanager_test.go +++ b/internal/notifier/alertmanager_test.go @@ -18,18 +18,13 @@ package notifier import ( "context" - "crypto/x509" "encoding/json" - "fmt" "io" "net/http" "net/http/httptest" "testing" - eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1" "github.com/stretchr/testify/require" - - fuzz "github.com/AdaLogics/go-fuzz-headers" ) func TestAlertmanager_Post(t *testing.T) { @@ -49,42 +44,3 @@ 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 := eventv1.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_fuzz_test.go b/internal/notifier/azure_devops_fuzz_test.go new file mode 100644 index 000000000..25c07c72e --- /dev/null +++ b/internal/notifier/azure_devops_fuzz_test.go @@ -0,0 +1,78 @@ +/* +Copyright 2020 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" + "crypto/x509" + "fmt" + "io" + "net/http" + "net/http/httptest" + "strings" + "testing" + + fuzz "github.com/AdaLogics/go-fuzz-headers" + eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1" +) + +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 := eventv1.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) + }) +} diff --git a/internal/notifier/azure_devops_test.go b/internal/notifier/azure_devops_test.go index 8949f8ebd..75c49f0e3 100644 --- a/internal/notifier/azure_devops_test.go +++ b/internal/notifier/azure_devops_test.go @@ -17,20 +17,10 @@ limitations under the License. package notifier import ( - "context" - "crypto/x509" - "fmt" - "io" - "net/http" - "net/http/httptest" - "strings" "testing" - eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1" "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) { @@ -70,53 +60,6 @@ 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 := eventv1.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_fuzz_test.go b/internal/notifier/bitbucket_fuzz_test.go new file mode 100644 index 000000000..e92bf2ce3 --- /dev/null +++ b/internal/notifier/bitbucket_fuzz_test.go @@ -0,0 +1,77 @@ +/* +Copyright 2020 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" + "crypto/x509" + "fmt" + "io" + "net/http" + "net/http/httptest" + "net/url" + "testing" + + fuzz "github.com/AdaLogics/go-fuzz-headers" + eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1" +) + +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 := eventv1.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/bitbucket_test.go b/internal/notifier/bitbucket_test.go index 0ae893a5b..519506eed 100644 --- a/internal/notifier/bitbucket_test.go +++ b/internal/notifier/bitbucket_test.go @@ -17,17 +17,8 @@ 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" - eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1" "github.com/stretchr/testify/assert" ) @@ -47,49 +38,3 @@ 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 := eventv1.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_fuzz_test.go b/internal/notifier/discord_fuzz_test.go new file mode 100644 index 000000000..d418a1b7d --- /dev/null +++ b/internal/notifier/discord_fuzz_test.go @@ -0,0 +1,63 @@ +/* +Copyright 2020 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" + "fmt" + "io" + "net/http" + "net/http/httptest" + "testing" + + fuzz "github.com/AdaLogics/go-fuzz-headers" + eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1" +) + +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 := eventv1.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/discord_test.go b/internal/notifier/discord_test.go index 9f0a1863d..db15eaa45 100644 --- a/internal/notifier/discord_test.go +++ b/internal/notifier/discord_test.go @@ -19,15 +19,12 @@ package notifier import ( "context" "encoding/json" - "fmt" "io" "net/http" "net/http/httptest" "strings" "testing" - fuzz "github.com/AdaLogics/go-fuzz-headers" - eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -52,37 +49,3 @@ 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 := eventv1.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_fuzz_test.go b/internal/notifier/forwarder_fuzz_test.go new file mode 100644 index 000000000..e4e33fcd2 --- /dev/null +++ b/internal/notifier/forwarder_fuzz_test.go @@ -0,0 +1,60 @@ +/* +Copyright 2020 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" + "crypto/x509" + "fmt" + "io" + "net/http" + "net/http/httptest" + "testing" + + fuzz "github.com/AdaLogics/go-fuzz-headers" + eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1" +) + +func Fuzz_Forwarder(f *testing.F) { + f.Add("", []byte{}, []byte{}, []byte{}) + + f.Fuzz(func(t *testing.T, + urlSuffix string, seed, response, hmacKey []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, hmacKey) + if err != nil { + return + } + + event := eventv1.Event{} + _ = fuzz.NewConsumer(seed).GenerateStruct(&event) + + _ = forwarder.Post(context.TODO(), event) + }) +} diff --git a/internal/notifier/forwarder_test.go b/internal/notifier/forwarder_test.go index 92377563d..d0f6815b4 100644 --- a/internal/notifier/forwarder_test.go +++ b/internal/notifier/forwarder_test.go @@ -18,19 +18,16 @@ package notifier import ( "context" - "crypto/x509" "encoding/json" - "fmt" "io" "net/http" "net/http/httptest" "testing" "time" - fuzz "github.com/AdaLogics/go-fuzz-headers" - eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1" "github.com/stretchr/testify/require" ) @@ -137,33 +134,3 @@ func TestForwarder_Post(t *testing.T) { }) } } - -func Fuzz_Forwarder(f *testing.F) { - f.Add("", []byte{}, []byte{}, []byte{}) - - f.Fuzz(func(t *testing.T, - urlSuffix string, seed, response, hmacKey []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, hmacKey) - if err != nil { - return - } - - event := eventv1.Event{} - _ = fuzz.NewConsumer(seed).GenerateStruct(&event) - - _ = forwarder.Post(context.TODO(), event) - }) -} diff --git a/internal/notifier/github_dispatch_fuzz_test.go b/internal/notifier/github_dispatch_fuzz_test.go new file mode 100644 index 000000000..357866368 --- /dev/null +++ b/internal/notifier/github_dispatch_fuzz_test.go @@ -0,0 +1,67 @@ +/* +Copyright 2020 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" + "crypto/x509" + "fmt" + "io" + "net/http" + "net/http/httptest" + "testing" + + fuzz "github.com/AdaLogics/go-fuzz-headers" + eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1" +) + +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 := eventv1.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_dispatch_test.go b/internal/notifier/github_dispatch_test.go index 0e501917b..0841f7d16 100644 --- a/internal/notifier/github_dispatch_test.go +++ b/internal/notifier/github_dispatch_test.go @@ -18,15 +18,8 @@ package notifier import ( "context" - "crypto/x509" - "fmt" - "io" - "net/http" - "net/http/httptest" "testing" - fuzz "github.com/AdaLogics/go-fuzz-headers" - eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -66,40 +59,3 @@ 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 := eventv1.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_fuzz_test.go b/internal/notifier/github_fuzz_test.go new file mode 100644 index 000000000..9f615b669 --- /dev/null +++ b/internal/notifier/github_fuzz_test.go @@ -0,0 +1,71 @@ +/* +Copyright 2020 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" + "crypto/x509" + "fmt" + "io" + "net/http" + "net/http/httptest" + "testing" + + fuzz "github.com/AdaLogics/go-fuzz-headers" + eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1" +) + +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 := eventv1.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) + }) +} diff --git a/internal/notifier/github_test.go b/internal/notifier/github_test.go index 19ddeaf0c..c320df17d 100644 --- a/internal/notifier/github_test.go +++ b/internal/notifier/github_test.go @@ -17,16 +17,8 @@ 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" - eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1" "github.com/google/go-github/v41/github" "github.com/stretchr/testify/assert" ) @@ -77,47 +69,6 @@ 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 := eventv1.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_fuzz_test.go b/internal/notifier/gitlab_fuzz_test.go new file mode 100644 index 000000000..a6d2b4920 --- /dev/null +++ b/internal/notifier/gitlab_fuzz_test.go @@ -0,0 +1,69 @@ +/* +Copyright 2020 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" + "crypto/x509" + "fmt" + "io" + "net/http" + "net/http/httptest" + "testing" + + fuzz "github.com/AdaLogics/go-fuzz-headers" + eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1" +) + +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 := eventv1.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/gitlab_test.go b/internal/notifier/gitlab_test.go index 00ef789f3..32a50bdf9 100644 --- a/internal/notifier/gitlab_test.go +++ b/internal/notifier/gitlab_test.go @@ -17,16 +17,8 @@ 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" - eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1" "github.com/stretchr/testify/assert" ) @@ -53,42 +45,3 @@ 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 := eventv1.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_fuzz_test.go b/internal/notifier/google_chat_fuzz_test.go new file mode 100644 index 000000000..8a490c936 --- /dev/null +++ b/internal/notifier/google_chat_fuzz_test.go @@ -0,0 +1,62 @@ +/* +Copyright 2020 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" + "fmt" + "io" + "net/http" + "net/http/httptest" + "testing" + + fuzz "github.com/AdaLogics/go-fuzz-headers" + eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1" +) + +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 := eventv1.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/google_chat_test.go b/internal/notifier/google_chat_test.go index 595300fe5..8ca3074fc 100644 --- a/internal/notifier/google_chat_test.go +++ b/internal/notifier/google_chat_test.go @@ -19,14 +19,11 @@ package notifier import ( "context" "encoding/json" - "fmt" "io" "net/http" "net/http/httptest" "testing" - fuzz "github.com/AdaLogics/go-fuzz-headers" - eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1" "github.com/stretchr/testify/require" ) @@ -52,36 +49,3 @@ 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 := eventv1.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_fuzz_test.go b/internal/notifier/grafana_fuzz_test.go new file mode 100644 index 000000000..fabd65bc6 --- /dev/null +++ b/internal/notifier/grafana_fuzz_test.go @@ -0,0 +1,65 @@ +/* +Copyright 2020 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" + "crypto/x509" + "fmt" + "io" + "net/http" + "net/http/httptest" + "testing" + + fuzz "github.com/AdaLogics/go-fuzz-headers" + eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1" +) + +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 := eventv1.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/grafana_test.go b/internal/notifier/grafana_test.go index a41a80520..6675cd907 100644 --- a/internal/notifier/grafana_test.go +++ b/internal/notifier/grafana_test.go @@ -18,16 +18,12 @@ package notifier import ( "context" - "crypto/x509" "encoding/json" - "fmt" "io" "net/http" "net/http/httptest" "testing" - fuzz "github.com/AdaLogics/go-fuzz-headers" - eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -55,38 +51,3 @@ 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 := eventv1.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_fuzz_test.go b/internal/notifier/lark_fuzz_test.go new file mode 100644 index 000000000..80937bec2 --- /dev/null +++ b/internal/notifier/lark_fuzz_test.go @@ -0,0 +1,45 @@ +package notifier + +import ( + "context" + "fmt" + "io" + "net/http" + "net/http/httptest" + "testing" + + fuzz "github.com/AdaLogics/go-fuzz-headers" + eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1" +) + +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 := eventv1.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/lark_test.go b/internal/notifier/lark_test.go index 84afdf41d..50f07ac4a 100644 --- a/internal/notifier/lark_test.go +++ b/internal/notifier/lark_test.go @@ -3,14 +3,11 @@ package notifier import ( "context" "encoding/json" - "fmt" "io" "net/http" "net/http/httptest" "testing" - fuzz "github.com/AdaLogics/go-fuzz-headers" - eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1" "github.com/stretchr/testify/require" ) @@ -33,35 +30,3 @@ 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 := eventv1.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_fuzz_test.go b/internal/notifier/matrix_fuzz_test.go new file mode 100644 index 000000000..33c88de1c --- /dev/null +++ b/internal/notifier/matrix_fuzz_test.go @@ -0,0 +1,45 @@ +package notifier + +import ( + "context" + "crypto/x509" + "fmt" + "io" + "net/http" + "net/http/httptest" + "testing" + + fuzz "github.com/AdaLogics/go-fuzz-headers" + eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1" +) + +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 := eventv1.Event{} + _ = fuzz.NewConsumer(seed).GenerateStruct(&event) + + event.Severity = severity + + _ = matrix.Post(context.TODO(), event) + }) +} diff --git a/internal/notifier/matrix_test.go b/internal/notifier/matrix_test.go index f9a21d0db..03cbac687 100644 --- a/internal/notifier/matrix_test.go +++ b/internal/notifier/matrix_test.go @@ -1,16 +1,9 @@ package notifier import ( - "context" - "crypto/x509" - "fmt" - "io" - "net/http" - "net/http/httptest" "testing" "time" - fuzz "github.com/AdaLogics/go-fuzz-headers" eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -55,34 +48,3 @@ 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 := eventv1.Event{} - _ = fuzz.NewConsumer(seed).GenerateStruct(&event) - - event.Severity = severity - - _ = matrix.Post(context.TODO(), event) - }) -} diff --git a/internal/notifier/opsgenie_fuzz_test.go b/internal/notifier/opsgenie_fuzz_test.go new file mode 100644 index 000000000..90b3e326c --- /dev/null +++ b/internal/notifier/opsgenie_fuzz_test.go @@ -0,0 +1,65 @@ +/* +Copyright 2020 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" + "crypto/x509" + "fmt" + "io" + "net/http" + "net/http/httptest" + "testing" + + fuzz "github.com/AdaLogics/go-fuzz-headers" + eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1" +) + +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 := eventv1.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/opsgenie_test.go b/internal/notifier/opsgenie_test.go index ad91d8ec3..9e5690f91 100644 --- a/internal/notifier/opsgenie_test.go +++ b/internal/notifier/opsgenie_test.go @@ -18,16 +18,12 @@ package notifier import ( "context" - "crypto/x509" "encoding/json" - "fmt" "io" "net/http" "net/http/httptest" "testing" - fuzz "github.com/AdaLogics/go-fuzz-headers" - eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1" "github.com/stretchr/testify/require" ) @@ -48,38 +44,3 @@ 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 := eventv1.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_fuzz_test.go b/internal/notifier/rocket_fuzz_test.go new file mode 100644 index 000000000..d1dde76f3 --- /dev/null +++ b/internal/notifier/rocket_fuzz_test.go @@ -0,0 +1,66 @@ +/* +Copyright 2020 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" + "crypto/x509" + "fmt" + "io" + "net/http" + "net/http/httptest" + "testing" + + fuzz "github.com/AdaLogics/go-fuzz-headers" + eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1" +) + +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 := eventv1.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/rocket_test.go b/internal/notifier/rocket_test.go index 42e547210..bfddcfc36 100644 --- a/internal/notifier/rocket_test.go +++ b/internal/notifier/rocket_test.go @@ -18,16 +18,12 @@ package notifier import ( "context" - "crypto/x509" "encoding/json" - "fmt" "io" "net/http" "net/http/httptest" "testing" - fuzz "github.com/AdaLogics/go-fuzz-headers" - eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1" "github.com/stretchr/testify/require" ) @@ -50,39 +46,3 @@ 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 := eventv1.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_fuzz_test.go b/internal/notifier/slack_fuzz_test.go new file mode 100644 index 000000000..aa263048d --- /dev/null +++ b/internal/notifier/slack_fuzz_test.go @@ -0,0 +1,67 @@ +/* +Copyright 2020 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" + "crypto/x509" + "fmt" + "io" + "net/http" + "net/http/httptest" + "testing" + + fuzz "github.com/AdaLogics/go-fuzz-headers" + eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1" +) + +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 := eventv1.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/slack_test.go b/internal/notifier/slack_test.go index 1f0a11514..e9bcc5a72 100644 --- a/internal/notifier/slack_test.go +++ b/internal/notifier/slack_test.go @@ -18,16 +18,12 @@ package notifier import ( "context" - "crypto/x509" "encoding/json" - "fmt" "io" "net/http" "net/http/httptest" "testing" - fuzz "github.com/AdaLogics/go-fuzz-headers" - eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1" "github.com/stretchr/testify/require" ) @@ -60,40 +56,3 @@ 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 := eventv1.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_fuzz_test.go b/internal/notifier/teams_fuzz_test.go new file mode 100644 index 000000000..4f364be45 --- /dev/null +++ b/internal/notifier/teams_fuzz_test.go @@ -0,0 +1,67 @@ +/* +Copyright 2020 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" + "crypto/x509" + "fmt" + "io" + "net/http" + "net/http/httptest" + "testing" + + fuzz "github.com/AdaLogics/go-fuzz-headers" + eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1" +) + +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 := eventv1.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/internal/notifier/teams_test.go b/internal/notifier/teams_test.go index 3af262a6a..2d5111db8 100644 --- a/internal/notifier/teams_test.go +++ b/internal/notifier/teams_test.go @@ -18,16 +18,12 @@ package notifier import ( "context" - "crypto/x509" "encoding/json" - "fmt" "io" "net/http" "net/http/httptest" "testing" - fuzz "github.com/AdaLogics/go-fuzz-headers" - eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1" "github.com/stretchr/testify/require" ) @@ -50,40 +46,3 @@ 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 := eventv1.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/internal/notifier/telegram_test.go b/internal/notifier/telegram_fuzz_test.go similarity index 100% rename from internal/notifier/telegram_test.go rename to internal/notifier/telegram_fuzz_test.go diff --git a/internal/notifier/util_fuzz_test.go b/internal/notifier/util_fuzz_test.go new file mode 100644 index 000000000..b62f7af42 --- /dev/null +++ b/internal/notifier/util_fuzz_test.go @@ -0,0 +1,45 @@ +/* +Copyright 2020 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 ( + "testing" + + fuzz "github.com/AdaLogics/go-fuzz-headers" + eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1" +) + +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 := eventv1.Event{} + _ = fuzz.NewConsumer(seed).GenerateStruct(&event) + + event.Reason = reason + + _, _ = formatNameAndDescription(event) + }) +} diff --git a/internal/notifier/util_test.go b/internal/notifier/util_test.go index 3c52f6fb2..062000e89 100644 --- a/internal/notifier/util_test.go +++ b/internal/notifier/util_test.go @@ -19,7 +19,6 @@ package notifier import ( "testing" - fuzz "github.com/AdaLogics/go-fuzz-headers" eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1" "github.com/stretchr/testify/require" v1 "k8s.io/api/core/v1" @@ -124,24 +123,3 @@ 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 := eventv1.Event{} - _ = fuzz.NewConsumer(seed).GenerateStruct(&event) - - event.Reason = reason - - _, _ = formatNameAndDescription(event) - }) -} diff --git a/internal/notifier/webex_fuzz_test.go b/internal/notifier/webex_fuzz_test.go new file mode 100644 index 000000000..ed48492b9 --- /dev/null +++ b/internal/notifier/webex_fuzz_test.go @@ -0,0 +1,66 @@ +/* +Copyright 2020 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" + "crypto/x509" + "fmt" + "io" + "net/http" + "net/http/httptest" + "testing" + + fuzz "github.com/AdaLogics/go-fuzz-headers" + eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1" +) + +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 := eventv1.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/internal/notifier/webex_test.go b/internal/notifier/webex_test.go index ee98e5e73..e8991e2d5 100644 --- a/internal/notifier/webex_test.go +++ b/internal/notifier/webex_test.go @@ -18,16 +18,12 @@ package notifier import ( "context" - "crypto/x509" "encoding/json" - "fmt" "io" "net/http" "net/http/httptest" "testing" - fuzz "github.com/AdaLogics/go-fuzz-headers" - eventv1 "github.com/fluxcd/pkg/apis/event/v1beta1" "github.com/stretchr/testify/require" ) @@ -58,39 +54,3 @@ 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 := eventv1.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/README.md b/tests/fuzz/README.md new file mode 100644 index 000000000..98e1d265d --- /dev/null +++ b/tests/fuzz/README.md @@ -0,0 +1,82 @@ +# fuzz testing + +Flux is part of Google's [oss fuzz] program which provides continuous fuzzing for +open source projects. + +The long running fuzzing execution is configured in the [oss-fuzz repository]. +Shorter executions are done on a per-PR basis, configured as a [github workflow]. + +### Testing locally + +Build fuzzers: + +```bash +make fuzz-build +``` +All fuzzers will be built into `./build/fuzz/out`. + +Smoke test fuzzers: + +All the fuzzers will be built and executed once, to ensure they are fully functional. + +```bash +make fuzz-smoketest +``` + +Run fuzzer locally: +```bash +./build/fuzz/out/fuzz_conditions_match +``` + +Run fuzzer inside a container: + +```bash + docker run --rm -ti \ + -v "$(pwd)/build/fuzz/out":/out \ + gcr.io/oss-fuzz/fluxcd \ + /out/fuzz_conditions_match +``` + +### Caveats of creating oss-fuzz compatible tests + +#### Segregate fuzz tests + +OSS-Fuzz does not properly support mixed `*_test.go` files, in which there is a combination +of fuzz and non-fuzz tests. To mitigate this problem, ensure your fuzz tests are not in the +same file as other Go tests. As a pattern, call your fuzz test files `*_fuzz_test.go`. + +#### Build tags to avoid conflicts when running Go tests + +Due to the issue above, code duplication will occur when creating fuzz tests that rely on +helper functions that are shared with other tests. To avoid build issues, add a conditional +build tag at the top of the `*_fuzz_test.go` file: +```go +//go:build gofuzz_libfuzzer +// +build gofuzz_libfuzzer +``` + +The build tag above is set at [go-118-fuzz-build]. +At this point in time we can't pass on specific tags from [compile_native_go_fuzzer]. + +### Running oss-fuzz locally + +The `make fuzz-smoketest` is meant to be an easy way to reproduce errors that may occur +upstream. If our checks ever run out of sync with upstream, the upstream tests can be +executed locally with: + +``` +git clone --depth 1 https://github.com/google/oss-fuzz +cd oss-fuzz +python infra/helper.py build_image fluxcd +python infra/helper.py build_fuzzers --sanitizer address --architecture x86_64 fluxcd +python infra/helper.py check_build --sanitizer address --architecture x86_64 fluxcd +``` + +For latest info on testing oss-fuzz locally, refer to the [upstream guide]. + +[oss fuzz]: https://github.com/google/oss-fuzz +[oss-fuzz repository]: https://github.com/google/oss-fuzz/tree/master/projects/fluxcd +[github workflow]: .github/workflows/cifuzz.yaml +[upstream guide]: https://google.github.io/oss-fuzz/getting-started/new-project-guide/#testing-locally +[go-118-fuzz-build]: https://github.com/AdamKorcz/go-118-fuzz-build/blob/b2031950a318d4f2dcf3ec3e128f904d5cf84623/main.go#L40 +[compile_native_go_fuzzer]: https://github.com/google/oss-fuzz/blob/c2d827cb78529fdc757c9b0b4fea0f1238a54814/infra/base-images/base-builder/compile_native_go_fuzzer#L32 \ No newline at end of file diff --git a/tests/fuzz/oss_fuzz_build.sh b/tests/fuzz/oss_fuzz_build.sh index 247d9b1fb..88a121f94 100755 --- a/tests/fuzz/oss_fuzz_build.sh +++ b/tests/fuzz/oss_fuzz_build.sh @@ -16,64 +16,66 @@ set -euxo pipefail +# This file aims for: +# - Dynamically discover and build all fuzz tests within the repository. +# - Work for both local make fuzz-smoketest and the upstream oss-fuzz. + GOPATH="${GOPATH:-/root/go}" GO_SRC="${GOPATH}/src" PROJECT_PATH="github.com/fluxcd/notification-controller" -TMP_DIR=$(mktemp -d /tmp/oss_fuzz-XXXXXX) - -cleanup(){ - rm -rf "${TMP_DIR}" -} -trap cleanup EXIT +# install_deps installs all dependencies needed for upstream oss-fuzz. +# Unfortunately we can't pin versions here, as we want to always +# have the latest, so that we can reproduce errors occuring upstream. 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 + if ! command -v go-118-fuzz-build &> /dev/null; then + go install github.com/AdamKorcz/go-118-fuzz-build@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 +# Ensure any project-specific requirements are catered for ahead of +# the generic build process. +if [ -f "tests/fuzz/oss_fuzz_prebuild.sh" ]; then + tests/fuzz/oss_fuzz_prebuild.sh +fi -# 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}" +modules=$(find . -mindepth 1 -maxdepth 4 -type f -name 'go.mod' | cut -c 3- | sed 's|/[^/]*$$||' | sort -u | sed 's;/go.mod;;g' | sed 's;go.mod;.;g') - 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}") +for module in ${modules}; do + + cd "${GO_SRC}/${PROJECT_PATH}/${module}" + + # TODO: stop ignoring recorder_fuzzer_test.go. Temporary fix for fuzzing building issues. + test_files=$(grep -r --include='**_test.go' --files-with-matches 'func Fuzz' . || echo "") + if [ -z "${test_files}" ]; then + continue + fi - echo "Building ${file}.${target_name} into ${fuzzer_name}" - compile_native_go_fuzzer "${target_dir}" "${target_name}" "${fuzzer_name}" + go get github.com/AdamKorcz/go-118-fuzz-build/testing + + # Iterate through all Go Fuzz targets, compiling each into a fuzzer. + for file in ${test_files}; do + # If the subdir is a module, skip this file, as it will be handled + # at the next iteration of the outer loop. + if [ -f "$(dirname "${file}")/go.mod" ]; then + continue + fi + + targets=$(grep -oP 'func \K(Fuzz\w*)' "${file}") + for target_name in ${targets}; do + # Transform module path into module name (e.g. git/libgit2 to git_libgit2). + module_name=$(echo ${module} | tr / _) + # Compose fuzzer name based on the lowercase version of the func names. + # The module name is added after the fuzz prefix, for better discoverability. + fuzzer_name=$(echo "${target_name}" | tr '[:upper:]' '[:lower:]' | sed "s;fuzz_;fuzz_${module_name}_;g") + target_dir=$(dirname "${file}") + + echo "Building ${file}.${target_name} into ${fuzzer_name}" + compile_native_go_fuzzer "${target_dir}" "${target_name}" "${fuzzer_name}" + done done done