Add ability to extend the binaries cataloguers (#2469)

* Add ability to extend the binaries cataloguers

Signed-off-by: Laurent Goderre <laurent.goderre@docker.com>

* restrict binary classifier package attributes

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

---------

Signed-off-by: Laurent Goderre <laurent.goderre@docker.com>
Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
Co-authored-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
Laurent Goderre 2024-01-05 15:32:07 -05:00 committed by GitHub
parent 3174a17efb
commit a16a4ad6c9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 523 additions and 390 deletions

View File

@ -14,6 +14,7 @@ import (
"github.com/anchore/syft/internal/log"
"github.com/anchore/syft/syft/cataloging"
"github.com/anchore/syft/syft/pkg/cataloger"
binaryCataloger "github.com/anchore/syft/syft/pkg/cataloger/binary"
golangCataloger "github.com/anchore/syft/syft/pkg/cataloger/golang"
javaCataloger "github.com/anchore/syft/syft/pkg/cataloger/java"
javascriptCataloger "github.com/anchore/syft/syft/pkg/cataloger/javascript"
@ -150,6 +151,7 @@ func (cfg Catalog) ToCatalogerConfig() cataloger.Config {
Javascript: javascriptCataloger.DefaultCatalogerConfig().
WithSearchRemoteLicenses(cfg.Javascript.SearchRemoteLicenses).
WithNpmBaseURL(cfg.Javascript.NpmBaseURL),
Binary: binaryCataloger.DefaultCatalogerConfig(),
Python: pythonCataloger.CatalogerConfig{
GuessUnpinnedRequirements: cfg.Python.GuessUnpinnedRequirements,
},

View File

@ -12,8 +12,20 @@ import (
const catalogerName = "binary-cataloger"
func NewCataloger() pkg.Cataloger {
return &Cataloger{}
type CatalogerConfig struct {
Classifiers []Classifier
}
func DefaultCatalogerConfig() CatalogerConfig {
return CatalogerConfig{
Classifiers: DefaultClassifiers(),
}
}
func NewCataloger(cfg CatalogerConfig) pkg.Cataloger {
return &Cataloger{
classifiers: cfg.Classifiers,
}
}
// Cataloger is the cataloger responsible for surfacing evidence of a very limited set of binary files,
@ -21,7 +33,9 @@ func NewCataloger() pkg.Cataloger {
// binary, but rather the specific set that has been curated to be important, predominantly related to toolchain-
// related runtimes like Python, Go, Java, or Node. Some exceptions can be made for widely-used binaries such
// as busybox.
type Cataloger struct{}
type Cataloger struct {
classifiers []Classifier
}
// Name returns a string that uniquely describes the Cataloger
func (c Cataloger) Name() string {
@ -34,7 +48,7 @@ func (c Cataloger) Catalog(resolver file.Resolver) ([]pkg.Package, []artifact.Re
var packages []pkg.Package
var relationships []artifact.Relationship
for _, cls := range defaultClassifiers {
for _, cls := range c.classifiers {
log.WithFields("classifier", cls.Class).Trace("cataloging binaries")
newPkgs, err := catalog(resolver, cls)
if err != nil {
@ -71,7 +85,7 @@ func mergePackages(target *pkg.Package, extra *pkg.Package) {
target.Metadata = meta
}
func catalog(resolver file.Resolver, cls classifier) (packages []pkg.Package, err error) {
func catalog(resolver file.Resolver, cls Classifier) (packages []pkg.Package, err error) {
locations, err := resolver.FilesByGlob(cls.FileGlob)
if err != nil {
return nil, err

View File

@ -780,7 +780,7 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
c := NewCataloger()
c := NewCataloger(DefaultCatalogerConfig())
src, err := source.NewFromDirectoryPath(test.fixtureDir)
require.NoError(t, err)
@ -819,7 +819,7 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases_Image(t *testing.T) {
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
c := NewCataloger()
c := NewCataloger(DefaultCatalogerConfig())
img := imagetest.GetFixtureImage(t, "docker-archive", test.fixtureImage)
src, err := source.NewFromStereoscopeImageObject(img, test.fixtureImage, nil)
@ -850,7 +850,7 @@ func Test_Cataloger_DefaultClassifiers_PositiveCases_Image(t *testing.T) {
}
func TestClassifierCataloger_DefaultClassifiers_NegativeCases(t *testing.T) {
c := NewCataloger()
c := NewCataloger(DefaultCatalogerConfig())
src, err := source.NewFromDirectoryPath("test-fixtures/classifiers/negative")
assert.NoError(t, err)
@ -863,6 +863,129 @@ func TestClassifierCataloger_DefaultClassifiers_NegativeCases(t *testing.T) {
assert.Equal(t, 0, len(actualResults))
}
func Test_Cataloger_CustomClassifiers(t *testing.T) {
defaultClassifers := DefaultClassifiers()
golangExpected := pkg.Package{
Name: "go",
Version: "1.14",
PURL: "pkg:generic/go@1.14",
Locations: locations("go"),
Metadata: metadata("go-binary"),
}
customExpected := pkg.Package{
Name: "foo",
Version: "1.2.3",
PURL: "pkg:generic/foo@1.2.3",
Locations: locations("foo"),
Metadata: metadata("foo-binary"),
}
fooClassifier := Classifier{
Class: "foo-binary",
FileGlob: "**/foo",
EvidenceMatcher: FileContentsVersionMatcher(
`(?m)foobar\s(?P<version>[0-9]+\.[0-9]+\.[0-9]+)`),
Package: "foo",
PURL: mustPURL("pkg:generic/foo@version"),
CPEs: singleCPE("cpe:2.3:a:foo:foo:*:*:*:*:*:*:*:*"),
}
tests := []struct {
name string
config CatalogerConfig
fixtureDir string
expected *pkg.Package
}{
{
name: "empty-negative",
config: CatalogerConfig{
Classifiers: []Classifier{},
},
fixtureDir: "test-fixtures/classifiers/positive/go-1.14",
expected: nil,
},
{
name: "default-positive",
config: CatalogerConfig{
Classifiers: defaultClassifers,
},
fixtureDir: "test-fixtures/classifiers/positive/go-1.14",
expected: &golangExpected,
},
{
name: "nodefault-negative",
config: CatalogerConfig{
Classifiers: []Classifier{fooClassifier},
},
fixtureDir: "test-fixtures/classifiers/positive/go-1.14",
expected: nil,
},
{
name: "default-extended-positive",
config: CatalogerConfig{
Classifiers: append(
append([]Classifier{}, defaultClassifers...),
fooClassifier,
),
},
fixtureDir: "test-fixtures/classifiers/positive/go-1.14",
expected: &golangExpected,
},
{
name: "default-cutsom-negative",
config: CatalogerConfig{
Classifiers: append(
append([]Classifier{}, defaultClassifers...),
Classifier{
Class: "foo-binary",
FileGlob: "**/foo",
EvidenceMatcher: FileContentsVersionMatcher(`(?m)not there`),
Package: "foo",
PURL: mustPURL("pkg:generic/foo@version"),
CPEs: singleCPE("cpe:2.3:a:foo:foo:*:*:*:*:*:*:*:*"),
},
),
},
fixtureDir: "test-fixtures/classifiers/positive/custom",
expected: nil,
},
{
name: "default-cutsom-positive",
config: CatalogerConfig{
Classifiers: append(
append([]Classifier{}, defaultClassifers...),
fooClassifier,
),
},
fixtureDir: "test-fixtures/classifiers/positive/custom",
expected: &customExpected,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
c := NewCataloger(test.config)
src, err := source.NewFromDirectoryPath(test.fixtureDir)
require.NoError(t, err)
resolver, err := src.FileResolver(source.SquashedScope)
require.NoError(t, err)
packages, _, err := c.Catalog(resolver)
require.NoError(t, err)
if test.expected == nil {
assert.Equal(t, 0, len(packages))
} else {
require.Len(t, packages, 1)
assertPackagesAreEqual(t, *test.expected, packages[0])
}
})
}
}
func locations(locations ...string) file.LocationSet {
var locs []file.Location
for _, s := range locations {
@ -1019,7 +1142,7 @@ func (p *panicyResolver) FileMetadataByLocation(_ file.Location) (file.Metadata,
var _ file.Resolver = (*panicyResolver)(nil)
func Test_Cataloger_ResilientToErrors(t *testing.T) {
c := NewCataloger()
c := NewCataloger(DefaultCatalogerConfig())
resolver := &panicyResolver{}
_, _, err := c.Catalog(resolver)

View File

@ -20,11 +20,9 @@ import (
"github.com/anchore/syft/syft/pkg/cataloger/internal/unionreader"
)
var emptyPURL = packageurl.PackageURL{}
// classifier is a generic package classifier that can be used to match a package definition
// to a file that meets the given content criteria of the evidenceMatcher.
type classifier struct {
// Classifier is a generic package classifier that can be used to match a package definition
// to a file that meets the given content criteria of the EvidenceMatcher.
type Classifier struct {
Class string
// FileGlob is a selector to narrow down file inspection using the **/glob* syntax
@ -32,19 +30,13 @@ type classifier struct {
// EvidenceMatcher is what will be used to match against the file in the source
// location. If the matcher returns a package, the file will be considered a candidate.
EvidenceMatcher evidenceMatcher
EvidenceMatcher EvidenceMatcher
// Information below is used to specify the Package information when returned
// Package is the name to use for the package
Package string
// Language is the language to classify this package as
Language pkg.Language
// Type is the package type to use for the package
Type pkg.Type
// PURL is the Package URL to use when generating a package
PURL packageurl.PackageURL
@ -52,11 +44,11 @@ type classifier struct {
CPEs []cpe.CPE
}
// evidenceMatcher is a function called to catalog Packages that match some sort of evidence
type evidenceMatcher func(resolver file.Resolver, classifier classifier, location file.Location) ([]pkg.Package, error)
// EvidenceMatcher is a function called to catalog Packages that match some sort of evidence
type EvidenceMatcher func(resolver file.Resolver, classifier Classifier, location file.Location) ([]pkg.Package, error)
func evidenceMatchers(matchers ...evidenceMatcher) evidenceMatcher {
return func(resolver file.Resolver, classifier classifier, location file.Location) ([]pkg.Package, error) {
func evidenceMatchers(matchers ...EvidenceMatcher) EvidenceMatcher {
return func(resolver file.Resolver, classifier Classifier, location file.Location) ([]pkg.Package, error) {
for _, matcher := range matchers {
match, err := matcher(resolver, classifier, location)
if err != nil {
@ -70,9 +62,9 @@ func evidenceMatchers(matchers ...evidenceMatcher) evidenceMatcher {
}
}
func fileNameTemplateVersionMatcher(fileNamePattern string, contentTemplate string) evidenceMatcher {
func fileNameTemplateVersionMatcher(fileNamePattern string, contentTemplate string) EvidenceMatcher {
pat := regexp.MustCompile(fileNamePattern)
return func(resolver file.Resolver, classifier classifier, location file.Location) ([]pkg.Package, error) {
return func(resolver file.Resolver, classifier Classifier, location file.Location) ([]pkg.Package, error) {
if !pat.MatchString(location.RealPath) {
return nil, nil
}
@ -116,9 +108,9 @@ func fileNameTemplateVersionMatcher(fileNamePattern string, contentTemplate stri
}
}
func fileContentsVersionMatcher(pattern string) evidenceMatcher {
func FileContentsVersionMatcher(pattern string) EvidenceMatcher {
pat := regexp.MustCompile(pattern)
return func(resolver file.Resolver, classifier classifier, location file.Location) ([]pkg.Package, error) {
return func(resolver file.Resolver, classifier Classifier, location file.Location) ([]pkg.Package, error) {
contents, err := getContents(resolver, location)
if err != nil {
return nil, fmt.Errorf("unable to get read contents for file: %w", err)
@ -136,9 +128,9 @@ func fileContentsVersionMatcher(pattern string) evidenceMatcher {
}
//nolint:gocognit
func sharedLibraryLookup(sharedLibraryPattern string, sharedLibraryMatcher evidenceMatcher) evidenceMatcher {
func sharedLibraryLookup(sharedLibraryPattern string, sharedLibraryMatcher EvidenceMatcher) EvidenceMatcher {
pat := regexp.MustCompile(sharedLibraryPattern)
return func(resolver file.Resolver, classifier classifier, location file.Location) (packages []pkg.Package, _ error) {
return func(resolver file.Resolver, classifier Classifier, location file.Location) (packages []pkg.Package, _ error) {
libs, err := sharedLibraries(resolver, location)
if err != nil {
return nil, err

View File

@ -13,16 +13,16 @@ func Test_ClassifierCPEs(t *testing.T) {
tests := []struct {
name string
fixture string
classifier classifier
classifier Classifier
cpes []string
}{
{
name: "no CPEs",
fixture: "test-fixtures/version.txt",
classifier: classifier{
classifier: Classifier{
Package: "some-app",
FileGlob: "**/version.txt",
EvidenceMatcher: fileContentsVersionMatcher(`(?m)my-verison:(?P<version>[0-9.]+)`),
EvidenceMatcher: FileContentsVersionMatcher(`(?m)my-verison:(?P<version>[0-9.]+)`),
CPEs: []cpe.CPE{},
},
cpes: nil,
@ -30,10 +30,10 @@ func Test_ClassifierCPEs(t *testing.T) {
{
name: "one CPE",
fixture: "test-fixtures/version.txt",
classifier: classifier{
classifier: Classifier{
Package: "some-app",
FileGlob: "**/version.txt",
EvidenceMatcher: fileContentsVersionMatcher(`(?m)my-verison:(?P<version>[0-9.]+)`),
EvidenceMatcher: FileContentsVersionMatcher(`(?m)my-verison:(?P<version>[0-9.]+)`),
CPEs: []cpe.CPE{
cpe.Must("cpe:2.3:a:some:app:*:*:*:*:*:*:*:*"),
},
@ -45,10 +45,10 @@ func Test_ClassifierCPEs(t *testing.T) {
{
name: "multiple CPEs",
fixture: "test-fixtures/version.txt",
classifier: classifier{
classifier: Classifier{
Package: "some-app",
FileGlob: "**/version.txt",
EvidenceMatcher: fileContentsVersionMatcher(`(?m)my-verison:(?P<version>[0-9.]+)`),
EvidenceMatcher: FileContentsVersionMatcher(`(?m)my-verison:(?P<version>[0-9.]+)`),
CPEs: []cpe.CPE{
cpe.Must("cpe:2.3:a:some:app:*:*:*:*:*:*:*:*"),
cpe.Must("cpe:2.3:a:some:apps:*:*:*:*:*:*:*:*"),

View File

@ -2,350 +2,351 @@ package binary
import (
"github.com/anchore/syft/syft/cpe"
"github.com/anchore/syft/syft/pkg"
)
var defaultClassifiers = []classifier{
{
Class: "python-binary",
FileGlob: "**/python*",
EvidenceMatcher: evidenceMatchers(
// try to find version information from libpython shared libraries
sharedLibraryLookup(
`^libpython[0-9]+(?:\.[0-9]+)+[a-z]?\.so.*$`,
libpythonMatcher),
// check for version information in the binary
fileNameTemplateVersionMatcher(
`(?:.*/|^)python(?P<version>[0-9]+(?:\.[0-9]+)+)$`,
pythonVersionTemplate),
),
Package: "python",
PURL: mustPURL("pkg:generic/python@version"),
CPEs: []cpe.CPE{
cpe.Must("cpe:2.3:a:python_software_foundation:python:*:*:*:*:*:*:*:*"),
cpe.Must("cpe:2.3:a:python:python:*:*:*:*:*:*:*:*"),
//nolint:funlen
func DefaultClassifiers() []Classifier {
return []Classifier{
{
Class: "python-binary",
FileGlob: "**/python*",
EvidenceMatcher: evidenceMatchers(
// try to find version information from libpython shared libraries
sharedLibraryLookup(
`^libpython[0-9]+(?:\.[0-9]+)+[a-z]?\.so.*$`,
libpythonMatcher),
// check for version information in the binary
fileNameTemplateVersionMatcher(
`(?:.*/|^)python(?P<version>[0-9]+(?:\.[0-9]+)+)$`,
pythonVersionTemplate),
),
Package: "python",
PURL: mustPURL("pkg:generic/python@version"),
CPEs: []cpe.CPE{
cpe.Must("cpe:2.3:a:python_software_foundation:python:*:*:*:*:*:*:*:*"),
cpe.Must("cpe:2.3:a:python:python:*:*:*:*:*:*:*:*"),
},
},
},
{
Class: "python-binary-lib",
FileGlob: "**/libpython*.so*",
EvidenceMatcher: libpythonMatcher,
Package: "python",
PURL: mustPURL("pkg:generic/python@version"),
CPEs: []cpe.CPE{
cpe.Must("cpe:2.3:a:python_software_foundation:python:*:*:*:*:*:*:*:*"),
cpe.Must("cpe:2.3:a:python:python:*:*:*:*:*:*:*:*"),
{
Class: "python-binary-lib",
FileGlob: "**/libpython*.so*",
EvidenceMatcher: libpythonMatcher,
Package: "python",
PURL: mustPURL("pkg:generic/python@version"),
CPEs: []cpe.CPE{
cpe.Must("cpe:2.3:a:python_software_foundation:python:*:*:*:*:*:*:*:*"),
cpe.Must("cpe:2.3:a:python:python:*:*:*:*:*:*:*:*"),
},
},
},
{
Class: "go-binary",
FileGlob: "**/go",
EvidenceMatcher: fileContentsVersionMatcher(
`(?m)go(?P<version>[0-9]+\.[0-9]+(\.[0-9]+|beta[0-9]+|alpha[0-9]+|rc[0-9]+)?)\x00`),
Package: "go",
PURL: mustPURL("pkg:generic/go@version"),
CPEs: singleCPE("cpe:2.3:a:golang:go:*:*:*:*:*:*:*:*"),
},
{
Class: "julia-binary",
FileGlob: "**/libjulia-internal.so",
EvidenceMatcher: fileContentsVersionMatcher(
`(?m)__init__\x00(?P<version>[0-9]+\.[0-9]+\.[0-9]+)\x00verify`),
Package: "julia",
PURL: mustPURL("pkg:generic/julia@version"),
CPEs: singleCPE("cpe:2.3:a:julialang:julia:*:*:*:*:*:*:*:*"),
},
{
Class: "helm",
FileGlob: "**/helm",
EvidenceMatcher: fileContentsVersionMatcher(
`(?m)\x00v(?P<version>[0-9]+\.[0-9]+\.[0-9]+)\x00`),
Package: "helm",
PURL: mustPURL("pkg:golang/helm.sh/helm@version"),
CPEs: singleCPE("cpe:2.3:a:helm:helm:*:*:*:*:*:*:*"),
},
{
Class: "redis-binary",
FileGlob: "**/redis-server",
EvidenceMatcher: evidenceMatchers(
fileContentsVersionMatcher(`(?s)payload %5.*?(?P<version>\d.\d\.\d\d*)[a-z0-9]{12,15}-[0-9]{19}`),
fileContentsVersionMatcher(`(?s)\x00(?P<version>\d.\d\.\d\d*)[a-z0-9]{12}-[0-9]{19}\x00.*?payload %5`),
),
Package: "redis",
PURL: mustPURL("pkg:generic/redis@version"),
CPEs: singleCPE("cpe:2.3:a:redislabs:redis:*:*:*:*:*:*:*:*"),
},
{
Class: "java-binary-openjdk",
FileGlob: "**/java",
EvidenceMatcher: fileContentsVersionMatcher(
// [NUL]openjdk[NUL]java[NUL]0.0[NUL]11.0.17+8-LTS[NUL]
// [NUL]openjdk[NUL]java[NUL]1.8[NUL]1.8.0_352-b08[NUL]
`(?m)\x00openjdk\x00java\x00(?P<release>[0-9]+[.0-9]*)\x00(?P<version>[0-9]+[^\x00]+)\x00`),
Package: "java",
PURL: mustPURL("pkg:generic/java@version"),
// TODO the updates might need to be part of the CPE, like: 1.8.0:update152
CPEs: singleCPE("cpe:2.3:a:oracle:openjdk:*:*:*:*:*:*:*:*"),
},
{
Class: "java-binary-ibm",
FileGlob: "**/java",
EvidenceMatcher: fileContentsVersionMatcher(
// [NUL]java[NUL]1.8[NUL][NUL][NUL][NUL]1.8.0-foreman_2022_09_22_15_30-b00[NUL]
`(?m)\x00java\x00(?P<release>[0-9]+[.0-9]+)\x00{4}(?P<version>[0-9]+[-._a-zA-Z0-9]+)\x00`),
Package: "java",
PURL: mustPURL("pkg:generic/java@version"),
CPEs: singleCPE("cpe:2.3:a:ibm:java:*:*:*:*:*:*:*:*"),
},
{
Class: "java-binary-oracle",
FileGlob: "**/java",
EvidenceMatcher: fileContentsVersionMatcher(
// [NUL]19.0.1+10-21[NUL]
`(?m)\x00(?P<version>[0-9]+[.0-9]+[+][-0-9]+)\x00`),
Package: "java",
PURL: mustPURL("pkg:generic/java@version"),
CPEs: singleCPE("cpe:2.3:a:oracle:jre:*:*:*:*:*:*:*:*"),
},
{
Class: "nodejs-binary",
FileGlob: "**/node",
EvidenceMatcher: fileContentsVersionMatcher(
`(?m)node\.js\/v(?P<version>[0-9]+\.[0-9]+\.[0-9]+)`),
Package: "node",
Language: pkg.JavaScript,
PURL: mustPURL("pkg:generic/node@version"),
CPEs: singleCPE("cpe:2.3:a:nodejs:node.js:*:*:*:*:*:*:*:*"),
},
{
Class: "go-binary-hint",
FileGlob: "**/VERSION",
EvidenceMatcher: fileContentsVersionMatcher(
`(?m)go(?P<version>[0-9]+\.[0-9]+(\.[0-9]+|beta[0-9]+|alpha[0-9]+|rc[0-9]+)?)`),
Package: "go",
PURL: mustPURL("pkg:generic/go@version"),
},
{
Class: "busybox-binary",
FileGlob: "**/busybox",
EvidenceMatcher: fileContentsVersionMatcher(
`(?m)BusyBox\s+v(?P<version>[0-9]+\.[0-9]+\.[0-9]+)`),
Package: "busybox",
PURL: mustPURL("pkg:generic/busybox@version"),
CPEs: singleCPE("cpe:2.3:a:busybox:busybox:*:*:*:*:*:*:*:*"),
},
{
Class: "haproxy-binary",
FileGlob: "**/haproxy",
EvidenceMatcher: evidenceMatchers(
fileContentsVersionMatcher(`(?m)HA-Proxy version (?P<version>[0-9]+\.[0-9]+\.[0-9]+)`),
fileContentsVersionMatcher(`(?m)(?P<version>[0-9]+\.[0-9]+\.[0-9]+)-[0-9a-zA-Z]{7}.+HAProxy version`),
),
Package: "haproxy",
PURL: mustPURL("pkg:generic/haproxy@version"),
CPEs: singleCPE("cpe:2.3:a:haproxy:haproxy:*:*:*:*:*:*:*:*"),
},
{
Class: "perl-binary",
FileGlob: "**/perl",
EvidenceMatcher: fileContentsVersionMatcher(
`(?m)\/usr\/local\/lib\/perl\d\/(?P<version>[0-9]+\.[0-9]+\.[0-9]+)`),
Package: "perl",
PURL: mustPURL("pkg:generic/perl@version"),
CPEs: singleCPE("cpe:2.3:a:perl:perl:*:*:*:*:*:*:*:*"),
},
{
Class: "php-cli-binary",
FileGlob: "**/php*",
EvidenceMatcher: fileNameTemplateVersionMatcher(
`(.*/|^)php[0-9]*$`,
`(?m)X-Powered-By: PHP\/(?P<version>[0-9]+\.[0-9]+\.[0-9]+(beta[0-9]+|alpha[0-9]+|RC[0-9]+)?)`),
Package: "php-cli",
PURL: mustPURL("pkg:generic/php-cli@version"),
CPEs: singleCPE("cpe:2.3:a:php:php:*:*:*:*:*:*:*:*"),
},
{
Class: "php-fpm-binary",
FileGlob: "**/php-fpm*",
EvidenceMatcher: fileContentsVersionMatcher(
`(?m)X-Powered-By: PHP\/(?P<version>[0-9]+\.[0-9]+\.[0-9]+(beta[0-9]+|alpha[0-9]+|RC[0-9]+)?)`),
Package: "php-fpm",
PURL: mustPURL("pkg:generic/php-fpm@version"),
CPEs: singleCPE("cpe:2.3:a:php:php:*:*:*:*:*:*:*:*"),
},
{
Class: "php-apache-binary",
FileGlob: "**/libphp*.so",
EvidenceMatcher: fileContentsVersionMatcher(
`(?m)X-Powered-By: PHP\/(?P<version>[0-9]+\.[0-9]+\.[0-9]+(beta[0-9]+|alpha[0-9]+|RC[0-9]+)?)`),
Package: "libphp",
PURL: mustPURL("pkg:generic/php@version"),
CPEs: singleCPE("cpe:2.3:a:php:php:*:*:*:*:*:*:*:*"),
},
{
Class: "php-composer-binary",
FileGlob: "**/composer*",
EvidenceMatcher: fileContentsVersionMatcher(
`(?m)'pretty_version'\s*=>\s*'(?P<version>[0-9]+\.[0-9]+\.[0-9]+(beta[0-9]+|alpha[0-9]+|RC[0-9]+)?)'`),
Package: "composer",
PURL: mustPURL("pkg:generic/composer@version"),
CPEs: singleCPE("cpe:2.3:a:getcomposer:composer:*:*:*:*:*:*:*:*"),
},
{
Class: "httpd-binary",
FileGlob: "**/httpd",
EvidenceMatcher: fileContentsVersionMatcher(
`(?m)Apache\/(?P<version>[0-9]+\.[0-9]+\.[0-9]+)`),
Package: "httpd",
PURL: mustPURL("pkg:generic/httpd@version"),
CPEs: singleCPE("cpe:2.3:a:apache:http_server:*:*:*:*:*:*:*:*"),
},
{
Class: "memcached-binary",
FileGlob: "**/memcached",
EvidenceMatcher: fileContentsVersionMatcher(
`(?m)memcached\s(?P<version>[0-9]+\.[0-9]+\.[0-9]+)`),
Package: "memcached",
PURL: mustPURL("pkg:generic/memcached@version"),
},
{
Class: "traefik-binary",
FileGlob: "**/traefik",
EvidenceMatcher: fileContentsVersionMatcher(
// [NUL]v1.7.34[NUL]
// [NUL]2.9.6[NUL]
`(?m)\x00v?(?P<version>[0-9]+\.[0-9]+\.[0-9]+(-alpha[0-9]|-beta[0-9]|-rc[0-9])?)\x00`),
Package: "traefik",
PURL: mustPURL("pkg:generic/traefik@version"),
},
{
Class: "postgresql-binary",
FileGlob: "**/postgres",
EvidenceMatcher: fileContentsVersionMatcher(
// [NUL]PostgreSQL 15beta4
// [NUL]PostgreSQL 15.1
// [NUL]PostgreSQL 9.6.24
// ?PostgreSQL 9.5alpha1
`(?m)(\x00|\?)PostgreSQL (?P<version>[0-9]+(\.[0-9]+)?(\.[0-9]+)?(alpha[0-9]|beta[0-9]|rc[0-9])?)`),
Package: "postgresql",
PURL: mustPURL("pkg:generic/postgresql@version"),
},
{
Class: "mysql-binary",
FileGlob: "**/mysql",
EvidenceMatcher: fileContentsVersionMatcher(
// ../../mysql-8.0.34
// /mysql-5.6.51/bld/client
`(?m).*/mysql-(?P<version>[0-9]+(\.[0-9]+)?(\.[0-9]+)?(alpha[0-9]|beta[0-9]|rc[0-9])?)`),
Package: "mysql",
PURL: mustPURL("pkg:generic/mysql@version"),
CPEs: singleCPE("cpe:2.3:a:oracle:mysql:*:*:*:*:*:*:*:*"),
},
{
Class: "mariadb-binary",
FileGlob: "**/mariadb",
EvidenceMatcher: fileContentsVersionMatcher(
// 10.6.15-MariaDB
`(?m)(?P<version>[0-9]+(\.[0-9]+)?(\.[0-9]+)?(alpha[0-9]|beta[0-9]|rc[0-9])?)-MariaDB`),
Package: "mariadb",
PURL: mustPURL("pkg:generic/mariadb@version"),
},
{
Class: "rust-standard-library-linux",
FileGlob: "**/libstd-????????????????.so",
EvidenceMatcher: fileContentsVersionMatcher(
// clang LLVM (rustc version 1.48.0 (7eac88abb 2020-11-16))
`(?m)(\x00)clang LLVM \(rustc version (?P<version>[0-9]+(\.[0-9]+)?(\.[0-9]+)) \(\w+ \d{4}\-\d{2}\-\d{2}\)`),
Package: "rust",
PURL: mustPURL("pkg:generic/rust@version"),
CPEs: singleCPE("cpe:2.3:a:rust-lang:rust:*:*:*:*:*:*:*:*"),
},
{
Class: "rust-standard-library-macos",
FileGlob: "**/libstd-????????????????.dylib",
EvidenceMatcher: fileContentsVersionMatcher(
// c 1.48.0 (7eac88abb 2020-11-16)
`(?m)c (?P<version>[0-9]+(\.[0-9]+)?(\.[0-9]+)) \(\w+ \d{4}\-\d{2}\-\d{2}\)`),
Package: "rust",
PURL: mustPURL("pkg:generic/rust@version"),
CPEs: singleCPE("cpe:2.3:a:rust-lang:rust:*:*:*:*:*:*:*:*"),
},
{
Class: "ruby-binary",
FileGlob: "**/ruby",
EvidenceMatcher: evidenceMatchers(
rubyMatcher,
sharedLibraryLookup(
// try to find version information from libruby shared libraries
`^libruby\.so.*$`,
rubyMatcher),
),
Package: "ruby",
PURL: mustPURL("pkg:generic/ruby@version"),
CPEs: singleCPE("cpe:2.3:a:ruby-lang:ruby:*:*:*:*:*:*:*:*"),
},
{
Class: "erlang-binary",
FileGlob: "**/erlexec",
EvidenceMatcher: fileContentsVersionMatcher(
// <artificial>[NUL]/usr/local/src/otp-25.3.2.7/erts/
`(?m)\<artificial\>\x00/usr/local/src/otp-(?P<version>[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+?)/erts/`,
),
Package: "erlang",
PURL: mustPURL("pkg:generic/erlang@version"),
CPEs: singleCPE("cpe:2.3:a:erlang:erlang\\/otp:*:*:*:*:*:*:*:*"),
},
{
Class: "consul-binary",
FileGlob: "**/consul",
EvidenceMatcher: fileContentsVersionMatcher(
// NOTE: This is brittle and may not work for past or future versions
`CONSUL_VERSION: (?P<version>\d+\.\d+\.\d+)`,
),
Package: "consul",
PURL: mustPURL("pkg:golang/github.com/hashicorp/consul@version"),
CPEs: singleCPE("cpe:2.3:a:hashicorp:consul:*:*:*:*:*:*:*:*"),
},
{
Class: "nginx-binary",
FileGlob: "**/nginx",
EvidenceMatcher: fileContentsVersionMatcher(
// [NUL]nginx version: nginx/1.25.1 - fetches '1.25.1'
// [NUL]nginx version: openresty/1.21.4.1 - fetches '1.21.4' as this is the nginx version part
`(?m)(\x00|\?)nginx version: [^\/]+\/(?P<version>[0-9]+\.[0-9]+\.[0-9]+(?:\+\d+)?(?:-\d+)?)`,
),
Package: "nginx",
PURL: mustPURL("pkg:generic/nginx@version"),
CPEs: []cpe.CPE{
cpe.Must("cpe:2.3:a:f5:nginx:*:*:*:*:*:*:*:*"),
cpe.Must("cpe:2.3:a:nginx:nginx:*:*:*:*:*:*:*:*"),
{
Class: "go-binary",
FileGlob: "**/go",
EvidenceMatcher: FileContentsVersionMatcher(
`(?m)go(?P<version>[0-9]+\.[0-9]+(\.[0-9]+|beta[0-9]+|alpha[0-9]+|rc[0-9]+)?)\x00`),
Package: "go",
PURL: mustPURL("pkg:generic/go@version"),
CPEs: singleCPE("cpe:2.3:a:golang:go:*:*:*:*:*:*:*:*"),
},
},
{
Class: "bash-binary",
FileGlob: "**/bash",
EvidenceMatcher: fileContentsVersionMatcher(
// @(#)Bash version 5.2.15(1) release GNU
// @(#)Bash version 5.2.0(1) alpha GNU
// @(#)Bash version 5.2.0(1) beta GNU
// @(#)Bash version 5.2.0(1) rc4 GNU
`(?m)@\(#\)Bash version (?P<version>[0-9]+\.[0-9]+\.[0-9]+)\([0-9]\) [a-z0-9]+ GNU`,
),
Package: "bash",
PURL: mustPURL("pkg:generic/bash@version"),
CPEs: singleCPE("cpe:2.3:a:gnu:bash:*:*:*:*:*:*:*:*"),
},
{
Class: "openssl-binary",
FileGlob: "**/openssl",
EvidenceMatcher: fileContentsVersionMatcher(
// [NUL]OpenSSL 3.1.4'
`\x00OpenSSL (?P<version>[0-9]+\.[0-9]+\.[0-9]+(-alpha[0-9]|-beta[0-9]|-rc[0-9])?)`,
),
Package: "openssl",
PURL: mustPURL("pkg:generic/openssl@version"),
CPEs: singleCPE("cpe:2.3:a:openssl:openssl:*:*:*:*:*:*:*:*"),
},
{
Class: "julia-binary",
FileGlob: "**/libjulia-internal.so",
EvidenceMatcher: FileContentsVersionMatcher(
`(?m)__init__\x00(?P<version>[0-9]+\.[0-9]+\.[0-9]+)\x00verify`),
Package: "julia",
PURL: mustPURL("pkg:generic/julia@version"),
CPEs: singleCPE("cpe:2.3:a:julialang:julia:*:*:*:*:*:*:*:*"),
},
{
Class: "helm",
FileGlob: "**/helm",
EvidenceMatcher: FileContentsVersionMatcher(
`(?m)\x00v(?P<version>[0-9]+\.[0-9]+\.[0-9]+)\x00`),
Package: "helm",
PURL: mustPURL("pkg:golang/helm.sh/helm@version"),
CPEs: singleCPE("cpe:2.3:a:helm:helm:*:*:*:*:*:*:*"),
},
{
Class: "redis-binary",
FileGlob: "**/redis-server",
EvidenceMatcher: evidenceMatchers(
FileContentsVersionMatcher(`(?s)payload %5.*?(?P<version>\d.\d\.\d\d*)[a-z0-9]{12,15}-[0-9]{19}`),
FileContentsVersionMatcher(`(?s)\x00(?P<version>\d.\d\.\d\d*)[a-z0-9]{12}-[0-9]{19}\x00.*?payload %5`),
),
Package: "redis",
PURL: mustPURL("pkg:generic/redis@version"),
CPEs: singleCPE("cpe:2.3:a:redislabs:redis:*:*:*:*:*:*:*:*"),
},
{
Class: "java-binary-openjdk",
FileGlob: "**/java",
EvidenceMatcher: FileContentsVersionMatcher(
// [NUL]openjdk[NUL]java[NUL]0.0[NUL]11.0.17+8-LTS[NUL]
// [NUL]openjdk[NUL]java[NUL]1.8[NUL]1.8.0_352-b08[NUL]
`(?m)\x00openjdk\x00java\x00(?P<release>[0-9]+[.0-9]*)\x00(?P<version>[0-9]+[^\x00]+)\x00`),
Package: "java",
PURL: mustPURL("pkg:generic/java@version"),
// TODO the updates might need to be part of the CPE, like: 1.8.0:update152
CPEs: singleCPE("cpe:2.3:a:oracle:openjdk:*:*:*:*:*:*:*:*"),
},
{
Class: "java-binary-ibm",
FileGlob: "**/java",
EvidenceMatcher: FileContentsVersionMatcher(
// [NUL]java[NUL]1.8[NUL][NUL][NUL][NUL]1.8.0-foreman_2022_09_22_15_30-b00[NUL]
`(?m)\x00java\x00(?P<release>[0-9]+[.0-9]+)\x00{4}(?P<version>[0-9]+[-._a-zA-Z0-9]+)\x00`),
Package: "java",
PURL: mustPURL("pkg:generic/java@version"),
CPEs: singleCPE("cpe:2.3:a:ibm:java:*:*:*:*:*:*:*:*"),
},
{
Class: "java-binary-oracle",
FileGlob: "**/java",
EvidenceMatcher: FileContentsVersionMatcher(
// [NUL]19.0.1+10-21[NUL]
`(?m)\x00(?P<version>[0-9]+[.0-9]+[+][-0-9]+)\x00`),
Package: "java",
PURL: mustPURL("pkg:generic/java@version"),
CPEs: singleCPE("cpe:2.3:a:oracle:jre:*:*:*:*:*:*:*:*"),
},
{
Class: "nodejs-binary",
FileGlob: "**/node",
EvidenceMatcher: FileContentsVersionMatcher(
`(?m)node\.js\/v(?P<version>[0-9]+\.[0-9]+\.[0-9]+)`),
Package: "node",
PURL: mustPURL("pkg:generic/node@version"),
CPEs: singleCPE("cpe:2.3:a:nodejs:node.js:*:*:*:*:*:*:*:*"),
},
{
Class: "go-binary-hint",
FileGlob: "**/VERSION",
EvidenceMatcher: FileContentsVersionMatcher(
`(?m)go(?P<version>[0-9]+\.[0-9]+(\.[0-9]+|beta[0-9]+|alpha[0-9]+|rc[0-9]+)?)`),
Package: "go",
PURL: mustPURL("pkg:generic/go@version"),
},
{
Class: "busybox-binary",
FileGlob: "**/busybox",
EvidenceMatcher: FileContentsVersionMatcher(
`(?m)BusyBox\s+v(?P<version>[0-9]+\.[0-9]+\.[0-9]+)`),
Package: "busybox",
PURL: mustPURL("pkg:generic/busybox@version"),
CPEs: singleCPE("cpe:2.3:a:busybox:busybox:*:*:*:*:*:*:*:*"),
},
{
Class: "haproxy-binary",
FileGlob: "**/haproxy",
EvidenceMatcher: evidenceMatchers(
FileContentsVersionMatcher(`(?m)HA-Proxy version (?P<version>[0-9]+\.[0-9]+\.[0-9]+)`),
FileContentsVersionMatcher(`(?m)(?P<version>[0-9]+\.[0-9]+\.[0-9]+)-[0-9a-zA-Z]{7}.+HAProxy version`),
),
Package: "haproxy",
PURL: mustPURL("pkg:generic/haproxy@version"),
CPEs: singleCPE("cpe:2.3:a:haproxy:haproxy:*:*:*:*:*:*:*:*"),
},
{
Class: "perl-binary",
FileGlob: "**/perl",
EvidenceMatcher: FileContentsVersionMatcher(
`(?m)\/usr\/local\/lib\/perl\d\/(?P<version>[0-9]+\.[0-9]+\.[0-9]+)`),
Package: "perl",
PURL: mustPURL("pkg:generic/perl@version"),
CPEs: singleCPE("cpe:2.3:a:perl:perl:*:*:*:*:*:*:*:*"),
},
{
Class: "php-cli-binary",
FileGlob: "**/php*",
EvidenceMatcher: fileNameTemplateVersionMatcher(
`(.*/|^)php[0-9]*$`,
`(?m)X-Powered-By: PHP\/(?P<version>[0-9]+\.[0-9]+\.[0-9]+(beta[0-9]+|alpha[0-9]+|RC[0-9]+)?)`),
Package: "php-cli",
PURL: mustPURL("pkg:generic/php-cli@version"),
CPEs: singleCPE("cpe:2.3:a:php:php:*:*:*:*:*:*:*:*"),
},
{
Class: "php-fpm-binary",
FileGlob: "**/php-fpm*",
EvidenceMatcher: FileContentsVersionMatcher(
`(?m)X-Powered-By: PHP\/(?P<version>[0-9]+\.[0-9]+\.[0-9]+(beta[0-9]+|alpha[0-9]+|RC[0-9]+)?)`),
Package: "php-fpm",
PURL: mustPURL("pkg:generic/php-fpm@version"),
CPEs: singleCPE("cpe:2.3:a:php:php:*:*:*:*:*:*:*:*"),
},
{
Class: "php-apache-binary",
FileGlob: "**/libphp*.so",
EvidenceMatcher: FileContentsVersionMatcher(
`(?m)X-Powered-By: PHP\/(?P<version>[0-9]+\.[0-9]+\.[0-9]+(beta[0-9]+|alpha[0-9]+|RC[0-9]+)?)`),
Package: "libphp",
PURL: mustPURL("pkg:generic/php@version"),
CPEs: singleCPE("cpe:2.3:a:php:php:*:*:*:*:*:*:*:*"),
},
{
Class: "php-composer-binary",
FileGlob: "**/composer*",
EvidenceMatcher: FileContentsVersionMatcher(
`(?m)'pretty_version'\s*=>\s*'(?P<version>[0-9]+\.[0-9]+\.[0-9]+(beta[0-9]+|alpha[0-9]+|RC[0-9]+)?)'`),
Package: "composer",
PURL: mustPURL("pkg:generic/composer@version"),
CPEs: singleCPE("cpe:2.3:a:getcomposer:composer:*:*:*:*:*:*:*:*"),
},
{
Class: "httpd-binary",
FileGlob: "**/httpd",
EvidenceMatcher: FileContentsVersionMatcher(
`(?m)Apache\/(?P<version>[0-9]+\.[0-9]+\.[0-9]+)`),
Package: "httpd",
PURL: mustPURL("pkg:generic/httpd@version"),
CPEs: singleCPE("cpe:2.3:a:apache:http_server:*:*:*:*:*:*:*:*"),
},
{
Class: "memcached-binary",
FileGlob: "**/memcached",
EvidenceMatcher: FileContentsVersionMatcher(
`(?m)memcached\s(?P<version>[0-9]+\.[0-9]+\.[0-9]+)`),
Package: "memcached",
PURL: mustPURL("pkg:generic/memcached@version"),
},
{
Class: "traefik-binary",
FileGlob: "**/traefik",
EvidenceMatcher: FileContentsVersionMatcher(
// [NUL]v1.7.34[NUL]
// [NUL]2.9.6[NUL]
`(?m)\x00v?(?P<version>[0-9]+\.[0-9]+\.[0-9]+(-alpha[0-9]|-beta[0-9]|-rc[0-9])?)\x00`),
Package: "traefik",
PURL: mustPURL("pkg:generic/traefik@version"),
},
{
Class: "postgresql-binary",
FileGlob: "**/postgres",
EvidenceMatcher: FileContentsVersionMatcher(
// [NUL]PostgreSQL 15beta4
// [NUL]PostgreSQL 15.1
// [NUL]PostgreSQL 9.6.24
// ?PostgreSQL 9.5alpha1
`(?m)(\x00|\?)PostgreSQL (?P<version>[0-9]+(\.[0-9]+)?(\.[0-9]+)?(alpha[0-9]|beta[0-9]|rc[0-9])?)`),
Package: "postgresql",
PURL: mustPURL("pkg:generic/postgresql@version"),
},
{
Class: "mysql-binary",
FileGlob: "**/mysql",
EvidenceMatcher: FileContentsVersionMatcher(
// ../../mysql-8.0.34
// /mysql-5.6.51/bld/client
`(?m).*/mysql-(?P<version>[0-9]+(\.[0-9]+)?(\.[0-9]+)?(alpha[0-9]|beta[0-9]|rc[0-9])?)`),
Package: "mysql",
PURL: mustPURL("pkg:generic/mysql@version"),
CPEs: singleCPE("cpe:2.3:a:oracle:mysql:*:*:*:*:*:*:*:*"),
},
{
Class: "mariadb-binary",
FileGlob: "**/mariadb",
EvidenceMatcher: FileContentsVersionMatcher(
// 10.6.15-MariaDB
`(?m)(?P<version>[0-9]+(\.[0-9]+)?(\.[0-9]+)?(alpha[0-9]|beta[0-9]|rc[0-9])?)-MariaDB`),
Package: "mariadb",
PURL: mustPURL("pkg:generic/mariadb@version"),
},
{
Class: "rust-standard-library-linux",
FileGlob: "**/libstd-????????????????.so",
EvidenceMatcher: FileContentsVersionMatcher(
// clang LLVM (rustc version 1.48.0 (7eac88abb 2020-11-16))
`(?m)(\x00)clang LLVM \(rustc version (?P<version>[0-9]+(\.[0-9]+)?(\.[0-9]+)) \(\w+ \d{4}\-\d{2}\-\d{2}\)`),
Package: "rust",
PURL: mustPURL("pkg:generic/rust@version"),
CPEs: singleCPE("cpe:2.3:a:rust-lang:rust:*:*:*:*:*:*:*:*"),
},
{
Class: "rust-standard-library-macos",
FileGlob: "**/libstd-????????????????.dylib",
EvidenceMatcher: FileContentsVersionMatcher(
// c 1.48.0 (7eac88abb 2020-11-16)
`(?m)c (?P<version>[0-9]+(\.[0-9]+)?(\.[0-9]+)) \(\w+ \d{4}\-\d{2}\-\d{2}\)`),
Package: "rust",
PURL: mustPURL("pkg:generic/rust@version"),
CPEs: singleCPE("cpe:2.3:a:rust-lang:rust:*:*:*:*:*:*:*:*"),
},
{
Class: "ruby-binary",
FileGlob: "**/ruby",
EvidenceMatcher: evidenceMatchers(
rubyMatcher,
sharedLibraryLookup(
// try to find version information from libruby shared libraries
`^libruby\.so.*$`,
rubyMatcher),
),
Package: "ruby",
PURL: mustPURL("pkg:generic/ruby@version"),
CPEs: singleCPE("cpe:2.3:a:ruby-lang:ruby:*:*:*:*:*:*:*:*"),
},
{
Class: "erlang-binary",
FileGlob: "**/erlexec",
EvidenceMatcher: FileContentsVersionMatcher(
// <artificial>[NUL]/usr/local/src/otp-25.3.2.7/erts/
`(?m)\<artificial\>\x00/usr/local/src/otp-(?P<version>[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+?)/erts/`,
),
Package: "erlang",
PURL: mustPURL("pkg:generic/erlang@version"),
CPEs: singleCPE("cpe:2.3:a:erlang:erlang\\/otp:*:*:*:*:*:*:*:*"),
},
{
Class: "consul-binary",
FileGlob: "**/consul",
EvidenceMatcher: FileContentsVersionMatcher(
// NOTE: This is brittle and may not work for past or future versions
`CONSUL_VERSION: (?P<version>\d+\.\d+\.\d+)`,
),
Package: "consul",
PURL: mustPURL("pkg:golang/github.com/hashicorp/consul@version"),
CPEs: singleCPE("cpe:2.3:a:hashicorp:consul:*:*:*:*:*:*:*:*"),
},
{
Class: "nginx-binary",
FileGlob: "**/nginx",
EvidenceMatcher: FileContentsVersionMatcher(
// [NUL]nginx version: nginx/1.25.1 - fetches '1.25.1'
// [NUL]nginx version: openresty/1.21.4.1 - fetches '1.21.4' as this is the nginx version part
`(?m)(\x00|\?)nginx version: [^\/]+\/(?P<version>[0-9]+\.[0-9]+\.[0-9]+(?:\+\d+)?(?:-\d+)?)`,
),
Package: "nginx",
PURL: mustPURL("pkg:generic/nginx@version"),
CPEs: []cpe.CPE{
cpe.Must("cpe:2.3:a:f5:nginx:*:*:*:*:*:*:*:*"),
cpe.Must("cpe:2.3:a:nginx:nginx:*:*:*:*:*:*:*:*"),
},
},
{
Class: "bash-binary",
FileGlob: "**/bash",
EvidenceMatcher: FileContentsVersionMatcher(
// @(#)Bash version 5.2.15(1) release GNU
// @(#)Bash version 5.2.0(1) alpha GNU
// @(#)Bash version 5.2.0(1) beta GNU
// @(#)Bash version 5.2.0(1) rc4 GNU
`(?m)@\(#\)Bash version (?P<version>[0-9]+\.[0-9]+\.[0-9]+)\([0-9]\) [a-z0-9]+ GNU`,
),
Package: "bash",
PURL: mustPURL("pkg:generic/bash@version"),
CPEs: singleCPE("cpe:2.3:a:gnu:bash:*:*:*:*:*:*:*:*"),
},
{
Class: "openssl-binary",
FileGlob: "**/openssl",
EvidenceMatcher: FileContentsVersionMatcher(
// [NUL]OpenSSL 3.1.4'
`\x00OpenSSL (?P<version>[0-9]+\.[0-9]+\.[0-9]+(-alpha[0-9]|-beta[0-9]|-rc[0-9])?)`,
),
Package: "openssl",
PURL: mustPURL("pkg:generic/openssl@version"),
CPEs: singleCPE("cpe:2.3:a:openssl:openssl:*:*:*:*:*:*:*:*"),
},
}
}
// in both binaries and shared libraries, the version pattern is [NUL]3.11.2[NUL]
@ -356,7 +357,7 @@ var libpythonMatcher = fileNameTemplateVersionMatcher(
pythonVersionTemplate,
)
var rubyMatcher = fileContentsVersionMatcher(
var rubyMatcher = FileContentsVersionMatcher(
// ruby 3.2.1 (2023-02-08 revision 31819e82c8) [x86_64-linux]
// ruby 2.7.7p221 (2022-11-24 revision 168ec2b1e5) [x86_64-linux]
`(?m)ruby (?P<version>[0-9]+\.[0-9]+\.[0-9]+(p[0-9]+)?) `)

View File

@ -3,12 +3,15 @@ package binary
import (
"reflect"
"github.com/anchore/packageurl-go"
"github.com/anchore/syft/syft/cpe"
"github.com/anchore/syft/syft/file"
"github.com/anchore/syft/syft/pkg"
)
func newPackage(classifier classifier, location file.Location, matchMetadata map[string]string) *pkg.Package {
var emptyPURL = packageurl.PackageURL{}
func newPackage(classifier Classifier, location file.Location, matchMetadata map[string]string) *pkg.Package {
version, ok := matchMetadata["version"]
if !ok {
return nil
@ -42,20 +45,12 @@ func newPackage(classifier classifier, location file.Location, matchMetadata map
},
}
if classifier.Type != "" {
p.Type = classifier.Type
}
if !reflect.DeepEqual(classifier.PURL, emptyPURL) {
purl := classifier.PURL
purl.Version = version
p.PURL = purl.ToString()
}
if classifier.Language != "" {
p.Language = classifier.Language
}
p.SetID()
return &p

View File

@ -0,0 +1,3 @@
blah
foobar 1.2.3
baz

View File

@ -44,7 +44,7 @@ func ImageCatalogers(cfg Config) []pkg.Cataloger {
return filterCatalogers([]pkg.Cataloger{
arch.NewDBCataloger(),
alpine.NewDBCataloger(),
binary.NewCataloger(),
binary.NewCataloger(cfg.Binary),
cpp.NewConanInfoCataloger(),
debian.NewDBCataloger(),
dotnet.NewDotnetPortableExecutableCataloger(),
@ -68,7 +68,7 @@ func DirectoryCatalogers(cfg Config) []pkg.Cataloger {
return filterCatalogers([]pkg.Cataloger{
arch.NewDBCataloger(),
alpine.NewDBCataloger(),
binary.NewCataloger(),
binary.NewCataloger(cfg.Binary),
cpp.NewConanCataloger(),
dart.NewPubspecLockCataloger(),
debian.NewDBCataloger(),
@ -107,7 +107,7 @@ func AllCatalogers(cfg Config) []pkg.Cataloger {
return filterCatalogers([]pkg.Cataloger{
arch.NewDBCataloger(),
alpine.NewDBCataloger(),
binary.NewCataloger(),
binary.NewCataloger(cfg.Binary),
cpp.NewConanCataloger(),
dart.NewPubspecLockCataloger(),
debian.NewDBCataloger(),

View File

@ -2,6 +2,7 @@ package cataloger
import (
"github.com/anchore/syft/syft/cataloging"
"github.com/anchore/syft/syft/pkg/cataloger/binary"
"github.com/anchore/syft/syft/pkg/cataloger/golang"
"github.com/anchore/syft/syft/pkg/cataloger/java"
"github.com/anchore/syft/syft/pkg/cataloger/javascript"
@ -17,6 +18,7 @@ type Config struct {
Python python.CatalogerConfig
Java java.ArchiveCatalogerConfig
Javascript javascript.CatalogerConfig
Binary binary.CatalogerConfig
Catalogers []string
Parallelism int
ExcludeBinaryOverlapByOwnership bool
@ -30,6 +32,7 @@ func DefaultConfig() Config {
Python: python.DefaultCatalogerConfig(),
Java: java.DefaultArchiveCatalogerConfig(),
Javascript: javascript.DefaultCatalogerConfig(),
Binary: binary.DefaultCatalogerConfig(),
ExcludeBinaryOverlapByOwnership: true,
}
}