mirror of
https://github.com/anchore/syft
synced 2024-11-13 23:57:07 +00:00
Consider filesystem types for mount points when ignoring system paths (#2675)
* consider fs types for mount points when ignoring system paths Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * address feedback Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> --------- Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
parent
63171b55dd
commit
48e5672a87
6 changed files with 215 additions and 113 deletions
5
go.mod
5
go.mod
|
@ -11,6 +11,7 @@ require (
|
||||||
github.com/anchore/bubbly v0.0.0-20231115134915-def0aba654a9
|
github.com/anchore/bubbly v0.0.0-20231115134915-def0aba654a9
|
||||||
github.com/anchore/clio v0.0.0-20240209204744-cb94e40a4f65
|
github.com/anchore/clio v0.0.0-20240209204744-cb94e40a4f65
|
||||||
github.com/anchore/fangs v0.0.0-20231201140849-5075d28d6d8b
|
github.com/anchore/fangs v0.0.0-20231201140849-5075d28d6d8b
|
||||||
|
github.com/anchore/go-collections v0.0.0-20240216171411-9321230ce537
|
||||||
github.com/anchore/go-logger v0.0.0-20230725134548-c21dafa1ec5a
|
github.com/anchore/go-logger v0.0.0-20230725134548-c21dafa1ec5a
|
||||||
github.com/anchore/go-macholibre v0.0.0-20220308212642-53e6d0aaf6fb
|
github.com/anchore/go-macholibre v0.0.0-20220308212642-53e6d0aaf6fb
|
||||||
github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04
|
github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04
|
||||||
|
@ -53,6 +54,7 @@ require (
|
||||||
github.com/mitchellh/go-homedir v1.1.0
|
github.com/mitchellh/go-homedir v1.1.0
|
||||||
github.com/mitchellh/hashstructure/v2 v2.0.2
|
github.com/mitchellh/hashstructure/v2 v2.0.2
|
||||||
github.com/mitchellh/mapstructure v1.5.0
|
github.com/mitchellh/mapstructure v1.5.0
|
||||||
|
github.com/moby/sys/mountinfo v0.7.1
|
||||||
github.com/olekukonko/tablewriter v0.0.5
|
github.com/olekukonko/tablewriter v0.0.5
|
||||||
github.com/opencontainers/go-digest v1.0.0
|
github.com/opencontainers/go-digest v1.0.0
|
||||||
github.com/pelletier/go-toml v1.9.5
|
github.com/pelletier/go-toml v1.9.5
|
||||||
|
@ -80,8 +82,6 @@ require (
|
||||||
modernc.org/sqlite v1.29.2
|
modernc.org/sqlite v1.29.2
|
||||||
)
|
)
|
||||||
|
|
||||||
require github.com/anchore/go-collections v0.0.0-20240216171411-9321230ce537
|
|
||||||
|
|
||||||
require (
|
require (
|
||||||
dario.cat/mergo v1.0.0 // indirect
|
dario.cat/mergo v1.0.0 // indirect
|
||||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect
|
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect
|
||||||
|
@ -161,7 +161,6 @@ require (
|
||||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||||
github.com/moby/locker v1.0.1 // indirect
|
github.com/moby/locker v1.0.1 // indirect
|
||||||
github.com/moby/sys/mountinfo v0.6.2 // indirect
|
|
||||||
github.com/moby/sys/sequential v0.5.0 // indirect
|
github.com/moby/sys/sequential v0.5.0 // indirect
|
||||||
github.com/moby/sys/signal v0.7.0 // indirect
|
github.com/moby/sys/signal v0.7.0 // indirect
|
||||||
github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70 // indirect
|
github.com/muesli/ansi v0.0.0-20211031195517-c9f0611b6c70 // indirect
|
||||||
|
|
4
go.sum
4
go.sum
|
@ -571,8 +571,8 @@ github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zx
|
||||||
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
|
||||||
github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
|
github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg=
|
||||||
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
|
github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
|
||||||
github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78=
|
github.com/moby/sys/mountinfo v0.7.1 h1:/tTvQaSJRr2FshkhXiIpux6fQ2Zvc4j7tAhMTStAG2g=
|
||||||
github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI=
|
github.com/moby/sys/mountinfo v0.7.1/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI=
|
||||||
github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc=
|
github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc=
|
||||||
github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo=
|
github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo=
|
||||||
github.com/moby/sys/signal v0.7.0 h1:25RW3d5TnQEoKvRbEKUGay6DCQ46IxAVTT9CUMgmsSI=
|
github.com/moby/sys/signal v0.7.0 h1:25RW3d5TnQEoKvRbEKUGay6DCQ46IxAVTT9CUMgmsSI=
|
||||||
|
|
|
@ -14,12 +14,6 @@ import (
|
||||||
"github.com/anchore/syft/syft/internal/windows"
|
"github.com/anchore/syft/syft/internal/windows"
|
||||||
)
|
)
|
||||||
|
|
||||||
var unixSystemRuntimePrefixes = []string{
|
|
||||||
"/proc",
|
|
||||||
"/dev",
|
|
||||||
"/sys",
|
|
||||||
}
|
|
||||||
|
|
||||||
var ErrSkipPath = errors.New("skip path")
|
var ErrSkipPath = errors.New("skip path")
|
||||||
|
|
||||||
var _ file.Resolver = (*Directory)(nil)
|
var _ file.Resolver = (*Directory)(nil)
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/moby/sys/mountinfo"
|
||||||
"github.com/wagoodman/go-partybus"
|
"github.com/wagoodman/go-partybus"
|
||||||
"github.com/wagoodman/go-progress"
|
"github.com/wagoodman/go-progress"
|
||||||
|
|
||||||
|
@ -38,7 +39,13 @@ func newDirectoryIndexer(path, base string, visitors ...PathIndexVisitor) *direc
|
||||||
base: base,
|
base: base,
|
||||||
tree: filetree.New(),
|
tree: filetree.New(),
|
||||||
index: filetree.NewIndex(),
|
index: filetree.NewIndex(),
|
||||||
pathIndexVisitors: append([]PathIndexVisitor{requireFileInfo, disallowByFileType, disallowUnixSystemRuntimePath}, visitors...),
|
pathIndexVisitors: append(
|
||||||
|
[]PathIndexVisitor{
|
||||||
|
requireFileInfo,
|
||||||
|
disallowByFileType,
|
||||||
|
newUnixSystemMountFinder().disallowUnixSystemRuntimePath},
|
||||||
|
visitors...,
|
||||||
|
),
|
||||||
errPaths: make(map[string]error),
|
errPaths: make(map[string]error),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -443,11 +450,52 @@ func (r *directoryIndexer) disallowRevisitingVisitor(_, path string, _ os.FileIn
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func disallowUnixSystemRuntimePath(base, path string, _ os.FileInfo, _ error) error {
|
type unixSystemMountFinder struct {
|
||||||
// note: we need to consider all paths relative to the base when being filtered, which is how they would appear
|
disallowedMountPaths []string
|
||||||
// when the resolver is being used. Then something like /some/mountpoint/dev with a base of /some/mountpoint
|
}
|
||||||
// would be considered as /dev when being filtered.
|
|
||||||
if internal.HasAnyOfPrefixes(relativePath(base, path), unixSystemRuntimePrefixes...) {
|
func newUnixSystemMountFinder() unixSystemMountFinder {
|
||||||
|
infos, err := mountinfo.GetMounts(nil)
|
||||||
|
if err != nil {
|
||||||
|
log.WithFields("error", err).Warnf("unable to get system mounts")
|
||||||
|
return unixSystemMountFinder{}
|
||||||
|
}
|
||||||
|
|
||||||
|
return unixSystemMountFinder{
|
||||||
|
disallowedMountPaths: keepUnixSystemMountPaths(infos),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func keepUnixSystemMountPaths(infos []*mountinfo.Info) []string {
|
||||||
|
var mountPaths []string
|
||||||
|
for _, info := range infos {
|
||||||
|
if info == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// we're only interested in ignoring the logical filesystems typically found at these mount points:
|
||||||
|
// - /proc
|
||||||
|
// - procfs
|
||||||
|
// - proc
|
||||||
|
// - /sys
|
||||||
|
// - sysfs
|
||||||
|
// - /dev
|
||||||
|
// - devfs - BSD/darwin flavored systems and old linux systems
|
||||||
|
// - devtmpfs - driver core maintained /dev tmpfs
|
||||||
|
// - udev - userspace implementation that replaced devfs
|
||||||
|
// - tmpfs - used for /dev in special instances (within a container)
|
||||||
|
|
||||||
|
switch info.FSType {
|
||||||
|
case "proc", "procfs", "sysfs", "devfs", "devtmpfs", "udev", "tmpfs":
|
||||||
|
log.WithFields("mountpoint", info.Mountpoint).Debug("ignoring system mountpoint")
|
||||||
|
|
||||||
|
mountPaths = append(mountPaths, info.Mountpoint)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mountPaths
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f unixSystemMountFinder) disallowUnixSystemRuntimePath(_, path string, _ os.FileInfo, _ error) error {
|
||||||
|
if internal.HasAnyOfPrefixes(path, f.disallowedMountPaths...) {
|
||||||
return fs.SkipDir
|
return fs.SkipDir
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
@ -475,32 +523,6 @@ func requireFileInfo(_, _ string, info os.FileInfo, _ error) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func relativePath(basePath, givenPath string) string {
|
|
||||||
var relPath string
|
|
||||||
var relErr error
|
|
||||||
|
|
||||||
if basePath != "" {
|
|
||||||
relPath, relErr = filepath.Rel(basePath, givenPath)
|
|
||||||
cleanPath := filepath.Clean(relPath)
|
|
||||||
if relErr == nil {
|
|
||||||
if cleanPath == "." {
|
|
||||||
relPath = string(filepath.Separator)
|
|
||||||
} else {
|
|
||||||
relPath = cleanPath
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !filepath.IsAbs(relPath) {
|
|
||||||
relPath = string(filepath.Separator) + relPath
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if relErr != nil || basePath == "" {
|
|
||||||
relPath = givenPath
|
|
||||||
}
|
|
||||||
|
|
||||||
return relPath
|
|
||||||
}
|
|
||||||
|
|
||||||
func indexingProgress(path string) (*progress.Stage, *progress.Manual) {
|
func indexingProgress(path string) (*progress.Stage, *progress.Manual) {
|
||||||
stage := &progress.Stage{}
|
stage := &progress.Stage{}
|
||||||
prog := progress.NewManual(-1)
|
prog := progress.NewManual(-1)
|
||||||
|
|
|
@ -4,11 +4,13 @@ import (
|
||||||
"io/fs"
|
"io/fs"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
"path/filepath"
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/google/go-cmp/cmp"
|
"github.com/google/go-cmp/cmp"
|
||||||
|
"github.com/moby/sys/mountinfo"
|
||||||
"github.com/scylladb/go-set/strset"
|
"github.com/scylladb/go-set/strset"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
@ -435,44 +437,170 @@ func Test_relativePath(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func relativePath(basePath, givenPath string) string {
|
||||||
|
var relPath string
|
||||||
|
var relErr error
|
||||||
|
|
||||||
|
if basePath != "" {
|
||||||
|
relPath, relErr = filepath.Rel(basePath, givenPath)
|
||||||
|
cleanPath := filepath.Clean(relPath)
|
||||||
|
if relErr == nil {
|
||||||
|
if cleanPath == "." {
|
||||||
|
relPath = string(filepath.Separator)
|
||||||
|
} else {
|
||||||
|
relPath = cleanPath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !filepath.IsAbs(relPath) {
|
||||||
|
relPath = string(filepath.Separator) + relPath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if relErr != nil || basePath == "" {
|
||||||
|
relPath = givenPath
|
||||||
|
}
|
||||||
|
|
||||||
|
return relPath
|
||||||
|
}
|
||||||
|
|
||||||
func Test_disallowUnixSystemRuntimePath(t *testing.T) {
|
func Test_disallowUnixSystemRuntimePath(t *testing.T) {
|
||||||
|
unixSubject := unixSystemMountFinder{
|
||||||
|
// mock out detecting the mount points
|
||||||
|
disallowedMountPaths: []string{"/proc", "/sys", "/dev"},
|
||||||
|
}
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
base string
|
|
||||||
path string
|
path string
|
||||||
wantErr require.ErrorAssertionFunc
|
base string
|
||||||
|
expected error
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "no base, matching path",
|
name: "relative path to proc is allowed",
|
||||||
base: "",
|
path: "proc/place",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "relative path within proc is not allowed",
|
||||||
|
path: "/proc/place",
|
||||||
|
expected: fs.SkipDir,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "path exactly to proc is not allowed",
|
||||||
|
path: "/proc",
|
||||||
|
expected: fs.SkipDir,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "similar to proc",
|
||||||
|
path: "/pro/c",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "similar to proc",
|
||||||
|
path: "/pro",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "dev is not allowed",
|
||||||
path: "/dev",
|
path: "/dev",
|
||||||
wantErr: require.Error,
|
expected: fs.SkipDir,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "no base, non-matching path",
|
name: "sys is not allowed",
|
||||||
base: "",
|
path: "/sys",
|
||||||
path: "/not-dev",
|
expected: fs.SkipDir,
|
||||||
wantErr: require.NoError,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "base, matching path",
|
name: "unrelated allowed path",
|
||||||
|
path: "/something/sys",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "do not consider base when matching paths (non-matching)",
|
||||||
base: "/a/b/c",
|
base: "/a/b/c",
|
||||||
path: "/a/b/c/dev",
|
path: "/a/b/c/dev",
|
||||||
wantErr: require.Error,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "base, non-matching path due to bad relative alignment",
|
name: "do not consider base when matching paths (matching)",
|
||||||
base: "/a/b/c",
|
base: "/a/b/c",
|
||||||
path: "/dev",
|
path: "/dev",
|
||||||
wantErr: require.NoError,
|
expected: fs.SkipDir,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, test := range tests {
|
||||||
|
t.Run(test.path, func(t *testing.T) {
|
||||||
|
assert.Equal(t, test.expected, unixSubject.disallowUnixSystemRuntimePath(test.base, test.path, nil, nil))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_keepUnixSystemMountPaths(t *testing.T) {
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
infos []*mountinfo.Info
|
||||||
|
want []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "all valid filesystems",
|
||||||
|
infos: []*mountinfo.Info{
|
||||||
|
{
|
||||||
|
Mountpoint: "/etc/hostname",
|
||||||
|
FSType: "/dev/vda1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Mountpoint: "/sys/fs/cgroup",
|
||||||
|
FSType: "cgroup",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Mountpoint: "/",
|
||||||
|
FSType: "overlay",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no valid filesystems",
|
||||||
|
infos: []*mountinfo.Info{
|
||||||
|
{
|
||||||
|
Mountpoint: "/proc",
|
||||||
|
FSType: "proc",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Mountpoint: "/proc-2",
|
||||||
|
FSType: "procfs",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Mountpoint: "/sys",
|
||||||
|
FSType: "sysfs",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Mountpoint: "/dev",
|
||||||
|
FSType: "devfs",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Mountpoint: "/dev-u",
|
||||||
|
FSType: "udev",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Mountpoint: "/dev-tmp",
|
||||||
|
FSType: "devtmpfs",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Mountpoint: "/run",
|
||||||
|
FSType: "tmpfs",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want: []string{
|
||||||
|
"/proc",
|
||||||
|
"/proc-2",
|
||||||
|
"/sys",
|
||||||
|
"/dev",
|
||||||
|
"/dev-u",
|
||||||
|
"/dev-tmp",
|
||||||
|
"/run",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
if tt.wantErr == nil {
|
assert.Equal(t, tt.want, keepUnixSystemMountPaths(tt.infos))
|
||||||
tt.wantErr = require.NoError
|
|
||||||
}
|
|
||||||
tt.wantErr(t, disallowUnixSystemRuntimePath(tt.base, tt.path, nil, nil))
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1108,47 +1108,6 @@ func Test_directoryResolver_FileContentsByLocation(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_isUnixSystemRuntimePath(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
path string
|
|
||||||
expected error
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
path: "proc/place",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "/proc/place",
|
|
||||||
expected: fs.SkipDir,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "/proc",
|
|
||||||
expected: fs.SkipDir,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "/pro/c",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "/pro",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "/dev",
|
|
||||||
expected: fs.SkipDir,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "/sys",
|
|
||||||
expected: fs.SkipDir,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
path: "/something/sys",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, test := range tests {
|
|
||||||
t.Run(test.path, func(t *testing.T) {
|
|
||||||
assert.Equal(t, test.expected, disallowUnixSystemRuntimePath("", test.path, nil, nil))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_SymlinkLoopWithGlobsShouldResolve(t *testing.T) {
|
func Test_SymlinkLoopWithGlobsShouldResolve(t *testing.T) {
|
||||||
test := func(t *testing.T) {
|
test := func(t *testing.T) {
|
||||||
resolver, err := NewFromDirectory("./test-fixtures/symlinks-loop", "")
|
resolver, err := NewFromDirectory("./test-fixtures/symlinks-loop", "")
|
||||||
|
|
Loading…
Reference in a new issue