mirror of
https://github.com/anchore/syft
synced 2024-11-10 06:14:16 +00:00
Add Linux Kernel cataloger (#1694)
* add kernel handler Signed-off-by: Avi Deitcher <avi@deitcher.net> * [wip] combine kernel and kernel module cataloging Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * [wip] combine kernel and kernel module cataloging Signed-off-by: Alex Goodman <alex.goodman@anchore.com> Signed-off-by: Avi Deitcher <avi@deitcher.net> * rename Kernel package to LinuxKernel package Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * split kernel and module packages within cataloger Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * wire up application configuration with kernel cataloger options Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * dont use references for packages on relationships Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * fix linting and tests Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * kernel cataloger should be resistent to partial failure Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * log upon kernel module metadata missing Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * add tests for linux kernel cataloger Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * update integration tests Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * update cli package test counts Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * add evidence annotations for kernel packages Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * reduce noise in cli test output Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * missed cli test to reduce noise for Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * fix package counts Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * update docs with linux kernel cataloging refs Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * bump json schema with new metadata fields Signed-off-by: Alex Goodman <alex.goodman@anchore.com> --------- Signed-off-by: Avi Deitcher <avi@deitcher.net> Signed-off-by: Alex Goodman <alex.goodman@anchore.com> Signed-off-by: <> Co-authored-by: Alex Goodman <alex.goodman@anchore.com>
This commit is contained in:
parent
5d156b8241
commit
cc731c7b19
37 changed files with 2516 additions and 78 deletions
|
@ -45,6 +45,8 @@ For commercial support options with Syft or Grype, please [contact Anchore](http
|
|||
- Java (jar, ear, war, par, sar, nar, native-image)
|
||||
- JavaScript (npm, yarn)
|
||||
- Jenkins Plugins (jpi, hpi)
|
||||
- Linux kernel archives (vmlinz)
|
||||
- Linux kernel modules (ko)
|
||||
- Nix (outputs in /nix/store)
|
||||
- PHP (composer)
|
||||
- Python (wheel, egg, poetry, requirements.txt)
|
||||
|
@ -513,6 +515,11 @@ golang:
|
|||
# SYFT_GOLANG_LOCAL_MOD_CACHE_DIR env var
|
||||
local-mod-cache-dir: ""
|
||||
|
||||
linux-kernel:
|
||||
# whether to catalog linux kernel modules found within lib/modules/** directories
|
||||
# SYFT_LINUX_KERNEL_CATALOG_MODULES env var
|
||||
catalog-modules: true
|
||||
|
||||
# cataloging file contents is exposed through the power-user subcommand
|
||||
file-contents:
|
||||
cataloger:
|
||||
|
|
1
go.mod
1
go.mod
|
@ -53,6 +53,7 @@ require (
|
|||
github.com/Masterminds/sprig/v3 v3.2.3
|
||||
github.com/anchore/go-logger v0.0.0-20220728155337-03b66a5207d8
|
||||
github.com/anchore/stereoscope v0.0.0-20230406143206-e95d60a265e3
|
||||
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da
|
||||
github.com/docker/docker v23.0.3+incompatible
|
||||
github.com/google/go-containerregistry v0.14.0
|
||||
github.com/google/licensecheck v0.3.1
|
||||
|
|
2
go.sum
2
go.sum
|
@ -145,6 +145,8 @@ github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ
|
|||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da h1:ZOjWpVsFZ06eIhnh4mkaceTiVoktdU67+M7KDHJ268M=
|
||||
github.com/deitch/magic v0.0.0-20230404182410-1ff89d7342da/go.mod h1:B3tI9iGHi4imdLi4Asdha1Sc6feLMTfPLXh9IUYmysk=
|
||||
github.com/dgrijalva/jwt-go/v4 v4.0.0-preview1/go.mod h1:+hnT3ywWDTAFrW5aE+u2Sa/wT555ZqwoCS+pk3p6ry4=
|
||||
github.com/docker/cli v23.0.1+incompatible h1:LRyWITpGzl2C9e9uGxzisptnxAn1zfZKXy13Ul2Q5oM=
|
||||
github.com/docker/cli v23.0.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
|
|
|
@ -19,6 +19,7 @@ import (
|
|||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger"
|
||||
golangCataloger "github.com/anchore/syft/syft/pkg/cataloger/golang"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/kernel"
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -50,6 +51,7 @@ type Application struct {
|
|||
Catalogers []string `yaml:"catalogers" json:"catalogers" mapstructure:"catalogers"`
|
||||
Package pkg `yaml:"package" json:"package" mapstructure:"package"`
|
||||
Golang golang `yaml:"golang" json:"golang" mapstructure:"golang"`
|
||||
LinuxKernel linuxKernel `yaml:"linux-kernel" json:"linux-kernel" mapstructure:"linux-kernel"`
|
||||
Attest attest `yaml:"attest" json:"attest" mapstructure:"attest"`
|
||||
FileMetadata FileMetadata `yaml:"file-metadata" json:"file-metadata" mapstructure:"file-metadata"`
|
||||
FileClassification fileClassification `yaml:"file-classification" json:"file-classification" mapstructure:"file-classification"`
|
||||
|
@ -76,6 +78,9 @@ func (cfg Application) ToCatalogerConfig() cataloger.Config {
|
|||
SearchLocalModCacheLicenses: cfg.Golang.SearchLocalModCacheLicenses,
|
||||
LocalModCacheDir: cfg.Golang.LocalModCacheDir,
|
||||
},
|
||||
LinuxKernel: kernel.LinuxCatalogerConfig{
|
||||
CatalogModules: cfg.LinuxKernel.CatalogModules,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
|
11
internal/config/linux_kernel.go
Normal file
11
internal/config/linux_kernel.go
Normal file
|
@ -0,0 +1,11 @@
|
|||
package config
|
||||
|
||||
import "github.com/spf13/viper"
|
||||
|
||||
type linuxKernel struct {
|
||||
CatalogModules bool `json:"catalog-modules" yaml:"catalog-modules" mapstructure:"catalog-modules"`
|
||||
}
|
||||
|
||||
func (cfg linuxKernel) loadDefaultValues(v *viper.Viper) {
|
||||
v.SetDefault("linux-kernel.catalog-modules", true)
|
||||
}
|
|
@ -6,5 +6,5 @@ const (
|
|||
|
||||
// JSONSchemaVersion is the current schema version output by the JSON encoder
|
||||
// This is roughly following the "SchemaVer" guidelines for versioning the JSON schema. Please see schema/json/README.md for details on how to increment.
|
||||
JSONSchemaVersion = "7.1.2"
|
||||
JSONSchemaVersion = "7.1.3"
|
||||
)
|
||||
|
|
|
@ -45,6 +45,8 @@ type artifactMetadataContainer struct {
|
|||
Hackage pkg.HackageMetadata
|
||||
Java pkg.JavaMetadata
|
||||
KbPackage pkg.KbPackageMetadata
|
||||
LinuxKernel pkg.LinuxKernelMetadata
|
||||
LinuxKernelModule pkg.LinuxKernelModuleMetadata
|
||||
Nix pkg.NixStoreMetadata
|
||||
NpmPackage pkg.NpmPackageJSONMetadata
|
||||
NpmPackageLock pkg.NpmPackageLockJSONMetadata
|
||||
|
|
1768
schema/json/schema-7.1.3.json
Normal file
1768
schema/json/schema-7.1.3.json
Normal file
File diff suppressed because it is too large
Load diff
|
@ -6,6 +6,7 @@ import (
|
|||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
//nolint:funlen
|
||||
func SourceInfo(p pkg.Package) string {
|
||||
answer := ""
|
||||
switch p.Type {
|
||||
|
@ -45,6 +46,10 @@ func SourceInfo(p pkg.Package) string {
|
|||
answer = "acquired package info from cabal or stack manifest files"
|
||||
case pkg.HexPkg:
|
||||
answer = "acquired package info from rebar3 or mix manifest file"
|
||||
case pkg.LinuxKernelPkg:
|
||||
answer = "acquired package info from linux kernel archive"
|
||||
case pkg.LinuxKernelModulePkg:
|
||||
answer = "acquired package info from linux kernel module files"
|
||||
case pkg.NixPkg:
|
||||
answer = "acquired package info from nix store path"
|
||||
default:
|
||||
|
|
|
@ -199,6 +199,22 @@ func Test_SourceInfo(t *testing.T) {
|
|||
"from rebar3 or mix manifest file",
|
||||
},
|
||||
},
|
||||
{
|
||||
input: pkg.Package{
|
||||
Type: pkg.LinuxKernelPkg,
|
||||
},
|
||||
expected: []string{
|
||||
"from linux kernel archive",
|
||||
},
|
||||
},
|
||||
{
|
||||
input: pkg.Package{
|
||||
Type: pkg.LinuxKernelModulePkg,
|
||||
},
|
||||
expected: []string{
|
||||
"from linux kernel module files",
|
||||
},
|
||||
},
|
||||
{
|
||||
input: pkg.Package{
|
||||
Type: pkg.NixPkg,
|
||||
|
|
|
@ -89,7 +89,7 @@
|
|||
}
|
||||
},
|
||||
"schema": {
|
||||
"version": "7.1.2",
|
||||
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-7.1.2.json"
|
||||
"version": "7.1.3",
|
||||
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-7.1.3.json"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -185,7 +185,7 @@
|
|||
}
|
||||
},
|
||||
"schema": {
|
||||
"version": "7.1.2",
|
||||
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-7.1.2.json"
|
||||
"version": "7.1.3",
|
||||
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-7.1.3.json"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -112,7 +112,7 @@
|
|||
}
|
||||
},
|
||||
"schema": {
|
||||
"version": "7.1.2",
|
||||
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-7.1.2.json"
|
||||
"version": "7.1.3",
|
||||
"url": "https://raw.githubusercontent.com/anchore/syft/main/schema/json/schema-7.1.3.json"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import (
|
|||
"github.com/anchore/syft/syft/pkg/cataloger/haskell"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/java"
|
||||
"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/php"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/portage"
|
||||
|
@ -55,6 +56,7 @@ func ImageCatalogers(cfg Config) []pkg.Cataloger {
|
|||
nix.NewStoreCataloger(),
|
||||
sbom.NewSBOMCataloger(),
|
||||
binary.NewCataloger(),
|
||||
kernel.NewLinuxKernelCataloger(cfg.Kernel()),
|
||||
}, cfg.Catalogers)
|
||||
}
|
||||
|
||||
|
@ -88,6 +90,7 @@ func DirectoryCatalogers(cfg Config) []pkg.Cataloger {
|
|||
binary.NewCataloger(),
|
||||
elixir.NewMixLockCataloger(),
|
||||
erlang.NewRebarLockCataloger(),
|
||||
kernel.NewLinuxKernelCataloger(cfg.Kernel()),
|
||||
nix.NewStoreCataloger(),
|
||||
}, cfg.Catalogers)
|
||||
}
|
||||
|
@ -126,6 +129,7 @@ func AllCatalogers(cfg Config) []pkg.Cataloger {
|
|||
binary.NewCataloger(),
|
||||
elixir.NewMixLockCataloger(),
|
||||
erlang.NewRebarLockCataloger(),
|
||||
kernel.NewLinuxKernelCataloger(cfg.Kernel()),
|
||||
nix.NewStoreCataloger(),
|
||||
}, cfg.Catalogers)
|
||||
}
|
||||
|
|
|
@ -3,11 +3,15 @@ package cataloger
|
|||
import (
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/golang"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/java"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/kernel"
|
||||
)
|
||||
|
||||
// TODO: these field naming vs helper function naming schemes are inconsistent.
|
||||
|
||||
type Config struct {
|
||||
Search SearchConfig
|
||||
Golang golang.GoCatalogerOpts
|
||||
LinuxKernel kernel.LinuxCatalogerConfig
|
||||
Catalogers []string
|
||||
Parallelism int
|
||||
}
|
||||
|
@ -16,6 +20,7 @@ func DefaultConfig() Config {
|
|||
return Config{
|
||||
Search: DefaultSearchConfig(),
|
||||
Parallelism: 1,
|
||||
LinuxKernel: kernel.DefaultLinuxCatalogerConfig(),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -29,3 +34,7 @@ func (c Config) Java() java.Config {
|
|||
func (c Config) Go() golang.GoCatalogerOpts {
|
||||
return c.Golang
|
||||
}
|
||||
|
||||
func (c Config) Kernel() kernel.LinuxCatalogerConfig {
|
||||
return c.LinuxKernel
|
||||
}
|
||||
|
|
127
syft/pkg/cataloger/kernel/cataloger.go
Normal file
127
syft/pkg/cataloger/kernel/cataloger.go
Normal file
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
Package kernel provides a concrete Cataloger implementation for linux kernel and module files.
|
||||
*/
|
||||
package kernel
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/go-multierror"
|
||||
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
var _ pkg.Cataloger = (*LinuxKernelCataloger)(nil)
|
||||
|
||||
type LinuxCatalogerConfig struct {
|
||||
CatalogModules bool
|
||||
}
|
||||
|
||||
type LinuxKernelCataloger struct {
|
||||
cfg LinuxCatalogerConfig
|
||||
}
|
||||
|
||||
func DefaultLinuxCatalogerConfig() LinuxCatalogerConfig {
|
||||
return LinuxCatalogerConfig{
|
||||
CatalogModules: true,
|
||||
}
|
||||
}
|
||||
|
||||
var kernelArchiveGlobs = []string{
|
||||
"**/kernel",
|
||||
"**/kernel-*",
|
||||
"**/vmlinux",
|
||||
"**/vmlinux-*",
|
||||
"**/vmlinuz",
|
||||
"**/vmlinuz-*",
|
||||
}
|
||||
|
||||
var kernelModuleGlobs = []string{
|
||||
"**/lib/modules/**/*.ko",
|
||||
}
|
||||
|
||||
// NewLinuxKernelCataloger returns a new kernel files cataloger object.
|
||||
func NewLinuxKernelCataloger(cfg LinuxCatalogerConfig) *LinuxKernelCataloger {
|
||||
return &LinuxKernelCataloger{
|
||||
cfg: cfg,
|
||||
}
|
||||
}
|
||||
|
||||
func (l LinuxKernelCataloger) Name() string {
|
||||
return "linux-kernel-cataloger"
|
||||
}
|
||||
|
||||
func (l LinuxKernelCataloger) Catalog(resolver source.FileResolver) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
var allPackages []pkg.Package
|
||||
var allRelationships []artifact.Relationship
|
||||
var errs error
|
||||
|
||||
kernelPackages, kernelRelationships, err := generic.NewCataloger(l.Name()).WithParserByGlobs(parseLinuxKernelFile, kernelArchiveGlobs...).Catalog(resolver)
|
||||
if err != nil {
|
||||
errs = multierror.Append(errs, err)
|
||||
}
|
||||
|
||||
allRelationships = append(allRelationships, kernelRelationships...)
|
||||
allPackages = append(allPackages, kernelPackages...)
|
||||
|
||||
if l.cfg.CatalogModules {
|
||||
modulePackages, moduleRelationships, err := generic.NewCataloger(l.Name()).WithParserByGlobs(parseLinuxKernelModuleFile, kernelModuleGlobs...).Catalog(resolver)
|
||||
if err != nil {
|
||||
errs = multierror.Append(errs, err)
|
||||
}
|
||||
|
||||
allPackages = append(allPackages, modulePackages...)
|
||||
|
||||
moduleToKernelRelationships := createKernelToModuleRelationships(kernelPackages, modulePackages)
|
||||
allRelationships = append(allRelationships, moduleRelationships...)
|
||||
allRelationships = append(allRelationships, moduleToKernelRelationships...)
|
||||
}
|
||||
|
||||
return allPackages, allRelationships, errs
|
||||
}
|
||||
|
||||
func createKernelToModuleRelationships(kernelPackages, modulePackages []pkg.Package) []artifact.Relationship {
|
||||
// organize kernel and module packages by kernel version
|
||||
kernelPackagesByVersion := make(map[string][]*pkg.Package)
|
||||
for idx, p := range kernelPackages {
|
||||
kernelPackagesByVersion[p.Version] = append(kernelPackagesByVersion[p.Version], &kernelPackages[idx])
|
||||
}
|
||||
|
||||
modulesByKernelVersion := make(map[string][]*pkg.Package)
|
||||
for idx, p := range modulePackages {
|
||||
m, ok := p.Metadata.(pkg.LinuxKernelModuleMetadata)
|
||||
if !ok {
|
||||
log.Debug("linux-kernel-module package found without metadata: %s@%s", p.Name, p.Version)
|
||||
continue
|
||||
}
|
||||
modulesByKernelVersion[m.KernelVersion] = append(modulesByKernelVersion[m.KernelVersion], &modulePackages[idx])
|
||||
}
|
||||
|
||||
// create relationships between kernel and modules: [module] --(depends on)--> [kernel]
|
||||
// since we try to use singular directions for relationships, we'll use "dependency of" here instead:
|
||||
// [kernel] --(dependency of)--> [module]
|
||||
var moduleToKernelRelationships []artifact.Relationship
|
||||
for kernelVersion, modules := range modulesByKernelVersion {
|
||||
kps, ok := kernelPackagesByVersion[kernelVersion]
|
||||
if !ok {
|
||||
// it's ok if there is a module that has no installed kernel...
|
||||
continue
|
||||
}
|
||||
|
||||
// we don't know which kernel is the "right" one, so we'll create a relationship for each one
|
||||
for _, kp := range kps {
|
||||
for _, mp := range modules {
|
||||
moduleToKernelRelationships = append(moduleToKernelRelationships, artifact.Relationship{
|
||||
// note: relationships should have Package objects, not pointers
|
||||
From: *kp,
|
||||
To: *mp,
|
||||
Type: artifact.DependencyOfRelationship,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return moduleToKernelRelationships
|
||||
}
|
92
syft/pkg/cataloger/kernel/cataloger_test.go
Normal file
92
syft/pkg/cataloger/kernel/cataloger_test.go
Normal file
|
@ -0,0 +1,92 @@
|
|||
package kernel
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
func Test_JavascriptCataloger(t *testing.T) {
|
||||
kernelPkg := pkg.Package{
|
||||
Name: "linux-kernel",
|
||||
Version: "6.2.9-200.fc37.x86_64",
|
||||
FoundBy: "linux-kernel-cataloger",
|
||||
Locations: source.NewLocationSet(
|
||||
source.NewVirtualLocation(
|
||||
"/lib/modules/6.2.9-200.fc37.x86_64/vmlinuz",
|
||||
"/lib/modules/6.2.9-200.fc37.x86_64/vmlinuz",
|
||||
),
|
||||
),
|
||||
Type: pkg.LinuxKernelPkg,
|
||||
PURL: "pkg:generic/linux-kernel@6.2.9-200.fc37.x86_64",
|
||||
MetadataType: pkg.LinuxKernelMetadataType,
|
||||
Metadata: pkg.LinuxKernelMetadata{
|
||||
Name: "",
|
||||
Architecture: "x86",
|
||||
Version: "6.2.9-200.fc37.x86_64",
|
||||
ExtendedVersion: "6.2.9-200.fc37.x86_64 (mockbuild@bkernel02.iad2.fedoraproject.org) #1 SMP PREEMPT_DYNAMIC Thu Mar 30 22:31:57 UTC 2023",
|
||||
BuildTime: "",
|
||||
Author: "",
|
||||
Format: "bzImage",
|
||||
RWRootFS: false,
|
||||
SwapDevice: 0,
|
||||
RootDevice: 0,
|
||||
VideoMode: "Video mode 65535",
|
||||
},
|
||||
}
|
||||
|
||||
kernelModulePkg := pkg.Package{
|
||||
Name: "fsa4480",
|
||||
Version: "",
|
||||
FoundBy: "linux-kernel-cataloger",
|
||||
Locations: source.NewLocationSet(
|
||||
source.NewVirtualLocation("/lib/modules/6.2.9-200.fc37.x86_64/kernel/drivers/usb/typec/mux/fsa4480.ko",
|
||||
"/lib/modules/6.2.9-200.fc37.x86_64/kernel/drivers/usb/typec/mux/fsa4480.ko",
|
||||
),
|
||||
),
|
||||
Licenses: []string{
|
||||
"GPL v2",
|
||||
},
|
||||
Type: pkg.LinuxKernelModulePkg,
|
||||
PURL: "pkg:generic/fsa4480",
|
||||
MetadataType: pkg.LinuxKernelModuleMetadataType,
|
||||
Metadata: pkg.LinuxKernelModuleMetadata{
|
||||
Name: "fsa4480",
|
||||
Version: "",
|
||||
SourceVersion: "",
|
||||
License: "GPL v2",
|
||||
Path: "/lib/modules/6.2.9-200.fc37.x86_64/kernel/drivers/usb/typec/mux/fsa4480.ko",
|
||||
Description: "ON Semiconductor FSA4480 driver",
|
||||
KernelVersion: "6.2.9-200.fc37.x86_64",
|
||||
VersionMagic: "6.2.9-200.fc37.x86_64 SMP preempt mod_unload ",
|
||||
Parameters: map[string]pkg.LinuxKernelModuleParameter{},
|
||||
},
|
||||
}
|
||||
|
||||
expectedPkgs := []pkg.Package{
|
||||
kernelPkg,
|
||||
kernelModulePkg,
|
||||
}
|
||||
expectedRelationships := []artifact.Relationship{
|
||||
{
|
||||
From: kernelPkg,
|
||||
To: kernelModulePkg,
|
||||
Type: artifact.DependencyOfRelationship,
|
||||
},
|
||||
}
|
||||
|
||||
pkgtest.NewCatalogTester().
|
||||
WithImageResolver(t, "image-kernel-and-modules").
|
||||
IgnoreLocationLayer().
|
||||
Expects(expectedPkgs, expectedRelationships).
|
||||
TestCataloger(t,
|
||||
NewLinuxKernelCataloger(
|
||||
LinuxCatalogerConfig{
|
||||
CatalogModules: true,
|
||||
},
|
||||
),
|
||||
)
|
||||
}
|
71
syft/pkg/cataloger/kernel/package.go
Normal file
71
syft/pkg/cataloger/kernel/package.go
Normal file
|
@ -0,0 +1,71 @@
|
|||
package kernel
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/anchore/packageurl-go"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
const linuxKernelPackageName = "linux-kernel"
|
||||
|
||||
func newLinuxKernelPackage(metadata pkg.LinuxKernelMetadata, locations ...source.Location) pkg.Package {
|
||||
p := pkg.Package{
|
||||
Name: linuxKernelPackageName,
|
||||
Version: metadata.Version,
|
||||
Locations: source.NewLocationSet(locations...),
|
||||
PURL: packageURL(linuxKernelPackageName, metadata.Version),
|
||||
Type: pkg.LinuxKernelPkg,
|
||||
MetadataType: pkg.LinuxKernelMetadataType,
|
||||
Metadata: metadata,
|
||||
}
|
||||
|
||||
p.SetID()
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
func newLinuxKernelModulePackage(metadata pkg.LinuxKernelModuleMetadata, locations ...source.Location) pkg.Package {
|
||||
var licenses []string
|
||||
if metadata.License != "" {
|
||||
licenses = []string{metadata.License}
|
||||
} else {
|
||||
licenses = []string{}
|
||||
}
|
||||
|
||||
p := pkg.Package{
|
||||
Name: metadata.Name,
|
||||
Version: metadata.Version,
|
||||
Locations: source.NewLocationSet(locations...),
|
||||
Licenses: licenses,
|
||||
PURL: packageURL(metadata.Name, metadata.Version),
|
||||
Type: pkg.LinuxKernelModulePkg,
|
||||
MetadataType: pkg.LinuxKernelModuleMetadataType,
|
||||
Metadata: metadata,
|
||||
}
|
||||
|
||||
p.SetID()
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
// packageURL returns the PURL for the specific Kernel package (see https://github.com/package-url/purl-spec)
|
||||
func packageURL(name, version string) string {
|
||||
var namespace string
|
||||
|
||||
fields := strings.SplitN(name, "/", 2)
|
||||
if len(fields) > 1 {
|
||||
namespace = fields[0]
|
||||
name = fields[1]
|
||||
}
|
||||
|
||||
return packageurl.NewPackageURL(
|
||||
packageurl.TypeGeneric,
|
||||
namespace,
|
||||
name,
|
||||
version,
|
||||
nil,
|
||||
"",
|
||||
).ToString()
|
||||
}
|
92
syft/pkg/cataloger/kernel/parse_linux_kernel_file.go
Normal file
92
syft/pkg/cataloger/kernel/parse_linux_kernel_file.go
Normal file
|
@ -0,0 +1,92 @@
|
|||
package kernel
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/deitch/magic/pkg/magic"
|
||||
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/internal/unionreader"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
const linuxKernelMagicName = "Linux kernel"
|
||||
|
||||
func parseLinuxKernelFile(_ source.FileResolver, _ *generic.Environment, reader source.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
unionReader, err := unionreader.GetUnionReader(reader)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("unable to get union reader for file: %w", err)
|
||||
}
|
||||
magicType, err := magic.GetType(unionReader)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("unable to get magic type for file: %w", err)
|
||||
}
|
||||
if len(magicType) < 1 || magicType[0] != linuxKernelMagicName {
|
||||
return nil, nil, nil
|
||||
}
|
||||
metadata := parseLinuxKernelMetadata(magicType)
|
||||
if metadata.Version == "" {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
return []pkg.Package{
|
||||
newLinuxKernelPackage(
|
||||
metadata,
|
||||
reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
),
|
||||
}, nil, nil
|
||||
}
|
||||
|
||||
func parseLinuxKernelMetadata(magicType []string) (p pkg.LinuxKernelMetadata) {
|
||||
// Linux kernel x86 boot executable bzImage,
|
||||
// version 5.10.121-linuxkit (root@buildkitsandbox) #1 SMP Fri Dec 2 10:35:42 UTC 2022,
|
||||
// RO-rootFS,
|
||||
// swap_dev 0XA,
|
||||
// Normal VGA
|
||||
for _, t := range magicType {
|
||||
switch {
|
||||
case strings.HasPrefix(t, "x86 "):
|
||||
p.Architecture = "x86"
|
||||
case strings.Contains(t, "ARM64 "):
|
||||
p.Architecture = "arm64"
|
||||
case strings.Contains(t, "ARM "):
|
||||
p.Architecture = "arm"
|
||||
case t == "bzImage":
|
||||
p.Format = "bzImage"
|
||||
case t == "zImage":
|
||||
p.Format = "zImage"
|
||||
case strings.HasPrefix(t, "version "):
|
||||
p.ExtendedVersion = strings.TrimPrefix(t, "version ")
|
||||
fields := strings.Fields(p.ExtendedVersion)
|
||||
if len(fields) > 0 {
|
||||
p.Version = fields[0]
|
||||
}
|
||||
case strings.Contains(t, "rootFS") && strings.HasPrefix(t, "RW-"):
|
||||
p.RWRootFS = true
|
||||
case strings.HasPrefix(t, "swap_dev "):
|
||||
swapDevStr := strings.TrimPrefix(t, "swap_dev ")
|
||||
swapDev, err := strconv.ParseInt(swapDevStr, 16, 32)
|
||||
if err != nil {
|
||||
log.Warnf("unable to parse swap device: %s", err)
|
||||
continue
|
||||
}
|
||||
p.SwapDevice = int(swapDev)
|
||||
case strings.HasPrefix(t, "root_dev "):
|
||||
rootDevStr := strings.TrimPrefix(t, "root_dev ")
|
||||
rootDev, err := strconv.ParseInt(rootDevStr, 16, 32)
|
||||
if err != nil {
|
||||
log.Warnf("unable to parse root device: %s", err)
|
||||
continue
|
||||
}
|
||||
p.SwapDevice = int(rootDev)
|
||||
case strings.Contains(t, "VGA") || strings.Contains(t, "Video"):
|
||||
p.VideoMode = t
|
||||
}
|
||||
}
|
||||
return p
|
||||
}
|
157
syft/pkg/cataloger/kernel/parse_linux_kernel_module_file.go
Normal file
157
syft/pkg/cataloger/kernel/parse_linux_kernel_module_file.go
Normal file
|
@ -0,0 +1,157 @@
|
|||
package kernel
|
||||
|
||||
import (
|
||||
"debug/elf"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/internal/unionreader"
|
||||
"github.com/anchore/syft/syft/source"
|
||||
)
|
||||
|
||||
const modinfoName = ".modinfo"
|
||||
|
||||
func parseLinuxKernelModuleFile(_ source.FileResolver, _ *generic.Environment, reader source.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
unionReader, err := unionreader.GetUnionReader(reader)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("unable to get union reader for file: %w", err)
|
||||
}
|
||||
metadata, err := parseLinuxKernelModuleMetadata(unionReader)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("unable to parse kernel module metadata: %w", err)
|
||||
}
|
||||
if metadata == nil || metadata.KernelVersion == "" {
|
||||
return nil, nil, nil
|
||||
}
|
||||
|
||||
metadata.Path = reader.Location.RealPath
|
||||
|
||||
return []pkg.Package{
|
||||
newLinuxKernelModulePackage(
|
||||
*metadata,
|
||||
reader.Location.WithAnnotation(pkg.EvidenceAnnotationKey, pkg.PrimaryEvidenceAnnotation),
|
||||
),
|
||||
}, nil, nil
|
||||
}
|
||||
|
||||
func parseLinuxKernelModuleMetadata(r unionreader.UnionReader) (p *pkg.LinuxKernelModuleMetadata, err error) {
|
||||
// filename: /lib/modules/5.15.0-1031-aws/kernel/zfs/zzstd.ko
|
||||
// version: 1.4.5a
|
||||
// license: Dual BSD/GPL
|
||||
// description: ZSTD Compression for ZFS
|
||||
// srcversion: F1F818A6E016499AB7F826E
|
||||
// depends: spl
|
||||
// retpoline: Y
|
||||
// name: zzstd
|
||||
// vermagic: 5.15.0-1031-aws SMP mod_unload modversions
|
||||
// sig_id: PKCS#7
|
||||
// signer: Build time autogenerated kernel key
|
||||
// sig_key: 49:A9:55:87:90:5B:33:41:AF:C0:A7:BE:2A:71:6C:D2:CA:34:E0:AE
|
||||
// sig_hashalgo: sha512
|
||||
//
|
||||
// OR
|
||||
//
|
||||
// filename: /home/ubuntu/eve/rootfs/lib/modules/5.10.121-linuxkit/kernel/drivers/net/wireless/realtek/rtl8821cu/8821cu.ko
|
||||
// version: v5.4.1_28754.20180921_COEX20180712-3232
|
||||
// author: Realtek Semiconductor Corp.
|
||||
// description: Realtek Wireless Lan Driver
|
||||
// license: GPL
|
||||
// srcversion: 960CCC648A0E0369171A2C9
|
||||
// depends: cfg80211
|
||||
// retpoline: Y
|
||||
// name: 8821cu
|
||||
// vermagic: 5.10.121-linuxkit SMP mod_unload
|
||||
p = &pkg.LinuxKernelModuleMetadata{
|
||||
Parameters: make(map[string]pkg.LinuxKernelModuleParameter),
|
||||
}
|
||||
f, err := elf.NewFile(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
modinfo := f.Section(modinfoName)
|
||||
if modinfo == nil {
|
||||
return nil, fmt.Errorf("no section %s", modinfoName)
|
||||
}
|
||||
b, err := modinfo.Data()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error reading secion %s: %w", modinfoName, err)
|
||||
}
|
||||
var (
|
||||
entry []byte
|
||||
)
|
||||
for _, b2 := range b {
|
||||
if b2 == 0 {
|
||||
if err := addLinuxKernelModuleEntry(p, entry); err != nil {
|
||||
return nil, fmt.Errorf("error parsing entry %s: %w", string(entry), err)
|
||||
}
|
||||
entry = []byte{}
|
||||
continue
|
||||
}
|
||||
entry = append(entry, b2)
|
||||
}
|
||||
if err := addLinuxKernelModuleEntry(p, entry); err != nil {
|
||||
return nil, fmt.Errorf("error parsing entry %s: %w", string(entry), err)
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
||||
|
||||
func addLinuxKernelModuleEntry(k *pkg.LinuxKernelModuleMetadata, entry []byte) error {
|
||||
if len(entry) == 0 {
|
||||
return nil
|
||||
}
|
||||
var key, value string
|
||||
parts := strings.SplitN(string(entry), "=", 2)
|
||||
if len(parts) > 0 {
|
||||
key = parts[0]
|
||||
}
|
||||
if len(parts) > 1 {
|
||||
value = parts[1]
|
||||
}
|
||||
|
||||
switch key {
|
||||
case "version":
|
||||
k.Version = value
|
||||
case "license":
|
||||
k.License = value
|
||||
case "author":
|
||||
k.Author = value
|
||||
case "name":
|
||||
k.Name = value
|
||||
case "vermagic":
|
||||
k.VersionMagic = value
|
||||
fields := strings.Fields(value)
|
||||
if len(fields) > 0 {
|
||||
k.KernelVersion = fields[0]
|
||||
}
|
||||
case "srcversion":
|
||||
k.SourceVersion = value
|
||||
case "description":
|
||||
k.Description = value
|
||||
case "parm":
|
||||
parts := strings.SplitN(value, ":", 2)
|
||||
if len(parts) != 2 {
|
||||
return fmt.Errorf("invalid parm entry: %s", value)
|
||||
}
|
||||
if m, ok := k.Parameters[parts[0]]; !ok {
|
||||
k.Parameters[parts[0]] = pkg.LinuxKernelModuleParameter{Description: parts[1]}
|
||||
} else {
|
||||
m.Description = parts[1]
|
||||
}
|
||||
case "parmtype":
|
||||
parts := strings.SplitN(value, ":", 2)
|
||||
if len(parts) != 2 {
|
||||
return fmt.Errorf("invalid parmtype entry: %s", value)
|
||||
}
|
||||
if m, ok := k.Parameters[parts[0]]; !ok {
|
||||
k.Parameters[parts[0]] = pkg.LinuxKernelModuleParameter{Type: parts[1]}
|
||||
} else {
|
||||
m.Type = parts[1]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
FROM fedora:37@sha256:3f987b7657e944cf87a129cc262982d4f80e38bd98f7db313ccaf90ca7069dd2
|
||||
|
||||
RUN dnf install 'dnf-command(download)' cpio xz -y
|
||||
RUN dnf download kernel-core kernel-modules-core -y
|
||||
|
||||
RUN rpm2cpio kernel-core-*.rpm | cpio -t && \
|
||||
rpm2cpio kernel-core-*.rpm | cpio -idmv ./lib/modules/6.2.9-200.fc37.x86_64/vmlinuz
|
||||
|
||||
RUN rpm2cpio kernel-modules-core-*.rpm | cpio -t && \
|
||||
rpm2cpio kernel-modules-core-*.rpm | cpio -idmv ./lib/modules/6.2.9-200.fc37.x86_64/kernel/drivers/usb/typec/mux/fsa4480.ko.xz
|
||||
|
||||
RUN unxz /lib/modules/6.2.9-200.fc37.x86_64/kernel/drivers/usb/typec/mux/fsa4480.ko.xz
|
||||
|
||||
FROM scratch
|
||||
|
||||
COPY --from=0 /lib/modules/6.2.9-200.fc37.x86_64/vmlinuz /lib/modules/6.2.9-200.fc37.x86_64/vmlinuz
|
||||
COPY --from=0 /lib/modules/6.2.9-200.fc37.x86_64/kernel/drivers/usb/typec/mux/fsa4480.ko /lib/modules/6.2.9-200.fc37.x86_64/kernel/drivers/usb/typec/mux/fsa4480.ko
|
34
syft/pkg/linux_kernel_metadata.go
Normal file
34
syft/pkg/linux_kernel_metadata.go
Normal file
|
@ -0,0 +1,34 @@
|
|||
package pkg
|
||||
|
||||
// LinuxKernelMetadata represents all captured data for a Linux kernel
|
||||
type LinuxKernelMetadata struct {
|
||||
Name string `mapstructure:"name" json:"name" cyclonedx:"name"`
|
||||
Architecture string `mapstructure:"architecture" json:"architecture" cyclonedx:"architecture"`
|
||||
Version string `mapstructure:"version" json:"version" cyclonedx:"version"`
|
||||
ExtendedVersion string `mapstructure:"extendedVersion" json:"extendedVersion,omitempty" cyclonedx:"extendedVersion"`
|
||||
BuildTime string `mapstructure:"buildTime" json:"buildTime,omitempty" cyclonedx:"buildTime"`
|
||||
Author string `mapstructure:"author" json:"author,omitempty" cyclonedx:"author"`
|
||||
Format string `mapstructure:"format" json:"format,omitempty" cyclonedx:"format"`
|
||||
RWRootFS bool `mapstructure:"rwRootFS" json:"rwRootFS,omitempty" cyclonedx:"rwRootFS"`
|
||||
SwapDevice int `mapstructure:"swapDevice" json:"swapDevice,omitempty" cyclonedx:"swapDevice"`
|
||||
RootDevice int `mapstructure:"rootDevice" json:"rootDevice,omitempty" cyclonedx:"rootDevice"`
|
||||
VideoMode string `mapstructure:"videoMode" json:"videoMode,omitempty" cyclonedx:"videoMode"`
|
||||
}
|
||||
|
||||
type LinuxKernelModuleMetadata struct {
|
||||
Name string `mapstructure:"name" json:"name,omitempty" cyclonedx:"name"`
|
||||
Version string `mapstructure:"version" json:"version,omitempty" cyclonedx:"version"`
|
||||
SourceVersion string `mapstructure:"sourceVersion" json:"sourceVersion,omitempty" cyclonedx:"sourceVersion"`
|
||||
Path string `mapstructure:"path" json:"path,omitempty" cyclonedx:"path"`
|
||||
Description string `mapstructure:"description" json:"description,omitempty" cyclonedx:"description"`
|
||||
Author string `mapstructure:"author" json:"author,omitempty" cyclonedx:"author"`
|
||||
License string `mapstructure:"license" json:"license,omitempty" cyclonedx:"license"`
|
||||
KernelVersion string `mapstructure:"kernelVersion" json:"kernelVersion,omitempty" cyclonedx:"kernelVersion"`
|
||||
VersionMagic string `mapstructure:"versionMagic" json:"versionMagic,omitempty" cyclonedx:"versionMagic"`
|
||||
Parameters map[string]LinuxKernelModuleParameter `mapstructure:"parameters" json:"parameters,omitempty" cyclonedx:"parameters"`
|
||||
}
|
||||
|
||||
type LinuxKernelModuleParameter struct {
|
||||
Type string `mapstructure:"type" json:"type,omitempty" cyclonedx:"type"`
|
||||
Description string `mapstructure:"description" json:"description,omitempty" cyclonedx:"description"`
|
||||
}
|
|
@ -26,6 +26,8 @@ const (
|
|||
HackageMetadataType MetadataType = "HackageMetadataType"
|
||||
JavaMetadataType MetadataType = "JavaMetadata"
|
||||
KbPackageMetadataType MetadataType = "KbPackageMetadata"
|
||||
LinuxKernelMetadataType MetadataType = "LinuxKernelMetadata"
|
||||
LinuxKernelModuleMetadataType MetadataType = "LinuxKernelModuleMetadata"
|
||||
MixLockMetadataType MetadataType = "MixLockMetadataType"
|
||||
NixStoreMetadataType MetadataType = "NixStoreMetadata"
|
||||
NpmPackageJSONMetadataType MetadataType = "NpmPackageJsonMetadata"
|
||||
|
@ -55,6 +57,8 @@ var AllMetadataTypes = []MetadataType{
|
|||
HackageMetadataType,
|
||||
JavaMetadataType,
|
||||
KbPackageMetadataType,
|
||||
LinuxKernelMetadataType,
|
||||
LinuxKernelModuleMetadataType,
|
||||
MixLockMetadataType,
|
||||
NixStoreMetadataType,
|
||||
NpmPackageJSONMetadataType,
|
||||
|
@ -84,6 +88,8 @@ var MetadataTypeByName = map[MetadataType]reflect.Type{
|
|||
HackageMetadataType: reflect.TypeOf(HackageMetadata{}),
|
||||
JavaMetadataType: reflect.TypeOf(JavaMetadata{}),
|
||||
KbPackageMetadataType: reflect.TypeOf(KbPackageMetadata{}),
|
||||
LinuxKernelMetadataType: reflect.TypeOf(LinuxKernelMetadata{}),
|
||||
LinuxKernelModuleMetadataType: reflect.TypeOf(LinuxKernelModuleMetadata{}),
|
||||
MixLockMetadataType: reflect.TypeOf(MixLockMetadata{}),
|
||||
NixStoreMetadataType: reflect.TypeOf(NixStoreMetadata{}),
|
||||
NpmPackageJSONMetadataType: reflect.TypeOf(NpmPackageJSONMetadata{}),
|
||||
|
|
|
@ -26,6 +26,8 @@ const (
|
|||
JavaPkg Type = "java-archive"
|
||||
JenkinsPluginPkg Type = "jenkins-plugin"
|
||||
KbPkg Type = "msrc-kb"
|
||||
LinuxKernelPkg Type = "linux-kernel"
|
||||
LinuxKernelModulePkg Type = "linux-kernel-module"
|
||||
NixPkg Type = "nix"
|
||||
NpmPkg Type = "npm"
|
||||
PhpComposerPkg Type = "php-composer"
|
||||
|
@ -52,6 +54,8 @@ var AllPkgs = []Type{
|
|||
JavaPkg,
|
||||
JenkinsPluginPkg,
|
||||
KbPkg,
|
||||
LinuxKernelPkg,
|
||||
LinuxKernelModulePkg,
|
||||
NixPkg,
|
||||
NpmPkg,
|
||||
PhpComposerPkg,
|
||||
|
@ -88,6 +92,10 @@ func (t Type) PackageURLType() string {
|
|||
return packageurl.TypeHackage
|
||||
case JavaPkg, JenkinsPluginPkg:
|
||||
return packageurl.TypeMaven
|
||||
case LinuxKernelPkg:
|
||||
return "generic/linux-kernel"
|
||||
case LinuxKernelModulePkg:
|
||||
return packageurl.TypeGeneric
|
||||
case PhpComposerPkg:
|
||||
return packageurl.TypeComposer
|
||||
case PythonPkg:
|
||||
|
@ -114,7 +122,11 @@ func TypeFromPURL(p string) Type {
|
|||
return UnknownPkg
|
||||
}
|
||||
|
||||
return TypeByName(purl.Type)
|
||||
ptype := purl.Type
|
||||
if ptype == "generic" {
|
||||
ptype = purl.Name
|
||||
}
|
||||
return TypeByName(ptype)
|
||||
}
|
||||
|
||||
func TypeByName(name string) Type {
|
||||
|
@ -155,6 +167,10 @@ func TypeByName(name string) Type {
|
|||
return PortagePkg
|
||||
case packageurl.TypeHex:
|
||||
return HexPkg
|
||||
case "linux-kernel":
|
||||
return LinuxKernelPkg
|
||||
case "linux-kernel-module":
|
||||
return LinuxKernelModulePkg
|
||||
case "nix":
|
||||
return NixPkg
|
||||
default:
|
||||
|
|
|
@ -83,6 +83,10 @@ func TestTypeFromPURL(t *testing.T) {
|
|||
purl: "pkg:hex/hpax/hpax@0.1.1",
|
||||
expected: HexPkg,
|
||||
},
|
||||
{
|
||||
purl: "pkg:generic/linux-kernel@5.10.15",
|
||||
expected: LinuxKernelPkg,
|
||||
},
|
||||
{
|
||||
purl: "pkg:nix/glibc@2.34?hash=h0cnbmfcn93xm5dg2x27ixhag1cwndga",
|
||||
expected: NixPkg,
|
||||
|
@ -101,6 +105,7 @@ func TestTypeFromPURL(t *testing.T) {
|
|||
expectedTypes.Remove(string(JenkinsPluginPkg))
|
||||
expectedTypes.Remove(string(PortagePkg))
|
||||
expectedTypes.Remove(string(BinaryPkg))
|
||||
expectedTypes.Remove(string(LinuxKernelModulePkg))
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(string(test.expected), func(t *testing.T) {
|
||||
|
|
|
@ -55,11 +55,7 @@ func TestAllFormatsConvertable(t *testing.T) {
|
|||
for _, traitFn := range assertions {
|
||||
traitFn(t, stdout, stderr, cmd.ProcessState.ExitCode())
|
||||
}
|
||||
if t.Failed() {
|
||||
t.Log("STDOUT:\n", stdout)
|
||||
t.Log("STDERR:\n", stderr)
|
||||
t.Log("COMMAND:", strings.Join(cmd.Args, " "))
|
||||
}
|
||||
logOutputOnFailure(t, cmd, stdout, stderr)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package cli
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
@ -34,11 +33,7 @@ func TestAllFormatsExpressible(t *testing.T) {
|
|||
for _, traitFn := range commonAssertions {
|
||||
traitFn(t, stdout, stderr, cmd.ProcessState.ExitCode())
|
||||
}
|
||||
if t.Failed() {
|
||||
t.Log("STDOUT:\n", stdout)
|
||||
t.Log("STDERR:\n", stderr)
|
||||
t.Log("COMMAND:", strings.Join(cmd.Args, " "))
|
||||
}
|
||||
logOutputOnFailure(t, cmd, stdout, stderr)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,11 +40,7 @@ func TestConvertCmd(t *testing.T) {
|
|||
for _, traitFn := range assertions {
|
||||
traitFn(t, stdout, stderr, cmd.ProcessState.ExitCode())
|
||||
}
|
||||
if t.Failed() {
|
||||
t.Log("STDOUT:\n", stdout)
|
||||
t.Log("STDERR:\n", stderr)
|
||||
t.Log("COMMAND:", strings.Join(cmd.Args, " "))
|
||||
}
|
||||
logOutputOnFailure(t, cmd, stdout, stderr)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -52,11 +52,7 @@ func TestValidCycloneDX(t *testing.T) {
|
|||
for _, traitFn := range test.assertions {
|
||||
traitFn(t, stdout, stderr, cmd.ProcessState.ExitCode())
|
||||
}
|
||||
if t.Failed() {
|
||||
t.Log("STDOUT:\n", stdout)
|
||||
t.Log("STDERR:\n", stderr)
|
||||
t.Log("COMMAND:", strings.Join(cmd.Args, " "))
|
||||
}
|
||||
logOutputOnFailure(t, cmd, stdout, stderr)
|
||||
|
||||
validateCycloneDXJSON(t, stdout)
|
||||
})
|
||||
|
@ -95,11 +91,7 @@ func assertValidCycloneDX(tb testing.TB, stdout, stderr string, rc int) {
|
|||
tb.Errorf("expected no validation failures for cyclonedx-cli but got rc=%d", rc)
|
||||
}
|
||||
|
||||
if tb.Failed() {
|
||||
tb.Log("STDOUT:\n", stdout)
|
||||
tb.Log("STDERR:\n", stderr)
|
||||
tb.Log("COMMAND:", strings.Join(cmd.Args, " "))
|
||||
}
|
||||
logOutputOnFailure(tb, cmd, stdout, stderr)
|
||||
}
|
||||
|
||||
// validate --input-format json --input-version v1_4 --input-file bom.json
|
||||
|
@ -134,9 +126,5 @@ func validateCycloneDXJSON(t *testing.T, stdout string) {
|
|||
t.Errorf("expected no validation failures for cyclonedx-cli but found invalid BOM")
|
||||
}
|
||||
|
||||
if t.Failed() {
|
||||
t.Log("STDOUT:\n", stdout)
|
||||
t.Log("STDERR:\n", stderr)
|
||||
t.Log("COMMAND:", strings.Join(cmd.Args, " "))
|
||||
}
|
||||
logOutputOnFailure(t, cmd, stdout, stderr)
|
||||
}
|
||||
|
|
|
@ -2,7 +2,6 @@ package cli
|
|||
|
||||
import (
|
||||
"os/exec"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
@ -37,10 +36,6 @@ func TestDirectoryScanCompletesWithinTimeout(t *testing.T) {
|
|||
traitFn(t, stdout, stderr, cmd.ProcessState.ExitCode())
|
||||
}
|
||||
|
||||
if t.Failed() {
|
||||
t.Log("STDOUT:\n", stdout)
|
||||
t.Log("STDERR:\n", stderr)
|
||||
t.Log("COMMAND:", strings.Join(cmd.Args, " "))
|
||||
}
|
||||
logOutputOnFailure(t, cmd, stdout, stderr)
|
||||
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ package cli
|
|||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
@ -97,7 +96,7 @@ func TestPackagesCmdFlags(t *testing.T) {
|
|||
name: "squashed-scope-flag",
|
||||
args: []string{"packages", "-o", "json", "-s", "squashed", coverageImage},
|
||||
assertions: []traitAssertion{
|
||||
assertPackageCount(35),
|
||||
assertPackageCount(37),
|
||||
assertSuccessfulReturnCode,
|
||||
},
|
||||
},
|
||||
|
@ -214,7 +213,7 @@ func TestPackagesCmdFlags(t *testing.T) {
|
|||
// the application config in the log matches that of what we expect to have been configured.
|
||||
assertInOutput("parallelism: 2"),
|
||||
assertInOutput("parallelism=2"),
|
||||
assertPackageCount(35),
|
||||
assertPackageCount(37),
|
||||
assertSuccessfulReturnCode,
|
||||
},
|
||||
},
|
||||
|
@ -225,7 +224,7 @@ func TestPackagesCmdFlags(t *testing.T) {
|
|||
// the application config in the log matches that of what we expect to have been configured.
|
||||
assertInOutput("parallelism: 1"),
|
||||
assertInOutput("parallelism=1"),
|
||||
assertPackageCount(35),
|
||||
assertPackageCount(37),
|
||||
assertSuccessfulReturnCode,
|
||||
},
|
||||
},
|
||||
|
@ -239,7 +238,7 @@ func TestPackagesCmdFlags(t *testing.T) {
|
|||
assertions: []traitAssertion{
|
||||
assertNotInOutput("secret_password"),
|
||||
assertNotInOutput("secret_key_path"),
|
||||
assertPackageCount(35),
|
||||
assertPackageCount(37),
|
||||
assertSuccessfulReturnCode,
|
||||
},
|
||||
},
|
||||
|
@ -251,11 +250,7 @@ func TestPackagesCmdFlags(t *testing.T) {
|
|||
for _, traitFn := range test.assertions {
|
||||
traitFn(t, stdout, stderr, cmd.ProcessState.ExitCode())
|
||||
}
|
||||
if t.Failed() {
|
||||
t.Log("STDOUT:\n", stdout)
|
||||
t.Log("STDERR:\n", stderr)
|
||||
t.Log("COMMAND:", strings.Join(cmd.Args, " "))
|
||||
}
|
||||
logOutputOnFailure(t, cmd, stdout, stderr)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -337,11 +332,7 @@ func TestRegistryAuth(t *testing.T) {
|
|||
for _, traitAssertionFn := range test.assertions {
|
||||
traitAssertionFn(t, stdout, stderr, cmd.ProcessState.ExitCode())
|
||||
}
|
||||
if t.Failed() {
|
||||
t.Log("STDOUT:\n", stdout)
|
||||
t.Log("STDERR:\n", stderr)
|
||||
t.Log("COMMAND:", strings.Join(cmd.Args, " "))
|
||||
}
|
||||
logOutputOnFailure(t, cmd, stdout, stderr)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package cli
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
@ -96,11 +95,7 @@ func TestPowerUserCmdFlags(t *testing.T) {
|
|||
for _, traitFn := range test.assertions {
|
||||
traitFn(t, stdout, stderr, cmd.ProcessState.ExitCode())
|
||||
}
|
||||
if t.Failed() {
|
||||
t.Log("STDOUT:\n", stdout)
|
||||
t.Log("STDERR:\n", stderr)
|
||||
t.Log("COMMAND:", strings.Join(cmd.Args, " "))
|
||||
}
|
||||
logOutputOnFailure(t, cmd, stdout, stderr)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ package cli
|
|||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/sergi/go-diff/diffmatchpatch"
|
||||
|
@ -109,11 +108,7 @@ func TestPersistentFlags(t *testing.T) {
|
|||
for _, traitFn := range test.assertions {
|
||||
traitFn(t, stdout, stderr, cmd.ProcessState.ExitCode())
|
||||
}
|
||||
if t.Failed() {
|
||||
t.Log("STDOUT:\n", stdout)
|
||||
t.Log("STDERR:\n", stderr)
|
||||
t.Log("COMMAND:", strings.Join(cmd.Args, " "))
|
||||
}
|
||||
logOutputOnFailure(t, cmd, stdout, stderr)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -151,11 +146,7 @@ func TestLogFile(t *testing.T) {
|
|||
for _, traitFn := range test.assertions {
|
||||
traitFn(t, stdout, stderr, cmd.ProcessState.ExitCode())
|
||||
}
|
||||
if t.Failed() {
|
||||
t.Log("STDOUT:\n", stdout)
|
||||
t.Log("STDERR:\n", stderr)
|
||||
t.Log("COMMAND:", strings.Join(cmd.Args, " "))
|
||||
}
|
||||
logOutputOnFailure(t, cmd, stdout, stderr)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package cli
|
|||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
|
@ -21,6 +22,16 @@ import (
|
|||
"github.com/anchore/stereoscope/pkg/imagetest"
|
||||
)
|
||||
|
||||
var showOutput = flag.Bool("show-output", false, "show stdout and stderr for failing tests")
|
||||
|
||||
func logOutputOnFailure(t testing.TB, cmd *exec.Cmd, stdout, stderr string) {
|
||||
if t.Failed() && showOutput != nil && *showOutput {
|
||||
t.Log("STDOUT:\n", stdout)
|
||||
t.Log("STDERR:\n", stderr)
|
||||
t.Log("COMMAND:", strings.Join(cmd.Args, " "))
|
||||
}
|
||||
}
|
||||
|
||||
func runAndShow(t *testing.T, cmd *exec.Cmd) {
|
||||
t.Helper()
|
||||
|
||||
|
|
|
@ -11,6 +11,20 @@ type testCase struct {
|
|||
}
|
||||
|
||||
var imageOnlyTestCases = []testCase{
|
||||
{
|
||||
name: "find kernel packages",
|
||||
pkgType: pkg.LinuxKernelPkg,
|
||||
pkgInfo: map[string]string{
|
||||
"linux-kernel": "6.2.9-200.fc37.x86_64",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "find kernel module packages",
|
||||
pkgType: pkg.LinuxKernelModulePkg,
|
||||
pkgInfo: map[string]string{
|
||||
"fsa4480": "",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "find gemspec packages",
|
||||
pkgType: pkg.GemPkg,
|
||||
|
|
|
@ -221,6 +221,8 @@ func TestPkgCoverageDirectory(t *testing.T) {
|
|||
observedPkgs.Remove(string(pkg.UnknownPkg))
|
||||
definedPkgs.Remove(string(pkg.BinaryPkg))
|
||||
definedPkgs.Remove(string(pkg.UnknownPkg))
|
||||
definedPkgs.Remove(string(pkg.LinuxKernelPkg))
|
||||
definedPkgs.Remove(string(pkg.LinuxKernelModulePkg))
|
||||
|
||||
// for directory scans we should not expect to see any of the following package types
|
||||
definedPkgs.Remove(string(pkg.KbPkg))
|
||||
|
|
|
@ -1,5 +1,22 @@
|
|||
FROM fedora:37@sha256:3f987b7657e944cf87a129cc262982d4f80e38bd98f7db313ccaf90ca7069dd2
|
||||
|
||||
RUN dnf install 'dnf-command(download)' cpio xz -y
|
||||
RUN dnf download kernel-core kernel-modules-core -y
|
||||
|
||||
RUN rpm2cpio kernel-core-*.rpm | cpio -t && \
|
||||
rpm2cpio kernel-core-*.rpm | cpio -idmv ./lib/modules/6.2.9-200.fc37.x86_64/vmlinuz
|
||||
|
||||
RUN rpm2cpio kernel-modules-core-*.rpm | cpio -t && \
|
||||
rpm2cpio kernel-modules-core-*.rpm | cpio -idmv ./lib/modules/6.2.9-200.fc37.x86_64/kernel/drivers/usb/typec/mux/fsa4480.ko.xz
|
||||
|
||||
RUN unxz /lib/modules/6.2.9-200.fc37.x86_64/kernel/drivers/usb/typec/mux/fsa4480.ko.xz
|
||||
|
||||
FROM scratch
|
||||
|
||||
COPY --from=0 /lib/modules/6.2.9-200.fc37.x86_64/vmlinuz /lib/modules/6.2.9-200.fc37.x86_64/vmlinuz
|
||||
COPY --from=0 /lib/modules/6.2.9-200.fc37.x86_64/kernel/drivers/usb/typec/mux/fsa4480.ko /lib/modules/6.2.9-200.fc37.x86_64/kernel/drivers/usb/typec/mux/fsa4480.ko
|
||||
|
||||
COPY pkgs/ .
|
||||
# we duplicate to show a package count difference between all-layers and squashed scopes
|
||||
COPY lib lib
|
||||
COPY etc/ .
|
||||
COPY etc/ .
|
||||
|
|
Loading…
Reference in a new issue