Files
syft/syft/internal/fileresolver/path_skipper_test.go
T
William MurphyandAlex Goodman 557ad73ee6 fix: only skip tmpfs mounts for some paths (#2918)
* fix: only skip tmpfs mounts for some paths

Signed-off-by: Will Murphy <will.murphy@anchore.com>

* refactor and add tests

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* add regression test for archive processing

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* bump to golang 1.22

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* remove rule 1 and add more tests

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

---------

Signed-off-by: Will Murphy <will.murphy@anchore.com>
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
Co-authored-by: Alex Goodman <wagoodman@users.noreply.github.com>
2024-06-04 15:21:45 -04:00

396 lines
7.5 KiB
Go

package fileresolver
import (
"io/fs"
"testing"
"github.com/moby/sys/mountinfo"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_newPathSkipper(t *testing.T) {
type expect struct {
path string
wantErr assert.ErrorAssertionFunc
}
unixSubject := []*mountinfo.Info{
{
Mountpoint: "/proc",
FSType: "procfs",
},
{
Mountpoint: "/sys",
FSType: "sysfs",
},
{
Mountpoint: "/dev",
FSType: "devfs",
},
{
Mountpoint: "/",
FSType: "/dev/disk3s1s1",
},
{
Mountpoint: "/dev/shm",
FSType: "shm",
},
{
Mountpoint: "/tmp",
FSType: "tmpfs",
},
}
tests := []struct {
name string
root string
base string
mounts []*mountinfo.Info
want []expect
}{
{
name: "happy path",
root: "/somewhere",
mounts: []*mountinfo.Info{
{
Mountpoint: "/home/somewhere/else",
FSType: "/dev/disk3s6",
},
{
Mountpoint: "/somewhere",
FSType: "/dev/disk3s7",
},
},
want: []expect{
{
// within a known mountpoint with valid type (1)
path: "/somewhere/dev",
},
{
// is a known mountpoint with valid type
path: "/somewhere",
},
{
// within a known mountpoint with valid type (2)
path: "/home/somewhere/else/too",
},
{
// outside of any known mountpoint should not be an error
path: "/bogus",
},
},
},
{
name: "ignore paths within a scan target",
root: "/somewhere",
mounts: []*mountinfo.Info{
{
Mountpoint: "/somewhere/doesnt/matter/proc",
FSType: "procfs",
},
{
Mountpoint: "/somewhere",
FSType: "/dev/disk3s7",
},
},
want: []expect{
{
// within a known mountpoint with valid type (1)
path: "/somewhere/dev",
},
{
// is a known mountpoint with valid type
path: "/somewhere",
},
{
// mountpoint that should be ignored
path: "/somewhere/doesnt/matter/proc",
wantErr: assertSkipErr(),
},
{
// within a mountpoint that should be ignored
path: "/somewhere/doesnt/matter/proc",
wantErr: assertSkipErr(),
},
},
},
{
name: "nested mountpoints behave correctly",
root: "/somewhere",
mounts: []*mountinfo.Info{
{
Mountpoint: "/somewhere/dev",
FSType: "devfs",
},
{
Mountpoint: "/somewhere/dev/includeme",
FSType: "/dev/disk3s7",
},
},
want: []expect{
{
// is a known mountpoint with valid type
path: "/somewhere/dev",
wantErr: assertSkipErr(),
},
{
// is a known mountpoint with valid type
path: "/somewhere/dev/includeme",
},
{
// within a known mountpoint with valid type
path: "/somewhere/dev/includeme/too!",
},
},
},
{
name: "keep some tmpfs mounts conditionally",
root: "/",
mounts: []*mountinfo.Info{
{
Mountpoint: "/run/somewhere",
FSType: "tmpfs",
},
{
Mountpoint: "/run/terrafirma",
FSType: "/dev/disk3s8",
},
{
Mountpoint: "/tmp",
FSType: "tmpfs",
},
{
Mountpoint: "/else/othertmp",
FSType: "tmpfs",
},
{
Mountpoint: "/else/othertmp/includeme",
FSType: "/dev/disk3s7",
},
},
want: []expect{
{
// since /run is explicitly ignored, this should be skipped
path: "/run/somewhere/else",
wantErr: assertSkipErr(),
},
{
path: "/run/terrafirma",
},
{
path: "/run/terrafirma/nested",
},
{
path: "/tmp",
},
{
path: "/else/othertmp/includeme",
},
{
path: "/else/othertmp/includeme/nested",
},
{
// no mount path, so we should include it
path: "/somewhere/dev/includeme",
},
{
// keep additional tmpfs mounts that are not explicitly ignored
path: "/else/othertmp",
},
},
},
{
name: "ignore known trixy tmpfs paths",
root: "/",
mounts: []*mountinfo.Info{
{
Mountpoint: "/",
FSType: "/dev/disk3s7",
},
{
Mountpoint: "/dev",
FSType: "tmpfs",
},
{
Mountpoint: "/run",
FSType: "tmpfs",
},
{
Mountpoint: "/var/run",
FSType: "tmpfs",
},
{
Mountpoint: "/var/lock",
FSType: "tmpfs",
},
{
Mountpoint: "/sys",
FSType: "tmpfs",
},
{
Mountpoint: "/tmp",
FSType: "tmpfs",
},
},
want: []expect{
{
path: "/dev",
wantErr: assertSkipErr(),
},
{
path: "/run",
wantErr: assertSkipErr(),
},
{
path: "/var/run",
wantErr: assertSkipErr(),
},
{
path: "/var/lock",
wantErr: assertSkipErr(),
},
{
path: "/sys",
wantErr: assertSkipErr(),
},
// show that we honor ignoring nested paths
{
path: "/sys/nested",
wantErr: assertSkipErr(),
},
// show that paths outside of the known mountpoints are not skipped
{
path: "/stuff",
},
// show that we allow other tmpfs paths that are not on the blocklist
{
path: "/tmp/allowed",
},
// show sibling paths with same prefix (e.g. /sys vs /system) to that of not allowed paths are not skipped
{
path: "/system",
},
},
},
{
name: "test unix paths",
mounts: unixSubject,
root: "/",
want: []expect{
{
// relative path to proc is allowed
path: "proc/place",
},
{
// relative path within proc is not allowed
path: "/proc/place",
wantErr: assertSkipErr(),
},
{
// path exactly to proc is not allowed
path: "/proc",
wantErr: assertSkipErr(),
},
{
// similar to proc
path: "/pro/c",
},
{
// similar to proc
path: "/pro",
},
{
// dev is not allowed
path: "/dev",
wantErr: assertSkipErr(),
},
{
// sys is not allowed
path: "/sys",
wantErr: assertSkipErr(),
},
},
},
{
name: "test unix paths with base",
mounts: unixSubject,
root: "/",
base: "/a/b/c",
want: []expect{
{
// do not consider base when matching paths (non-matching)
path: "/a/b/c/dev",
},
{
// do not consider base when matching paths (matching)
path: "/dev",
wantErr: assertSkipErr(),
},
},
},
{
name: "mimic nixos setup",
root: "/",
mounts: []*mountinfo.Info{
{
Mountpoint: "/",
FSType: "tmpfs", // this is an odd setup, but valid
},
{
Mountpoint: "/home",
FSType: "/dev/disk3s7",
},
},
want: []expect{
{
path: "/home/somewhere",
},
{
path: "/home",
},
{
path: "/somewhere",
},
{
// still not allowed...
path: "/run",
wantErr: assertSkipErr(),
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.base == "" {
tt.base = tt.root
}
require.NotEmpty(t, tt.want)
ps := newPathSkipperFromMounts(tt.root, tt.mounts)
for _, exp := range tt.want {
t.Run(exp.path, func(t *testing.T) {
got := ps.pathIndexVisitor(tt.base, exp.path, nil, nil)
if exp.wantErr == nil {
assert.NoError(t, got)
return
}
exp.wantErr(t, got)
})
}
})
}
}
func assertSkipErr() assert.ErrorAssertionFunc {
return assertErrorIs(fs.SkipDir)
}
func assertErrorIs(want error) assert.ErrorAssertionFunc {
return func(t assert.TestingT, got error, msgAndArgs ...interface{}) bool {
return assert.ErrorIs(t, got, want, msgAndArgs...)
}
}