mirror of
https://github.com/anchore/syft.git
synced 2026-08-20 17:18:30 +02:00
fix: panic when scanning squashfs symlinks (#5119)
Signed-off-by: Keith Zantow <kzantow@gmail.com>
This commit is contained in:
parent
ab508169e6
commit
ed76e96749
@ -314,19 +314,7 @@ func squashfsVisitor(ft filetree.Writer, fileCatalog *image.FileCatalog, size *i
|
|||||||
|
|
||||||
prog.AtomicStage.Set(path)
|
prog.AtomicStage.Set(path)
|
||||||
|
|
||||||
var f filesystem.File
|
|
||||||
var mimeType string
|
var mimeType string
|
||||||
var err error
|
|
||||||
|
|
||||||
if !d.IsDir() {
|
|
||||||
f, err = fsys.OpenFile(intFile.ToFSPath(path), os.O_RDONLY)
|
|
||||||
if err != nil {
|
|
||||||
log.WithFields("error", err, "path", path).Trace("unable to open squash file path")
|
|
||||||
} else {
|
|
||||||
defer f.Close()
|
|
||||||
mimeType = stereoFile.MIMEType(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var ty stereoFile.Type
|
var ty stereoFile.Type
|
||||||
var linkPath string
|
var linkPath string
|
||||||
@ -335,11 +323,21 @@ func squashfsVisitor(ft filetree.Writer, fileCatalog *image.FileCatalog, size *i
|
|||||||
// in some implementations, the mode does not indicate a directory, so we check the FileInfo type explicitly
|
// in some implementations, the mode does not indicate a directory, so we check the FileInfo type explicitly
|
||||||
ty = stereoFile.TypeDirectory
|
ty = stereoFile.TypeDirectory
|
||||||
default:
|
default:
|
||||||
|
f, err := fsys.OpenFile(intFile.ToFSPath(path), os.O_RDONLY)
|
||||||
|
if err != nil {
|
||||||
|
log.WithFields("error", err, "path", path).Trace("unable to open squash file path")
|
||||||
|
}
|
||||||
|
if f != nil {
|
||||||
|
defer f.Close()
|
||||||
|
}
|
||||||
|
|
||||||
ty = stereoFile.TypeFromMode(d.Mode())
|
ty = stereoFile.TypeFromMode(d.Mode())
|
||||||
if ty == stereoFile.TypeSymLink && f != nil {
|
if ty == stereoFile.TypeSymLink && f != nil {
|
||||||
if l, ok := f.(linker); ok {
|
if l, ok := f.(linker); ok {
|
||||||
linkPath, _ = l.Readlink()
|
linkPath, _ = l.Readlink()
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
mimeType = stereoFile.MIMEType(f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,23 +1,31 @@
|
|||||||
package snapsource
|
package snapsource
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"crypto"
|
"crypto"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io/fs"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
diskFile "github.com/diskfs/go-diskfs/backend/file"
|
diskFile "github.com/diskfs/go-diskfs/backend/file"
|
||||||
|
"github.com/diskfs/go-diskfs/filesystem"
|
||||||
"github.com/diskfs/go-diskfs/filesystem/squashfs"
|
"github.com/diskfs/go-diskfs/filesystem/squashfs"
|
||||||
"github.com/spf13/afero"
|
"github.com/spf13/afero"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
"github.com/wagoodman/go-progress"
|
||||||
|
|
||||||
"github.com/anchore/go-homedir"
|
"github.com/anchore/go-homedir"
|
||||||
|
stereoFile "github.com/anchore/stereoscope/pkg/file"
|
||||||
|
"github.com/anchore/stereoscope/pkg/filetree"
|
||||||
"github.com/anchore/stereoscope/pkg/image"
|
"github.com/anchore/stereoscope/pkg/image"
|
||||||
|
"github.com/anchore/syft/syft/event/monitor"
|
||||||
"github.com/anchore/syft/syft/source"
|
"github.com/anchore/syft/syft/source"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -294,3 +302,102 @@ func writeSquashfs(t *testing.T, manifest string) string {
|
|||||||
require.NoError(t, fs.Finalize(squashfs.FinalizeOptions{}))
|
require.NoError(t, fs.Finalize(squashfs.FinalizeOptions{}))
|
||||||
return snapPath
|
return snapPath
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Test_SquashfsSymlinkCrash(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
path string
|
||||||
|
info fakeFileInfo
|
||||||
|
file *fakeSquashFile
|
||||||
|
wantType stereoFile.Type
|
||||||
|
wantLinkDest string
|
||||||
|
wantMIME assert.ValueAssertionFunc
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
// regression: previously the visitor opened every non-dir file and computed its MIME type
|
||||||
|
// before inspecting the type, which consumed the symlink's reader so Readlink returned
|
||||||
|
// nothing and the symlink was given a bogus MIME type. The fix only reads the link for
|
||||||
|
// symlinks and only computes a MIME type for everything else.
|
||||||
|
name: "symlink resolves link destination and is not given a mime type",
|
||||||
|
path: "/link",
|
||||||
|
info: fakeFileInfo{name: "link", mode: os.ModeSymlink | 0o777},
|
||||||
|
// the reader returns content so that the (buggy) behavior of computing a MIME type would
|
||||||
|
// have produced a non-empty MIME type, making this test fail against the old code.
|
||||||
|
file: &fakeSquashFile{Reader: bytes.NewReader([]byte("/usr/bin/target")), linkTarget: "/usr/bin/target"},
|
||||||
|
wantType: stereoFile.TypeSymLink,
|
||||||
|
wantLinkDest: "/usr/bin/target",
|
||||||
|
wantMIME: assert.Empty,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "regular file is given a mime type",
|
||||||
|
path: "/file.txt",
|
||||||
|
info: fakeFileInfo{name: "file.txt", mode: 0o644, size: 11},
|
||||||
|
file: &fakeSquashFile{Reader: bytes.NewReader([]byte("hello world"))},
|
||||||
|
wantType: stereoFile.TypeRegular,
|
||||||
|
wantMIME: assert.NotEmpty,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
tree := filetree.New()
|
||||||
|
catalog := image.NewFileCatalog()
|
||||||
|
prog := &monitor.TaskProgress{
|
||||||
|
AtomicStage: progress.NewAtomicStage(""),
|
||||||
|
Manual: progress.NewManual(-1),
|
||||||
|
}
|
||||||
|
|
||||||
|
fsys := fakeSquashFS{openFile: func(string, int) (filesystem.File, error) {
|
||||||
|
return tt.file, nil
|
||||||
|
}}
|
||||||
|
|
||||||
|
visit := squashfsVisitor(tree, catalog, nil, prog)
|
||||||
|
require.NoError(t, visit(fsys, tt.path, tt.info, nil))
|
||||||
|
|
||||||
|
entries, err := catalog.GetByBasename(tt.info.name)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Len(t, entries, 1)
|
||||||
|
|
||||||
|
entry := entries[0]
|
||||||
|
assert.Equal(t, tt.wantType, entry.Metadata.Type)
|
||||||
|
assert.Equal(t, tt.wantLinkDest, entry.Metadata.LinkDestination)
|
||||||
|
tt.wantMIME(t, entry.Metadata.MIMEType)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fakeFileInfo is a minimal os.FileInfo for driving squashfsVisitor in tests.
|
||||||
|
type fakeFileInfo struct {
|
||||||
|
name string
|
||||||
|
mode os.FileMode
|
||||||
|
size int64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f fakeFileInfo) Name() string { return f.name }
|
||||||
|
func (f fakeFileInfo) Size() int64 { return f.size }
|
||||||
|
func (f fakeFileInfo) Mode() os.FileMode { return f.mode }
|
||||||
|
func (f fakeFileInfo) ModTime() time.Time { return time.Time{} }
|
||||||
|
func (f fakeFileInfo) IsDir() bool { return f.mode.IsDir() }
|
||||||
|
func (f fakeFileInfo) Sys() any { return nil }
|
||||||
|
|
||||||
|
// fakeSquashFile implements filesystem.File and the linker interface used by squashfsVisitor.
|
||||||
|
type fakeSquashFile struct {
|
||||||
|
*bytes.Reader
|
||||||
|
linkTarget string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *fakeSquashFile) Write([]byte) (int, error) { return 0, fs.ErrInvalid }
|
||||||
|
func (f *fakeSquashFile) Close() error { return nil }
|
||||||
|
func (f *fakeSquashFile) Stat() (fs.FileInfo, error) { return nil, fs.ErrInvalid }
|
||||||
|
func (f *fakeSquashFile) Readlink() (string, error) { return f.linkTarget, nil }
|
||||||
|
|
||||||
|
// fakeSquashFS implements filesystem.FileSystem but only supports OpenFile; all other methods
|
||||||
|
// are inherited from the embedded nil interface and will panic if unexpectedly called.
|
||||||
|
type fakeSquashFS struct {
|
||||||
|
filesystem.FileSystem
|
||||||
|
openFile func(string, int) (filesystem.File, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f fakeSquashFS) OpenFile(name string, flag int) (filesystem.File, error) {
|
||||||
|
return f.openFile(name, flag)
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user