github.com/devseccon/trivy@v0.47.1-0.20231123133102-bd902a0bd996/pkg/javadb/client.go (about)

     1  package javadb
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"os"
     8  	"path/filepath"
     9  	"sort"
    10  	"sync"
    11  	"time"
    12  
    13  	"golang.org/x/xerrors"
    14  
    15  	"github.com/aquasecurity/go-dep-parser/pkg/java/jar"
    16  	"github.com/aquasecurity/trivy-java-db/pkg/db"
    17  	"github.com/aquasecurity/trivy-java-db/pkg/types"
    18  	ftypes "github.com/devseccon/trivy/pkg/fanal/types"
    19  	"github.com/devseccon/trivy/pkg/log"
    20  	"github.com/devseccon/trivy/pkg/oci"
    21  )
    22  
    23  const (
    24  	mediaType = "application/vnd.aquasec.trivy.javadb.layer.v1.tar+gzip"
    25  )
    26  
    27  var updater *Updater
    28  
    29  type Updater struct {
    30  	repo           string
    31  	dbDir          string
    32  	skip           bool
    33  	quiet          bool
    34  	registryOption ftypes.RegistryOptions
    35  	once           sync.Once // we need to update java-db once per run
    36  }
    37  
    38  func (u *Updater) Update() error {
    39  	dbDir := u.dbDir
    40  	metac := db.NewMetadata(dbDir)
    41  
    42  	meta, err := metac.Get()
    43  	if err != nil {
    44  		if !errors.Is(err, os.ErrNotExist) {
    45  			return xerrors.Errorf("Java DB metadata error: %w", err)
    46  		} else if u.skip {
    47  			log.Logger.Error("The first run cannot skip downloading Java DB")
    48  			return xerrors.New("'--skip-java-db-update' cannot be specified on the first run")
    49  		}
    50  	}
    51  
    52  	if (meta.Version != db.SchemaVersion || meta.NextUpdate.Before(time.Now().UTC())) && !u.skip {
    53  		// Download DB
    54  		log.Logger.Infof("Java DB Repository: %s", u.repo)
    55  		log.Logger.Info("Downloading the Java DB...")
    56  
    57  		// TODO: support remote options
    58  		var a *oci.Artifact
    59  		if a, err = oci.NewArtifact(u.repo, u.quiet, u.registryOption); err != nil {
    60  			return xerrors.Errorf("oci error: %w", err)
    61  		}
    62  		if err = a.Download(context.Background(), dbDir, oci.DownloadOption{MediaType: mediaType}); err != nil {
    63  			return xerrors.Errorf("DB download error: %w", err)
    64  		}
    65  
    66  		// Parse the newly downloaded metadata.json
    67  		meta, err = metac.Get()
    68  		if err != nil {
    69  			return xerrors.Errorf("Java DB metadata error: %w", err)
    70  		}
    71  
    72  		// Update DownloadedAt
    73  		meta.DownloadedAt = time.Now().UTC()
    74  		if err = metac.Update(meta); err != nil {
    75  			return xerrors.Errorf("Java DB metadata update error: %w", err)
    76  		}
    77  		log.Logger.Info("The Java DB is cached for 3 days. If you want to update the database more frequently, " +
    78  			"the '--reset' flag clears the DB cache.")
    79  	}
    80  
    81  	return nil
    82  }
    83  
    84  func Init(cacheDir, javaDBRepository string, skip, quiet bool, registryOption ftypes.RegistryOptions) {
    85  	updater = &Updater{
    86  		repo:           fmt.Sprintf("%s:%d", javaDBRepository, db.SchemaVersion),
    87  		dbDir:          filepath.Join(cacheDir, "java-db"),
    88  		skip:           skip,
    89  		quiet:          quiet,
    90  		registryOption: registryOption,
    91  	}
    92  }
    93  
    94  func Update() error {
    95  	if updater == nil {
    96  		return xerrors.New("Java DB client not initialized")
    97  	}
    98  
    99  	var err error
   100  	updater.once.Do(func() {
   101  		err = updater.Update()
   102  	})
   103  	return err
   104  }
   105  
   106  type DB struct {
   107  	driver db.DB
   108  }
   109  
   110  func NewClient() (*DB, error) {
   111  	if err := Update(); err != nil {
   112  		return nil, xerrors.Errorf("Java DB update failed: %s", err)
   113  	}
   114  
   115  	dbc, err := db.New(updater.dbDir)
   116  	if err != nil {
   117  		return nil, xerrors.Errorf("Java DB open error: %w", err)
   118  	}
   119  
   120  	return &DB{driver: dbc}, nil
   121  }
   122  
   123  func (d *DB) Exists(groupID, artifactID string) (bool, error) {
   124  	index, err := d.driver.SelectIndexByArtifactIDAndGroupID(artifactID, groupID)
   125  	if err != nil {
   126  		return false, err
   127  	}
   128  	return index.ArtifactID != "", nil
   129  }
   130  
   131  func (d *DB) SearchBySHA1(sha1 string) (jar.Properties, error) {
   132  	index, err := d.driver.SelectIndexBySha1(sha1)
   133  	if err != nil {
   134  		return jar.Properties{}, xerrors.Errorf("select error: %w", err)
   135  	} else if index.ArtifactID == "" {
   136  		return jar.Properties{}, xerrors.Errorf("digest %s: %w", sha1, jar.ArtifactNotFoundErr)
   137  	}
   138  	return jar.Properties{
   139  		GroupID:    index.GroupID,
   140  		ArtifactID: index.ArtifactID,
   141  		Version:    index.Version,
   142  	}, nil
   143  }
   144  
   145  func (d *DB) SearchByArtifactID(artifactID string) (string, error) {
   146  	indexes, err := d.driver.SelectIndexesByArtifactIDAndFileType(artifactID, types.JarType)
   147  	if err != nil {
   148  		return "", xerrors.Errorf("select error: %w", err)
   149  	} else if len(indexes) == 0 {
   150  		return "", xerrors.Errorf("artifactID %s: %w", artifactID, jar.ArtifactNotFoundErr)
   151  	}
   152  	sort.Slice(indexes, func(i, j int) bool {
   153  		return indexes[i].GroupID < indexes[j].GroupID
   154  	})
   155  
   156  	// Some artifacts might have the same artifactId.
   157  	// e.g. "javax.servlet:jstl" and "jstl:jstl"
   158  	groupIDs := make(map[string]int)
   159  	for _, index := range indexes {
   160  		if i, ok := groupIDs[index.GroupID]; ok {
   161  			groupIDs[index.GroupID] = i + 1
   162  			continue
   163  		}
   164  		groupIDs[index.GroupID] = 1
   165  	}
   166  	maxCount := 0
   167  	var groupID string
   168  	for k, v := range groupIDs {
   169  		if v > maxCount {
   170  			maxCount = v
   171  			groupID = k
   172  		}
   173  	}
   174  
   175  	return groupID, nil
   176  }
   177  
   178  func (d *DB) Close() error {
   179  	if d == nil {
   180  		return nil
   181  	}
   182  	return d.driver.Close()
   183  }