--exclude is not honored for paths reached via a symlink target
- Dominant language
- Go
- Stars
- 9.6k
- Forks
- 954
- Avg merge
- 1d 5h
- Merged PRs (30d)
- 42
Description
**What happened**:
A directory excluded with `--exclude` is still indexed and cataloged when a symlink elsewhere points into it.
Exclusions are matched per-path against the paths being indexed and do not consider symlink resolution.
Ex: Matching `/boot` hits only the `/boot` directory entry itself.
Indexing can travel to `/boot/grub2/grub.cfg` via symlink on an un excluded path without ever visiting `/boot`.
**Steps to reproduce the issue**:
```
mkdir -p /tmp/repro/boot/grub2 /tmp/repro/etc
echo menuentry > /tmp/repro/boot/grub2/grub.cfg
ln -s ../boot/grub2/grub.cfg /tmp/repro/etc/grub2.cfg
syft scan dir:/tmp/repro --exclude ./boot -o json | grep grub
```
**Anything else we need to know?**:
Quick test in `syft/source/directorysource/` can show the failure:
```
func Test_excludeVsSymlinkTarget(t *testing.T) {
root, err := filepath.EvalSymlinks(t.TempDir())
require.NoError(t, err)
require.NoError(t, os.MkdirAll(filepath.Join(root, "boot", "grub2"), 0o755))
require.NoError(t, os.WriteFile(filepath.Join(root, "boot", "grub2", "grub.cfg"), []byte("menuentry"), 0o644))
require.NoError(t, os.MkdirAll(filepath.Join(root, "etc"), 0o755))
require.NoError(t, os.Symlink(filepath.Join("..", "boot", "grub2", "grub.cfg"), filepath.Join(root, "etc", "grub2.cfg")))
visitors, err := GetDirectoryExclusionFunctions(root, []string{"./boot"})
require.NoError(t, err)
res, err := fileresolver.NewFromDirectory(root, root, visitors...)
require.NoError(t, err)
var indexed []string
for l := range res.AllLocations(context.Background()) {
if strings.Contains(l.RealPath, "/boot/") {
indexed = append(indexed, l.RealPath)
}
}
require.Empty(t, indexed, "excluded paths were indexed anyway")
}
```
This was split out of #3258, which reported this alongside a fatal crash on the same setup.
The crash was a separate defect and was fixed by #5170. This half was not.
I think this is a design question rather than a one-line fix and should be discussed by the team.
This behavior can be seen in RHEL-family hosts, where `/etc/grub2.cfg` and `/etc/grub2-efi.cfg` are symlinks into /boot/grub2.
Contributor guide
Research direction
Start in syft/source/directorysource/ with GetDirectoryExclusionFunctions and the quick test described in the issue; trace how fileresolver.NewFromDirectory handles symlink targets and exclusions. Confirm the intended behavior with the team, then ensure the test passes with no /boot paths indexed through the symlink while preserving existing exclusion behavior.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- go
- Domain
- cli, tooling
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100