package installer import ( "archive/tar" "bytes" "context" "os" "path/filepath" "testing" "github.com/stretchr/testify/require" ) func TestTarPreflightRejectsOversizedHeaderBeforeReadingPayload(t *testing.T) { var archive bytes.Buffer writer := tar.NewWriter(&archive) require.NoError(t, writer.WriteHeader(&tar.Header{Name: "huge", Typeflag: tar.TypeReg, Mode: 0600, Size: int64(maxSevenZipExpandedSize + 1)})) // No payload follows the advertised huge file. Reading/skipping it would // return unexpected EOF; preflight must reject its budget first. _, err := preflightTar(context.Background(), tar.NewReader(bytes.NewReader(archive.Bytes()))) require.ErrorContains(t, err, "expands beyond") } type cancelAfterFirstRead struct { reader *bytes.Reader cancel context.CancelFunc } func (r *cancelAfterFirstRead) Read(p []byte) (int, error) { n, err := r.reader.Read(p) r.cancel() return n, err } func TestTarPreflightObservesCancellationWhileSkippingBody(t *testing.T) { data := makeTarBytes(t, map[string][]byte{"payload": bytes.Repeat([]byte("x"), 128<<10)}) ctx, cancel := context.WithCancel(context.Background()) defer cancel() reader := contextReader{ctx: ctx, reader: &cancelAfterFirstRead{reader: bytes.NewReader(data), cancel: cancel}} _, err := preflightTar(ctx, tar.NewReader(reader)) require.ErrorIs(t, err, context.Canceled) } func TestCancelledTarExtractionAndHashLeaveDestinationUntouched(t *testing.T) { root := t.TempDir() source := filepath.Join(root, "broken.tar.gz") destination := filepath.Join(root, "live") require.NoError(t, os.WriteFile(source, []byte("malformed gzip"), 0600)) require.NoError(t, os.Mkdir(destination, 0700)) sentinel := filepath.Join(destination, "original") require.NoError(t, os.WriteFile(sentinel, []byte("keep"), 0600)) ctx, cancel := context.WithCancel(context.Background()) cancel() inst := NewInstaller(ReleaseInfo{}, "") require.ErrorIs(t, inst.extractArchive(ctx, source, destination, nil), context.Canceled) require.ErrorIs(t, verifyBLAKE3File(ctx, source, blake3HashOf([]byte("malformed gzip"))), context.Canceled) assertTransactionFile(t, sentinel, "keep") entries, err := os.ReadDir(root) require.NoError(t, err) require.Len(t, entries, 2) }