Update trivy from 0.35.0 to 0.49.1 (#1806)
* Update trivy 0.35.0->0.48.0
- Specify oras-go 1.2.4 in indirect dependencies
docker/docker changes a part of its API at 24.0
- registry: return concrete service type · moby/moby@7b3acdf
- 7b3acdff5d (diff-8325eae896b1149bf92c826d07fc29005b1b102000b766ffa5a238d791e0849bR18-R21)
oras-go 1.2.3 uses 23.0.1 and trivy transitively depends on docker/docker 24.y.z.
There is a build error between oras-go and docker/dockr.
- Update disabled analyzers
- Update language scanners, enable all of them
* move javadb init to scan.go
* Add options for java db init()
* Update scanner/base.go
* Remove unused codes
* Add some lock file names
* Typo fix
* Remove space character (0x20)
* Add java-db options for integration scan
* Minor fomartting fix
* minor fix
* conda is NOT supported by Trivy for library scan
* Configure trivy log in report command too
* Init trivy in scanner
* Use trivy's jar.go and replace client which does almost nothing
* mv jar.go
* Add sha1 hash to result and add filepath for report phase
* Undo added 'vuls scan' options
* Update oras-go to 1.2.4
* Move Java DB related config items to report side
* Add java db search in detect phase
* filter top level jar only
* Update trivy to 0.49.1
* go mod tidy
* Update to newer interface
* Refine lock file list, h/t MaineK00n
* Avoid else clauses if possible, h/t MaineK00n
* Avoid missing word for find and lang types, h/t MaineK00n
* Add missing ecosystems, h/t MaineK00n
* Add comments why to use custom jar analyzer, h/t MaineK00n
* Misc
* Misc
* Misc
* Include go-dep-parser's pares.go for modification
* Move digest field from LibraryScanner to Library
* Use inner jars sha1 for each
* Add Seek to file head before handling zip file entry
* Leave Digest feild empty for entries from pom.xml
* Don't import python/pkg (don't look into package.json)
* Make privete where private is sufficient
* Remove duplicate after Java DB lookup
* misc
* go mod tidy
* Comment out ruby/gemspec
* misc
* Comment out python/packaging
* misc
* Use custom jar
* Update scanner/trivy/jar/parse.go
Co-authored-by: MaineK00n <mainek00n.1229@gmail.com>
* Update scanner/trivy/jar/parse.go
Co-authored-by: MaineK00n <mainek00n.1229@gmail.com>
* Update scanner/trivy/jar/parse.go
Co-authored-by: MaineK00n <mainek00n.1229@gmail.com>
* Update scanner/trivy/jar/parse.go
Co-authored-by: MaineK00n <mainek00n.1229@gmail.com>
* Update scanner/trivy/jar/parse.go
Co-authored-by: MaineK00n <mainek00n.1229@gmail.com>
* Update scanner/trivy/jar/jar.go
Co-authored-by: MaineK00n <mainek00n.1229@gmail.com>
* Update detector/library.go
Co-authored-by: MaineK00n <mainek00n.1229@gmail.com>
* Update models/library.go
Co-authored-by: MaineK00n <mainek00n.1229@gmail.com>
* Update scanner/base.go
Co-authored-by: MaineK00n <mainek00n.1229@gmail.com>
* Update scanner/trivy/jar/parse.go
Co-authored-by: MaineK00n <mainek00n.1229@gmail.com>
* Update scanner/trivy/jar/parse.go
Co-authored-by: MaineK00n <mainek00n.1229@gmail.com>
* Missing changes in name change
* Update models/github.go
Co-authored-by: MaineK00n <mainek00n.1229@gmail.com>
* Update models/library.go
Co-authored-by: MaineK00n <mainek00n.1229@gmail.com>
* Update models/library.go
Co-authored-by: MaineK00n <mainek00n.1229@gmail.com>
* Update models/library.go
Co-authored-by: MaineK00n <mainek00n.1229@gmail.com>
* Update scanner/base.go
Co-authored-by: MaineK00n <mainek00n.1229@gmail.com>
* Update scanner/base.go
Co-authored-by: MaineK00n <mainek00n.1229@gmail.com>
* Update scanner/trivy/jar/jar.go
Co-authored-by: MaineK00n <mainek00n.1229@gmail.com>
* Don't import fanal/types at github.go
* Rewrite code around java db initialization
* Add comment
* refactor
* Close java db client
* rename
* Let LibraryScanner have java db client
* Update detector/library.go
Co-authored-by: MaineK00n <mainek00n.1229@gmail.com>
* Update detector/library.go
Co-authored-by: MaineK00n <mainek00n.1229@gmail.com>
* Update detector/library.go
Co-authored-by: MaineK00n <mainek00n.1229@gmail.com>
* Update detector/library.go
Co-authored-by: MaineK00n <mainek00n.1229@gmail.com>
* inline variable
* misc
* Fix typo
---------
Co-authored-by: MaineK00n <mainek00n.1229@gmail.com>
This commit is contained in:
committed by
GitHub
parent
d7e1e82299
commit
351cf4f712
115
scanner/trivy/jar/jar.go
Normal file
115
scanner/trivy/jar/jar.go
Normal file
@@ -0,0 +1,115 @@
|
||||
package jar
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
dio "github.com/aquasecurity/go-dep-parser/pkg/io"
|
||||
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
|
||||
"github.com/aquasecurity/trivy/pkg/fanal/types"
|
||||
"github.com/aquasecurity/trivy/pkg/parallel"
|
||||
)
|
||||
|
||||
func init() {
|
||||
analyzer.RegisterPostAnalyzer(analyzer.TypeJar, newJavaLibraryAnalyzer)
|
||||
}
|
||||
|
||||
const version = 1
|
||||
|
||||
var requiredExtensions = []string{
|
||||
".jar",
|
||||
".war",
|
||||
".ear",
|
||||
".par",
|
||||
}
|
||||
|
||||
// javaLibraryAnalyzer analyzes jar/war/ear/par files
|
||||
type javaLibraryAnalyzer struct {
|
||||
parallel int
|
||||
}
|
||||
|
||||
func newJavaLibraryAnalyzer(options analyzer.AnalyzerOptions) (analyzer.PostAnalyzer, error) {
|
||||
return &javaLibraryAnalyzer{
|
||||
parallel: options.Parallel,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (a *javaLibraryAnalyzer) PostAnalyze(ctx context.Context, input analyzer.PostAnalysisInput) (*analyzer.AnalysisResult, error) {
|
||||
// It will be called on each JAR file
|
||||
onFile := func(path string, info fs.FileInfo, r dio.ReadSeekerAt) (*types.Application, error) {
|
||||
p := newParser(withSize(info.Size()), withFilePath(path))
|
||||
parsedLibs, err := p.parse(r)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("Failed to parse %s. err: %w", path, err)
|
||||
}
|
||||
|
||||
return toApplication(path, parsedLibs), nil
|
||||
}
|
||||
|
||||
var apps []types.Application
|
||||
onResult := func(app *types.Application) error {
|
||||
if app == nil {
|
||||
return nil
|
||||
}
|
||||
apps = append(apps, *app)
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := parallel.WalkDir(ctx, input.FS, ".", a.parallel, onFile, onResult); err != nil {
|
||||
return nil, xerrors.Errorf("Failed to walk dir. err: %w", err)
|
||||
}
|
||||
|
||||
return &analyzer.AnalysisResult{
|
||||
Applications: apps,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func toApplication(rootFilePath string, libs []jarLibrary) *types.Application {
|
||||
if len(libs) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
pkgs := make([]types.Package, 0, len(libs))
|
||||
for _, lib := range libs {
|
||||
libPath := rootFilePath
|
||||
if lib.filePath != "" {
|
||||
libPath = lib.filePath
|
||||
}
|
||||
|
||||
pkgs = append(pkgs, types.Package{
|
||||
Name: lib.name,
|
||||
Version: lib.version,
|
||||
FilePath: libPath,
|
||||
Digest: lib.digest,
|
||||
})
|
||||
}
|
||||
|
||||
return &types.Application{
|
||||
Type: types.Jar,
|
||||
FilePath: rootFilePath,
|
||||
Libraries: pkgs,
|
||||
}
|
||||
}
|
||||
|
||||
func (a *javaLibraryAnalyzer) Required(filePath string, _ os.FileInfo) bool {
|
||||
ext := filepath.Ext(filePath)
|
||||
for _, required := range requiredExtensions {
|
||||
if strings.EqualFold(ext, required) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (a *javaLibraryAnalyzer) Type() analyzer.Type {
|
||||
return analyzer.TypeJar
|
||||
}
|
||||
|
||||
func (a *javaLibraryAnalyzer) Version() int {
|
||||
return version
|
||||
}
|
||||
401
scanner/trivy/jar/parse.go
Normal file
401
scanner/trivy/jar/parse.go
Normal file
@@ -0,0 +1,401 @@
|
||||
package jar
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
dio "github.com/aquasecurity/go-dep-parser/pkg/io"
|
||||
"github.com/aquasecurity/go-dep-parser/pkg/log"
|
||||
"github.com/aquasecurity/trivy/pkg/digest"
|
||||
"github.com/samber/lo"
|
||||
"go.uber.org/zap"
|
||||
"golang.org/x/xerrors"
|
||||
)
|
||||
|
||||
var (
|
||||
jarFileRegEx = regexp.MustCompile(`^([a-zA-Z0-9\._-]*[^-*])-(\d\S*(?:-SNAPSHOT)?).[jwep]ar$`)
|
||||
)
|
||||
|
||||
type jarLibrary struct {
|
||||
id string
|
||||
name string
|
||||
version string
|
||||
filePath string
|
||||
// SHA1 hash for later use at detect phase.
|
||||
// When this record has come from pom.properties, no Java DB look up needed and this field must be left empty.
|
||||
digest digest.Digest
|
||||
}
|
||||
|
||||
type properties struct {
|
||||
groupID string
|
||||
artifactID string
|
||||
version string
|
||||
filePath string // path to file containing these props
|
||||
digest digest.Digest
|
||||
}
|
||||
|
||||
func (p properties) library() jarLibrary {
|
||||
return jarLibrary{
|
||||
name: fmt.Sprintf("%s:%s", p.groupID, p.artifactID),
|
||||
version: p.version,
|
||||
filePath: p.filePath,
|
||||
digest: p.digest,
|
||||
}
|
||||
}
|
||||
|
||||
func (p properties) valid() bool {
|
||||
return p.groupID != "" && p.artifactID != "" && p.version != ""
|
||||
}
|
||||
|
||||
func (p properties) string() string {
|
||||
return fmt.Sprintf("%s:%s:%s", p.groupID, p.artifactID, p.version)
|
||||
}
|
||||
|
||||
type parser struct {
|
||||
rootFilePath string
|
||||
size int64
|
||||
}
|
||||
|
||||
type option func(*parser)
|
||||
|
||||
func withFilePath(filePath string) option {
|
||||
return func(p *parser) {
|
||||
p.rootFilePath = filePath
|
||||
}
|
||||
}
|
||||
|
||||
func withSize(size int64) option {
|
||||
return func(p *parser) {
|
||||
p.size = size
|
||||
}
|
||||
}
|
||||
|
||||
func newParser(opts ...option) *parser {
|
||||
p := &parser{}
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(p)
|
||||
}
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
func (p *parser) parse(r dio.ReadSeekerAt) ([]jarLibrary, error) {
|
||||
libs, err := p.parseArtifact(p.rootFilePath, p.size, r)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("Failed to parse %s. err: %w", p.rootFilePath, err)
|
||||
}
|
||||
return removeLibraryDuplicates(libs), nil
|
||||
}
|
||||
|
||||
// This function MUST NOT return empty list unless an error occurred.
|
||||
// The least element contains file path and SHA1 digest, they can be used at detect phase to
|
||||
// determine actual name and version.
|
||||
func (p *parser) parseArtifact(filePath string, size int64, r dio.ReadSeekerAt) ([]jarLibrary, error) {
|
||||
log.Logger.Debugw("Parsing Java artifacts...", zap.String("file", filePath))
|
||||
|
||||
sha1, err := digest.CalcSHA1(r)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("Failed to calculate SHA1. err: %w", err)
|
||||
}
|
||||
|
||||
zr, err := zip.NewReader(r, size)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("Failed to open zip. err: %w", err)
|
||||
}
|
||||
|
||||
// Try to extract artifactId and version from the file name
|
||||
// e.g. spring-core-5.3.4-SNAPSHOT.jar => sprint-core, 5.3.4-SNAPSHOT
|
||||
fileProps := parseFileName(filePath, sha1)
|
||||
|
||||
var libs []jarLibrary
|
||||
var m manifest
|
||||
var foundPomProps bool
|
||||
|
||||
for _, fileInJar := range zr.File {
|
||||
switch {
|
||||
case filepath.Base(fileInJar.Name) == "pom.properties":
|
||||
props, err := parsePomProperties(fileInJar, filePath)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("Failed to parse %s. err: %w", fileInJar.Name, err)
|
||||
}
|
||||
libs = append(libs, props.library())
|
||||
|
||||
// Check if the pom.properties is for the original JAR/WAR/EAR
|
||||
if fileProps.artifactID == props.artifactID && fileProps.version == props.version {
|
||||
foundPomProps = true
|
||||
}
|
||||
case filepath.Base(fileInJar.Name) == "MANIFEST.MF":
|
||||
m, err = parseManifest(fileInJar)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("Failed to parse MANIFEST.MF. err: %w", err)
|
||||
}
|
||||
case isArtifact(fileInJar.Name):
|
||||
innerLibs, err := p.parseInnerJar(fileInJar, filePath) //TODO process inner deps
|
||||
if err != nil {
|
||||
log.Logger.Debugf("Failed to parse %s. err: %s", fileInJar.Name, err)
|
||||
continue
|
||||
}
|
||||
libs = append(libs, innerLibs...)
|
||||
}
|
||||
}
|
||||
|
||||
// If pom.properties is found, it should be preferred than MANIFEST.MF.
|
||||
if foundPomProps {
|
||||
return libs, nil
|
||||
}
|
||||
|
||||
manifestProps := m.properties(filePath, sha1)
|
||||
if manifestProps.valid() {
|
||||
return append(libs, manifestProps.library()), nil
|
||||
}
|
||||
|
||||
// At this point, no library information from pom nor manifests.
|
||||
// Add one from fileProps, which may have no artifact ID or version, but it will be
|
||||
// rescued at detect phase by SHA1.
|
||||
return append(libs, fileProps.library()), nil
|
||||
}
|
||||
|
||||
func (p *parser) parseInnerJar(zf *zip.File, rootPath string) ([]jarLibrary, error) {
|
||||
fr, err := zf.Open()
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("Failed to open file %s. err: %w", zf.Name, err)
|
||||
}
|
||||
|
||||
f, err := os.CreateTemp("", "inner-*")
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("Failed to create tmp file for %s. err: %w", zf.Name, err)
|
||||
}
|
||||
defer func() {
|
||||
f.Close()
|
||||
os.Remove(f.Name())
|
||||
}()
|
||||
|
||||
// Copy the file content to the temp file and rewind it at the beginning
|
||||
if _, err = io.Copy(f, fr); err != nil {
|
||||
return nil, xerrors.Errorf("Failed to copy file %s. err: %w", zf.Name, err)
|
||||
}
|
||||
if _, err = f.Seek(0, io.SeekStart); err != nil {
|
||||
return nil, xerrors.Errorf("Failed to seek file %s. err: %w", zf.Name, err)
|
||||
}
|
||||
|
||||
// build full path to inner jar
|
||||
fullPath := path.Join(rootPath, zf.Name)
|
||||
|
||||
// Parse jar/war/ear recursively
|
||||
innerLibs, err := p.parseArtifact(fullPath, int64(zf.UncompressedSize64), f)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("Failed to parse file %s. err: %w", zf.Name, err)
|
||||
}
|
||||
|
||||
return innerLibs, nil
|
||||
}
|
||||
|
||||
func isArtifact(name string) bool {
|
||||
ext := filepath.Ext(name)
|
||||
if ext == ".jar" || ext == ".ear" || ext == ".war" {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func parseFileName(filePath string, sha1 digest.Digest) properties {
|
||||
fileName := filepath.Base(filePath)
|
||||
packageVersion := jarFileRegEx.FindStringSubmatch(fileName)
|
||||
if len(packageVersion) != 3 {
|
||||
return properties{
|
||||
filePath: filePath,
|
||||
digest: sha1,
|
||||
}
|
||||
}
|
||||
|
||||
return properties{
|
||||
artifactID: packageVersion[1],
|
||||
version: packageVersion[2],
|
||||
filePath: filePath,
|
||||
digest: sha1,
|
||||
}
|
||||
}
|
||||
|
||||
func parsePomProperties(f *zip.File, filePath string) (properties, error) {
|
||||
file, err := f.Open()
|
||||
if err != nil {
|
||||
return properties{}, xerrors.Errorf("Failed to open pom.properties. err: %w", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
p := properties{
|
||||
filePath: filePath,
|
||||
}
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
line := strings.TrimSpace(scanner.Text())
|
||||
switch {
|
||||
case strings.HasPrefix(line, "groupId="):
|
||||
p.groupID = strings.TrimPrefix(line, "groupId=")
|
||||
case strings.HasPrefix(line, "artifactId="):
|
||||
p.artifactID = strings.TrimPrefix(line, "artifactId=")
|
||||
case strings.HasPrefix(line, "version="):
|
||||
p.version = strings.TrimPrefix(line, "version=")
|
||||
}
|
||||
}
|
||||
|
||||
if err = scanner.Err(); err != nil {
|
||||
return properties{}, xerrors.Errorf("Failed to scan %s. err: %w", f.Name, err)
|
||||
}
|
||||
return p, nil
|
||||
}
|
||||
|
||||
type manifest struct {
|
||||
implementationVersion string
|
||||
implementationTitle string
|
||||
implementationVendor string
|
||||
implementationVendorID string
|
||||
specificationTitle string
|
||||
specificationVersion string
|
||||
specificationVendor string
|
||||
bundleName string
|
||||
bundleVersion string
|
||||
bundleSymbolicName string
|
||||
}
|
||||
|
||||
func parseManifest(f *zip.File) (manifest, error) {
|
||||
file, err := f.Open()
|
||||
if err != nil {
|
||||
return manifest{}, xerrors.Errorf("Failed to open MANIFEST.MF. err: %w", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
var m manifest
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
|
||||
// Skip variables. e.g. Bundle-Name: %bundleName
|
||||
ss := strings.Fields(line)
|
||||
if len(ss) <= 1 || (len(ss) > 1 && strings.HasPrefix(ss[1], "%")) {
|
||||
continue
|
||||
}
|
||||
|
||||
// It is not determined which fields are present in each application.
|
||||
// In some cases, none of them are included, in which case they cannot be detected.
|
||||
switch {
|
||||
case strings.HasPrefix(line, "Implementation-Version:"):
|
||||
m.implementationVersion = strings.TrimPrefix(line, "Implementation-Version:")
|
||||
case strings.HasPrefix(line, "Implementation-Title:"):
|
||||
m.implementationTitle = strings.TrimPrefix(line, "Implementation-Title:")
|
||||
case strings.HasPrefix(line, "Implementation-Vendor:"):
|
||||
m.implementationVendor = strings.TrimPrefix(line, "Implementation-Vendor:")
|
||||
case strings.HasPrefix(line, "Implementation-Vendor-Id:"):
|
||||
m.implementationVendorID = strings.TrimPrefix(line, "Implementation-Vendor-Id:")
|
||||
case strings.HasPrefix(line, "Specification-Version:"):
|
||||
m.specificationVersion = strings.TrimPrefix(line, "Specification-Version:")
|
||||
case strings.HasPrefix(line, "Specification-Title:"):
|
||||
m.specificationTitle = strings.TrimPrefix(line, "Specification-Title:")
|
||||
case strings.HasPrefix(line, "Specification-Vendor:"):
|
||||
m.specificationVendor = strings.TrimPrefix(line, "Specification-Vendor:")
|
||||
case strings.HasPrefix(line, "Bundle-Version:"):
|
||||
m.bundleVersion = strings.TrimPrefix(line, "Bundle-Version:")
|
||||
case strings.HasPrefix(line, "Bundle-Name:"):
|
||||
m.bundleName = strings.TrimPrefix(line, "Bundle-Name:")
|
||||
case strings.HasPrefix(line, "Bundle-SymbolicName:"):
|
||||
m.bundleSymbolicName = strings.TrimPrefix(line, "Bundle-SymbolicName:")
|
||||
}
|
||||
}
|
||||
|
||||
if err = scanner.Err(); err != nil {
|
||||
return manifest{}, xerrors.Errorf("Failed to scan %s. err: %w", f.Name, err)
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (m manifest) properties(filePath string, sha1 digest.Digest) properties {
|
||||
groupID, err := m.determineGroupID()
|
||||
if err != nil {
|
||||
return properties{}
|
||||
}
|
||||
|
||||
artifactID, err := m.determineArtifactID()
|
||||
if err != nil {
|
||||
return properties{}
|
||||
}
|
||||
|
||||
version, err := m.determineVersion()
|
||||
if err != nil {
|
||||
return properties{}
|
||||
}
|
||||
|
||||
return properties{
|
||||
groupID: groupID,
|
||||
artifactID: artifactID,
|
||||
version: version,
|
||||
filePath: filePath,
|
||||
digest: sha1,
|
||||
}
|
||||
}
|
||||
|
||||
func (m manifest) determineGroupID() (string, error) {
|
||||
var groupID string
|
||||
switch {
|
||||
case m.implementationVendorID != "":
|
||||
groupID = m.implementationVendorID
|
||||
case m.bundleSymbolicName != "":
|
||||
groupID = m.bundleSymbolicName
|
||||
|
||||
// e.g. "com.fasterxml.jackson.core.jackson-databind" => "com.fasterxml.jackson.core"
|
||||
idx := strings.LastIndex(m.bundleSymbolicName, ".")
|
||||
if idx > 0 {
|
||||
groupID = m.bundleSymbolicName[:idx]
|
||||
}
|
||||
case m.implementationVendor != "":
|
||||
groupID = m.implementationVendor
|
||||
case m.specificationVendor != "":
|
||||
groupID = m.specificationVendor
|
||||
default:
|
||||
return "", xerrors.New("No groupID found")
|
||||
}
|
||||
return strings.TrimSpace(groupID), nil
|
||||
}
|
||||
|
||||
func (m manifest) determineArtifactID() (string, error) {
|
||||
var artifactID string
|
||||
switch {
|
||||
case m.implementationTitle != "":
|
||||
artifactID = m.implementationTitle
|
||||
case m.specificationTitle != "":
|
||||
artifactID = m.specificationTitle
|
||||
case m.bundleName != "":
|
||||
artifactID = m.bundleName
|
||||
default:
|
||||
return "", xerrors.New("No artifactID found")
|
||||
}
|
||||
return strings.TrimSpace(artifactID), nil
|
||||
}
|
||||
|
||||
func (m manifest) determineVersion() (string, error) {
|
||||
var version string
|
||||
switch {
|
||||
case m.implementationVersion != "":
|
||||
version = m.implementationVersion
|
||||
case m.specificationVersion != "":
|
||||
version = m.specificationVersion
|
||||
case m.bundleVersion != "":
|
||||
version = m.bundleVersion
|
||||
default:
|
||||
return "", xerrors.New("No version found")
|
||||
}
|
||||
return strings.TrimSpace(version), nil
|
||||
}
|
||||
|
||||
func removeLibraryDuplicates(libs []jarLibrary) []jarLibrary {
|
||||
return lo.UniqBy(libs, func(lib jarLibrary) string {
|
||||
return fmt.Sprintf("%s::%s::%s", lib.name, lib.version, lib.filePath)
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user