feat: support multiple DB repositories for vulnerability and Java DB (#7605)

Signed-off-by: nikpivkin <nikita.pivkin@smartforce.io>
This commit is contained in:
Nikita Pivkin
2024-10-01 19:16:06 +06:00
committed by GitHub
parent 7602d14654
commit 3562529ddf
28 changed files with 301 additions and 203 deletions

View File

@@ -29,7 +29,7 @@ trivy filesystem [flags] PATH
--config-data strings specify paths from which data for the Rego checks will be recursively loaded
--config-file-schemas strings specify paths to JSON configuration file schemas to determine that a file matches some configuration and pass the schema to Rego checks for type checking
--custom-headers strings custom headers in client mode
--db-repository string OCI repository to retrieve trivy-db from (default "ghcr.io/aquasecurity/trivy-db:2")
--db-repository strings OCI repository(ies) to retrieve trivy-db in order of priority (default [ghcr.io/aquasecurity/trivy-db:2])
--dependency-tree [EXPERIMENTAL] show dependency origin tree of vulnerable packages
--detection-priority string specify the detection priority:
- "precise": Prioritizes precise by minimizing false positives.
@@ -56,7 +56,7 @@ trivy filesystem [flags] PATH
--include-deprecated-checks include deprecated checks (default true)
--include-dev-deps include development dependencies in the report (supported: npm, yarn)
--include-non-failures include successes and exceptions, available with '--scanners misconfig'
--java-db-repository string OCI repository to retrieve trivy-java-db from (default "ghcr.io/aquasecurity/trivy-java-db:1")
--java-db-repository strings OCI repository(ies) to retrieve trivy-java-db in order of priority (default [ghcr.io/aquasecurity/trivy-java-db:1])
--license-confidence-level float specify license classifier's confidence level (default 0.9)
--license-full eagerly look for licenses in source code headers and license files
--list-all-pkgs output all packages in the JSON report regardless of vulnerability

View File

@@ -43,7 +43,7 @@ trivy image [flags] IMAGE_NAME
--config-data strings specify paths from which data for the Rego checks will be recursively loaded
--config-file-schemas strings specify paths to JSON configuration file schemas to determine that a file matches some configuration and pass the schema to Rego checks for type checking
--custom-headers strings custom headers in client mode
--db-repository string OCI repository to retrieve trivy-db from (default "ghcr.io/aquasecurity/trivy-db:2")
--db-repository strings OCI repository(ies) to retrieve trivy-db in order of priority (default [ghcr.io/aquasecurity/trivy-db:2])
--dependency-tree [EXPERIMENTAL] show dependency origin tree of vulnerable packages
--detection-priority string specify the detection priority:
- "precise": Prioritizes precise by minimizing false positives.
@@ -74,7 +74,7 @@ trivy image [flags] IMAGE_NAME
--include-deprecated-checks include deprecated checks (default true)
--include-non-failures include successes and exceptions, available with '--scanners misconfig'
--input string input file path instead of image name
--java-db-repository string OCI repository to retrieve trivy-java-db from (default "ghcr.io/aquasecurity/trivy-java-db:1")
--java-db-repository strings OCI repository(ies) to retrieve trivy-java-db in order of priority (default [ghcr.io/aquasecurity/trivy-java-db:1])
--license-confidence-level float specify license classifier's confidence level (default 0.9)
--license-full eagerly look for licenses in source code headers and license files
--list-all-pkgs output all packages in the JSON report regardless of vulnerability

View File

@@ -38,7 +38,7 @@ trivy kubernetes [flags] [CONTEXT]
--config-check strings specify the paths to the Rego check files or to the directories containing them, applying config files
--config-data strings specify paths from which data for the Rego checks will be recursively loaded
--config-file-schemas strings specify paths to JSON configuration file schemas to determine that a file matches some configuration and pass the schema to Rego checks for type checking
--db-repository string OCI repository to retrieve trivy-db from (default "ghcr.io/aquasecurity/trivy-db:2")
--db-repository strings OCI repository(ies) to retrieve trivy-db in order of priority (default [ghcr.io/aquasecurity/trivy-db:2])
--dependency-tree [EXPERIMENTAL] show dependency origin tree of vulnerable packages
--detection-priority string specify the detection priority:
- "precise": Prioritizes precise by minimizing false positives.
@@ -70,7 +70,7 @@ trivy kubernetes [flags] [CONTEXT]
--include-kinds strings indicate the kinds included in scanning (example: node)
--include-namespaces strings indicate the namespaces included in scanning (example: kube-system)
--include-non-failures include successes and exceptions, available with '--scanners misconfig'
--java-db-repository string OCI repository to retrieve trivy-java-db from (default "ghcr.io/aquasecurity/trivy-java-db:1")
--java-db-repository strings OCI repository(ies) to retrieve trivy-java-db in order of priority (default [ghcr.io/aquasecurity/trivy-java-db:1])
--k8s-version string specify k8s version to validate outdated api by it (example: 1.21.0)
--kubeconfig string specify the kubeconfig file path to use
--list-all-pkgs output all packages in the JSON report regardless of vulnerability

View File

@@ -29,7 +29,7 @@ trivy repository [flags] (REPO_PATH | REPO_URL)
--config-data strings specify paths from which data for the Rego checks will be recursively loaded
--config-file-schemas strings specify paths to JSON configuration file schemas to determine that a file matches some configuration and pass the schema to Rego checks for type checking
--custom-headers strings custom headers in client mode
--db-repository string OCI repository to retrieve trivy-db from (default "ghcr.io/aquasecurity/trivy-db:2")
--db-repository strings OCI repository(ies) to retrieve trivy-db in order of priority (default [ghcr.io/aquasecurity/trivy-db:2])
--dependency-tree [EXPERIMENTAL] show dependency origin tree of vulnerable packages
--detection-priority string specify the detection priority:
- "precise": Prioritizes precise by minimizing false positives.
@@ -56,7 +56,7 @@ trivy repository [flags] (REPO_PATH | REPO_URL)
--include-deprecated-checks include deprecated checks (default true)
--include-dev-deps include development dependencies in the report (supported: npm, yarn)
--include-non-failures include successes and exceptions, available with '--scanners misconfig'
--java-db-repository string OCI repository to retrieve trivy-java-db from (default "ghcr.io/aquasecurity/trivy-java-db:1")
--java-db-repository strings OCI repository(ies) to retrieve trivy-java-db in order of priority (default [ghcr.io/aquasecurity/trivy-java-db:1])
--license-confidence-level float specify license classifier's confidence level (default 0.9)
--license-full eagerly look for licenses in source code headers and license files
--list-all-pkgs output all packages in the JSON report regardless of vulnerability

View File

@@ -31,7 +31,7 @@ trivy rootfs [flags] ROOTDIR
--config-data strings specify paths from which data for the Rego checks will be recursively loaded
--config-file-schemas strings specify paths to JSON configuration file schemas to determine that a file matches some configuration and pass the schema to Rego checks for type checking
--custom-headers strings custom headers in client mode
--db-repository string OCI repository to retrieve trivy-db from (default "ghcr.io/aquasecurity/trivy-db:2")
--db-repository strings OCI repository(ies) to retrieve trivy-db in order of priority (default [ghcr.io/aquasecurity/trivy-db:2])
--dependency-tree [EXPERIMENTAL] show dependency origin tree of vulnerable packages
--detection-priority string specify the detection priority:
- "precise": Prioritizes precise by minimizing false positives.
@@ -58,7 +58,7 @@ trivy rootfs [flags] ROOTDIR
--ignorefile string specify .trivyignore file (default ".trivyignore")
--include-deprecated-checks include deprecated checks (default true)
--include-non-failures include successes and exceptions, available with '--scanners misconfig'
--java-db-repository string OCI repository to retrieve trivy-java-db from (default "ghcr.io/aquasecurity/trivy-java-db:1")
--java-db-repository strings OCI repository(ies) to retrieve trivy-java-db in order of priority (default [ghcr.io/aquasecurity/trivy-java-db:1])
--license-confidence-level float specify license classifier's confidence level (default 0.9)
--license-full eagerly look for licenses in source code headers and license files
--list-all-pkgs output all packages in the JSON report regardless of vulnerability

View File

@@ -20,54 +20,54 @@ trivy sbom [flags] SBOM_PATH
### Options
```
--cache-backend string [EXPERIMENTAL] cache backend (e.g. redis://localhost:6379) (default "memory")
--cache-ttl duration cache TTL when using redis as cache backend
--compliance string compliance report to generate
--custom-headers strings custom headers in client mode
--db-repository string OCI repository to retrieve trivy-db from (default "ghcr.io/aquasecurity/trivy-db:2")
--detection-priority string specify the detection priority:
- "precise": Prioritizes precise by minimizing false positives.
- "comprehensive": Aims to detect more security findings at the cost of potential false positives.
(precise,comprehensive) (default "precise")
--download-db-only download/update vulnerability database but don't run a scan
--download-java-db-only download/update Java index database but don't run a scan
--exit-code int specify exit code when any security issues are found
--exit-on-eol int exit with the specified code when the OS reaches end of service/life
--file-patterns strings specify config file patterns
-f, --format string format (table,json,template,sarif,cyclonedx,spdx,spdx-json,github,cosign-vuln) (default "table")
-h, --help help for sbom
--ignore-policy string specify the Rego file path to evaluate each vulnerability
--ignore-status strings comma-separated list of vulnerability status to ignore (unknown,not_affected,affected,fixed,under_investigation,will_not_fix,fix_deferred,end_of_life)
--ignore-unfixed display only fixed vulnerabilities
--ignored-licenses strings specify a list of license to ignore
--ignorefile string specify .trivyignore file (default ".trivyignore")
--java-db-repository string OCI repository to retrieve trivy-java-db from (default "ghcr.io/aquasecurity/trivy-java-db:1")
--list-all-pkgs output all packages in the JSON report regardless of vulnerability
--no-progress suppress progress bar
--offline-scan do not issue API requests to identify dependencies
-o, --output string output file name
--output-plugin-arg string [EXPERIMENTAL] output plugin arguments
--pkg-relationships strings list of package relationships (unknown,root,direct,indirect) (default [unknown,root,direct,indirect])
--pkg-types strings list of package types (os,library) (default [os,library])
--redis-ca string redis ca file location, if using redis as cache backend
--redis-cert string redis certificate file location, if using redis as cache backend
--redis-key string redis key file location, if using redis as cache backend
--redis-tls enable redis TLS with public certificates, if using redis as cache backend
--rekor-url string [EXPERIMENTAL] address of rekor STL server (default "https://rekor.sigstore.dev")
--sbom-sources strings [EXPERIMENTAL] try to retrieve SBOM from the specified sources (oci,rekor)
--scanners strings comma-separated list of what security issues to detect (vuln,license) (default [vuln])
--server string server address in client mode
-s, --severity strings severities of security issues to be displayed (UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL) (default [UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL])
--show-suppressed [EXPERIMENTAL] show suppressed vulnerabilities
--skip-db-update skip updating vulnerability database
--skip-dirs strings specify the directories or glob patterns to skip
--skip-files strings specify the files or glob patterns to skip
--skip-java-db-update skip updating Java index database
--skip-vex-repo-update [EXPERIMENTAL] Skip VEX Repository update
-t, --template string output template
--token string for authentication in client/server mode
--token-header string specify a header name for token in client/server mode (default "Trivy-Token")
--vex strings [EXPERIMENTAL] VEX sources ("repo", "oci" or file path)
--cache-backend string [EXPERIMENTAL] cache backend (e.g. redis://localhost:6379) (default "memory")
--cache-ttl duration cache TTL when using redis as cache backend
--compliance string compliance report to generate
--custom-headers strings custom headers in client mode
--db-repository strings OCI repository(ies) to retrieve trivy-db in order of priority (default [ghcr.io/aquasecurity/trivy-db:2])
--detection-priority string specify the detection priority:
- "precise": Prioritizes precise by minimizing false positives.
- "comprehensive": Aims to detect more security findings at the cost of potential false positives.
(precise,comprehensive) (default "precise")
--download-db-only download/update vulnerability database but don't run a scan
--download-java-db-only download/update Java index database but don't run a scan
--exit-code int specify exit code when any security issues are found
--exit-on-eol int exit with the specified code when the OS reaches end of service/life
--file-patterns strings specify config file patterns
-f, --format string format (table,json,template,sarif,cyclonedx,spdx,spdx-json,github,cosign-vuln) (default "table")
-h, --help help for sbom
--ignore-policy string specify the Rego file path to evaluate each vulnerability
--ignore-status strings comma-separated list of vulnerability status to ignore (unknown,not_affected,affected,fixed,under_investigation,will_not_fix,fix_deferred,end_of_life)
--ignore-unfixed display only fixed vulnerabilities
--ignored-licenses strings specify a list of license to ignore
--ignorefile string specify .trivyignore file (default ".trivyignore")
--java-db-repository strings OCI repository(ies) to retrieve trivy-java-db in order of priority (default [ghcr.io/aquasecurity/trivy-java-db:1])
--list-all-pkgs output all packages in the JSON report regardless of vulnerability
--no-progress suppress progress bar
--offline-scan do not issue API requests to identify dependencies
-o, --output string output file name
--output-plugin-arg string [EXPERIMENTAL] output plugin arguments
--pkg-relationships strings list of package relationships (unknown,root,direct,indirect) (default [unknown,root,direct,indirect])
--pkg-types strings list of package types (os,library) (default [os,library])
--redis-ca string redis ca file location, if using redis as cache backend
--redis-cert string redis certificate file location, if using redis as cache backend
--redis-key string redis key file location, if using redis as cache backend
--redis-tls enable redis TLS with public certificates, if using redis as cache backend
--rekor-url string [EXPERIMENTAL] address of rekor STL server (default "https://rekor.sigstore.dev")
--sbom-sources strings [EXPERIMENTAL] try to retrieve SBOM from the specified sources (oci,rekor)
--scanners strings comma-separated list of what security issues to detect (vuln,license) (default [vuln])
--server string server address in client mode
-s, --severity strings severities of security issues to be displayed (UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL) (default [UNKNOWN,LOW,MEDIUM,HIGH,CRITICAL])
--show-suppressed [EXPERIMENTAL] show suppressed vulnerabilities
--skip-db-update skip updating vulnerability database
--skip-dirs strings specify the directories or glob patterns to skip
--skip-files strings specify the files or glob patterns to skip
--skip-java-db-update skip updating Java index database
--skip-vex-repo-update [EXPERIMENTAL] Skip VEX Repository update
-t, --template string output template
--token string for authentication in client/server mode
--token-header string specify a header name for token in client/server mode (default "Trivy-Token")
--vex strings [EXPERIMENTAL] VEX sources ("repo", "oci" or file path)
```
### Options inherited from parent commands

View File

@@ -22,7 +22,7 @@ trivy server [flags]
```
--cache-backend string [EXPERIMENTAL] cache backend (e.g. redis://localhost:6379) (default "fs")
--cache-ttl duration cache TTL when using redis as cache backend
--db-repository string OCI repository to retrieve trivy-db from (default "ghcr.io/aquasecurity/trivy-db:2")
--db-repository strings OCI repository(ies) to retrieve trivy-db in order of priority (default [ghcr.io/aquasecurity/trivy-db:2])
--download-db-only download/update vulnerability database but don't run a scan
--enable-modules strings [EXPERIMENTAL] module names to enable
-h, --help help for server

View File

@@ -27,7 +27,7 @@ trivy vm [flags] VM_IMAGE
--compliance string compliance report to generate
--config-file-schemas strings specify paths to JSON configuration file schemas to determine that a file matches some configuration and pass the schema to Rego checks for type checking
--custom-headers strings custom headers in client mode
--db-repository string OCI repository to retrieve trivy-db from (default "ghcr.io/aquasecurity/trivy-db:2")
--db-repository strings OCI repository(ies) to retrieve trivy-db in order of priority (default [ghcr.io/aquasecurity/trivy-db:2])
--dependency-tree [EXPERIMENTAL] show dependency origin tree of vulnerable packages
--detection-priority string specify the detection priority:
- "precise": Prioritizes precise by minimizing false positives.
@@ -52,7 +52,7 @@ trivy vm [flags] VM_IMAGE
--ignore-unfixed display only fixed vulnerabilities
--ignorefile string specify .trivyignore file (default ".trivyignore")
--include-non-failures include successes and exceptions, available with '--scanners misconfig'
--java-db-repository string OCI repository to retrieve trivy-java-db from (default "ghcr.io/aquasecurity/trivy-java-db:1")
--java-db-repository strings OCI repository(ies) to retrieve trivy-java-db in order of priority (default [ghcr.io/aquasecurity/trivy-java-db:1])
--list-all-pkgs output all packages in the JSON report regardless of vulnerability
--misconfig-scanners strings comma-separated list of misconfig scanners to use for misconfiguration scanning (default [azure-arm,cloudformation,dockerfile,helm,kubernetes,terraform,terraformplan-json,terraformplan-snapshot])
--module-dir string specify directory to the wasm modules that will be loaded (default "$HOME/.trivy/modules")

View File

@@ -104,7 +104,8 @@ db:
download-only: false
# Same as '--java-db-repository'
java-repository: "ghcr.io/aquasecurity/trivy-java-db:1"
java-repository:
- ghcr.io/aquasecurity/trivy-java-db:1
# Same as '--skip-java-db-update'
java-skip-update: false
@@ -113,7 +114,8 @@ db:
no-progress: false
# Same as '--db-repository'
repository: "ghcr.io/aquasecurity/trivy-db:2"
repository:
- ghcr.io/aquasecurity/trivy-db:2
# Same as '--skip-db-update'
skip-update: false

View File

@@ -62,7 +62,7 @@ func NewFakeDB(t *testing.T, dbPath string, opts FakeDBOptions) *oci.Artifact {
opt := ftypes.RegistryOptions{
Insecure: false,
}
return oci.NewArtifact("dummy", true, opt, oci.WithImage(img))
return oci.NewArtifact("dummy", opt, oci.WithImage(img))
}
func ArchiveDir(t *testing.T, dir string) string {

View File

@@ -624,7 +624,7 @@ func NewServerCommand(globalFlags *flag.GlobalFlagGroup) *cobra.Command {
// java-db only works on client side.
serverFlags.DBFlagGroup.DownloadJavaDBOnly = nil // disable '--download-java-db-only'
serverFlags.DBFlagGroup.SkipJavaDBUpdate = nil // disable '--skip-java-db-update'
serverFlags.DBFlagGroup.JavaDBRepository = nil // disable '--java-db-repository'
serverFlags.DBFlagGroup.JavaDBRepositories = nil // disable '--java-db-repository'
cmd := &cobra.Command{
Use: "server [flags]",

View File

@@ -291,7 +291,7 @@ func (r *runner) initDB(ctx context.Context, opts flag.Options) error {
// download the database file
noProgress := opts.Quiet || opts.NoProgress
if err := operation.DownloadDB(ctx, opts.AppVersion, opts.CacheDir, opts.DBRepository, noProgress, opts.SkipDBUpdate, opts.RegistryOpts()); err != nil {
if err := operation.DownloadDB(ctx, opts.AppVersion, opts.CacheDir, opts.DBRepositories, noProgress, opts.SkipDBUpdate, opts.RegistryOpts()); err != nil {
return err
}
@@ -321,7 +321,7 @@ func (r *runner) initJavaDB(opts flag.Options) error {
// Update the Java DB
noProgress := opts.Quiet || opts.NoProgress
javadb.Init(opts.CacheDir, opts.JavaDBRepository, opts.SkipJavaDBUpdate, noProgress, opts.RegistryOpts())
javadb.Init(opts.CacheDir, opts.JavaDBRepositories, opts.SkipJavaDBUpdate, noProgress, opts.RegistryOpts())
if opts.DownloadJavaDBOnly {
if err := javadb.Update(); err != nil {
return xerrors.Errorf("Java DB error: %w", err)

View File

@@ -21,14 +21,14 @@ import (
var mu sync.Mutex
// DownloadDB downloads the DB
func DownloadDB(ctx context.Context, appVersion, cacheDir string, dbRepository name.Reference, quiet, skipUpdate bool,
func DownloadDB(ctx context.Context, appVersion, cacheDir string, dbRepositories []name.Reference, quiet, skipUpdate bool,
opt ftypes.RegistryOptions) error {
mu.Lock()
defer mu.Unlock()
ctx = log.WithContextPrefix(ctx, "db")
dbDir := db.Dir(cacheDir)
client := db.NewClient(dbDir, quiet, db.WithDBRepository(dbRepository))
client := db.NewClient(dbDir, quiet, db.WithDBRepository(dbRepositories))
needsUpdate, err := client.NeedsUpdate(ctx, appVersion, skipUpdate)
if err != nil {
return xerrors.Errorf("database error: %w", err)
@@ -36,7 +36,6 @@ func DownloadDB(ctx context.Context, appVersion, cacheDir string, dbRepository n
if needsUpdate {
log.InfoContext(ctx, "Need to update DB")
log.InfoContext(ctx, "Downloading DB...", log.String("repository", dbRepository.String()))
if err = client.Download(ctx, dbDir, opt); err != nil {
return xerrors.Errorf("failed to download vulnerability DB: %w", err)
}

View File

@@ -26,7 +26,7 @@ func Run(ctx context.Context, opts flag.Options) (err error) {
defer cleanup()
// download the database file
if err = operation.DownloadDB(ctx, opts.AppVersion, opts.CacheDir, opts.DBRepository,
if err = operation.DownloadDB(ctx, opts.AppVersion, opts.CacheDir, opts.DBRepositories,
true, opts.SkipDBUpdate, opts.RegistryOpts()); err != nil {
return err
}
@@ -50,6 +50,6 @@ func Run(ctx context.Context, opts flag.Options) (err error) {
m.Register()
server := rpcServer.NewServer(opts.AppVersion, opts.Listen, opts.CacheDir, opts.Token, opts.TokenHeader,
opts.PathPrefix, opts.DBRepository, opts.RegistryOpts())
opts.PathPrefix, opts.DBRepositories, opts.RegistryOpts())
return server.ListenAndServe(ctx, cacheClient, opts.SkipDBUpdate)
}

View File

@@ -2,14 +2,13 @@ package db
import (
"context"
"errors"
"fmt"
"os"
"path/filepath"
"time"
"github.com/google/go-containerregistry/pkg/name"
"github.com/google/go-containerregistry/pkg/v1/remote/transport"
"github.com/samber/lo"
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy-db/pkg/db"
@@ -18,7 +17,6 @@ import (
"github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/oci"
"github.com/aquasecurity/trivy/pkg/version/doc"
)
const (
@@ -27,8 +25,9 @@ const (
)
var (
DefaultRepository = fmt.Sprintf("%s:%d", "ghcr.io/aquasecurity/trivy-db", db.SchemaVersion)
defaultRepository, _ = name.NewTag(DefaultRepository)
// GitHub Container Registry
DefaultGHCRRepository = fmt.Sprintf("%s:%d", "ghcr.io/aquasecurity/trivy-db", db.SchemaVersion)
defaultGHCRRepository = lo.Must(name.NewTag(DefaultGHCRRepository))
Init = db.Init
Close = db.Close
@@ -36,8 +35,8 @@ var (
)
type options struct {
artifact *oci.Artifact
dbRepository name.Reference
artifact *oci.Artifact
dbRepositories []name.Reference
}
// Option is a functional option
@@ -51,9 +50,9 @@ func WithOCIArtifact(art *oci.Artifact) Option {
}
// WithDBRepository takes a dbRepository
func WithDBRepository(dbRepository name.Reference) Option {
func WithDBRepository(dbRepository []name.Reference) Option {
return func(opts *options) {
opts.dbRepository = dbRepository
opts.dbRepositories = dbRepository
}
}
@@ -73,7 +72,9 @@ func Dir(cacheDir string) string {
// NewClient is the factory method for DB client
func NewClient(dbDir string, quiet bool, opts ...Option) *Client {
o := &options{
dbRepository: defaultRepository,
dbRepositories: []name.Reference{
defaultGHCRRepository,
},
}
for _, opt := range opts {
@@ -153,20 +154,8 @@ func (c *Client) Download(ctx context.Context, dst string, opt types.RegistryOpt
log.Debug("No metadata file")
}
art := c.initOCIArtifact(opt)
if err := art.Download(ctx, dst, oci.DownloadOption{MediaType: dbMediaType}); err != nil {
var terr *transport.Error
if errors.As(err, &terr) {
for _, diagnostic := range terr.Errors {
// For better user experience
if diagnostic.Code == transport.DeniedErrorCode || diagnostic.Code == transport.UnauthorizedErrorCode {
// e.g. https://aquasecurity.github.io/trivy/latest/docs/references/troubleshooting/#db
log.Warnf("See %s", doc.URL("/docs/references/troubleshooting/", "db"))
break
}
}
}
return xerrors.Errorf("database download error: %w", err)
if err := c.downloadDB(ctx, opt, dst); err != nil {
return xerrors.Errorf("OCI artifact error: %w", err)
}
if err := c.updateDownloadedAt(ctx, dst); err != nil {
@@ -201,11 +190,20 @@ func (c *Client) updateDownloadedAt(ctx context.Context, dbDir string) error {
return nil
}
func (c *Client) initOCIArtifact(opt types.RegistryOptions) *oci.Artifact {
func (c *Client) initArtifacts(opt types.RegistryOptions) oci.Artifacts {
if c.artifact != nil {
return c.artifact
return oci.Artifacts{c.artifact}
}
return oci.NewArtifact(c.dbRepository.String(), c.quiet, opt)
return oci.NewArtifacts(c.dbRepositories, opt)
}
func (c *Client) downloadDB(ctx context.Context, opt types.RegistryOptions, dst string) error {
log.Info("Downloading vulnerability DB...")
downloadOpt := oci.DownloadOption{MediaType: dbMediaType, Quiet: c.quiet}
if err := c.initArtifacts(opt).Download(ctx, dst, downloadOpt); err != nil {
return xerrors.Errorf("failed to download vulnerability DB: %w", err)
}
return nil
}
func (c *Client) ShowInfo() error {

View File

@@ -159,7 +159,7 @@ func TestClient_Download(t *testing.T) {
{
name: "invalid gzip",
input: "testdata/trivy.db",
wantErr: "unexpected EOF",
wantErr: "OCI artifact error: failed to download vulnerability DB",
},
}

View File

@@ -623,9 +623,9 @@ func TestAnalyzerGroup_PostAnalyze(t *testing.T) {
if tt.analyzerType == analyzer.TypeJar {
// init java-trivy-db with skip update
repo, err := name.NewTag(javadb.DefaultRepository)
repo, err := name.NewTag(javadb.DefaultGHCRRepository)
require.NoError(t, err)
javadb.Init("./language/java/jar/testdata", repo, true, false, types.RegistryOptions{Insecure: false})
javadb.Init("./language/java/jar/testdata", []name.Reference{repo}, true, false, types.RegistryOptions{Insecure: false})
}
ctx := context.Background()

View File

@@ -132,9 +132,9 @@ func Test_javaLibraryAnalyzer_Analyze(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// init java-trivy-db with skip update
repo, err := name.NewTag(javadb.DefaultRepository)
repo, err := name.NewTag(javadb.DefaultGHCRRepository)
require.NoError(t, err)
javadb.Init("testdata", repo, true, false, types.RegistryOptions{Insecure: false})
javadb.Init("testdata", []name.Reference{repo}, true, false, types.RegistryOptions{Insecure: false})
a := javaLibraryAnalyzer{}
ctx := context.Background()

View File

@@ -95,10 +95,11 @@ func (a Artifact) parseReferrer(ctx context.Context, repo string, desc v1.Descri
defer os.RemoveAll(tmpDir)
// Download SBOM to local filesystem
referrer := oci.NewArtifact(repoName, true, a.artifactOption.ImageOption.RegistryOptions)
referrer := oci.NewArtifact(repoName, a.artifactOption.ImageOption.RegistryOptions)
if err = referrer.Download(ctx, tmpDir, oci.DownloadOption{
MediaType: desc.ArtifactType,
Filename: fileName,
Quiet: true,
}); err != nil {
return artifact.Reference{}, xerrors.Errorf("SBOM download error: %w", err)
}

View File

@@ -50,17 +50,17 @@ var (
ConfigName: "db.no-progress",
Usage: "suppress progress bar",
}
DBRepositoryFlag = Flag[string]{
DBRepositoryFlag = Flag[[]string]{
Name: "db-repository",
ConfigName: "db.repository",
Default: db.DefaultRepository,
Usage: "OCI repository to retrieve trivy-db from",
Default: []string{db.DefaultGHCRRepository},
Usage: "OCI repository(ies) to retrieve trivy-db in order of priority",
}
JavaDBRepositoryFlag = Flag[string]{
JavaDBRepositoryFlag = Flag[[]string]{
Name: "java-db-repository",
ConfigName: "db.java-repository",
Default: javadb.DefaultRepository,
Usage: "OCI repository to retrieve trivy-java-db from",
Default: []string{javadb.DefaultGHCRRepository},
Usage: "OCI repository(ies) to retrieve trivy-java-db in order of priority",
}
LightFlag = Flag[bool]{
Name: "light",
@@ -78,8 +78,8 @@ type DBFlagGroup struct {
DownloadJavaDBOnly *Flag[bool]
SkipJavaDBUpdate *Flag[bool]
NoProgress *Flag[bool]
DBRepository *Flag[string]
JavaDBRepository *Flag[string]
DBRepositories *Flag[[]string]
JavaDBRepositories *Flag[[]string]
Light *Flag[bool] // deprecated
}
@@ -90,8 +90,8 @@ type DBOptions struct {
DownloadJavaDBOnly bool
SkipJavaDBUpdate bool
NoProgress bool
DBRepository name.Reference
JavaDBRepository name.Reference
DBRepositories []name.Reference
JavaDBRepositories []name.Reference
}
// NewDBFlagGroup returns a default DBFlagGroup
@@ -104,8 +104,8 @@ func NewDBFlagGroup() *DBFlagGroup {
SkipJavaDBUpdate: SkipJavaDBUpdateFlag.Clone(),
Light: LightFlag.Clone(),
NoProgress: NoProgressFlag.Clone(),
DBRepository: DBRepositoryFlag.Clone(),
JavaDBRepository: JavaDBRepositoryFlag.Clone(),
DBRepositories: DBRepositoryFlag.Clone(),
JavaDBRepositories: JavaDBRepositoryFlag.Clone(),
}
}
@@ -121,8 +121,8 @@ func (f *DBFlagGroup) Flags() []Flagger {
f.DownloadJavaDBOnly,
f.SkipJavaDBUpdate,
f.NoProgress,
f.DBRepository,
f.JavaDBRepository,
f.DBRepositories,
f.JavaDBRepositories,
f.Light,
}
}
@@ -147,30 +147,21 @@ func (f *DBFlagGroup) ToOptions() (DBOptions, error) {
return DBOptions{}, xerrors.New("--skip-java-db-update and --download-java-db-only options can not be specified both")
}
var dbRepository, javaDBRepository name.Reference
var err error
if f.DBRepository != nil {
if dbRepository, err = name.ParseReference(f.DBRepository.Value(), name.WithDefaultTag("")); err != nil {
return DBOptions{}, xerrors.Errorf("invalid db repository: %w", err)
}
// Add the schema version if the tag is not specified for backward compatibility.
if t, ok := dbRepository.(name.Tag); ok && t.TagStr() == "" {
dbRepository = t.Tag(fmt.Sprint(db.SchemaVersion))
log.Info("Adding schema version to the DB repository for backward compatibility",
log.String("repository", dbRepository.String()))
var dbRepositories, javaDBRepositories []name.Reference
for _, repo := range f.DBRepositories.Value() {
ref, err := parseRepository(repo, db.SchemaVersion)
if err != nil {
return DBOptions{}, xerrors.Errorf("invalid DB repository: %w", err)
}
dbRepositories = append(dbRepositories, ref)
}
if f.JavaDBRepository != nil {
if javaDBRepository, err = name.ParseReference(f.JavaDBRepository.Value(), name.WithDefaultTag("")); err != nil {
for _, repo := range f.JavaDBRepositories.Value() {
ref, err := parseRepository(repo, javadb.SchemaVersion)
if err != nil {
return DBOptions{}, xerrors.Errorf("invalid javadb repository: %w", err)
}
// Add the schema version if the tag is not specified for backward compatibility.
if t, ok := javaDBRepository.(name.Tag); ok && t.TagStr() == "" {
javaDBRepository = t.Tag(fmt.Sprint(javadb.SchemaVersion))
log.Info("Adding schema version to the Java DB repository for backward compatibility",
log.String("repository", javaDBRepository.String()))
}
javaDBRepositories = append(javaDBRepositories, ref)
}
return DBOptions{
@@ -180,7 +171,26 @@ func (f *DBFlagGroup) ToOptions() (DBOptions, error) {
DownloadJavaDBOnly: downloadJavaDBOnly,
SkipJavaDBUpdate: skipJavaDBUpdate,
NoProgress: f.NoProgress.Value(),
DBRepository: dbRepository,
JavaDBRepository: javaDBRepository,
DBRepositories: dbRepositories,
JavaDBRepositories: javaDBRepositories,
}, nil
}
func parseRepository(repo string, dbSchemaVersion int) (name.Reference, error) {
dbRepository, err := name.ParseReference(repo, name.WithDefaultTag(""))
if err != nil {
return nil, err
}
// Add the schema version if the tag is not specified for backward compatibility.
t, ok := dbRepository.(name.Tag)
if !ok || t.TagStr() != "" {
return dbRepository, nil
}
dbRepository = t.Tag(fmt.Sprint(dbSchemaVersion))
log.Info("Adding schema version to the DB repository for backward compatibility",
log.String("repository", dbRepository.String()))
return dbRepository, nil
}

View File

@@ -17,31 +17,34 @@ func TestDBFlagGroup_ToOptions(t *testing.T) {
SkipDBUpdate bool
DownloadDBOnly bool
Light bool
DBRepository string
JavaDBRepository string
DBRepository []string
JavaDBRepository []string
}
tests := []struct {
name string
fields fields
want flag.DBOptions
wantLogs []string
assertion require.ErrorAssertionFunc
name string
fields fields
want flag.DBOptions
wantLogs []string
wantErr string
}{
{
name: "happy",
fields: fields{
SkipDBUpdate: true,
DownloadDBOnly: false,
DBRepository: "ghcr.io/aquasecurity/trivy-db",
JavaDBRepository: "ghcr.io/aquasecurity/trivy-java-db",
DBRepository: []string{"ghcr.io/aquasecurity/trivy-db"},
JavaDBRepository: []string{"ghcr.io/aquasecurity/trivy-java-db"},
},
want: flag.DBOptions{
SkipDBUpdate: true,
DownloadDBOnly: false,
DBRepository: name.Tag{}, // All fields are unexported
JavaDBRepository: name.Tag{}, // All fields are unexported
SkipDBUpdate: true,
DownloadDBOnly: false,
DBRepositories: []name.Reference{name.Tag{}}, // All fields are unexported
JavaDBRepositories: []name.Reference{name.Tag{}}, // All fields are unexported
},
wantLogs: []string{
`Adding schema version to the DB repository for backward compatibility repository="ghcr.io/aquasecurity/trivy-db:2"`,
`Adding schema version to the DB repository for backward compatibility repository="ghcr.io/aquasecurity/trivy-java-db:1"`,
},
assertion: require.NoError,
},
{
name: "sad",
@@ -49,25 +52,36 @@ func TestDBFlagGroup_ToOptions(t *testing.T) {
SkipDBUpdate: true,
DownloadDBOnly: true,
},
assertion: func(t require.TestingT, err error, msgs ...any) {
require.ErrorContains(t, err, "--skip-db-update and --download-db-only options can not be specified both")
},
wantErr: "--skip-db-update and --download-db-only options can not be specified both",
},
{
name: "invalid repo",
fields: fields{
SkipDBUpdate: true,
DownloadDBOnly: false,
DBRepository: "foo:bar:baz",
DBRepository: []string{"foo:bar:baz"},
},
assertion: func(t require.TestingT, err error, msgs ...any) {
require.ErrorContains(t, err, "invalid db repository")
wantErr: "invalid DB repository",
},
{
name: "multiple repos",
fields: fields{
SkipDBUpdate: true,
DownloadDBOnly: false,
DBRepository: []string{"ghcr.io/aquasecurity/trivy-db:2", "gallery.ecr.aws/aquasecurity/trivy-db:2"},
JavaDBRepository: []string{"ghcr.io/aquasecurity/trivy-java-db:1", "gallery.ecr.aws/aquasecurity/trivy-java-db:1"},
},
want: flag.DBOptions{
SkipDBUpdate: true,
DownloadDBOnly: false,
DBRepositories: []name.Reference{name.Tag{}, name.Tag{}}, // All fields are unexported
JavaDBRepositories: []name.Reference{name.Tag{}, name.Tag{}}, // All fields are unexported
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
out := newLogger(log.LevelWarn)
out := newLogger(log.LevelInfo)
viper.Set(flag.SkipDBUpdateFlag.ConfigName, tt.fields.SkipDBUpdate)
viper.Set(flag.DownloadDBOnlyFlag.ConfigName, tt.fields.DownloadDBOnly)
@@ -76,13 +90,19 @@ func TestDBFlagGroup_ToOptions(t *testing.T) {
// Assert options
f := &flag.DBFlagGroup{
DownloadDBOnly: flag.DownloadDBOnlyFlag.Clone(),
SkipDBUpdate: flag.SkipDBUpdateFlag.Clone(),
DBRepository: flag.DBRepositoryFlag.Clone(),
JavaDBRepository: flag.JavaDBRepositoryFlag.Clone(),
DownloadDBOnly: flag.DownloadDBOnlyFlag.Clone(),
SkipDBUpdate: flag.SkipDBUpdateFlag.Clone(),
DBRepositories: flag.DBRepositoryFlag.Clone(),
JavaDBRepositories: flag.JavaDBRepositoryFlag.Clone(),
}
got, err := f.ToOptions()
tt.assertion(t, err)
if tt.wantErr != "" {
require.Error(t, err)
assert.ErrorContains(t, err, tt.wantErr)
return
}
require.NoError(t, err)
assert.EqualExportedValues(t, tt.want, got)
// Assert log messages

View File

@@ -26,12 +26,15 @@ const (
mediaType = "application/vnd.aquasec.trivy.javadb.layer.v1.tar+gzip"
)
var DefaultRepository = fmt.Sprintf("%s:%d", "ghcr.io/aquasecurity/trivy-java-db", SchemaVersion)
var (
// GitHub Container Registry
DefaultGHCRRepository = fmt.Sprintf("%s:%d", "ghcr.io/aquasecurity/trivy-java-db", SchemaVersion)
)
var updater *Updater
type Updater struct {
repo name.Reference
repos []name.Reference
dbDir string
skip bool
quiet bool
@@ -40,8 +43,7 @@ type Updater struct {
}
func (u *Updater) Update() error {
dbDir := u.dbDir
metac := db.NewMetadata(dbDir)
metac := db.NewMetadata(u.dbDir)
meta, err := metac.Get()
if err != nil {
@@ -55,13 +57,9 @@ func (u *Updater) Update() error {
if (meta.Version != SchemaVersion || !u.isNewDB(meta)) && !u.skip {
// Download DB
log.Info("Java DB Repository", log.Any("repository", u.repo))
log.Info("Downloading the Java DB...")
// TODO: support remote options
art := oci.NewArtifact(u.repo.String(), u.quiet, u.registryOption)
if err = art.Download(context.Background(), dbDir, oci.DownloadOption{MediaType: mediaType}); err != nil {
return xerrors.Errorf("DB download error: %w", err)
if err := u.downloadDB(); err != nil {
return xerrors.Errorf("OCI artifact error: %w", err)
}
// Parse the newly downloaded metadata.json
@@ -96,9 +94,21 @@ func (u *Updater) isNewDB(meta db.Metadata) bool {
return false
}
func Init(cacheDir string, javaDBRepository name.Reference, skip, quiet bool, registryOption ftypes.RegistryOptions) {
func (u *Updater) downloadDB() error {
log.Info("Downloading Java DB...")
artifacts := oci.NewArtifacts(u.repos, u.registryOption)
downloadOpt := oci.DownloadOption{MediaType: mediaType, Quiet: u.quiet}
if err := artifacts.Download(context.Background(), u.dbDir, downloadOpt); err != nil {
return xerrors.Errorf("failed to download vulnerability DB: %w", err)
}
return xerrors.New("failed to download Java DB from any source")
}
func Init(cacheDir string, javaDBRepositories []name.Reference, skip, quiet bool, registryOption ftypes.RegistryOptions) {
updater = &Updater{
repo: javaDBRepository,
repos: javaDBRepositories,
dbDir: dbDir(cacheDir),
skip: skip,
quiet: quiet,

View File

@@ -23,11 +23,11 @@ func Install(ctx context.Context, dir, repo string, quiet bool, opt types.Regist
}
log.Info("Installing the module from the repository...", log.String("repo", repo))
art := oci.NewArtifact(repo, quiet, opt)
art := oci.NewArtifact(repo, opt)
dst := filepath.Join(dir, ref.Context().Name())
log.Debug("Installing the module...", log.String("dst", dst))
if err = art.Download(ctx, dst, oci.DownloadOption{MediaType: mediaType}); err != nil {
if err = art.Download(ctx, dst, oci.DownloadOption{MediaType: mediaType, Quiet: quiet}); err != nil {
return xerrors.Errorf("module download error: %w", err)
}

View File

@@ -2,6 +2,7 @@ package oci
import (
"context"
"errors"
"io"
"os"
"path/filepath"
@@ -10,11 +11,15 @@ import (
"github.com/cheggaaa/pb/v3"
"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/remote/transport"
"github.com/samber/lo"
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy/pkg/downloader"
"github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/remote"
"github.com/aquasecurity/trivy/pkg/version/doc"
)
const (
@@ -48,7 +53,6 @@ func WithImage(img v1.Image) Option {
type Artifact struct {
m sync.Mutex
repository string
quiet bool
// For OCI registries
types.RegistryOptions
@@ -57,10 +61,9 @@ type Artifact struct {
}
// NewArtifact returns a new artifact
func NewArtifact(repo string, quiet bool, registryOpt types.RegistryOptions, opts ...Option) *Artifact {
func NewArtifact(repo string, registryOpt types.RegistryOptions, opts ...Option) *Artifact {
art := &Artifact{
repository: repo,
quiet: quiet,
RegistryOptions: registryOpt,
}
@@ -98,6 +101,7 @@ func (a *Artifact) populate(ctx context.Context, opt types.RegistryOptions) erro
type DownloadOption struct {
MediaType string // Accept any media type if not specified
Filename string // Use the annotation if not specified
Quiet bool
}
func (a *Artifact) Download(ctx context.Context, dir string, opt DownloadOption) error {
@@ -140,14 +144,14 @@ func (a *Artifact) Download(ctx context.Context, dir string, opt DownloadOption)
return xerrors.Errorf("unacceptable media type: %s", string(layerMediaType))
}
if err = a.download(ctx, layer, fileName, dir); err != nil {
if err = a.download(ctx, layer, fileName, dir, opt.Quiet); err != nil {
return xerrors.Errorf("oci download error: %w", err)
}
return nil
}
func (a *Artifact) download(ctx context.Context, layer v1.Layer, fileName, dir string) error {
func (a *Artifact) download(ctx context.Context, layer v1.Layer, fileName, dir string, quiet bool) error {
size, err := layer.Size()
if err != nil {
return xerrors.Errorf("size error: %w", err)
@@ -161,7 +165,7 @@ func (a *Artifact) download(ctx context.Context, layer v1.Layer, fileName, dir s
// Show progress bar
bar := pb.Full.Start64(size)
if a.quiet {
if quiet {
bar.SetWriter(io.Discard)
}
pr := bar.NewProxyReader(rc)
@@ -207,3 +211,54 @@ func (a *Artifact) Digest(ctx context.Context) (string, error) {
}
return digest.String(), nil
}
type Artifacts []*Artifact
// NewArtifacts returns a slice of artifacts.
func NewArtifacts(repos []name.Reference, opt types.RegistryOptions, opts ...Option) Artifacts {
return lo.Map(repos, func(r name.Reference, _ int) *Artifact {
return NewArtifact(r.String(), opt, opts...)
})
}
// Download downloads artifacts until one of them succeeds.
// Attempts to download next artifact if the first one fails due to a temporary error.
func (a Artifacts) Download(ctx context.Context, dst string, opt DownloadOption) error {
for i, art := range a {
log.Info("Downloading artifact...", log.String("repo", art.repository))
err := art.Download(ctx, dst, opt)
if err == nil {
log.Info("Artifact successfully downloaded", log.String("repo", art.repository))
return nil
}
if !shouldTryOtherRepo(err) {
return xerrors.Errorf("failed to download artifact from %s: %w", art.repository, err)
}
log.Error("Failed to download artifact", log.String("repo", art.repository), log.Err(err))
if i < len(a)-1 {
log.Info("Trying to download artifact from other repository...")
}
}
return xerrors.New("failed to download artifact from any source")
}
func shouldTryOtherRepo(err error) bool {
var terr *transport.Error
if !errors.As(err, &terr) {
return false
}
for _, diagnostic := range terr.Errors {
// For better user experience
if diagnostic.Code == transport.DeniedErrorCode || diagnostic.Code == transport.UnauthorizedErrorCode {
// e.g. https://aquasecurity.github.io/trivy/latest/docs/references/troubleshooting/#db
log.Warnf("See %s", doc.URL("/docs/references/troubleshooting/", "db"))
break
}
}
// try the following artifact only if a temporary error occurs
return terr.Temporary()
}

View File

@@ -116,9 +116,10 @@ func TestArtifact_Download(t *testing.T) {
},
}, nil)
artifact := oci.NewArtifact("repo", true, ftypes.RegistryOptions{}, oci.WithImage(img))
artifact := oci.NewArtifact("repo", ftypes.RegistryOptions{}, oci.WithImage(img))
err = artifact.Download(context.Background(), tempDir, oci.DownloadOption{
MediaType: tt.mediaType,
Quiet: true,
})
if tt.wantErr != "" {
assert.ErrorContains(t, err, tt.wantErr)

View File

@@ -92,7 +92,7 @@ func NewClient(cacheDir string, quiet bool, checkBundleRepo string, opts ...Opti
func (c *Client) populateOCIArtifact(registryOpts types.RegistryOptions) {
if c.artifact == nil {
log.Debug("Loading check bundle", log.String("repository", c.checkBundleRepo))
c.artifact = oci.NewArtifact(c.checkBundleRepo, c.quiet, registryOpts)
c.artifact = oci.NewArtifact(c.checkBundleRepo, registryOpts)
}
}
@@ -101,7 +101,9 @@ func (c *Client) DownloadBuiltinPolicies(ctx context.Context, registryOpts types
c.populateOCIArtifact(registryOpts)
dst := c.contentDir()
if err := c.artifact.Download(ctx, dst, oci.DownloadOption{MediaType: policyMediaType}); err != nil {
if err := c.artifact.Download(ctx, dst,
oci.DownloadOption{MediaType: policyMediaType, Quiet: c.quiet},
); err != nil {
return xerrors.Errorf("download error: %w", err)
}

View File

@@ -116,7 +116,7 @@ func TestClient_LoadBuiltinPolicies(t *testing.T) {
}, nil)
// Mock OCI artifact
art := oci.NewArtifact("repo", true, ftypes.RegistryOptions{}, oci.WithImage(img))
art := oci.NewArtifact("repo", ftypes.RegistryOptions{}, oci.WithImage(img))
c, err := policy.NewClient(tt.cacheDir, true, "", policy.WithOCIArtifact(art))
require.NoError(t, err)
@@ -255,7 +255,7 @@ func TestClient_NeedsUpdate(t *testing.T) {
require.NoError(t, err)
}
art := oci.NewArtifact("repo", true, ftypes.RegistryOptions{}, oci.WithImage(img))
art := oci.NewArtifact("repo", ftypes.RegistryOptions{}, oci.WithImage(img))
c, err := policy.NewClient(tmpDir, true, "", policy.WithOCIArtifact(art), policy.WithClock(tt.clock))
require.NoError(t, err)
@@ -357,7 +357,7 @@ func TestClient_DownloadBuiltinPolicies(t *testing.T) {
}, nil)
// Mock OCI artifact
art := oci.NewArtifact("repo", true, ftypes.RegistryOptions{}, oci.WithImage(img))
art := oci.NewArtifact("repo", ftypes.RegistryOptions{}, oci.WithImage(img))
c, err := policy.NewClient(tempDir, true, "", policy.WithClock(tt.clock), policy.WithOCIArtifact(art))
require.NoError(t, err)

View File

@@ -29,21 +29,21 @@ const updateInterval = 1 * time.Hour
// Server represents Trivy server
type Server struct {
appVersion string
addr string
cacheDir string
dbDir string
token string
tokenHeader string
pathPrefix string
dbRepository name.Reference
appVersion string
addr string
cacheDir string
dbDir string
token string
tokenHeader string
pathPrefix string
dbRepositories []name.Reference
// For OCI registries
types.RegistryOptions
}
// NewServer returns an instance of Server
func NewServer(appVersion, addr, cacheDir, token, tokenHeader, pathPrefix string, dbRepository name.Reference, opt types.RegistryOptions) Server {
func NewServer(appVersion, addr, cacheDir, token, tokenHeader, pathPrefix string, dbRepositories []name.Reference, opt types.RegistryOptions) Server {
return Server{
appVersion: appVersion,
addr: addr,
@@ -52,7 +52,7 @@ func NewServer(appVersion, addr, cacheDir, token, tokenHeader, pathPrefix string
token: token,
tokenHeader: tokenHeader,
pathPrefix: pathPrefix,
dbRepository: dbRepository,
dbRepositories: dbRepositories,
RegistryOptions: opt,
}
}
@@ -63,7 +63,7 @@ func (s Server) ListenAndServe(ctx context.Context, serverCache cache.Cache, ski
dbUpdateWg := &sync.WaitGroup{}
go func() {
worker := newDBWorker(db.NewClient(s.dbDir, true, db.WithDBRepository(s.dbRepository)))
worker := newDBWorker(db.NewClient(s.dbDir, true, db.WithDBRepository(s.dbRepositories)))
for {
time.Sleep(updateInterval)
if err := worker.update(ctx, s.appVersion, s.dbDir, skipDBUpdate, dbUpdateWg, requestWg, s.RegistryOptions); err != nil {