mirror of
https://github.com/anchore/syft.git
synced 2025-11-17 16:33:21 +01:00
Re-introduce linux kernel cataloger (#2526)
* re-add linux kernel cataloger Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * ensure there is at least a directory or image tag on each task Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * fix CLI tests to account for kernel finding (+2) 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:
parent
c6ce1de928
commit
4c77783461
@ -18,6 +18,7 @@ import (
|
|||||||
"github.com/anchore/syft/syft/pkg/cataloger/haskell"
|
"github.com/anchore/syft/syft/pkg/cataloger/haskell"
|
||||||
"github.com/anchore/syft/syft/pkg/cataloger/java"
|
"github.com/anchore/syft/syft/pkg/cataloger/java"
|
||||||
"github.com/anchore/syft/syft/pkg/cataloger/javascript"
|
"github.com/anchore/syft/syft/pkg/cataloger/javascript"
|
||||||
|
"github.com/anchore/syft/syft/pkg/cataloger/kernel"
|
||||||
"github.com/anchore/syft/syft/pkg/cataloger/nix"
|
"github.com/anchore/syft/syft/pkg/cataloger/nix"
|
||||||
"github.com/anchore/syft/syft/pkg/cataloger/php"
|
"github.com/anchore/syft/syft/pkg/cataloger/php"
|
||||||
"github.com/anchore/syft/syft/pkg/cataloger/python"
|
"github.com/anchore/syft/syft/pkg/cataloger/python"
|
||||||
@ -112,9 +113,16 @@ func DefaultPackageTaskFactories() PackageTaskFactories {
|
|||||||
func(cfg CatalogingFactoryConfig) pkg.Cataloger {
|
func(cfg CatalogingFactoryConfig) pkg.Cataloger {
|
||||||
return binary.NewCataloger(cfg.PackagesConfig.Binary)
|
return binary.NewCataloger(cfg.PackagesConfig.Binary)
|
||||||
},
|
},
|
||||||
pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, "binary"),
|
pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, "binary",
|
||||||
|
),
|
||||||
newSimplePackageTaskFactory(githubactions.NewActionUsageCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, "github", "github-actions"),
|
newSimplePackageTaskFactory(githubactions.NewActionUsageCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, "github", "github-actions"),
|
||||||
newSimplePackageTaskFactory(githubactions.NewWorkflowUsageCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, "github", "github-actions"),
|
newSimplePackageTaskFactory(githubactions.NewWorkflowUsageCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, "github", "github-actions"),
|
||||||
|
newPackageTaskFactory(
|
||||||
|
func(cfg CatalogingFactoryConfig) pkg.Cataloger {
|
||||||
|
return kernel.NewLinuxKernelCataloger(cfg.PackagesConfig.LinuxKernel)
|
||||||
|
},
|
||||||
|
pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, "linux", "kernel",
|
||||||
|
),
|
||||||
newSimplePackageTaskFactory(sbomCataloger.NewCataloger, pkgcataloging.ImageTag, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, "sbom"), // note: not evidence of installed packages
|
newSimplePackageTaskFactory(sbomCataloger.NewCataloger, pkgcataloging.ImageTag, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, "sbom"), // note: not evidence of installed packages
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import (
|
|||||||
const (
|
const (
|
||||||
// this is the number of packages that should be found in the image-pkg-coverage fixture image
|
// this is the number of packages that should be found in the image-pkg-coverage fixture image
|
||||||
// when analyzed with the squashed scope.
|
// when analyzed with the squashed scope.
|
||||||
coverageImageSquashedPackageCount = 25
|
coverageImageSquashedPackageCount = 27
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestPackagesCmdFlags(t *testing.T) {
|
func TestPackagesCmdFlags(t *testing.T) {
|
||||||
|
|||||||
@ -122,6 +122,7 @@ func assertPackageCount(length uint) traitAssertion {
|
|||||||
type NameAndVersion struct {
|
type NameAndVersion struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
|
Type string `json:"type"`
|
||||||
}
|
}
|
||||||
type partial struct {
|
type partial struct {
|
||||||
Artifacts []NameAndVersion `json:"artifacts"`
|
Artifacts []NameAndVersion `json:"artifacts"`
|
||||||
@ -136,7 +137,7 @@ func assertPackageCount(length uint) traitAssertion {
|
|||||||
tb.Errorf("expected package count of %d, but found %d", length, len(data.Artifacts))
|
tb.Errorf("expected package count of %d, but found %d", length, len(data.Artifacts))
|
||||||
debugArtifacts := make([]string, len(data.Artifacts))
|
debugArtifacts := make([]string, len(data.Artifacts))
|
||||||
for i, a := range data.Artifacts {
|
for i, a := range data.Artifacts {
|
||||||
debugArtifacts[i] = fmt.Sprintf("%s:%s", a.Name, a.Version)
|
debugArtifacts[i] = fmt.Sprintf("%s@%s (%s)", a.Name, a.Version, a.Type)
|
||||||
}
|
}
|
||||||
sort.Strings(debugArtifacts)
|
sort.Strings(debugArtifacts)
|
||||||
for i, a := range debugArtifacts {
|
for i, a := range debugArtifacts {
|
||||||
|
|||||||
201
test/integration/package_catalogers_represented_test.go
Normal file
201
test/integration/package_catalogers_represented_test.go
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
package integration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"go/ast"
|
||||||
|
"go/parser"
|
||||||
|
"go/token"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/scylladb/go-set/strset"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/internal/task"
|
||||||
|
"github.com/anchore/syft/syft/cataloging/pkgcataloging"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestAllPackageCatalogersReachableInTasks(t *testing.T) {
|
||||||
|
// we want to see if we can get a task for all package catalogers. This is a bit tricky since we
|
||||||
|
// don't have a nice way to find all cataloger names in the codebase. Instead, we'll look at the
|
||||||
|
// count of unique task names from the package task factory set and compare that with the known constructors
|
||||||
|
// from a source analysis... they should match.
|
||||||
|
|
||||||
|
// additionally, at this time they should either have a "directory" or "image" tag as well. If there is no tag
|
||||||
|
// on a cataloger task then the test should fail.
|
||||||
|
|
||||||
|
taskFactories := task.DefaultPackageTaskFactories()
|
||||||
|
taskTagsByName := make(map[string][]string)
|
||||||
|
for _, factory := range taskFactories {
|
||||||
|
tsk := factory(task.DefaultCatalogingFactoryConfig())
|
||||||
|
if taskTagsByName[tsk.Name()] != nil {
|
||||||
|
t.Fatalf("duplicate task name: %q", tsk.Name())
|
||||||
|
}
|
||||||
|
|
||||||
|
require.NotNil(t, tsk)
|
||||||
|
if sel, ok := tsk.(task.Selector); ok {
|
||||||
|
taskTagsByName[tsk.Name()] = sel.Selectors()
|
||||||
|
} else {
|
||||||
|
taskTagsByName[tsk.Name()] = []string{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var constructorCount int
|
||||||
|
constructorsPerPackage := getCatalogerConstructors(t)
|
||||||
|
for _, constructors := range constructorsPerPackage {
|
||||||
|
constructorCount += constructors.Size()
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.Equal(t, len(taskTagsByName), constructorCount, "mismatch in number of cataloger constructors and task names")
|
||||||
|
|
||||||
|
for taskName, tags := range taskTagsByName {
|
||||||
|
if !strset.New(tags...).HasAny(pkgcataloging.ImageTag, pkgcataloging.DirectoryTag) {
|
||||||
|
t.Errorf("task %q is missing 'directory' or 'image' a tag", taskName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestAllPackageCatalogersRepresentedInSource(t *testing.T) {
|
||||||
|
// find all functions in syft/pkg/cataloger/** that either:
|
||||||
|
// - match the name glob "New*Cataloger"
|
||||||
|
// - are in cataloger.go and match the name glob "New*"
|
||||||
|
//
|
||||||
|
// Then:
|
||||||
|
// - keep track of all packages with cataloger constructors
|
||||||
|
// - keep track of all constructors
|
||||||
|
constructorsPerPackage := getCatalogerConstructors(t)
|
||||||
|
|
||||||
|
// look at the source file in internal/task/package_tasks.go:
|
||||||
|
// - ensure all go packages that have constructors are imported
|
||||||
|
// - ensure there is a reference to all package constructors
|
||||||
|
assertAllPackageCatalogersRepresented(t, constructorsPerPackage)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCatalogerConstructors(t *testing.T) map[string]*strset.Set {
|
||||||
|
t.Helper()
|
||||||
|
root := repoRoot(t)
|
||||||
|
catalogerPath := filepath.Join(root, "syft", "pkg", "cataloger")
|
||||||
|
|
||||||
|
constructorsPerPackage := make(map[string]*strset.Set)
|
||||||
|
|
||||||
|
err := filepath.Walk(catalogerPath, func(path string, info os.FileInfo, err error) error {
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// ignore directories and test files...
|
||||||
|
if info.IsDir() || strings.HasSuffix(info.Name(), "_test.go") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
partialResults := getConstructorsFromExpectedFile(t, path, info)
|
||||||
|
|
||||||
|
constructorsPerPackage = mergeConstructors(constructorsPerPackage, partialResults)
|
||||||
|
|
||||||
|
partialResults = getCatalogerConstructorsFromPackage(t, path, info)
|
||||||
|
|
||||||
|
constructorsPerPackage = mergeConstructors(constructorsPerPackage, partialResults)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// remove some exceptions
|
||||||
|
delete(constructorsPerPackage, "generic") // this is not an actual cataloger
|
||||||
|
|
||||||
|
return constructorsPerPackage
|
||||||
|
}
|
||||||
|
|
||||||
|
func getConstructorsFromExpectedFile(t *testing.T, path string, info os.FileInfo) map[string][]string {
|
||||||
|
constructorsPerPackage := make(map[string][]string)
|
||||||
|
|
||||||
|
if !strings.HasSuffix(info.Name(), "cataloger.go") && !strings.HasSuffix(info.Name(), "catalogers.go") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
fset := token.NewFileSet()
|
||||||
|
node, err := parser.ParseFile(fset, path, nil, parser.ParseComments)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
for _, f := range node.Decls {
|
||||||
|
fn, ok := f.(*ast.FuncDecl)
|
||||||
|
if !ok || fn.Recv != nil || !strings.HasPrefix(fn.Name.Name, "New") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
pkg := node.Name.Name
|
||||||
|
constructorsPerPackage[pkg] = append(constructorsPerPackage[pkg], fn.Name.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return constructorsPerPackage
|
||||||
|
}
|
||||||
|
|
||||||
|
func getCatalogerConstructorsFromPackage(t *testing.T, path string, info os.FileInfo) map[string][]string {
|
||||||
|
constructorsPerPackage := make(map[string][]string)
|
||||||
|
|
||||||
|
if info.IsDir() || !strings.HasSuffix(info.Name(), ".go") {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
fset := token.NewFileSet()
|
||||||
|
node, err := parser.ParseFile(fset, path, nil, parser.ParseComments)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
for _, f := range node.Decls {
|
||||||
|
fn, ok := f.(*ast.FuncDecl)
|
||||||
|
if !ok || fn.Recv != nil || !strings.HasPrefix(fn.Name.Name, "New") || !strings.HasSuffix(fn.Name.Name, "Cataloger") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
pkg := node.Name.Name
|
||||||
|
constructorsPerPackage[pkg] = append(constructorsPerPackage[pkg], fn.Name.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return constructorsPerPackage
|
||||||
|
}
|
||||||
|
|
||||||
|
func assertAllPackageCatalogersRepresented(t *testing.T, constructorsPerPackage map[string]*strset.Set) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
contents, err := os.ReadFile(filepath.Join(repoRoot(t), "internal", "task", "package_tasks.go"))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// ensure all packages (keys) are represented in the package_tasks.go file
|
||||||
|
for pkg, constructors := range constructorsPerPackage {
|
||||||
|
if !assert.True(t, bytes.Contains(contents, []byte(pkg)), "missing package %q", pkg) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
for _, constructor := range constructors.List() {
|
||||||
|
assert.True(t, bytes.Contains(contents, []byte(constructor)), "missing constructor %q for package %q", constructor, pkg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func repoRoot(t testing.TB) string {
|
||||||
|
t.Helper()
|
||||||
|
root, err := exec.Command("git", "rev-parse", "--show-toplevel").Output()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("unable to find repo root dir: %+v", err)
|
||||||
|
}
|
||||||
|
absRepoRoot, err := filepath.Abs(strings.TrimSpace(string(root)))
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal("unable to get abs path to repo root:", err)
|
||||||
|
}
|
||||||
|
return absRepoRoot
|
||||||
|
}
|
||||||
|
|
||||||
|
func mergeConstructors(constructorsPerPackage map[string]*strset.Set, partialResults map[string][]string) map[string]*strset.Set {
|
||||||
|
for pkg, constructors := range partialResults {
|
||||||
|
if _, ok := constructorsPerPackage[pkg]; !ok {
|
||||||
|
constructorsPerPackage[pkg] = strset.New()
|
||||||
|
}
|
||||||
|
constructorsPerPackage[pkg].Add(constructors...)
|
||||||
|
}
|
||||||
|
|
||||||
|
return constructorsPerPackage
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user