Skip to content

Commit

Permalink
add kernel handler
Browse files Browse the repository at this point in the history
Signed-off-by: Avi Deitcher <avi@deitcher.net>
  • Loading branch information
deitch committed Mar 29, 2023
1 parent 81b87dd commit bcfff4e
Show file tree
Hide file tree
Showing 14 changed files with 455 additions and 1 deletion.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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-20230323161519-d7551b7f46f5
github.com/deitch/magic v0.0.0-20230323094151-ad691daf393c
github.com/docker/docker v23.0.1+incompatible
github.com/google/go-containerregistry v0.14.0
github.com/google/licensecheck v0.3.1
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -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-20230323094151-ad691daf393c h1:oV9G+Jq+pW7pll/BWcZjbWB9RpNsJQ7v9ynrW6Z6tE8=
github.com/deitch/magic v0.0.0-20230323094151-ad691daf393c/go.mod h1:D5iaOhreaX/4N2s6RVZ7N1HQmqCatksnJHtatAdeq3Q=
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=
Expand Down
43 changes: 43 additions & 0 deletions syft/pkg/cataloger/binary/classifier.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
"github.com/anchore/syft/syft/pkg"
"github.com/anchore/syft/syft/pkg/cataloger/internal/unionreader"
"github.com/anchore/syft/syft/source"
"github.com/deitch/magic/pkg/magic"
)

var emptyPURL = packageurl.PackageURL{}
Expand Down Expand Up @@ -124,6 +125,48 @@ func fileContentsVersionMatcher(pattern string) evidenceMatcher {
}
}

type versionFinder func([]string) string

func fileTypeMatcher(filetype string, finder versionFinder) evidenceMatcher {
return func(resolver source.FileResolver, classifier classifier, location source.Location) ([]pkg.Package, error) {
reader, err := resolver.FileContentsByLocation(location)
if err != nil {
return nil, err
}
unionReader, err := unionreader.GetUnionReader(reader)
if err != nil {
return nil, fmt.Errorf("unable to get union reader for file: %w", err)
}
magicType, err := magic.GetType(unionReader)
if err != nil {
return nil, fmt.Errorf("unable to get magic type for file: %w", err)
}
if len(magicType) < 1 || magicType[0] != filetype {
return nil, nil
}
var version string
if finder != nil {
version = finder(magicType)
}
matchMetadata := map[string]string{
"version": version,
}

return singlePackage(classifier, location, matchMetadata), nil
}
}

func prefixVersionFinder(prefix string) versionFinder {
return func(magicType []string) string {
for _, t := range magicType {
if strings.HasPrefix(t, prefix) {
return strings.TrimPrefix(t, prefix)
}
}
return ""
}
}

//nolint:gocognit
func sharedLibraryLookup(sharedLibraryPattern string, sharedLibraryMatcher evidenceMatcher) evidenceMatcher {
pat := regexp.MustCompile(sharedLibraryPattern)
Expand Down
5 changes: 5 additions & 0 deletions syft/pkg/cataloger/cataloger.go
Original file line number Diff line number Diff line change
Expand Up @@ -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/php"
"github.com/anchore/syft/syft/pkg/cataloger/portage"
"github.com/anchore/syft/syft/pkg/cataloger/python"
Expand Down Expand Up @@ -85,6 +86,8 @@ func DirectoryCatalogers(cfg Config) []pkg.Cataloger {
binary.NewCataloger(),
elixir.NewMixLockCataloger(),
erlang.NewRebarLockCataloger(),
kernel.NewKernelCataloger(cfg.Kernel()),
kernel.NewKernelModuleCataloger(cfg.Kernel()),
}, cfg.Catalogers)
}

Expand Down Expand Up @@ -121,6 +124,8 @@ func AllCatalogers(cfg Config) []pkg.Cataloger {
binary.NewCataloger(),
elixir.NewMixLockCataloger(),
erlang.NewRebarLockCataloger(),
kernel.NewKernelCataloger(cfg.Kernel()),
kernel.NewKernelModuleCataloger(cfg.Kernel()),
}, cfg.Catalogers)
}

Expand Down
6 changes: 6 additions & 0 deletions syft/pkg/cataloger/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ 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"
)

type Config struct {
Search SearchConfig
Golang golang.GoCatalogerOpts
KernelOpts kernel.KernelCatalogerOpts
Catalogers []string
Parallelism int
}
Expand All @@ -29,3 +31,7 @@ func (c Config) Java() java.Config {
func (c Config) Go() golang.GoCatalogerOpts {
return c.Golang
}

func (c Config) Kernel() kernel.KernelCatalogerOpts {
return c.KernelOpts
}
52 changes: 52 additions & 0 deletions syft/pkg/cataloger/kernel/cataloger.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/*
Package kernel provides a concrete Cataloger implementation for linux kernel and module files.
*/
package kernel

import (
"github.com/anchore/syft/syft/pkg/cataloger/generic"
)

type KernelCatalogerOpts struct {
KernelFilenameAppends []string
KernelModuleFilenameAppends []string
}

var kernelFiles = []string{
"kernel",
"kernel-*",
"vmlinux",
"vmlinux-*",
"vmlinuz",
"vmlinuz-*",
}

var kernelModuleFiles = []string{
"*.ko",
}

// NewKernelCataloger returns a new kernel files cataloger object.
func NewKernelCataloger(opts KernelCatalogerOpts) *generic.Cataloger {
var fileList []string
for _, file := range kernelFiles {
fileList = append(fileList, "**/"+file)
}
for _, file := range opts.KernelFilenameAppends {
fileList = append(fileList, "**/"+file)
}
return generic.NewCataloger("linux-kernel-cataloger").
WithParserByGlobs(parseKernelFile, fileList...)
}

// NewKernelModuleCataloger returns a new kernel module files cataloger object.
func NewKernelModuleCataloger(opts KernelCatalogerOpts) *generic.Cataloger {
var fileList []string
for _, file := range kernelModuleFiles {
fileList = append(fileList, "**/"+file)
}
for _, file := range opts.KernelModuleFilenameAppends {
fileList = append(fileList, "**/"+file)
}
return generic.NewCataloger("linux-kernel-module-cataloger").
WithParserByGlobs(parseKernelModuleFile, fileList...)
}
8 changes: 8 additions & 0 deletions syft/pkg/cataloger/kernel/consts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package kernel

const (
linuxKernelName = "Linux kernel"
linuxKernelVersionPrefix = "version "
packageName = "linux-kernel"
modinfoName = ".modinfo"
)
27 changes: 27 additions & 0 deletions syft/pkg/cataloger/kernel/package.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package kernel

import (
"strings"

"github.com/anchore/packageurl-go"
)

// 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()
}
94 changes: 94 additions & 0 deletions syft/pkg/cataloger/kernel/parse_kernel_file.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package kernel

import (
"fmt"
"strconv"
"strings"

"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"
"github.com/deitch/magic/pkg/magic"
)

func parseKernelFile(resolver 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] != linuxKernelName {
return nil, nil, nil
}
metadata := parseKernelMetadata(magicType)
if metadata.Version == "" {
return nil, nil, nil
}
p := pkg.Package{
Name: packageName,
Version: metadata.Version,
PURL: packageURL(packageName, metadata.Version),
Type: pkg.KernelPkg,
MetadataType: pkg.KernelPackageMetadataType,
Metadata: metadata,
Locations: source.NewLocationSet(reader.Location),
}

p.SetID()
return []pkg.Package{p}, nil, nil
}

func parseKernelMetadata(magicType []string) (p pkg.KernelPackageMetadata) {
// 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
}
Loading

0 comments on commit bcfff4e

Please sign in to comment.