mirror of
https://github.com/anchore/syft.git
synced 2026-10-11 21:57:22 +02:00
ci: run the full unit suite on windows (#5370)
* ci: run the full unit suite on windows via make unit Drops the curated `unit:windows` subset; the windows job now bootstraps like linux and runs `make unit`. Exceptions get added as failures show up. Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * ci: skip fixture refresh for windows unit tests binny has no windows release, so the bootstrap action can't install the tools the fixture refresh needs. Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * test: skip linux container fixtures when docker can't run them Windows CI runners only run windows containers, so image fixtures go through `testutils.SkipWithoutLinuxContainers`. No behavior change on linux/mac. Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * fix: close leaked file handles and posix squashfs paths on windows - output writers are now closed on early returns in `scan`/`convert` (open handles block cleanup on windows) - `newSBOMMultiWriter` used `path.Dir` on host paths, so output subdirectories were never created on windows - squashfs walk paths were built with `filepath.Join` - skip a few posix-only test cases (dir perms, xdg fallback, go-rpmdb ndb handle leak upstream) Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * fix(golang): resolve go source module dir natively on windows - `parseGoModFile` passed the resolver's posix (`/c/...`) path to the go toolchain, so go source resolution silently found nothing on windows - skip container-built fixtures (go archs, java builds) and golden image tarballs on windows; stereoscope's layer cache filenames contain `:`, which windows rejects Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * fix: keep posix paths posix on windows - `ChrootContext.ToChrootPath` nested volumes (`D:\cwd\C:\root`) when the scan root was on another drive, so the prefix was never trimmed - bitnami, python wheel/egg, and alpm parsers used `filepath` on posix location paths - alpm backup-file lookups silently missed on windows Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * ci: pull the test fixture cache for windows unit tests Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * fix: don't map virtual file tree paths through the host on windows `FiletreeResolver` without a chroot (snaps) ran request paths through `filepath.Abs`, rooting them on the current volume (`/payload.txt` -> `/d/payload.txt`), so snap scans on windows found nothing. Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * fix: match tar entry globs as posix on windows - `matchesAnyGlob` used `doublestar.PathMatch` (host separator) on tar entry names, so tar-wrapped java archives silently produced nothing on windows - test helpers now treat binary snippet keys and `FromFile` locations as posix Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * incorporate windows path fix Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * incorporate go-rpmdb close fix Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * close squashfs fixture writers before finalizing - windows dir listings can report a zero size for files still open, so the manifest was packed empty (also adds a temporary windows diagnostic for the binary cataloger) Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * temp: surface windows binary cataloger diagnostics Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * fix: keep directory symlink targets posix on windows - the directory indexer joined symlink targets with native separators, so links in the posix file tree pointed nowhere and globs on link names found nothing - binary fixture logical keys used native separators, so nested snippets were picked as the fixture dir on windows Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * keep addSymlinkToIndex under the funlen limit Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * drop windows skips covered by the stereoscope and go-rpmdb bumps 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:
@@ -96,16 +96,24 @@ jobs:
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
# go-make's setup only (not ./.github/actions/bootstrap): no apt, binny tools, or fixture cache are needed
|
||||
# go-make's setup only (not ./.github/actions/bootstrap): binny ships no windows release, so binny tools
|
||||
# are unavailable here (`make unit` skips the fixture refresh on windows)
|
||||
- name: Setup go + go-make tooling
|
||||
uses: anchore/go-make/.github/actions/setup@fa5421b3bf24c9d18f11a736c5eb4172f2b7d683 # v0.8.1
|
||||
with:
|
||||
# keep in sync with the go-version default in ./.github/actions/bootstrap
|
||||
go-version: "1.26.2"
|
||||
|
||||
# the full unit suite leans on linux docker fixtures, so this runs the windows-relevant subset
|
||||
# the binny-free version of `make download-test-fixture-cache` (keep the oras version in sync with .binny.yaml)
|
||||
- name: Download test fixture cache
|
||||
shell: bash
|
||||
run: |
|
||||
curl -sSfL https://github.com/oras-project/oras/releases/download/v1.3.4/oras_1.3.4_windows_amd64.zip -o "$RUNNER_TEMP/oras.zip"
|
||||
unzip -q "$RUNNER_TEMP/oras.zip" oras.exe -d "$RUNNER_TEMP"
|
||||
"$RUNNER_TEMP/oras.exe" pull ghcr.io/anchore/oss-cache/syft-test-fixture-cache:v2
|
||||
|
||||
- name: Run unit tests
|
||||
run: make unit:windows
|
||||
run: make unit
|
||||
|
||||
Integration-Test:
|
||||
# Note: changing this job name requires making the same update in the .github/workflows/release.yaml pipeline
|
||||
|
||||
+10
-19
@@ -1,7 +1,6 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
@@ -97,23 +96,6 @@ func main() {
|
||||
},
|
||||
},
|
||||
|
||||
// windows unit tests: deliberately not hooked into "test" (no RunsOn). The full unit suite leans on
|
||||
// linux docker fixture images and posix-only assumptions, so this covers the packages that own
|
||||
// host path handling (where windows bugs live) and skips the tests that need docker image fixtures.
|
||||
// ponytail: curated package list, widen it as more of the suite is made windows-safe.
|
||||
Task{
|
||||
Name: "unit:windows",
|
||||
Description: "run the windows-relevant subset of unit tests",
|
||||
Run: func() {
|
||||
Run("go test -count=1"+
|
||||
` -skip "Image|Squash|AllLayers|MixFileTypes|GivenCoordinates"`+
|
||||
" ./syft/internal/windows/... ./syft/internal/fileresolver/... ./syft/source/directorysource/... ./syft/source/filesource/..."+
|
||||
" ./syft/file ./syft/file/cataloger/filedigest/... ./syft/file/cataloger/filecontent/...",
|
||||
run.Stdout(os.Stderr),
|
||||
)
|
||||
},
|
||||
},
|
||||
|
||||
// default validation pipeline (replaces Taskfile `default`/`pr-validations`/`validations`).
|
||||
Task{
|
||||
Name: "default",
|
||||
@@ -157,7 +139,7 @@ func main() {
|
||||
Task{
|
||||
Name: "unit:syft",
|
||||
RunsOn: lang.List("unit"),
|
||||
Dependencies: Deps("refresh-fixtures"),
|
||||
Dependencies: unitFixtureDeps(),
|
||||
},
|
||||
Task{
|
||||
Name: "clean:syft",
|
||||
@@ -185,6 +167,15 @@ func raceEnabled() bool {
|
||||
return config.CI && !config.Windows
|
||||
}
|
||||
|
||||
// unitFixtureDeps skips the fixture cache refresh on windows: it needs binny-managed tools (binny ships
|
||||
// no windows release) and linux docker, so fixture-backed tests there have to skip themselves instead.
|
||||
func unitFixtureDeps() []string {
|
||||
if config.Windows {
|
||||
return nil
|
||||
}
|
||||
return Deps("refresh-fixtures")
|
||||
}
|
||||
|
||||
// race applies raceEnabled() to a gotest suite. gotest exposes no functional option
|
||||
// for the race detector, but gotest.Config.Race is exported.
|
||||
func race() gotest.Option {
|
||||
|
||||
@@ -114,6 +114,10 @@ func RunConvert(ctx context.Context, opts *ConvertOptions, userInput string) err
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// output files are opened up front; release them if we bail out before writing
|
||||
if c, ok := writer.(io.Closer); ok {
|
||||
defer func() { _ = c.Close() }()
|
||||
}
|
||||
}
|
||||
|
||||
for _, output := range unchanged {
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
@@ -181,6 +182,10 @@ func runScan(ctx context.Context, id clio.Identification, opts *scanOptions, use
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// output files are opened up front; release them if we bail out before writing
|
||||
if c, ok := writer.(io.Closer); ok {
|
||||
defer func() { _ = c.Close() }()
|
||||
}
|
||||
|
||||
sources := opts.From
|
||||
if len(sources) == 0 {
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -22,9 +23,10 @@ func Test_defaultDir(t *testing.T) {
|
||||
homeDir := filepath.Join(tmpDir, "fake-home")
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
env map[string]string
|
||||
expected string
|
||||
name string
|
||||
env map[string]string
|
||||
expected string
|
||||
skipOnWindows bool
|
||||
}{
|
||||
{
|
||||
name: "no-xdg",
|
||||
@@ -32,6 +34,8 @@ func Test_defaultDir(t *testing.T) {
|
||||
"HOME": homeDir,
|
||||
},
|
||||
expected: homeDir,
|
||||
// xdg falls back to LOCALAPPDATA on windows, not HOME
|
||||
skipOnWindows: true,
|
||||
},
|
||||
{
|
||||
name: "xdg-cache",
|
||||
@@ -59,6 +63,9 @@ func Test_defaultDir(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
if test.skipOnWindows && runtime.GOOS == "windows" {
|
||||
t.Skip("posix-only cache dir fallback")
|
||||
}
|
||||
restoreCache(t)
|
||||
|
||||
for k, v := range test.env {
|
||||
|
||||
@@ -2,10 +2,11 @@ package options
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
@@ -20,7 +21,10 @@ import (
|
||||
"github.com/anchore/syft/syft/sbom"
|
||||
)
|
||||
|
||||
var _ sbom.Writer = (*sbomMultiWriter)(nil)
|
||||
var _ interface {
|
||||
io.Closer
|
||||
sbom.Writer
|
||||
} = (*sbomMultiWriter)(nil)
|
||||
|
||||
var _ interface {
|
||||
io.Closer
|
||||
@@ -164,6 +168,12 @@ func newSBOMMultiWriter(options ...sbomWriterDescription) (_ *sbomMultiWriter, e
|
||||
}
|
||||
|
||||
out := &sbomMultiWriter{}
|
||||
defer func() {
|
||||
// don't leak the files already opened when a later output fails
|
||||
if err != nil {
|
||||
_ = out.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
for _, option := range options {
|
||||
switch len(option.Path) {
|
||||
@@ -172,8 +182,8 @@ func newSBOMMultiWriter(options ...sbomWriterDescription) (_ *sbomMultiWriter, e
|
||||
format: option.Format,
|
||||
})
|
||||
default:
|
||||
// create any missing subdirectories
|
||||
dir := path.Dir(option.Path)
|
||||
// create any missing subdirectories (option.Path is a host path, so this must be filepath)
|
||||
dir := filepath.Dir(option.Path)
|
||||
if dir != "" {
|
||||
s, err := os.Stat(dir)
|
||||
if err != nil {
|
||||
@@ -210,6 +220,20 @@ func (m *sbomMultiWriter) Write(s sbom.SBOM) (errs error) {
|
||||
return errs
|
||||
}
|
||||
|
||||
// Close releases any files held by the writers. Write already closes each file it writes to, so this matters
|
||||
// when the SBOM is never written (e.g. an error after the writer was created).
|
||||
func (m *sbomMultiWriter) Close() error {
|
||||
var errs error
|
||||
for _, w := range m.writers {
|
||||
if c, ok := w.(io.Closer); ok {
|
||||
if err := c.Close(); err != nil && !errors.Is(err, os.ErrClosed) {
|
||||
errs = multierror.Append(errs, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
// sbomStreamWriter implements sbom.Writer for a given format and io.Writer, also providing a close function for cleanup
|
||||
type sbomStreamWriter struct {
|
||||
format sbom.FormatEncoder
|
||||
|
||||
@@ -202,9 +202,9 @@ func Test_newSBOMMultiWriter(t *testing.T) {
|
||||
if test.err {
|
||||
assert.Error(t, err)
|
||||
return
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
require.NoError(t, err)
|
||||
t.Cleanup(func() { _ = mw.Close() })
|
||||
|
||||
assert.Len(t, mw.writers, len(test.expected))
|
||||
|
||||
|
||||
@@ -18,11 +18,11 @@ require (
|
||||
github.com/anchore/go-homedir v0.1.1
|
||||
github.com/anchore/go-logger v0.2.0
|
||||
github.com/anchore/go-macholibre v0.1.1
|
||||
github.com/anchore/go-rpmdb v0.2.0
|
||||
github.com/anchore/go-rpmdb v0.2.2-0.20261002172926-a488674d6c87
|
||||
github.com/anchore/go-sync v0.1.2
|
||||
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b
|
||||
github.com/anchore/packageurl-go v0.2.0
|
||||
github.com/anchore/stereoscope v0.3.3
|
||||
github.com/anchore/stereoscope v0.3.4-0.20261002170525-59d3a69590a6
|
||||
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be
|
||||
github.com/aquasecurity/go-pep440-version v0.0.1
|
||||
github.com/bitnami/go-version v0.0.0-20250131085805-b1f57a8634ef
|
||||
|
||||
@@ -140,8 +140,8 @@ github.com/anchore/go-lzo v0.1.1 h1:IwL/fvkdtlIrYIXck6WxZ3nb8WjjHziYYmGxlooyOnM=
|
||||
github.com/anchore/go-lzo v0.1.1/go.mod h1:3kLx0bve2oN1iDwgM1U5zGku1Tfbdb0No5qp1eL1fIk=
|
||||
github.com/anchore/go-macholibre v0.1.1 h1:sTBHi3S1WVIOCamc6NyT8Sn9mBmD30DCTfjfVm4k6/M=
|
||||
github.com/anchore/go-macholibre v0.1.1/go.mod h1:YNq2610RlvGCK0Za+Klz/OCMtUaEnwwAGA/VfUAQaro=
|
||||
github.com/anchore/go-rpmdb v0.2.0 h1:ixcyMg/guhBL9XjBVwhTv9CG48xO07Qkg2uDX2+zx/c=
|
||||
github.com/anchore/go-rpmdb v0.2.0/go.mod h1:ATsRlpCXstnoYfzqBfhwGw0U2dolx1BBQ9rAU8jWBAw=
|
||||
github.com/anchore/go-rpmdb v0.2.2-0.20261002172926-a488674d6c87 h1:aY7DcEkjgzMRbCCJsGHu8eQeL5I88V8G9ISEWtfrlCU=
|
||||
github.com/anchore/go-rpmdb v0.2.2-0.20261002172926-a488674d6c87/go.mod h1:Y64EbetdqhryMjtk8IzXkdCQw/aNDx+feZCT9aj6rjs=
|
||||
github.com/anchore/go-struct-converter v0.2.0-rc2 h1:q+859fW2/jbHJHB2etbNfRlFwYpknyvbqqk1hUdamQ4=
|
||||
github.com/anchore/go-struct-converter v0.2.0-rc2/go.mod h1:cDBA5vhcR62nXWo8QH9/Kk2807o65ISaHPNPX66L+Uw=
|
||||
github.com/anchore/go-sync v0.1.2 h1:RDD5RCanrWqhbtBulqPSZpERadUlgcYjpybYzYd3Uk8=
|
||||
@@ -150,8 +150,8 @@ github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b h1:e1bmaoJfZV
|
||||
github.com/anchore/go-version v1.2.2-0.20200701162849-18adb9c92b9b/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E=
|
||||
github.com/anchore/packageurl-go v0.2.0 h1:CkrM4RMUwrEGAiE1OVlxaZNzWj0TuHRey7o4T/EAErk=
|
||||
github.com/anchore/packageurl-go v0.2.0/go.mod h1:2JCgOQMIsqZ7TmliXG4PnUthPJAKE3mWQbsW2XHjAOE=
|
||||
github.com/anchore/stereoscope v0.3.3 h1:sur8G2THAOnOrSXDjIc1SXzQwco8hjFBtGFq1jFvNVA=
|
||||
github.com/anchore/stereoscope v0.3.3/go.mod h1:LxL4iOFtB8XMbA1MykUUTkmtQmDuYdbr4+TkBkCfljU=
|
||||
github.com/anchore/stereoscope v0.3.4-0.20261002170525-59d3a69590a6 h1:6l3YwREGEXX/w4tBhRcPb7efYWo4pf8+fmAy0mND4EY=
|
||||
github.com/anchore/stereoscope v0.3.4-0.20261002170525-59d3a69590a6/go.mod h1:LxL4iOFtB8XMbA1MykUUTkmtQmDuYdbr4+TkBkCfljU=
|
||||
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
|
||||
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
|
||||
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
|
||||
@@ -428,8 +428,8 @@ github.com/gkampitakis/ciinfo v0.3.4 h1:5eBSibVuSMbb/H6Elc0IIEFbkzCJi3lm94n0+U7Z
|
||||
github.com/gkampitakis/ciinfo v0.3.4/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo=
|
||||
github.com/gkampitakis/go-snaps v0.5.23 h1:okh5QR48zpUjpWtu65AtqxdCY8huJq+dEDuUzd1PuKg=
|
||||
github.com/gkampitakis/go-snaps v0.5.23/go.mod h1:uy3lVzCCRRsAwYqSocyw5fY8xRLCYEfqoOJNxr8HonM=
|
||||
github.com/glebarez/go-sqlite v1.20.3 h1:89BkqGOXR9oRmG58ZrzgoY/Fhy5x0M+/WV48U5zVrZ4=
|
||||
github.com/glebarez/go-sqlite v1.20.3/go.mod h1:u3N6D/wftiAzIOJtZl6BmedqxmmkDfH3q+ihjqxC9u0=
|
||||
github.com/glebarez/go-sqlite v1.23.0 h1:FyhIq4jqmgphQAUlY79zPldYGwISEZikaDfhiGWkkaI=
|
||||
github.com/glebarez/go-sqlite v1.23.0/go.mod h1:IIYrOH3L0rHY3jb4IXOHoWdklNajSGUN2eJcvK8WrnI=
|
||||
github.com/gliderlabs/ssh v0.3.8 h1:a4YXD1V7xMF9g5nTkdfnja3Sxy1PVDCj1Zg4Wb8vY6c=
|
||||
github.com/gliderlabs/ssh v0.3.8/go.mod h1:xYoytBv1sV0aL3CavoDuJIQNURXkkfPA/wxQ1pL1fAU=
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
|
||||
|
||||
Vendored
+4
@@ -6,6 +6,7 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@@ -95,6 +96,9 @@ func Test_makeDiskKey(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_errors(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
t.Skip("windows ignores unix permission bits on directories")
|
||||
}
|
||||
tmp := t.TempDir()
|
||||
cache := filepath.Join(tmp, "cache")
|
||||
// make a non-writable directory
|
||||
|
||||
@@ -212,7 +212,7 @@ func parseGenericCatalogerFunction(funcDecl *ast.FuncDecl, filePath, repoRoot st
|
||||
// extractPackageNameFromPath extracts the package name from a cataloger source file path
|
||||
// e.g., "syft/pkg/cataloger/swift/cataloger.go" -> "swift"
|
||||
func extractPackageNameFromPath(filePath string) string {
|
||||
parts := strings.Split(filePath, string(filepath.Separator))
|
||||
parts := strings.Split(filepath.ToSlash(filePath), "/")
|
||||
|
||||
// find the LAST occurrence of "cataloger" in the path
|
||||
// (to handle test fixtures with multiple "cataloger" segments)
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"go/ast"
|
||||
"go/parser"
|
||||
"go/token"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -337,7 +338,7 @@ func TestResolveImportPath(t *testing.T) {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := resolveImportPath(tt.importPath, testRepoRoot)
|
||||
require.Equal(t, tt.want, got)
|
||||
require.Equal(t, filepath.FromSlash(tt.want), got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"errors"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/diskfs/go-diskfs/filesystem"
|
||||
@@ -25,7 +25,8 @@ func WalkDiskDir(fsys filesystem.FileSystem, root string, fn WalkDiskDirFunc) er
|
||||
}
|
||||
|
||||
for _, info := range infos {
|
||||
p := filepath.Join(root, info.Name())
|
||||
// squashfs paths are posix regardless of host os
|
||||
p := path.Join(root, info.Name())
|
||||
err = walkDiskDir(fsys, p, info, fn)
|
||||
if err != nil {
|
||||
if errors.Is(err, fs.SkipDir) {
|
||||
@@ -74,7 +75,7 @@ func walkDiskDir(fsys filesystem.FileSystem, name string, d os.FileInfo, walkDir
|
||||
}
|
||||
|
||||
for _, d1 := range dirs {
|
||||
name1 := filepath.Join(name, d1.Name())
|
||||
name1 := path.Join(name, d1.Name())
|
||||
if err := walkDiskDir(fsys, name1, d1, walkDirFn); err != nil {
|
||||
if errors.Is(err, fs.SkipDir) {
|
||||
break
|
||||
|
||||
@@ -19,6 +19,8 @@ func createTestFS(t *testing.T) filesystem.FileSystem {
|
||||
filename := "test.squashfs"
|
||||
f, err := os.Create(filepath.Join(dir, filename))
|
||||
require.NoError(t, err)
|
||||
// windows can't remove the temp dir while the backing file is open
|
||||
t.Cleanup(func() { _ = f.Close() })
|
||||
|
||||
b := file.New(f, false)
|
||||
fsys, err := squashfs.Create(b, 0, 0, 4096)
|
||||
|
||||
@@ -84,7 +84,8 @@ func ExtractGlobsFromTarToUniqueTempFile(ctx context.Context, archivePath, dir s
|
||||
|
||||
func matchesAnyGlob(name string, globs ...string) bool {
|
||||
for _, glob := range globs {
|
||||
if matches, err := doublestar.PathMatch(glob, name); err == nil && matches {
|
||||
// tar entry names are always posix, so don't match with the host separator (PathMatch)
|
||||
if matches, err := doublestar.Match(glob, name); err == nil && matches {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/anchore/syft/internal/os"
|
||||
"github.com/anchore/syft/internal/sbomsync"
|
||||
"github.com/anchore/syft/internal/task"
|
||||
"github.com/anchore/syft/internal/testutils"
|
||||
"github.com/anchore/syft/syft"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/linux"
|
||||
@@ -84,6 +85,7 @@ func Test_EnvironmentTask(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
tarPath := imagetest.GetFixtureImageTarPath(t, test.name)
|
||||
|
||||
// get the source
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package testutils
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var dockerServerOS = sync.OnceValue(func() string {
|
||||
out, err := exec.Command("docker", "version", "--format", "{{.Server.Os}}").Output()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return strings.TrimSpace(string(out))
|
||||
})
|
||||
|
||||
// SkipWithoutLinuxContainers skips tests whose fixtures are linux container images (built or run via docker).
|
||||
// This only kicks in on windows, where the docker daemon is often running windows containers (or is absent),
|
||||
// so the linux fixture images can't be built. Everywhere else the fixture tooling is left to fail loudly.
|
||||
func SkipWithoutLinuxContainers(t testing.TB) {
|
||||
t.Helper()
|
||||
if runtime.GOOS != "windows" {
|
||||
return
|
||||
}
|
||||
if os := dockerServerOS(); os != "linux" {
|
||||
t.Skipf("linux container fixtures unavailable (docker server os=%q)", os)
|
||||
}
|
||||
}
|
||||
@@ -265,8 +265,9 @@ func TestLazyCreation(t *testing.T) {
|
||||
// root dir is not created until needed
|
||||
assert.Equal(t, "", td.root)
|
||||
|
||||
_, _, err := td.NewFile("trigger-*")
|
||||
f, _, err := td.NewFile("trigger-*")
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, f.Close())
|
||||
|
||||
assert.NotEmpty(t, td.root)
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
stereoscopeFile "github.com/anchore/stereoscope/pkg/file"
|
||||
"github.com/anchore/stereoscope/pkg/imagetest"
|
||||
intFile "github.com/anchore/syft/internal/file"
|
||||
"github.com/anchore/syft/internal/testutils"
|
||||
"github.com/anchore/syft/internal/unknown"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
@@ -92,6 +93,7 @@ func TestDigestsCataloger(t *testing.T) {
|
||||
func TestDigestsCataloger_MixFileTypes(t *testing.T) {
|
||||
testImage := "image-file-type-mix"
|
||||
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", testImage)
|
||||
|
||||
src := stereoscopesource.New(img, stereoscopesource.ImageConfig{
|
||||
@@ -162,6 +164,7 @@ func TestDigestsCataloger_MixFileTypes(t *testing.T) {
|
||||
func TestFileDigestCataloger_GivenCoordinates(t *testing.T) {
|
||||
testImage := "image-file-type-mix"
|
||||
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", testImage)
|
||||
|
||||
c := NewCataloger([]crypto.Hash{crypto.SHA256})
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
stereoscopeFile "github.com/anchore/stereoscope/pkg/file"
|
||||
"github.com/anchore/stereoscope/pkg/imagetest"
|
||||
"github.com/anchore/syft/internal/testutils"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
"github.com/anchore/syft/syft/source/stereoscopesource"
|
||||
@@ -18,6 +19,7 @@ import (
|
||||
func TestFileMetadataCataloger(t *testing.T) {
|
||||
testImage := "image-file-type-mix"
|
||||
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", testImage)
|
||||
|
||||
c := NewCataloger()
|
||||
@@ -157,6 +159,7 @@ func TestFileMetadataCataloger(t *testing.T) {
|
||||
func TestFileMetadataCataloger_GivenCoordinates(t *testing.T) {
|
||||
testImage := "image-file-type-mix"
|
||||
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", testImage)
|
||||
|
||||
c := NewCataloger()
|
||||
|
||||
@@ -2,6 +2,7 @@ package internal
|
||||
|
||||
import (
|
||||
"context"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
@@ -10,6 +11,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/stereoscope/pkg/imagetest"
|
||||
"github.com/anchore/syft/internal/testutils"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
"github.com/anchore/syft/syft/source/directorysource"
|
||||
@@ -19,15 +21,16 @@ import (
|
||||
func Test_allRegularFiles(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
setup func() file.Resolver
|
||||
setup func(t *testing.T) file.Resolver
|
||||
wantRealPaths *strset.Set
|
||||
wantAccessPaths *strset.Set
|
||||
}{
|
||||
{
|
||||
name: "image",
|
||||
setup: func() file.Resolver {
|
||||
setup: func(t *testing.T) file.Resolver {
|
||||
testImage := "image-file-type-mix"
|
||||
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", testImage)
|
||||
|
||||
s := stereoscopesource.New(img, stereoscopesource.ImageConfig{
|
||||
@@ -44,7 +47,12 @@ func Test_allRegularFiles(t *testing.T) {
|
||||
},
|
||||
{
|
||||
name: "directory",
|
||||
setup: func() file.Resolver {
|
||||
setup: func(t *testing.T) file.Resolver {
|
||||
if runtime.GOOS == "windows" {
|
||||
// file symlinks from a git checkout don't index as access paths on windows yet (same gap the
|
||||
// fileresolver symlink fixtures skip for)
|
||||
t.Skip("fixture relies on symlinks")
|
||||
}
|
||||
s, err := directorysource.NewFromPath("testdata/symlinked-root/nested/link-root")
|
||||
require.NoError(t, err)
|
||||
r, err := s.FileResolver(source.SquashedScope)
|
||||
@@ -57,7 +65,7 @@ func Test_allRegularFiles(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
resolver := tt.setup()
|
||||
resolver := tt.setup(t)
|
||||
locations := AllRegularFiles(context.Background(), resolver)
|
||||
realLocations := strset.New()
|
||||
virtualLocations := strset.New()
|
||||
|
||||
@@ -2,6 +2,7 @@ package github
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/anchore/syft/syft/format/internal/testutil"
|
||||
@@ -11,6 +12,10 @@ var updateSnapshot = flag.Bool("update-github", false, "update the *.golden file
|
||||
var updateImage = flag.Bool("update-image", false, "update the golden image used for image encoder testing")
|
||||
|
||||
func TestGithubDirectoryEncoder(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
// the golden snapshot embeds the posix source path (and the source ID derived from it)
|
||||
t.Skip("directory snapshot is posix path specific")
|
||||
}
|
||||
dir := t.TempDir()
|
||||
testutil.AssertEncoderAgainstGoldenSnapshot(t,
|
||||
testutil.EncoderSnapshotTestConfig{
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/anchore/stereoscope/pkg/filetree"
|
||||
"github.com/anchore/stereoscope/pkg/image"
|
||||
"github.com/anchore/stereoscope/pkg/imagetest"
|
||||
"github.com/anchore/syft/internal/testutils"
|
||||
"github.com/anchore/syft/syft/cpe"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/linux"
|
||||
@@ -35,6 +36,7 @@ func ImageInput(t testing.TB, testImage string, options ...ImageOption) sbom.SBO
|
||||
case true:
|
||||
img = imagetest.GetGoldenFixtureImage(t, testImage)
|
||||
default:
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img = imagetest.GetFixtureImage(t, "docker-archive", testImage)
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"flag"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -90,6 +91,10 @@ func TestEscapeHTML(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestDirectoryEncoder(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
// the golden snapshot embeds the posix source path (and the source ID derived from it)
|
||||
t.Skip("directory snapshot is posix path specific")
|
||||
}
|
||||
cfg := DefaultEncoderConfig()
|
||||
cfg.Pretty = true
|
||||
enc, err := NewFormatEncoderWithConfig(cfg)
|
||||
|
||||
@@ -2,6 +2,7 @@ package text
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"runtime"
|
||||
"testing"
|
||||
|
||||
"github.com/anchore/syft/syft/format/internal/testutil"
|
||||
@@ -11,6 +12,10 @@ var updateSnapshot = flag.Bool("update-text", false, "update the *.golden files
|
||||
var updateImage = flag.Bool("update-image", false, "update the golden image used for image encoder testing")
|
||||
|
||||
func TestTextDirectoryEncoder(t *testing.T) {
|
||||
if runtime.GOOS == "windows" {
|
||||
// the golden snapshot embeds the posix source path (and the source ID derived from it)
|
||||
t.Skip("directory snapshot is posix path specific")
|
||||
}
|
||||
dir := t.TempDir()
|
||||
testutil.AssertEncoderAgainstGoldenSnapshot(t,
|
||||
testutil.EncoderSnapshotTestConfig{
|
||||
|
||||
@@ -210,8 +210,13 @@ func (r ChrootContext) ToChrootPath(nativePath string) string {
|
||||
prefix = r.base
|
||||
} else {
|
||||
// we need to account for the cwd relative to the running process and the given root for the directory resolver
|
||||
prefix = filepath.Clean(filepath.Join(r.cwd, r.cwdRelativeToRoot))
|
||||
prefix += string(filepath.Separator)
|
||||
prefix = r.cwdRelativeToRoot
|
||||
// on windows an absolute root stays absolute here (see ChangeDirectory), and joining it onto the cwd
|
||||
// would nest one volume inside another (D:\cwd\C:\root)
|
||||
if !filepath.IsAbs(prefix) {
|
||||
prefix = filepath.Join(r.cwd, prefix)
|
||||
}
|
||||
prefix = filepath.Clean(prefix) + string(filepath.Separator)
|
||||
}
|
||||
responsePath = strings.TrimPrefix(responsePath, prefix)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
package fileresolver
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// windows CI runs from D: while t.TempDir() lives on C:, so the root and cwd don't share a volume
|
||||
func TestChrootContext_ToChrootPath_rootOnAnotherVolume(t *testing.T) {
|
||||
ctx := ChrootContext{
|
||||
root: `C:\scan\root`,
|
||||
cwd: `D:\work`,
|
||||
cwdRelativeToRoot: `C:\scan\root`,
|
||||
}
|
||||
assert.Equal(t, "dir/file.txt", ctx.ToChrootPath("/c/scan/root/dir/file.txt"))
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/stereoscope/pkg/imagetest"
|
||||
"github.com/anchore/syft/internal/testutils"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
)
|
||||
|
||||
@@ -91,6 +92,7 @@ func TestAllLayersResolver_FilesByPath(t *testing.T) {
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks")
|
||||
|
||||
resolver, err := NewFromContainerImageAllLayers(img)
|
||||
@@ -201,6 +203,7 @@ func TestAllLayersResolver_FilesByGlob(t *testing.T) {
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks")
|
||||
|
||||
resolver, err := NewFromContainerImageAllLayers(img)
|
||||
@@ -249,6 +252,7 @@ func Test_imageAllLayersResolver_FilesByMIMEType(t *testing.T) {
|
||||
}
|
||||
for _, test := range tests {
|
||||
t.Run(test.fixtureName, func(t *testing.T) {
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", test.fixtureName)
|
||||
|
||||
resolver, err := NewFromContainerImageAllLayers(img)
|
||||
@@ -266,6 +270,7 @@ func Test_imageAllLayersResolver_FilesByMIMEType(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_imageAllLayersResolver_hasFilesystemIDInLocation(t *testing.T) {
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", "image-duplicate-path")
|
||||
|
||||
resolver, err := NewFromContainerImageAllLayers(img)
|
||||
@@ -326,6 +331,7 @@ func TestAllLayersImageResolver_FilesContents(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks")
|
||||
|
||||
resolver, err := NewFromContainerImageAllLayers(img)
|
||||
@@ -353,6 +359,7 @@ func TestAllLayersImageResolver_FilesContents(t *testing.T) {
|
||||
|
||||
func TestAllLayersImageResolver_FilesContents_errorOnDirRequest(t *testing.T) {
|
||||
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks")
|
||||
|
||||
resolver, err := NewFromContainerImageAllLayers(img)
|
||||
@@ -500,6 +507,7 @@ func Test_imageAllLayersResolver_resolvesLinks(t *testing.T) {
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks")
|
||||
|
||||
resolver, err := NewFromContainerImageAllLayers(img)
|
||||
@@ -514,6 +522,7 @@ func Test_imageAllLayersResolver_resolvesLinks(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAllLayersResolver_AllLocations(t *testing.T) {
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", "image-files-deleted")
|
||||
|
||||
resolver, err := NewFromContainerImageAllLayers(img)
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"go.uber.org/goleak"
|
||||
|
||||
"github.com/anchore/stereoscope/pkg/imagetest"
|
||||
"github.com/anchore/syft/internal/testutils"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
)
|
||||
|
||||
@@ -115,6 +116,7 @@ func Test_ContainerImageDeepSquash_FilesByPath(t *testing.T) {
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks")
|
||||
|
||||
resolver, err := NewFromContainerImageDeepSquash(img)
|
||||
@@ -231,6 +233,7 @@ func Test_ContainerImageDeepSquash_FilesByGlob(t *testing.T) {
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks")
|
||||
|
||||
resolver, err := NewFromContainerImageDeepSquash(img)
|
||||
@@ -288,6 +291,7 @@ func Test_ContainerImageDeepSquash_FilesByMIMEType(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.fixtureName, func(t *testing.T) {
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", test.fixtureName)
|
||||
|
||||
resolver, err := NewFromContainerImageDeepSquash(img)
|
||||
@@ -305,6 +309,7 @@ func Test_ContainerImageDeepSquash_FilesByMIMEType(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_ContainerImageDeepSquash_hasFilesystemIDInLocation(t *testing.T) {
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", "image-duplicate-path")
|
||||
|
||||
resolver, err := NewFromContainerImageDeepSquash(img)
|
||||
@@ -365,6 +370,7 @@ func Test_ContainerImageDeepSquash_FilesContents(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks")
|
||||
|
||||
resolver, err := NewFromContainerImageDeepSquash(img)
|
||||
@@ -389,6 +395,7 @@ func Test_ContainerImageDeepSquash_FilesContents(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_ContainerImageDeepSquash_FilesContents_errorOnDirRequest(t *testing.T) {
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks")
|
||||
|
||||
resolver, err := NewFromContainerImageDeepSquash(img)
|
||||
@@ -557,6 +564,7 @@ func Test_ContainerImageDeepSquash_resolvesLinks(t *testing.T) {
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks")
|
||||
|
||||
resolver, err := NewFromContainerImageDeepSquash(img)
|
||||
@@ -571,6 +579,7 @@ func Test_ContainerImageDeepSquash_resolvesLinks(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_ContainerImageDeepSquash_AllLocations(t *testing.T) {
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", "image-files-deleted")
|
||||
|
||||
resolver, err := NewFromContainerImageDeepSquash(img)
|
||||
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
|
||||
stereoscopeFile "github.com/anchore/stereoscope/pkg/file"
|
||||
"github.com/anchore/stereoscope/pkg/imagetest"
|
||||
"github.com/anchore/syft/internal/testutils"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
)
|
||||
|
||||
@@ -75,6 +76,7 @@ func TestImageSquashResolver_FilesByPath(t *testing.T) {
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks")
|
||||
|
||||
resolver, err := NewFromContainerImageSquash(img)
|
||||
@@ -184,6 +186,7 @@ func TestImageSquashResolver_FilesByGlob(t *testing.T) {
|
||||
}
|
||||
for _, c := range cases {
|
||||
t.Run(c.name, func(t *testing.T) {
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks")
|
||||
|
||||
resolver, err := NewFromContainerImageSquash(img)
|
||||
@@ -241,6 +244,7 @@ func Test_imageSquashResolver_FilesByMIMEType(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.fixtureName, func(t *testing.T) {
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", test.fixtureName)
|
||||
|
||||
resolver, err := NewFromContainerImageSquash(img)
|
||||
@@ -258,6 +262,7 @@ func Test_imageSquashResolver_FilesByMIMEType(t *testing.T) {
|
||||
}
|
||||
|
||||
func Test_imageSquashResolver_hasFilesystemIDInLocation(t *testing.T) {
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", "image-duplicate-path")
|
||||
|
||||
resolver, err := NewFromContainerImageSquash(img)
|
||||
@@ -316,6 +321,7 @@ func TestSquashImageResolver_FilesContents(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks")
|
||||
|
||||
resolver, err := NewFromContainerImageSquash(img)
|
||||
@@ -341,6 +347,7 @@ func TestSquashImageResolver_FilesContents(t *testing.T) {
|
||||
|
||||
func TestSquashImageResolver_FilesContents_errorOnDirRequest(t *testing.T) {
|
||||
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks")
|
||||
|
||||
resolver, err := NewFromContainerImageSquash(img)
|
||||
@@ -499,6 +506,7 @@ func Test_imageSquashResolver_resolvesLinks(t *testing.T) {
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", "image-symlinks")
|
||||
|
||||
resolver, err := NewFromContainerImageSquash(img)
|
||||
@@ -545,6 +553,7 @@ func locationSorter(a, b file.Location) int {
|
||||
}
|
||||
|
||||
func TestSquashResolver_AllLocations(t *testing.T) {
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", "image-files-deleted")
|
||||
|
||||
resolver, err := NewFromContainerImageSquash(img)
|
||||
@@ -580,6 +589,7 @@ func TestSquashResolver_AllLocations(t *testing.T) {
|
||||
// content and metadata), so that image results are in parity with directory results (which cannot distinguish a
|
||||
// hardlink from a regular file). this must hold for both the squashed and all-layers resolvers.
|
||||
func TestImageResolvers_Hardlinks(t *testing.T) {
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", "image-hardlinks")
|
||||
|
||||
resolvers := map[string]file.Resolver{}
|
||||
|
||||
@@ -351,17 +351,12 @@ func (r directoryIndexer) addFileToIndex(p string, info os.FileInfo) error {
|
||||
func (r directoryIndexer) addSymlinkToIndex(p string, info os.FileInfo) (string, error) {
|
||||
linkTarget, err := os.Readlink(p)
|
||||
if err != nil {
|
||||
isOnWindows := windows.HostRunningOnWindows()
|
||||
if isOnWindows {
|
||||
p = windows.FromPosix(p)
|
||||
}
|
||||
|
||||
linkTarget, err = filepath.EvalSymlinks(p)
|
||||
|
||||
if isOnWindows {
|
||||
p = windows.ToPosix(p)
|
||||
nativePath := p
|
||||
if windows.HostRunningOnWindows() {
|
||||
nativePath = windows.FromPosix(p)
|
||||
}
|
||||
|
||||
linkTarget, err = filepath.EvalSymlinks(nativePath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unable to readlink for path=%q: %w", p, err)
|
||||
}
|
||||
@@ -414,16 +409,21 @@ func (r directoryIndexer) addSymlinkToIndex(p string, info os.FileInfo) (string,
|
||||
}
|
||||
}
|
||||
|
||||
ref, err := r.tree.AddSymLink(file.Path(p), file.Path(linkTarget))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
targetAbsPath := linkTarget
|
||||
if !filepath.IsAbs(targetAbsPath) {
|
||||
targetAbsPath = filepath.Clean(filepath.Join(path.Dir(p), linkTarget))
|
||||
}
|
||||
|
||||
// the tree is posix (like p), but the joins above produce native separators on windows
|
||||
if windows.HostRunningOnWindows() {
|
||||
linkTarget = windows.ToPosix(linkTarget)
|
||||
}
|
||||
|
||||
ref, err := r.tree.AddSymLink(file.Path(p), file.Path(linkTarget))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
metadata := NewMetadataFromPath(p, info)
|
||||
metadata.LinkDestination = linkTarget
|
||||
r.index.Add(*ref, metadata)
|
||||
|
||||
@@ -35,9 +35,19 @@ func nativeOSFileOpener(ref stereoscopeFile.Reference) (io.ReadCloser, error) {
|
||||
return stereoscopeFile.NewLazyReadCloser(filePath), nil
|
||||
}
|
||||
|
||||
// virtual reports whether the tree is not backed by the host filesystem (e.g. a squashfs), in which case there is
|
||||
// no chroot and request/response paths are already the posix tree paths. Mapping them through the host (as the
|
||||
// chroot does) would root them on the current volume on windows (/payload.txt -> /d/payload.txt).
|
||||
func (r FiletreeResolver) virtual() bool {
|
||||
return r.Chroot == ChrootContext{}
|
||||
}
|
||||
|
||||
// requestPath converts a user (chroot) path into the path the file tree is keyed on. The tree is always posix
|
||||
// (windows paths are volume-encoded, e.g. /c/some/path), while ToNativePath yields a native host path.
|
||||
func (r *FiletreeResolver) requestPath(userPath string) (string, error) {
|
||||
if r.virtual() {
|
||||
return userPath, nil
|
||||
}
|
||||
nativePath, err := r.Chroot.ToNativePath(userPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
@@ -50,6 +60,9 @@ func (r *FiletreeResolver) requestPath(userPath string) (string, error) {
|
||||
|
||||
// responsePath takes a path from the underlying fs domain and converts it to a path that is relative to the root of the file resolver.
|
||||
func (r FiletreeResolver) responsePath(path string) string {
|
||||
if r.virtual() {
|
||||
return path
|
||||
}
|
||||
return r.Chroot.ToChrootPath(path)
|
||||
}
|
||||
|
||||
@@ -111,6 +124,9 @@ func (r FiletreeResolver) FilesByPath(userPaths ...string) ([]file.Location, err
|
||||
|
||||
// requestGlob is the glob equivalent of requestPath.
|
||||
func (r FiletreeResolver) requestGlob(pattern string) (string, error) {
|
||||
if r.virtual() {
|
||||
return pattern, nil
|
||||
}
|
||||
glob, err := r.Chroot.ToNativeGlob(pattern)
|
||||
if err != nil {
|
||||
return "", err
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"io"
|
||||
"math"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
@@ -522,14 +523,15 @@ func TestSafeTensorsMergeProcessor(t *testing.T) {
|
||||
|
||||
t.Run("dir scan: config.json _name_or_path beats the parent directory fallback", func(t *testing.T) {
|
||||
// When a sibling config.json carries _name_or_path
|
||||
dir := t.TempDir()
|
||||
require.NoError(t, os.WriteFile(filepath.Join(dir, "config.json"),
|
||||
// (location paths are posix, so keep the temp dir slash-separated for windows)
|
||||
dir := filepath.ToSlash(t.TempDir())
|
||||
require.NoError(t, os.WriteFile(path.Join(dir, "config.json"),
|
||||
[]byte(`{"_name_or_path":"org/preferred-name"}`), 0o644))
|
||||
stPath := filepath.Join(dir, "weights.safetensors")
|
||||
stPath := path.Join(dir, "weights.safetensors")
|
||||
p := dirPkg(stPath, pkg.SafeTensorsModelInfo{
|
||||
Format: "safetensors", TensorCount: 1, MetadataHash: "abc",
|
||||
})
|
||||
resolver := file.NewMockResolverForPaths(filepath.Join(dir, "config.json"))
|
||||
resolver := file.NewMockResolverForPaths(path.Join(dir, "config.json"))
|
||||
out, _, err := safeTensorsMergeProcessor(context.Background(), resolver, []pkg.Package{p}, nil, nil)
|
||||
require.NoError(t, err)
|
||||
require.Len(t, out, 1)
|
||||
|
||||
@@ -114,7 +114,7 @@ func fetchPkgFiles(base string, resolver file.Resolver) ([]pkg.AlpmFileRecord, f
|
||||
|
||||
func fetchBackupFiles(base string, resolver file.Resolver) ([]pkg.AlpmFileRecord, file.Location, error) {
|
||||
// We only really do this to get any backup database entries from the files database
|
||||
target := filepath.Join(base, "files")
|
||||
target := path.Join(base, "files")
|
||||
|
||||
loc, err := getLocation(target, resolver)
|
||||
if err != nil {
|
||||
@@ -395,7 +395,9 @@ func parseMtreeWithLimits(r io.Reader, maxSize int64, maxLines int) ([]pkg.AlpmF
|
||||
if ok := ignoredFiles[f.Name]; ok {
|
||||
continue
|
||||
}
|
||||
path := fmt.Sprintf("/%s", f.Name)
|
||||
// go-mtree runs filepath.Clean on entry names, which turns them into host paths on windows.
|
||||
// ponytail: a vis-encoded backslash (\134) in a name gets mangled on windows only, unvis first if that matters
|
||||
path := fmt.Sprintf("/%s", filepath.ToSlash(f.Name))
|
||||
fileFields["path"] = path
|
||||
for _, kv := range f.Keywords {
|
||||
kw := string(kv.Keyword())
|
||||
|
||||
@@ -17,6 +17,7 @@ import (
|
||||
|
||||
"github.com/anchore/packageurl-go"
|
||||
"github.com/anchore/stereoscope/pkg/imagetest"
|
||||
"github.com/anchore/syft/internal/testutils"
|
||||
"github.com/anchore/syft/syft/cpe"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
@@ -3169,6 +3170,7 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases_Image(t *testing.T) {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
c := NewClassifierCataloger(DefaultClassifierCatalogerConfig())
|
||||
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", test.fixtureImage)
|
||||
src := stereoscopesource.New(img, stereoscopesource.ImageConfig{
|
||||
Reference: test.fixtureImage,
|
||||
|
||||
+5
-4
@@ -1,6 +1,7 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -123,10 +124,10 @@ func TestAllStorePaths(t *testing.T) {
|
||||
},
|
||||
dest: "dest",
|
||||
expected: []string{
|
||||
"dest/test/1.0/platform1/test1",
|
||||
"dest/test/1.0/platform1/test2",
|
||||
"dest/test/1.0/platform2/test1",
|
||||
"dest/test/1.0/platform2/test2",
|
||||
filepath.FromSlash("dest/test/1.0/platform1/test1"),
|
||||
filepath.FromSlash("dest/test/1.0/platform1/test2"),
|
||||
filepath.FromSlash("dest/test/1.0/platform2/test1"),
|
||||
filepath.FromSlash("dest/test/1.0/platform2/test2"),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"fmt"
|
||||
"iter"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"sort"
|
||||
@@ -167,7 +168,7 @@ func getLogicalKey(managedBinaryPath string) (*LogicalEntryKey, error) {
|
||||
OrgName: items[0],
|
||||
Version: items[1],
|
||||
Platform: items[2],
|
||||
Filename: filepath.Join(items[3:]...),
|
||||
Filename: path.Join(items[3:]...), // posix, since this is part of the logical key
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -64,8 +64,8 @@ func TestListAllBinaries(t *testing.T) {
|
||||
|
||||
assert.Equal(t,
|
||||
Entries{
|
||||
LogicalEntryKey{OrgName: "busybox", Version: "1.3.6", Platform: "linux-amd64", Filename: "busybox"}: EntryInfo{IsConfigured: true, BinaryPath: "testdata/bin/busybox/1.3.6/linux-amd64/busybox", SnippetPath: ""},
|
||||
LogicalEntryKey{OrgName: "busybox", Version: "1.3.6", Platform: "linux-arm64", Filename: "busybox"}: EntryInfo{IsConfigured: false, BinaryPath: "testdata/bin/busybox/1.3.6/linux-arm64/busybox", SnippetPath: ""},
|
||||
LogicalEntryKey{OrgName: "busybox", Version: "1.3.6", Platform: "linux-amd64", Filename: "busybox"}: EntryInfo{IsConfigured: true, BinaryPath: filepath.FromSlash("testdata/bin/busybox/1.3.6/linux-amd64/busybox"), SnippetPath: ""},
|
||||
LogicalEntryKey{OrgName: "busybox", Version: "1.3.6", Platform: "linux-arm64", Filename: "busybox"}: EntryInfo{IsConfigured: false, BinaryPath: filepath.FromSlash("testdata/bin/busybox/1.3.6/linux-arm64/busybox"), SnippetPath: ""},
|
||||
},
|
||||
entries,
|
||||
)
|
||||
@@ -105,10 +105,10 @@ func TestListAllEntries(t *testing.T) {
|
||||
|
||||
assert.Equal(t,
|
||||
Entries{
|
||||
LogicalEntryKey{OrgName: "busybox", Version: "1.3.6", Platform: "linux-amd64", Filename: "busybox"}: EntryInfo{IsConfigured: true, BinaryPath: "testdata/bin/busybox/1.3.6/linux-amd64/busybox", SnippetPath: "testdata/snippets/busybox/1.3.6/linux-amd64/busybox"},
|
||||
LogicalEntryKey{OrgName: "busybox", Version: "1.3.6", Platform: "linux-arm64", Filename: "busybox"}: EntryInfo{IsConfigured: false, BinaryPath: "testdata/bin/busybox/1.3.6/linux-arm64/busybox", SnippetPath: "testdata/snippets/busybox/1.3.6/linux-arm64/busybox"},
|
||||
LogicalEntryKey{OrgName: "busybox", Version: "1.3.6", Platform: "linux-amd64", Filename: "busybox"}: EntryInfo{IsConfigured: true, BinaryPath: filepath.FromSlash("testdata/bin/busybox/1.3.6/linux-amd64/busybox"), SnippetPath: filepath.FromSlash("testdata/snippets/busybox/1.3.6/linux-amd64/busybox")},
|
||||
LogicalEntryKey{OrgName: "busybox", Version: "1.3.6", Platform: "linux-arm64", Filename: "busybox"}: EntryInfo{IsConfigured: false, BinaryPath: filepath.FromSlash("testdata/bin/busybox/1.3.6/linux-arm64/busybox"), SnippetPath: filepath.FromSlash("testdata/snippets/busybox/1.3.6/linux-arm64/busybox")},
|
||||
// note the standalone snippet!
|
||||
LogicalEntryKey{OrgName: "postgres", Version: "9.6.10", Platform: "linux-amd64", Filename: "postgres"}: EntryInfo{IsConfigured: false, BinaryPath: "", SnippetPath: "testdata/snippets/postgres/9.6.10/linux-amd64/postgres"},
|
||||
LogicalEntryKey{OrgName: "postgres", Version: "9.6.10", Platform: "linux-amd64", Filename: "postgres"}: EntryInfo{IsConfigured: false, BinaryPath: "", SnippetPath: filepath.FromSlash("testdata/snippets/postgres/9.6.10/linux-amd64/postgres")},
|
||||
},
|
||||
entries,
|
||||
)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -13,7 +14,7 @@ func TestSplitFilepath(t *testing.T) {
|
||||
path string
|
||||
expected []string
|
||||
}{
|
||||
{path: "a/b/c", expected: []string{"a", "b", "c"}},
|
||||
{path: filepath.Join("a", "b", "c"), expected: []string{"a", "b", "c"}},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
|
||||
@@ -3,7 +3,9 @@ package testutil
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
@@ -18,10 +20,11 @@ import (
|
||||
// not specify the "bin" or "snippets" parent path... this is determined logically [snippets > binary unless told
|
||||
// otherwise]). Path should also be to the directory containing the binary or snippets of interest (not the binaries
|
||||
// or snippets itself).
|
||||
func SnippetOrBinary(t *testing.T, path string, requireBinary bool) string {
|
||||
func SnippetOrBinary(t *testing.T, logicalPath string, requireBinary bool) string {
|
||||
t.Helper()
|
||||
|
||||
require.Len(t, internal.SplitFilepath(path), 3, "path must be a in the form <name>/<version>/<arch>")
|
||||
// logicalPath is a posix key, not a host path
|
||||
require.Len(t, strings.Split(logicalPath, "/"), 3, "path must be a in the form <name>/<version>/<arch>")
|
||||
|
||||
// cd to testdata directory and load the config
|
||||
|
||||
@@ -43,11 +46,11 @@ func SnippetOrBinary(t *testing.T, path string, requireBinary bool) string {
|
||||
|
||||
var fixturePath string
|
||||
for k, v := range entries.Sorted() {
|
||||
if filepath.Dir(k.Path()) == path {
|
||||
if path.Dir(k.Path()) == logicalPath {
|
||||
// prefer the snippet over the binary
|
||||
if !requireBinary {
|
||||
if v.SnippetPath != "" {
|
||||
t.Logf("using snippet for %q", path)
|
||||
t.Logf("using snippet for %q", logicalPath)
|
||||
require.NoError(t, validateSnippet(v.BinaryPath, v.SnippetPath))
|
||||
fixturePath = v.SnippetPath
|
||||
break
|
||||
@@ -56,10 +59,10 @@ func SnippetOrBinary(t *testing.T, path string, requireBinary bool) string {
|
||||
fixturePath = v.BinaryPath
|
||||
break
|
||||
}
|
||||
t.Fatalf("no binary or snippet found for %q", path)
|
||||
t.Fatalf("no binary or snippet found for %q", logicalPath)
|
||||
}
|
||||
if v.BinaryPath != "" {
|
||||
t.Logf("forcing the use of the original binary for %q", path)
|
||||
t.Logf("forcing the use of the original binary for %q", logicalPath)
|
||||
fixturePath = v.BinaryPath
|
||||
break
|
||||
}
|
||||
@@ -68,12 +71,12 @@ func SnippetOrBinary(t *testing.T, path string, requireBinary bool) string {
|
||||
t.Skip("no binary found, but is covered by a snippet. Please add this case to the 'binary/testdata/config.yaml' and recreate the snippet")
|
||||
}
|
||||
|
||||
t.Fatalf("no binary found for %q", path)
|
||||
t.Fatalf("no binary found for %q", logicalPath)
|
||||
}
|
||||
}
|
||||
|
||||
if fixturePath == "" {
|
||||
t.Fatalf("no fixture found for %q", path)
|
||||
t.Fatalf("no fixture found for %q", logicalPath)
|
||||
}
|
||||
|
||||
// this should be relative to the tests-fixtures directory and should be the directory containing the binary or
|
||||
@@ -101,6 +104,7 @@ func validateSnippet(binaryPath, snippetPath string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
expected, err := internal.Sha256SumFile(f)
|
||||
if err != nil {
|
||||
|
||||
@@ -5,7 +5,7 @@ package bitnami
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/anchore/syft/internal/log"
|
||||
@@ -73,7 +73,7 @@ func parseSBOM(_ context.Context, resolver file.Resolver, _ *generic.Environment
|
||||
|
||||
// Bitnami packages reported in a SPDX file are shipped under the same directory
|
||||
// as the SPDX file itself.
|
||||
metadata.Path = filepath.Dir(reader.RealPath)
|
||||
metadata.Path = path.Dir(reader.RealPath)
|
||||
if p.ID() != mainPkgID {
|
||||
metadata.Files = packageFiles(s.Relationships, p, metadata.Path)
|
||||
secondaryPkgsFiles = append(secondaryPkgsFiles, metadata.Files...)
|
||||
|
||||
@@ -3,7 +3,6 @@ package bitnami
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
@@ -73,7 +72,7 @@ func packageFiles(relationships []artifact.Relationship, p pkg.Package, baseDire
|
||||
|
||||
// mainPkgFiles returns the files owned by the main package in the SPDX file.
|
||||
func mainPkgFiles(resolver file.Resolver, spdxFilePath string, secondaryPkgsFiles []string) ([]string, error) {
|
||||
ownedPathGlob := fmt.Sprintf("%s/**", filepath.Dir(spdxFilePath))
|
||||
ownedPathGlob := fmt.Sprintf("%s/**", path.Dir(spdxFilePath))
|
||||
ownedLocations, err := resolver.FilesByGlob(ownedPathGlob)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"path"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
@@ -67,7 +67,7 @@ func parseComponentsJSON(_ context.Context, _ file.Resolver, _ *generic.Environm
|
||||
Revision: revision,
|
||||
Architecture: entry.Arch,
|
||||
Distro: entry.Distro,
|
||||
Path: filepath.Join(filepath.Dir(reader.RealPath), name),
|
||||
Path: path.Join(path.Dir(reader.RealPath), name),
|
||||
}
|
||||
|
||||
p := pkg.Package{
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package golang
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/spf13/afero"
|
||||
@@ -202,10 +205,23 @@ func TestFindAllLicenseCandidatesUpwards(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
// Create in-memory filesystem
|
||||
fs := afero.NewMemMapFs()
|
||||
tt.setupFS(fs)
|
||||
setupFS, startDir, stopAt, expectedFiles := afero.Fs(fs), tt.startDir, tt.stopAt, tt.expectedFiles
|
||||
if runtime.GOOS == "windows" {
|
||||
// the finder wants native absolute paths, so root the posix-style cases on a drive
|
||||
setupFS = afero.NewBasePathFs(fs, `C:\`)
|
||||
startDir, stopAt = winAbs(startDir), winAbs(stopAt)
|
||||
expectedFiles = nil
|
||||
if tt.expectedFiles != nil {
|
||||
expectedFiles = []string{}
|
||||
}
|
||||
for _, f := range tt.expectedFiles {
|
||||
expectedFiles = append(expectedFiles, winAbs(f))
|
||||
}
|
||||
}
|
||||
tt.setupFS(setupFS)
|
||||
|
||||
// Run the function
|
||||
result, err := findAllLicenseCandidatesUpwards(tt.startDir, tt.stopAt, fs)
|
||||
result, err := findAllLicenseCandidatesUpwards(startDir, stopAt, fs)
|
||||
|
||||
// Check error expectation
|
||||
if tt.expectedError {
|
||||
@@ -214,7 +230,15 @@ func TestFindAllLicenseCandidatesUpwards(t *testing.T) {
|
||||
}
|
||||
|
||||
require.NoError(t, err, tt.description)
|
||||
assert.Equal(t, tt.expectedFiles, result, tt.description)
|
||||
assert.Equal(t, expectedFiles, result, tt.description)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// winAbs roots posix absolute paths on C:\, leaving relative ones relative (some cases assert they're rejected)
|
||||
func winAbs(p string) string {
|
||||
if !strings.HasPrefix(p, "/") {
|
||||
return p
|
||||
}
|
||||
return filepath.Join(`C:\`, filepath.FromSlash(p))
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/syft/internal/testutils"
|
||||
"github.com/anchore/syft/syft/cataloging"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/internal/fileresolver"
|
||||
@@ -30,6 +31,8 @@ import (
|
||||
|
||||
// make will run the default make target for the given test fixture path
|
||||
func runMakeTarget(t *testing.T, fixtureName string) {
|
||||
// the fixture binaries are built inside linux containers
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
cwd, err := os.Getwd()
|
||||
require.NoError(t, err)
|
||||
fixtureDir := filepath.Join(cwd, "testdata/", fixtureName)
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/internal/fileresolver"
|
||||
"github.com/anchore/syft/syft/internal/windows"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||
)
|
||||
@@ -38,7 +39,12 @@ func newGoModCataloger(opts CatalogerConfig) *goModCataloger {
|
||||
|
||||
// parseGoModFile takes a go.mod and tries to resolve and lists all packages discovered.
|
||||
func (c *goModCataloger) parseGoModFile(ctx context.Context, resolver file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) (pkgs []pkg.Package, relationships []artifact.Relationship, err error) {
|
||||
modDir := filepath.Dir(string(reader.Location.Reference().RealPath))
|
||||
realPath := string(reader.Location.Reference().RealPath)
|
||||
if windows.HostRunningOnWindows() {
|
||||
// directory resolvers hand back volume-encoded posix paths (/c/...), the go toolchain needs the native path
|
||||
realPath = windows.FromPosix(realPath)
|
||||
}
|
||||
modDir := filepath.Dir(realPath)
|
||||
digests, err := parseGoSumFile(resolver, reader)
|
||||
if err != nil {
|
||||
log.Debugf("unable to get go.sum: %v", err)
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
stereoscopeFile "github.com/anchore/stereoscope/pkg/file"
|
||||
"github.com/anchore/stereoscope/pkg/imagetest"
|
||||
"github.com/anchore/syft/internal/testutils"
|
||||
)
|
||||
|
||||
// every case here is one the magic scan must reject, asserted on parseUPXInfo's errNotUPX result.
|
||||
@@ -114,6 +115,7 @@ func TestParseUPXInfo_ValidHeader(t *testing.T) {
|
||||
// ignores that padding if there is some. A build that needs none passes either way, which is how the bug
|
||||
// went unnoticed. The case without padding is covered by TestDecompressUPX_TailExtentsArePlacedPastTheLoader.
|
||||
func TestImageSmallUPXNeedsLoaderPadding(t *testing.T) {
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", "image-small-upx")
|
||||
rc, err := img.OpenPathFromSquash(stereoscopeFile.Path("/run-me"))
|
||||
require.NoError(t, err)
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/stereoscope/pkg/imagetest"
|
||||
"github.com/anchore/syft/internal/testutils"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
"github.com/anchore/syft/syft/source/stereoscopesource"
|
||||
@@ -199,6 +200,7 @@ func fixtureResolvers(t *testing.T, fixtures ...string) map[string]file.Resolver
|
||||
continue
|
||||
}
|
||||
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", fixture)
|
||||
|
||||
s := stereoscopesource.New(img, stereoscopesource.ImageConfig{
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
"github.com/anchore/syft/internal/cmptest"
|
||||
"github.com/anchore/syft/internal/licenses"
|
||||
"github.com/anchore/syft/internal/relationship"
|
||||
"github.com/anchore/syft/internal/testutils"
|
||||
"github.com/anchore/syft/internal/tmpdir"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
@@ -150,9 +151,11 @@ func (p *CatalogTester) FromFile(t *testing.T, path string) *CatalogTester {
|
||||
|
||||
fixture, err := os.Open(path)
|
||||
require.NoError(t, err)
|
||||
// parsers don't own the reader (the generic cataloger closes it), so close it here
|
||||
t.Cleanup(func() { _ = fixture.Close() })
|
||||
|
||||
p.reader = file.LocationReadCloser{
|
||||
Location: file.NewVirtualLocationFromDirectory(fixture.Name(), fixture.Name(), *stereofile.NewFileReference(stereofile.Path(absPath))),
|
||||
Location: file.NewVirtualLocationFromDirectory(filepath.ToSlash(fixture.Name()), filepath.ToSlash(fixture.Name()), *stereofile.NewFileReference(stereofile.Path(absPath))),
|
||||
ReadCloser: fixture,
|
||||
}
|
||||
return p
|
||||
@@ -201,6 +204,7 @@ func (p *CatalogTester) WithImageResolver(t *testing.T, fixtureName string) *Cat
|
||||
return p
|
||||
}
|
||||
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", fixtureName)
|
||||
|
||||
s := stereoscopesource.New(img, stereoscopesource.ImageConfig{
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"syscall"
|
||||
@@ -18,6 +19,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/syft/internal/testutils"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/license"
|
||||
@@ -1605,6 +1607,9 @@ func generateJavaBuildFixture(t *testing.T, fixturePath string) {
|
||||
return
|
||||
}
|
||||
|
||||
// the fixtures are built with maven/gradle inside linux containers
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
|
||||
makeTask := strings.TrimPrefix(fixturePath, "testdata/java-builds/")
|
||||
t.Log(color.Bold.Sprintf("Generating Fixture from 'make %s'", makeTask))
|
||||
|
||||
@@ -1630,7 +1635,8 @@ func generateJavaMetadataJarFixture(t *testing.T, fixtureName string, fileExtens
|
||||
return fixturePath
|
||||
}
|
||||
|
||||
makeTask := filepath.Join("cache", fixtureName+"."+fileExtension)
|
||||
// make targets are posix paths regardless of host
|
||||
makeTask := path.Join("cache", fixtureName+"."+fileExtension)
|
||||
t.Log(color.Bold.Sprintf("Generating Fixture from 'make %s'", makeTask))
|
||||
|
||||
cwd, err := os.Getwd()
|
||||
|
||||
@@ -47,6 +47,8 @@ func Test_defaultMavenLocalRepoDir(t *testing.T) {
|
||||
home, err := filepath.Abs(test.home)
|
||||
require.NoError(t, err)
|
||||
t.Setenv("HOME", home)
|
||||
// os.UserHomeDir reads USERPROFILE on windows
|
||||
t.Setenv("USERPROFILE", home)
|
||||
}
|
||||
got := defaultMavenLocalRepoDir()
|
||||
require.Equal(t, test.expected, got)
|
||||
|
||||
@@ -31,7 +31,7 @@ func MockRepo(t *testing.T, dir string) (url string) {
|
||||
|
||||
t.Cleanup(server.Close)
|
||||
|
||||
matches, err := doublestar.Glob(os.DirFS(dir), filepath.Join("**", "*.pom"))
|
||||
matches, err := doublestar.Glob(os.DirFS(dir), "**/*.pom")
|
||||
require.NoError(t, err)
|
||||
|
||||
for _, match := range matches {
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/anchore/stereoscope/pkg/imagetest"
|
||||
"github.com/anchore/syft/internal/packagemetadata"
|
||||
"github.com/anchore/syft/internal/testutils"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
@@ -523,6 +524,7 @@ func TestCpanCatalogers_metadataTypes(t *testing.T) {
|
||||
func imageResolver(t *testing.T, fixture string) file.Resolver {
|
||||
t.Helper()
|
||||
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
img := imagetest.GetFixtureImage(t, "docker-archive", fixture)
|
||||
resolver, err := stereoscopesource.New(img, stereoscopesource.ImageConfig{Reference: fixture}).
|
||||
FileResolver(source.SquashedScope)
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/scylladb/go-set/strset"
|
||||
@@ -53,7 +52,7 @@ func fetchInstalledFiles(resolver file.Resolver, metadataLocation file.Location,
|
||||
// to reconcile the installed-files.txt path to the same layer (or the next adjacent lower layer).
|
||||
|
||||
// find the installed-files.txt file relative to the directory where the METADATA file resides (in path AND layer structure)
|
||||
installedFilesPath := filepath.Join(filepath.Dir(metadataLocation.RealPath), "installed-files.txt")
|
||||
installedFilesPath := path.Join(path.Dir(metadataLocation.RealPath), "installed-files.txt")
|
||||
installedFilesRef := resolver.RelativeFileByPath(metadataLocation, installedFilesPath)
|
||||
|
||||
if installedFilesRef != nil {
|
||||
@@ -83,7 +82,7 @@ func fetchRecordFiles(resolver file.Resolver, metadataLocation file.Location) (f
|
||||
// to reconcile the RECORD path to the same layer (or the next adjacent lower layer).
|
||||
|
||||
// find the RECORD file relative to the directory where the METADATA file resides (in path AND layer structure)
|
||||
recordPath := filepath.Join(filepath.Dir(metadataLocation.RealPath), "RECORD")
|
||||
recordPath := path.Join(path.Dir(metadataLocation.RealPath), "RECORD")
|
||||
recordRef := resolver.RelativeFileByPath(metadataLocation, recordPath)
|
||||
|
||||
if recordRef != nil {
|
||||
@@ -107,8 +106,8 @@ func fetchRecordFiles(resolver file.Resolver, metadataLocation file.Location) (f
|
||||
// fetchTopLevelPackages finds a corresponding top_level.txt file for the given python package metadata file and returns the set of package names contained.
|
||||
func fetchTopLevelPackages(resolver file.Resolver, metadataLocation file.Location) (pkgs []string, sources []file.Location, err error) {
|
||||
// a top_level.txt file specifies the python top-level packages (provided by this python package) installed into site-packages
|
||||
parentDir := filepath.Dir(metadataLocation.RealPath)
|
||||
topLevelPath := filepath.Join(parentDir, "top_level.txt")
|
||||
parentDir := path.Dir(metadataLocation.RealPath)
|
||||
topLevelPath := path.Join(parentDir, "top_level.txt")
|
||||
topLevelLocation := resolver.RelativeFileByPath(metadataLocation, topLevelPath)
|
||||
|
||||
if topLevelLocation == nil {
|
||||
@@ -157,8 +156,8 @@ type vcsInfo struct {
|
||||
}
|
||||
|
||||
func fetchDirectURLData(resolver file.Resolver, metadataLocation file.Location) (d *pkg.PythonDirectURLOriginInfo, sources []file.Location, err error) {
|
||||
parentDir := filepath.Dir(metadataLocation.RealPath)
|
||||
directURLPath := filepath.Join(parentDir, "direct_url.json")
|
||||
parentDir := path.Dir(metadataLocation.RealPath)
|
||||
directURLPath := path.Join(parentDir, "direct_url.json")
|
||||
directURLLocation := resolver.RelativeFileByPath(metadataLocation, directURLPath)
|
||||
|
||||
if directURLLocation == nil {
|
||||
@@ -269,7 +268,7 @@ func findLicenses(ctx context.Context, resolver file.Resolver, m parsedData) pkg
|
||||
continue
|
||||
}
|
||||
|
||||
if licenses.IsLicenseFile(filepath.Base(f.Path)) {
|
||||
if licenses.IsLicenseFile(path.Base(f.Path)) {
|
||||
candidatePaths.Add(path.Join(m.SitePackagesRootPath, f.Path))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package python
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/bmatcuk/doublestar/v4"
|
||||
@@ -48,13 +48,13 @@ func parseWheelOrEggMetadata(locationReader file.LocationReadCloser) (parsedData
|
||||
}
|
||||
|
||||
// add additional metadata not stored in the egg/wheel metadata file
|
||||
path := locationReader.Path()
|
||||
metadataPath := locationReader.Path()
|
||||
|
||||
pd.SitePackagesRootPath = determineSitePackagesRootPath(path)
|
||||
pd.SitePackagesRootPath = determineSitePackagesRootPath(metadataPath)
|
||||
if pd.Licenses != "" || pd.LicenseExpression != "" {
|
||||
pd.LicenseFilePath = path
|
||||
pd.LicenseFilePath = metadataPath
|
||||
} else if pd.LicenseFile != "" {
|
||||
pd.LicenseFilePath = filepath.Join(filepath.Dir(path), pd.LicenseFile)
|
||||
pd.LicenseFilePath = path.Join(path.Dir(metadataPath), pd.LicenseFile)
|
||||
}
|
||||
|
||||
pd.DistInfoLocation = locationReader.Location
|
||||
@@ -144,18 +144,18 @@ func getFieldType(key, in string) any {
|
||||
// isEggRegularFile determines if the specified path is the regular file variant
|
||||
// of egg metadata (as opposed to a directory that contains more metadata
|
||||
// files).
|
||||
func isEggRegularFile(path string) bool {
|
||||
return doublestar.MatchUnvalidated(eggInfoGlob, path)
|
||||
func isEggRegularFile(p string) bool {
|
||||
return doublestar.MatchUnvalidated(eggInfoGlob, p)
|
||||
}
|
||||
|
||||
// determineSitePackagesRootPath returns the path of the site packages root,
|
||||
// given the egg metadata file or directory specified in the path.
|
||||
func determineSitePackagesRootPath(path string) string {
|
||||
if isEggRegularFile(path) {
|
||||
return filepath.Clean(filepath.Dir(path))
|
||||
func determineSitePackagesRootPath(p string) string {
|
||||
if isEggRegularFile(p) {
|
||||
return path.Clean(path.Dir(p))
|
||||
}
|
||||
|
||||
return filepath.Clean(filepath.Dir(filepath.Dir(path)))
|
||||
return path.Clean(path.Dir(path.Dir(p)))
|
||||
}
|
||||
|
||||
// handleFieldBodyContinuation returns the updated value for the specified field after processing the specified line.
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
@@ -87,11 +88,13 @@ func parseInstalledFiles(reader io.Reader, location, sitePackagesRootPath string
|
||||
}
|
||||
|
||||
if location != "" && sitePackagesRootPath != "" {
|
||||
joinedPath := filepath.Join(filepath.Dir(location), line)
|
||||
joinedPath := path.Join(path.Dir(location), line)
|
||||
// the path package has no Rel, and filepath.Rel hands back host separators on windows
|
||||
line, err = filepath.Rel(sitePackagesRootPath, joinedPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
line = filepath.ToSlash(line)
|
||||
}
|
||||
|
||||
installedFile := pkg.PythonFileRecord{
|
||||
|
||||
@@ -122,8 +122,7 @@ func TestParsePackageResolved_empty(t *testing.T) {
|
||||
|
||||
dir := t.TempDir()
|
||||
fixture = filepath.Join(dir, "Package.resolved")
|
||||
_, err := os.Create(fixture)
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, os.WriteFile(fixture, nil, 0600))
|
||||
|
||||
pkgtest.TestFileParser(t, fixture, parsePackageResolved, nil, nil)
|
||||
}
|
||||
|
||||
@@ -30,6 +30,10 @@ import (
|
||||
)
|
||||
|
||||
func TestNewFromLocal(t *testing.T) {
|
||||
// the request is resolved with filepath.Abs, which adds a volume on windows
|
||||
localSnap, err := filepath.Abs("/test/local.snap")
|
||||
require.NoError(t, err)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
cfg Config
|
||||
@@ -40,13 +44,13 @@ func TestNewFromLocal(t *testing.T) {
|
||||
{
|
||||
name: "local file exists",
|
||||
cfg: Config{
|
||||
Request: "/test/local.snap",
|
||||
Request: localSnap,
|
||||
DigestAlgorithms: []crypto.Hash{crypto.SHA256},
|
||||
},
|
||||
setup: func(fs afero.Fs) {
|
||||
require.NoError(t, createMockSquashfsFile(fs, "/test/local.snap"))
|
||||
require.NoError(t, createMockSquashfsFile(fs, localSnap))
|
||||
},
|
||||
wantRequest: "/test/local.snap",
|
||||
wantRequest: localSnap,
|
||||
},
|
||||
{
|
||||
name: "resolve home dir exists",
|
||||
@@ -65,18 +69,18 @@ func TestNewFromLocal(t *testing.T) {
|
||||
{
|
||||
name: "local file with architecture specified",
|
||||
cfg: Config{
|
||||
Request: "/test/local.snap",
|
||||
Request: localSnap,
|
||||
Platform: &image.Platform{
|
||||
Architecture: "arm64",
|
||||
},
|
||||
},
|
||||
setup: func(fs afero.Fs) {
|
||||
require.NoError(t, createMockSquashfsFile(fs, "/test/local.snap"))
|
||||
require.NoError(t, createMockSquashfsFile(fs, localSnap))
|
||||
},
|
||||
wantErr: func(t assert.TestingT, err error, msgAndArgs ...any) bool {
|
||||
return assert.Error(t, err, msgAndArgs...) && assert.Contains(t, err.Error(), "architecture cannot be specified for local snap files", msgAndArgs...)
|
||||
},
|
||||
wantRequest: "/test/local.snap",
|
||||
wantRequest: localSnap,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
@@ -288,6 +292,8 @@ func writeSquashfs(t *testing.T, manifest string) string {
|
||||
require.NoError(t, err)
|
||||
_, err = w.Write([]byte(contents))
|
||||
require.NoError(t, err)
|
||||
// close before finalizing: windows directory listings can report a stale (zero) size for open files
|
||||
require.NoError(t, w.Close())
|
||||
}
|
||||
|
||||
// note: go-diskfs wants paths relative to the image root, without a leading slash
|
||||
|
||||
@@ -145,7 +145,9 @@ func TestNewSnapFromFile(t *testing.T) {
|
||||
DigestAlgorithms: []crypto.Hash{crypto.SHA256},
|
||||
},
|
||||
setup: func() string {
|
||||
path := "/test/valid.snap"
|
||||
// newSnapFromFile resolves the request with filepath.Abs, which adds a volume on windows
|
||||
path, err := filepath.Abs("/test/valid.snap")
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, createMockSquashfsFile(fs, path))
|
||||
return path
|
||||
},
|
||||
@@ -179,7 +181,9 @@ func TestNewSnapFromFile(t *testing.T) {
|
||||
name: "path is directory",
|
||||
cfg: Config{},
|
||||
setup: func() string {
|
||||
path := "/test/directory"
|
||||
// newSnapFromFile resolves the request with filepath.Abs, which adds a volume on windows
|
||||
path, err := filepath.Abs("/test/directory")
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, fs.MkdirAll(path, 0755))
|
||||
return path
|
||||
},
|
||||
@@ -471,7 +475,9 @@ func TestGetSnapFileInfo(t *testing.T) {
|
||||
{
|
||||
name: "path is directory",
|
||||
setup: func() string {
|
||||
path := "/test/directory"
|
||||
// newSnapFromFile resolves the request with filepath.Abs, which adds a volume on windows
|
||||
path, err := filepath.Abs("/test/directory")
|
||||
require.NoError(t, err)
|
||||
require.NoError(t, fs.MkdirAll(path, 0755))
|
||||
return path
|
||||
},
|
||||
|
||||
@@ -10,6 +10,7 @@ import (
|
||||
|
||||
"github.com/anchore/stereoscope"
|
||||
"github.com/anchore/stereoscope/pkg/imagetest"
|
||||
"github.com/anchore/syft/internal/testutils"
|
||||
"github.com/anchore/syft/syft/internal/testutil"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
@@ -78,6 +79,7 @@ func Test_StereoscopeImage_Exclusions(t *testing.T) {
|
||||
|
||||
for _, test := range testCases {
|
||||
t.Run(test.desc, func(t *testing.T) {
|
||||
testutils.SkipWithoutLinuxContainers(t)
|
||||
imageName := strings.SplitN(imagetest.PrepareFixtureImage(t, "docker-archive", test.input), ":", 2)[1]
|
||||
|
||||
img, err := stereoscope.GetImage(context.TODO(), imageName)
|
||||
|
||||
Reference in New Issue
Block a user