mirror of
https://github.com/anchore/syft.git
synced 2025-11-17 08:23:15 +01:00
Add JVM cataloger (#3217)
* add jvm cataloger Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * simplify version selection Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * CPEs from JVM cataloger should be declared Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * ensure package overlap is enabled for sensitive use cases Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> * more permissive glob Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com> --------- Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
parent
7815d8e4d9
commit
01de99b253
@ -249,6 +249,11 @@ func (cfg *Catalog) PostLoad() error {
|
|||||||
return fmt.Errorf("bad scope value %q", cfg.Scope)
|
return fmt.Errorf("bad scope value %q", cfg.Scope)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// the binary package exclusion code depends on the file overlap relationships being created upstream in processing
|
||||||
|
if !cfg.Relationships.PackageFileOwnershipOverlap && cfg.Package.ExcludeBinaryOverlapByOwnership {
|
||||||
|
return fmt.Errorf("cannot enable exclude-binary-overlap-by-ownership without enabling package-file-ownership-overlap")
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -57,6 +57,14 @@ func TestCatalog_PostLoad(t *testing.T) {
|
|||||||
assert.Empty(t, options.Catalogers)
|
assert.Empty(t, options.Catalogers)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "must have package overlap flag when pruning binaries by overlap",
|
||||||
|
options: Catalog{
|
||||||
|
Package: packageConfig{ExcludeBinaryOverlapByOwnership: true},
|
||||||
|
Relationships: relationshipsConfig{PackageFileOwnershipOverlap: false},
|
||||||
|
},
|
||||||
|
wantErr: assert.Error,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
|||||||
@ -3,5 +3,5 @@ package internal
|
|||||||
const (
|
const (
|
||||||
// JSONSchemaVersion is the current schema version output by the JSON encoder
|
// JSONSchemaVersion is the current schema version output by the JSON encoder
|
||||||
// This is roughly following the "SchemaVer" guidelines for versioning the JSON schema. Please see schema/json/README.md for details on how to increment.
|
// This is roughly following the "SchemaVer" guidelines for versioning the JSON schema. Please see schema/json/README.md for details on how to increment.
|
||||||
JSONSchemaVersion = "16.0.16"
|
JSONSchemaVersion = "16.0.17"
|
||||||
)
|
)
|
||||||
|
|||||||
@ -25,55 +25,102 @@ var (
|
|||||||
binaryMetadataTypes = []string{
|
binaryMetadataTypes = []string{
|
||||||
reflect.TypeOf(pkg.ELFBinaryPackageNoteJSONPayload{}).Name(),
|
reflect.TypeOf(pkg.ELFBinaryPackageNoteJSONPayload{}).Name(),
|
||||||
reflect.TypeOf(pkg.BinarySignature{}).Name(),
|
reflect.TypeOf(pkg.BinarySignature{}).Name(),
|
||||||
|
reflect.TypeOf(pkg.JavaVMInstallation{}).Name(),
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func ExcludeBinariesByFileOwnershipOverlap(accessor sbomsync.Accessor) {
|
func ExcludeBinariesByFileOwnershipOverlap(accessor sbomsync.Accessor) {
|
||||||
accessor.WriteToSBOM(func(s *sbom.SBOM) {
|
accessor.WriteToSBOM(func(s *sbom.SBOM) {
|
||||||
for _, r := range s.Relationships {
|
for _, r := range s.Relationships {
|
||||||
if excludeBinaryByFileOwnershipOverlap(r, s.Artifacts.Packages) {
|
if idToRemove := excludeByFileOwnershipOverlap(r, s.Artifacts.Packages); idToRemove != "" {
|
||||||
s.Artifacts.Packages.Delete(r.To.ID())
|
s.Artifacts.Packages.Delete(idToRemove)
|
||||||
s.Relationships = RemoveRelationshipsByID(s.Relationships, r.To.ID())
|
s.Relationships = RemoveRelationshipsByID(s.Relationships, idToRemove)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// excludeBinaryByFileOwnershipOverlap will remove packages from a collection given the following properties are true
|
// excludeByFileOwnershipOverlap will remove packages that should be overridden by a more authoritative package,
|
||||||
// 1) the relationship between packages is OwnershipByFileOverlap
|
// such as an OS package or a package from a cataloger with more specific information being raised up.
|
||||||
// 2) the parent is an "os" package
|
func excludeByFileOwnershipOverlap(r artifact.Relationship, c *pkg.Collection) artifact.ID {
|
||||||
// 3) the child is a synthetic package generated by the binary cataloger
|
|
||||||
// 4) the package names are identical
|
|
||||||
// This was implemented as a way to help resolve: https://github.com/anchore/syft/issues/931
|
|
||||||
func excludeBinaryByFileOwnershipOverlap(r artifact.Relationship, c *pkg.Collection) bool {
|
|
||||||
if artifact.OwnershipByFileOverlapRelationship != r.Type {
|
if artifact.OwnershipByFileOverlapRelationship != r.Type {
|
||||||
return false
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
parent := c.Package(r.From.ID())
|
parent := c.Package(r.From.ID())
|
||||||
if parent == nil {
|
if parent == nil {
|
||||||
return false
|
return ""
|
||||||
}
|
|
||||||
|
|
||||||
parentInExclusion := slices.Contains(osCatalogerTypes, parent.Type)
|
|
||||||
if !parentInExclusion {
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
child := c.Package(r.To.ID())
|
child := c.Package(r.To.ID())
|
||||||
if child == nil {
|
if child == nil {
|
||||||
return false
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
if slices.Contains(binaryCatalogerTypes, child.Type) {
|
if idToRemove := identifyOverlappingOSRelationship(parent, child); idToRemove != "" {
|
||||||
return true
|
return idToRemove
|
||||||
|
}
|
||||||
|
|
||||||
|
if idToRemove := identifyOverlappingJVMRelationship(parent, child); idToRemove != "" {
|
||||||
|
return idToRemove
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// identifyOverlappingJVMRelationship indicates the package to remove if this is a binary -> binary pkg relationship
|
||||||
|
// with a java binary signature package and a more authoritative JVM release package.
|
||||||
|
func identifyOverlappingJVMRelationship(parent *pkg.Package, child *pkg.Package) artifact.ID {
|
||||||
|
if !slices.Contains(binaryCatalogerTypes, parent.Type) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if !slices.Contains(binaryCatalogerTypes, child.Type) {
|
||||||
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
if child.Metadata == nil {
|
if child.Metadata == nil {
|
||||||
return false
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
childMetadataType := reflect.TypeOf(child.Metadata)
|
var (
|
||||||
|
foundJVM bool
|
||||||
|
idToRemove artifact.ID
|
||||||
|
)
|
||||||
|
for _, p := range []*pkg.Package{parent, child} {
|
||||||
|
switch p.Metadata.(type) {
|
||||||
|
case pkg.JavaVMInstallation:
|
||||||
|
foundJVM = true
|
||||||
|
default:
|
||||||
|
idToRemove = p.ID()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return slices.Contains(binaryMetadataTypes, childMetadataType.Name())
|
if foundJVM {
|
||||||
|
return idToRemove
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// identifyOverlappingOSRelationship indicates the package ID to remove if this is an OS pkg -> bin pkg relationship.
|
||||||
|
// This was implemented as a way to help resolve: https://github.com/anchore/syft/issues/931
|
||||||
|
func identifyOverlappingOSRelationship(parent *pkg.Package, child *pkg.Package) artifact.ID {
|
||||||
|
if !slices.Contains(osCatalogerTypes, parent.Type) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if slices.Contains(binaryCatalogerTypes, child.Type) {
|
||||||
|
return child.ID()
|
||||||
|
}
|
||||||
|
|
||||||
|
if child.Metadata == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if !slices.Contains(binaryMetadataTypes, reflect.TypeOf(child.Metadata).Name()) {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return child.ID()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,18 +3,17 @@ package relationship
|
|||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/artifact"
|
"github.com/anchore/syft/syft/artifact"
|
||||||
"github.com/anchore/syft/syft/pkg"
|
"github.com/anchore/syft/syft/pkg"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestExclude(t *testing.T) {
|
func TestExcludeByFileOwnershipOverlap(t *testing.T) {
|
||||||
packageA := pkg.Package{Name: "package-a", Type: pkg.ApkPkg}
|
packageA := pkg.Package{Name: "package-a", Type: pkg.ApkPkg}
|
||||||
packageB := pkg.Package{Name: "package-a", Type: pkg.PythonPkg}
|
packageB := pkg.Package{Name: "package-b", Type: pkg.BinaryPkg, Metadata: pkg.JavaVMInstallation{}}
|
||||||
packageC := pkg.Package{Name: "package-a", Type: pkg.BinaryPkg}
|
packageC := pkg.Package{Name: "package-c", Type: pkg.BinaryPkg, Metadata: pkg.ELFBinaryPackageNoteJSONPayload{Type: "rpm"}}
|
||||||
packageD := pkg.Package{Name: "package-d", Type: pkg.BinaryPkg}
|
for _, p := range []*pkg.Package{&packageA, &packageB, &packageC} {
|
||||||
packageE := pkg.Package{Name: "package-e", Type: pkg.RpmPkg, Metadata: pkg.ELFBinaryPackageNoteJSONPayload{Type: "rpm"}}
|
|
||||||
packageF := pkg.Package{Name: "package-f", Type: pkg.RpmPkg, Metadata: pkg.BinarySignature{}}
|
|
||||||
for _, p := range []*pkg.Package{&packageA, &packageB, &packageC, &packageD, &packageE, &packageF} {
|
|
||||||
p := p
|
p := p
|
||||||
p.SetID()
|
p.SetID()
|
||||||
}
|
}
|
||||||
@ -26,73 +25,152 @@ func TestExclude(t *testing.T) {
|
|||||||
shouldExclude bool
|
shouldExclude bool
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "no exclusions from os -> python",
|
// prove that OS -> bin exclusions are wired
|
||||||
|
name: "exclusions from os -> elf binary (as RPM)",
|
||||||
relationship: artifact.Relationship{
|
relationship: artifact.Relationship{
|
||||||
Type: artifact.OwnershipByFileOverlapRelationship,
|
Type: artifact.OwnershipByFileOverlapRelationship,
|
||||||
From: packageA,
|
From: packageA, // OS
|
||||||
To: packageB,
|
To: packageC, // ELF binary
|
||||||
},
|
|
||||||
packages: pkg.NewCollection(packageA, packageB),
|
|
||||||
shouldExclude: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "exclusions from os -> binary",
|
|
||||||
relationship: artifact.Relationship{
|
|
||||||
Type: artifact.OwnershipByFileOverlapRelationship,
|
|
||||||
From: packageA,
|
|
||||||
To: packageC,
|
|
||||||
},
|
},
|
||||||
packages: pkg.NewCollection(packageA, packageC),
|
packages: pkg.NewCollection(packageA, packageC),
|
||||||
shouldExclude: true,
|
shouldExclude: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "exclusions from os -> elf binary (as RPM)",
|
// prove that bin -> JVM exclusions are wired
|
||||||
|
name: "exclusions from binary -> binary with JVM metadata",
|
||||||
relationship: artifact.Relationship{
|
relationship: artifact.Relationship{
|
||||||
Type: artifact.OwnershipByFileOverlapRelationship,
|
Type: artifact.OwnershipByFileOverlapRelationship,
|
||||||
From: packageA,
|
From: packageB, // binary with JVM metadata
|
||||||
To: packageE,
|
To: packageC, // binary
|
||||||
},
|
},
|
||||||
packages: pkg.NewCollection(packageA, packageE),
|
packages: pkg.NewCollection(packageC, packageB),
|
||||||
shouldExclude: true,
|
shouldExclude: true,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "exclusions from os -> binary (masquerading as RPM)",
|
|
||||||
relationship: artifact.Relationship{
|
|
||||||
Type: artifact.OwnershipByFileOverlapRelationship,
|
|
||||||
From: packageA,
|
|
||||||
To: packageF,
|
|
||||||
},
|
|
||||||
packages: pkg.NewCollection(packageA, packageF),
|
|
||||||
shouldExclude: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "no exclusions from python -> binary",
|
|
||||||
relationship: artifact.Relationship{
|
|
||||||
Type: artifact.OwnershipByFileOverlapRelationship,
|
|
||||||
From: packageB,
|
|
||||||
To: packageC,
|
|
||||||
},
|
|
||||||
packages: pkg.NewCollection(packageB, packageC),
|
|
||||||
shouldExclude: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "no exclusions for different package names",
|
|
||||||
relationship: artifact.Relationship{
|
|
||||||
Type: artifact.OwnershipByFileOverlapRelationship,
|
|
||||||
From: packageA,
|
|
||||||
To: packageD,
|
|
||||||
},
|
|
||||||
packages: pkg.NewCollection(packageA, packageD),
|
|
||||||
shouldExclude: false,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, test := range tests {
|
for _, test := range tests {
|
||||||
t.Run(test.name, func(t *testing.T) {
|
t.Run(test.name, func(t *testing.T) {
|
||||||
if !excludeBinaryByFileOwnershipOverlap(test.relationship, test.packages) && test.shouldExclude {
|
actualExclude := excludeByFileOwnershipOverlap(test.relationship, test.packages)
|
||||||
|
didExclude := actualExclude != ""
|
||||||
|
if !didExclude && test.shouldExclude {
|
||||||
t.Errorf("expected to exclude relationship %+v", test.relationship)
|
t.Errorf("expected to exclude relationship %+v", test.relationship)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestIdentifyOverlappingOSRelationship(t *testing.T) {
|
||||||
|
packageA := pkg.Package{Name: "package-a", Type: pkg.ApkPkg} // OS package
|
||||||
|
packageB := pkg.Package{Name: "package-b", Type: pkg.BinaryPkg}
|
||||||
|
packageC := pkg.Package{Name: "package-c", Type: pkg.BinaryPkg, Metadata: pkg.BinarySignature{}}
|
||||||
|
packageD := pkg.Package{Name: "package-d", Type: pkg.PythonPkg} // Language package
|
||||||
|
packageE := pkg.Package{Name: "package-e", Type: pkg.BinaryPkg, Metadata: pkg.ELFBinaryPackageNoteJSONPayload{}}
|
||||||
|
|
||||||
|
for _, p := range []*pkg.Package{&packageA, &packageB, &packageC, &packageD, &packageE} {
|
||||||
|
p.SetID()
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
parent *pkg.Package
|
||||||
|
child *pkg.Package
|
||||||
|
expectedID artifact.ID
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "OS -> binary without metadata",
|
||||||
|
parent: &packageA,
|
||||||
|
child: &packageB,
|
||||||
|
expectedID: packageB.ID(), // OS package to binary package, should return child ID
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OS -> binary with binary metadata",
|
||||||
|
parent: &packageA,
|
||||||
|
child: &packageC,
|
||||||
|
expectedID: packageC.ID(), // OS package to binary package with binary metadata, should return child ID
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OS -> non-binary package",
|
||||||
|
parent: &packageA,
|
||||||
|
child: &packageD,
|
||||||
|
expectedID: "", // OS package to non-binary package, no exclusion
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OS -> binary with ELF metadata",
|
||||||
|
parent: &packageA,
|
||||||
|
child: &packageE,
|
||||||
|
expectedID: packageE.ID(), // OS package to binary package with ELF metadata, should return child ID
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "non-OS parent",
|
||||||
|
parent: &packageD, // non-OS package
|
||||||
|
child: &packageC,
|
||||||
|
expectedID: "", // non-OS parent, no exclusion
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
resultID := identifyOverlappingOSRelationship(tt.parent, tt.child)
|
||||||
|
assert.Equal(t, tt.expectedID, resultID)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestIdentifyOverlappingJVMRelationship(t *testing.T) {
|
||||||
|
|
||||||
|
packageA := pkg.Package{Name: "package-a", Type: pkg.BinaryPkg}
|
||||||
|
packageB := pkg.Package{Name: "package-b", Type: pkg.BinaryPkg, Metadata: pkg.BinarySignature{}}
|
||||||
|
packageC := pkg.Package{Name: "package-c", Type: pkg.BinaryPkg, Metadata: pkg.JavaVMInstallation{}}
|
||||||
|
packageD := pkg.Package{Name: "package-d", Type: pkg.PythonPkg}
|
||||||
|
packageE := pkg.Package{Name: "package-e", Type: pkg.BinaryPkg}
|
||||||
|
|
||||||
|
for _, p := range []*pkg.Package{&packageA, &packageB, &packageC, &packageD, &packageE} {
|
||||||
|
p.SetID()
|
||||||
|
}
|
||||||
|
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
parent *pkg.Package
|
||||||
|
child *pkg.Package
|
||||||
|
expectedID artifact.ID
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "binary -> binary with JVM installation",
|
||||||
|
parent: &packageA,
|
||||||
|
child: &packageC,
|
||||||
|
expectedID: packageA.ID(), // JVM found, return BinaryPkg ID
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "binary -> binary with binary signature",
|
||||||
|
parent: &packageA,
|
||||||
|
child: &packageB,
|
||||||
|
expectedID: "", // binary signatures only found, no exclusion
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "binary -> python (non-binary child)",
|
||||||
|
parent: &packageA,
|
||||||
|
child: &packageD,
|
||||||
|
expectedID: "", // non-binary child, no exclusion
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no JVM or signature in binary -> binary",
|
||||||
|
parent: &packageA,
|
||||||
|
child: &packageE,
|
||||||
|
expectedID: "", // no JVM or binary signature, no exclusion
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "non-binary parent",
|
||||||
|
parent: &packageD,
|
||||||
|
child: &packageC,
|
||||||
|
expectedID: "", // non-binary parent, no exclusion
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
resultID := identifyOverlappingJVMRelationship(tt.parent, tt.child)
|
||||||
|
assert.Equal(t, tt.expectedID, resultID)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -142,6 +142,7 @@ func DefaultPackageTaskFactories() PackageTaskFactories {
|
|||||||
newSimplePackageTaskFactory(binary.NewELFPackageCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, "binary", "elf-package"),
|
newSimplePackageTaskFactory(binary.NewELFPackageCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, "binary", "elf-package"),
|
||||||
newSimplePackageTaskFactory(githubactions.NewActionUsageCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, "github", "github-actions"),
|
newSimplePackageTaskFactory(githubactions.NewActionUsageCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, "github", "github-actions"),
|
||||||
newSimplePackageTaskFactory(githubactions.NewWorkflowUsageCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, "github", "github-actions"),
|
newSimplePackageTaskFactory(githubactions.NewWorkflowUsageCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, "github", "github-actions"),
|
||||||
|
newSimplePackageTaskFactory(java.NewJvmDistributionCataloger, pkgcataloging.DeclaredTag, pkgcataloging.DirectoryTag, pkgcataloging.InstalledTag, pkgcataloging.ImageTag, "java", "jvm", "jdk", "jre"),
|
||||||
newPackageTaskFactory(
|
newPackageTaskFactory(
|
||||||
func(cfg CatalogingFactoryConfig) pkg.Cataloger {
|
func(cfg CatalogingFactoryConfig) pkg.Cataloger {
|
||||||
return kernel.NewLinuxKernelCataloger(cfg.PackagesConfig.LinuxKernel)
|
return kernel.NewLinuxKernelCataloger(cfg.PackagesConfig.LinuxKernel)
|
||||||
|
|||||||
2721
schema/json/schema-16.0.17.json
Normal file
2721
schema/json/schema-16.0.17.json
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||||
"$id": "anchore.io/schema/syft/json/16.0.16/document",
|
"$id": "anchore.io/schema/syft/json/16.0.17/document",
|
||||||
"$ref": "#/$defs/Document",
|
"$ref": "#/$defs/Document",
|
||||||
"$defs": {
|
"$defs": {
|
||||||
"AlpmDbEntry": {
|
"AlpmDbEntry": {
|
||||||
@ -955,6 +955,24 @@
|
|||||||
"virtualPath"
|
"virtualPath"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"JavaJvmInstallation": {
|
||||||
|
"properties": {
|
||||||
|
"release": {
|
||||||
|
"$ref": "#/$defs/JavaVMRelease"
|
||||||
|
},
|
||||||
|
"files": {
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": "array"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"release",
|
||||||
|
"files"
|
||||||
|
]
|
||||||
|
},
|
||||||
"JavaManifest": {
|
"JavaManifest": {
|
||||||
"properties": {
|
"properties": {
|
||||||
"main": {
|
"main": {
|
||||||
@ -1062,6 +1080,77 @@
|
|||||||
"version"
|
"version"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"JavaVMRelease": {
|
||||||
|
"properties": {
|
||||||
|
"implementor": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"implementorVersion": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"javaRuntimeVersion": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"javaVersion": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"javaVersionDate": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"libc": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"modules": {
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"type": "array"
|
||||||
|
},
|
||||||
|
"osArch": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"osName": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"osVersion": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"source": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"buildSource": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"buildSourceRepo": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"sourceRepo": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"fullVersion": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"semanticVersion": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"buildInfo": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"jvmVariant": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"jvmVersion": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"imageType": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"buildType": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
"JavascriptNpmPackage": {
|
"JavascriptNpmPackage": {
|
||||||
"properties": {
|
"properties": {
|
||||||
"name": {
|
"name": {
|
||||||
@ -1583,6 +1672,9 @@
|
|||||||
{
|
{
|
||||||
"$ref": "#/$defs/JavaArchive"
|
"$ref": "#/$defs/JavaArchive"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"$ref": "#/$defs/JavaJvmInstallation"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"$ref": "#/$defs/JavascriptNpmPackage"
|
"$ref": "#/$defs/JavascriptNpmPackage"
|
||||||
},
|
},
|
||||||
|
|||||||
@ -62,6 +62,10 @@ func Originator(p pkg.Package) (typ string, author string) { //nolint: funlen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case pkg.JavaVMInstallation:
|
||||||
|
typ = orgType
|
||||||
|
author = metadata.Release.Implementor
|
||||||
|
|
||||||
case pkg.LinuxKernelModule:
|
case pkg.LinuxKernelModule:
|
||||||
author = metadata.Author
|
author = metadata.Author
|
||||||
|
|
||||||
|
|||||||
@ -178,6 +178,18 @@ func Test_OriginatorSupplier(t *testing.T) {
|
|||||||
},
|
},
|
||||||
// note: empty!
|
// note: empty!
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "from java -- jvm installation",
|
||||||
|
input: pkg.Package{
|
||||||
|
Metadata: pkg.JavaVMInstallation{
|
||||||
|
Release: pkg.JavaVMRelease{
|
||||||
|
Implementor: "Oracle",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
originator: "Organization: Oracle",
|
||||||
|
supplier: "Organization: Oracle",
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "from linux kernel module",
|
name: "from linux kernel module",
|
||||||
input: pkg.Package{
|
input: pkg.Package{
|
||||||
|
|||||||
@ -140,6 +140,40 @@ func (r ChrootContext) ToNativePath(chrootPath string) (string, error) {
|
|||||||
return responsePath, nil
|
return responsePath, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r ChrootContext) ToNativeGlob(chrootPath string) (string, error) {
|
||||||
|
// split on any *
|
||||||
|
parts := strings.Split(chrootPath, "*")
|
||||||
|
if len(parts) == 0 || parts[0] == "" {
|
||||||
|
// either this is an empty string or a path that starts with * so there is nothing we can do
|
||||||
|
return chrootPath, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(parts) == 1 {
|
||||||
|
// this has no glob, treat it like a path
|
||||||
|
return r.ToNativePath(chrootPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
responsePath := parts[0]
|
||||||
|
|
||||||
|
if filepath.IsAbs(responsePath) {
|
||||||
|
// don't allow input to potentially hop above root path
|
||||||
|
responsePath = path.Join(r.root, responsePath)
|
||||||
|
} else {
|
||||||
|
// ensure we take into account any relative difference between the root path and the CWD for relative requests
|
||||||
|
responsePath = path.Join(r.cwdRelativeToRoot, responsePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
responsePath, err = filepath.Abs(responsePath)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
parts[0] = strings.TrimRight(responsePath, "/") + "/"
|
||||||
|
|
||||||
|
return strings.Join(parts, "*"), nil
|
||||||
|
}
|
||||||
|
|
||||||
// ToChrootPath takes a path from the underlying fs domain and converts it to a path that is relative to the current root context.
|
// ToChrootPath takes a path from the underlying fs domain and converts it to a path that is relative to the current root context.
|
||||||
func (r ChrootContext) ToChrootPath(nativePath string) string {
|
func (r ChrootContext) ToChrootPath(nativePath string) string {
|
||||||
responsePath := nativePath
|
responsePath := nativePath
|
||||||
|
|||||||
@ -479,3 +479,98 @@ func Test_ChrootContext_RequestResponse(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestToNativeGlob(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
chrootContext ChrootContext
|
||||||
|
chrootPath string
|
||||||
|
expectedResult string
|
||||||
|
expectedError error
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "ignore empty path",
|
||||||
|
chrootContext: ChrootContext{
|
||||||
|
root: "/root",
|
||||||
|
cwdRelativeToRoot: "/cwd",
|
||||||
|
},
|
||||||
|
chrootPath: "",
|
||||||
|
expectedResult: "",
|
||||||
|
expectedError: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ignore if just a path",
|
||||||
|
chrootContext: ChrootContext{
|
||||||
|
root: "/root",
|
||||||
|
cwdRelativeToRoot: "/cwd",
|
||||||
|
},
|
||||||
|
chrootPath: "/some/path/file.txt",
|
||||||
|
expectedResult: "/root/some/path/file.txt",
|
||||||
|
expectedError: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ignore starting with glob",
|
||||||
|
chrootContext: ChrootContext{
|
||||||
|
root: "/root",
|
||||||
|
cwdRelativeToRoot: "/cwd",
|
||||||
|
},
|
||||||
|
chrootPath: "*/relative/path/*",
|
||||||
|
expectedResult: "*/relative/path/*",
|
||||||
|
expectedError: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "absolute path with glob",
|
||||||
|
chrootContext: ChrootContext{
|
||||||
|
root: "/root",
|
||||||
|
cwdRelativeToRoot: "/cwd",
|
||||||
|
},
|
||||||
|
chrootPath: "/some/path/*",
|
||||||
|
expectedResult: "/root/some/path/*",
|
||||||
|
expectedError: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "relative path with glob",
|
||||||
|
chrootContext: ChrootContext{
|
||||||
|
root: "/root",
|
||||||
|
cwdRelativeToRoot: "/cwd",
|
||||||
|
},
|
||||||
|
chrootPath: "relative/path/*",
|
||||||
|
expectedResult: "/cwd/relative/path/*",
|
||||||
|
expectedError: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "relative path with no root",
|
||||||
|
chrootContext: ChrootContext{
|
||||||
|
root: "",
|
||||||
|
cwdRelativeToRoot: "/cwd",
|
||||||
|
},
|
||||||
|
chrootPath: "relative/path/*",
|
||||||
|
expectedResult: "/cwd/relative/path/*",
|
||||||
|
expectedError: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "globs everywhere",
|
||||||
|
chrootContext: ChrootContext{
|
||||||
|
root: "/root",
|
||||||
|
cwdRelativeToRoot: "/cwd",
|
||||||
|
},
|
||||||
|
chrootPath: "relative/path/**/file*.txt",
|
||||||
|
expectedResult: "/cwd/relative/path/**/file*.txt",
|
||||||
|
expectedError: nil,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
result, err := tt.chrootContext.ToNativeGlob(tt.chrootPath)
|
||||||
|
|
||||||
|
if tt.expectedError != nil {
|
||||||
|
assert.Error(t, err)
|
||||||
|
assert.Equal(t, tt.expectedError, err)
|
||||||
|
} else {
|
||||||
|
assert.NoError(t, err)
|
||||||
|
assert.Equal(t, tt.expectedResult, result)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -145,13 +145,21 @@ func (r Directory) FilesByPath(userPaths ...string) ([]file.Location, error) {
|
|||||||
return references, nil
|
return references, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r Directory) requestGlob(pattern string) (string, error) {
|
||||||
|
return r.chroot.ToNativeGlob(pattern)
|
||||||
|
}
|
||||||
|
|
||||||
// FilesByGlob returns all file.References that match the given path glob pattern from any layer in the image.
|
// FilesByGlob returns all file.References that match the given path glob pattern from any layer in the image.
|
||||||
func (r Directory) FilesByGlob(patterns ...string) ([]file.Location, error) {
|
func (r Directory) FilesByGlob(patterns ...string) ([]file.Location, error) {
|
||||||
uniqueFileIDs := stereoscopeFile.NewFileReferenceSet()
|
uniqueFileIDs := stereoscopeFile.NewFileReferenceSet()
|
||||||
uniqueLocations := make([]file.Location, 0)
|
uniqueLocations := make([]file.Location, 0)
|
||||||
|
|
||||||
for _, pattern := range patterns {
|
for _, pattern := range patterns {
|
||||||
refVias, err := r.searchContext.SearchByGlob(pattern, filetree.FollowBasenameLinks)
|
requestGlob, err := r.requestGlob(pattern)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
refVias, err := r.searchContext.SearchByGlob(requestGlob, filetree.FollowBasenameLinks)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,6 +27,7 @@ func AllTypes() []any {
|
|||||||
pkg.HackageStackYamlEntry{},
|
pkg.HackageStackYamlEntry{},
|
||||||
pkg.HackageStackYamlLockEntry{},
|
pkg.HackageStackYamlLockEntry{},
|
||||||
pkg.JavaArchive{},
|
pkg.JavaArchive{},
|
||||||
|
pkg.JavaVMInstallation{},
|
||||||
pkg.LinuxKernel{},
|
pkg.LinuxKernel{},
|
||||||
pkg.LinuxKernelModule{},
|
pkg.LinuxKernelModule{},
|
||||||
pkg.LuaRocksPackage{},
|
pkg.LuaRocksPackage{},
|
||||||
|
|||||||
@ -81,6 +81,7 @@ var jsonTypes = makeJSONTypes(
|
|||||||
jsonNames(pkg.HackageStackYamlLockEntry{}, "haskell-hackage-stack-lock-entry", "HackageMetadataType"),
|
jsonNames(pkg.HackageStackYamlLockEntry{}, "haskell-hackage-stack-lock-entry", "HackageMetadataType"),
|
||||||
jsonNamesWithoutLookup(pkg.HackageStackYamlEntry{}, "haskell-hackage-stack-entry", "HackageMetadataType"), // the legacy value is split into two types, where the other is preferred
|
jsonNamesWithoutLookup(pkg.HackageStackYamlEntry{}, "haskell-hackage-stack-entry", "HackageMetadataType"), // the legacy value is split into two types, where the other is preferred
|
||||||
jsonNames(pkg.JavaArchive{}, "java-archive", "JavaMetadata"),
|
jsonNames(pkg.JavaArchive{}, "java-archive", "JavaMetadata"),
|
||||||
|
jsonNames(pkg.JavaVMInstallation{}, "java-jvm-installation"),
|
||||||
jsonNames(pkg.MicrosoftKbPatch{}, "microsoft-kb-patch", "KbPatchMetadata"),
|
jsonNames(pkg.MicrosoftKbPatch{}, "microsoft-kb-patch", "KbPatchMetadata"),
|
||||||
jsonNames(pkg.LinuxKernel{}, "linux-kernel-archive", "LinuxKernel"),
|
jsonNames(pkg.LinuxKernel{}, "linux-kernel-archive", "LinuxKernel"),
|
||||||
jsonNames(pkg.LinuxKernelModule{}, "linux-kernel-module", "LinuxKernelModule"),
|
jsonNames(pkg.LinuxKernelModule{}, "linux-kernel-module", "LinuxKernelModule"),
|
||||||
|
|||||||
@ -43,3 +43,9 @@ func NewGradleLockfileCataloger() pkg.Cataloger {
|
|||||||
return generic.NewCataloger("java-gradle-lockfile-cataloger").
|
return generic.NewCataloger("java-gradle-lockfile-cataloger").
|
||||||
WithParserByGlobs(parseGradleLockfile, gradleLockfileGlob)
|
WithParserByGlobs(parseGradleLockfile, gradleLockfileGlob)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewJvmDistributionCataloger returns packages representing JDK/JRE installations (of multiple distribution types).
|
||||||
|
func NewJvmDistributionCataloger() pkg.Cataloger {
|
||||||
|
return generic.NewCataloger("java-jvm-cataloger").
|
||||||
|
WithParserByGlobs(parseJVMRelease, jvmReleaseGlob)
|
||||||
|
}
|
||||||
|
|||||||
@ -4,6 +4,9 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/anchore/syft/syft/cataloging"
|
"github.com/anchore/syft/syft/cataloging"
|
||||||
|
"github.com/anchore/syft/syft/cpe"
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
|
"github.com/anchore/syft/syft/pkg"
|
||||||
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
|
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -102,3 +105,113 @@ func Test_POMCataloger_Globs(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestJvmDistributionCataloger(t *testing.T) {
|
||||||
|
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
fixture string
|
||||||
|
expected pkg.Package
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "valid 1.8.0",
|
||||||
|
fixture: "test-fixtures/jvm-installs/oracle-jdk-se-8",
|
||||||
|
expected: pkg.Package{
|
||||||
|
Name: "jdk",
|
||||||
|
Version: "1.8.0_411-b25",
|
||||||
|
FoundBy: "java-jvm-cataloger",
|
||||||
|
Locations: file.NewLocationSet(file.NewLocation("usr/lib/jvm/jdk-1.8-oracle-x64/release")),
|
||||||
|
Licenses: pkg.NewLicenseSet(),
|
||||||
|
Type: pkg.BinaryPkg,
|
||||||
|
CPEs: []cpe.CPE{
|
||||||
|
cpe.Must("cpe:2.3:a:oracle:java_se:1.8.0:update411:*:*:*:*:*:*", cpe.DeclaredSource),
|
||||||
|
cpe.Must("cpe:2.3:a:oracle:jre:1.8.0:update411:*:*:*:*:*:*", cpe.DeclaredSource),
|
||||||
|
cpe.Must("cpe:2.3:a:oracle:jdk:1.8.0:update411:*:*:*:*:*:*", cpe.DeclaredSource),
|
||||||
|
},
|
||||||
|
PURL: "pkg:generic/oracle/jdk@1.8.0_411-b25",
|
||||||
|
Metadata: pkg.JavaVMInstallation{
|
||||||
|
Release: pkg.JavaVMRelease{
|
||||||
|
JavaRuntimeVersion: "1.8.0_411-b25",
|
||||||
|
JavaVersion: "1.8.0_411",
|
||||||
|
OsArch: "amd64",
|
||||||
|
OsName: "Linux",
|
||||||
|
OsVersion: "2.6",
|
||||||
|
Source: ".:git:71ec2089cf8c+",
|
||||||
|
BuildType: "commercial",
|
||||||
|
},
|
||||||
|
Files: []string{
|
||||||
|
"usr/lib/jvm/jdk-1.8-oracle-x64/bin/javac",
|
||||||
|
"usr/lib/jvm/jdk-1.8-oracle-x64/release",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "valid post-jep223",
|
||||||
|
fixture: "test-fixtures/jvm-installs/valid-post-jep223",
|
||||||
|
expected: pkg.Package{
|
||||||
|
Name: "openjdk",
|
||||||
|
Version: "21.0.4+7-LTS",
|
||||||
|
FoundBy: "java-jvm-cataloger",
|
||||||
|
Locations: file.NewLocationSet(file.NewLocation("jvm/openjdk/release")),
|
||||||
|
Licenses: pkg.NewLicenseSet(),
|
||||||
|
Type: pkg.BinaryPkg,
|
||||||
|
CPEs: []cpe.CPE{cpe.Must("cpe:2.3:a:oracle:openjdk:21.0.4:*:*:*:*:*:*:*", cpe.DeclaredSource)},
|
||||||
|
PURL: "pkg:generic/oracle/openjdk@21.0.4%2B7-LTS?repository_url=https://github.com/adoptium/jdk21u.git",
|
||||||
|
Metadata: pkg.JavaVMInstallation{
|
||||||
|
Release: pkg.JavaVMRelease{
|
||||||
|
Implementor: "Eclipse Adoptium",
|
||||||
|
ImplementorVersion: "Temurin-21.0.4+7",
|
||||||
|
JavaRuntimeVersion: "21.0.4+7-LTS",
|
||||||
|
JavaVersion: "21.0.4",
|
||||||
|
JavaVersionDate: "2024-07-16",
|
||||||
|
Libc: "gnu",
|
||||||
|
Modules: []string{
|
||||||
|
"java.base", "java.compiler", "java.datatransfer", "java.xml", "java.prefs",
|
||||||
|
"java.desktop", "java.instrument", "java.logging", "java.management",
|
||||||
|
"java.security.sasl", "java.naming", "java.rmi", "java.management.rmi",
|
||||||
|
"java.net.http", "java.scripting", "java.security.jgss",
|
||||||
|
"java.transaction.xa", "java.sql", "java.sql.rowset", "java.xml.crypto", "java.se",
|
||||||
|
"java.smartcardio", "jdk.accessibility", "jdk.internal.jvmstat", "jdk.attach",
|
||||||
|
"jdk.charsets", "jdk.internal.opt", "jdk.zipfs", "jdk.compiler", "jdk.crypto.ec",
|
||||||
|
"jdk.crypto.cryptoki", "jdk.dynalink", "jdk.internal.ed", "jdk.editpad", "jdk.hotspot.agent",
|
||||||
|
"jdk.httpserver", "jdk.incubator.vector", "jdk.internal.le", "jdk.internal.vm.ci",
|
||||||
|
"jdk.internal.vm.compiler", "jdk.internal.vm.compiler.management", "jdk.jartool",
|
||||||
|
"jdk.javadoc", "jdk.jcmd", "jdk.management", "jdk.management.agent", "jdk.jconsole",
|
||||||
|
"jdk.jdeps", "jdk.jdwp.agent", "jdk.jdi", "jdk.jfr", "jdk.jlink", "jdk.jpackage", "jdk.jshell",
|
||||||
|
"jdk.jsobject", "jdk.jstatd", "jdk.localedata", "jdk.management.jfr", "jdk.naming.dns",
|
||||||
|
"jdk.naming.rmi", "jdk.net", "jdk.nio.mapmode", "jdk.random", "jdk.sctp", "jdk.security.auth",
|
||||||
|
"jdk.security.jgss", "jdk.unsupported", "jdk.unsupported.desktop", "jdk.xml.dom",
|
||||||
|
},
|
||||||
|
OsArch: "aarch64",
|
||||||
|
OsName: "Linux",
|
||||||
|
Source: ".:git:13710926b798",
|
||||||
|
BuildSource: "git:1271f10a26c47e1489a814dd2731f936a588d621",
|
||||||
|
BuildSourceRepo: "https://github.com/adoptium/temurin-build.git",
|
||||||
|
SourceRepo: "https://github.com/adoptium/jdk21u.git",
|
||||||
|
FullVersion: "21.0.4+7-LTS",
|
||||||
|
SemanticVersion: "21.0.4+7",
|
||||||
|
BuildInfo: "OS: Linux Version: 5.4.0-150-generic",
|
||||||
|
JvmVariant: "Hotspot",
|
||||||
|
JvmVersion: "21.0.4+7-LTS",
|
||||||
|
ImageType: "JDK",
|
||||||
|
},
|
||||||
|
Files: []string{
|
||||||
|
"jvm/openjdk/release",
|
||||||
|
"jvm/openjdk/sibling/child/file1.txt",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range cases {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
p := tt.expected
|
||||||
|
p.SetID()
|
||||||
|
|
||||||
|
pkgtest.TestCataloger(t, tt.fixture, NewJvmDistributionCataloger(), []pkg.Package{p}, nil)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
423
syft/pkg/cataloger/java/parse_jvm_release.go
Normal file
423
syft/pkg/cataloger/java/parse_jvm_release.go
Normal file
@ -0,0 +1,423 @@
|
|||||||
|
package java
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"path"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/mitchellh/mapstructure"
|
||||||
|
|
||||||
|
"github.com/anchore/packageurl-go"
|
||||||
|
stereoFile "github.com/anchore/stereoscope/pkg/file"
|
||||||
|
"github.com/anchore/syft/internal/log"
|
||||||
|
"github.com/anchore/syft/syft/artifact"
|
||||||
|
"github.com/anchore/syft/syft/cpe"
|
||||||
|
"github.com/anchore/syft/syft/file"
|
||||||
|
"github.com/anchore/syft/syft/pkg"
|
||||||
|
"github.com/anchore/syft/syft/pkg/cataloger/generic"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// this is a very permissive glob that will match more than just the JVM release file.
|
||||||
|
// we started with "**/{java,jvm}/*/release", but this prevents scanning JVM archive contents (e.g. jdk8u402.zip).
|
||||||
|
// this approach lets us check more files for JVM release info, but be rather silent about errors.
|
||||||
|
jvmReleaseGlob = "**/release"
|
||||||
|
oracleVendor = "oracle"
|
||||||
|
openJdkProduct = "openjdk"
|
||||||
|
jre = "jre"
|
||||||
|
jdk = "jdk"
|
||||||
|
)
|
||||||
|
|
||||||
|
// the /opt/java/openjdk/release file (and similar paths) is a file that is present in the multiple OpenJDK distributions
|
||||||
|
// here's an example of the contents of the file:
|
||||||
|
//
|
||||||
|
// IMPLEMENTOR="Eclipse Adoptium"
|
||||||
|
// IMPLEMENTOR_VERSION="Temurin-21.0.4+7"
|
||||||
|
// JAVA_RUNTIME_VERSION="21.0.4+7-LTS"
|
||||||
|
// JAVA_VERSION="21.0.4"
|
||||||
|
// JAVA_VERSION_DATE="2024-07-16"
|
||||||
|
// LIBC="gnu"
|
||||||
|
// MODULES="java.base java.compiler java.datatransfer java.xml java.prefs java.desktop java.instrument java.logging java.management java.security.sasl java.naming java.rmi java.management.rmi java.net.http java.scripting java.security.jgss java.transaction.xa java.sql java.sql.rowset java.xml.crypto java.se java.smartcardio jdk.accessibility jdk.internal.jvmstat jdk.attach jdk.charsets jdk.internal.opt jdk.zipfs jdk.compiler jdk.crypto.ec jdk.crypto.cryptoki jdk.dynalink jdk.internal.ed jdk.editpad jdk.hotspot.agent jdk.httpserver jdk.incubator.vector jdk.internal.le jdk.internal.vm.ci jdk.internal.vm.compiler jdk.internal.vm.compiler.management jdk.jartool jdk.javadoc jdk.jcmd jdk.management jdk.management.agent jdk.jconsole jdk.jdeps jdk.jdwp.agent jdk.jdi jdk.jfr jdk.jlink jdk.jpackage jdk.jshell jdk.jsobject jdk.jstatd jdk.localedata jdk.management.jfr jdk.naming.dns jdk.naming.rmi jdk.net jdk.nio.mapmode jdk.random jdk.sctp jdk.security.auth jdk.security.jgss jdk.unsupported jdk.unsupported.desktop jdk.xml.dom"
|
||||||
|
// OS_ARCH="aarch64"
|
||||||
|
// OS_NAME="Linux"
|
||||||
|
// SOURCE=".:git:13710926b798"
|
||||||
|
// BUILD_SOURCE="git:1271f10a26c47e1489a814dd2731f936a588d621"
|
||||||
|
// BUILD_SOURCE_REPO="https://github.com/adoptium/temurin-build.git"
|
||||||
|
// SOURCE_REPO="https://github.com/adoptium/jdk21u.git"
|
||||||
|
// FULL_VERSION="21.0.4+7-LTS"
|
||||||
|
// SEMANTIC_VERSION="21.0.4+7"
|
||||||
|
// BUILD_INFO="OS: Linux Version: 5.4.0-150-generic"
|
||||||
|
// JVM_VARIANT="Hotspot"
|
||||||
|
// JVM_VERSION="21.0.4+7-LTS"
|
||||||
|
// IMAGE_TYPE="JDK"
|
||||||
|
//
|
||||||
|
// In terms of the temurin flavor, these are controlled by:
|
||||||
|
// - config: https://github.com/adoptium/temurin-build/blob/v2023.01.03/sbin/common/config_init.sh
|
||||||
|
// - build script: https://github.com/adoptium/temurin-build/blob/v2023.01.03/sbin/build.sh#L1584-L1796
|
||||||
|
|
||||||
|
type jvmCpeInfo struct {
|
||||||
|
vendor, product, version string
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseJVMRelease(_ context.Context, resolver file.Resolver, _ *generic.Environment, reader file.LocationReadCloser) ([]pkg.Package, []artifact.Relationship, error) {
|
||||||
|
ri, err := parseJvmReleaseInfo(reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("unable to parse JVM release info %q: %w", reader.Path(), err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if ri == nil {
|
||||||
|
// TODO: known-unknown: expected JDK installation package
|
||||||
|
return nil, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
version := jvmPackageVersion(ri)
|
||||||
|
// TODO: detect old and new version format from multiple fields
|
||||||
|
|
||||||
|
licenses := jvmLicenses(resolver, ri)
|
||||||
|
|
||||||
|
locations := file.NewLocationSet(reader.Location)
|
||||||
|
|
||||||
|
for _, lic := range licenses.ToSlice() {
|
||||||
|
locations.Add(lic.Locations.ToSlice()...)
|
||||||
|
}
|
||||||
|
|
||||||
|
installDir := path.Dir(reader.Path())
|
||||||
|
files, hasJdk := findJvmFiles(resolver, installDir)
|
||||||
|
|
||||||
|
vendor, product := jvmPrimaryVendorProduct(ri.Implementor, reader.Path(), ri.ImageType, hasJdk)
|
||||||
|
|
||||||
|
p := pkg.Package{
|
||||||
|
Name: product,
|
||||||
|
Locations: locations,
|
||||||
|
Version: version,
|
||||||
|
CPEs: jvmCpes(version, vendor, product, ri.ImageType, hasJdk),
|
||||||
|
PURL: jvmPurl(*ri, version, vendor, product),
|
||||||
|
Licenses: licenses,
|
||||||
|
Type: pkg.BinaryPkg,
|
||||||
|
Metadata: pkg.JavaVMInstallation{
|
||||||
|
Release: *ri,
|
||||||
|
Files: files,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
p.SetID()
|
||||||
|
|
||||||
|
return []pkg.Package{p}, nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func jvmLicenses(_ file.Resolver, _ *pkg.JavaVMRelease) pkg.LicenseSet {
|
||||||
|
// TODO: get this from the dir(<RELEASE>)/legal/**/LICENSE files when we start cataloging license content
|
||||||
|
// see https://github.com/anchore/syft/issues/656
|
||||||
|
return pkg.NewLicenseSet()
|
||||||
|
}
|
||||||
|
|
||||||
|
func findJvmFiles(resolver file.Resolver, installDir string) ([]string, bool) {
|
||||||
|
ownedLocations, err := resolver.FilesByGlob(installDir + "/**")
|
||||||
|
if err != nil {
|
||||||
|
// TODO: known-unknowns
|
||||||
|
log.WithFields("path", installDir, "error", err).Trace("unable to find installed JVM files")
|
||||||
|
}
|
||||||
|
|
||||||
|
var results []string
|
||||||
|
var hasJdk bool
|
||||||
|
for _, loc := range ownedLocations {
|
||||||
|
p := loc.Path()
|
||||||
|
results = append(results, p)
|
||||||
|
if !hasJdk && strings.HasSuffix(p, "bin/javac") {
|
||||||
|
hasJdk = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Strings(results)
|
||||||
|
|
||||||
|
return results, hasJdk
|
||||||
|
}
|
||||||
|
|
||||||
|
func jvmPurl(ri pkg.JavaVMRelease, version, vendor, product string) string {
|
||||||
|
var qualifiers []packageurl.Qualifier
|
||||||
|
if ri.SourceRepo != "" {
|
||||||
|
qualifiers = append(qualifiers, packageurl.Qualifier{
|
||||||
|
Key: "repository_url",
|
||||||
|
Value: ri.SourceRepo,
|
||||||
|
})
|
||||||
|
} else if ri.BuildSourceRepo != "" {
|
||||||
|
qualifiers = append(qualifiers, packageurl.Qualifier{
|
||||||
|
Key: "repository_url",
|
||||||
|
Value: ri.BuildSourceRepo,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pURL := packageurl.NewPackageURL(
|
||||||
|
packageurl.TypeGeneric,
|
||||||
|
vendor,
|
||||||
|
product,
|
||||||
|
version,
|
||||||
|
qualifiers,
|
||||||
|
"")
|
||||||
|
return pURL.ToString()
|
||||||
|
}
|
||||||
|
|
||||||
|
func jvmPrimaryVendorProduct(implementor, path, imageType string, hasJdk bool) (string, string) {
|
||||||
|
implementor = strings.ReplaceAll(strings.ToLower(implementor), " ", "")
|
||||||
|
|
||||||
|
pickProduct := func() string {
|
||||||
|
if hasJdk || jvmProjectByType(imageType) == jdk {
|
||||||
|
return jdk
|
||||||
|
}
|
||||||
|
return jre
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case strings.Contains(implementor, "azul") || strings.Contains(path, "zulu"):
|
||||||
|
return "azul", "zulu"
|
||||||
|
|
||||||
|
case strings.Contains(implementor, "sun"):
|
||||||
|
return "sun", pickProduct()
|
||||||
|
|
||||||
|
case strings.Contains(implementor, "oracle") || strings.Contains(path, "oracle"):
|
||||||
|
return oracleVendor, pickProduct()
|
||||||
|
}
|
||||||
|
return oracleVendor, openJdkProduct
|
||||||
|
}
|
||||||
|
|
||||||
|
func jvmCpes(version, primaryVendor, primaryProduct, imageType string, hasJdk bool) []cpe.CPE {
|
||||||
|
// see https://github.com/anchore/syft/issues/2422 for more context
|
||||||
|
|
||||||
|
var candidates []jvmCpeInfo
|
||||||
|
|
||||||
|
newCandidate := func(ven, prod, ver string) {
|
||||||
|
candidates = append(candidates, jvmCpeInfo{
|
||||||
|
vendor: ven,
|
||||||
|
product: prod,
|
||||||
|
version: ver,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
newEnterpriseCandidate := func(ven, ver string) {
|
||||||
|
newCandidate(ven, jre, ver)
|
||||||
|
if hasJdk || jvmProjectByType(imageType) == jdk {
|
||||||
|
newCandidate(ven, jdk, ver)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case primaryVendor == "azul":
|
||||||
|
newCandidate(primaryVendor, "zulu", version)
|
||||||
|
newCandidate(oracleVendor, openJdkProduct, version)
|
||||||
|
|
||||||
|
case primaryVendor == "sun":
|
||||||
|
newEnterpriseCandidate(primaryVendor, version)
|
||||||
|
|
||||||
|
case primaryVendor == oracleVendor && primaryProduct != openJdkProduct:
|
||||||
|
newCandidate(primaryVendor, "java_se", version)
|
||||||
|
newEnterpriseCandidate(primaryVendor, version)
|
||||||
|
default:
|
||||||
|
newCandidate(primaryVendor, primaryProduct, version)
|
||||||
|
}
|
||||||
|
|
||||||
|
var cpes []cpe.CPE
|
||||||
|
for _, candidate := range candidates {
|
||||||
|
c := newJvmCpe(candidate)
|
||||||
|
if c == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
cpes = append(cpes, *c)
|
||||||
|
}
|
||||||
|
|
||||||
|
return cpes
|
||||||
|
}
|
||||||
|
|
||||||
|
func getJVMVersionAndUpdate(version string) (string, string) {
|
||||||
|
hasPlus := strings.Contains(version, "+")
|
||||||
|
hasUnderscore := strings.Contains(version, "_")
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case hasUnderscore:
|
||||||
|
// assume legacy version strings are provided
|
||||||
|
// example: 1.8.0_302-b08
|
||||||
|
fields := strings.Split(version, "_")
|
||||||
|
if len(fields) == 2 {
|
||||||
|
shortVer := fields[0]
|
||||||
|
fields = strings.Split(fields[1], "-")
|
||||||
|
return shortVer, fields[0]
|
||||||
|
}
|
||||||
|
case hasPlus:
|
||||||
|
// assume JEP 223 version strings are provided
|
||||||
|
// example: 9.0.1+20
|
||||||
|
fields := strings.Split(version, "+")
|
||||||
|
return fields[0], ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// this could be a legacy or modern string that does not have an update
|
||||||
|
return version, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func newJvmCpe(candidate jvmCpeInfo) *cpe.CPE {
|
||||||
|
if candidate.vendor == "" || candidate.product == "" || candidate.version == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
shortVer, update := getJVMVersionAndUpdate(candidate.version)
|
||||||
|
|
||||||
|
if shortVer == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if update != "" && !strings.Contains(strings.ToLower(update), "update") {
|
||||||
|
update = "update" + trim0sFromLeft(update)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &cpe.CPE{
|
||||||
|
Attributes: cpe.Attributes{
|
||||||
|
Part: "a",
|
||||||
|
Vendor: candidate.vendor,
|
||||||
|
Product: candidate.product,
|
||||||
|
Version: shortVer,
|
||||||
|
Update: update,
|
||||||
|
},
|
||||||
|
// note: we must use a declared source here. Though we are not directly raising up raw CPEs from cataloged material,
|
||||||
|
// these are vastly more reliable and accurate than what would be generated from the cpe generator logic.
|
||||||
|
// We want these CPEs to override any generated CPEs (and in fact prevent the generation of CPEs for these packages altogether).
|
||||||
|
Source: cpe.DeclaredSource,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func jvmProjectByType(ty string) string {
|
||||||
|
if strings.Contains(strings.ToLower(ty), jre) {
|
||||||
|
return jre
|
||||||
|
}
|
||||||
|
return jdk
|
||||||
|
}
|
||||||
|
|
||||||
|
// jvmPackageVersion attempts to extract the correct version value for the JVM given a platter of version strings to choose
|
||||||
|
// from, and makes special consideration to what a valid version is relative to JEP 223.
|
||||||
|
//
|
||||||
|
// example version values (openjdk >8):
|
||||||
|
//
|
||||||
|
// IMPLEMENTOR_VERSION "Temurin-21.0.4+7"
|
||||||
|
// JAVA_RUNTIME_VERSION "21.0.4+7-LTS"
|
||||||
|
// FULL_VERSION "21.0.4+7-LTS"
|
||||||
|
// SEMANTIC_VERSION "21.0.4+7"
|
||||||
|
// JAVA_VERSION "21.0.4"
|
||||||
|
//
|
||||||
|
// example version values (openjdk 8):
|
||||||
|
//
|
||||||
|
// JAVA_VERSION "1.8.0_422"
|
||||||
|
// FULL_VERSION "1.8.0_422-b05"
|
||||||
|
// SEMANTIC_VERSION "8.0.422+5"
|
||||||
|
//
|
||||||
|
// example version values (openjdk 8, but older):
|
||||||
|
//
|
||||||
|
// JAVA_VERSION "1.8.0_302"
|
||||||
|
// FULL_VERSION "1.8.0_302-b08"
|
||||||
|
// SEMANTIC_VERSION "8.0.302+8"
|
||||||
|
//
|
||||||
|
// example version values (oracle):
|
||||||
|
//
|
||||||
|
// IMPLEMENTOR_VERSION (missing)
|
||||||
|
// JAVA_RUNTIME_VERSION "22.0.2+9-70"
|
||||||
|
// JAVA_VERSION "22.0.2"
|
||||||
|
//
|
||||||
|
// example version values (mariner):
|
||||||
|
//
|
||||||
|
// IMPLEMENTOR_VERSION "Microsoft-9889599"
|
||||||
|
// JAVA_RUNTIME_VERSION "17.0.12+7-LTS"
|
||||||
|
// JAVA_VERSION "17.0.12"
|
||||||
|
//
|
||||||
|
// example version values (amazon):
|
||||||
|
//
|
||||||
|
// IMPLEMENTOR_VERSION "Corretto-17.0.12.7.1"
|
||||||
|
// JAVA_RUNTIME_VERSION "17.0.12+7-LTS"
|
||||||
|
// JAVA_VERSION "17.0.12"
|
||||||
|
//
|
||||||
|
// JEP 223 changes to JVM version string in the following way:
|
||||||
|
//
|
||||||
|
// Pre JEP 223 Post JEP 223
|
||||||
|
// Release Type long short long short
|
||||||
|
// ------------ -------------------- --------------------
|
||||||
|
// Early Access 1.9.0-ea-b19 9-ea 9-ea+19 9-ea
|
||||||
|
// Major 1.9.0-b100 9 9+100 9
|
||||||
|
// Security #1 1.9.0_5-b20 9u5 9.0.1+20 9.0.1
|
||||||
|
// Security #2 1.9.0_11-b12 9u11 9.0.2+12 9.0.2
|
||||||
|
// Minor #1 1.9.0_20-b62 9u20 9.1.2+62 9.1.2
|
||||||
|
// Security #3 1.9.0_25-b15 9u25 9.1.3+15 9.1.3
|
||||||
|
// Security #4 1.9.0_31-b08 9u31 9.1.4+8 9.1.4
|
||||||
|
// Minor #2 1.9.0_40-b45 9u40 9.2.4+45 9.2.4
|
||||||
|
//
|
||||||
|
// What does this mean for us? In terms of the version selected, use semver-compliant strings when available.
|
||||||
|
//
|
||||||
|
// In terms of where to get the version:
|
||||||
|
//
|
||||||
|
// SEMANTIC_VERSION Reasonably prevalent, but most accurate in terms of comparable versions
|
||||||
|
// JAVA_RUNTIME_VERSION Reasonable prevalent, but difficult to distinguish pre-release info vs aux info (jep 223 sensitive)
|
||||||
|
// FULL_VERSION Reasonable prevalent, but difficult to distinguish pre-release info vs aux info (jep 223 sensitive)
|
||||||
|
// JAVA_VERSION Most prevalent, but least specific (jep 223 sensitive)
|
||||||
|
// IMPLEMENTOR_VERSION Unusable or missing in some cases
|
||||||
|
func jvmPackageVersion(ri *pkg.JavaVMRelease) string {
|
||||||
|
var version string
|
||||||
|
switch {
|
||||||
|
case ri.JavaRuntimeVersion != "":
|
||||||
|
return ri.JavaRuntimeVersion
|
||||||
|
case ri.FullVersion != "":
|
||||||
|
// if the full version major version matches the java version major version, then use the full version
|
||||||
|
fullMajor := strings.Split(ri.FullVersion, ".")[0]
|
||||||
|
javaMajor := strings.Split(ri.JavaVersion, ".")[0]
|
||||||
|
if fullMajor == javaMajor {
|
||||||
|
return ri.FullVersion
|
||||||
|
}
|
||||||
|
fallthrough
|
||||||
|
case ri.JavaVersion != "":
|
||||||
|
return ri.JavaVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
return version
|
||||||
|
}
|
||||||
|
|
||||||
|
func trim0sFromLeft(v string) string {
|
||||||
|
if v == "0" {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
return strings.TrimLeft(v, "0")
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseJvmReleaseInfo(r io.ReadCloser) (*pkg.JavaVMRelease, error) {
|
||||||
|
defer r.Close()
|
||||||
|
|
||||||
|
data := make(map[string]any)
|
||||||
|
scanner := bufio.NewScanner(io.LimitReader(r, 500*stereoFile.KB))
|
||||||
|
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := scanner.Text()
|
||||||
|
parts := strings.SplitN(line, "=", 2)
|
||||||
|
if len(parts) != 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
key := parts[0]
|
||||||
|
value := strings.Trim(parts[1], `"`)
|
||||||
|
|
||||||
|
if key == "MODULES" {
|
||||||
|
data[key] = strings.Split(value, " ")
|
||||||
|
} else {
|
||||||
|
data[key] = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we're missing key fields, then we don't have a JVM release file
|
||||||
|
if data["JAVA_VERSION"] == nil && data["JAVA_RUNTIME_VERSION"] == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var ri pkg.JavaVMRelease
|
||||||
|
if err := mapstructure.Decode(data, &ri); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &ri, nil
|
||||||
|
}
|
||||||
438
syft/pkg/cataloger/java/parse_jvm_release_test.go
Normal file
438
syft/pkg/cataloger/java/parse_jvm_release_test.go
Normal file
@ -0,0 +1,438 @@
|
|||||||
|
package java
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/anchore/syft/syft/cpe"
|
||||||
|
"github.com/anchore/syft/syft/pkg"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestJvmCpes(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
pkgVersion string
|
||||||
|
primaryVendor string
|
||||||
|
primaryProduct string
|
||||||
|
imageType string
|
||||||
|
hasJdk bool
|
||||||
|
expected []cpe.CPE
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "zulu release",
|
||||||
|
pkgVersion: "9.0.1+20",
|
||||||
|
primaryVendor: "azul",
|
||||||
|
primaryProduct: "zulu",
|
||||||
|
imageType: "jdk",
|
||||||
|
expected: []cpe.CPE{
|
||||||
|
{
|
||||||
|
Attributes: cpe.Attributes{
|
||||||
|
Part: "a",
|
||||||
|
Vendor: "azul",
|
||||||
|
Product: "zulu",
|
||||||
|
Version: "9.0.1",
|
||||||
|
Update: "",
|
||||||
|
},
|
||||||
|
Source: cpe.DeclaredSource,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Attributes: cpe.Attributes{
|
||||||
|
Part: "a",
|
||||||
|
Vendor: "oracle",
|
||||||
|
Product: "openjdk",
|
||||||
|
Version: "9.0.1",
|
||||||
|
Update: "",
|
||||||
|
},
|
||||||
|
Source: cpe.DeclaredSource,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "sun release",
|
||||||
|
pkgVersion: "1.6.0_322-b002",
|
||||||
|
primaryVendor: "sun",
|
||||||
|
primaryProduct: "jre",
|
||||||
|
imageType: "jre",
|
||||||
|
hasJdk: true,
|
||||||
|
expected: []cpe.CPE{
|
||||||
|
{
|
||||||
|
Attributes: cpe.Attributes{
|
||||||
|
Part: "a",
|
||||||
|
Vendor: "sun",
|
||||||
|
Product: "jre",
|
||||||
|
Version: "1.6.0",
|
||||||
|
Update: "update322",
|
||||||
|
},
|
||||||
|
Source: cpe.DeclaredSource,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Attributes: cpe.Attributes{
|
||||||
|
Part: "a",
|
||||||
|
Vendor: "sun",
|
||||||
|
Product: "jdk",
|
||||||
|
Version: "1.6.0",
|
||||||
|
Update: "update322",
|
||||||
|
},
|
||||||
|
Source: cpe.DeclaredSource,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "oracle se release",
|
||||||
|
pkgVersion: "1.8.0_322-b02",
|
||||||
|
primaryVendor: "oracle",
|
||||||
|
primaryProduct: "java_se",
|
||||||
|
imageType: "jdk",
|
||||||
|
hasJdk: true,
|
||||||
|
expected: []cpe.CPE{
|
||||||
|
{
|
||||||
|
Attributes: cpe.Attributes{
|
||||||
|
Part: "a",
|
||||||
|
Vendor: "oracle",
|
||||||
|
Product: "java_se",
|
||||||
|
Version: "1.8.0",
|
||||||
|
Update: "update322",
|
||||||
|
},
|
||||||
|
Source: cpe.DeclaredSource,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Attributes: cpe.Attributes{
|
||||||
|
Part: "a",
|
||||||
|
Vendor: "oracle",
|
||||||
|
Product: "jre",
|
||||||
|
Version: "1.8.0",
|
||||||
|
Update: "update322",
|
||||||
|
},
|
||||||
|
Source: cpe.DeclaredSource,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Attributes: cpe.Attributes{
|
||||||
|
Part: "a",
|
||||||
|
Vendor: "oracle",
|
||||||
|
Product: "jdk",
|
||||||
|
Version: "1.8.0",
|
||||||
|
Update: "update322",
|
||||||
|
},
|
||||||
|
Source: cpe.DeclaredSource,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "JEP 223 version with build info",
|
||||||
|
pkgVersion: "9.0.1+20",
|
||||||
|
primaryVendor: "oracle",
|
||||||
|
primaryProduct: "openjdk",
|
||||||
|
imageType: "openjdk",
|
||||||
|
expected: []cpe.CPE{
|
||||||
|
{
|
||||||
|
Attributes: cpe.Attributes{
|
||||||
|
Part: "a",
|
||||||
|
Vendor: "oracle",
|
||||||
|
Product: "openjdk",
|
||||||
|
Version: "9.0.1",
|
||||||
|
Update: "",
|
||||||
|
},
|
||||||
|
Source: cpe.DeclaredSource,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "JEP 223 version without build info",
|
||||||
|
pkgVersion: "11.0.9",
|
||||||
|
primaryVendor: "oracle",
|
||||||
|
primaryProduct: "openjdk",
|
||||||
|
imageType: "openjdk",
|
||||||
|
expected: []cpe.CPE{
|
||||||
|
{
|
||||||
|
Attributes: cpe.Attributes{
|
||||||
|
Part: "a",
|
||||||
|
Vendor: "oracle",
|
||||||
|
Product: "openjdk",
|
||||||
|
Version: "11.0.9",
|
||||||
|
Update: "",
|
||||||
|
},
|
||||||
|
Source: cpe.DeclaredSource,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no plus sign in version string",
|
||||||
|
pkgVersion: "1.8.0",
|
||||||
|
primaryVendor: "oracle",
|
||||||
|
primaryProduct: "openjdk",
|
||||||
|
imageType: "openjdk",
|
||||||
|
expected: []cpe.CPE{
|
||||||
|
{
|
||||||
|
Attributes: cpe.Attributes{
|
||||||
|
Part: "a",
|
||||||
|
Vendor: "oracle",
|
||||||
|
Product: "openjdk",
|
||||||
|
Version: "1.8.0",
|
||||||
|
Update: "",
|
||||||
|
},
|
||||||
|
Source: cpe.DeclaredSource,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty version string",
|
||||||
|
pkgVersion: "",
|
||||||
|
primaryVendor: "oracle",
|
||||||
|
primaryProduct: "",
|
||||||
|
imageType: "",
|
||||||
|
expected: nil,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
result := jvmCpes(tt.pkgVersion, tt.primaryVendor, tt.primaryProduct, tt.imageType, tt.hasJdk)
|
||||||
|
assert.Equal(t, tt.expected, result)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJvmVersion(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
input *pkg.JavaVMRelease
|
||||||
|
expected string
|
||||||
|
}{
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "JavaRuntimeVersion fallback",
|
||||||
|
input: &pkg.JavaVMRelease{
|
||||||
|
JavaRuntimeVersion: "21.0.4+7-LTS",
|
||||||
|
JavaVersion: "bogus",
|
||||||
|
FullVersion: "bogus",
|
||||||
|
SemanticVersion: "bogus",
|
||||||
|
},
|
||||||
|
expected: "21.0.4+7-LTS",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "JavaVersion fallback",
|
||||||
|
input: &pkg.JavaVMRelease{
|
||||||
|
JavaVersion: "21.0.4",
|
||||||
|
FullVersion: "bogus",
|
||||||
|
SemanticVersion: "bogus",
|
||||||
|
},
|
||||||
|
expected: "21.0.4",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// there is an example of this in eclipse-temurin:8u312-b07-jdk
|
||||||
|
name: "FullVersion is more accurate",
|
||||||
|
input: &pkg.JavaVMRelease{
|
||||||
|
JavaVersion: "1.8.0_131",
|
||||||
|
FullVersion: "1.8.0_131+b08",
|
||||||
|
},
|
||||||
|
expected: "1.8.0_131+b08",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty input fields",
|
||||||
|
input: &pkg.JavaVMRelease{},
|
||||||
|
expected: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
result := jvmPackageVersion(tt.input)
|
||||||
|
assert.Equal(t, tt.expected, result)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGetJVMVersionAndUpdate(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
version string
|
||||||
|
expectedVer string
|
||||||
|
expectedUpdate string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "legacy version with underscore and build",
|
||||||
|
version: "1.8.0_302-b08",
|
||||||
|
expectedVer: "1.8.0",
|
||||||
|
expectedUpdate: "302",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "legacy version with underscore but no build",
|
||||||
|
version: "1.8.0_302",
|
||||||
|
expectedVer: "1.8.0",
|
||||||
|
expectedUpdate: "302",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "JEP 223 version with plus sign",
|
||||||
|
version: "9.0.1+20",
|
||||||
|
expectedVer: "9.0.1",
|
||||||
|
expectedUpdate: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "JEP 223 version with plus but no update",
|
||||||
|
version: "11.0.9+",
|
||||||
|
expectedVer: "11.0.9",
|
||||||
|
expectedUpdate: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "modern version without plus or underscore",
|
||||||
|
version: "11.0.9",
|
||||||
|
expectedVer: "11.0.9",
|
||||||
|
expectedUpdate: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "legacy version without underscore or plus",
|
||||||
|
version: "1.7.0",
|
||||||
|
expectedVer: "1.7.0",
|
||||||
|
expectedUpdate: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "empty version string",
|
||||||
|
version: "",
|
||||||
|
expectedVer: "",
|
||||||
|
expectedUpdate: "",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
ver, update := getJVMVersionAndUpdate(tt.version)
|
||||||
|
assert.Equal(t, tt.expectedVer, ver)
|
||||||
|
assert.Equal(t, tt.expectedUpdate, update)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJvmPrimaryVendorProduct(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
implementor string
|
||||||
|
path string
|
||||||
|
imageType string
|
||||||
|
hasJdk bool
|
||||||
|
expectedVendor string
|
||||||
|
expectedProduct string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "Azul implementor with Zulu in path",
|
||||||
|
implementor: "Azul Systems",
|
||||||
|
path: "/usr/lib/jvm/zulu-11-amd64/release",
|
||||||
|
imageType: "JDK",
|
||||||
|
hasJdk: true,
|
||||||
|
expectedVendor: "azul",
|
||||||
|
expectedProduct: "zulu",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Sun implementor with JDK",
|
||||||
|
implementor: "Sun Microsystems",
|
||||||
|
path: "/usr/lib/jvm/jdk-1.8-sun-amd64/release",
|
||||||
|
imageType: "JDK",
|
||||||
|
hasJdk: true,
|
||||||
|
expectedVendor: "sun",
|
||||||
|
expectedProduct: "jdk",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Oracle implementor with JRE",
|
||||||
|
implementor: "Oracle Corporation",
|
||||||
|
path: "/usr/lib/jvm/jdk-1.8-oracle-x64/release",
|
||||||
|
imageType: "JRE",
|
||||||
|
hasJdk: false,
|
||||||
|
expectedVendor: "oracle",
|
||||||
|
expectedProduct: "jre",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Oracle vendor with JDK in path",
|
||||||
|
implementor: "",
|
||||||
|
path: "/usr/lib/jvm/jdk-1.8-oracle-x64/release",
|
||||||
|
imageType: "JDK",
|
||||||
|
hasJdk: true,
|
||||||
|
expectedVendor: "oracle",
|
||||||
|
expectedProduct: "jdk",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "OpenJDK with JDK",
|
||||||
|
implementor: "OpenJDK",
|
||||||
|
path: "/opt/java/openjdk/release",
|
||||||
|
imageType: "JDK",
|
||||||
|
hasJdk: true,
|
||||||
|
expectedVendor: "oracle", // like temurin
|
||||||
|
expectedProduct: "openjdk",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Amazon Corretto with JDK",
|
||||||
|
implementor: "Amazon Corretto",
|
||||||
|
path: "/usr/lib/jvm/java-17-amazon-corretto/release",
|
||||||
|
imageType: "JDK",
|
||||||
|
hasJdk: true,
|
||||||
|
expectedVendor: "oracle", // corretto upstream is oracle openjdk
|
||||||
|
expectedProduct: "openjdk",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
vendor, product := jvmPrimaryVendorProduct(tt.implementor, tt.path, tt.imageType, tt.hasJdk)
|
||||||
|
assert.Equal(t, tt.expectedVendor, vendor)
|
||||||
|
assert.Equal(t, tt.expectedProduct, product)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestJvmPurl(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
ri pkg.JavaVMRelease
|
||||||
|
version string
|
||||||
|
vendor string
|
||||||
|
product string
|
||||||
|
expectedPURL string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "build source repo provided",
|
||||||
|
ri: pkg.JavaVMRelease{
|
||||||
|
BuildSourceRepo: "https://github.com/adoptium/temurin-build.git",
|
||||||
|
},
|
||||||
|
version: "21.0.4",
|
||||||
|
vendor: "oracle",
|
||||||
|
product: "jdk",
|
||||||
|
expectedPURL: "pkg:generic/oracle/jdk@21.0.4?repository_url=https://github.com/adoptium/temurin-build.git",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "source repo provided, no build source repo",
|
||||||
|
ri: pkg.JavaVMRelease{
|
||||||
|
SourceRepo: "https://github.com/adoptium/jdk21u.git",
|
||||||
|
},
|
||||||
|
version: "21.0.4",
|
||||||
|
vendor: "azul",
|
||||||
|
product: "zulu",
|
||||||
|
expectedPURL: "pkg:generic/azul/zulu@21.0.4?repository_url=https://github.com/adoptium/jdk21u.git",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "no repository URLs provided",
|
||||||
|
ri: pkg.JavaVMRelease{
|
||||||
|
// No repository URLs provided
|
||||||
|
},
|
||||||
|
version: "17.0.2",
|
||||||
|
vendor: "oracle",
|
||||||
|
product: "jdk",
|
||||||
|
expectedPURL: "pkg:generic/oracle/jdk@17.0.2",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "JRE with source repo",
|
||||||
|
ri: pkg.JavaVMRelease{
|
||||||
|
SourceRepo: "https://github.com/adoptium/jre-repo.git",
|
||||||
|
},
|
||||||
|
version: "1.8.0_302",
|
||||||
|
vendor: "oracle",
|
||||||
|
product: "jre",
|
||||||
|
expectedPURL: "pkg:generic/oracle/jre@1.8.0_302?repository_url=https://github.com/adoptium/jre-repo.git",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
actualPURL := jvmPurl(tt.ri, tt.version, tt.vendor, tt.product)
|
||||||
|
assert.Equal(t, tt.expectedPURL, actualPURL)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
1
syft/pkg/cataloger/java/test-fixtures/jvm-installs/oracle-jdk-se-8/usr/lib/jvm/.gitignore
vendored
Normal file
1
syft/pkg/cataloger/java/test-fixtures/jvm-installs/oracle-jdk-se-8/usr/lib/jvm/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
!/jdk-1.8-oracle-x64/bin
|
||||||
@ -0,0 +1 @@
|
|||||||
|
compiler!
|
||||||
@ -0,0 +1,7 @@
|
|||||||
|
JAVA_VERSION="1.8.0_411"
|
||||||
|
JAVA_RUNTIME_VERSION="1.8.0_411-b25"
|
||||||
|
OS_NAME="Linux"
|
||||||
|
OS_VERSION="2.6"
|
||||||
|
OS_ARCH="amd64"
|
||||||
|
SOURCE=".:git:71ec2089cf8c+"
|
||||||
|
BUILD_TYPE="commercial"
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
IMPLEMENTOR="Eclipse Adoptium"
|
||||||
|
IMPLEMENTOR_VERSION="Temurin-21.0.4+7"
|
||||||
|
JAVA_RUNTIME_VERSION="21.0.4+7-LTS"
|
||||||
|
JAVA_VERSION="21.0.4"
|
||||||
|
JAVA_VERSION_DATE="2024-07-16"
|
||||||
|
LIBC="gnu"
|
||||||
|
MODULES="java.base java.compiler java.datatransfer java.xml java.prefs java.desktop java.instrument java.logging java.management java.security.sasl java.naming java.rmi java.management.rmi java.net.http java.scripting java.security.jgss java.transaction.xa java.sql java.sql.rowset java.xml.crypto java.se java.smartcardio jdk.accessibility jdk.internal.jvmstat jdk.attach jdk.charsets jdk.internal.opt jdk.zipfs jdk.compiler jdk.crypto.ec jdk.crypto.cryptoki jdk.dynalink jdk.internal.ed jdk.editpad jdk.hotspot.agent jdk.httpserver jdk.incubator.vector jdk.internal.le jdk.internal.vm.ci jdk.internal.vm.compiler jdk.internal.vm.compiler.management jdk.jartool jdk.javadoc jdk.jcmd jdk.management jdk.management.agent jdk.jconsole jdk.jdeps jdk.jdwp.agent jdk.jdi jdk.jfr jdk.jlink jdk.jpackage jdk.jshell jdk.jsobject jdk.jstatd jdk.localedata jdk.management.jfr jdk.naming.dns jdk.naming.rmi jdk.net jdk.nio.mapmode jdk.random jdk.sctp jdk.security.auth jdk.security.jgss jdk.unsupported jdk.unsupported.desktop jdk.xml.dom"
|
||||||
|
OS_ARCH="aarch64"
|
||||||
|
OS_NAME="Linux"
|
||||||
|
SOURCE=".:git:13710926b798"
|
||||||
|
BUILD_SOURCE="git:1271f10a26c47e1489a814dd2731f936a588d621"
|
||||||
|
BUILD_SOURCE_REPO="https://github.com/adoptium/temurin-build.git"
|
||||||
|
SOURCE_REPO="https://github.com/adoptium/jdk21u.git"
|
||||||
|
FULL_VERSION="21.0.4+7-LTS"
|
||||||
|
SEMANTIC_VERSION="21.0.4+7"
|
||||||
|
BUILD_INFO="OS: Linux Version: 5.4.0-150-generic"
|
||||||
|
JVM_VARIANT="Hotspot"
|
||||||
|
JVM_VERSION="21.0.4+7-LTS"
|
||||||
|
IMAGE_TYPE="JDK"
|
||||||
@ -0,0 +1 @@
|
|||||||
|
content!
|
||||||
@ -18,6 +18,80 @@ var jenkinsPluginPomPropertiesGroupIDs = []string{
|
|||||||
"com.cloudbees.jenkins.plugins",
|
"com.cloudbees.jenkins.plugins",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type JavaVMInstallation struct {
|
||||||
|
Release JavaVMRelease `json:"release"`
|
||||||
|
Files []string `json:"files"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m JavaVMInstallation) OwnedFiles() []string {
|
||||||
|
return m.Files
|
||||||
|
}
|
||||||
|
|
||||||
|
type JavaVMRelease struct {
|
||||||
|
// Implementor is extracted with the `java.vendor` JVM property
|
||||||
|
Implementor string `mapstructure:"IMPLEMENTOR,omitempty" json:"implementor,omitempty"`
|
||||||
|
|
||||||
|
// ImplementorVersion is extracted with the `java.vendor.version` JVM property
|
||||||
|
ImplementorVersion string `mapstructure:"IMPLEMENTOR_VERSION,omitempty" json:"implementorVersion,omitempty"`
|
||||||
|
|
||||||
|
// JavaRuntimeVersion is extracted from the 'java.runtime.version' JVM property
|
||||||
|
JavaRuntimeVersion string `mapstructure:"JAVA_RUNTIME_VERSION,omitempty" json:"javaRuntimeVersion,omitempty"`
|
||||||
|
|
||||||
|
// JavaVersion matches that from `java -version` command output
|
||||||
|
JavaVersion string `mapstructure:"JAVA_VERSION,omitempty" json:"javaVersion,omitempty"`
|
||||||
|
|
||||||
|
// JavaVersionDate is extracted from the 'java.version.date' JVM property
|
||||||
|
JavaVersionDate string `mapstructure:"JAVA_VERSION_DATE,omitempty" json:"javaVersionDate,omitempty"`
|
||||||
|
|
||||||
|
// Libc can either be 'glibc' or 'musl'
|
||||||
|
Libc string `mapstructure:"LIBC,omitempty" json:"libc,omitempty"`
|
||||||
|
|
||||||
|
// Modules is a list of JVM modules that are packaged
|
||||||
|
Modules []string `mapstructure:"MODULES,omitempty" json:"modules,omitempty"`
|
||||||
|
|
||||||
|
// OsArch is the target CPU architecture
|
||||||
|
OsArch string `mapstructure:"OS_ARCH,omitempty" json:"osArch,omitempty"`
|
||||||
|
|
||||||
|
// OsName is the name of the target runtime operating system environment
|
||||||
|
OsName string `mapstructure:"OS_NAME,omitempty" json:"osName,omitempty"`
|
||||||
|
|
||||||
|
// OsVersion is the version of the target runtime operating system environment
|
||||||
|
OsVersion string `mapstructure:"OS_VERSION,omitempty" json:"osVersion,omitempty"`
|
||||||
|
|
||||||
|
// Source refers to the origin repository of OpenJDK source
|
||||||
|
Source string `mapstructure:"SOURCE,omitempty" json:"source,omitempty"`
|
||||||
|
|
||||||
|
// BuildSource Git SHA of the build repository
|
||||||
|
BuildSource string `mapstructure:"BUILD_SOURCE,omitempty" json:"buildSource,omitempty"`
|
||||||
|
|
||||||
|
// BuildSourceRepo refers to rhe repository URL for the build source
|
||||||
|
BuildSourceRepo string `mapstructure:"BUILD_SOURCE_REPO,omitempty" json:"buildSourceRepo,omitempty"`
|
||||||
|
|
||||||
|
// SourceRepo refers to the OpenJDK repository URL
|
||||||
|
SourceRepo string `mapstructure:"SOURCE_REPO,omitempty" json:"sourceRepo,omitempty"`
|
||||||
|
|
||||||
|
// FullVersion is extracted from the 'java.runtime.version' JVM property
|
||||||
|
FullVersion string `mapstructure:"FULL_VERSION,omitempty" json:"fullVersion,omitempty"`
|
||||||
|
|
||||||
|
// SemanticVersion is derived from the OpenJDK version
|
||||||
|
SemanticVersion string `mapstructure:"SEMANTIC_VERSION,omitempty" json:"semanticVersion,omitempty"`
|
||||||
|
|
||||||
|
// BuildInfo contains additional build information
|
||||||
|
BuildInfo string `mapstructure:"BUILD_INFO,omitempty" json:"buildInfo,omitempty"`
|
||||||
|
|
||||||
|
// JvmVariant specifies the JVM variant (e.g., Hotspot or OpenJ9)
|
||||||
|
JvmVariant string `mapstructure:"JVM_VARIANT,omitempty" json:"jvmVariant,omitempty"`
|
||||||
|
|
||||||
|
// JvmVersion is extracted from the 'java.vm.version' JVM property
|
||||||
|
JvmVersion string `mapstructure:"JVM_VERSION,omitempty" json:"jvmVersion,omitempty"`
|
||||||
|
|
||||||
|
// ImageType can be 'JDK' or 'JRE'
|
||||||
|
ImageType string `mapstructure:"IMAGE_TYPE,omitempty" json:"imageType,omitempty"`
|
||||||
|
|
||||||
|
// BuildType can be 'commercial' (used in some older oracle JDK distributions)
|
||||||
|
BuildType string `mapstructure:"BUILD_TYPE,omitempty" json:"buildType,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
// JavaArchive encapsulates all Java ecosystem metadata for a package as well as an (optional) parent relationship.
|
// JavaArchive encapsulates all Java ecosystem metadata for a package as well as an (optional) parent relationship.
|
||||||
type JavaArchive struct {
|
type JavaArchive struct {
|
||||||
VirtualPath string `json:"virtualPath" cyclonedx:"virtualPath"` // we need to include the virtual path in cyclonedx documents to prevent deduplication of jars within jars
|
VirtualPath string `json:"virtualPath" cyclonedx:"virtualPath"` // we need to include the virtual path in cyclonedx documents to prevent deduplication of jars within jars
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user