refactor: unify cache implementations (#6977)

Signed-off-by: knqyf263 <knqyf263@gmail.com>
This commit is contained in:
Teppei Fukuda
2024-06-21 10:35:33 +04:00
committed by GitHub
parent 9dc8a2ba6b
commit 6dff4223ed
65 changed files with 481 additions and 962 deletions

23
go.mod
View File

@@ -30,13 +30,12 @@ require (
github.com/aquasecurity/trivy-java-db v0.0.0-20240109071736-184bd7481d48
github.com/aquasecurity/trivy-kubernetes v0.6.7-0.20240516051533-4c5a4aad13b7
github.com/aws/aws-sdk-go-v2 v1.27.2
github.com/aws/aws-sdk-go-v2/config v1.27.15
github.com/aws/aws-sdk-go-v2/credentials v1.17.15
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.20
github.com/aws/aws-sdk-go-v2/service/ec2 v1.161.3
github.com/aws/aws-sdk-go-v2/service/ecr v1.28.2
github.com/aws/aws-sdk-go-v2/service/s3 v1.54.2
github.com/aws/aws-sdk-go-v2/service/sts v1.28.9 // indirect
github.com/aws/aws-sdk-go-v2/config v1.27.18
github.com/aws/aws-sdk-go-v2/credentials v1.17.18
github.com/aws/aws-sdk-go-v2/service/ec2 v1.163.1
github.com/aws/aws-sdk-go-v2/service/ecr v1.28.5
github.com/aws/aws-sdk-go-v2/service/s3 v1.55.1
github.com/aws/aws-sdk-go-v2/service/sts v1.28.12 // indirect
github.com/aws/smithy-go v1.20.2
github.com/bitnami/go-version v0.0.0-20231130084017-bb00604d650c
github.com/bmatcuk/doublestar/v4 v4.6.1
@@ -169,19 +168,15 @@ require (
github.com/apparentlymart/go-textseg/v15 v15.0.0 // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/aws/aws-sdk-go v1.53.0 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.2 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.5 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.9 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.9 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 // indirect
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.7 // indirect
github.com/aws/aws-sdk-go-v2/service/ebs v1.21.7 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.9 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.9 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.7 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.20.8 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.2 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.11 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.20.11 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.5 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
github.com/briandowns/spinner v1.23.0 // indirect

46
go.sum
View File

@@ -791,46 +791,36 @@ github.com/aws/aws-sdk-go v1.53.0 h1:MMo1x1ggPPxDfHMXJnQudTbGXYlD4UigUAud1DJxPVo
github.com/aws/aws-sdk-go v1.53.0/go.mod h1:LF8svs817+Nz+DmiMQKTO3ubZ/6IaTpq3TjupRn3Eqk=
github.com/aws/aws-sdk-go-v2 v1.27.2 h1:pLsTXqX93rimAOZG2FIYraDQstZaaGVVN4tNw65v0h8=
github.com/aws/aws-sdk-go-v2 v1.27.2/go.mod h1:ffIFB97e2yNsv4aTSGkqtHnppsIJzw7G7BReUZ3jCXM=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.2 h1:x6xsQXGSmW6frevwDA+vi/wqhp1ct18mVXYN08/93to=
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.2/go.mod h1:lPprDr1e6cJdyYeGXnRaJoP4Md+cDBvi2eOj00BlGmg=
github.com/aws/aws-sdk-go-v2/config v1.27.15 h1:uNnGLZ+DutuNEkuPh6fwqK7LpEiPmzb7MIMA1mNWEUc=
github.com/aws/aws-sdk-go-v2/config v1.27.15/go.mod h1:7j7Kxx9/7kTmL7z4LlhwQe63MYEE5vkVV6nWg4ZAI8M=
github.com/aws/aws-sdk-go-v2/credentials v1.17.15 h1:YDexlvDRCA8ems2T5IP1xkMtOZ1uLJOCJdTr0igs5zo=
github.com/aws/aws-sdk-go-v2/credentials v1.17.15/go.mod h1:vxHggqW6hFNaeNC0WyXS3VdyjcV0a4KMUY4dKJ96buU=
github.com/aws/aws-sdk-go-v2/config v1.27.18 h1:wFvAnwOKKe7QAyIxziwSKjmer9JBMH1vzIL6W+fYuKk=
github.com/aws/aws-sdk-go-v2/config v1.27.18/go.mod h1:0xz6cgdX55+kmppvPm2IaKzIXOheGJhAufacPJaXZ7c=
github.com/aws/aws-sdk-go-v2/credentials v1.17.18 h1:D/ALDWqK4JdY3OFgA2thcPO1c9aYTT5STS/CvnkqY1c=
github.com/aws/aws-sdk-go-v2/credentials v1.17.18/go.mod h1:JuitCWq+F5QGUrmMPsk945rop6bB57jdscu+Glozdnc=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.5 h1:dDgptDO9dxeFkXy+tEgVkzSClHZje/6JkPW5aZyEvrQ=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.5/go.mod h1:gjvE2KBUgUQhcv89jqxrIxH9GaKs1JbZzWejj/DaHGA=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.20 h1:NCM9wYaJCmlIWZSO/JwUEveKf0NCvsSgo9V9BwOAolo=
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.16.20/go.mod h1:dmxIx3qriuepxqZgFeFMitFuftWPB94+MZv/6Btpth4=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.9 h1:cy8ahBJuhtM8GTTSyOkfy6WVPV1IE+SS5/wfXUYuulw=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.9/go.mod h1:CZBXGLaJnEZI6EVNcPd7a6B5IC5cA/GkRWtu9fp3S6Y=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.9 h1:A4SYk07ef04+vxZToz9LWvAXl9LW0NClpPpMsi31cz0=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.9/go.mod h1:5jJcHuwDagxN+ErjQ3PU3ocf6Ylc/p9x+BLO/+X4iXw=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0 h1:hT8rVHwugYE2lEfdFE0QWVo81lF7jMrYJVDWI+f+VxU=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.0/go.mod h1:8tu/lYfQfFe6IGnaOdrpVgEL2IrrDOf6/m9RQum4NkY=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.7 h1:/FUtT3xsoHO3cfh+I/kCbcMCN98QZRsiFet/V8QkWSs=
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.7/go.mod h1:MaCAgWpGooQoCWZnMur97rGn5dp350w2+CeiV5406wE=
github.com/aws/aws-sdk-go-v2/service/ebs v1.21.7 h1:CRzzXjmgx9p362yO39D6hbZULdMI23gaKqSxijJCXHM=
github.com/aws/aws-sdk-go-v2/service/ebs v1.21.7/go.mod h1:wnsHqpi3RgDwklS5SPHUgjcUUpontGPKJ+GJYOdV7pY=
github.com/aws/aws-sdk-go-v2/service/ec2 v1.161.3 h1:l0mvKOGm25yo/Fy+Y/08Cm4aTA4XmnIuq4ppy+shfMI=
github.com/aws/aws-sdk-go-v2/service/ec2 v1.161.3/go.mod h1:iJ2sQeUTkjNp3nL7kE/Bav0xXYhtiRCRP5ZXk4jFhCQ=
github.com/aws/aws-sdk-go-v2/service/ecr v1.28.2 h1:xUpMnRZonKfrHaNLC77IMpWZSUMRRXIi6IU5EhAPsrM=
github.com/aws/aws-sdk-go-v2/service/ecr v1.28.2/go.mod h1:X52zjAVRaXklEU1TE/wO8kyyJSr9cJx9ZsqliWbyRys=
github.com/aws/aws-sdk-go-v2/service/ec2 v1.163.1 h1:0RiDkJO1veM6/FQ+GJcGiIhZgPwXlscX29B0zFE4Ulo=
github.com/aws/aws-sdk-go-v2/service/ec2 v1.163.1/go.mod h1:gYk1NtyvkH1SxPcndDtfro3lwbiE5t0tW4eRki5YnOQ=
github.com/aws/aws-sdk-go-v2/service/ecr v1.28.5 h1:dvvTFXpWSv9+8lTNPl1EPNZL6BCUV6MgVckEMvXaOgk=
github.com/aws/aws-sdk-go-v2/service/ecr v1.28.5/go.mod h1:Ogt6AOZ/sPBlJZpVFJgOK+jGGREuo8DMjNg+O/7gpjI=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2 h1:Ji0DY1xUsUr3I8cHps0G+XM3WWU16lP6yG8qu1GAZAs=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.2/go.mod h1:5CsjAbs3NlGQyZNFACh+zztPDI7fU6eW9QsxjfnuBKg=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.9 h1:UXqEWQI0n+q0QixzU0yUUQBZXRd5037qdInTIHFTl98=
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.9/go.mod h1:xP6Gq6fzGZT8w/ZN+XvGMZ2RU1LeEs7b2yUP5DN8NY4=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.9 h1:Wx0rlZoEJR7JwlSZcHnEa7CNjrSIyVxMFWGAaXy4fJY=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.9/go.mod h1:aVMHdE0aHO3v+f/iw01fmXV/5DbfQ3Bi9nN7nd9bE9Y=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.7 h1:uO5XR6QGBcmPyo2gxofYJLFkcVQ4izOoGDNenlZhTEk=
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.7/go.mod h1:feeeAYfAcwTReM6vbwjEyDmiGho+YgBhaFULuXDW8kc=
github.com/aws/aws-sdk-go-v2/service/s3 v1.54.2 h1:gYSJhNiOF6J9xaYxu2NFNstoiNELwt0T9w29FxSfN+Y=
github.com/aws/aws-sdk-go-v2/service/s3 v1.54.2/go.mod h1:739CllldowZiPPsDFcJHNF4FXrVxaSGVnZ9Ez9Iz9hc=
github.com/aws/aws-sdk-go-v2/service/sso v1.20.8 h1:Kv1hwNG6jHC/sxMTe5saMjH6t6ZLkgfvVxyEjfWL1ks=
github.com/aws/aws-sdk-go-v2/service/sso v1.20.8/go.mod h1:c1qtZUWtygI6ZdvKppzCSXsDOq5I4luJPZ0Ud3juFCA=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.2 h1:nWBZ1xHCF+A7vv9sDzJOq4NWIdzFYm0kH7Pr4OjHYsQ=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.2/go.mod h1:9lmoVDVLz/yUZwLaQ676TK02fhCu4+PgRSmMaKR1ozk=
github.com/aws/aws-sdk-go-v2/service/sts v1.28.9 h1:Qp6Boy0cGDloOE3zI6XhNLNZgjNS8YmiFQFHe71SaW0=
github.com/aws/aws-sdk-go-v2/service/sts v1.28.9/go.mod h1:0Aqn1MnEuitqfsCNyKsdKLhDUOr4txD/g19EfiUqgws=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.11 h1:o4T+fKxA3gTMcluBNZZXE9DNaMkJuUL1O3mffCUjoJo=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.11/go.mod h1:84oZdJ+VjuJKs9v1UTC9NaodRZRseOXCTgku+vQJWR8=
github.com/aws/aws-sdk-go-v2/service/s3 v1.55.1 h1:UAxBuh0/8sFJk1qOkvOKewP5sWeWaTPDknbQz0ZkDm0=
github.com/aws/aws-sdk-go-v2/service/s3 v1.55.1/go.mod h1:hWjsYGjVuqCgfoveVcVFPXIWgz0aByzwaxKlN1StKcM=
github.com/aws/aws-sdk-go-v2/service/sso v1.20.11 h1:gEYM2GSpr4YNWc6hCd5nod4+d4kd9vWIAWrmGuLdlMw=
github.com/aws/aws-sdk-go-v2/service/sso v1.20.11/go.mod h1:gVvwPdPNYehHSP9Rs7q27U1EU+3Or2ZpXvzAYJNh63w=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.5 h1:iXjh3uaH3vsVcnyZX7MqCoCfcyxIrVE9iOQruRaWPrQ=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.24.5/go.mod h1:5ZXesEuy/QcO0WUnt+4sDkxhdXRHTu2yG0uCSH8B6os=
github.com/aws/aws-sdk-go-v2/service/sts v1.28.12 h1:M/1u4HBpwLuMtjlxuI2y6HoVLzF5e2mfxHCg7ZVMYmk=
github.com/aws/aws-sdk-go-v2/service/sts v1.28.12/go.mod h1:kcfd+eTdEi/40FIbLq4Hif3XMXnl5b/+t/KTfLt9xIk=
github.com/aws/smithy-go v1.20.2 h1:tbp628ireGtzcHDDmLT/6ADHidqnwgF57XOXZe6tp4Q=
github.com/aws/smithy-go v1.20.2/go.mod h1:krry+ya/rV9RDcV/Q16kpu6ypI4K2czasz0NC3qS14E=
github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=

200
pkg/cache/client.go vendored Normal file
View File

@@ -0,0 +1,200 @@
package cache
import (
"crypto/tls"
"crypto/x509"
"fmt"
"os"
"strings"
"time"
"github.com/go-redis/redis/v8"
"github.com/samber/lo"
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy/pkg/log"
)
const (
TypeFS Type = "fs"
TypeRedis Type = "redis"
)
type Client struct {
Cache
}
type Type string
type Options struct {
Type Type
TTL time.Duration
Redis RedisOptions
}
func NewOptions(backend, redisCACert, redisCert, redisKey string, redisTLS bool, ttl time.Duration) (Options, error) {
t, err := NewType(backend)
if err != nil {
return Options{}, xerrors.Errorf("cache type error: %w", err)
}
var redisOpts RedisOptions
if t == TypeRedis {
redisTLSOpts, err := NewRedisTLSOptions(redisCACert, redisCert, redisKey)
if err != nil {
return Options{}, xerrors.Errorf("redis TLS option error: %w", err)
}
redisOpts = RedisOptions{
Backend: backend,
TLS: redisTLS,
TLSOptions: redisTLSOpts,
}
} else if ttl != 0 {
log.Warn("'--cache-ttl' is only available with Redis cache backend")
}
return Options{
Type: t,
TTL: ttl,
Redis: redisOpts,
}, nil
}
type RedisOptions struct {
Backend string
TLS bool
TLSOptions RedisTLSOptions
}
// BackendMasked returns the redis connection string masking credentials
func (o *RedisOptions) BackendMasked() string {
endIndex := strings.Index(o.Backend, "@")
if endIndex == -1 {
return o.Backend
}
startIndex := strings.Index(o.Backend, "//")
return fmt.Sprintf("%s****%s", o.Backend[:startIndex+2], o.Backend[endIndex:])
}
// RedisTLSOptions holds the options for redis cache
type RedisTLSOptions struct {
CACert string
Cert string
Key string
}
func NewRedisTLSOptions(caCert, cert, key string) (RedisTLSOptions, error) {
opts := RedisTLSOptions{
CACert: caCert,
Cert: cert,
Key: key,
}
// If one of redis option not nil, make sure CA, cert, and key provided
if !lo.IsEmpty(opts) {
if opts.CACert == "" || opts.Cert == "" || opts.Key == "" {
return RedisTLSOptions{}, xerrors.Errorf("you must provide Redis CA, cert and key file path when using TLS")
}
}
return opts, nil
}
func NewType(backend string) (Type, error) {
// "redis://" or "fs" are allowed for now
// An empty value is also allowed for testability
switch {
case strings.HasPrefix(backend, "redis://"):
return TypeRedis, nil
case backend == "fs", backend == "":
return TypeFS, nil
default:
return "", xerrors.Errorf("unknown cache backend: %s", backend)
}
}
// NewClient returns a new cache client
func NewClient(opts Options) (*Client, error) {
if opts.Type == TypeRedis {
log.Info("Redis cache", log.String("url", opts.Redis.BackendMasked()))
options, err := redis.ParseURL(opts.Redis.Backend)
if err != nil {
return nil, err
}
if tlsOpts := opts.Redis.TLSOptions; !lo.IsEmpty(tlsOpts) {
caCert, cert, err := GetTLSConfig(tlsOpts.CACert, tlsOpts.Cert, tlsOpts.Key)
if err != nil {
return nil, err
}
options.TLSConfig = &tls.Config{
RootCAs: caCert,
Certificates: []tls.Certificate{cert},
MinVersion: tls.VersionTLS12,
}
} else if opts.Redis.TLS {
options.TLSConfig = &tls.Config{
MinVersion: tls.VersionTLS12,
}
}
redisCache := NewRedisCache(options, opts.TTL)
return &Client{Cache: redisCache}, nil
}
// standalone mode
fsCache, err := NewFSCache(Dir())
if err != nil {
return nil, xerrors.Errorf("unable to initialize fs cache: %w", err)
}
return &Client{Cache: fsCache}, nil
}
// Reset resets the cache
func (c *Client) Reset() (err error) {
if err := c.ClearDB(); err != nil {
return xerrors.Errorf("failed to clear the database: %w", err)
}
if err := c.ClearArtifacts(); err != nil {
return xerrors.Errorf("failed to clear the artifact cache: %w", err)
}
return nil
}
// ClearDB clears the DB cache
func (c *Client) ClearDB() (err error) {
log.Info("Removing DB file...")
if err = os.RemoveAll(Dir()); err != nil {
return xerrors.Errorf("failed to remove the directory (%s) : %w", Dir(), err)
}
return nil
}
// ClearArtifacts clears the artifact cache
func (c *Client) ClearArtifacts() error {
log.Info("Removing artifact caches...")
if err := c.Clear(); err != nil {
return xerrors.Errorf("failed to remove the cache: %w", err)
}
return nil
}
// GetTLSConfig gets tls config from CA, Cert and Key file
func GetTLSConfig(caCertPath, certPath, keyPath string) (*x509.CertPool, tls.Certificate, error) {
cert, err := tls.LoadX509KeyPair(certPath, keyPath)
if err != nil {
return nil, tls.Certificate{}, err
}
caCert, err := os.ReadFile(caCertPath)
if err != nil {
return nil, tls.Certificate{}, err
}
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
return caCertPool, cert, nil
}

129
pkg/cache/client_test.go vendored Normal file
View File

@@ -0,0 +1,129 @@
package cache_test
import (
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/aquasecurity/trivy/pkg/cache"
)
func TestNewOptions(t *testing.T) {
type args struct {
backend string
redisCACert string
redisCert string
redisKey string
redisTLS bool
ttl time.Duration
}
tests := []struct {
name string
args args
want cache.Options
assertion require.ErrorAssertionFunc
}{
{
name: "fs",
args: args{backend: "fs"},
want: cache.Options{Type: cache.TypeFS},
assertion: require.NoError,
},
{
name: "redis",
args: args{backend: "redis://localhost:6379"},
want: cache.Options{
Type: cache.TypeRedis,
Redis: cache.RedisOptions{Backend: "redis://localhost:6379"},
},
assertion: require.NoError,
},
{
name: "redis tls",
args: args{
backend: "redis://localhost:6379",
redisCACert: "ca-cert.pem",
redisCert: "cert.pem",
redisKey: "key.pem",
},
want: cache.Options{
Type: cache.TypeRedis,
Redis: cache.RedisOptions{
Backend: "redis://localhost:6379",
TLSOptions: cache.RedisTLSOptions{
CACert: "ca-cert.pem",
Cert: "cert.pem",
Key: "key.pem",
},
},
},
assertion: require.NoError,
},
{
name: "redis tls with public certificates",
args: args{
backend: "redis://localhost:6379",
redisTLS: true,
},
want: cache.Options{
Type: cache.TypeRedis,
Redis: cache.RedisOptions{
Backend: "redis://localhost:6379",
TLS: true,
},
},
assertion: require.NoError,
},
{
name: "unknown backend",
args: args{backend: "unknown"},
assertion: func(t require.TestingT, err error, msgs ...any) {
require.ErrorContains(t, err, "unknown cache backend")
},
},
{
name: "sad redis tls",
args: args{
backend: "redis://localhost:6379",
redisCACert: "ca-cert.pem",
},
assertion: func(t require.TestingT, err error, msgs ...any) {
require.ErrorContains(t, err, "you must provide Redis CA, cert and key file path when using TLS")
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := cache.NewOptions(tt.args.backend, tt.args.redisCACert, tt.args.redisCert, tt.args.redisKey, tt.args.redisTLS, tt.args.ttl)
tt.assertion(t, err)
assert.Equal(t, tt.want, got)
})
}
}
func TestRedisOptions_BackendMasked(t *testing.T) {
tests := []struct {
name string
fields cache.RedisOptions
want string
}{
{
name: "redis cache backend masked",
fields: cache.RedisOptions{Backend: "redis://root:password@localhost:6379"},
want: "redis://****@localhost:6379",
},
{
name: "redis cache backend masked does nothing",
fields: cache.RedisOptions{Backend: "redis://localhost:6379"},
want: "redis://localhost:6379",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equal(t, tt.want, tt.fields.BackendMasked())
})
}
}

30
pkg/cache/dir.go vendored Normal file
View File

@@ -0,0 +1,30 @@
package cache
import (
"os"
"path/filepath"
)
var cacheDir string
// defaultDir returns/creates the cache-dir to be used for trivy operations
func defaultDir() string {
tmpDir, err := os.UserCacheDir()
if err != nil {
tmpDir = os.TempDir()
}
return filepath.Join(tmpDir, "trivy")
}
// Dir returns the directory used for caching
func Dir() string {
if cacheDir == "" {
return defaultDir()
}
return cacheDir
}
// SetDir sets the trivy cache dir
func SetDir(dir string) {
cacheDir = dir
}

19
pkg/cache/nop.go vendored
View File

@@ -1,16 +1,11 @@
package cache
import "github.com/aquasecurity/trivy/pkg/fanal/cache"
import "github.com/aquasecurity/trivy/pkg/fanal/types"
func NopCache(ac cache.ArtifactCache) cache.Cache {
return nopCache{ArtifactCache: ac}
}
type NopCache struct{}
type nopCache struct {
cache.ArtifactCache
cache.LocalArtifactCache
}
func (nopCache) Close() error {
return nil
}
func NewNopCache() NopCache { return NopCache{} }
func (NopCache) GetArtifact(string) (types.ArtifactInfo, error) { return types.ArtifactInfo{}, nil }
func (NopCache) GetBlob(string) (types.BlobInfo, error) { return types.BlobInfo{}, nil }
func (NopCache) Close() error { return nil }
func (NopCache) Clear() error { return nil }

View File

@@ -11,7 +11,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/aquasecurity/trivy/pkg/fanal/cache"
"github.com/aquasecurity/trivy/pkg/cache"
"github.com/aquasecurity/trivy/pkg/fanal/types"
)

8
pkg/cache/remote.go vendored
View File

@@ -7,7 +7,6 @@ import (
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy/pkg/fanal/cache"
"github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/rpc"
"github.com/aquasecurity/trivy/pkg/rpc/client"
@@ -21,7 +20,7 @@ type RemoteCache struct {
}
// NewRemoteCache is the factory method for RemoteCache
func NewRemoteCache(url string, customHeaders http.Header, insecure bool) cache.ArtifactCache {
func NewRemoteCache(url string, customHeaders http.Header, insecure bool) ArtifactCache {
ctx := client.WithCustomHeaders(context.Background(), customHeaders)
httpClient := &http.Client{
@@ -33,7 +32,10 @@ func NewRemoteCache(url string, customHeaders http.Header, insecure bool) cache.
},
}
c := rpcCache.NewCacheProtobufClient(url, httpClient)
return &RemoteCache{ctx: ctx, client: c}
return &RemoteCache{
ctx: ctx,
client: c,
}
}
// PutArtifact sends artifact to remote client

View File

@@ -15,14 +15,13 @@ import (
"google.golang.org/protobuf/types/known/emptypb"
"github.com/aquasecurity/trivy/pkg/cache"
fcache "github.com/aquasecurity/trivy/pkg/fanal/cache"
"github.com/aquasecurity/trivy/pkg/fanal/types"
rpcCache "github.com/aquasecurity/trivy/rpc/cache"
rpcScanner "github.com/aquasecurity/trivy/rpc/scanner"
)
type mockCacheServer struct {
cache fcache.Cache
cache cache.Cache
}
func (s *mockCacheServer) PutArtifact(_ context.Context, in *rpcCache.PutArtifactRequest) (*emptypb.Empty, error) {
@@ -47,7 +46,10 @@ func (s *mockCacheServer) MissingBlobs(_ context.Context, in *rpcCache.MissingBl
}
layerIDs = append(layerIDs, layerID)
}
return &rpcCache.MissingBlobsResponse{MissingArtifact: true, MissingBlobIds: layerIDs}, nil
return &rpcCache.MissingBlobsResponse{
MissingArtifact: true,
MissingBlobIds: layerIDs,
}, nil
}
func (s *mockCacheServer) DeleteBlobs(_ context.Context, in *rpcCache.DeleteBlobsRequest) (*emptypb.Empty, error) {

View File

@@ -5,10 +5,11 @@ package artifact
import (
"context"
"github.com/google/wire"
"github.com/aquasecurity/trivy/pkg/cache"
"github.com/aquasecurity/trivy/pkg/fanal/artifact"
"github.com/aquasecurity/trivy/pkg/fanal/cache"
"github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/rpc/client"
"github.com/aquasecurity/trivy/pkg/scanner"

View File

@@ -12,11 +12,10 @@ import (
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy-db/pkg/db"
tcache "github.com/aquasecurity/trivy/pkg/cache"
"github.com/aquasecurity/trivy/pkg/cache"
"github.com/aquasecurity/trivy/pkg/commands/operation"
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
"github.com/aquasecurity/trivy/pkg/fanal/artifact"
"github.com/aquasecurity/trivy/pkg/fanal/cache"
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/fanal/walker"
"github.com/aquasecurity/trivy/pkg/flag"
@@ -31,7 +30,6 @@ import (
"github.com/aquasecurity/trivy/pkg/rpc/client"
"github.com/aquasecurity/trivy/pkg/scanner"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/aquasecurity/trivy/pkg/utils/fsutils"
"github.com/aquasecurity/trivy/pkg/version/doc"
)
@@ -92,8 +90,9 @@ type Runner interface {
}
type runner struct {
cache cache.Cache
dbOpen bool
cache cache.ArtifactCache
localCache cache.LocalArtifactCache
dbOpen bool
// WASM modules
module *module.Manager
@@ -106,6 +105,7 @@ type runnerOption func(*runner)
func WithCacheClient(c cache.Cache) runnerOption {
return func(r *runner) {
r.cache = c
r.localCache = c
}
}
@@ -143,7 +143,7 @@ func NewRunner(ctx context.Context, cliOptions flag.Options, opts ...runnerOptio
// Close closes everything
func (r *runner) Close(ctx context.Context) error {
var errs error
if err := r.cache.Close(); err != nil {
if err := r.localCache.Close(); err != nil {
errs = multierror.Append(errs, err)
}
@@ -259,7 +259,7 @@ func (r *runner) ScanVM(ctx context.Context, opts flag.Options) (types.Report, e
}
func (r *runner) scanArtifact(ctx context.Context, opts flag.Options, initializeScanner InitializeScanner) (types.Report, error) {
report, err := scan(ctx, opts, initializeScanner, r.cache)
report, err := r.scan(ctx, opts, initializeScanner)
if err != nil {
return types.Report{}, xerrors.Errorf("scan error: %w", err)
}
@@ -344,18 +344,18 @@ func (r *runner) initCache(opts flag.Options) error {
// client/server mode
if opts.ServerAddr != "" {
remoteCache := tcache.NewRemoteCache(opts.ServerAddr, opts.CustomHeaders, opts.Insecure)
r.cache = tcache.NopCache(remoteCache)
r.cache = cache.NewRemoteCache(opts.ServerAddr, opts.CustomHeaders, opts.Insecure)
r.localCache = cache.NewNopCache() // No need to use local cache in client/server mode
return nil
}
// standalone mode
fsutils.SetCacheDir(opts.CacheDir)
cacheClient, err := operation.NewCache(opts.CacheOptions)
cache.SetDir(opts.CacheDir)
cacheClient, err := cache.NewClient(opts.CacheOptions.CacheBackendOptions)
if err != nil {
return xerrors.Errorf("unable to initialize the cache: %w", err)
}
log.Debug("Cache dir", log.String("dir", fsutils.CacheDir()))
log.Debug("Cache dir", log.String("dir", cache.Dir()))
if opts.Reset {
defer cacheClient.Close()
@@ -366,7 +366,7 @@ func (r *runner) initCache(opts flag.Options) error {
}
if opts.ResetChecksBundle {
c, err := policy.NewClient(fsutils.CacheDir(), true, opts.MisconfOptions.ChecksBundleRepository)
c, err := policy.NewClient(cache.Dir(), true, opts.MisconfOptions.ChecksBundleRepository)
if err != nil {
return xerrors.Errorf("failed to instantiate check client: %w", err)
}
@@ -384,7 +384,8 @@ func (r *runner) initCache(opts flag.Options) error {
return SkipScan
}
r.cache = cacheClient
r.cache = cacheClient.Cache
r.localCache = cacheClient.Cache
return nil
}
@@ -526,7 +527,7 @@ func filterMisconfigAnalyzers(included, all []analyzer.Type) ([]analyzer.Type, e
return lo.Without(all, included...), nil
}
func initScannerConfig(opts flag.Options, cacheClient cache.Cache) (ScannerConfig, types.ScanOptions, error) {
func (r *runner) initScannerConfig(opts flag.Options) (ScannerConfig, types.ScanOptions, error) {
target := opts.Target
if opts.Input != "" {
target = opts.Input
@@ -616,8 +617,8 @@ func initScannerConfig(opts flag.Options, cacheClient cache.Cache) (ScannerConfi
return ScannerConfig{
Target: target,
ArtifactCache: cacheClient,
LocalArtifactCache: cacheClient,
ArtifactCache: r.cache,
LocalArtifactCache: r.localCache,
ServerOption: client.ScannerOption{
RemoteURL: opts.ServerAddr,
CustomHeaders: opts.CustomHeaders,
@@ -675,9 +676,8 @@ func initScannerConfig(opts flag.Options, cacheClient cache.Cache) (ScannerConfi
}, scanOptions, nil
}
func scan(ctx context.Context, opts flag.Options, initializeScanner InitializeScanner, cacheClient cache.Cache) (
types.Report, error) {
scannerConfig, scanOptions, err := initScannerConfig(opts, cacheClient)
func (r *runner) scan(ctx context.Context, opts flag.Options, initializeScanner InitializeScanner) (types.Report, error) {
scannerConfig, scanOptions, err := r.initScannerConfig(opts)
if err != nil {
return types.Report{}, err
}

View File

@@ -16,7 +16,7 @@ import (
"github.com/aquasecurity/trivy/pkg/fanal/artifact/repo"
"github.com/aquasecurity/trivy/pkg/fanal/artifact/sbom"
"github.com/aquasecurity/trivy/pkg/fanal/artifact/vm"
"github.com/aquasecurity/trivy/pkg/fanal/cache"
"github.com/aquasecurity/trivy/pkg/cache"
"github.com/aquasecurity/trivy/pkg/fanal/image"
"github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/fanal/walker"

View File

@@ -2,114 +2,22 @@ package operation
import (
"context"
"crypto/tls"
"crypto/x509"
"os"
"strings"
"sync"
"github.com/go-redis/redis/v8"
"github.com/google/go-containerregistry/pkg/name"
"github.com/google/wire"
"github.com/samber/lo"
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy-db/pkg/metadata"
"github.com/aquasecurity/trivy/pkg/db"
"github.com/aquasecurity/trivy/pkg/fanal/cache"
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/flag"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/policy"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/aquasecurity/trivy/pkg/utils/fsutils"
)
var mu sync.Mutex
// SuperSet binds cache dependencies
var SuperSet = wire.NewSet(
cache.NewFSCache,
wire.Bind(new(cache.LocalArtifactCache), new(cache.FSCache)),
NewCache,
)
// Cache implements the local cache
type Cache struct {
cache.Cache
}
// NewCache is the factory method for Cache
func NewCache(c flag.CacheOptions) (Cache, error) {
if strings.HasPrefix(c.CacheBackend, "redis://") {
log.Info("Redis cache", log.String("url", c.CacheBackendMasked()))
options, err := redis.ParseURL(c.CacheBackend)
if err != nil {
return Cache{}, err
}
if !lo.IsEmpty(c.RedisOptions) {
caCert, cert, err := GetTLSConfig(c.RedisCACert, c.RedisCert, c.RedisKey)
if err != nil {
return Cache{}, err
}
options.TLSConfig = &tls.Config{
RootCAs: caCert,
Certificates: []tls.Certificate{cert},
MinVersion: tls.VersionTLS12,
}
} else if c.RedisTLS {
options.TLSConfig = &tls.Config{
MinVersion: tls.VersionTLS12,
}
}
redisCache := cache.NewRedisCache(options, c.CacheTTL)
return Cache{Cache: redisCache}, nil
}
if c.CacheTTL != 0 {
log.Warn("'--cache-ttl' is only available with Redis cache backend")
}
// standalone mode
fsCache, err := cache.NewFSCache(fsutils.CacheDir())
if err != nil {
return Cache{}, xerrors.Errorf("unable to initialize fs cache: %w", err)
}
return Cache{Cache: fsCache}, nil
}
// Reset resets the cache
func (c Cache) Reset() (err error) {
if err := c.ClearDB(); err != nil {
return xerrors.Errorf("failed to clear the database: %w", err)
}
if err := c.ClearArtifacts(); err != nil {
return xerrors.Errorf("failed to clear the artifact cache: %w", err)
}
return nil
}
// ClearDB clears the DB cache
func (c Cache) ClearDB() (err error) {
log.Info("Removing DB file...")
if err = os.RemoveAll(fsutils.CacheDir()); err != nil {
return xerrors.Errorf("failed to remove the directory (%s) : %w", fsutils.CacheDir(), err)
}
return nil
}
// ClearArtifacts clears the artifact cache
func (c Cache) ClearArtifacts() error {
log.Info("Removing artifact caches...")
if err := c.Clear(); err != nil {
return xerrors.Errorf("failed to remove the cache: %w", err)
}
return nil
}
// DownloadDB downloads the DB
func DownloadDB(ctx context.Context, appVersion, cacheDir string, dbRepository name.Reference, quiet, skipUpdate bool,
opt ftypes.RegistryOptions) error {
@@ -186,24 +94,6 @@ func InitBuiltinPolicies(ctx context.Context, cacheDir string, quiet, skipUpdate
return policyPaths, nil
}
// GetTLSConfig gets tls config from CA, Cert and Key file
func GetTLSConfig(caCertPath, certPath, keyPath string) (*x509.CertPool, tls.Certificate, error) {
cert, err := tls.LoadX509KeyPair(certPath, keyPath)
if err != nil {
return nil, tls.Certificate{}, err
}
caCert, err := os.ReadFile(caCertPath)
if err != nil {
return nil, tls.Certificate{}, err
}
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(caCert)
return caCertPool, cert, nil
}
func Exit(opts flag.Options, failedResults bool, m types.Metadata) error {
if opts.ExitOnEOL != 0 && m.OS != nil && m.OS.Eosl {
log.Error("Detected EOL OS", log.String("family", string(m.OS.Family)),

View File

@@ -6,12 +6,12 @@ import (
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy-db/pkg/db"
"github.com/aquasecurity/trivy/pkg/cache"
"github.com/aquasecurity/trivy/pkg/commands/operation"
"github.com/aquasecurity/trivy/pkg/flag"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/module"
rpcServer "github.com/aquasecurity/trivy/pkg/rpc/server"
"github.com/aquasecurity/trivy/pkg/utils/fsutils"
)
// Run runs the scan
@@ -19,16 +19,16 @@ func Run(ctx context.Context, opts flag.Options) (err error) {
log.InitLogger(opts.Debug, opts.Quiet)
// configure cache dir
fsutils.SetCacheDir(opts.CacheDir)
cache, err := operation.NewCache(opts.CacheOptions)
cache.SetDir(opts.CacheDir)
cacheClient, err := cache.NewClient(opts.CacheOptions.CacheBackendOptions)
if err != nil {
return xerrors.Errorf("server cache error: %w", err)
}
defer cache.Close()
log.Debug("Cache", log.String("dir", fsutils.CacheDir()))
defer cacheClient.Close()
log.Debug("Cache", log.String("dir", cache.Dir()))
if opts.Reset {
return cache.ClearDB()
return cacheClient.ClearDB()
}
// download the database file
@@ -57,5 +57,5 @@ func Run(ctx context.Context, opts flag.Options) (err error) {
server := rpcServer.NewServer(opts.AppVersion, opts.Listen, opts.CacheDir, opts.Token, opts.TokenHeader,
opts.DBRepository, opts.RegistryOpts())
return server.ListenAndServe(ctx, cache, opts.SkipDBUpdate)
return server.ListenAndServe(ctx, cacheClient, opts.SkipDBUpdate)
}

View File

@@ -3,8 +3,8 @@ package applier
import (
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy/pkg/cache"
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
"github.com/aquasecurity/trivy/pkg/fanal/cache"
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
)

View File

@@ -8,8 +8,8 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/aquasecurity/trivy/pkg/cache"
"github.com/aquasecurity/trivy/pkg/fanal/applier"
"github.com/aquasecurity/trivy/pkg/fanal/cache"
"github.com/aquasecurity/trivy/pkg/fanal/types"
)

View File

@@ -14,9 +14,9 @@ import (
"github.com/samber/lo"
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy/pkg/cache"
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
"github.com/aquasecurity/trivy/pkg/fanal/artifact"
"github.com/aquasecurity/trivy/pkg/fanal/cache"
"github.com/aquasecurity/trivy/pkg/fanal/handler"
"github.com/aquasecurity/trivy/pkg/fanal/image"
"github.com/aquasecurity/trivy/pkg/fanal/types"

View File

@@ -11,10 +11,10 @@ import (
"github.com/stretchr/testify/require"
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy/pkg/cache"
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
"github.com/aquasecurity/trivy/pkg/fanal/artifact"
image2 "github.com/aquasecurity/trivy/pkg/fanal/artifact/image"
"github.com/aquasecurity/trivy/pkg/fanal/cache"
"github.com/aquasecurity/trivy/pkg/fanal/image"
"github.com/aquasecurity/trivy/pkg/fanal/types"

View File

@@ -14,9 +14,9 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/aquasecurity/trivy/pkg/cache"
"github.com/aquasecurity/trivy/pkg/fanal/artifact"
image2 "github.com/aquasecurity/trivy/pkg/fanal/artifact/image"
"github.com/aquasecurity/trivy/pkg/fanal/cache"
"github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/rekortest"

View File

@@ -14,9 +14,9 @@ import (
"github.com/opencontainers/go-digest"
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy/pkg/cache"
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
"github.com/aquasecurity/trivy/pkg/fanal/artifact"
"github.com/aquasecurity/trivy/pkg/fanal/cache"
"github.com/aquasecurity/trivy/pkg/fanal/handler"
"github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/fanal/walker"

View File

@@ -10,9 +10,9 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/aquasecurity/trivy/pkg/cache"
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
"github.com/aquasecurity/trivy/pkg/fanal/artifact"
"github.com/aquasecurity/trivy/pkg/fanal/cache"
"github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/fanal/walker"
"github.com/aquasecurity/trivy/pkg/misconf"

View File

@@ -12,9 +12,9 @@ import (
"github.com/hashicorp/go-multierror"
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy/pkg/cache"
"github.com/aquasecurity/trivy/pkg/fanal/artifact"
"github.com/aquasecurity/trivy/pkg/fanal/artifact/local"
"github.com/aquasecurity/trivy/pkg/fanal/cache"
"github.com/aquasecurity/trivy/pkg/fanal/walker"
)

View File

@@ -12,8 +12,8 @@ import (
"github.com/stretchr/testify/require"
"github.com/aquasecurity/trivy/internal/gittest"
"github.com/aquasecurity/trivy/pkg/cache"
"github.com/aquasecurity/trivy/pkg/fanal/artifact"
"github.com/aquasecurity/trivy/pkg/fanal/cache"
"github.com/aquasecurity/trivy/pkg/fanal/walker"
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/config/all"

View File

@@ -11,9 +11,9 @@ import (
"github.com/samber/lo"
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy/pkg/cache"
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
"github.com/aquasecurity/trivy/pkg/fanal/artifact"
"github.com/aquasecurity/trivy/pkg/fanal/cache"
"github.com/aquasecurity/trivy/pkg/fanal/handler"
"github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/log"

View File

@@ -11,9 +11,9 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/aquasecurity/trivy/pkg/cache"
"github.com/aquasecurity/trivy/pkg/fanal/artifact"
"github.com/aquasecurity/trivy/pkg/fanal/artifact/sbom"
"github.com/aquasecurity/trivy/pkg/fanal/cache"
"github.com/aquasecurity/trivy/pkg/fanal/types"
)

View File

@@ -8,9 +8,9 @@ import (
ebsfile "github.com/masahiro331/go-ebs-file"
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy/pkg/cache"
"github.com/aquasecurity/trivy/pkg/cloud/aws/config"
"github.com/aquasecurity/trivy/pkg/fanal/artifact"
"github.com/aquasecurity/trivy/pkg/fanal/cache"
"github.com/aquasecurity/trivy/pkg/log"
)

View File

@@ -12,8 +12,8 @@ import (
"github.com/opencontainers/go-digest"
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy/pkg/cache"
"github.com/aquasecurity/trivy/pkg/fanal/artifact"
"github.com/aquasecurity/trivy/pkg/fanal/cache"
"github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/fanal/vm"
"github.com/aquasecurity/trivy/pkg/fanal/vm/disk"

View File

@@ -10,9 +10,9 @@ import (
"github.com/google/wire"
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy/pkg/cache"
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
"github.com/aquasecurity/trivy/pkg/fanal/artifact"
"github.com/aquasecurity/trivy/pkg/fanal/cache"
"github.com/aquasecurity/trivy/pkg/fanal/handler"
"github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/fanal/walker"

View File

@@ -14,10 +14,10 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/aquasecurity/trivy/pkg/cache"
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
"github.com/aquasecurity/trivy/pkg/fanal/artifact"
"github.com/aquasecurity/trivy/pkg/fanal/artifact/vm"
"github.com/aquasecurity/trivy/pkg/fanal/cache"
"github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/fanal/walker"
"github.com/aquasecurity/trivy/pkg/misconf"

180
pkg/fanal/cache/s3.go vendored
View File

@@ -1,180 +0,0 @@
package cache
import (
"bytes"
"context"
"encoding/json"
"fmt"
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/feature/s3/manager"
"github.com/aws/aws-sdk-go-v2/service/s3"
"github.com/hashicorp/go-multierror"
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy/pkg/fanal/types"
)
var _ Cache = &S3Cache{}
type s3API interface {
HeadObject(ctx context.Context, params *s3.HeadObjectInput, optFns ...func(*s3.Options)) (*s3.HeadObjectOutput, error)
PutObject(ctx context.Context, params *s3.PutObjectInput, optFns ...func(*s3.Options)) (*s3.PutObjectOutput, error)
DeleteBucket(ctx context.Context, params *s3.DeleteBucketInput, optFns ...func(*s3.Options)) (*s3.DeleteBucketOutput, error)
}
type S3Cache struct {
s3Client s3API
downloader *manager.Downloader
bucketName string
prefix string
}
func NewS3Cache(bucketName, prefix string, api s3API, downloaderAPI *manager.Downloader) S3Cache {
return S3Cache{
s3Client: api,
downloader: downloaderAPI,
bucketName: bucketName,
prefix: prefix,
}
}
func (c S3Cache) PutArtifact(artifactID string, artifactConfig types.ArtifactInfo) (err error) {
key := fmt.Sprintf("%s/%s/%s", artifactBucket, c.prefix, artifactID)
if err := c.put(key, artifactConfig); err != nil {
return xerrors.Errorf("unable to store artifact information in cache (%s): %w", artifactID, err)
}
return nil
}
func (c S3Cache) DeleteBlobs(blobIDs []string) error {
var errs error
for _, blobID := range blobIDs {
key := fmt.Sprintf("%s/%s/%s", blobBucket, c.prefix, blobID)
input := &s3.DeleteBucketInput{Bucket: aws.String(key)}
if _, err := c.s3Client.DeleteBucket(context.TODO(), input); err != nil {
errs = multierror.Append(errs, err)
}
}
return errs
}
func (c S3Cache) PutBlob(blobID string, blobInfo types.BlobInfo) error {
key := fmt.Sprintf("%s/%s/%s", blobBucket, c.prefix, blobID)
if err := c.put(key, blobInfo); err != nil {
return xerrors.Errorf("unable to store blob information in cache (%s): %w", blobID, err)
}
return nil
}
func (c S3Cache) put(key string, body any) (err error) {
b, err := json.Marshal(body)
if err != nil {
return err
}
params := &s3.PutObjectInput{
Bucket: aws.String(c.bucketName),
Key: aws.String(key),
Body: bytes.NewReader(b),
}
_, err = c.s3Client.PutObject(context.TODO(), params)
if err != nil {
return xerrors.Errorf("unable to put object: %w", err)
}
// Index file due S3 caveat read after write consistency
_, err = c.s3Client.PutObject(context.TODO(), &s3.PutObjectInput{
Bucket: aws.String(c.bucketName),
Key: aws.String(fmt.Sprintf("%s.index", key)),
})
if err != nil {
return xerrors.Errorf("unable to put index object: %w", err)
}
return nil
}
func (c S3Cache) GetBlob(blobID string) (types.BlobInfo, error) {
var blobInfo types.BlobInfo
buf := manager.NewWriteAtBuffer([]byte{})
_, err := c.downloader.Download(context.TODO(), buf, &s3.GetObjectInput{
Bucket: aws.String(c.bucketName),
Key: aws.String(fmt.Sprintf("%s/%s/%s", blobBucket, c.prefix, blobID)),
})
if err != nil {
return types.BlobInfo{}, xerrors.Errorf("failed to get blob from the cache: %w", err)
}
err = json.Unmarshal(buf.Bytes(), &blobInfo)
if err != nil {
return types.BlobInfo{}, xerrors.Errorf("JSON unmarshal error: %w", err)
}
return blobInfo, nil
}
func (c S3Cache) GetArtifact(artifactID string) (types.ArtifactInfo, error) {
var info types.ArtifactInfo
buf := manager.NewWriteAtBuffer([]byte{})
_, err := c.downloader.Download(context.TODO(), buf, &s3.GetObjectInput{
Bucket: aws.String(c.bucketName),
Key: aws.String(fmt.Sprintf("%s/%s/%s", artifactBucket, c.prefix, artifactID)),
})
if err != nil {
return types.ArtifactInfo{}, xerrors.Errorf("failed to get artifact from the cache: %w", err)
}
err = json.Unmarshal(buf.Bytes(), &info)
if err != nil {
return types.ArtifactInfo{}, xerrors.Errorf("JSON unmarshal error: %w", err)
}
return info, nil
}
func (c S3Cache) getIndex(key, keyType string) error {
_, err := c.s3Client.HeadObject(context.TODO(), &s3.HeadObjectInput{
Key: aws.String(fmt.Sprintf("%s/%s/%s.index", keyType, c.prefix, key)),
Bucket: &c.bucketName,
})
if err != nil {
return xerrors.Errorf("failed to get index from the cache: %w", err)
}
return nil
}
func (c S3Cache) MissingBlobs(artifactID string, blobIDs []string) (bool, []string, error) {
var missingArtifact bool
var missingBlobIDs []string
for _, blobID := range blobIDs {
err := c.getIndex(blobID, blobBucket)
if err != nil {
// error means cache missed blob info
missingBlobIDs = append(missingBlobIDs, blobID)
continue
}
blobInfo, err := c.GetBlob(blobID)
if err != nil {
return true, missingBlobIDs, xerrors.Errorf("the blob object (%s) doesn't exist in S3 even though the index file exists: %w", blobID, err)
}
if blobInfo.SchemaVersion != types.BlobJSONSchemaVersion {
missingBlobIDs = append(missingBlobIDs, blobID)
}
}
// get artifact info
err := c.getIndex(artifactID, artifactBucket)
// error means cache missed artifact info
if err != nil {
return true, missingBlobIDs, nil
}
artifactInfo, err := c.GetArtifact(artifactID)
if err != nil {
return true, missingBlobIDs, xerrors.Errorf("the artifact object (%s) doesn't exist in S3 even though the index file exists: %w", artifactID, err)
}
if artifactInfo.SchemaVersion != types.ArtifactJSONSchemaVersion {
missingArtifact = true
}
return missingArtifact, missingBlobIDs, nil
}
func (c S3Cache) Close() error {
return nil
}
func (c S3Cache) Clear() error {
return nil
}

View File

@@ -1,312 +0,0 @@
package cache
import (
"context"
"errors"
"reflect"
"testing"
"time"
"github.com/aws/aws-sdk-go-v2/feature/s3/manager"
"github.com/aws/aws-sdk-go-v2/service/s3"
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy/pkg/fanal/types"
)
type mockS3Client struct {
s3API
}
const (
correctHash = "sha256:24df0d4e20c0f42d3703bf1f1db2bdd77346c7956f74f423603d651e8e5ae8a7"
)
func (m *mockS3Client) PutObject(ctx context.Context, in *s3.PutObjectInput, optFns ...func(*s3.Options)) (*s3.PutObjectOutput, error) {
return &s3.PutObjectOutput{}, nil
}
func (m *mockS3Client) HeadObject(ctx context.Context, params *s3.HeadObjectInput, optFns ...func(*s3.Options)) (*s3.HeadObjectOutput, error) {
return &s3.HeadObjectOutput{}, nil
}
func (m *mockS3Client) DeleteBucket(ctx context.Context, in *s3.DeleteBucketInput, optFns ...func(*s3.Options)) (*s3.DeleteBucketOutput, error) {
if in != nil && *in.Bucket == blobBucket+"/prefix/"+correctHash {
return &s3.DeleteBucketOutput{}, nil
}
return nil, errors.New("unknown bucket")
}
func TestS3Cache_PutBlob(t *testing.T) {
mockSvc := &mockS3Client{}
type fields struct {
S3 s3API
Downloader *manager.Downloader
BucketName string
Prefix string
}
type args struct {
blobID string
blobInfo types.BlobInfo
}
tests := []struct {
name string
fields fields
args args
wantErr bool
}{
{
name: "happy path",
fields: fields{
S3: mockSvc,
BucketName: "test",
Prefix: "prefix",
},
args: args{
blobID: "sha256:24df0d4e20c0f42d3703bf1f1db2bdd77346c7956f74f423603d651e8e5ae8a7",
blobInfo: types.BlobInfo{
SchemaVersion: 1,
OS: types.OS{
Family: "alpine",
Name: "3.10",
},
}},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := NewS3Cache(tt.fields.BucketName, tt.fields.Prefix, tt.fields.S3, tt.fields.Downloader)
if err := c.PutBlob(tt.args.blobID, tt.args.blobInfo); (err != nil) != tt.wantErr {
t.Errorf("S3Cache.PutBlob() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func TestS3Cache_PutArtifact(t *testing.T) {
mockSvc := &mockS3Client{}
type fields struct {
S3 s3API
Downloader *manager.Downloader
BucketName string
Prefix string
}
type args struct {
artifactID string
artifactConfig types.ArtifactInfo
}
tests := []struct {
name string
fields fields
args args
wantErr bool
}{
{
name: "happy path",
fields: fields{
S3: mockSvc,
BucketName: "test",
Prefix: "prefix",
},
args: args{
artifactID: "sha256:58701fd185bda36cab0557bb6438661831267aa4a9e0b54211c4d5317a48aff4",
artifactConfig: types.ArtifactInfo{
SchemaVersion: 1,
Architecture: "amd64",
Created: time.Date(2020, 1, 2, 3, 4, 5, 0, time.UTC),
DockerVersion: "18.06.1-ce",
OS: "linux",
HistoryPackages: []types.Package{
{
Name: "musl",
Version: "1.2.3",
},
},
}},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := NewS3Cache(tt.fields.BucketName, tt.fields.Prefix, tt.fields.S3, tt.fields.Downloader)
if err := c.PutArtifact(tt.args.artifactID, tt.args.artifactConfig); (err != nil) != tt.wantErr {
t.Errorf("S3Cache.PutArtifact() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func TestS3Cache_getIndex(t *testing.T) {
mockSvc := &mockS3Client{}
type fields struct {
S3 s3API
Downloader *manager.Downloader
BucketName string
Prefix string
}
type args struct {
key string
keyType string
}
tests := []struct {
name string
fields fields
args args
wantErr bool
}{
{
name: "happy path",
fields: fields{
S3: mockSvc,
BucketName: "test",
Prefix: "prefix",
},
args: args{
key: "key",
keyType: "artifactBucket",
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := NewS3Cache(tt.fields.BucketName, tt.fields.Prefix, tt.fields.S3, tt.fields.Downloader)
if err := c.getIndex(tt.args.key, tt.args.keyType); (err != nil) != tt.wantErr {
t.Errorf("S3Cache.getIndex() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
type mockS3ClientMissingBlobs struct {
s3API
}
func (m *mockS3ClientMissingBlobs) PutObject(ctx context.Context, in *s3.PutObjectInput, optFns ...func(*s3.Options)) (*s3.PutObjectOutput, error) {
return &s3.PutObjectOutput{}, nil
}
func (m *mockS3ClientMissingBlobs) HeadObject(ctx context.Context, params *s3.HeadObjectInput, optFns ...func(*s3.Options)) (*s3.HeadObjectOutput, error) {
return &s3.HeadObjectOutput{}, xerrors.Errorf("the object doesn't exist in S3")
}
func TestS3Cache_MissingBlobs(t *testing.T) {
mockSvc := &mockS3ClientMissingBlobs{}
type fields struct {
S3 s3API
Downloader *manager.Downloader
BucketName string
Prefix string
}
type args struct {
artifactID string
blobIDs []string
analyzerVersions map[string]int
configAnalyzerVersions map[string]int
}
tests := []struct {
name string
fields fields
args args
want bool
wantStringSlice []string
wantErr bool
}{{
name: "happy path",
fields: fields{
S3: mockSvc,
BucketName: "test",
Prefix: "prefix",
},
args: args{
artifactID: "sha256:58701fd185bda36cab0557bb6438661831267aa4a9e0b54211c4d5317a48aff4/1",
blobIDs: []string{"sha256:24df0d4e20c0f42d3703bf1f1db2bdd77346c7956f74f423603d651e8e5ae8a7/10011"},
},
want: true,
wantStringSlice: []string{"sha256:24df0d4e20c0f42d3703bf1f1db2bdd77346c7956f74f423603d651e8e5ae8a7/10011"},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := NewS3Cache(tt.fields.BucketName, tt.fields.Prefix, tt.fields.S3, tt.fields.Downloader)
got, got1, err := c.MissingBlobs(tt.args.artifactID, tt.args.blobIDs)
if (err != nil) != tt.wantErr {
t.Errorf("S3Cache.MissingBlobs() error = %v, wantErr %v", err, tt.wantErr)
return
}
if got != tt.want {
t.Errorf("S3Cache.MissingBlobs() got = %v, want %v", got, tt.want)
}
if !reflect.DeepEqual(got1, tt.wantStringSlice) {
t.Errorf("S3Cache.MissingBlobs() got1 = %v, want %v", got1, tt.wantStringSlice)
}
})
}
}
func TestS3Cache_DeleteBlobs(t *testing.T) {
mockSvc := &mockS3Client{}
type fields struct {
S3 s3API
Downloader *manager.Downloader
BucketName string
Prefix string
}
type args struct {
blobIDs []string
}
tests := []struct {
name string
fields fields
args args
wantErr bool
}{
{
name: "happy path",
fields: fields{
S3: mockSvc,
BucketName: "test",
Prefix: "prefix",
},
args: args{
blobIDs: []string{correctHash},
},
},
{
name: "delete blob with bad ID",
fields: fields{
S3: mockSvc,
BucketName: "test",
Prefix: "prefix",
},
args: args{
blobIDs: []string{"unde"},
},
wantErr: true,
},
{
name: "delete blobs with bad ID",
fields: fields{
S3: mockSvc,
BucketName: "test",
Prefix: "prefix",
},
args: args{
blobIDs: []string{correctHash},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := NewS3Cache(tt.fields.BucketName, tt.fields.Prefix, tt.fields.S3, tt.fields.Downloader)
if err := c.DeleteBlobs(tt.args.blobIDs); (err != nil) != tt.wantErr {
t.Errorf("S3Cache.PutBlob() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}

View File

@@ -27,11 +27,11 @@ import (
"github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/wait"
"github.com/aquasecurity/trivy/pkg/cache"
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
"github.com/aquasecurity/trivy/pkg/fanal/applier"
"github.com/aquasecurity/trivy/pkg/fanal/artifact"
aimage "github.com/aquasecurity/trivy/pkg/fanal/artifact/image"
"github.com/aquasecurity/trivy/pkg/fanal/cache"
"github.com/aquasecurity/trivy/pkg/fanal/image"
"github.com/aquasecurity/trivy/pkg/fanal/types"
)

View File

@@ -20,11 +20,11 @@ import (
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
"github.com/aquasecurity/trivy/pkg/cache"
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/all"
"github.com/aquasecurity/trivy/pkg/fanal/applier"
"github.com/aquasecurity/trivy/pkg/fanal/artifact"
aimage "github.com/aquasecurity/trivy/pkg/fanal/artifact/image"
"github.com/aquasecurity/trivy/pkg/fanal/cache"
_ "github.com/aquasecurity/trivy/pkg/fanal/handler/all"
"github.com/aquasecurity/trivy/pkg/fanal/image"
"github.com/aquasecurity/trivy/pkg/fanal/types"

View File

@@ -20,12 +20,12 @@ import (
testcontainers "github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/wait"
"github.com/aquasecurity/trivy/pkg/cache"
"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/all"
"github.com/aquasecurity/trivy/pkg/fanal/applier"
"github.com/aquasecurity/trivy/pkg/fanal/artifact"
aimage "github.com/aquasecurity/trivy/pkg/fanal/artifact/image"
"github.com/aquasecurity/trivy/pkg/fanal/cache"
"github.com/aquasecurity/trivy/pkg/fanal/image"
testdocker "github.com/aquasecurity/trivy/pkg/fanal/test/integration/docker"
"github.com/aquasecurity/trivy/pkg/fanal/types"

View File

@@ -1,12 +1,11 @@
package flag
import (
"fmt"
"strings"
"time"
"github.com/samber/lo"
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy/pkg/cache"
)
// e.g. config yaml:
@@ -70,18 +69,8 @@ type CacheFlagGroup struct {
}
type CacheOptions struct {
ClearCache bool
CacheBackend string
CacheTTL time.Duration
RedisTLS bool
RedisOptions
}
// RedisOptions holds the options for redis cache
type RedisOptions struct {
RedisCACert string
RedisCert string
RedisKey string
ClearCache bool
CacheBackendOptions cache.Options
}
// NewCacheFlagGroup returns a default CacheFlagGroup
@@ -118,43 +107,14 @@ func (fg *CacheFlagGroup) ToOptions() (CacheOptions, error) {
return CacheOptions{}, err
}
cacheBackend := fg.CacheBackend.Value()
redisOptions := RedisOptions{
RedisCACert: fg.RedisCACert.Value(),
RedisCert: fg.RedisCert.Value(),
RedisKey: fg.RedisKey.Value(),
}
// "redis://" or "fs" are allowed for now
// An empty value is also allowed for testability
if !strings.HasPrefix(cacheBackend, "redis://") &&
cacheBackend != "fs" && cacheBackend != "" {
return CacheOptions{}, xerrors.Errorf("unsupported cache backend: %s", cacheBackend)
}
// if one of redis option not nil, make sure CA, cert, and key provided
if !lo.IsEmpty(redisOptions) {
if redisOptions.RedisCACert == "" || redisOptions.RedisCert == "" || redisOptions.RedisKey == "" {
return CacheOptions{}, xerrors.Errorf("you must provide Redis CA, cert and key file path when using TLS")
}
backendOpts, err := cache.NewOptions(fg.CacheBackend.Value(), fg.RedisCACert.Value(), fg.RedisCert.Value(),
fg.RedisKey.Value(), fg.RedisTLS.Value(), fg.CacheTTL.Value())
if err != nil {
return CacheOptions{}, xerrors.Errorf("failed to initialize cache options: %w", err)
}
return CacheOptions{
ClearCache: fg.ClearCache.Value(),
CacheBackend: cacheBackend,
CacheTTL: fg.CacheTTL.Value(),
RedisTLS: fg.RedisTLS.Value(),
RedisOptions: redisOptions,
ClearCache: fg.ClearCache.Value(),
CacheBackendOptions: backendOpts,
}, nil
}
// CacheBackendMasked returns the redis connection string masking credentials
func (o *CacheOptions) CacheBackendMasked() string {
endIndex := strings.Index(o.CacheBackend, "@")
if endIndex == -1 {
return o.CacheBackend
}
startIndex := strings.Index(o.CacheBackend, "//")
return fmt.Sprintf("%s****%s", o.CacheBackend[:startIndex+2], o.CacheBackend[endIndex:])
}

View File

@@ -1,160 +0,0 @@
package flag_test
import (
"testing"
"time"
"github.com/spf13/viper"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/aquasecurity/trivy/pkg/flag"
)
func TestCacheFlagGroup_ToOptions(t *testing.T) {
type fields struct {
ClearCache bool
CacheBackend string
CacheTTL time.Duration
RedisTLS bool
RedisCACert string
RedisCert string
RedisKey string
}
tests := []struct {
name string
fields fields
want flag.CacheOptions
assertion require.ErrorAssertionFunc
}{
{
name: "fs",
fields: fields{
CacheBackend: "fs",
},
want: flag.CacheOptions{
CacheBackend: "fs",
},
assertion: require.NoError,
},
{
name: "redis",
fields: fields{
CacheBackend: "redis://localhost:6379",
},
want: flag.CacheOptions{
CacheBackend: "redis://localhost:6379",
},
assertion: require.NoError,
},
{
name: "redis tls",
fields: fields{
CacheBackend: "redis://localhost:6379",
RedisCACert: "ca-cert.pem",
RedisCert: "cert.pem",
RedisKey: "key.pem",
},
want: flag.CacheOptions{
CacheBackend: "redis://localhost:6379",
RedisOptions: flag.RedisOptions{
RedisCACert: "ca-cert.pem",
RedisCert: "cert.pem",
RedisKey: "key.pem",
},
},
assertion: require.NoError,
},
{
name: "redis tls with public certificates",
fields: fields{
CacheBackend: "redis://localhost:6379",
RedisTLS: true,
},
want: flag.CacheOptions{
CacheBackend: "redis://localhost:6379",
RedisTLS: true,
},
assertion: require.NoError,
},
{
name: "unknown backend",
fields: fields{
CacheBackend: "unknown",
},
assertion: func(t require.TestingT, err error, msgs ...any) {
require.ErrorContains(t, err, "unsupported cache backend")
},
},
{
name: "sad redis tls",
fields: fields{
CacheBackend: "redis://localhost:6379",
RedisCACert: "ca-cert.pem",
},
assertion: func(t require.TestingT, err error, msgs ...any) {
require.ErrorContains(t, err, "you must provide Redis CA")
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
viper.Set(flag.ClearCacheFlag.ConfigName, tt.fields.ClearCache)
viper.Set(flag.CacheBackendFlag.ConfigName, tt.fields.CacheBackend)
viper.Set(flag.CacheTTLFlag.ConfigName, tt.fields.CacheTTL)
viper.Set(flag.RedisTLSFlag.ConfigName, tt.fields.RedisTLS)
viper.Set(flag.RedisCACertFlag.ConfigName, tt.fields.RedisCACert)
viper.Set(flag.RedisCertFlag.ConfigName, tt.fields.RedisCert)
viper.Set(flag.RedisKeyFlag.ConfigName, tt.fields.RedisKey)
f := &flag.CacheFlagGroup{
ClearCache: flag.ClearCacheFlag.Clone(),
CacheBackend: flag.CacheBackendFlag.Clone(),
CacheTTL: flag.CacheTTLFlag.Clone(),
RedisTLS: flag.RedisTLSFlag.Clone(),
RedisCACert: flag.RedisCACertFlag.Clone(),
RedisCert: flag.RedisCertFlag.Clone(),
RedisKey: flag.RedisKeyFlag.Clone(),
}
got, err := f.ToOptions()
tt.assertion(t, err)
assert.Equalf(t, tt.want, got, "ToOptions()")
})
}
}
func TestCacheOptions_CacheBackendMasked(t *testing.T) {
type fields struct {
backend string
}
tests := []struct {
name string
fields fields
want string
}{
{
name: "redis cache backend masked",
fields: fields{
backend: "redis://root:password@localhost:6379",
},
want: "redis://****@localhost:6379",
},
{
name: "redis cache backend masked does nothing",
fields: fields{
backend: "redis://localhost:6379",
},
want: "redis://localhost:6379",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := &flag.CacheOptions{
CacheBackend: tt.fields.backend,
}
assert.Equal(t, tt.want, c.CacheBackendMasked())
})
}
}

View File

@@ -6,7 +6,7 @@ import (
"github.com/spf13/cobra"
"github.com/aquasecurity/trivy/pkg/utils/fsutils"
"github.com/aquasecurity/trivy/pkg/cache"
)
var (
@@ -55,7 +55,7 @@ var (
CacheDirFlag = Flag[string]{
Name: "cache-dir",
ConfigName: "cache.dir",
Default: fsutils.CacheDir(),
Default: cache.Dir(),
Usage: "cache directory",
Persistent: true,
}

View File

@@ -6,7 +6,7 @@ package k8s
import (
"github.com/google/wire"
"github.com/aquasecurity/trivy/pkg/fanal/cache"
"github.com/aquasecurity/trivy/pkg/cache"
)
func initializeScanK8s(localArtifactCache cache.LocalArtifactCache) *ScanKubernetes {

View File

@@ -9,7 +9,7 @@ package k8s
import (
"github.com/aquasecurity/trivy-db/pkg/db"
"github.com/aquasecurity/trivy/pkg/fanal/applier"
"github.com/aquasecurity/trivy/pkg/fanal/cache"
"github.com/aquasecurity/trivy/pkg/cache"
"github.com/aquasecurity/trivy/pkg/scanner/langpkg"
"github.com/aquasecurity/trivy/pkg/scanner/local"
"github.com/aquasecurity/trivy/pkg/scanner/ospkg"

View File

@@ -14,9 +14,9 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/aquasecurity/trivy/pkg/cache"
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/oci"
"github.com/aquasecurity/trivy/pkg/utils/fsutils"
)
type fakeLayer struct {
@@ -97,7 +97,7 @@ func TestArtifact_Download(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
tempDir := t.TempDir()
fsutils.SetCacheDir(tempDir)
cache.SetDir(tempDir)
// Mock image
img := new(fakei.FakeImage)

View File

@@ -12,13 +12,13 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/aquasecurity/trivy/pkg/cache"
"github.com/aquasecurity/trivy/pkg/plugin"
"github.com/aquasecurity/trivy/pkg/utils/fsutils"
)
func TestManager_Update(t *testing.T) {
tempDir := t.TempDir()
fsutils.SetCacheDir(tempDir)
cache.SetDir(tempDir)
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, err := w.Write([]byte(`this is index`))
@@ -73,7 +73,7 @@ bar A bar plugin
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fsutils.SetCacheDir(tt.dir)
cache.SetDir(tt.dir)
var got bytes.Buffer
m := plugin.NewManager(plugin.WithWriter(&got))

View File

@@ -14,6 +14,7 @@ import (
"gopkg.in/yaml.v3"
"github.com/aquasecurity/go-version/pkg/semver"
"github.com/aquasecurity/trivy/pkg/cache"
"github.com/aquasecurity/trivy/pkg/downloader"
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/log"
@@ -63,7 +64,7 @@ func NewManager(opts ...ManagerOption) *Manager {
indexURL: indexURL,
logger: log.WithPrefix("plugin"),
pluginRoot: filepath.Join(fsutils.HomeDir(), pluginsRelativeDir),
indexPath: filepath.Join(fsutils.CacheDir(), "plugin", "index.yaml"),
indexPath: filepath.Join(cache.Dir(), "plugin", "index.yaml"),
}
for _, opt := range opts {
opt(m)

View File

@@ -20,11 +20,11 @@ import (
"github.com/stretchr/testify/require"
"github.com/aquasecurity/trivy/internal/gittest"
"github.com/aquasecurity/trivy/pkg/cache"
"github.com/aquasecurity/trivy/pkg/clock"
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/plugin"
"github.com/aquasecurity/trivy/pkg/utils/fsutils"
)
func setupGitRepository(t *testing.T, repo, dir string) *httptest.Server {
@@ -200,7 +200,7 @@ func TestManager_Install(t *testing.T) {
t.Setenv("XDG_DATA_HOME", dst)
// For plugin index
fsutils.SetCacheDir("testdata")
cache.SetDir("testdata")
if tt.installed != nil {
setupInstalledPlugin(t, dst, *tt.installed)

View File

@@ -6,7 +6,7 @@ package server
import (
"github.com/google/wire"
"github.com/aquasecurity/trivy/pkg/fanal/cache"
"github.com/aquasecurity/trivy/pkg/cache"
)
func initializeScanServer(localArtifactCache cache.LocalArtifactCache) *ScanServer {

View File

@@ -15,8 +15,8 @@ import (
"github.com/aquasecurity/trivy-db/pkg/db"
"github.com/aquasecurity/trivy-db/pkg/metadata"
"github.com/aquasecurity/trivy/pkg/cache"
dbc "github.com/aquasecurity/trivy/pkg/db"
"github.com/aquasecurity/trivy/pkg/fanal/cache"
"github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/utils/fsutils"

View File

@@ -17,9 +17,9 @@ import (
trivydb "github.com/aquasecurity/trivy-db/pkg/db"
"github.com/aquasecurity/trivy-db/pkg/metadata"
"github.com/aquasecurity/trivy/internal/dbtest"
"github.com/aquasecurity/trivy/pkg/cache"
"github.com/aquasecurity/trivy/pkg/clock"
"github.com/aquasecurity/trivy/pkg/db"
"github.com/aquasecurity/trivy/pkg/fanal/cache"
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/policy"
"github.com/aquasecurity/trivy/pkg/version"

View File

@@ -8,7 +8,7 @@ import (
"golang.org/x/xerrors"
"google.golang.org/protobuf/types/known/emptypb"
"github.com/aquasecurity/trivy/pkg/fanal/cache"
"github.com/aquasecurity/trivy/pkg/cache"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/rpc"
"github.com/aquasecurity/trivy/pkg/scanner"

View File

@@ -15,7 +15,7 @@ import (
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy-db/pkg/utils"
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability"
"github.com/aquasecurity/trivy/pkg/fanal/cache"
"github.com/aquasecurity/trivy/pkg/cache"
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/scanner"
"github.com/aquasecurity/trivy/pkg/types"

View File

@@ -9,7 +9,7 @@ package server
import (
"github.com/aquasecurity/trivy-db/pkg/db"
"github.com/aquasecurity/trivy/pkg/fanal/applier"
"github.com/aquasecurity/trivy/pkg/fanal/cache"
"github.com/aquasecurity/trivy/pkg/cache"
"github.com/aquasecurity/trivy/pkg/scanner/langpkg"
"github.com/aquasecurity/trivy/pkg/scanner/local"
"github.com/aquasecurity/trivy/pkg/scanner/ospkg"

View File

@@ -18,30 +18,6 @@ const (
xdgDataHome = "XDG_DATA_HOME"
)
var cacheDir string
// defaultCacheDir returns/creates the cache-dir to be used for trivy operations
func defaultCacheDir() string {
tmpDir, err := os.UserCacheDir()
if err != nil {
tmpDir = os.TempDir()
}
return filepath.Join(tmpDir, "trivy")
}
// CacheDir returns the directory used for caching
func CacheDir() string {
if cacheDir == "" {
return defaultCacheDir()
}
return cacheDir
}
// SetCacheDir sets the trivy cacheDir
func SetCacheDir(dir string) {
cacheDir = dir
}
func HomeDir() string {
dataHome := os.Getenv(xdgDataHome)
if dataHome != "" {