From bd6749a2e16d1bfe35a594af4cd9d9519a251115 Mon Sep 17 00:00:00 2001 From: Rex P Date: Thu, 7 Nov 2024 16:24:20 +1100 Subject: [PATCH] A quick fix to handle symbolic links --- internal/image/image.go | 21 ++++++++++++++------- internal/image/layer.go | 30 ++++++++++++++++++++++++------ pkg/osvscanner/osvscanner.go | 7 +++++++ 3 files changed, 45 insertions(+), 13 deletions(-) diff --git a/internal/image/image.go b/internal/image/image.go index 0be6f53bf2..212845ebfd 100644 --- a/internal/image/image.go +++ b/internal/image/image.go @@ -180,6 +180,7 @@ func LoadImage(imagePath string) (*Image, error) { // filepath.Clean first to convert to OS specific file path // TODO: Escape invalid characters on windows that's valid on linux absoluteDiskPath := filepath.Join(dirPath, filepath.Clean(cleanedFilePath)) + symlinkTarget := "" var fileType fileType // write out the file/dir to disk @@ -191,8 +192,7 @@ func LoadImage(imagePath string) (*Image, error) { } } fileType = Dir - - default: // Assume if it's not a directory, it's a normal file + case tar.TypeReg: // Write all files as read/writable by the current user, inaccessible by anyone else // Actual permission bits are stored in FileNode f, err := os.OpenFile(absoluteDiskPath, os.O_CREATE|os.O_RDWR, filePermission) @@ -210,6 +210,11 @@ func LoadImage(imagePath string) (*Image, error) { } fileType = RegularFile f.Close() + case tar.TypeSymlink: + fileType = Symlink + symlinkTarget = header.Linkname + default: // Assume if it's not a directory or normal file + // TODO: Handle these cases } // Each outer loop, we add a layer to each relevant output flattenedLayers slice @@ -238,11 +243,13 @@ func LoadImage(imagePath string) (*Image, error) { err := currentMap.fileNodeTrie.Insert(virtualPath, &FileNode{ rootImage: &outputImage, // Select the original layer of the file - originLayer: &outputImage.layers[i], - virtualPath: virtualPath, - fileType: fileType, - isWhiteout: tombstone, - permission: fs.FileMode(header.Mode), //nolint:gosec + originLayer: &outputImage.layers[i], + virtualPath: virtualPath, + fileType: fileType, + linkTargetPath: symlinkTarget, + isWhiteout: tombstone, + // TODO: Fix file mode bits to contain the high bits + permission: fs.FileMode(header.Mode), //nolint:gosec }) if err != nil { diff --git a/internal/image/layer.go b/internal/image/layer.go index eb66a37752..c25a089f93 100644 --- a/internal/image/layer.go +++ b/internal/image/layer.go @@ -18,17 +18,20 @@ type fileType int const ( RegularFile fileType = iota Dir + Symlink ) // FileNode represents a file on a specific layer, mapping the contents to an extracted file on disk type FileNode struct { // TODO: Determine the performance implications of having a pointer to base image in every fileNode - rootImage *Image - fileType fileType - isWhiteout bool - originLayer *Layer - virtualPath string - permission fs.FileMode + rootImage *Image + // TODO: Filetype is redundant if permission is set correctly + fileType fileType + isWhiteout bool + originLayer *Layer + virtualPath string + linkTargetPath string + permission fs.FileMode } var _ fs.DirEntry = FileNode{} @@ -82,6 +85,13 @@ func (f FileNodeFileInfo) Sys() any { // Stat returns the FileInfo structure describing file. func (f *FileNode) Stat() (fs.FileInfo, error) { + // TODO: Implement this properly + if f.fileType == Symlink { + return FileNodeFileInfo{ + fileNode: f, + }, nil + } + baseFileInfo, err := os.Stat(f.absoluteDiskPath()) if err != nil { return nil, err @@ -121,6 +131,10 @@ func (filemap Layer) Open(path string) (fs.File, error) { return nil, err } + if node.fileType == Symlink { + return filemap.Open(node.linkTargetPath) + } + return node.Open() } @@ -130,6 +144,10 @@ func (filemap Layer) Stat(path string) (fs.FileInfo, error) { return nil, err } + if node.fileType == Symlink { + return filemap.Stat(node.linkTargetPath) + } + return node.Stat() } diff --git a/pkg/osvscanner/osvscanner.go b/pkg/osvscanner/osvscanner.go index e04b01b8d9..074f00613e 100644 --- a/pkg/osvscanner/osvscanner.go +++ b/pkg/osvscanner/osvscanner.go @@ -1118,6 +1118,13 @@ func patchPackageForRequest(pkg scannedPackage) scannedPackage { } } + // TODO: This should be done on the osv.dev side + // This is needed because Ubuntu ecosystem appends LTS + // but the scanner does not have this information. + if pkg.Ecosystem == "Ubuntu:20.04" { + pkg.Ecosystem = "Ubuntu:20.04:LTS" + } + return pkg }