CVE-2023-30551 (Go, published 2023-05-08T16:15Z)
Description: value: Rekor is an open source software supply chain transparency log. Rekor prior to version 1.1.1 may crash due to out of memory (OOM) conditions caused by reading archive metadata files into memory without checking their sizes first. Verification of a JAR file submitted to Rekor can cause an out of memory crash if files within the META-INF directory of the JAR are sufficiently large. Parsing of an APK file submitted to Rekor can cause an out of memory crash if the .SIGN or .PKGINFO files within the APK are sufficiently large. The OOM crash has been patched in Rekor version 1.1.1. There are no known workarounds.
Severity: nan (CVSS v3 base score: 7.5)
Weakness: CWE-770 \u2014 Allocation of Resources Without Limits or Throttling
The product allocates a reusable resource or group of resources on behalf of an actor without imposing any restrictions on the size or number of resources that can be allocated, in violation of the intended security policy for that actor.
Repository: https://github.com/sigstore/rekor
Files changed:
cmd/rekor-server/app/root.go
pkg/types/alpine/apk.go
pkg/types/alpine/apk_test.go
pkg/types/jar/v0.0.1/entry.go
pkg/types/jar/v0.0.1/entry_test.go
Commit message: Merge pull request from GHSA-2h5h-59f5-c5x9

* check size of archive metadata files before read

Signed-off-by: Ceridwen Coghlan <cdriskill@google.com>

* fix your ints

Signed-off-by: Ceridwen Coghlan <cdriskill@google.com>

* unit tests for metadata size errors

Signed-off-by: Ceridwen Coghlan <cdriskill@google.com>

* update for other tests

Signed-off-by: Ceridwen Coghlan <cdriskill@google.com>

* quick checks and comments

Signed-off-by: Ceridwen Coghlan <cdriskill@google.com>

* checks + comments take 2

Signed-off-by: Ceridwen Coghlan <cdriskill@google.com>

---------

Signed-off-by: Ceridwen Coghlan <cdriskill@google.com>

Vulnerable code:

Fixed code:
	rootCmd.PersistentFlags().Uint64("max_jar_metadata_size", 1048576, "maximum permitted size for jar META-INF/ files, in bytes; set to 0 for unlimited")
	rootCmd.PersistentFlags().Uint64("max_apk_metadata_size", 1048576, "maximum permitted size for apk .SIGN and .PKGINFO files, in bytes; set to 0 for unlimited")
	"github.com/spf13/viper"
			if header.Size < 0 {
				return errors.New("negative header size for .SIGN file")
			}
			if uint64(header.Size) > viper.GetUint64("max_apk_metadata_size") && viper.GetUint64("max_apk_metadata_size") > 0 {
				return fmt.Errorf("uncompressed .SIGN file size %d exceeds max allowed size %d", header.Size, viper.GetUint64("max_apk_metadata_size"))
			}
			if header.Size < 0 {
				return errors.New("negative header size for .PKGINFO file")
			}
			if uint64(header.Size) > viper.GetUint64("max_apk_metadata_size") && viper.GetUint64("max_apk_metadata_size") > 0 {
				return fmt.Errorf("uncompressed .PKGINFO file size %d exceeds max allowed size %d", header.Size, viper.GetUint64("max_apk_metadata_size"))
			}
	"strings"
	"github.com/spf13/viper"


func TestAlpineMetadataSize(t *testing.T) {
	os.Setenv("MAX_APK_METADATA_SIZE", "10")
	viper.AutomaticEnv()

	inputArchive, err := os.Open("tests/test_alpine.apk")
	if err != nil {
		t.Fatalf("could not open archive %v", err)
	}

	p := Package{}
	err = p.Unmarshal(inputArchive)
	if err == nil {
		t.Fatal("expecting metadata too large err")
	}
	if !strings.Contains(err.Error(), "exceeds max allowed size 10") {
		t.Fatalf("unexpected error %v", err)
	}
}
	"github.com/spf13/viper"
	// Checking that uncompressed metadata files are within acceptable bounds before reading into memory.
	// Checks match those performed by the relic library in the jarutils.Verify method below. For example,
	// the META-INF/MANIFEST.MF is read into memory by the relic lib, but a META-INF/LICENSE file is not.
	for _, f := range zipReader.File {
		dir, name := path.Split(strings.ToUpper(f.Name))
		if dir != "META-INF/" || name == "" || strings.LastIndex(name, ".") < 0 {
			continue
		}
		if f.UncompressedSize64 > viper.GetUint64("max_jar_metadata_size") && viper.GetUint64("max_jar_metadata_size") > 0 {
			return nil, nil, types.ValidationError(
				fmt.Errorf("uncompressed jar metadata of size %d exceeds max allowed size %d", f.UncompressedSize64, viper.GetUint64("max_jar_metadata_size")))
		}
	}

	"strings"
	"github.com/spf13/viper"

func TestJarMetadataSize(t *testing.T) {
	type TestCase struct {
		caseDesc                  string
		entry                     V001Entry
		expectUnmarshalSuccess    bool
		expectCanonicalizeSuccess bool
		expectedVerifierSuccess   bool
	}

	jarBytes, _ := os.ReadFile("tests/test.jar")

	os.Setenv("MAX_JAR_METADATA_SIZE", "10")
	viper.AutomaticEnv()

	v := V001Entry{
		JARModel: models.JarV001Schema{
			Archive: &models.JarV001SchemaArchive{
				Content: strfmt.Base64(jarBytes),
			},
		},
	}

	r := models.Jar{
		APIVersion: swag.String(v.APIVersion()),
		Spec:       v.JARModel,
	}

	if err := v.Unmarshal(&r); err != nil {
		t.Errorf("unexpected unmarshal failure: %v", err)
	}

	_, err := v.Canonicalize(context.TODO())
	if err == nil {
		t.Fatal("expecting metadata too large err")
	}
	if !strings.Contains(err.Error(), "exceeds max allowed size 10") {
		t.Fatalf("unexpected error %v", err)
	}
}