diff --git a/syft/internal/fileresolver/directory_indexer.go b/syft/internal/fileresolver/directory_indexer.go index 1facdc44b..bc3d8a5f2 100644 --- a/syft/internal/fileresolver/directory_indexer.go +++ b/syft/internal/fileresolver/directory_indexer.go @@ -322,7 +322,7 @@ func (r directoryIndexer) addDirectoryToIndex(p string, info os.FileInfo) error return err } - metadata := file.NewMetadataFromPath(p, info) + metadata := NewMetadataFromPath(p, info) r.index.Add(*ref, metadata) return nil @@ -334,7 +334,7 @@ func (r directoryIndexer) addFileToIndex(p string, info os.FileInfo) error { return err } - metadata := file.NewMetadataFromPath(p, info) + metadata := NewMetadataFromPath(p, info) r.index.Add(*ref, metadata) return nil @@ -416,7 +416,7 @@ func (r directoryIndexer) addSymlinkToIndex(p string, info os.FileInfo) (string, targetAbsPath = filepath.Clean(filepath.Join(path.Dir(p), linkTarget)) } - metadata := file.NewMetadataFromPath(p, info) + metadata := NewMetadataFromPath(p, info) metadata.LinkDestination = linkTarget r.index.Add(*ref, metadata) diff --git a/syft/internal/fileresolver/file_indexer.go b/syft/internal/fileresolver/file_indexer.go index cf257dc95..086dc75a9 100644 --- a/syft/internal/fileresolver/file_indexer.go +++ b/syft/internal/fileresolver/file_indexer.go @@ -173,7 +173,7 @@ func (r *fileIndexer) addDirectoryToIndex(path string, info os.FileInfo) error { return err } - metadata := file.NewMetadataFromPath(path, info) + metadata := NewMetadataFromPath(path, info) r.index.Add(*ref, metadata) return nil @@ -185,7 +185,7 @@ func (r *fileIndexer) addFileToIndex(path string, info os.FileInfo) error { return err } - metadata := file.NewMetadataFromPath(path, info) + metadata := NewMetadataFromPath(path, info) r.index.Add(*ref, metadata) return nil diff --git a/syft/internal/fileresolver/get_xid.go b/syft/internal/fileresolver/get_xid.go new file mode 100644 index 000000000..8e00bc7a0 --- /dev/null +++ b/syft/internal/fileresolver/get_xid.go @@ -0,0 +1,20 @@ +//go:build !windows + +package fileresolver + +import ( + "os" + "syscall" +) + +// getXid is the UID GID system info for unix +func getXid(info os.FileInfo) (uid, gid int) { + uid = -1 + gid = -1 + if stat, ok := info.Sys().(*syscall.Stat_t); ok { + uid = int(stat.Uid) + gid = int(stat.Gid) + } + + return uid, gid +} diff --git a/syft/internal/fileresolver/get_xid_win.go b/syft/internal/fileresolver/get_xid_win.go new file mode 100644 index 000000000..c5202e92b --- /dev/null +++ b/syft/internal/fileresolver/get_xid_win.go @@ -0,0 +1,12 @@ +//go:build windows + +package fileresolver + +import ( + "os" +) + +// getXid is a placeholder for windows file information +func getXid(info os.FileInfo) (uid, gid int) { + return -1, -1 +} diff --git a/syft/internal/fileresolver/metadata.go b/syft/internal/fileresolver/metadata.go new file mode 100644 index 000000000..af15c8733 --- /dev/null +++ b/syft/internal/fileresolver/metadata.go @@ -0,0 +1,44 @@ +package fileresolver + +import ( + "os" + + "github.com/anchore/stereoscope/pkg/file" + "github.com/anchore/syft/internal" + "github.com/anchore/syft/syft/internal/windows" +) + +func NewMetadataFromPath(path string, info os.FileInfo) file.Metadata { + var mimeType string + uid, gid := getXid(info) + + ty := file.TypeFromMode(info.Mode()) + + if ty == file.TypeRegular { + usablePath := path + // denormalize the path back to windows so we can open the file + if windows.HostRunningOnWindows() { + usablePath = windows.FromPosix(usablePath) + } + + f, err := os.Open(usablePath) + if err != nil { + // TODO: it may be that the file is inaccessible, however, this is not an error or a warning. In the future we need to track these as known-unknowns + f = nil + } else { + defer internal.CloseAndLogError(f, usablePath) + } + + mimeType = file.MIMEType(f) + } + + return file.Metadata{ + FileInfo: info, + Path: path, + Type: ty, + // unsupported across platforms + UserID: uid, + GroupID: gid, + MIMEType: mimeType, + } +} diff --git a/syft/internal/fileresolver/metadata_test.go b/syft/internal/fileresolver/metadata_test.go new file mode 100644 index 000000000..936cd227c --- /dev/null +++ b/syft/internal/fileresolver/metadata_test.go @@ -0,0 +1,50 @@ +package fileresolver + +import ( + "os" + "testing" + + "github.com/anchore/stereoscope/pkg/file" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestFileMetadataFromPath(t *testing.T) { + + tests := []struct { + path string + expectedType file.Type + expectedMIMEType string + }{ + { + path: "test-fixtures/symlinks-simple/readme", + expectedType: file.TypeRegular, + expectedMIMEType: "text/plain", + }, + { + path: "test-fixtures/symlinks-simple/link_to_new_readme", + expectedType: file.TypeSymLink, + expectedMIMEType: "", + }, + { + path: "test-fixtures/symlinks-simple/link_to_link_to_new_readme", + expectedType: file.TypeSymLink, + expectedMIMEType: "", + }, + { + path: "test-fixtures/symlinks-simple", + expectedType: file.TypeDirectory, + expectedMIMEType: "", + }, + } + for _, test := range tests { + t.Run(test.path, func(t *testing.T) { + info, err := os.Lstat(test.path) + require.NoError(t, err) + + actual := NewMetadataFromPath(test.path, info) + assert.Equal(t, test.expectedMIMEType, actual.MIMEType, "unexpected MIME type for %s", test.path) + assert.Equal(t, test.expectedType, actual.Type, "unexpected type for %s", test.path) + }) + } +}