mirror of
https://github.com/anchore/syft.git
synced 2026-10-11 21:57:22 +02:00
Signed-off-by: Rez Moss <hi@rezmoss.com>
This commit is contained in:
@@ -9,6 +9,7 @@ import (
|
||||
"iter"
|
||||
"os"
|
||||
"path"
|
||||
"regexp"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
@@ -301,6 +302,9 @@ func (j *archiveParser) discoverNameVersionLicense(ctx context.Context, manifest
|
||||
if version == "" {
|
||||
version = selectVersion(manifest, j.fileInfo)
|
||||
}
|
||||
if version == "" {
|
||||
version = j.versionFromPropertiesFile(ctx, manifest)
|
||||
}
|
||||
|
||||
if len(lics) == 0 {
|
||||
fileLicenses := j.getLicenseFromFileInArchive(ctx)
|
||||
@@ -609,6 +613,44 @@ func (j *archiveParser) getLicenseFromFileInArchive(ctx context.Context) []pkg.L
|
||||
return identified
|
||||
}
|
||||
|
||||
// optional "v" + leading digit + version-ish chars; rejects placeholders like "${revision}"
|
||||
var releaseVersionPattern = regexp.MustCompile(`^v?(\d[0-9A-Za-z.\-_+]*)$`)
|
||||
|
||||
// versionFromPropertiesFile: last-resort ver from root version.properties for uber-jars built
|
||||
// outside maven/gradle e.g. metabase, "tag=v0.63.5", shading can land a deps file at t same
|
||||
// path, so be conservative => Main-Class jars only, "version"/"tag" keys only, version shaped values only
|
||||
func (j *archiveParser) versionFromPropertiesFile(ctx context.Context, manifest *pkg.JavaManifest) string {
|
||||
if manifest == nil || manifest.Main.MustGet("Main-Class") == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
matches := j.fileManifest.GlobMatch(true, "/version.properties")
|
||||
if len(matches) != 1 {
|
||||
return ""
|
||||
}
|
||||
|
||||
contents, err := intFile.ContentsFromZip(ctx, j.archivePath, matches...)
|
||||
if err != nil {
|
||||
log.Debugf("unable to extract version.properties (%s): %v", j.location, err)
|
||||
return ""
|
||||
}
|
||||
|
||||
props := map[string]string{}
|
||||
for line := range strings.Lines(contents[matches[0]]) {
|
||||
if k, v, ok := strings.Cut(line, "="); ok {
|
||||
props[strings.ToLower(strings.TrimSpace(k))] = strings.TrimSpace(v)
|
||||
}
|
||||
}
|
||||
|
||||
for _, key := range []string{"version", "tag"} {
|
||||
if m := releaseVersionPattern.FindStringSubmatch(props[key]); m != nil {
|
||||
return m[1]
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
func (j *archiveParser) discoverPkgsFromNestedArchives(ctx context.Context, parentPkg *pkg.Package) ([]pkg.Package, []artifact.Relationship, error) {
|
||||
// we know that all java archives are zip formatted files, so we can use the shared zip helper
|
||||
return discoverPkgsFromZip(ctx, j.location, j.archivePath, j.contentPath, j.fileManifest, parentPkg, j.cfg)
|
||||
|
||||
+16
-1
@@ -15,6 +15,9 @@ MULTIPLE_MATCHING = multiple-matching-2.11.5
|
||||
ORG_MULTIPLE_THENAME = org.multiple-thename
|
||||
MICRONAUT_AOP = micronaut-aop-4.9.11
|
||||
COMMONS_LANG3 = commons-lang3-3.12.0
|
||||
UBER_APP_VERSION_PROPERTIES = uber-app-version-properties
|
||||
UBER_APP_VERSION_PLACEHOLDER = uber-app-version-placeholder
|
||||
LIB_WITH_VERSION_PROPERTIES = lib-with-version-properties
|
||||
|
||||
.DEFAULT_GOAL := fixtures
|
||||
|
||||
@@ -24,7 +27,7 @@ fixtures: $(CACHE_DIR)
|
||||
# requirement 2: 'fingerprint' goal to determine if the fixture input that indicates any existing cache should be busted
|
||||
fingerprint: $(FINGERPRINT_FILE)
|
||||
|
||||
$(CACHE_DIR): $(CACHE_DIR)/$(JACKSON_CORE).jar $(CACHE_DIR)/$(SBT_JACKSON_CORE).jar $(CACHE_DIR)/$(OPENSAML_CORE).jar $(CACHE_DIR)/$(API_ALL_SOURCES).jar $(CACHE_DIR)/$(SPRING_INSTRUMENTATION).jar $(CACHE_DIR)/$(MULTIPLE_MATCHING).jar $(CACHE_DIR)/$(MICRONAUT_AOP).jar $(CACHE_DIR)/$(COMMONS_LANG3).jar
|
||||
$(CACHE_DIR): $(CACHE_DIR)/$(JACKSON_CORE).jar $(CACHE_DIR)/$(SBT_JACKSON_CORE).jar $(CACHE_DIR)/$(OPENSAML_CORE).jar $(CACHE_DIR)/$(API_ALL_SOURCES).jar $(CACHE_DIR)/$(SPRING_INSTRUMENTATION).jar $(CACHE_DIR)/$(MULTIPLE_MATCHING).jar $(CACHE_DIR)/$(MICRONAUT_AOP).jar $(CACHE_DIR)/$(COMMONS_LANG3).jar $(CACHE_DIR)/$(UBER_APP_VERSION_PROPERTIES).jar $(CACHE_DIR)/$(UBER_APP_VERSION_PLACEHOLDER).jar $(CACHE_DIR)/$(LIB_WITH_VERSION_PROPERTIES).jar
|
||||
|
||||
$(CACHE_DIR)/$(JACKSON_CORE).jar:
|
||||
mkdir -p $(CACHE_DIR)
|
||||
@@ -62,6 +65,18 @@ $(CACHE_DIR)/$(COMMONS_LANG3).jar:
|
||||
mkdir -p $(CACHE_DIR)
|
||||
cd $(COMMONS_LANG3) && zip -r $(CACHE_PATH)/$(COMMONS_LANG3).jar .
|
||||
|
||||
$(CACHE_DIR)/$(UBER_APP_VERSION_PROPERTIES).jar:
|
||||
mkdir -p $(CACHE_DIR)
|
||||
cd $(UBER_APP_VERSION_PROPERTIES) && zip -r $(CACHE_PATH)/$(UBER_APP_VERSION_PROPERTIES).jar .
|
||||
|
||||
$(CACHE_DIR)/$(UBER_APP_VERSION_PLACEHOLDER).jar:
|
||||
mkdir -p $(CACHE_DIR)
|
||||
cd $(UBER_APP_VERSION_PLACEHOLDER) && zip -r $(CACHE_PATH)/$(UBER_APP_VERSION_PLACEHOLDER).jar .
|
||||
|
||||
$(CACHE_DIR)/$(LIB_WITH_VERSION_PROPERTIES).jar:
|
||||
mkdir -p $(CACHE_DIR)
|
||||
cd $(LIB_WITH_VERSION_PROPERTIES) && zip -r $(CACHE_PATH)/$(LIB_WITH_VERSION_PROPERTIES).jar .
|
||||
|
||||
# Jenkins plugins typically do not have the version included in the archive name,
|
||||
# so it is important to not include it in the generated test fixture
|
||||
$(CACHE_DIR)/gradle.hpi:
|
||||
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
Manifest-Version: 1.0
|
||||
Created-By: Apache Maven
|
||||
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
version=1.2.3
|
||||
Vendored
+4
@@ -0,0 +1,4 @@
|
||||
Manifest-Version: 1.0
|
||||
Created-By: build.clj
|
||||
Main-Class: example.core.bootstrap
|
||||
|
||||
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
version=${revision}
|
||||
build.date=${build_timestamp}
|
||||
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
Manifest-Version: 1.0
|
||||
Multi-Release: true
|
||||
Created-By: build.clj
|
||||
Build-Jdk-Spec: 25
|
||||
Main-Class: example.core.bootstrap
|
||||
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
tag=v0.63.5
|
||||
hash=1dca717
|
||||
date=2026-08-05
|
||||
@@ -0,0 +1,124 @@
|
||||
package java
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
intFile "github.com/anchore/syft/internal/file"
|
||||
"github.com/anchore/syft/syft/file"
|
||||
"github.com/anchore/syft/syft/pkg"
|
||||
"github.com/anchore/syft/syft/pkg/cataloger/internal/pkgtest"
|
||||
)
|
||||
|
||||
func readManifest(t *testing.T, ctx context.Context, parser *archiveParser) *pkg.JavaManifest {
|
||||
t.Helper()
|
||||
|
||||
matches := parser.fileManifest.GlobMatch(false, manifestGlob)
|
||||
require.Len(t, matches, 1)
|
||||
|
||||
contents, err := intFile.ContentsFromZip(ctx, parser.archivePath, matches...)
|
||||
require.NoError(t, err)
|
||||
|
||||
manifest, err := parseJavaManifest(parser.archivePath, strings.NewReader(contents[matches[0]]))
|
||||
require.NoError(t, err)
|
||||
|
||||
return manifest
|
||||
}
|
||||
|
||||
func Test_archiveParser_versionFromPropertiesFile(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
fixture string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
// "tag=v0.63.5"
|
||||
name: "release tag from root version.properties",
|
||||
fixture: "uber-app-version-properties",
|
||||
want: "0.63.5",
|
||||
},
|
||||
{
|
||||
// clickhouse-jdbc ships "version=${revision}" must not become a version
|
||||
name: "unresolved build placeholder is rejected",
|
||||
fixture: "uber-app-version-placeholder",
|
||||
want: "",
|
||||
},
|
||||
{
|
||||
// no Main-Class -> cant tell app uberjar from bundled dep
|
||||
name: "library without Main-Class is rejected",
|
||||
fixture: "lib-with-version-properties",
|
||||
want: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
ctx := pkgtest.Context(t)
|
||||
|
||||
fixturePath := generateJavaMetadataJarFixture(t, tt.fixture, "jar")
|
||||
fixture, err := os.Open(fixturePath)
|
||||
require.NoError(t, err)
|
||||
|
||||
parser, cleanupFn, err := newJavaArchiveParser(ctx,
|
||||
file.LocationReadCloser{
|
||||
Location: file.NewLocation(fixture.Name()),
|
||||
ReadCloser: fixture,
|
||||
}, false, DefaultArchiveCatalogerConfig())
|
||||
defer cleanupFn()
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Equal(t, tt.want, parser.versionFromPropertiesFile(ctx, readManifest(t, ctx, parser)))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_archiveParser_versionFromPropertiesFile_noManifest(t *testing.T) {
|
||||
ctx := pkgtest.Context(t)
|
||||
|
||||
fixturePath := generateJavaMetadataJarFixture(t, "uber-app-version-properties", "jar")
|
||||
fixture, err := os.Open(fixturePath)
|
||||
require.NoError(t, err)
|
||||
|
||||
parser, cleanupFn, err := newJavaArchiveParser(ctx,
|
||||
file.LocationReadCloser{
|
||||
Location: file.NewLocation(fixture.Name()),
|
||||
ReadCloser: fixture,
|
||||
}, false, DefaultArchiveCatalogerConfig())
|
||||
defer cleanupFn()
|
||||
require.NoError(t, err)
|
||||
|
||||
assert.Empty(t, parser.versionFromPropertiesFile(ctx, nil))
|
||||
}
|
||||
|
||||
func Test_releaseVersionPattern(t *testing.T) {
|
||||
tests := []struct {
|
||||
value string
|
||||
want string
|
||||
}{
|
||||
{"v0.63.5", "0.63.5"},
|
||||
{"0.63.5", "0.63.5"},
|
||||
{"2.5.1", "2.5.1"},
|
||||
{"1.0.0-SNAPSHOT", "1.0.0-SNAPSHOT"},
|
||||
{"3", "3"},
|
||||
{"${revision}", ""},
|
||||
{"trunk-nightly build", ""},
|
||||
{"valhalla", ""},
|
||||
{"", ""},
|
||||
{"v", ""},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.value, func(t *testing.T) {
|
||||
var got string
|
||||
if m := releaseVersionPattern.FindStringSubmatch(tt.value); m != nil {
|
||||
got = m[1]
|
||||
}
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user