From 6b88198009b241e6bd353581b851ba313cbea08e Mon Sep 17 00:00:00 2001 From: Brandt Keller Date: Fri, 14 Aug 2026 16:46:25 +0000 Subject: [PATCH 1/2] fix: replace mimetype with archives Signed-off-by: Brandt Keller --- go.mod | 2 +- src/pkg/packager/pull.go | 47 +++++++++++++++++++++-------------- src/pkg/packager/pull_test.go | 31 ++++++++++++++++++++++- 3 files changed, 60 insertions(+), 20 deletions(-) diff --git a/go.mod b/go.mod index 4dae203995..fe68949657 100644 --- a/go.mod +++ b/go.mod @@ -127,6 +127,7 @@ require ( github.com/extism/go-sdk v1.7.1 // indirect github.com/fluxcd/cli-utils v1.2.1 // indirect github.com/fxamacker/cbor/v2 v2.9.2 // indirect + github.com/gabriel-vasile/mimetype v1.4.13 // indirect github.com/go-chi/chi/v5 v5.3.0 // indirect github.com/go-jose/go-jose/v4 v4.1.4 // indirect github.com/go-logr/logr v1.4.4 // indirect @@ -414,7 +415,6 @@ require ( github.com/fluxcd/pkg/apis/acl v0.10.0 // indirect github.com/fsnotify/fsnotify v1.10.1 // indirect github.com/fvbommel/sortorder v1.1.0 // indirect - github.com/gabriel-vasile/mimetype v1.4.13 github.com/gdamore/encoding v1.0.1 // indirect github.com/github/go-spdx/v2 v2.7.0 // indirect github.com/glebarez/go-sqlite v1.22.0 // indirect diff --git a/src/pkg/packager/pull.go b/src/pkg/packager/pull.go index a2ec16350d..e31e6acfa6 100644 --- a/src/pkg/packager/pull.go +++ b/src/pkg/packager/pull.go @@ -23,7 +23,7 @@ import ( "github.com/defenseunicorns/pkg/helpers/v2" "github.com/defenseunicorns/pkg/oci" - "github.com/gabriel-vasile/mimetype" + "github.com/mholt/archives" ocispec "github.com/opencontainers/image-spec/specs-go/v1" "github.com/zarf-dev/zarf/src/api" @@ -206,13 +206,13 @@ func pullOCI(ctx context.Context, opts pullOCIOptions) (*layout.PackageLayout, e return pkgLayout, nil } -func pullHTTP(ctx context.Context, src, tarDir, shasum string, insecureTLSSkipVerify bool) (string, error) { +func pullHTTP(ctx context.Context, src, tarDir, shasum string, insecureTLSSkipVerify bool) (_ string, err error) { if shasum == "" { return "", errors.New("shasum cannot be empty") } tarPath := filepath.Join(tarDir, "data") - err := pullHTTPFile(ctx, src, tarPath, insecureTLSSkipVerify) + err = pullHTTPFile(ctx, src, tarPath, insecureTLSSkipVerify) if err != nil { return "", err } @@ -225,28 +225,39 @@ func pullHTTP(ctx context.Context, src, tarDir, shasum string, insecureTLSSkipVe return "", fmt.Errorf("shasum mismatch for file %s, expected %s but got %s", tarPath, shasum, received) } - mtype, err := mimetype.DetectFile(tarPath) + file, err := os.Open(tarPath) if err != nil { return "", err } + defer func() { + err = errors.Join(err, file.Close()) + }() + + format, _, err := archives.Identify(ctx, "data", file) + if errors.Is(err, archives.NoMatch) { + // A zstd filename lets archives identify streams that start with a skippable frame. + if _, err := file.Seek(0, io.SeekStart); err != nil { + return "", fmt.Errorf("unsupported archive format: %w", err) + } + format, _, err = archives.Identify(ctx, "data.zst", file) + } + if err != nil { + return "", fmt.Errorf("unsupported archive format: %w", err) + } newPath := filepath.Join(tarDir, "data.tar") + switch format.MediaType() { + case "application/x-tar": + case "application/zstd": + newPath += ".zst" + default: + return "", fmt.Errorf("unsupported archive format: %s", format.MediaType()) + } - if mtype.Is("application/x-tar") { - err = os.Rename(tarPath, newPath) - if err != nil { - return "", err - } - return newPath, nil - } else if mtype.Is("application/zstd") { - newPath = fmt.Sprintf("%s.zst", newPath) - err = os.Rename(tarPath, newPath) - if err != nil { - return "", err - } - return newPath, nil + if err := os.Rename(tarPath, newPath); err != nil { + return "", err } - return "", fmt.Errorf("unsupported file type: %s", mtype.Extension()) + return newPath, nil } func pullHTTPFile(ctx context.Context, src, tarPath string, insecureTLSSkipVerify bool) (err error) { diff --git a/src/pkg/packager/pull_test.go b/src/pkg/packager/pull_test.go index 5ab70c1886..db80c9b749 100644 --- a/src/pkg/packager/pull_test.go +++ b/src/pkg/packager/pull_test.go @@ -4,6 +4,8 @@ package packager import ( + "crypto/sha256" + "encoding/hex" "io" "net/http" "net/http/httptest" @@ -110,7 +112,34 @@ func TestPullUnsupported(t *testing.T) { SHASum: "6e9dccce07ba9d3c45b7c872fae863c5415d296fd5e2fb72a2583530aa750ccd", Architecture: "amd64", }) - require.EqualError(t, err, "unsupported file type: .txt", "unsupported file type: .txt") + require.ErrorContains(t, err, "unsupported archive format:") +} + +func TestPullZstdWithSkippableFrame(t *testing.T) { + t.Parallel() + + ctx := testutil.TestContext(t) + packageData, err := os.ReadFile(filepath.Join("testdata", "load-package", "compressed", "zarf-package-test-amd64-0.0.1.tar.zst")) + require.NoError(t, err) + + payload := append([]byte{0x50, 0x2a, 0x4d, 0x18, 0x00, 0x00, 0x00, 0x00}, packageData...) + shasum := sha256.Sum256(payload) + srv := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, _ *http.Request) { + //nolint:errcheck // ignore + rw.Write(payload) + })) + t.Cleanup(srv.Close) + + dir := t.TempDir() + pulledPath, err := Pull(ctx, srv.URL, dir, PullOptions{ + SHASum: hex.EncodeToString(shasum[:]), + Architecture: "amd64", + }) + require.NoError(t, err) + + expectedPath := filepath.Join(dir, "zarf-package-test-amd64-0.0.1.tar.zst") + require.Equal(t, expectedPath, pulledPath) + require.FileExists(t, pulledPath) } func TestSupportsFiltering(t *testing.T) { From 8351b12fc444765c50858b9f1e48282d189cc0df Mon Sep 17 00:00:00 2001 From: Brandt Keller Date: Fri, 14 Aug 2026 17:37:16 +0000 Subject: [PATCH 2/2] fix: windows semantics for file handling Signed-off-by: Brandt Keller --- src/pkg/packager/pull.go | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/pkg/packager/pull.go b/src/pkg/packager/pull.go index e31e6acfa6..a89b2ecc55 100644 --- a/src/pkg/packager/pull.go +++ b/src/pkg/packager/pull.go @@ -230,7 +230,9 @@ func pullHTTP(ctx context.Context, src, tarDir, shasum string, insecureTLSSkipVe return "", err } defer func() { - err = errors.Join(err, file.Close()) + if file != nil { + err = errors.Join(err, file.Close()) + } }() format, _, err := archives.Identify(ctx, "data", file) @@ -254,6 +256,11 @@ func pullHTTP(ctx context.Context, src, tarDir, shasum string, insecureTLSSkipVe return "", fmt.Errorf("unsupported archive format: %s", format.MediaType()) } + if closeErr := file.Close(); closeErr != nil { + return "", closeErr + } + file = nil + if err := os.Rename(tarPath, newPath); err != nil { return "", err }