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 }