feat(java): use trivy-java-db to get GAV (#3484)

Co-authored-by: knqyf263 <knqyf263@gmail.com>
This commit is contained in:
DmitriyLewen
2023-02-01 12:48:05 +03:00
committed by GitHub
parent 023e45b896
commit 7bf1e192ec
20 changed files with 458 additions and 152 deletions

167
pkg/javadb/client.go Normal file
View 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
}