github.com/anchore/syft@v1.38.2/syft/internal/fileresolver/path_skipper_test.go (about) 1 package fileresolver 2 3 import ( 4 "io/fs" 5 "testing" 6 7 "github.com/moby/sys/mountinfo" 8 "github.com/stretchr/testify/assert" 9 "github.com/stretchr/testify/require" 10 ) 11 12 func Test_newPathSkipper(t *testing.T) { 13 type expect struct { 14 path string 15 wantErr assert.ErrorAssertionFunc 16 } 17 unixSubject := []*mountinfo.Info{ 18 { 19 Mountpoint: "/proc", 20 FSType: "procfs", 21 }, 22 { 23 Mountpoint: "/sys", 24 FSType: "sysfs", 25 }, 26 { 27 Mountpoint: "/dev", 28 FSType: "devfs", 29 }, 30 { 31 Mountpoint: "/", 32 FSType: "/dev/disk3s1s1", 33 }, 34 { 35 Mountpoint: "/dev/shm", 36 FSType: "shm", 37 }, 38 { 39 Mountpoint: "/tmp", 40 FSType: "tmpfs", 41 }, 42 } 43 44 tests := []struct { 45 name string 46 root string 47 base string 48 mounts []*mountinfo.Info 49 want []expect 50 }{ 51 { 52 name: "happy path", 53 root: "/somewhere", 54 mounts: []*mountinfo.Info{ 55 { 56 Mountpoint: "/home/somewhere/else", 57 FSType: "/dev/disk3s6", 58 }, 59 { 60 Mountpoint: "/somewhere", 61 FSType: "/dev/disk3s7", 62 }, 63 }, 64 want: []expect{ 65 { 66 // within a known mountpoint with valid type (1) 67 path: "/somewhere/dev", 68 }, 69 { 70 // is a known mountpoint with valid type 71 path: "/somewhere", 72 }, 73 { 74 // within a known mountpoint with valid type (2) 75 path: "/home/somewhere/else/too", 76 }, 77 { 78 // outside of any known mountpoint should not be an error 79 path: "/bogus", 80 }, 81 }, 82 }, 83 { 84 name: "ignore paths within a scan target", 85 root: "/somewhere", 86 mounts: []*mountinfo.Info{ 87 { 88 Mountpoint: "/somewhere/doesnt/matter/proc", 89 FSType: "procfs", 90 }, 91 { 92 Mountpoint: "/somewhere", 93 FSType: "/dev/disk3s7", 94 }, 95 }, 96 want: []expect{ 97 { 98 // within a known mountpoint with valid type (1) 99 path: "/somewhere/dev", 100 }, 101 { 102 // is a known mountpoint with valid type 103 path: "/somewhere", 104 }, 105 { 106 // mountpoint that should be ignored 107 path: "/somewhere/doesnt/matter/proc", 108 wantErr: assertSkipErr(), 109 }, 110 { 111 // within a mountpoint that should be ignored 112 path: "/somewhere/doesnt/matter/proc", 113 wantErr: assertSkipErr(), 114 }, 115 }, 116 }, 117 { 118 name: "nested mountpoints behave correctly", 119 root: "/somewhere", 120 mounts: []*mountinfo.Info{ 121 { 122 Mountpoint: "/somewhere/dev", 123 FSType: "devfs", 124 }, 125 { 126 Mountpoint: "/somewhere/dev/includeme", 127 FSType: "/dev/disk3s7", 128 }, 129 }, 130 want: []expect{ 131 { 132 // is a known mountpoint with valid type 133 path: "/somewhere/dev", 134 wantErr: assertSkipErr(), 135 }, 136 { 137 // is a known mountpoint with valid type 138 path: "/somewhere/dev/includeme", 139 }, 140 { 141 // within a known mountpoint with valid type 142 path: "/somewhere/dev/includeme/too!", 143 }, 144 }, 145 }, 146 { 147 name: "keep some tmpfs mounts conditionally", 148 root: "/", 149 mounts: []*mountinfo.Info{ 150 { 151 Mountpoint: "/run/somewhere", 152 FSType: "tmpfs", 153 }, 154 { 155 Mountpoint: "/run/terrafirma", 156 FSType: "/dev/disk3s8", 157 }, 158 { 159 Mountpoint: "/tmp", 160 FSType: "tmpfs", 161 }, 162 { 163 Mountpoint: "/else/othertmp", 164 FSType: "tmpfs", 165 }, 166 { 167 Mountpoint: "/else/othertmp/includeme", 168 FSType: "/dev/disk3s7", 169 }, 170 }, 171 want: []expect{ 172 { 173 // since /run is explicitly ignored, this should be skipped 174 path: "/run/somewhere/else", 175 wantErr: assertSkipErr(), 176 }, 177 { 178 path: "/run/terrafirma", 179 }, 180 { 181 path: "/run/terrafirma/nested", 182 }, 183 { 184 path: "/tmp", 185 }, 186 { 187 path: "/else/othertmp/includeme", 188 }, 189 { 190 path: "/else/othertmp/includeme/nested", 191 }, 192 { 193 // no mount path, so we should include it 194 path: "/somewhere/dev/includeme", 195 }, 196 { 197 // keep additional tmpfs mounts that are not explicitly ignored 198 path: "/else/othertmp", 199 }, 200 }, 201 }, 202 { 203 name: "ignore known trixy tmpfs paths", 204 root: "/", 205 mounts: []*mountinfo.Info{ 206 { 207 Mountpoint: "/", 208 FSType: "/dev/disk3s7", 209 }, 210 { 211 Mountpoint: "/dev", 212 FSType: "tmpfs", 213 }, 214 { 215 Mountpoint: "/run", 216 FSType: "tmpfs", 217 }, 218 { 219 Mountpoint: "/var/run", 220 FSType: "tmpfs", 221 }, 222 { 223 Mountpoint: "/var/lock", 224 FSType: "tmpfs", 225 }, 226 { 227 Mountpoint: "/sys", 228 FSType: "tmpfs", 229 }, 230 { 231 Mountpoint: "/tmp", 232 FSType: "tmpfs", 233 }, 234 }, 235 want: []expect{ 236 { 237 path: "/dev", 238 wantErr: assertSkipErr(), 239 }, 240 { 241 path: "/run", 242 wantErr: assertSkipErr(), 243 }, 244 { 245 path: "/var/run", 246 wantErr: assertSkipErr(), 247 }, 248 { 249 path: "/var/lock", 250 wantErr: assertSkipErr(), 251 }, 252 { 253 path: "/sys", 254 wantErr: assertSkipErr(), 255 }, 256 // show that we honor ignoring nested paths 257 { 258 path: "/sys/nested", 259 wantErr: assertSkipErr(), 260 }, 261 // show that paths outside of the known mountpoints are not skipped 262 { 263 path: "/stuff", 264 }, 265 // show that we allow other tmpfs paths that are not on the blocklist 266 { 267 path: "/tmp/allowed", 268 }, 269 // show sibling paths with same prefix (e.g. /sys vs /system) to that of not allowed paths are not skipped 270 { 271 path: "/system", 272 }, 273 }, 274 }, 275 { 276 name: "test unix paths", 277 mounts: unixSubject, 278 root: "/", 279 want: []expect{ 280 { 281 // relative path to proc is allowed 282 path: "proc/place", 283 }, 284 { 285 // relative path within proc is not allowed 286 path: "/proc/place", 287 wantErr: assertSkipErr(), 288 }, 289 { 290 // path exactly to proc is not allowed 291 path: "/proc", 292 wantErr: assertSkipErr(), 293 }, 294 { 295 // similar to proc 296 path: "/pro/c", 297 }, 298 { 299 // similar to proc 300 path: "/pro", 301 }, 302 { 303 // dev is not allowed 304 path: "/dev", 305 wantErr: assertSkipErr(), 306 }, 307 { 308 // sys is not allowed 309 path: "/sys", 310 wantErr: assertSkipErr(), 311 }, 312 }, 313 }, 314 { 315 name: "test unix paths with base", 316 mounts: unixSubject, 317 root: "/", 318 base: "/a/b/c", 319 want: []expect{ 320 { 321 // do not consider base when matching paths (non-matching) 322 path: "/a/b/c/dev", 323 }, 324 { 325 // do not consider base when matching paths (matching) 326 path: "/dev", 327 wantErr: assertSkipErr(), 328 }, 329 }, 330 }, 331 { 332 name: "mimic nixos setup", 333 root: "/", 334 mounts: []*mountinfo.Info{ 335 { 336 Mountpoint: "/", 337 FSType: "tmpfs", // this is an odd setup, but valid 338 }, 339 { 340 Mountpoint: "/home", 341 FSType: "/dev/disk3s7", 342 }, 343 }, 344 want: []expect{ 345 { 346 path: "/home/somewhere", 347 }, 348 { 349 path: "/home", 350 }, 351 { 352 path: "/somewhere", 353 }, 354 { 355 // still not allowed... 356 path: "/run", 357 wantErr: assertSkipErr(), 358 }, 359 }, 360 }, 361 { 362 name: "buildkit github ubuntu 22.04", 363 root: "/run/src/core/sbom", 364 mounts: []*mountinfo.Info{ 365 {Mountpoint: "/", FSType: "overlay"}, 366 {Mountpoint: "/proc", FSType: "proc"}, 367 {Mountpoint: "/dev", FSType: "tmpfs"}, 368 {Mountpoint: "/dev/pts", FSType: "devpts"}, 369 {Mountpoint: "/dev/shm", FSType: "tmpfs"}, 370 {Mountpoint: "/dev/mqueue", FSType: "mqueue"}, 371 {Mountpoint: "/sys", FSType: "sysfs"}, 372 {Mountpoint: "/etc/resolv.conf", FSType: "ext4"}, 373 {Mountpoint: "/etc/hosts", FSType: "ext4"}, 374 {Mountpoint: "/sys/fs/cgroup", FSType: "cgroup2"}, 375 {Mountpoint: "/run/out", FSType: "ext4"}, 376 {Mountpoint: "/run/src/core/sbom", FSType: "overlay"}, 377 {Mountpoint: "/tmp", FSType: "tmpfs"}, 378 {Mountpoint: "/dev/otel-grpc.sock", FSType: "overlay"}, 379 {Mountpoint: "/proc/bus", FSType: "proc"}, 380 {Mountpoint: "/proc/fs", FSType: "proc"}, 381 {Mountpoint: "/proc/irq", FSType: "proc"}, 382 {Mountpoint: "/proc/sys", FSType: "proc"}, 383 {Mountpoint: "/proc/sysrq-trigger", FSType: "proc"}, 384 {Mountpoint: "/proc/acpi", FSType: "tmpfs"}, 385 {Mountpoint: "/proc/kcore", FSType: "tmpfs"}, 386 {Mountpoint: "/proc/keys", FSType: "tmpfs"}, 387 {Mountpoint: "/proc/latency_stats", FSType: "tmpfs"}, 388 {Mountpoint: "/proc/timer_list", FSType: "tmpfs"}, 389 {Mountpoint: "/sys/firmware", FSType: "tmpfs"}, 390 {Mountpoint: "/proc/scsi", FSType: "tmpfs"}, 391 }, 392 want: []expect{ 393 { 394 path: "/run/src/core/sbom", 395 }, 396 }, 397 }, 398 } 399 for _, tt := range tests { 400 t.Run(tt.name, func(t *testing.T) { 401 if tt.base == "" { 402 tt.base = tt.root 403 } 404 405 require.NotEmpty(t, tt.want) 406 ps := newPathSkipperFromMounts(tt.root, tt.mounts) 407 408 for _, exp := range tt.want { 409 t.Run(exp.path, func(t *testing.T) { 410 411 got := ps.pathIndexVisitor(tt.base, exp.path, nil, nil) 412 if exp.wantErr == nil { 413 assert.NoError(t, got) 414 return 415 } 416 exp.wantErr(t, got) 417 418 }) 419 } 420 }) 421 } 422 } 423 424 func assertSkipErr() assert.ErrorAssertionFunc { 425 return assertErrorIs(fs.SkipDir) 426 } 427 428 func assertErrorIs(want error) assert.ErrorAssertionFunc { 429 return func(t assert.TestingT, got error, msgAndArgs ...interface{}) bool { 430 return assert.ErrorIs(t, got, want, msgAndArgs...) 431 } 432 }