Skip to content

Commit

Permalink
add jvm version comparison
Browse files Browse the repository at this point in the history
Signed-off-by: Alex Goodman <[email protected]>
  • Loading branch information
wagoodman committed Sep 16, 2024
1 parent 9fb2194 commit a07fe7d
Show file tree
Hide file tree
Showing 21 changed files with 779 additions and 46 deletions.
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ bin/
/.task

# changelog generation
CHANGELOG.md
VERSION
/CHANGELOG.md
/VERSION

# IDE configuration
.vscode/
Expand Down
4 changes: 4 additions & 0 deletions cmd/grype/cli/commands/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"github.com/anchore/grype/grype/matcher/golang"
"github.com/anchore/grype/grype/matcher/java"
"github.com/anchore/grype/grype/matcher/javascript"
"github.com/anchore/grype/grype/matcher/jvm"
"github.com/anchore/grype/grype/matcher/python"
"github.com/anchore/grype/grype/matcher/ruby"
"github.com/anchore/grype/grype/matcher/stock"
Expand Down Expand Up @@ -285,6 +286,9 @@ func getMatchers(opts *options.Grype) []matcher.Matcher {
ExternalSearchConfig: opts.ExternalSources.ToJavaMatcherConfig(),
UseCPEs: opts.Match.Java.UseCPEs,
},
JVM: jvm.MatcherConfig{
UseCPEs: opts.Match.JVM.UseCPEs,
},
Ruby: ruby.MatcherConfig(opts.Match.Ruby),
Python: python.MatcherConfig(opts.Match.Python),
Dotnet: dotnet.MatcherConfig(opts.Match.Dotnet),
Expand Down
2 changes: 2 additions & 0 deletions cmd/grype/cli/options/match.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import "github.com/anchore/clio"
// matchConfig contains all matching-related configuration options available to the user via the application config.
type matchConfig struct {
Java matcherConfig `yaml:"java" json:"java" mapstructure:"java"` // settings for the java matcher
JVM matcherConfig `yaml:"jvm" json:"jvm" mapstructure:"jvm"` // settings for the jvm matcher
Dotnet matcherConfig `yaml:"dotnet" json:"dotnet" mapstructure:"dotnet"` // settings for the dotnet matcher
Golang golangConfig `yaml:"golang" json:"golang" mapstructure:"golang"` // settings for the golang matcher
Javascript matcherConfig `yaml:"javascript" json:"javascript" mapstructure:"javascript"` // settings for the javascript matcher
Expand Down Expand Up @@ -43,6 +44,7 @@ func defaultMatchConfig() matchConfig {
dontUseCpe := matcherConfig{UseCPEs: false}
return matchConfig{
Java: dontUseCpe,
JVM: useCpe,
Dotnet: dontUseCpe,
Golang: defaultGolangConfig(),
Javascript: dontUseCpe,
Expand Down
11 changes: 6 additions & 5 deletions grype/internal/packagemetadata/names.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@ import (
// not the same it may be important to select different names. This design decision has been deferred, for now
// the same metadata types that have been used in the past should be used here.
var jsonNameFromType = map[reflect.Type][]string{
reflect.TypeOf(pkg.ApkMetadata{}): nameList("ApkMetadata"),
reflect.TypeOf(pkg.GolangBinMetadata{}): nameList("GolangBinMetadata"),
reflect.TypeOf(pkg.GolangModMetadata{}): nameList("GolangModMetadata"),
reflect.TypeOf(pkg.JavaMetadata{}): nameList("JavaMetadata"),
reflect.TypeOf(pkg.RpmMetadata{}): nameList("RpmMetadata"),
reflect.TypeOf(pkg.ApkMetadata{}): nameList("ApkMetadata"),
reflect.TypeOf(pkg.GolangBinMetadata{}): nameList("GolangBinMetadata"),
reflect.TypeOf(pkg.GolangModMetadata{}): nameList("GolangModMetadata"),
reflect.TypeOf(pkg.JavaMetadata{}): nameList("JavaMetadata"),
reflect.TypeOf(pkg.RpmMetadata{}): nameList("RpmMetadata"),
reflect.TypeOf(pkg.JavaVMInstallationMetadata{}): nameList("JavaVMInstallationMetadata"),
}

//nolint:unparam
Expand Down
2 changes: 2 additions & 0 deletions grype/match/matcher_type.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ const (
GoModuleMatcher MatcherType = "go-module-matcher"
OpenVexMatcher MatcherType = "openvex-matcher"
RustMatcher MatcherType = "rust-matcher"
JVMMatcher MatcherType = "jvm-matcher"
)

var AllMatcherTypes = []MatcherType{
Expand All @@ -32,6 +33,7 @@ var AllMatcherTypes = []MatcherType{
GoModuleMatcher,
OpenVexMatcher,
RustMatcher,
JVMMatcher,
}

type MatcherType string
51 changes: 51 additions & 0 deletions grype/matcher/jvm/matcher.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package jvm

import (
"fmt"

"github.com/anchore/grype/grype/distro"
"github.com/anchore/grype/grype/match"
"github.com/anchore/grype/grype/pkg"
"github.com/anchore/grype/grype/search"
"github.com/anchore/grype/grype/vulnerability"
syftPkg "github.com/anchore/syft/syft/pkg"
)

type MatcherConfig struct {
UseCPEs bool
}

type Matcher struct {
cfg MatcherConfig
}

func NewJVMMatcher(cfg MatcherConfig) *Matcher {
return &Matcher{
cfg: cfg,
}
}

func (m *Matcher) PackageTypes() []syftPkg.Type {
return []syftPkg.Type{syftPkg.BinaryPkg}
}

func (m *Matcher) Type() match.MatcherType {
return match.JVMMatcher
}

func (m *Matcher) Match(store vulnerability.Provider, d *distro.Distro, p pkg.Package) ([]match.Match, error) {
if !pkg.IsJvmPackage(p) {
return nil, nil
}

criteria := search.CommonCriteria
if m.cfg.UseCPEs {
criteria = append(criteria, search.ByCPE)
}
matches, err := search.ByCriteria(store, d, p, m.Type(), criteria...)
if err != nil {
return nil, fmt.Errorf("failed to match by exact package: %w", err)
}

return matches, nil
}
3 changes: 3 additions & 0 deletions grype/matcher/matchers.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/anchore/grype/grype/matcher/golang"
"github.com/anchore/grype/grype/matcher/java"
"github.com/anchore/grype/grype/matcher/javascript"
"github.com/anchore/grype/grype/matcher/jvm"
"github.com/anchore/grype/grype/matcher/msrc"
"github.com/anchore/grype/grype/matcher/portage"
"github.com/anchore/grype/grype/matcher/python"
Expand All @@ -19,6 +20,7 @@ import (
// Config contains values used by individual matcher structs for advanced configuration
type Config struct {
Java java.MatcherConfig
JVM jvm.MatcherConfig
Ruby ruby.MatcherConfig
Python python.MatcherConfig
Dotnet dotnet.MatcherConfig
Expand All @@ -36,6 +38,7 @@ func NewDefaultMatchers(mc Config) []Matcher {
dotnet.NewDotnetMatcher(mc.Dotnet),
&rpm.Matcher{},
java.NewJavaMatcher(mc.Java),
jvm.NewJVMMatcher(mc.JVM),
javascript.NewJavascriptMatcher(mc.Javascript),
&apk.Matcher{},
golang.NewGolangMatcher(mc.Golang),
Expand Down
8 changes: 8 additions & 0 deletions grype/matcher/stock/matcher.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,17 @@ func (m *Matcher) Type() match.MatcherType {
}

func (m *Matcher) Match(store vulnerability.Provider, d *distro.Distro, p pkg.Package) ([]match.Match, error) {
if !inboundsForMatcher(p) {
return nil, nil
}

criteria := search.CommonCriteria
if m.cfg.UseCPEs {
criteria = append(criteria, search.ByCPE)
}
return search.ByCriteria(store, d, p, m.Type(), criteria...)
}

func inboundsForMatcher(p pkg.Package) bool {
return !pkg.IsJvmPackage(p)
}
31 changes: 31 additions & 0 deletions grype/pkg/java_metadata.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
package pkg

import (
"strings"

"github.com/anchore/syft/syft/pkg"
)

type JavaMetadata struct {
VirtualPath string `json:"virtualPath"`
PomArtifactID string `json:"pomArtifactID"`
Expand All @@ -12,3 +18,28 @@ type Digest struct {
Algorithm string `json:"algorithm"`
Value string `json:"value"`
}

type JavaVMInstallationMetadata struct {
Release JavaVMReleaseMetadata `json:"release,omitempty"`
}

type JavaVMReleaseMetadata struct {
JavaRuntimeVersion string `json:"javaRuntimeVersion,omitempty"`
JavaVersion string `json:"javaVersion,omitempty"`
FullVersion string `json:"fullVersion,omitempty"`
SemanticVersion string `json:"semanticVersion,omitempty"`
}

func IsJvmPackage(p Package) bool {
if p.Type == pkg.BinaryPkg {
if strings.Contains(p.Name, "jdk") || strings.Contains(p.Name, "jre") || strings.Contains(p.Name, "java") {
return true
}
}

if _, ok := p.Metadata.(JavaVMInstallationMetadata); ok {
return true
}

return false
}
17 changes: 17 additions & 0 deletions grype/pkg/package.go
Original file line number Diff line number Diff line change
Expand Up @@ -214,10 +214,27 @@ func dataFromPkg(p pkg.Package) (interface{}, []UpstreamPackage) {
case pkg.ApkDBEntry:
metadata = apkMetadataFromPkg(p)
upstreams = apkDataFromPkg(p)
case pkg.JavaVMInstallation:

Check failure on line 217 in grype/pkg/package.go

View workflow job for this annotation

GitHub Actions / Static analysis

undefined: pkg.JavaVMInstallation

Check failure on line 217 in grype/pkg/package.go

View workflow job for this annotation

GitHub Actions / Static analysis

undefined: pkg.JavaVMInstallation

Check failure on line 217 in grype/pkg/package.go

View workflow job for this annotation

GitHub Actions / Static analysis

undefined: pkg.JavaVMInstallation

Check failure on line 217 in grype/pkg/package.go

View workflow job for this annotation

GitHub Actions / Static analysis

undefined: pkg.JavaVMInstallation

Check failure on line 217 in grype/pkg/package.go

View workflow job for this annotation

GitHub Actions / Quality tests

undefined: pkg.JavaVMInstallation

Check failure on line 217 in grype/pkg/package.go

View workflow job for this annotation

GitHub Actions / Integration tests

undefined: pkg.JavaVMInstallation

Check failure on line 217 in grype/pkg/package.go

View workflow job for this annotation

GitHub Actions / Unit tests

undefined: pkg.JavaVMInstallation
metadata = javaVMDataFromPkg(p)
}
return metadata, upstreams
}

func javaVMDataFromPkg(p pkg.Package) any {
if value, ok := p.Metadata.(pkg.JavaVMInstallation); ok {

Check failure on line 224 in grype/pkg/package.go

View workflow job for this annotation

GitHub Actions / Static analysis

undefined: pkg.JavaVMInstallation (typecheck)

Check failure on line 224 in grype/pkg/package.go

View workflow job for this annotation

GitHub Actions / Static analysis

undefined: pkg.JavaVMInstallation) (typecheck)

Check failure on line 224 in grype/pkg/package.go

View workflow job for this annotation

GitHub Actions / Static analysis

undefined: pkg.JavaVMInstallation) (typecheck)

Check failure on line 224 in grype/pkg/package.go

View workflow job for this annotation

GitHub Actions / Quality tests

undefined: pkg.JavaVMInstallation

Check failure on line 224 in grype/pkg/package.go

View workflow job for this annotation

GitHub Actions / Integration tests

undefined: pkg.JavaVMInstallation

Check failure on line 224 in grype/pkg/package.go

View workflow job for this annotation

GitHub Actions / Unit tests

undefined: pkg.JavaVMInstallation
return JavaVMInstallationMetadata{
Release: JavaVMReleaseMetadata{
JavaRuntimeVersion: value.Release.JavaRuntimeVersion,
JavaVersion: value.Release.JavaVersion,
FullVersion: value.Release.FullVersion,
SemanticVersion: value.Release.SemanticVersion,
},
}
}

return nil
}

func apkMetadataFromPkg(p pkg.Package) interface{} {
if m, ok := p.Metadata.(pkg.ApkDBEntry); ok {
metadata := ApkMetadata{}
Expand Down
17 changes: 16 additions & 1 deletion grype/search/cpe.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,14 @@ func ByPackageCPE(store vulnerability.ProviderByCPE, d *distro.Distro, p pkg.Pac
if searchVersion == wfn.NA || searchVersion == wfn.Any {
searchVersion = p.Version
}
verObj, err := version.NewVersion(searchVersion, version.FormatFromPkgType(p.Type))

format := version.FormatFromPkg(p)

if format == version.JVMFormat {
searchVersion = specificLegacyJvmVersion(searchVersion, c.Attributes.Update)
}

verObj, err := version.NewVersion(searchVersion, format)
if err != nil {
return nil, fmt.Errorf("matcher failed to parse version pkg=%q ver=%q: %w", p.Name, p.Version, err)
}
Expand Down Expand Up @@ -140,6 +147,14 @@ func ByPackageCPE(store vulnerability.ProviderByCPE, d *distro.Distro, p pkg.Pac
return toMatches(matchesByFingerprint), nil
}

func specificLegacyJvmVersion(searchVersion, updateCpeField string) string {
// we should take into consideration the CPE update field for JVM packages
if strings.HasPrefix(searchVersion, "1.") && !strings.Contains(searchVersion, "_") && updateCpeField != wfn.NA && updateCpeField != wfn.Any {
searchVersion = fmt.Sprintf("%s_%s", searchVersion, strings.TrimPrefix(updateCpeField, "update"))
}
return searchVersion
}

func addNewMatch(matchesByFingerprint map[match.Fingerprint]match.Match, vuln vulnerability.Vulnerability, p pkg.Package, searchVersion version.Version, upstreamMatcher match.MatcherType, searchedByCPE cpe.CPE) {
candidateMatch := match.Match{

Expand Down
2 changes: 2 additions & 0 deletions grype/version/constraint.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ func GetConstraint(constStr string, format Format) (Constraint, error) {
return newKBConstraint(constStr)
case PortageFormat:
return newPortageConstraint(constStr)
case JVMFormat:
return newJvmConstraint(constStr)
case UnknownFormat:
return newFuzzyConstraint(constStr, "unknown")
}
Expand Down
59 changes: 34 additions & 25 deletions grype/version/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ package version
import (
"strings"

"github.com/anchore/syft/syft/pkg"
"github.com/anchore/grype/grype/pkg"
syftPkg "github.com/anchore/syft/syft/pkg"
)

const (
Expand All @@ -18,6 +19,7 @@ const (
GemFormat
PortageFormat
GolangFormat
JVMFormat
)

type Format int
Expand All @@ -34,6 +36,7 @@ var formatStr = []string{
"Gem",
"Portage",
"Go",
"JVM",
}

var Formats = []Format{
Expand All @@ -46,6 +49,8 @@ var Formats = []Format{
KBFormat,
GemFormat,
PortageFormat,
GolangFormat,
JVMFormat,
}

func ParseFormat(userStr string) Format {
Expand All @@ -70,35 +75,39 @@ func ParseFormat(userStr string) Format {
return GemFormat
case strings.ToLower(PortageFormat.String()), "portage":
return PortageFormat
case strings.ToLower(JVMFormat.String()), "jvm", "jre", "jdk", "openjdk", "jep223":
return JVMFormat
}
return UnknownFormat
}

func FormatFromPkgType(t pkg.Type) Format {
var format Format
switch t {
case pkg.ApkPkg:
format = ApkFormat
case pkg.DebPkg:
format = DebFormat
case pkg.JavaPkg:
format = MavenFormat
case pkg.RpmPkg:
format = RpmFormat
case pkg.GemPkg:
format = GemFormat
case pkg.PythonPkg:
format = PythonFormat
case pkg.KbPkg:
format = KBFormat
case pkg.PortagePkg:
format = PortageFormat
case pkg.GoModulePkg:
format = GolangFormat
default:
format = UnknownFormat
func FormatFromPkg(p pkg.Package) Format {
switch p.Type {
case syftPkg.ApkPkg:
return ApkFormat
case syftPkg.DebPkg:
return DebFormat
case syftPkg.JavaPkg:
return MavenFormat
case syftPkg.RpmPkg:
return RpmFormat
case syftPkg.GemPkg:
return GemFormat
case syftPkg.PythonPkg:
return PythonFormat
case syftPkg.KbPkg:
return KBFormat
case syftPkg.PortagePkg:
return PortageFormat
case syftPkg.GoModulePkg:
return GolangFormat
}

if pkg.IsJvmPackage(p) {
return JVMFormat
}
return format

return UnknownFormat
}

func (f Format) String() string {
Expand Down
Loading

0 comments on commit a07fe7d

Please sign in to comment.