mirror of
https://github.com/anchore/syft.git
synced 2025-11-17 08:23:15 +01:00
fix: align binary java detection with jvm cataloger + support IBM (#4046)
Signed-off-by: Keith Zantow <kzantow@gmail.com>
This commit is contained in:
parent
78c7cd2cc2
commit
48bf81cf7f
@ -41,11 +41,11 @@ func MatchNamedCaptureGroups(regEx *regexp.Regexp, content string) map[string]st
|
||||
// 1.5x the reader chunk size (1MB * 1.5).
|
||||
func MatchNamedCaptureGroupsFromReader(re *regexp.Regexp, r io.Reader) (map[string]string, error) {
|
||||
results := make(map[string]string)
|
||||
_, err := processReaderInChunks(r, readerChunkSize, matchNamedCaptureGroupsHandler(re, results))
|
||||
matches, err := processReaderInChunks(r, readerChunkSize, matchNamedCaptureGroupsHandler(re, results))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(results) == 0 {
|
||||
if !matches {
|
||||
return nil, nil
|
||||
}
|
||||
return results, nil
|
||||
|
||||
@ -697,21 +697,21 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
|
||||
{
|
||||
logicalFixture: "java-jre-openjdk/1.8.0_352-b08/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "java/jre",
|
||||
Name: "openjdk",
|
||||
Version: "1.8.0_352-b08",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/java/jre@1.8.0_352-b08",
|
||||
PURL: "pkg:generic/oracle/openjdk@1.8.0_352-b08",
|
||||
Locations: locations("java"),
|
||||
Metadata: metadata("java-binary-openjdk", "java"),
|
||||
Metadata: metadata("java-binary-openjdk-with-update", "java"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "java-jre-openjdk/11.0.17/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "java/jre",
|
||||
Name: "openjdk",
|
||||
Version: "11.0.17+8-LTS",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/java/jre@11.0.17%2B8-LTS",
|
||||
PURL: "pkg:generic/oracle/openjdk@11.0.17%2B8-LTS",
|
||||
Locations: locations("java"),
|
||||
Metadata: metadata("java-binary-openjdk", "java"),
|
||||
},
|
||||
@ -719,10 +719,10 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
|
||||
{
|
||||
logicalFixture: "java-jre-openjdk-eclipse/11.0.22/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "java/jre",
|
||||
Name: "openjdk",
|
||||
Version: "11.0.22+7",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/java/jre@11.0.22%2B7",
|
||||
PURL: "pkg:generic/oracle/openjdk@11.0.22%2B7",
|
||||
Locations: locations("java"),
|
||||
Metadata: metadata("java-binary-openjdk", "java"),
|
||||
},
|
||||
@ -730,10 +730,10 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
|
||||
{
|
||||
logicalFixture: "java-jre-openjdk-arm64-eclipse/11.0.22/linux-arm64",
|
||||
expected: pkg.Package{
|
||||
Name: "java/jre",
|
||||
Name: "openjdk",
|
||||
Version: "11.0.22+7",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/java/jre@11.0.22%2B7",
|
||||
PURL: "pkg:generic/oracle/openjdk@11.0.22%2B7",
|
||||
Locations: locations("java"),
|
||||
Metadata: metadata("java-binary-openjdk", "java"),
|
||||
},
|
||||
@ -741,10 +741,10 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
|
||||
{
|
||||
logicalFixture: "java-graal-openjdk/17.0.3+7-jvmci-22.1-b06/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "java/graalvm",
|
||||
Name: "graalvm",
|
||||
Version: "17.0.3+7-jvmci-22.1-b06",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/java/graalvm@17.0.3%2B7-jvmci-22.1-b06",
|
||||
PURL: "pkg:generic/oracle/graalvm@17.0.3%2B7-jvmci-22.1-b06",
|
||||
Locations: locations("java"),
|
||||
Metadata: metadata("java-binary-graalvm", "java"),
|
||||
},
|
||||
@ -754,10 +754,10 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
|
||||
// note: cannot find the original binary, using a custom snippet based on the original snippet in the repo
|
||||
logicalFixture: "java-jre-oracle/19.0.1/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "java/jre",
|
||||
Name: "jre",
|
||||
Version: "19.0.1+10-21",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/java/jre@19.0.1%2B10-21",
|
||||
PURL: "pkg:generic/oracle/jre@19.0.1%2B10-21",
|
||||
Locations: locations("java"),
|
||||
Metadata: metadata("java-binary-oracle", "java"),
|
||||
},
|
||||
@ -767,10 +767,10 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
|
||||
// note: cannot find the original binary, using a custom snippet based on the original snippet in the repo
|
||||
logicalFixture: "java-jre-oracle/19.0.1/darwin",
|
||||
expected: pkg.Package{
|
||||
Name: "java/jre",
|
||||
Name: "jre",
|
||||
Version: "19.0.1+10-21",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/java/jre@19.0.1%2B10-21",
|
||||
PURL: "pkg:generic/oracle/jre@19.0.1%2B10-21",
|
||||
Locations: locations("java"),
|
||||
Metadata: metadata("java-binary-oracle", "java"),
|
||||
},
|
||||
@ -778,23 +778,45 @@ func Test_Cataloger_PositiveCases(t *testing.T) {
|
||||
{
|
||||
logicalFixture: "java-jre-ibm/1.8.0_391/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "java/jre",
|
||||
Name: "java",
|
||||
Version: "1.8.0-foreman_2023_10_12_13_27-b00",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/java/jre@1.8.0-foreman_2023_10_12_13_27-b00",
|
||||
PURL: "pkg:generic/ibm/java@1.8.0-foreman_2023_10_12_13_27-b00",
|
||||
Locations: locations("java"),
|
||||
Metadata: metadata("java-binary-ibm", "java"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "java-ibm-8-jre/1.8.0_451/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "java",
|
||||
Version: "1.8.0-_2025_04_14_02_37-b00",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/ibm/java@1.8.0-_2025_04_14_02_37-b00",
|
||||
Locations: locations("java"),
|
||||
Metadata: metadata("java-binary-ibm", "java"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "java-ibm-8-sdk-alpine/1.8.0_321/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "java_sdk",
|
||||
Version: "1.8.0-foreman_2022_01_20_09_33-b00",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/ibm/java_sdk@1.8.0-foreman_2022_01_20_09_33-b00",
|
||||
Locations: locations("jdb"),
|
||||
Metadata: metadata("java-sdk-binary-ibm", "jdb"),
|
||||
},
|
||||
},
|
||||
{
|
||||
logicalFixture: "java-jdk-openjdk/21.0.2+13-LTS/linux-amd64",
|
||||
expected: pkg.Package{
|
||||
Name: "java/jdk",
|
||||
Name: "openjdk",
|
||||
Version: "21.0.2+13-LTS",
|
||||
Type: "binary",
|
||||
PURL: "pkg:generic/java/jdk@21.0.2%2B13-LTS",
|
||||
PURL: "pkg:generic/oracle/openjdk@21.0.2%2B13-LTS",
|
||||
Locations: locations("jdb"),
|
||||
Metadata: metadata("java-binary-jdk", "java"),
|
||||
Metadata: metadata("java-binary-openjdk-fallthrough", "jdb"),
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -1463,8 +1485,8 @@ func Test_Cataloger_CustomClassifiers(t *testing.T) {
|
||||
Class: "foo-binary",
|
||||
FileGlob: "**/foo",
|
||||
EvidenceMatcher: binutils.FileContentsVersionMatcher(
|
||||
`(?m)foobar\s(?P<version>[0-9]+\.[0-9]+\.[0-9]+)`,
|
||||
catalogerName,
|
||||
`(?m)foobar\s(?P<version>[0-9]+\.[0-9]+\.[0-9]+)`,
|
||||
),
|
||||
Package: "foo",
|
||||
PURL: mustPURL("pkg:generic/foo@version"),
|
||||
@ -1521,7 +1543,7 @@ func Test_Cataloger_CustomClassifiers(t *testing.T) {
|
||||
binutils.Classifier{
|
||||
Class: "foo-binary",
|
||||
FileGlob: "**/foo",
|
||||
EvidenceMatcher: binutils.FileContentsVersionMatcher(`(?m)not there`, catalogerName),
|
||||
EvidenceMatcher: binutils.FileContentsVersionMatcher(catalogerName, `(?m)not there`),
|
||||
Package: "foo",
|
||||
PURL: mustPURL("pkg:generic/foo@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:foo:foo:*:*:*:*:*:*:*:*"),
|
||||
@ -1739,7 +1761,7 @@ func TestCatalogerConfig_MarshalJSON(t *testing.T) {
|
||||
{
|
||||
Class: "class",
|
||||
FileGlob: "glob",
|
||||
EvidenceMatcher: binutils.FileContentsVersionMatcher(".thing", catalogerName),
|
||||
EvidenceMatcher: binutils.FileContentsVersionMatcher(catalogerName, ".thing"),
|
||||
Package: "pkg",
|
||||
PURL: packageurl.PackageURL{
|
||||
Type: "type",
|
||||
|
||||
@ -28,11 +28,11 @@ func DefaultClassifiers() []binutils.Classifier {
|
||||
// ruby 2.7.7p221 (2022-11-24 revision 168ec2b1e5) [x86_64-linux]
|
||||
`(?m)ruby (?P<version>[0-9]+\.[0-9]+\.[0-9]+((p|preview|rc|dev)[0-9]*)?) `)
|
||||
|
||||
return []binutils.Classifier{
|
||||
classifiers := []binutils.Classifier{
|
||||
{
|
||||
Class: "python-binary",
|
||||
FileGlob: "**/python*",
|
||||
EvidenceMatcher: binutils.EvidenceMatchers(
|
||||
EvidenceMatcher: binutils.MatchAny(
|
||||
// try to find version information from libpython shared libraries
|
||||
binutils.SharedLibraryLookup(
|
||||
`^libpython[0-9]+(?:\.[0-9]+)+[a-z]?\.so.*$`,
|
||||
@ -98,7 +98,7 @@ func DefaultClassifiers() []binutils.Classifier {
|
||||
{
|
||||
Class: "redis-binary",
|
||||
FileGlob: "**/redis-server",
|
||||
EvidenceMatcher: binutils.EvidenceMatchers(
|
||||
EvidenceMatcher: binutils.MatchAny(
|
||||
// matches most recent versions of redis (~v7), e.g. "7.0.14buildkitsandbox-1702957741000000000"
|
||||
m.FileContentsVersionMatcher(`[^\d](?P<version>\d+.\d+\.\d+)buildkitsandbox-\d+`),
|
||||
// matches against older versions of redis (~v3 - v6), e.g. "4.0.11841ce7054bd9-1542359302000000000"
|
||||
@ -113,73 +113,10 @@ func DefaultClassifiers() []binutils.Classifier {
|
||||
cpe.Must("cpe:2.3:a:redis:redis:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
},
|
||||
{
|
||||
Class: "java-binary-openjdk",
|
||||
FileGlob: "**/java",
|
||||
EvidenceMatcher: binutils.MatchExcluding(
|
||||
binutils.EvidenceMatchers(
|
||||
m.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`),
|
||||
m.FileContentsVersionMatcher(
|
||||
// arm64 versions: [NUL]0.0[NUL][NUL][NUL][NUL][NUL]11.0.22+7[NUL][NUL][NUL][NUL][NUL][NUL][NUL]openjdk[NUL]java[NUL]
|
||||
`(?m)\x00(?P<release>[0-9]+[.0-9]*)\x00+(?P<version>[0-9]+[^\x00]+)\x00+openjdk\x00java`),
|
||||
),
|
||||
// don't match graalvm
|
||||
"-jvmci-",
|
||||
),
|
||||
Package: "java/jre",
|
||||
PURL: mustPURL("pkg:generic/java/jre@version"),
|
||||
// TODO the updates might need to be part of the CPE Attributes, like: 1.8.0:update152
|
||||
CPEs: singleCPE("cpe:2.3:a:oracle:openjdk:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
{
|
||||
Class: "java-binary-ibm",
|
||||
FileGlob: "**/java",
|
||||
EvidenceMatcher: m.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/jre",
|
||||
PURL: mustPURL("pkg:generic/java/jre@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:ibm:java:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
{
|
||||
Class: "java-binary-oracle",
|
||||
FileGlob: "**/java",
|
||||
EvidenceMatcher: binutils.MatchExcluding(
|
||||
m.FileContentsVersionMatcher(
|
||||
// [NUL]19.0.1+10-21[NUL]
|
||||
`(?m)\x00(?P<version>[0-9]+[.0-9]+[+][-0-9]+)\x00`),
|
||||
// don't match openjdk
|
||||
`\x00openjdk\x00`,
|
||||
),
|
||||
Package: "java/jre",
|
||||
PURL: mustPURL("pkg:generic/java/jre@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:oracle:jre:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
{
|
||||
Class: "java-binary-graalvm",
|
||||
FileGlob: "**/java",
|
||||
EvidenceMatcher: m.FileContentsVersionMatcher(
|
||||
`(?m)\x00(?P<version>[0-9]+[.0-9]+[.0-9]+\+[0-9]+-jvmci-[0-9]+[.0-9]+-b[0-9]+)\x00`),
|
||||
Package: "java/graalvm",
|
||||
PURL: mustPURL("pkg:generic/java/graalvm@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:oracle:graalvm:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
{
|
||||
Class: "java-binary-jdk",
|
||||
FileGlob: "**/jdb",
|
||||
EvidenceMatcher: m.FileContentsVersionMatcher(
|
||||
`(?m)\x00(?P<version>[0-9]+\.[0-9]+\.[0-9]+(\+[0-9]+)?([-._a-zA-Z0-9]+)?)\x00`),
|
||||
Package: "java/jdk",
|
||||
PURL: mustPURL("pkg:generic/java/jdk@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:oracle:jdk:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
{
|
||||
Class: "nodejs-binary",
|
||||
FileGlob: "**/node",
|
||||
EvidenceMatcher: binutils.EvidenceMatchers(
|
||||
EvidenceMatcher: binutils.MatchAny(
|
||||
// [NUL]node v0.10.48[NUL]
|
||||
// [NUL]v0.12.18[NUL]
|
||||
// [NUL]v4.9.1[NUL]
|
||||
@ -221,7 +158,7 @@ func DefaultClassifiers() []binutils.Classifier {
|
||||
{
|
||||
Class: "haproxy-binary",
|
||||
FileGlob: "**/haproxy",
|
||||
EvidenceMatcher: binutils.EvidenceMatchers(
|
||||
EvidenceMatcher: binutils.MatchAny(
|
||||
m.FileContentsVersionMatcher(`(?m)version (?P<version>[0-9]+\.[0-9]+(\.|-dev|-rc)[0-9]+)(-[a-z0-9]{7})?, released 20`),
|
||||
m.FileContentsVersionMatcher(`(?m)HA-Proxy version (?P<version>[0-9]+\.[0-9]+(\.|-dev)[0-9]+)`),
|
||||
m.FileContentsVersionMatcher(`(?m)(?P<version>[0-9]+\.[0-9]+(\.|-dev)[0-9]+)-[0-9a-zA-Z]{7}.+HAProxy version`),
|
||||
@ -303,7 +240,7 @@ func DefaultClassifiers() []binutils.Classifier {
|
||||
{
|
||||
Class: "mysql-binary",
|
||||
FileGlob: "**/mysql",
|
||||
EvidenceMatcher: binutils.EvidenceMatchers(
|
||||
EvidenceMatcher: binutils.MatchAny(
|
||||
// shutdown[NUL]8.0.37[NUL][NUL][NUL][NUL][NUL]mysql_real_esc
|
||||
m.FileContentsVersionMatcher(`\x00(?P<version>[0-9]+(\.[0-9]+)?(\.[0-9]+)?(alpha[0-9]|beta[0-9]|rc[0-9])?)\x00+mysql`),
|
||||
// /export/home/pb2/build/sb_0-26781090-1516292385.58/release/mysql-8.0.4-rc/mysys_ssl/my_default.cc
|
||||
@ -380,7 +317,7 @@ func DefaultClassifiers() []binutils.Classifier {
|
||||
{
|
||||
Class: "ruby-binary",
|
||||
FileGlob: "**/ruby",
|
||||
EvidenceMatcher: binutils.EvidenceMatchers(
|
||||
EvidenceMatcher: binutils.MatchAny(
|
||||
rubyMatcher,
|
||||
binutils.SharedLibraryLookup(
|
||||
// try to find version information from libruby shared libraries
|
||||
@ -394,7 +331,7 @@ func DefaultClassifiers() []binutils.Classifier {
|
||||
{
|
||||
Class: "erlang-binary",
|
||||
FileGlob: "**/erlexec",
|
||||
EvidenceMatcher: binutils.EvidenceMatchers(
|
||||
EvidenceMatcher: binutils.MatchAny(
|
||||
m.FileContentsVersionMatcher(
|
||||
// <artificial>[NUL]/usr/src/otp_src_25.3.2.6/erts/
|
||||
`(?m)/src/otp_src_(?P<version>[0-9]+\.[0-9]+(\.[0-9]+){0,2}(-rc[0-9])?)/erts/`,
|
||||
@ -411,7 +348,7 @@ func DefaultClassifiers() []binutils.Classifier {
|
||||
{
|
||||
Class: "erlang-alpine-binary",
|
||||
FileGlob: "**/beam.smp",
|
||||
EvidenceMatcher: binutils.EvidenceMatchers(
|
||||
EvidenceMatcher: binutils.MatchAny(
|
||||
m.FileContentsVersionMatcher(
|
||||
// <artificial>[NUL]/usr/src/otp_src_25.3.2.6/erts/
|
||||
`(?m)/src/otp_src_(?P<version>[0-9]+\.[0-9]+(\.[0-9]+){0,2}(-rc[0-9])?)/erts/`,
|
||||
@ -432,7 +369,7 @@ func DefaultClassifiers() []binutils.Classifier {
|
||||
{
|
||||
Class: "erlang-library",
|
||||
FileGlob: "**/liberts_internal.a",
|
||||
EvidenceMatcher: binutils.EvidenceMatchers(
|
||||
EvidenceMatcher: binutils.MatchAny(
|
||||
m.FileContentsVersionMatcher(
|
||||
// <artificial>[NUL]/usr/src/otp_src_25.3.2.6/erts/
|
||||
`(?m)/src/otp_src_(?P<version>[0-9]+\.[0-9]+(\.[0-9]+){0,2}(-rc[0-9])?)/erts/`,
|
||||
@ -681,6 +618,8 @@ func DefaultClassifiers() []binutils.Classifier {
|
||||
CPEs: singleCPE("cpe:2.3:a:google:chrome:*:*:*:*:*:*:*:*"),
|
||||
},
|
||||
}
|
||||
|
||||
return append(classifiers, defaultJavaClassifiers()...)
|
||||
}
|
||||
|
||||
// singleCPE returns a []cpe.CPE with Source: Generated based on the cpe string or panics if the
|
||||
|
||||
196
syft/pkg/cataloger/binary/classifiers_java.go
Normal file
196
syft/pkg/cataloger/binary/classifiers_java.go
Normal file
@ -0,0 +1,196 @@
|
||||
package binary
|
||||
|
||||
import (
|
||||
"github.com/anchore/syft/syft/cpe"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/internal/binutils"
|
||||
)
|
||||
|
||||
//nolint:funlen
|
||||
func defaultJavaClassifiers() []binutils.Classifier {
|
||||
m := binutils.ContextualEvidenceMatchers{CatalogerName: catalogerName}
|
||||
|
||||
return []binutils.Classifier{
|
||||
{
|
||||
Class: "java-binary",
|
||||
FileGlob: "**/java",
|
||||
EvidenceMatcher: binutils.BranchingEvidenceMatcher([]binutils.Classifier{
|
||||
{
|
||||
Class: "java-binary-graalvm",
|
||||
EvidenceMatcher: m.FileContentsVersionMatcher(
|
||||
`(?m)\x00(?P<version>[0-9]+[.0-9]+[.0-9]+\+[0-9]+-jvmci-[0-9]+[.0-9]+-b[0-9]+)\x00`),
|
||||
Package: "graalvm",
|
||||
PURL: mustPURL("pkg:generic/oracle/graalvm@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:oracle:graalvm:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
{
|
||||
Class: "java-binary-openjdk-zulu",
|
||||
EvidenceMatcher: binutils.MatchAll(
|
||||
binutils.MatchPath(`**/*zulu*/**`),
|
||||
binutils.MatchAny(
|
||||
m.FileContentsVersionMatcher(
|
||||
// [NUL]openjdk[NUL]java[NUL]0.0[NUL]11.0.17+8-LTS[NUL]
|
||||
`(?m)\x00java\x00(?P<release>[0-9]+[.0-9]*)\x00(?P<version>[0-9]+[^\x00]+)\x00`),
|
||||
m.FileContentsVersionMatcher(
|
||||
// arm64 versions: [NUL]0.0[NUL][NUL][NUL][NUL][NUL]11.0.22+7[NUL][NUL][NUL][NUL][NUL][NUL][NUL]openjdk[NUL]java[NUL]
|
||||
`(?m)\x00(?P<release>[0-9]+[.0-9]*)\x00+(?P<version>[0-9]+[^\x00]+)\x00+(openjdk|java)`),
|
||||
),
|
||||
),
|
||||
Package: "zulu",
|
||||
PURL: mustPURL("pkg:generic/azul/zulu@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:azul:zulu:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
{
|
||||
Class: "java-binary-openjdk-with-update",
|
||||
EvidenceMatcher: binutils.MatchAny(
|
||||
m.FileContentsVersionMatcher(
|
||||
`openjdk`,
|
||||
// [NUL]openjdk[NUL]java[NUL]1.8[NUL]1.8.0_352-b08[NUL]
|
||||
`(?m)java\x00(?P<release>[0-9]+[.0-9]*)\x00(?P<version>(?<primary>[0-9]+[^\x00]+)_(?<update>[^\x00]+)-[^\x00]+)\x00`),
|
||||
m.FileContentsVersionMatcher(
|
||||
`openjdk`,
|
||||
// arm64 versions: [NUL]0.0[NUL][NUL][NUL][NUL][NUL]1.8.0_352-b08[NUL][NUL][NUL][NUL][NUL][NUL][NUL]openjdk[NUL]java[NUL]
|
||||
`(?m)\x00(?P<release>[0-9]+[.0-9]*)\x00+(?P<version>(?<primary>[0-9]+[^\x00]+)_(?<update>[^\x00]+)-[^\x00]+)\x00+openjdk\x00java`),
|
||||
),
|
||||
Package: "openjdk",
|
||||
PURL: mustPURL("pkg:generic/oracle/openjdk@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:oracle:openjdk:{{.primary}}:update{{.update}}:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
{
|
||||
Class: "java-binary-openjdk",
|
||||
EvidenceMatcher: binutils.MatchAny(
|
||||
m.FileContentsVersionMatcher(
|
||||
// [NUL]openjdk[NUL]java[NUL]0.0[NUL]11.0.17+8-LTS[NUL]
|
||||
`(?m)\x00openjdk\x00java\x00(?P<release>[0-9]+[.0-9]*)\x00(?P<version>[0-9]+[^\x00]+)\x00`),
|
||||
m.FileContentsVersionMatcher(
|
||||
// arm64 versions: [NUL]0.0[NUL][NUL][NUL][NUL][NUL]11.0.22+7[NUL][NUL][NUL][NUL][NUL][NUL][NUL]openjdk[NUL]java[NUL]
|
||||
`(?m)\x00(?P<release>[0-9]+[.0-9]*)\x00+(?P<version>[0-9]+[^\x00]+)\x00+openjdk\x00java`),
|
||||
),
|
||||
Package: "openjdk",
|
||||
PURL: mustPURL("pkg:generic/oracle/openjdk@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:oracle:openjdk:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
{
|
||||
Class: "java-binary-ibm",
|
||||
EvidenceMatcher: binutils.MatchAll(
|
||||
binutils.MatchAny(
|
||||
binutils.MatchPath("**/ibm/**"),
|
||||
binutils.SharedLibraryLookup(
|
||||
`^libjli\.so$`,
|
||||
m.FileContentsVersionMatcher("IBM_JAVA")),
|
||||
),
|
||||
m.FileContentsVersionMatcher(
|
||||
// [NUL]java[NUL]1.8[NUL][NUL][NUL]1.8.0-foreman_2022_01_20_09_33-b00[NUL]
|
||||
`(?m)\x00java\x00+(?P<release>[0-9]+[.0-9]+)\x00+(?P<version>[0-9]+[-._a-zA-Z0-9]+)\x00`),
|
||||
),
|
||||
Package: "java",
|
||||
PURL: mustPURL("pkg:generic/ibm/java@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:ibm:java:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
{
|
||||
Class: "java-binary-openjdk-fallthrough",
|
||||
EvidenceMatcher: m.FileContentsVersionMatcher(
|
||||
"openjdk",
|
||||
// [NUL]19.0.1+10-21[NUL]
|
||||
`(?m)\x00(?P<version>[0-9]+[.0-9]+[+][-0-9]+)\x00`,
|
||||
),
|
||||
Package: "jre",
|
||||
PURL: mustPURL("pkg:generic/oracle/jre@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:oracle:jre:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
{
|
||||
Class: "java-binary-oracle",
|
||||
EvidenceMatcher: m.FileContentsVersionMatcher(
|
||||
// [NUL]19.0.1+10-21[NUL]
|
||||
// java[NUL]1.8[NUL]1.8.0_451-b10
|
||||
`(?m)\x00(?P<version>[0-9]+\.[0-9]+\.[-._+a-zA-Z0-9]+)\x00`,
|
||||
),
|
||||
Package: "jre",
|
||||
PURL: mustPURL("pkg:generic/oracle/jre@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:oracle:jre:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
}...),
|
||||
},
|
||||
{
|
||||
Class: "java-jdb-binary",
|
||||
FileGlob: "**/jdb",
|
||||
EvidenceMatcher: binutils.BranchingEvidenceMatcher([]binutils.Classifier{
|
||||
{
|
||||
Class: "java-binary-graalvm",
|
||||
EvidenceMatcher: m.FileContentsVersionMatcher(
|
||||
`(?m)\x00(?P<version>[0-9]+[.0-9]+[.0-9]+\+[0-9]+-jvmci-[0-9]+[.0-9]+-b[0-9]+)\x00`),
|
||||
Package: "graalvm",
|
||||
PURL: mustPURL("pkg:generic/oracle/graalvm@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:oracle:graalvm_for_jdk:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
{
|
||||
Class: "java-jdb-binary-openjdk",
|
||||
EvidenceMatcher: binutils.MatchAll(
|
||||
m.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`),
|
||||
m.FileContentsVersionMatcher(
|
||||
// arm64 versions: [NUL]0.0[NUL][NUL][NUL][NUL][NUL]11.0.22+7[NUL][NUL][NUL][NUL][NUL][NUL][NUL]openjdk[NUL]java[NUL]
|
||||
`(?m)\x00(?P<release>[0-9]+[.0-9]*)\x00+(?P<version>[0-9]+[^\x00]+)\x00+openjdk\x00java`),
|
||||
),
|
||||
Package: "openjdk",
|
||||
PURL: mustPURL("pkg:generic/oracle/openjdk@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:oracle:openjdk:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
{
|
||||
Class: "jdb-binary-openjdk-zulu",
|
||||
EvidenceMatcher: binutils.MatchAll(
|
||||
binutils.MatchPath("**/*zulu*/**"),
|
||||
binutils.MatchAny(
|
||||
m.FileContentsVersionMatcher(
|
||||
// [NUL]openjdk[NUL]java[NUL]0.0[NUL]11.0.17+8-LTS[NUL]
|
||||
`(?m)\x00openjdk\x00java\x00(?P<release>[0-9]+[.0-9]*)\x00(?P<version>[0-9]+[^\x00]+)\x00`),
|
||||
m.FileContentsVersionMatcher(
|
||||
// arm64 versions: [NUL]0.0[NUL][NUL][NUL][NUL][NUL]11.0.22+7[NUL][NUL][NUL][NUL][NUL][NUL][NUL]openjdk[NUL]java[NUL]
|
||||
`(?m)\x00(?P<release>[0-9]+[.0-9]*)\x00+(?P<version>[0-9]+[^\x00]+)\x00+openjdk\x00java`),
|
||||
),
|
||||
),
|
||||
Package: "zulu",
|
||||
PURL: mustPURL("pkg:generic/azul/zulu@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:azul:zulu:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
{
|
||||
Class: "java-sdk-binary-ibm",
|
||||
EvidenceMatcher: binutils.MatchAll(
|
||||
binutils.MatchAny(
|
||||
binutils.MatchPath("**/ibm/**"),
|
||||
binutils.SharedLibraryLookup(
|
||||
`^libjli\.so$`,
|
||||
m.FileContentsVersionMatcher("IBM_JAVA")),
|
||||
),
|
||||
m.FileContentsVersionMatcher(
|
||||
// [NUL]java[NUL]./lib/tools.jar./lib/sa-jdi.jar./classes.-J-ms8m[NUL][NUL]1.8[NUL][NUL][NUL]1.8.0-foreman_2022_01_20_09_33-b00[NUL]
|
||||
`(?m)\x00java\x00.+?\x00(?P<version>[0-9]+[-._a-zA-Z0-9]+)\x00`),
|
||||
),
|
||||
Package: "java_sdk",
|
||||
PURL: mustPURL("pkg:generic/ibm/java_sdk@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:ibm:java_sdk:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
{
|
||||
Class: "java-binary-openjdk-fallthrough",
|
||||
EvidenceMatcher: binutils.MatchAll(
|
||||
m.FileContentsVersionMatcher(
|
||||
"openjdk",
|
||||
`(?m)\x00(?P<version>[0-9]+\.[0-9]+\.[0-9]+(\+[0-9]+)?([-._a-zA-Z0-9]+)?)\x00`),
|
||||
),
|
||||
Package: "openjdk",
|
||||
PURL: mustPURL("pkg:generic/oracle/openjdk@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:oracle:openjdk:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
{
|
||||
Class: "java-binary-jdk",
|
||||
EvidenceMatcher: m.FileContentsVersionMatcher(
|
||||
`(?m)\x00(?P<version>[0-9]+\.[0-9]+\.[0-9]+(\+[0-9]+)?([-._a-zA-Z0-9]+)?)\x00`),
|
||||
Package: "jdk",
|
||||
PURL: mustPURL("pkg:generic/oracle/jdk@version"),
|
||||
CPEs: singleCPE("cpe:2.3:a:oracle:jdk:*:*:*:*:*:*:*:*", cpe.NVDDictionaryLookupSource),
|
||||
},
|
||||
}...),
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -14,5 +14,5 @@ type EvidenceMatcher = binutils.EvidenceMatcher
|
||||
func FileContentsVersionMatcher(
|
||||
pattern string,
|
||||
) EvidenceMatcher {
|
||||
return binutils.FileContentsVersionMatcher(pattern, catalogerName)
|
||||
return binutils.FileContentsVersionMatcher(catalogerName, pattern)
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -194,6 +194,25 @@ from-images:
|
||||
platform: linux/amd64
|
||||
paths:
|
||||
- /opt/ibm/java/jre/bin/java
|
||||
- /opt/ibm/java/jre/lib/amd64/jli/libjli.so
|
||||
|
||||
- name: java-ibm-8-jre
|
||||
version: 1.8.0_451
|
||||
images:
|
||||
- ref: ibmjava:8-jre@sha256:3588cd1cc9b8646fe03b3b15210e69b1b520f1321f8518b69c0e7013d702fd23
|
||||
platform: linux/amd64
|
||||
paths:
|
||||
- /opt/ibm/java/jre/bin/java
|
||||
- /opt/ibm/java/jre/lib/amd64/jli/libjli.so
|
||||
|
||||
- name: java-ibm-8-sdk-alpine
|
||||
version: 1.8.0_321
|
||||
images:
|
||||
- ref: ibmjava:8-sdk-alpine@sha256:4f8ad2029e78f7b91721745a77fc6011a7c0e09b9edeffb6b20b6ec34a6e63cd
|
||||
platform: linux/amd64
|
||||
paths:
|
||||
- /opt/ibm/java/bin/jdb
|
||||
- /opt/ibm/java/lib/amd64/jli/libjli.so
|
||||
|
||||
- version: 10.6.15
|
||||
images:
|
||||
@ -320,6 +339,23 @@ from-images:
|
||||
paths:
|
||||
- /usr/local/bin/node
|
||||
|
||||
- name: java-jdk-oracle
|
||||
version: 1.8.0_451-b10
|
||||
images:
|
||||
- ref: container-registry.oracle.com/java/jdk:8u451-oraclelinux8@sha256:f9c980e52853bd0a8815ad908c9a4025ed68cb52f31f3b8cd0cd442ee367bfd1
|
||||
platform: linux/amd64
|
||||
paths:
|
||||
- /usr/java/jdk-8/bin/java
|
||||
- /usr/java/jdk-8/bin/jdb
|
||||
|
||||
- name: java-jre-oracle-sdk
|
||||
version: 1.8.0_451-b10
|
||||
images:
|
||||
- ref: container-registry.oracle.com/java/serverjre:1.8.0_451@sha256:fe78fe3efd292dceb5685882ce1b13eb78492f383ccfd52a1b88a4f755b9d996
|
||||
platform: linux/amd64
|
||||
paths:
|
||||
- /usr/java/jdk-8/bin/java
|
||||
|
||||
- name: java-jre-openjdk
|
||||
version: 1.8.0_352-b08
|
||||
images:
|
||||
|
||||
43
syft/pkg/cataloger/internal/binutils/branching_matcher.go
Normal file
43
syft/pkg/cataloger/internal/binutils/branching_matcher.go
Normal file
@ -0,0 +1,43 @@
|
||||
package binutils
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/anchore/syft/internal"
|
||||
"github.com/anchore/syft/syft/internal/unionreader"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
)
|
||||
|
||||
// BranchingEvidenceMatcher loads the contents of the reader into memory, assuming that all
|
||||
// classifier matchers are going to be reading the same file, e.g. a "java" binary, continuing to
|
||||
// try classifier EvidenceMatcher until the first set of packages is found
|
||||
func BranchingEvidenceMatcher(classifiers ...Classifier) EvidenceMatcher {
|
||||
return func(_ Classifier, context MatcherContext) ([]pkg.Package, error) {
|
||||
// we are scanning the same contents multiple times, so read it into memory for a reader that can reset
|
||||
rdr, err := getReader(context)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer internal.CloseAndLogError(rdr, context.Location.RealPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, c := range classifiers {
|
||||
pkgs, err := c.EvidenceMatcher(c, MatcherContext{
|
||||
Resolver: context.Resolver,
|
||||
Location: context.Location,
|
||||
GetReader: func(_ MatcherContext) (unionreader.UnionReader, error) {
|
||||
_, err := rdr.Seek(0, io.SeekStart)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return rdr, nil
|
||||
},
|
||||
})
|
||||
if len(pkgs) > 0 || err != nil {
|
||||
return pkgs, err
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,95 @@
|
||||
package binutils
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/internal/unionreader"
|
||||
)
|
||||
|
||||
func Test_BranchingMatcher(t *testing.T) {
|
||||
matchingTest := FileContentsVersionMatcher("", `my-verison:(?<version>\d+\.\d+)`)
|
||||
notMatchingTest := MatchPath("**/not-version*")
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
matcher EvidenceMatcher
|
||||
expectedPackageNames []string
|
||||
}{
|
||||
{
|
||||
name: "not matching",
|
||||
matcher: BranchingEvidenceMatcher(
|
||||
Classifier{
|
||||
EvidenceMatcher: MatchAll(
|
||||
notMatchingTest,
|
||||
matchingTest,
|
||||
),
|
||||
Package: "a-pkg",
|
||||
},
|
||||
),
|
||||
expectedPackageNames: nil,
|
||||
},
|
||||
{
|
||||
name: "both match",
|
||||
matcher: BranchingEvidenceMatcher(
|
||||
Classifier{
|
||||
EvidenceMatcher: matchingTest,
|
||||
Package: "a-pkg",
|
||||
},
|
||||
Classifier{
|
||||
EvidenceMatcher: matchingTest,
|
||||
Package: "b-pkg",
|
||||
},
|
||||
),
|
||||
expectedPackageNames: []string{"a-pkg"},
|
||||
},
|
||||
{
|
||||
name: "first-does-not-match",
|
||||
matcher: BranchingEvidenceMatcher(
|
||||
Classifier{
|
||||
EvidenceMatcher: MatchAll(
|
||||
notMatchingTest,
|
||||
matchingTest,
|
||||
),
|
||||
Package: "b-pkg",
|
||||
},
|
||||
Classifier{
|
||||
EvidenceMatcher: matchingTest,
|
||||
Package: "c-pkg",
|
||||
},
|
||||
),
|
||||
expectedPackageNames: []string{"c-pkg"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
resolver := file.NewMockResolverForPaths("test-fixtures/version.txt", "test-fixtures/version-parts.txt")
|
||||
locs, err := resolver.FilesByGlob("**/version.txt")
|
||||
require.NoError(t, err)
|
||||
require.Len(t, locs, 1)
|
||||
loc := locs[0]
|
||||
rdr, err := resolver.FileContentsByLocation(loc)
|
||||
require.NoError(t, err)
|
||||
urdr, err := unionreader.GetUnionReader(rdr)
|
||||
require.NoError(t, err)
|
||||
pkgs, err := test.matcher(Classifier{
|
||||
Package: "a-pkg",
|
||||
}, MatcherContext{
|
||||
Resolver: resolver,
|
||||
Location: loc,
|
||||
GetReader: func(resolver MatcherContext) (unionreader.UnionReader, error) {
|
||||
return urdr, nil
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
var got []string
|
||||
for i := range pkgs {
|
||||
got = append(got, pkgs[i].Name)
|
||||
}
|
||||
require.EqualValues(t, test.expectedPackageNames, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -8,11 +8,14 @@ import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"maps"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/bmatcuk/doublestar/v4"
|
||||
|
||||
"github.com/anchore/packageurl-go"
|
||||
"github.com/anchore/syft/internal"
|
||||
"github.com/anchore/syft/internal/log"
|
||||
@ -71,7 +74,8 @@ func (cfg Classifier) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(m)
|
||||
}
|
||||
|
||||
// EvidenceMatcher is a function called to catalog Packages that match some sort of evidence
|
||||
// EvidenceMatcher is a function called to identify based on some sort of evidence in the filesystem contents.
|
||||
// A non-nil return value indicates a successful match, regardless of packages being returned.
|
||||
type EvidenceMatcher func(classifier Classifier, context MatcherContext) ([]pkg.Package, error)
|
||||
|
||||
type MatcherContext struct {
|
||||
@ -80,13 +84,16 @@ type MatcherContext struct {
|
||||
GetReader func(resolver MatcherContext) (unionreader.UnionReader, error)
|
||||
}
|
||||
|
||||
func EvidenceMatchers(matchers ...EvidenceMatcher) EvidenceMatcher {
|
||||
// MatchAny returns a combined evidence matcher that returns results from the first
|
||||
// matcher that returns results
|
||||
func MatchAny(matchers ...EvidenceMatcher) EvidenceMatcher {
|
||||
return func(classifier Classifier, context MatcherContext) ([]pkg.Package, error) {
|
||||
for _, matcher := range matchers {
|
||||
match, err := matcher(classifier, context)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// only return when results
|
||||
if match != nil {
|
||||
return match, nil
|
||||
}
|
||||
@ -95,6 +102,23 @@ func EvidenceMatchers(matchers ...EvidenceMatcher) EvidenceMatcher {
|
||||
}
|
||||
}
|
||||
|
||||
// MatchAll executes all matchers until one returns nil results, only returning the final results
|
||||
func MatchAll(matchers ...EvidenceMatcher) EvidenceMatcher {
|
||||
return func(classifier Classifier, context MatcherContext) ([]pkg.Package, error) {
|
||||
var out []pkg.Package
|
||||
for _, matcher := range matchers {
|
||||
match, err := matcher(classifier, context)
|
||||
if match == nil || err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(match) > 0 {
|
||||
out = match
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
}
|
||||
|
||||
type ContextualEvidenceMatchers struct {
|
||||
CatalogerName string
|
||||
}
|
||||
@ -103,8 +127,8 @@ func (c ContextualEvidenceMatchers) FileNameTemplateVersionMatcher(fileNamePatte
|
||||
return FileNameTemplateVersionMatcher(fileNamePattern, contentTemplate, c.CatalogerName)
|
||||
}
|
||||
|
||||
func (c ContextualEvidenceMatchers) FileContentsVersionMatcher(pattern string) EvidenceMatcher {
|
||||
return FileContentsVersionMatcher(pattern, c.CatalogerName)
|
||||
func (c ContextualEvidenceMatchers) FileContentsVersionMatcher(patterns ...string) EvidenceMatcher {
|
||||
return FileContentsVersionMatcher(c.CatalogerName, patterns...)
|
||||
}
|
||||
|
||||
func FileNameTemplateVersionMatcher(fileNamePattern, contentTemplate, catalogerName string) EvidenceMatcher {
|
||||
@ -156,18 +180,37 @@ func FileNameTemplateVersionMatcher(fileNamePattern, contentTemplate, catalogerN
|
||||
}
|
||||
}
|
||||
|
||||
func FileContentsVersionMatcher(pattern, catalogerName string) EvidenceMatcher {
|
||||
pat := regexp.MustCompile(pattern)
|
||||
// FileContentsVersionMatcher will match all provided patterns, extracting named capture groups from each pattern, overwriting earlier results
|
||||
func FileContentsVersionMatcher(catalogerName string, patterns ...string) EvidenceMatcher {
|
||||
if len(patterns) == 0 {
|
||||
panic("must specify at least one pattern")
|
||||
}
|
||||
var pats []*regexp.Regexp
|
||||
for _, pattern := range patterns {
|
||||
pats = append(pats, regexp.MustCompile(pattern))
|
||||
}
|
||||
return func(classifier Classifier, context MatcherContext) ([]pkg.Package, error) {
|
||||
var matchMetadata map[string]string
|
||||
|
||||
for _, pat := range pats {
|
||||
contents, err := getReader(context)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to get read contents for file: %w", err)
|
||||
}
|
||||
|
||||
matchMetadata, err := internal.MatchNamedCaptureGroupsFromReader(pat, contents)
|
||||
match, err := internal.MatchNamedCaptureGroupsFromReader(pat, contents)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to match version: %w", err)
|
||||
}
|
||||
if match == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if matchMetadata == nil {
|
||||
matchMetadata = match
|
||||
} else {
|
||||
maps.Copy(matchMetadata, match)
|
||||
}
|
||||
}
|
||||
|
||||
// Convert {major: 1, minor: 2, patch: 3} to "1.2.3"
|
||||
_, versionOk := matchMetadata["version"]
|
||||
@ -187,6 +230,10 @@ func FileContentsVersionMatcher(pattern, catalogerName string) EvidenceMatcher {
|
||||
|
||||
p := NewClassifierPackage(classifier, context.Location, matchMetadata, catalogerName)
|
||||
if p == nil {
|
||||
if matchMetadata != nil {
|
||||
// if we had a successful metadata match, but no packages, return a successful match result
|
||||
return []pkg.Package{}, nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
@ -194,29 +241,6 @@ func FileContentsVersionMatcher(pattern, catalogerName string) EvidenceMatcher {
|
||||
}
|
||||
}
|
||||
|
||||
// MatchExcluding tests the provided regular expressions against the file, and if matched, DOES NOT return
|
||||
// anything that the matcher would otherwise return
|
||||
func MatchExcluding(matcher EvidenceMatcher, contentPatternsToExclude ...string) EvidenceMatcher {
|
||||
var nonMatchPatterns []*regexp.Regexp
|
||||
for _, p := range contentPatternsToExclude {
|
||||
nonMatchPatterns = append(nonMatchPatterns, regexp.MustCompile(p))
|
||||
}
|
||||
return func(classifier Classifier, context MatcherContext) ([]pkg.Package, error) {
|
||||
contents, err := getReader(context)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to get read contents for file: %w", err)
|
||||
}
|
||||
matches, err := internal.MatchAnyFromReader(contents, nonMatchPatterns...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to match content: %w", err)
|
||||
}
|
||||
if matches {
|
||||
return nil, nil
|
||||
}
|
||||
return matcher(classifier, context)
|
||||
}
|
||||
}
|
||||
|
||||
func SharedLibraryLookup(sharedLibraryPattern string, sharedLibraryMatcher EvidenceMatcher) EvidenceMatcher {
|
||||
pat := regexp.MustCompile(sharedLibraryPattern)
|
||||
return func(classifier Classifier, context MatcherContext) (packages []pkg.Package, _ error) {
|
||||
@ -234,16 +258,19 @@ func SharedLibraryLookup(sharedLibraryPattern string, sharedLibraryMatcher Evide
|
||||
return nil, err
|
||||
}
|
||||
for _, libraryLocation := range locations {
|
||||
// create a new resolver without the cached context lookup -- this is decidedly a different file
|
||||
newResolver := MatcherContext{
|
||||
Resolver: context.Resolver,
|
||||
Location: libraryLocation,
|
||||
GetReader: context.GetReader,
|
||||
}
|
||||
newResolver.Location = libraryLocation
|
||||
pkgs, err := sharedLibraryMatcher(classifier, newResolver)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// not a successful match
|
||||
if pkgs == nil {
|
||||
continue
|
||||
}
|
||||
for _, p := range pkgs {
|
||||
// set the source binary as the first location
|
||||
locationSet := file.NewLocationSet(context.Location)
|
||||
@ -260,12 +287,28 @@ func SharedLibraryLookup(sharedLibraryPattern string, sharedLibraryMatcher Evide
|
||||
}
|
||||
packages = append(packages, p)
|
||||
}
|
||||
// return non-nil package results as a successful match indication if the evidence matcher returned a successful match indication
|
||||
if packages == nil {
|
||||
packages = pkgs
|
||||
}
|
||||
}
|
||||
}
|
||||
return packages, nil
|
||||
}
|
||||
}
|
||||
|
||||
func MatchPath(path string) EvidenceMatcher {
|
||||
if !doublestar.ValidatePattern(path) {
|
||||
panic("invalid pattern")
|
||||
}
|
||||
return func(_ Classifier, context MatcherContext) ([]pkg.Package, error) {
|
||||
if doublestar.MatchUnvalidated(path, context.Location.RealPath) {
|
||||
return []pkg.Package{}, nil // return non-nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
func getReader(context MatcherContext) (unionreader.UnionReader, error) {
|
||||
if context.GetReader != nil {
|
||||
return context.GetReader(context)
|
||||
|
||||
@ -1,9 +1,12 @@
|
||||
package binutils
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"reflect"
|
||||
"text/template"
|
||||
|
||||
"github.com/anchore/packageurl-go"
|
||||
"github.com/anchore/syft/internal/log"
|
||||
"github.com/anchore/syft/syft/cpe"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
@ -21,8 +24,8 @@ func NewClassifierPackage(classifier Classifier, location file.Location, matchMe
|
||||
|
||||
var cpes []cpe.CPE
|
||||
for _, c := range classifier.CPEs {
|
||||
c.Attributes.Version = version
|
||||
c.Attributes.Update = update
|
||||
c.Attributes.Version = templatedUpdate(c.Attributes.Version, matchMetadata, version)
|
||||
c.Attributes.Update = templatedUpdate(c.Attributes.Update, matchMetadata, update)
|
||||
cpes = append(cpes, c)
|
||||
}
|
||||
|
||||
@ -55,3 +58,28 @@ func NewClassifierPackage(classifier Classifier, location file.Location, matchMe
|
||||
|
||||
return &p
|
||||
}
|
||||
|
||||
func templatedUpdate(providedValue string, matchMetadata map[string]string, defaultValue string) string {
|
||||
// if no template provided, just use the value directly
|
||||
if providedValue == "" {
|
||||
return defaultValue
|
||||
}
|
||||
// support templated updates
|
||||
t, err := template.New("").Option("missingkey=zero").Parse(providedValue)
|
||||
if err != nil {
|
||||
log.Debugf("unable to parse classifier template=%q : %w", providedValue, err)
|
||||
} else {
|
||||
update := bytes.Buffer{}
|
||||
err = t.Execute(&update, matchMetadata)
|
||||
if err != nil {
|
||||
log.Debugf("unable to render template: %w", err)
|
||||
} else {
|
||||
// only use the template result if it's non-empty
|
||||
providedValue = update.String()
|
||||
if providedValue != "" {
|
||||
return providedValue
|
||||
}
|
||||
}
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
@ -27,7 +27,7 @@ func Test_ClassifierCPEs(t *testing.T) {
|
||||
classifier: Classifier{
|
||||
Package: "some-app",
|
||||
FileGlob: "**/version.txt",
|
||||
EvidenceMatcher: FileContentsVersionMatcher(`(?m)my-verison:(?P<version>[0-9.]+)`, "cataloger-name"),
|
||||
EvidenceMatcher: FileContentsVersionMatcher("cataloger-name", `(?m)my-verison:(?P<version>[0-9.]+)`),
|
||||
CPEs: []cpe.CPE{},
|
||||
},
|
||||
cpes: nil,
|
||||
@ -38,7 +38,7 @@ func Test_ClassifierCPEs(t *testing.T) {
|
||||
classifier: Classifier{
|
||||
Package: "some-app",
|
||||
FileGlob: "**/version.txt",
|
||||
EvidenceMatcher: FileContentsVersionMatcher(`(?m)my-verison:(?P<version>[0-9.]+)`, "cataloger-name"),
|
||||
EvidenceMatcher: FileContentsVersionMatcher("cataloger-name", `(?m)my-verison:(?P<version>[0-9.]+)`),
|
||||
CPEs: []cpe.CPE{
|
||||
cpe.Must("cpe:2.3:a:some:app:*:*:*:*:*:*:*:*", cpe.GeneratedSource),
|
||||
},
|
||||
@ -53,7 +53,7 @@ func Test_ClassifierCPEs(t *testing.T) {
|
||||
classifier: Classifier{
|
||||
Package: "some-app",
|
||||
FileGlob: "**/version.txt",
|
||||
EvidenceMatcher: FileContentsVersionMatcher(`(?m)my-verison:(?P<version>[0-9.]+)`, "cataloger-name"),
|
||||
EvidenceMatcher: FileContentsVersionMatcher("cataloger-name", `(?m)my-verison:(?P<version>[0-9.]+)`),
|
||||
CPEs: []cpe.CPE{
|
||||
cpe.Must("cpe:2.3:a:some:app:*:*:*:*:*:*:*:*", cpe.GeneratedSource),
|
||||
cpe.Must("cpe:2.3:a:some:apps:*:*:*:*:*:*:*:*", cpe.GeneratedSource),
|
||||
@ -70,7 +70,7 @@ func Test_ClassifierCPEs(t *testing.T) {
|
||||
classifier: Classifier{
|
||||
Package: "some-app",
|
||||
FileGlob: "**/version-parts.txt",
|
||||
EvidenceMatcher: FileContentsVersionMatcher(`(?m)\x00(?P<major>[0-9.]+)\x00(?P<minor>[0-9.]+)\x00(?P<patch>[0-9.]+)\x00`, "cataloger-name"),
|
||||
EvidenceMatcher: FileContentsVersionMatcher("cataloger-name", `(?m)\x00(?P<major>[0-9.]+)\x00(?P<minor>[0-9.]+)\x00(?P<patch>[0-9.]+)\x00`),
|
||||
CPEs: []cpe.CPE{},
|
||||
},
|
||||
cpes: nil,
|
||||
@ -113,7 +113,7 @@ func TestClassifier_MarshalJSON(t *testing.T) {
|
||||
classifier: Classifier{
|
||||
Class: "class",
|
||||
FileGlob: "glob",
|
||||
EvidenceMatcher: FileContentsVersionMatcher(".thing", "cataloger-name"),
|
||||
EvidenceMatcher: FileContentsVersionMatcher("cataloger-name", ".thing"),
|
||||
Package: "pkg",
|
||||
PURL: packageurl.PackageURL{
|
||||
Type: "type",
|
||||
@ -168,7 +168,7 @@ func TestFileContentsVersionMatcher(t *testing.T) {
|
||||
mockGetContent := func(context MatcherContext) (unionreader.UnionReader, error) {
|
||||
return unionreader.GetUnionReader(io.NopCloser(bytes.NewBufferString(tt.data)))
|
||||
}
|
||||
fn := FileContentsVersionMatcher(tt.pattern, "cataloger-name")
|
||||
fn := FileContentsVersionMatcher("cataloger-name", tt.pattern)
|
||||
p, err := fn(Classifier{}, MatcherContext{
|
||||
GetReader: mockGetContent,
|
||||
})
|
||||
|
||||
@ -11,7 +11,7 @@ import (
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/anchore/syft/syft/artifact"
|
||||
"github.com/anchore/syft/syft/cpe"
|
||||
@ -36,16 +36,16 @@ func TestParseNativeImage(t *testing.T) {
|
||||
for _, test := range tests {
|
||||
t.Run(test.fixture, func(t *testing.T) {
|
||||
f, err := os.Open("test-fixtures/java-builds/packages/" + test.fixture)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
readerCloser := io.NopCloser(f)
|
||||
reader, err := unionreader.GetUnionReader(readerCloser)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
parsed := false
|
||||
readers, err := unionreader.GetReaders(reader)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
for _, r := range readers {
|
||||
ni, err := test.newFn(test.fixture, r)
|
||||
assert.NoError(t, err)
|
||||
require.NoError(t, err)
|
||||
_, _, err = ni.fetchPkgs()
|
||||
if err == nil {
|
||||
t.Fatalf("should have failed to extract SBOM.")
|
||||
@ -228,11 +228,11 @@ func buildPackageMap(packages []pkg.Package) map[string]pkg.Package {
|
||||
}
|
||||
|
||||
func verifyPackageFields(t *testing.T, expected, actual pkg.Package) {
|
||||
assert.Equal(t, expected.Name, actual.Name)
|
||||
assert.Equal(t, expected.Version, actual.Version)
|
||||
assert.Equal(t, expected.FoundBy, actual.FoundBy)
|
||||
assert.Equal(t, expected.PURL, actual.PURL)
|
||||
assert.ElementsMatch(t, expected.CPEs, actual.CPEs)
|
||||
require.Equal(t, expected.Name, actual.Name)
|
||||
require.Equal(t, expected.Version, actual.Version)
|
||||
require.Equal(t, expected.FoundBy, actual.FoundBy)
|
||||
require.Equal(t, expected.PURL, actual.PURL)
|
||||
require.ElementsMatch(t, expected.CPEs, actual.CPEs)
|
||||
}
|
||||
|
||||
func verifyRelationships(t *testing.T, expected, actual []artifact.Relationship) {
|
||||
@ -260,8 +260,8 @@ func buildRelationshipMap(relationships []artifact.Relationship) map[string][]ar
|
||||
}
|
||||
|
||||
func verifyRelationshipFields(t *testing.T, expected, actual []artifact.Relationship) {
|
||||
assert.Equal(t, len(expected), len(actual))
|
||||
require.Equal(t, len(expected), len(actual))
|
||||
for i := range expected {
|
||||
assert.Equal(t, expected[i].Type, actual[i].Type)
|
||||
require.Equal(t, expected[i].Type, actual[i].Type)
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,7 +91,7 @@ func parseJVMRelease(_ context.Context, resolver file.Resolver, _ *generic.Envir
|
||||
// the reason we use the reference to get the real path is in cases where the file cataloger is involved
|
||||
// (thus the real path is not available except for in the original file reference). This is important
|
||||
// since the path is critical for distinguishing between different JVM vendors.
|
||||
vendor, product := jvmPrimaryVendorProduct(ri.Implementor, string(reader.Reference().RealPath), ri.ImageType, hasJdk)
|
||||
vendor, product := jvmPrimaryVendorProduct(ri, string(reader.Reference().RealPath), hasJdk)
|
||||
|
||||
p := pkg.Package{
|
||||
Name: product,
|
||||
@ -163,11 +163,11 @@ func jvmPurl(ri pkg.JavaVMRelease, version, vendor, product string) string {
|
||||
return pURL.ToString()
|
||||
}
|
||||
|
||||
func jvmPrimaryVendorProduct(implementor, path, imageType string, hasJdk bool) (string, string) {
|
||||
implementor = strings.ReplaceAll(strings.ToLower(implementor), " ", "")
|
||||
func jvmPrimaryVendorProduct(ri *pkg.JavaVMRelease, path string, hasJdk bool) (string, string) {
|
||||
implementor := strings.ReplaceAll(strings.ToLower(ri.Implementor), " ", "")
|
||||
|
||||
pickProduct := func() string {
|
||||
if hasJdk || jvmProjectByType(imageType) == jdk {
|
||||
if hasJdk || jvmProjectByType(ri.ImageType) == jdk {
|
||||
return jdk
|
||||
}
|
||||
return jre
|
||||
@ -180,7 +180,13 @@ func jvmPrimaryVendorProduct(implementor, path, imageType string, hasJdk bool) (
|
||||
case strings.Contains(implementor, "sun"):
|
||||
return "sun", pickProduct()
|
||||
|
||||
case strings.Contains(implementor, "oracle") || strings.Contains(path, "oracle"):
|
||||
case strings.Contains(implementor, "ibm") || strings.Contains(path, "/ibm"):
|
||||
if hasJdk {
|
||||
return "ibm", "java_sdk"
|
||||
}
|
||||
return "ibm", "java"
|
||||
|
||||
case strings.Contains(implementor, "oracle") || strings.Contains(path, "oracle") || strings.Contains(ri.BuildType, "commercial"):
|
||||
return oracleVendor, pickProduct()
|
||||
}
|
||||
return oracleVendor, openJdkProduct
|
||||
|
||||
@ -307,6 +307,7 @@ func TestJvmPrimaryVendorProduct(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
implementor string
|
||||
buildType string
|
||||
path string
|
||||
imageType string
|
||||
hasJdk bool
|
||||
@ -340,6 +341,25 @@ func TestJvmPrimaryVendorProduct(t *testing.T) {
|
||||
expectedVendor: "oracle",
|
||||
expectedProduct: "jre",
|
||||
},
|
||||
{
|
||||
name: "Oracle commercial build type with JRE",
|
||||
implementor: "",
|
||||
buildType: "commercial",
|
||||
path: "/usr/lib/jvm/jdk8/release",
|
||||
imageType: "JRE",
|
||||
hasJdk: false,
|
||||
expectedVendor: "oracle",
|
||||
expectedProduct: "jre",
|
||||
},
|
||||
{
|
||||
name: "Oracle commercial build type with JDK",
|
||||
implementor: "",
|
||||
buildType: "commercial",
|
||||
path: "/usr/lib/jvm/jdk8/release",
|
||||
hasJdk: true,
|
||||
expectedVendor: "oracle",
|
||||
expectedProduct: "jdk",
|
||||
},
|
||||
{
|
||||
name: "Oracle vendor with JDK in path",
|
||||
implementor: "",
|
||||
@ -367,11 +387,29 @@ func TestJvmPrimaryVendorProduct(t *testing.T) {
|
||||
expectedVendor: "oracle", // corretto upstream is oracle openjdk
|
||||
expectedProduct: "openjdk",
|
||||
},
|
||||
{
|
||||
name: "IBM JRE",
|
||||
path: "/opt/ibm/java/release",
|
||||
expectedVendor: "ibm",
|
||||
expectedProduct: "java",
|
||||
},
|
||||
{
|
||||
name: "IBM JDK",
|
||||
path: "/opt/ibm/java/release",
|
||||
hasJdk: true,
|
||||
expectedVendor: "ibm",
|
||||
expectedProduct: "java_sdk",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
vendor, product := jvmPrimaryVendorProduct(tt.implementor, tt.path, tt.imageType, tt.hasJdk)
|
||||
ri := pkg.JavaVMRelease{
|
||||
Implementor: tt.implementor,
|
||||
BuildType: tt.buildType,
|
||||
ImageType: tt.imageType,
|
||||
}
|
||||
vendor, product := jvmPrimaryVendorProduct(&ri, tt.path, tt.hasJdk)
|
||||
assert.Equal(t, tt.expectedVendor, vendor)
|
||||
assert.Equal(t, tt.expectedProduct, product)
|
||||
})
|
||||
|
||||
@ -219,7 +219,7 @@ func (p interpreterCataloger) getClassifier(realPath string) (string, *binutils.
|
||||
|
||||
return name, &binutils.Classifier{
|
||||
Class: fmt.Sprintf("php-ext-%s-binary", name),
|
||||
EvidenceMatcher: binutils.FileContentsVersionMatcher(match, p.name),
|
||||
EvidenceMatcher: binutils.FileContentsVersionMatcher(p.name, match),
|
||||
Package: name,
|
||||
PURL: packageurl.PackageURL{
|
||||
Type: packageurl.TypeGeneric,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user