From 1344a5f598d9abc5424b1f79beeebf1b35f29e2f Mon Sep 17 00:00:00 2001 From: Keith Zantow Date: Wed, 28 Oct 2026 17:28:01 -0400 Subject: [PATCH] fix: properly read mimetype Signed-off-by: Keith Zantow --- syft/source/filesource/file_source.go | 8 +++++++- syft/source/filesource/file_source_test.go | 17 +++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/syft/source/filesource/file_source.go b/syft/source/filesource/file_source.go index 05783ab68..b9e18d58b 100644 --- a/syft/source/filesource/file_source.go +++ b/syft/source/filesource/file_source.go @@ -70,8 +70,14 @@ func New(cfg Config) (source.Source, error) { return nil, fmt.Errorf("given path is a directory: %q", cfg.Path) } + mimeType := stereoFile.MIMEType(f) + var digests []file.Digest if len(cfg.DigestAlgorithms) > 0 { + // reading the MIME type moved past the start of the file + if _, err := f.Seek(0, io.SeekStart); err != nil { + return nil, fmt.Errorf("unable to seek file=%q: %w", cfg.Path, err) + } digests, err = intFile.NewDigestsFromFile(context.TODO(), f, cfg.DigestAlgorithms) if err != nil { return nil, fmt.Errorf("unable to calculate digests for file=%q: %w", cfg.Path, err) @@ -98,7 +104,7 @@ func New(cfg Config) (source.Source, error) { analysisPath: analysisPath, digestForVersion: versionDigest, digests: digests, - mimeType: stereoFile.MIMEType(f), + mimeType: mimeType, }, nil } diff --git a/syft/source/filesource/file_source_test.go b/syft/source/filesource/file_source_test.go index fdfae2f1d..900cff8fb 100644 --- a/syft/source/filesource/file_source_test.go +++ b/syft/source/filesource/file_source_test.go @@ -4,6 +4,7 @@ import ( "archive/zip" "bytes" "context" + "crypto" "io" "os" "os/exec" @@ -243,6 +244,22 @@ func TestNewFromFile_RarExtension(t *testing.T) { } } +func TestNewFromFile_MIMETypeWithDigests(t *testing.T) { + // the digests read the whole file, which must not leave the MIME type to be read from its end + archivePath := filepath.Join(t.TempDir(), "example.zip") + require.NoError(t, os.WriteFile(archivePath, zipBytes(t, [][2]string{{"a.txt", "a"}}), 0o600)) + + src, err := New(Config{Path: archivePath, DigestAlgorithms: []crypto.Hash{crypto.SHA256}}) + require.NoError(t, err) + t.Cleanup(func() { + require.NoError(t, src.Close()) + }) + + m := src.Describe().Metadata.(source.FileMetadata) + assert.Equal(t, "application/zip", m.MIMEType) + assert.Len(t, m.Digests, 1) +} + func TestNewFromFile_ArchivePathsAreRootedAtTheArchive(t *testing.T) { // an extracted archive is scanned like a directory: its root is the empty path, and the temporary directory it // was extracted to does not appear