Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion cli/context/store/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -474,11 +474,18 @@ func importZip(name string, s Writer, reader io.Reader) error {
}
importedMetaFile = true
} else if strings.HasPrefix(zf.Name, "tls/") {
// Reject entries whose advertised uncompressed size exceeds
// the per-file cap without decompressing, to avoid allocating
// gigabytes for a zip bomb (see #6917).
if zf.UncompressedSize64 > uint64(maxAllowedFileSizeToImport) {
return invalidParameter(fmt.Errorf("%s: tls file exceeds maximum allowed size", zf.Name))
}
f, err := zf.Open()
if err != nil {
return err
}
data, err := io.ReadAll(f)
// Defense in depth in case the zip header is spoofed.
data, err := io.ReadAll(&limitedReader{R: f, N: maxAllowedFileSizeToImport})
defer f.Close()
if err != nil {
return err
Expand Down
35 changes: 35 additions & 0 deletions cli/context/store/store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,41 @@ func TestImportZip(t *testing.T) {
assert.NilError(t, err)
}

// TestImportZipTLSTooLarge verifies that a TLS entry whose uncompressed
// size exceeds the per-file limit is rejected instead of being read into
// memory unbounded (zip-bomb protection, see issue #6917).
func TestImportZipTLSTooLarge(t *testing.T) {
meta, err := json.Marshal(Metadata{
Endpoints: map[string]any{
"ep1": endpoint{Foo: "bar"},
},
Metadata: context{Bar: "baz"},
Name: "source",
})
assert.NilError(t, err)

buf := new(bytes.Buffer)
w := zip.NewWriter(buf)

mf, err := w.Create("meta.json")
assert.NilError(t, err)
_, err = mf.Write(meta)
assert.NilError(t, err)

tf, err := w.Create(path.Join("tls", "docker", "ca.pem"))
assert.NilError(t, err)
// Write well over the per-file cap; zeros compress to a tiny archive
// so the outer archive-size cap is not hit first.
oversized := make([]byte, 2*maxAllowedFileSizeToImport)
_, err = tf.Write(oversized)
assert.NilError(t, err)
assert.NilError(t, w.Close())

s := New(t.TempDir(), testCfg)
err = Import("zipBomb", s, bytes.NewReader(buf.Bytes()))
assert.ErrorContains(t, err, "tls file exceeds maximum allowed size")
}

func TestImportZipInvalid(t *testing.T) {
testDir := t.TempDir()
zf := path.Join(testDir, "test.zip")
Expand Down