mirror of
https://github.com/aquasecurity/trivy.git
synced 2025-12-21 14:50:53 -08:00
feat(java): use trivy-java-db to get GAV (#3484)
Co-authored-by: knqyf263 <knqyf263@gmail.com>
This commit is contained in:
167
pkg/javadb/client.go
Normal file
167
pkg/javadb/client.go
Normal file
@@ -0,0 +1,167 @@
|
||||
package javadb
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"time"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/aquasecurity/go-dep-parser/pkg/java/jar"
|
||||
"github.com/aquasecurity/trivy-java-db/pkg/db"
|
||||
"github.com/aquasecurity/trivy-java-db/pkg/metadata"
|
||||
"github.com/aquasecurity/trivy-java-db/pkg/types"
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
"github.com/aquasecurity/trivy/pkg/oci"
|
||||
)
|
||||
|
||||
const (
|
||||
version = 1
|
||||
defaultJavaDBRepository = "ghcr.io/aquasecurity/trivy-java-db"
|
||||
mediaType = "application/vnd.aquasec.trivy.javadb.layer.v1.tar+gzip"
|
||||
)
|
||||
|
||||
var updater *Updater
|
||||
|
||||
type Updater struct {
|
||||
repo string
|
||||
dbDir string
|
||||
skip bool
|
||||
quiet bool
|
||||
insecure bool
|
||||
}
|
||||
|
||||
func (u *Updater) Update() error {
|
||||
dbDir := u.dbDir
|
||||
metac := metadata.New(dbDir)
|
||||
|
||||
meta, err := metac.Get()
|
||||
if err != nil {
|
||||
if !errors.Is(err, os.ErrNotExist) {
|
||||
return xerrors.Errorf("Java DB metadata error: %w", err)
|
||||
} else if u.skip {
|
||||
log.Logger.Error("The first run cannot skip downloading java DB")
|
||||
return xerrors.New("--skip-java-update cannot be specified on the first run")
|
||||
}
|
||||
}
|
||||
|
||||
if (meta.Version != version || meta.NextUpdate.Before(time.Now().UTC())) && !u.skip {
|
||||
// Download DB
|
||||
log.Logger.Info("Downloading the Java DB...")
|
||||
|
||||
var a *oci.Artifact
|
||||
if a, err = oci.NewArtifact(u.repo, mediaType, u.quiet, u.insecure); err != nil {
|
||||
return xerrors.Errorf("oci error: %w", err)
|
||||
}
|
||||
if err = a.Download(context.Background(), dbDir); err != nil {
|
||||
return xerrors.Errorf("DB download error: %w", err)
|
||||
}
|
||||
|
||||
// Parse the newly downloaded metadata.json
|
||||
meta, err = metac.Get()
|
||||
if err != nil {
|
||||
return xerrors.Errorf("Java DB metadata error: %w", err)
|
||||
}
|
||||
|
||||
// Update DownloadedAt
|
||||
meta.DownloadedAt = time.Now().UTC()
|
||||
if err = metac.Update(meta); err != nil {
|
||||
return xerrors.Errorf("Java DB metadata update error: %w", err)
|
||||
}
|
||||
log.Logger.Info("The Java DB is cached for 3 days. If you want to update the database more frequently, " +
|
||||
"the '--reset' flag clears the DB cache.")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func Init(cacheDir string, skip, quiet, insecure bool) {
|
||||
updater = &Updater{
|
||||
repo: fmt.Sprintf("%s:%d", defaultJavaDBRepository, version), // TODO: make it configurable
|
||||
dbDir: filepath.Join(cacheDir, "java-db"),
|
||||
skip: skip,
|
||||
quiet: quiet,
|
||||
insecure: insecure,
|
||||
}
|
||||
}
|
||||
|
||||
func Update() error {
|
||||
if updater == nil {
|
||||
return xerrors.New("not initialized")
|
||||
}
|
||||
if err := updater.Update(); err != nil {
|
||||
return xerrors.Errorf("Java DB update error: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type DB struct {
|
||||
driver db.DB
|
||||
}
|
||||
|
||||
func NewClient() (*DB, error) {
|
||||
if err := Update(); err != nil {
|
||||
return nil, xerrors.Errorf("Java DB update failed: %s", err)
|
||||
}
|
||||
|
||||
dbc, err := db.New(updater.dbDir)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("Java DB open error: %w", err)
|
||||
}
|
||||
|
||||
return &DB{driver: dbc}, nil
|
||||
}
|
||||
|
||||
func (d *DB) Exists(groupID, artifactID string) (bool, error) {
|
||||
index, err := d.driver.SelectIndexByArtifactIDAndGroupID(groupID, artifactID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return index.ArtifactID != "", nil
|
||||
}
|
||||
|
||||
func (d *DB) SearchBySHA1(sha1 string) (jar.Properties, error) {
|
||||
index, err := d.driver.SelectIndexBySha1(sha1)
|
||||
if err != nil {
|
||||
return jar.Properties{}, xerrors.Errorf("select error: %w", err)
|
||||
} else if index.ArtifactID == "" {
|
||||
return jar.Properties{}, xerrors.Errorf("digest %s: %w", sha1, jar.ArtifactNotFoundErr)
|
||||
}
|
||||
return jar.Properties{
|
||||
GroupID: index.GroupID,
|
||||
ArtifactID: index.ArtifactID,
|
||||
Version: index.Version,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *DB) SearchByArtifactID(artifactID string) (string, error) {
|
||||
indexes, err := d.driver.SelectIndexesByArtifactIDAndFileType(artifactID, types.JarType)
|
||||
if err != nil {
|
||||
return "", xerrors.Errorf("select error: %w", err)
|
||||
} else if len(indexes) == 0 {
|
||||
return "", xerrors.Errorf("artifactID %s: %w", artifactID, jar.ArtifactNotFoundErr)
|
||||
}
|
||||
|
||||
// Some artifacts might have the same artifactId.
|
||||
// e.g. "javax.servlet:jstl" and "jstl:jstl"
|
||||
groupIDs := map[string]int{}
|
||||
for _, index := range indexes {
|
||||
if i, ok := groupIDs[index.GroupID]; ok {
|
||||
groupIDs[index.GroupID] = i + 1
|
||||
continue
|
||||
}
|
||||
groupIDs[index.GroupID] = 1
|
||||
}
|
||||
maxCount := 0
|
||||
var groupID string
|
||||
for k, v := range groupIDs {
|
||||
if v > maxCount {
|
||||
groupID = k
|
||||
}
|
||||
}
|
||||
|
||||
return groupID, nil
|
||||
}
|
||||
Reference in New Issue
Block a user