Skip to content

Commit

Permalink
Merge pull request #308 from pjbgf/go-native
Browse files Browse the repository at this point in the history
fuzz: Refactor Fuzzers based on Go native fuzzing
  • Loading branch information
Paulo Gomes authored Sep 8, 2022
2 parents 9333ee6 + a0320fa commit 8cd9d54
Show file tree
Hide file tree
Showing 8 changed files with 230 additions and 117 deletions.
14 changes: 12 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ BUILD_ARGS ?=
# Architectures to build images for.
BUILD_PLATFORMS ?= linux/amd64

# FUZZ_TIME defines the max amount of time, in Go Duration,
# each fuzzer should run for.
FUZZ_TIME ?= 1m

# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
ifeq (,$(shell go env GOBIN))
GOBIN=$(shell go env GOPATH)/bin
Expand Down Expand Up @@ -134,7 +138,7 @@ rm -rf $$TMP_DIR ;\
}
endef

# Build fuzzers
# Build fuzzers used by oss-fuzz.
fuzz-build:
rm -rf $(shell pwd)/build/fuzz/
mkdir -p $(shell pwd)/build/fuzz/out/
Expand All @@ -148,11 +152,17 @@ 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 \
-v "$(shell pwd)/tests/fuzz/oss_fuzz_run.sh":/runner.sh \
-e ENVTEST_KUBERNETES_VERSION="$(ENVTEST_KUBERNETES_VERSION)" \
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
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/

package fuzz
package controllers

import (
"context"
Expand All @@ -30,6 +30,7 @@ import (
"os/exec"
"path/filepath"
"sync"
"testing"
"time"

"go.uber.org/zap/zapcore"
Expand All @@ -48,7 +49,6 @@ import (
fuzz "github.com/AdaLogics/go-fuzz-headers"

imagev1 "github.com/fluxcd/image-reflector-controller/api/v1beta1"
"github.com/fluxcd/image-reflector-controller/controllers"
"github.com/fluxcd/image-reflector-controller/internal/database"
"github.com/fluxcd/image-reflector-controller/internal/test"
)
Expand All @@ -58,25 +58,94 @@ var cfg *rest.Config
var k8sClient client.Client
var k8sMgr ctrl.Manager
var stopManager func()
var imageRepoReconciler *controllers.ImageRepositoryReconciler
var imagePolicyReconciler *controllers.ImagePolicyReconciler
var imageRepoReconciler *ImageRepositoryReconciler
var imagePolicyReconciler *ImagePolicyReconciler
var testEnv *envtest.Environment
var badgerDir string
var badgerDB *badger.DB
var initter sync.Once

const defaultBinVersion = "1.23"

func envtestBinVersion() string {
if binVersion := os.Getenv("ENVTEST_KUBERNETES_VERSION"); binVersion != "" {
return binVersion
}
return defaultBinVersion
}
const defaultBinVersion = "1.24"

//go:embed testdata/crd/*.yaml
var testFiles embed.FS

// Fuzz implements a fuzzer that creates pseudo-random objects
// for the ImageRepositoryController to reconcile.
func Fuzz_ImageRepositoryController(f *testing.F) {
f.Fuzz(func(t *testing.T, seed []byte) {

initter.Do(initFunc)
registryServer = test.NewRegistryServer()
defer registryServer.Close()
fc := fuzz.NewConsumer(seed)

imgRepo := test.RegistryName(registryServer)
repo := imagev1.ImageRepository{}
err := fc.GenerateStruct(&repo)
if err != nil {
return
}
repo.Spec.Image = imgRepo

objectName, err := fc.GetStringFrom("abcdefghijklmnopqrstuvwxyz123456789", 59)
if err != nil {
return
}
imageObjectName := types.NamespacedName{
Name: objectName,
Namespace: "default",
}
repo.Name = imageObjectName.Name
repo.Namespace = imageObjectName.Namespace

ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*200)
defer cancel()

r := imageRepoReconciler
if r == nil {
return
}
err = r.Create(ctx, &repo)
if err != nil {
return
}
time.Sleep(30 * time.Millisecond)
err = r.Get(ctx, imageObjectName, &repo)
if err != nil || repo.Status.LastScanResult != nil {
return
}

polNs, err := fc.GetStringFrom("abcdefghijklmnopqrstuvwxyz123456789", 59)
if err != nil {
return
}
polName := types.NamespacedName{
Name: polNs,
Namespace: imageObjectName.Namespace,
}
pol := imagev1.ImagePolicy{}
err = fc.GenerateStruct(&pol)
if err != nil {
return
}
pol.Spec.ImageRepositoryRef.Name = imageObjectName.Name

pol.Namespace = polName.Namespace
pol.Name = polName.Name

ctx, cancel = context.WithTimeout(context.Background(), time.Millisecond*200)
defer cancel()

err = r.Create(ctx, &pol)
if err != nil {
return
}
time.Sleep(time.Millisecond * 30)
r.Get(ctx, polName, &pol)
})
}

// ensureDependencies ensure that:
// a) setup-envtest is installed and a specific version of envtest is deployed.
// b) the embedded crd files are exported onto the "runner container".
Expand Down Expand Up @@ -178,22 +247,22 @@ func initFunc() {
panic(err)
}

imageRepoReconciler = &controllers.ImageRepositoryReconciler{
imageRepoReconciler = &ImageRepositoryReconciler{
Client: k8sMgr.GetClient(),
Scheme: scheme.Scheme,
Database: database.NewBadgerDatabase(badgerDB),
}
err = imageRepoReconciler.SetupWithManager(k8sMgr, controllers.ImageRepositoryReconcilerOptions{})
err = imageRepoReconciler.SetupWithManager(k8sMgr, ImageRepositoryReconcilerOptions{})
if err != nil {
panic(err)
}

imagePolicyReconciler = &controllers.ImagePolicyReconciler{
imagePolicyReconciler = &ImagePolicyReconciler{
Client: k8sMgr.GetClient(),
Scheme: scheme.Scheme,
Database: database.NewBadgerDatabase(badgerDB),
}
err = imagePolicyReconciler.SetupWithManager(k8sMgr, controllers.ImagePolicyReconcilerOptions{})
err = imagePolicyReconciler.SetupWithManager(k8sMgr, ImagePolicyReconcilerOptions{})
if err != nil {
panic(err)
}
Expand All @@ -213,79 +282,9 @@ func initFunc() {
}
}

// Fuzz implements a fuzzer that creates pseudo-random objects
// for the ImageRepositoryController to reconcile.
func FuzzImageRepositoryController(data []byte) int {
initter.Do(initFunc)
registryServer = test.NewRegistryServer()
defer registryServer.Close()
f := fuzz.NewConsumer(data)

imgRepo := test.RegistryName(registryServer)
repo := imagev1.ImageRepository{}
err := f.GenerateStruct(&repo)
if err != nil {
return 0
}
repo.Spec.Image = imgRepo

objectName, err := f.GetStringFrom("abcdefghijklmnopqrstuvwxyz123456789", 59)
if err != nil {
return 0
}
imageObjectName := types.NamespacedName{
Name: objectName,
Namespace: "default",
}
repo.Name = imageObjectName.Name
repo.Namespace = imageObjectName.Namespace

ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*200)
defer cancel()

r := imageRepoReconciler
if r == nil {
return 0
}
err = r.Create(ctx, &repo)
if err != nil {
return 0
}
time.Sleep(30 * time.Millisecond)
err = r.Get(ctx, imageObjectName, &repo)
if err != nil || repo.Status.LastScanResult != nil {
return 0
}

polNs, err := f.GetStringFrom("abcdefghijklmnopqrstuvwxyz123456789", 59)
if err != nil {
return 0
}
polName := types.NamespacedName{
Name: polNs,
Namespace: imageObjectName.Namespace,
}
pol := imagev1.ImagePolicy{}
err = f.GenerateStruct(&pol)
if err != nil {
return 0
}
pol.Spec.ImageRepositoryRef.Name = imageObjectName.Name

pol.Namespace = polName.Namespace
pol.Name = polName.Name

ctx, cancel = context.WithTimeout(context.Background(), time.Millisecond*200)
defer cancel()

err = r.Create(ctx, &pol)
if err != nil {
return 0
}
time.Sleep(time.Millisecond * 30)
err = r.Get(ctx, polName, &pol)
if err != nil {
return 0
func envtestBinVersion() string {
if binVersion := os.Getenv("ENVTEST_KUBERNETES_VERSION"); binVersion != "" {
return binVersion
}
return 1
return defaultBinVersion
}
15 changes: 15 additions & 0 deletions controllers/imagerepository_controller_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package controllers

import "testing"

func Fuzz_imagerepository_getURLHost(f *testing.F) {
f.Add("http://test")
f.Add("http://")
f.Add("http:///")
f.Add("test")
f.Add(" ")

f.Fuzz(func(t *testing.T, url string) {
_, _ = getURLHost(url)
})
}
7 changes: 6 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.18
replace github.com/fluxcd/image-reflector-controller/api => ./api

require (
github.com/AdaLogics/go-fuzz-headers v0.0.0-20220903154154-e8044f6e4c72
github.com/Masterminds/semver/v3 v3.1.1
github.com/dgraph-io/badger/v3 v3.2103.2
github.com/fluxcd/image-reflector-controller/api v0.20.1
Expand All @@ -15,8 +16,10 @@ require (
github.com/fluxcd/pkg/version v0.2.0
github.com/google/go-containerregistry v0.11.0
github.com/google/go-containerregistry/pkg/authn/k8schain v0.0.0-20220824164412-87b3a7966622
github.com/onsi/ginkgo v1.16.5
github.com/onsi/gomega v1.20.0
github.com/spf13/pflag v1.0.5
go.uber.org/zap v1.23.0
k8s.io/api v0.25.0
k8s.io/apimachinery v0.25.0
k8s.io/client-go v0.25.0
Expand Down Expand Up @@ -64,6 +67,7 @@ require (
github.com/cespare/xxhash/v2 v2.1.2 // indirect
github.com/chrismellard/docker-credential-acr-env v0.0.0-20220327082430-c57b701bfc08 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.12.0 // indirect
github.com/cyphar/filepath-securejoin v0.2.3 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgraph-io/ristretto v0.1.0 // indirect
github.com/dimchansky/utfbom v1.1.1 // indirect
Expand Down Expand Up @@ -107,6 +111,7 @@ require (
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/nxadm/tail v1.4.8 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.0.3-0.20220729202839-6ad7100eb087 // indirect
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
Expand All @@ -120,7 +125,6 @@ require (
go.opencensus.io v0.23.0 // indirect
go.uber.org/atomic v1.7.0 // indirect
go.uber.org/multierr v1.6.0 // indirect
go.uber.org/zap v1.23.0 // indirect
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d // indirect
golang.org/x/net v0.0.0-20220722155237-a158d28d115b // indirect
golang.org/x/oauth2 v0.0.0-20220718184931-c8730f7fcb92 // indirect
Expand All @@ -133,6 +137,7 @@ require (
google.golang.org/appengine v1.6.7 // indirect
google.golang.org/protobuf v1.28.1 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/apiextensions-apiserver v0.24.2 // indirect
Expand Down
Loading

0 comments on commit 8cd9d54

Please sign in to comment.