diff --git a/pkg/fuzz/alpine_utils.go b/pkg/fuzz/alpine_utils.go new file mode 100644 index 000000000..384260a46 --- /dev/null +++ b/pkg/fuzz/alpine_utils.go @@ -0,0 +1,348 @@ +// +// Copyright 2023 The Sigstore 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 fuzz + +import ( + "archive/tar" + "bytes" + "compress/gzip" + "fmt" + "os" + "strings" + + fuzz "github.com/AdaLogics/go-fuzz-headers" + "github.com/sigstore/rekor/pkg/types" +) + +// Allows the fuzzer to create a .SIGN filename +func getSignFilename(ff *fuzz.ConsumeFuzzer) (string, error) { + keyName, err := ff.GetString() + if err != nil { + return "", err + } + var b strings.Builder + b.WriteString(".SIGN.RSA.") + b.WriteString(keyName) + b.WriteString(".rsa.pub") + return b.String(), nil +} + +// createPkgInfoFileContents creates a structured pkginfo file +// +// .PKGINFO files look like this: +// +// # Generated by abuild 3.9.0-r2 +// # using fakeroot version 1.25.3 +// # Wed Jul 6 19:09:49 UTC 2022 +// pkgname = busybox +// pkgver = 1.35.0-r18 +// pkgdesc = Size optimized toolbox of many common UNIX utilities +// url = https://busybox.net/ +// builddate = 1657134589 +// packager = Buildozer +// size = 958464 +// arch = x86_64 +// origin = busybox +// commit = 332d2fff53cd4537d415e15e55e8ceb6fe6eaedb +// maintainer = Sören Tempel +// provider_priority = 100 +// license = GPL-2.0-only +// replaces = busybox-initscripts +// provides = /bin/sh +// triggers = /bin /usr/bin /sbin /usr/sbin /lib/modules/* +// # automatically detected: +// provides = cmd:busybox=1.35.0-r18 +// provides = cmd:sh=1.35.0-r18 +// depend = so:libc.musl-x86_64.so.1 +// datahash = 7d3351ac6c3ebaf18182efb5390061f50d077ce5ade60a15909d91278f70ada7 +func createPkgInfoFileContents(ff *fuzz.ConsumeFuzzer) ([]byte, error) { + var b strings.Builder + noOfRows, err := ff.GetInt() + if err != nil { + return []byte(""), err + } + + // Comments at the top of the pkginfo file + header, err := ff.GetBytes() + if err != nil { + return []byte(""), err + } + b.Write(header) + + for i := 0; i < noOfRows; i++ { + key, err := ff.GetBytes() + if err != nil { + return []byte(""), err + } + value, err := ff.GetBytes() + if err != nil { + return []byte(""), err + } + b.Write(key) + b.Write([]byte(" = ")) + b.Write(value) + b.WriteString("\n") + } + return []byte(b.String()), nil +} + +// Adds a .SIGN file to tarBytes +func addSignFile(ff *fuzz.ConsumeFuzzer, tarFiles []*fuzz.TarFile) ([]*fuzz.TarFile, error) { + SIGNFileContents, err := ff.GetBytes() + if err != nil { + return tarFiles, err + } + + SIGNFileName, err := getSignFilename(ff) + if err != nil { + return tarFiles, err + } + signFile := &fuzz.TarFile{ + Body: SIGNFileContents, + Hdr: &tar.Header{ + Name: SIGNFileName, + Mode: 0644, + Size: int64(len(SIGNFileContents)), + Typeflag: tar.TypeReg, + Gid: 0, + Uid: 0, + }, + } + tarFiles = append(tarFiles, signFile) + + return tarFiles, nil +} + +// Allows the fuzzer to randomize whether a .SIGN file should +// be added to tarBytes +func shouldAddSignFile(ff *fuzz.ConsumeFuzzer, tarFiles []*fuzz.TarFile) bool { + shouldRequireSIGNFile, err := ff.GetBool() + if err != nil { + return false + } + if shouldRequireSIGNFile { + for _, tarFile := range tarFiles { + if strings.HasPrefix(tarFile.Hdr.Name, ".SIGN") { + return false + } + } + return true + } + return false +} + +// Allows the fuzzer to randomize whether a .PKGINFO file should +// be added to tarBytes +func shouldAddPkgInfoFile(ff *fuzz.ConsumeFuzzer, tarFiles []*fuzz.TarFile) bool { + shouldRequirePKGINFOFile, err := ff.GetBool() + if err != nil { + return false + } + if shouldRequirePKGINFOFile { + for _, tarFile := range tarFiles { + if strings.HasPrefix(tarFile.Hdr.Name, ".PKGINFO") { + return false + } + } + return true + } + return false +} + +// Adds the .PKGINFO file to the tar files +func addPkgInfoFile(ff *fuzz.ConsumeFuzzer, tarFiles []*fuzz.TarFile) ([]*fuzz.TarFile, error) { + tarFile := &fuzz.TarFile{} + PKGINFOFileContents, err := createPkgInfoFileContents(ff) + if err != nil { + return tarFiles, err + } + tarFile.Body = PKGINFOFileContents + tarFile.Hdr = &tar.Header{ + Name: ".PKGINFO", + Mode: 0644, + Size: int64(len(PKGINFOFileContents)), + Typeflag: tar.TypeReg, + Gid: 0, + Uid: 0, + } + + return tarFiles, nil +} + +func AlpineArtifactBytes(ff *fuzz.ConsumeFuzzer) ([]byte, error) { + var tarFiles, tarFiles2 []*fuzz.TarFile + var err error + + tarFiles, err = ff.TarFiles() + if err != nil { + return []byte(""), err + } + if shouldAddSignFile(ff, tarFiles) { + tarFiles, err = addSignFile(ff, tarFiles) + if err != nil { + return []byte(""), err + } + } + + tarFiles2, err = ff.TarFiles() + if err != nil { + return []byte(""), err + } + + if shouldAddPkgInfoFile(ff, tarFiles2) { + tarFiles2, err = addPkgInfoFile(ff, tarFiles2) + if err != nil { + return []byte(""), err + } + } + + return concatenateTarArchives(tarFiles, tarFiles2) +} + +func concatenateTarArchives(tarFiles1 []*fuzz.TarFile, tarFiles2 []*fuzz.TarFile) ([]byte, error) { + var buf1, buf2 bytes.Buffer + var err error + + tw1 := tar.NewWriter(&buf1) + for _, tf := range tarFiles1 { + err = tw1.WriteHeader(tf.Hdr) + if err != nil { + return []byte(""), err + } + err = tw1.Write(tf.Body) + if err != nil { + return []byte(""), err + } + } + tw1.Close() + tarBytes := buf1.Bytes() + + tw2 := tar.NewWriter(&buf2) + for _, tf := range tarFiles2 { + err = tw2.WriteHeader(tf.Hdr) + if err != nil { + return []byte(""), err + } + err = tw2.Write(tf.Body) + if err != nil { + return []byte(""), err + } + } + tw2.Close() + tarBytes2 := buf2.Bytes() + + var b1 bytes.Buffer + w1 := gzip.NewWriter(&b1) + defer w1.Close() + _, err := w1.Write(tarBytes) + if err != nil { + return []byte(""), err + } + w1.Close() + + var b2 bytes.Buffer + w2 := gzip.NewWriter(&b2) + defer w2.Close() + _, err = w2.Write(tarBytes2) + if err != nil { + return []byte(""), err + } + w2.Close() + concatenated := append(b1.Bytes(), b2.Bytes()...) + return concatenated, nil +} + +func setAlpineArtifactFields(ff *fuzz.ConsumeFuzzer, props *types.ArtifactProperties) (func(), error) { + cleanup := func() {} + + err := setArtifactHash(ff, props) + if err != nil { + return cleanup, err + } + + artifactBytes, err := AlpineArtifactBytes(ff) + if err != nil { + return cleanup, err + } + + shouldSetArtifactBytes, err := ff.GetBool() + if err != nil { + return cleanup, err + } + + if shouldSetArtifactBytes { + props.ArtifactBytes = artifactBytes + return func() { + // do nothing + }, nil + } + artifactFile, err := createAbsFile(ff, "ArtifactFile", artifactBytes) + cleanup = func() { + os.Remove("ArtifactFile") + } + props.ArtifactPath = artifactFile + return cleanup, err +} + +// Creates an ArtifactProperties with values determined by the fuzzer +func CreateAlpineProps(ff *fuzz.ConsumeFuzzer) (types.ArtifactProperties, func(), error) { + props := &types.ArtifactProperties{} + + cleanupArtifactFile, err := setAlpineArtifactFields(ff, props) + if err != nil { + return *props, cleanupArtifactFile, err + } + if props.ArtifactPath == nil && props.ArtifactBytes == nil { + return *props, cleanupArtifactFile, fmt.Errorf("ArtifactPath and ArtifactBytes cannot both be nil") + } + + err = setAdditionalAuthenticatedData(ff, props) + if err != nil { + return *props, cleanupArtifactFile, fmt.Errorf("Failed setting AdditionalAuthenticatedData") + } + + cleanupSignatureFile, err := setSignatureFields(ff, props) + if err != nil { + return *props, func() { + cleanupArtifactFile() + cleanupSignatureFile() + }, fmt.Errorf("failed setting signature fields: %v", err) + } + + cleanupPublicKeyFile, err := setPublicKeyFields(ff, props) + if err != nil { + return *props, func() { + cleanupArtifactFile() + cleanupSignatureFile() + cleanupPublicKeyFile() + }, fmt.Errorf("failed setting public key fields: %v", err) + } + + err = setPKIFormat(ff, props) + if err != nil { + return *props, func() { + cleanupArtifactFile() + cleanupSignatureFile() + cleanupPublicKeyFile() + }, fmt.Errorf("failed setting PKI Format: %v", err) + } + + return *props, func() { + cleanupArtifactFile() + cleanupSignatureFile() + cleanupPublicKeyFile() + }, nil +} diff --git a/pkg/types/alpine/fuzz_test.go b/pkg/types/alpine/fuzz_test.go index b211b3830..8417863c1 100644 --- a/pkg/types/alpine/fuzz_test.go +++ b/pkg/types/alpine/fuzz_test.go @@ -16,248 +16,23 @@ package alpine import ( - "archive/tar" - "bytes" - "compress/gzip" - "strings" "testing" fuzz "github.com/AdaLogics/go-fuzz-headers" + utils "github.com/sigstore/rekor/pkg/fuzz" ) -// Allows the fuzzer to create a .SIGN filename -func getSignFilename(ff *fuzz.ConsumeFuzzer) (string, error) { - keyName, err := ff.GetString() - if err != nil { - return "", err - } - var b strings.Builder - b.WriteString(".SIGN.RSA.") - b.WriteString(keyName) - b.WriteString(".rsa.pub") - return b.String(), nil -} - -// createPkgInfoFileContents creates a structured pkginfo file -// -// .PKGINFO files look like this: -// -// # Generated by abuild 3.9.0-r2 -// # using fakeroot version 1.25.3 -// # Wed Jul 6 19:09:49 UTC 2022 -// pkgname = busybox -// pkgver = 1.35.0-r18 -// pkgdesc = Size optimized toolbox of many common UNIX utilities -// url = https://busybox.net/ -// builddate = 1657134589 -// packager = Buildozer -// size = 958464 -// arch = x86_64 -// origin = busybox -// commit = 332d2fff53cd4537d415e15e55e8ceb6fe6eaedb -// maintainer = Sören Tempel -// provider_priority = 100 -// license = GPL-2.0-only -// replaces = busybox-initscripts -// provides = /bin/sh -// triggers = /bin /usr/bin /sbin /usr/sbin /lib/modules/* -// # automatically detected: -// provides = cmd:busybox=1.35.0-r18 -// provides = cmd:sh=1.35.0-r18 -// depend = so:libc.musl-x86_64.so.1 -// datahash = 7d3351ac6c3ebaf18182efb5390061f50d077ce5ade60a15909d91278f70ada7 -func createPkgInfoFileContents(ff *fuzz.ConsumeFuzzer) ([]byte, error) { - var b strings.Builder - noOfRows, err := ff.GetInt() - if err != nil { - return []byte(""), err - } - - // Comments at the top of the pkginfo file - header, err := ff.GetBytes() - if err != nil { - return []byte(""), err - } - b.Write(header) - - for i := 0; i < noOfRows; i++ { - key, err := ff.GetBytes() - if err != nil { - return []byte(""), err - } - value, err := ff.GetBytes() - if err != nil { - return []byte(""), err - } - b.Write(key) - b.Write([]byte(" = ")) - b.Write(value) - b.WriteString("\n") - } - return []byte(b.String()), nil -} - -// Adds a .SIGN file to tarBytes -func addSignFile(ff *fuzz.ConsumeFuzzer, tarFiles []*fuzz.TarFile) ([]*fuzz.TarFile, error) { - SIGNFileContents, err := ff.GetBytes() - if err != nil { - return tarFiles, err - } - - SIGNFileName, err := getSignFilename(ff) - if err != nil { - return tarFiles, err - } - signFile := &fuzz.TarFile{ - Body: SIGNFileContents, - Hdr: &tar.Header{ - Name: SIGNFileName, - Mode: 0644, - Size: int64(len(SIGNFileContents)), - Typeflag: tar.TypeReg, - Gid: 0, - Uid: 0, - }, - } - tarFiles = append(tarFiles, signFile) - - return tarFiles, nil -} - -// Allows the fuzzer to randomize whether a .SIGN file should -// be added to tarBytes -func shouldAddSignFile(ff *fuzz.ConsumeFuzzer, tarFiles []*fuzz.TarFile) bool { - shouldRequireSIGNFile, err := ff.GetBool() - if err != nil { - return false - } - if shouldRequireSIGNFile { - for _, tarFile := range tarFiles { - if strings.HasPrefix(tarFile.Hdr.Name, ".SIGN") { - return false - } - } - return true - } - return false -} - -// Allows the fuzzer to randomize whether a .PKGINFO file should -// be added to tarBytes -func shouldAddPkgInfoFile(ff *fuzz.ConsumeFuzzer, tarFiles []*fuzz.TarFile) bool { - shouldRequirePKGINFOFile, err := ff.GetBool() - if err != nil { - return false - } - if shouldRequirePKGINFOFile { - for _, tarFile := range tarFiles { - if strings.HasPrefix(tarFile.Hdr.Name, ".PKGINFO") { - return false - } - } - return true - } - return false -} - -// Adds the .PKGINFO file to the tar files -func addPkgInfoFile(ff *fuzz.ConsumeFuzzer, tarFiles []*fuzz.TarFile) ([]*fuzz.TarFile, error) { - tarFile := &fuzz.TarFile{} - PKGINFOFileContents, err := createPkgInfoFileContents(ff) - if err != nil { - return tarFiles, err - } - tarFile.Body = PKGINFOFileContents - tarFile.Hdr = &tar.Header{ - Name: ".PKGINFO", - Mode: 0644, - Size: int64(len(PKGINFOFileContents)), - Typeflag: tar.TypeReg, - Gid: 0, - Uid: 0, - } - - return tarFiles, nil -} - // FuzzPackageUnmarshal implements the fuzz test func FuzzPackageUnmarshal(f *testing.F) { f.Fuzz(func(t *testing.T, data []byte) { - var tarFiles, tarFiles2 []*fuzz.TarFile - var err error - ff := fuzz.NewConsumer(data) - - tarFiles, err = ff.TarFiles() - if err != nil { - return - } - if shouldAddSignFile(ff, tarFiles) { - tarFiles, err = addSignFile(ff, tarFiles) - if err != nil { - t.Skip() - } - } - - tarFiles2, err = ff.TarFiles() - if err != nil { - t.Skip() - } - - if shouldAddPkgInfoFile(ff, tarFiles2) { - tarFiles2, err = addPkgInfoFile(ff, tarFiles2) - if err != nil { - t.Skip() - } - } - - concatenated, err := concatenateTarArchives(tarFiles, tarFiles2) + artifactBytes, err := utils.AlpineArtifactBytes(ff) if err != nil { t.Skip() } p := &Package{} - p.Unmarshal(bytes.NewReader(concatenated)) + p.Unmarshal(bytes.NewReader(artifactBytes)) }) } - -func concatenateTarArchives(tarFiles1 []*fuzz.TarFile, tarFiles2 []*fuzz.TarFile) ([]byte, error) { - var buf1, buf2 bytes.Buffer - - tw1 := tar.NewWriter(&buf1) - for _, tf := range tarFiles1 { - tw1.WriteHeader(tf.Hdr) - tw1.Write(tf.Body) - } - tw1.Close() - tarBytes := buf1.Bytes() - - tw2 := tar.NewWriter(&buf2) - for _, tf := range tarFiles2 { - tw2.WriteHeader(tf.Hdr) - tw2.Write(tf.Body) - } - tw2.Close() - tarBytes2 := buf2.Bytes() - - var b1 bytes.Buffer - w1 := gzip.NewWriter(&b1) - defer w1.Close() - _, err := w1.Write(tarBytes) - if err != nil { - return []byte(""), err - } - w1.Close() - - var b2 bytes.Buffer - w2 := gzip.NewWriter(&b2) - defer w2.Close() - _, err = w2.Write(tarBytes2) - if err != nil { - return []byte(""), err - } - w2.Close() - concatenated := append(b1.Bytes(), b2.Bytes()...) - return concatenated, nil -} diff --git a/pkg/types/alpine/v0.0.1/fuzz_test.go b/pkg/types/alpine/v0.0.1/fuzz_test.go index 0ec29e04e..a1f3cf8a7 100644 --- a/pkg/types/alpine/v0.0.1/fuzz_test.go +++ b/pkg/types/alpine/v0.0.1/fuzz_test.go @@ -36,7 +36,7 @@ func FuzzAlpineCreateProposedEntry(f *testing.F) { ff := fuzz.NewConsumer(propsData) - props, cleanup, err := fuzzUtils.CreateProps(ff) + props, cleanup, err := fuzzUtils.CreateAlpineProps(ff) if err != nil { t.Skip() }