anchore / anchore/syft

--exclude is not honored for paths reached via a symlink target

Open
#5,232 0 comments 0 reactions 0 assignees View on GitHub
bug
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.