diff --git a/pkg/bundle/builder_test.go b/pkg/bundle/builder_test.go index 01e3f5bf1b..62160a5baf 100644 --- a/pkg/bundle/builder_test.go +++ b/pkg/bundle/builder_test.go @@ -131,14 +131,13 @@ func TestBuildTektonBundle(t *testing.T) { // If the user bundled this up as a tar file then we need to untar it. treader := tar.NewReader(rc) - header, err := treader.Next() - if err != nil { + if _, err := treader.Next(); err != nil { t.Errorf("layer is not a tarball") } - contents := make([]byte, header.Size) - if _, err := treader.Read(contents); err != nil && err != io.EOF { - // We only allow 1 resource per layer so this tar bundle should have one and only one file. + limited := io.LimitReader(treader, MaxLayerSize+1) + contents, err := io.ReadAll(limited) + if err != nil && err != io.EOF { t.Errorf("failed to read tar bundle: %v", err) } diff --git a/pkg/bundle/reader.go b/pkg/bundle/reader.go index 3c7672537e..c966cfad83 100644 --- a/pkg/bundle/reader.go +++ b/pkg/bundle/reader.go @@ -15,6 +15,9 @@ import ( // Tekton bundle. The `version`, `kind`, and `name` fields map 1:1 with the same named fields in the Tekton Bundle spec. type ObjectVisitor func(version, kind, name string, element runtime.Object, raw []byte) +// setting a limit reader to avoid reading more than 1.5 MiB from the layer +const MaxLayerSize = 15 << 20 / 10 + // List will call visitor for every single layer in the img. func List(img v1.Image, visitor ObjectVisitor) error { manifest, err := img.Manifest() @@ -110,15 +113,16 @@ func readTarLayer(l v1.Layer) ([]byte, error) { // If the user bundled this up as a tar file then we need to untar it. treader := tar.NewReader(rc) - header, err := treader.Next() - if err != nil { + if _, err := treader.Next(); err != nil { return nil, fmt.Errorf("layer is not a tarball") } - - contents := make([]byte, header.Size) - if _, err := io.ReadFull(treader, contents); err != nil && err != io.EOF { - // We only allow 1 resource per layer so this tar bundle should have one and only one file. + limited := io.LimitReader(treader, MaxLayerSize+1) + contents, err := io.ReadAll(limited) + if err != nil { return nil, fmt.Errorf("failed to read tar bundle: %w", err) } + if int64(len(contents)) > MaxLayerSize { + return nil, fmt.Errorf("layer exceeds maximum allowed size of %d bytes", MaxLayerSize) + } return contents, nil } diff --git a/pkg/bundle/reader_test.go b/pkg/bundle/reader_test.go index 39533b992a..7a54df8a4e 100644 --- a/pkg/bundle/reader_test.go +++ b/pkg/bundle/reader_test.go @@ -4,6 +4,7 @@ import ( "archive/tar" "bytes" "fmt" + "io" "strings" "testing" @@ -125,3 +126,33 @@ func TestReader(t *testing.T) { t.Error(err) } } + +func TestReadTarLayerExceedsMaxSize(t *testing.T) { + oversized := make([]byte, MaxLayerSize+1) + + var buf bytes.Buffer + tw := tar.NewWriter(&buf) + _ = tw.WriteHeader(&tar.Header{ + Name: "oversizedtask.yaml", + Size: int64(len(oversized)), + }) + _, _ = tw.Write(oversized) + _ = tw.Close() + + // Wrap as a fake v1.Layer via tarball.FromOpener + opener := func() (io.ReadCloser, error) { + return io.NopCloser(bytes.NewReader(buf.Bytes())), nil + } + layer, err := tarball.LayerFromOpener(opener) + if err != nil { + t.Fatalf("failed to create layer: %v", err) + } + + _, err = readTarLayer(layer) + if err == nil { + t.Fatal("expected error for oversized layer, got nil") + } + if !strings.Contains(err.Error(), "layer exceeds maximum allowed size") { + t.Errorf("unexpected error message: %v", err) + } +} diff --git a/pkg/cmd/bundle/push_test.go b/pkg/cmd/bundle/push_test.go index aa3179b4e3..80966a1dad 100644 --- a/pkg/cmd/bundle/push_test.go +++ b/pkg/cmd/bundle/push_test.go @@ -245,15 +245,13 @@ func readTarLayer(t *testing.T, layer v1.Layer) string { // If the user bundled this up as a tar file then we need to untar it. treader := tar.NewReader(rc) - header, err := treader.Next() - if err != nil { + if _, err := treader.Next(); err != nil { t.Errorf("layer is not a tarball") - } - contents := make([]byte, header.Size) - if _, err := treader.Read(contents); err != nil && err != io.EOF { - // We only allow 1 resource per layer so this tar bundle should have one and only one file. + limited := io.LimitReader(treader, bundle.MaxLayerSize+1) + contents, err := io.ReadAll(limited) + if err != nil && err != io.EOF { t.Errorf("failed to read tar bundle: %v", err) } return string(contents)