Add linter check support (#679)

* add linter supports

* add only minor version

* use latest version

* Fix println with format issue

* Fix test

* Fix tests

* For slice with unknown length, preallocating the array

* fix code-coverage

* Removed linter rules

* Reverting linter fixes, adding TODO for later

* Ignore linter error for import

* Remove another err var.

* Ignore shadow error

* Fixes

* Fix issue

* Add back goimports local-prefixes

* Update local prefixes

* Removed extra spaces and merge the imports

* more refactoring

* Update photon.go

Co-authored-by: Teppei Fukuda <knqyf263@gmail.com>
This commit is contained in:
rahul2393
2020-10-20 17:50:04 +05:30
committed by GitHub
parent 4a94477532
commit 793a1aa3c8
67 changed files with 519 additions and 148 deletions

View File

@@ -1,6 +1,17 @@
name: Test
on: pull_request
jobs:
golangci:
name: lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: golangci-lint
uses: golangci/golangci-lint-action@v2
with:
version: v1.31
args: --deadline=30m
integration:
name: Integration Test
runs-on: ubuntu-latest
@@ -34,3 +45,4 @@ jobs:
with:
version: latest
args: release --snapshot --rm-dist --skip-publish

71
.golangci.yaml Normal file
View File

@@ -0,0 +1,71 @@
linters-settings:
errcheck:
check-type-assertions: true
check-blank: true
govet:
check-shadowing: false
gofmt:
simplify: false
golint:
min-confidence: 0
gocyclo:
min-complexity: 10
maligned:
suggest-new: true
dupl:
threshold: 100
goconst:
min-len: 3
min-occurrences: 3
misspell:
locale: US
goimports:
local-prefixes: github.com/aquasecurity
linters:
disable-all: true
enable:
- structcheck
- ineffassign
- typecheck
- govet
- errcheck
- varcheck
- deadcode
- golint
- gosec
- unconvert
- goconst
- gocyclo
- gofmt
- goimports
- maligned
- misspell
run:
skip-files:
- ".*._mock.go$"
- ".*._test.go$"
- "integration/*"
issues:
exclude-rules:
- linters:
- gosec
text: "G304: Potential file inclusion"
- linters:
- gosec
text: "Deferring unsafe method"
- linters:
- errcheck
text: "Close` is not checked"
- linters:
- errcheck
text: "os.*` is not checked"
- linters:
- golint
text: "a blank import should be only in a main or test package"
exclude:
- "should have a package comment, unless it's in another file for this package"
exclude-use-default: false
max-same-issues: 0

2
go.mod
View File

@@ -34,4 +34,4 @@ require (
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1
k8s.io/utils v0.0.0-20191114184206-e782cd3c129f
)
)

2
go.sum
View File

@@ -748,4 +748,4 @@ moul.io/http2curl v1.0.0 h1:6XwpyZOYsgZJrU8exnG87ncVkU1FVCcTRpwzOkTDUi8=
moul.io/http2curl v1.0.0/go.mod h1:f6cULg+e4Md/oW1cYmwW4IWQOVl2lGbmCNGOHvzX2kE=
sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
sigs.k8s.io/structured-merge-diff v1.0.1-0.20191108220359-b1b620dd3f06/go.mod h1:/ULNhyfzRopfcjskuui0cTITekDduZ7ycKN3oUT9R18=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=

View File

@@ -15,12 +15,14 @@ import (
"github.com/docker/docker/api/types"
)
// RegistryConfig holds the config for docker registry
type RegistryConfig struct {
URL *url.URL
Username string
Password string
}
// GetAuthConfig returns the docker registry authConfig
func (c RegistryConfig) GetAuthConfig() types.AuthConfig {
return types.AuthConfig{
Username: c.Username,
@@ -29,6 +31,7 @@ func (c RegistryConfig) GetAuthConfig() types.AuthConfig {
}
}
// GetRegistryAuth returns the json encoded docker registry auth
func (c RegistryConfig) GetRegistryAuth() (string, error) {
authConfig := types.AuthConfig{
Username: c.Username,
@@ -41,10 +44,12 @@ func (c RegistryConfig) GetRegistryAuth() (string, error) {
return base64.URLEncoding.EncodeToString(encodedJSON), nil
}
// Docker returns docker client
type Docker struct {
cli *client.Client
}
// New is the factory method to return docker client
func New() (Docker, error) {
cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
if err != nil {
@@ -73,7 +78,7 @@ func (d Docker) ReplicateImage(ctx context.Context, imageRef, imagePath string,
if err != nil {
return err
}
if _, err := io.Copy(ioutil.Discard, resp.Body); err != nil {
if _, err = io.Copy(ioutil.Discard, resp.Body); err != nil {
return err
}
defer resp.Body.Close()

View File

@@ -20,6 +20,7 @@ import (
"github.com/aquasecurity/trivy/pkg/vulnerability"
)
// VersionInfo holds the trivy DB version Info
type VersionInfo struct {
Version string `json:",omitempty"`
VulnerabilityDB *db.Metadata `json:",omitempty"`
@@ -250,6 +251,7 @@ var (
}
)
// NewApp is the factory method to return Trivy CLI
func NewApp(version string) *cli.App {
cli.VersionPrinter = func(c *cli.Context) {
showVersion(c.String("cache-dir"), c.String("format"), c.App.Version, c.App.Writer)
@@ -307,7 +309,7 @@ func setHidden(flags []cli.Flag, hidden bool) []cli.Flag {
func showVersion(cacheDir, outputFormat, version string, outputWriter io.Writer) {
var dbMeta *db.Metadata
metadata, _ := tdb.NewMetadata(afero.NewOsFs(), cacheDir).Get()
metadata, _ := tdb.NewMetadata(afero.NewOsFs(), cacheDir).Get() // nolint: errcheck
if !metadata.UpdatedAt.IsZero() && !metadata.NextUpdate.IsZero() && metadata.Version != 0 {
dbMeta = &db.Metadata{
Version: metadata.Version,
@@ -319,7 +321,7 @@ func showVersion(cacheDir, outputFormat, version string, outputWriter io.Writer)
switch outputFormat {
case "json":
b, _ := json.Marshal(VersionInfo{
b, _ := json.Marshal(VersionInfo{ // nolint: errcheck
Version: version,
VulnerabilityDB: dbMeta,
})
@@ -345,6 +347,7 @@ func showVersion(cacheDir, outputFormat, version string, outputWriter io.Writer)
}
}
// NewImageCommand is the factory method to add image command
func NewImageCommand() *cli.Command {
return &cli.Command{
Name: "image",
@@ -356,6 +359,7 @@ func NewImageCommand() *cli.Command {
}
}
// NewFilesystemCommand is the factory method to add filesystem command
func NewFilesystemCommand() *cli.Command {
return &cli.Command{
Name: "filesystem",
@@ -389,6 +393,7 @@ func NewFilesystemCommand() *cli.Command {
}
}
// NewRepositoryCommand is the factory method to add repository command
func NewRepositoryCommand() *cli.Command {
return &cli.Command{
Name: "repository",
@@ -422,6 +427,7 @@ func NewRepositoryCommand() *cli.Command {
}
}
// NewClientCommand is the factory method to add client command
func NewClientCommand() *cli.Command {
return &cli.Command{
Name: "client",
@@ -465,6 +471,7 @@ func NewClientCommand() *cli.Command {
}
}
// NewServerCommand is the factory method to add server command
func NewServerCommand() *cli.Command {
return &cli.Command{
Name: "server",

View File

@@ -109,3 +109,12 @@ Vulnerability DB:
})
}
}
func TestNewCommands(t *testing.T) {
NewApp("test")
NewClientCommand()
NewFilesystemCommand()
NewImageCommand()
NewRepositoryCommand()
NewServerCommand()
}

View File

@@ -7,6 +7,7 @@ import (
"github.com/aquasecurity/trivy/internal/config"
)
// Config holds the artifact config
type Config struct {
config.GlobalConfig
config.ArtifactConfig
@@ -22,6 +23,7 @@ type Config struct {
autoRefresh bool
}
// New is the factory method to return config
func New(c *cli.Context) (Config, error) {
gc, err := config.NewGlobalConfig(c)
if err != nil {
@@ -41,6 +43,7 @@ func New(c *cli.Context) (Config, error) {
}, nil
}
// Init initializes the artifact config
func (c *Config) Init(image bool) error {
if err := c.ReportConfig.Init(c.Logger); err != nil {
return err
@@ -53,7 +56,7 @@ func (c *Config) Init(image bool) error {
}
// --clear-cache, --download-db-only and --reset don't conduct the scan
if c.ClearCache || c.DownloadDBOnly || c.Reset {
if c.skipScan() {
return nil
}
@@ -69,3 +72,10 @@ func (c *Config) Init(image bool) error {
return nil
}
func (c *Config) skipScan() bool {
if c.ClearCache || c.DownloadDBOnly || c.Reset {
return true
}
return false
}

View File

@@ -21,6 +21,7 @@ func filesystemScanner(ctx context.Context, dir string, ac cache.ArtifactCache,
return s, cleanup, nil
}
// FilesystemRun runs scan on filesystem
func FilesystemRun(cliCtx *cli.Context) error {
c, err := config.New(cliCtx)
if err != nil {

View File

@@ -30,6 +30,7 @@ func dockerScanner(ctx context.Context, imageName string, ac cache.ArtifactCache
return s, cleanup, nil
}
// ImageRun runs scan on docker image
func ImageRun(cliCtx *cli.Context) error {
c, err := config.New(cliCtx)
if err != nil {

View File

@@ -21,6 +21,7 @@ func repositoryScanner(ctx context.Context, dir string, ac cache.ArtifactCache,
return s, cleanup, nil
}
// RepositoryRun runs scan on repository
func RepositoryRun(cliCtx *cli.Context) error {
c, err := config.New(cliCtx)
if err != nil {

View File

@@ -19,9 +19,12 @@ import (
"github.com/aquasecurity/trivy/pkg/utils"
)
// InitializeScanner type to define initialize function signature
type InitializeScanner func(context.Context, string, cache.ArtifactCache, cache.LocalArtifactCache, time.Duration) (
scanner.Scanner, func(), error)
// nolint: gocyclo
// TODO: refactror and fix cyclometic complexity
func run(c config.Config, initializeScanner InitializeScanner) error {
if err := log.InitLogger(c.Debug, c.Quiet); err != nil {
l.Fatal(err)

View File

@@ -10,6 +10,7 @@ import (
"github.com/aquasecurity/trivy/internal/config"
)
// Config holds the Trivy client config
type Config struct {
config.GlobalConfig
config.ArtifactConfig
@@ -25,6 +26,7 @@ type Config struct {
CustomHeaders http.Header
}
// New is the factory method for Config
func New(c *cli.Context) (Config, error) {
gc, err := config.NewGlobalConfig(c)
if err != nil {
@@ -43,6 +45,7 @@ func New(c *cli.Context) (Config, error) {
}, nil
}
// Init initializes the config
func (c *Config) Init() (err error) {
// --clear-cache doesn't conduct the scan
if c.ClearCache {

View File

@@ -17,6 +17,7 @@ import (
"github.com/aquasecurity/trivy/pkg/utils"
)
// Run runs the scan
func Run(cliCtx *cli.Context) error {
c, err := config.New(cliCtx)
if err != nil {
@@ -25,6 +26,8 @@ func Run(cliCtx *cli.Context) error {
return run(c)
}
// nolint: gocyclo
// TODO: refactror and fix cyclometic complexity
func run(c config.Config) (err error) {
if err = log.InitLogger(c.Debug, c.Quiet); err != nil {
return xerrors.Errorf("failed to initialize a logger: %w", err)

View File

@@ -10,6 +10,7 @@ import (
"golang.org/x/xerrors"
)
// ArtifactConfig holds the config for a artifact scanning
type ArtifactConfig struct {
Input string
Timeout time.Duration
@@ -24,6 +25,7 @@ type ArtifactConfig struct {
Target string
}
// NewArtifactConfig is the factory method to return artifact config
func NewArtifactConfig(c *cli.Context) ArtifactConfig {
return ArtifactConfig{
Input: c.String("input"),
@@ -34,10 +36,11 @@ func NewArtifactConfig(c *cli.Context) ArtifactConfig {
}
}
// Init initialize the CLI context for artifact scanning
func (c *ArtifactConfig) Init(ctx *cli.Context, logger *zap.SugaredLogger) (err error) {
if c.Input == "" && ctx.Args().Len() == 0 {
logger.Debug(`trivy requires at least 1 argument or --input option`)
_ = cli.ShowSubcommandHelp(ctx)
_ = cli.ShowSubcommandHelp(ctx) // nolint: errcheck
os.Exit(0)
} else if ctx.Args().Len() > 1 {
logger.Error(`multiple targets cannot be specified`)

View File

@@ -5,6 +5,7 @@ import (
"golang.org/x/xerrors"
)
// DBConfig holds the config for trivy DB
type DBConfig struct {
Reset bool
DownloadDBOnly bool
@@ -13,6 +14,7 @@ type DBConfig struct {
NoProgress bool
}
// NewDBConfig is the factory method to return the DBConfig
func NewDBConfig(c *cli.Context) DBConfig {
return DBConfig{
Reset: c.Bool("reset"),
@@ -23,6 +25,7 @@ func NewDBConfig(c *cli.Context) DBConfig {
}
}
// Init initialize the DBConfig
func (c *DBConfig) Init() (err error) {
if c.SkipUpdate && c.DownloadDBOnly {
return xerrors.New("--skip-update and --download-db-only options can not be specified both")

View File

@@ -8,6 +8,7 @@ import (
"github.com/aquasecurity/trivy/pkg/log"
)
// GlobalConfig holds the global config for trivy
type GlobalConfig struct {
Context *cli.Context
Logger *zap.SugaredLogger
@@ -18,6 +19,7 @@ type GlobalConfig struct {
CacheDir string
}
// NewGlobalConfig is the factory method to return GlobalConfig
func NewGlobalConfig(c *cli.Context) (GlobalConfig, error) {
quiet := c.Bool("quiet")
debug := c.Bool("debug")

View File

@@ -7,11 +7,13 @@ import (
"golang.org/x/xerrors"
)
// ImageConfig holds the config for scanning images
type ImageConfig struct {
ScanRemovedPkgs bool
ListAllPkgs bool
}
// NewImageConfig is the factory method to return imageConfig
func NewImageConfig(c *cli.Context) ImageConfig {
return ImageConfig{
ScanRemovedPkgs: c.Bool("removed-pkgs"),
@@ -19,6 +21,7 @@ func NewImageConfig(c *cli.Context) ImageConfig {
}
}
// Init initializes the imageConfig
func (c *ImageConfig) Init(args cli.Args, logger *zap.SugaredLogger) (err error) {
imageName := args.First()

View File

@@ -11,6 +11,7 @@ import (
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
)
// ReportConfig holds the config for reporting scan results
type ReportConfig struct {
Format string
Template string
@@ -31,6 +32,7 @@ type ReportConfig struct {
Severities []dbTypes.Severity
}
// NewReportConfig is the factory method to return ReportConfig
func NewReportConfig(c *cli.Context) ReportConfig {
return ReportConfig{
output: c.String("output"),
@@ -46,6 +48,7 @@ func NewReportConfig(c *cli.Context) ReportConfig {
}
}
// Init initializes the ReportConfig
func (c *ReportConfig) Init(logger *zap.SugaredLogger) (err error) {
if c.Template != "" {
if c.Format == "" {

View File

@@ -15,20 +15,24 @@ import (
"github.com/aquasecurity/trivy/pkg/utils"
)
// 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 {
client cache.LocalArtifactCache
}
// NewCache is the factory method for Cache
func NewCache(client cache.LocalArtifactCache) Cache {
return Cache{client: client}
}
// 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)
@@ -39,6 +43,7 @@ func (c Cache) Reset() (err error) {
return nil
}
// ClearDB clears the DB cache
func (c Cache) ClearDB() (err error) {
log.Logger.Info("Removing DB file...")
if err = os.RemoveAll(utils.CacheDir()); err != nil {
@@ -47,6 +52,7 @@ func (c Cache) ClearDB() (err error) {
return nil
}
// ClearImages clears the cache images
func (c Cache) ClearImages() error {
log.Logger.Info("Removing image caches...")
if err := c.client.Clear(); err != nil {
@@ -55,6 +61,7 @@ func (c Cache) ClearImages() error {
return nil
}
// DownloadDB downloads the DB
func DownloadDB(appVersion, cacheDir string, quiet, light, skipUpdate bool) error {
client := initializeDBClient(cacheDir, quiet)
ctx := context.Background()
@@ -66,7 +73,7 @@ func DownloadDB(appVersion, cacheDir string, quiet, light, skipUpdate bool) erro
if needsUpdate {
log.Logger.Info("Need to update DB")
log.Logger.Info("Downloading DB...")
if err := client.Download(ctx, cacheDir, light); err != nil {
if err = client.Download(ctx, cacheDir, light); err != nil {
return xerrors.Errorf("failed to download vulnerability DB: %w", err)
}
if err = client.UpdateMetadata(cacheDir); err != nil {

View File

@@ -1,10 +1,12 @@
package config
import (
"github.com/aquasecurity/trivy/internal/config"
"github.com/urfave/cli/v2"
"github.com/aquasecurity/trivy/internal/config"
)
// Config holds the Trivy config
type Config struct {
config.GlobalConfig
config.DBConfig
@@ -14,10 +16,10 @@ type Config struct {
TokenHeader string
}
// New is the factory method to return cofig
func New(c *cli.Context) Config {
// the error is ignored because logger is unnecessary
gc, _ := config.NewGlobalConfig(c)
gc, _ := config.NewGlobalConfig(c) // nolint: errcheck
return Config{
GlobalConfig: gc,
DBConfig: config.NewDBConfig(c),
@@ -28,6 +30,7 @@ func New(c *cli.Context) Config {
}
}
// Init initializes the DB config
func (c *Config) Init() (err error) {
if err := c.DBConfig.Init(); err != nil {
return err

View File

@@ -13,6 +13,7 @@ import (
"github.com/aquasecurity/trivy/pkg/utils"
)
// Run runs the scan
func Run(ctx *cli.Context) error {
return run(config.New(ctx))
}

View File

@@ -32,7 +32,7 @@ func debianEOL() {
if len(fields) < 6 && fields[0] != "" {
fmt.Printf("\"%s\": time.Date(3000, 1, 1, 23, 59, 59, 0, time.UTC),\n", fields[0])
} else if len(fields) == 6 {
eol, _ := time.Parse("2006-1-2", fields[5])
eol, _ := time.Parse("2006-1-2", fields[5]) // nolint: errcheck
fmt.Printf("\"%s\": time.Date(%d, %d, %d, 23, 59, 59, 0, time.UTC),\n", fields[0], eol.Year(), eol.Month(), eol.Day())
}
}
@@ -49,8 +49,7 @@ func ubuntuEOL() {
for scanner.Scan() {
line := scanner.Text()
fields := strings.Split(line, ",")
eol, _ := time.Parse("2006-1-2", fields[len(fields)-1])
eol, _ := time.Parse("2006-1-2", fields[len(fields)-1]) // nolint: errcheck
fmt.Printf("\"%s\": time.Date(%d, %d, %d, 23, 59, 59, 0, time.UTC),\n", strings.Fields(fields[0])[0], eol.Year(), eol.Month(), eol.Day())
}
}

10
pkg/cache/remote.go vendored
View File

@@ -13,35 +13,41 @@ import (
rpcCache "github.com/aquasecurity/trivy/rpc/cache"
)
// RemoteCache implements remote cache
type RemoteCache struct {
ctx context.Context // for custom header
client rpcCache.Cache
}
// RemoteURL to hold remote host
type RemoteURL string
// NewRemoteCache is the factory method for RemoteCache
func NewRemoteCache(url RemoteURL, customHeaders http.Header) cache.ArtifactCache {
ctx := client.WithCustomHeaders(context.Background(), customHeaders)
c := rpcCache.NewCacheProtobufClient(string(url), &http.Client{})
return &RemoteCache{ctx: ctx, client: c}
}
// PutArtifact sends artifact to remote client
func (c RemoteCache) PutArtifact(imageID string, imageInfo types.ArtifactInfo) error {
_, err := c.client.PutArtifact(c.ctx, rpc.ConvertToRpcArtifactInfo(imageID, imageInfo))
_, err := c.client.PutArtifact(c.ctx, rpc.ConvertToRPCArtifactInfo(imageID, imageInfo))
if err != nil {
return xerrors.Errorf("unable to store cache on the server: %w", err)
}
return nil
}
// PutBlob sends blobInfo to remote client
func (c RemoteCache) PutBlob(diffID string, layerInfo types.BlobInfo) error {
_, err := c.client.PutBlob(c.ctx, rpc.ConvertToRpcBlobInfo(diffID, layerInfo))
_, err := c.client.PutBlob(c.ctx, rpc.ConvertToRPCBlobInfo(diffID, layerInfo))
if err != nil {
return xerrors.Errorf("unable to store cache on the server: %w", err)
}
return nil
}
// MissingBlobs fetches missing blobs from RemoteCache
func (c RemoteCache) MissingBlobs(imageID string, layerIDs []string) (bool, []string, error) {
layers, err := c.client.MissingBlobs(c.ctx, rpc.ConvertToMissingBlobsRequest(imageID, layerIDs))
if err != nil {

View File

@@ -24,8 +24,11 @@ const (
lightDB = "trivy-light.db.gz"
metadataFile = "metadata.json"
gb = 1024 * 1024 * 1024
)
// SuperSet binds the dependencies
var SuperSet = wire.NewSet(
// indicator.ProgressBar
indicator.NewProgressBar,
@@ -51,6 +54,7 @@ var SuperSet = wire.NewSet(
wire.Bind(new(Operation), new(Client)),
)
// Operation defines the DB operations
type Operation interface {
NeedsUpdate(cliVersion string, skip, light bool) (need bool, err error)
Download(ctx context.Context, cacheDir string, light bool) (err error)
@@ -62,6 +66,7 @@ type dbOperation interface {
StoreMetadata(metadata db.Metadata, dir string) (err error)
}
// Client implements DB operations
type Client struct {
dbc dbOperation
githubClient github.Operation
@@ -70,6 +75,7 @@ type Client struct {
metadata Metadata
}
// NewClient is the factory method for DB client
func NewClient(dbc dbOperation, githubClient github.Operation, pb indicator.ProgressBar, clock clock.Clock, metadata Metadata) Client {
return Client{
dbc: dbc,
@@ -80,6 +86,7 @@ func NewClient(dbc dbOperation, githubClient github.Operation, pb indicator.Prog
}
}
// NeedsUpdate check is DB needs update
func (c Client) NeedsUpdate(cliVersion string, light, skip bool) (bool, error) {
dbType := db.TypeFull
if light {
@@ -103,18 +110,11 @@ func (c Client) NeedsUpdate(cliVersion string, light, skip bool) (bool, error) {
}
if skip {
if db.SchemaVersion != metadata.Version {
log.Logger.Error("The local DB is old and needs to be updated")
return false, xerrors.New("--skip-update cannot be specified with the old DB")
} else if metadata.Type != dbType {
if dbType == db.TypeFull {
log.Logger.Error("The local DB is a lightweight DB. You have to download a full DB")
} else {
log.Logger.Error("The local DB is a full DB. You have to download a lightweight DB")
}
return false, xerrors.New("--skip-update cannot be specified with the different schema DB")
if err = c.validate(dbType, metadata); err != nil {
return false, err
}
return false, nil
}
if db.SchemaVersion == metadata.Version && metadata.Type == dbType &&
@@ -125,6 +125,22 @@ func (c Client) NeedsUpdate(cliVersion string, light, skip bool) (bool, error) {
return true, nil
}
func (c Client) validate(dbType db.Type, metadata db.Metadata) error {
if db.SchemaVersion != metadata.Version {
log.Logger.Error("The local DB is old and needs to be updated")
return xerrors.New("--skip-update cannot be specified with the old DB")
} else if metadata.Type != dbType {
if dbType == db.TypeFull {
log.Logger.Error("The local DB is a lightweight DB. You have to download a full DB")
} else {
log.Logger.Error("The local DB is a full DB. You have to download a lightweight DB")
}
return xerrors.New("--skip-update cannot be specified with the different schema DB")
}
return nil
}
// Download downloads the DB file
func (c Client) Download(ctx context.Context, cacheDir string, light bool) error {
// Remove the metadata file before downloading DB
if err := c.metadata.Delete(); err != nil {
@@ -145,7 +161,6 @@ func (c Client) Download(ctx context.Context, cacheDir string, light bool) error
bar := c.pb.Start(int64(size))
barReader := bar.NewProxyReader(rc)
defer bar.Finish()
gr, err := gzip.NewReader(barReader)
if err != nil {
return xerrors.Errorf("invalid gzip file: %w", err)
@@ -164,13 +179,15 @@ func (c Client) Download(ctx context.Context, cacheDir string, light bool) error
}
defer file.Close()
if _, err = io.Copy(file, gr); err != nil {
limited := io.LimitReader(gr, 2*gb)
if _, err = io.Copy(file, limited); err != nil {
return xerrors.Errorf("failed to save DB file: %w", err)
}
return nil
}
// UpdateMetadata updates the DB metadata
func (c Client) UpdateMetadata(cacheDir string) error {
log.Logger.Debug("Updating database metadata...")
@@ -192,11 +209,13 @@ func (c Client) UpdateMetadata(cacheDir string) error {
return nil
}
// Metadata defines the file meta
type Metadata struct { // TODO: Move all Metadata things to trivy-db repo
fs afero.Fs
filePath string
}
// NewMetadata is the factory method for file Metadata
func NewMetadata(fs afero.Fs, cacheDir string) Metadata {
filePath := MetadataPath(cacheDir)
return Metadata{
@@ -205,13 +224,14 @@ func NewMetadata(fs afero.Fs, cacheDir string) Metadata {
}
}
// MetadataPath returns the metaData file path
func MetadataPath(cacheDir string) string {
dbPath := db.Path(cacheDir)
dbDir := filepath.Dir(dbPath)
return filepath.Join(dbDir, metadataFile)
}
// DeleteMetadata deletes the file of database metadata
// Delete deletes the file of database metadata
func (m Metadata) Delete() error {
if err := m.fs.Remove(m.filePath); err != nil {
return xerrors.Errorf("unable to remove the metadata file: %w", err)
@@ -219,6 +239,7 @@ func (m Metadata) Delete() error {
return nil
}
// Get returns the file metadata
func (m Metadata) Get() (db.Metadata, error) {
f, err := m.fs.Open(m.filePath)
if err != nil {

View File

@@ -19,6 +19,7 @@ type Advisory struct {
comparer comparer
}
// NewAdvisory is the factory method of Advisory
func NewAdvisory(lang string) *Advisory {
return &Advisory{
lang: lang,

View File

@@ -11,20 +11,24 @@ import (
"github.com/aquasecurity/trivy/pkg/types"
)
// VulnSrc defines the operation on bundler vulnerability
type VulnSrc interface {
Get(pkgName string) ([]bundlerSrc.Advisory, error)
}
// Advisory implements the bundler VulnSrc
type Advisory struct {
vs VulnSrc
}
// NewAdvisory is the factory method to return bundler.Advisory
func NewAdvisory() *Advisory {
return &Advisory{
vs: bundlerSrc.NewVulnSrc(),
}
}
// DetectVulnerabilities scans and returns Vulnerability in bundler
func (a *Advisory) DetectVulnerabilities(pkgName string, pkgVer *semver.Version) ([]types.DetectedVulnerability, error) {
advisories, err := a.vs.Get(pkgName)
if err != nil {

View File

@@ -52,11 +52,12 @@ func TestScanner_Detect(t *testing.T) {
}
versionStr := "1.9.25-x64-mingw32"
v, _ := semver.NewVersion(versionStr)
v, err := semver.NewVersion(versionStr)
assert.NoError(t, err)
vulns, err := s.DetectVulnerabilities("ffi", v)
assert.Nil(t, err)
assert.NoError(t, err)
assert.Equal(t, 1, len(vulns))
})
}

View File

@@ -3,24 +3,27 @@ package cargo
import (
"strings"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/Masterminds/semver/v3"
"golang.org/x/xerrors"
cargoSrc "github.com/aquasecurity/trivy-db/pkg/vulnsrc/cargo"
"github.com/aquasecurity/trivy/pkg/scanner/utils"
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy/pkg/types"
)
// Advisory encapsulates the cargo vulnerability scanner
type Advisory struct {
vs cargoSrc.VulnSrc
}
// NewAdvisory is the factory method to return cargo Scanner
func NewAdvisory() *Advisory {
return &Advisory{
vs: cargoSrc.NewVulnSrc(),
}
}
// DetectVulnerabilities scans and returns the cargo vulnerabilities
func (s *Advisory) DetectVulnerabilities(pkgName string, pkgVer *semver.Version) ([]types.DetectedVulnerability, error) {
advisories, err := s.vs.Get(pkgName)
if err != nil {

View File

@@ -14,16 +14,19 @@ import (
"github.com/aquasecurity/trivy/pkg/scanner/utils"
)
// Advisory encapsulates composer.VulnSrc
type Advisory struct {
vs composerSrc.VulnSrc
}
// NewAdvisory is the factory method of Advisory
func NewAdvisory() *Advisory {
return &Advisory{
vs: composerSrc.NewVulnSrc(),
}
}
// DetectVulnerabilities returns the vulnerabilities in a package
func (s *Advisory) DetectVulnerabilities(pkgName string, pkgVer *semver.Version) ([]types.DetectedVulnerability, error) {
ref := fmt.Sprintf("composer://%s", pkgName)
advisories, err := s.vs.Get(ref)

View File

@@ -4,19 +4,17 @@ import (
"path/filepath"
"time"
ftypes "github.com/aquasecurity/fanal/types"
"github.com/google/wire"
"github.com/Masterminds/semver/v3"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/google/wire"
"golang.org/x/xerrors"
ftypes "github.com/aquasecurity/fanal/types"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/scanner/utils"
"github.com/aquasecurity/trivy/pkg/types"
)
// SuperSet binds the dependencies for library scan
var SuperSet = wire.NewSet(
wire.Struct(new(DriverFactory)),
wire.Bind(new(Factory), new(DriverFactory)),
@@ -24,18 +22,22 @@ var SuperSet = wire.NewSet(
wire.Bind(new(Operation), new(Detector)),
)
// Operation defines library scan operations
type Operation interface {
Detect(imageName string, filePath string, created time.Time, pkgs []ftypes.LibraryInfo) (vulns []types.DetectedVulnerability, err error)
}
// Detector implements driverFactory
type Detector struct {
driverFactory Factory
}
// NewDetector is the factory method for detector
func NewDetector(factory Factory) Detector {
return Detector{driverFactory: factory}
}
// Detect scans and returns vulnerabilities of library
func (d Detector) Detect(_, filePath string, _ time.Time, pkgs []ftypes.LibraryInfo) ([]types.DetectedVulnerability, error) {
log.Logger.Debugf("Detecting library vulnerabilities, path: %s", filePath)
driver, err := d.driverFactory.NewDriver(filepath.Base(filePath))

View File

@@ -4,6 +4,8 @@ import (
"fmt"
"github.com/Masterminds/semver/v3"
"golang.org/x/xerrors"
ecosystem "github.com/aquasecurity/trivy-db/pkg/vulnsrc/ghsa"
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability"
"github.com/aquasecurity/trivy/pkg/detector/library/bundler"
@@ -13,9 +15,9 @@ import (
"github.com/aquasecurity/trivy/pkg/detector/library/node"
"github.com/aquasecurity/trivy/pkg/detector/library/python"
"github.com/aquasecurity/trivy/pkg/types"
"golang.org/x/xerrors"
)
// Factory defines library operations
type Factory interface {
NewDriver(filename string) (Driver, error)
}
@@ -24,8 +26,10 @@ type advisory interface {
DetectVulnerabilities(string, *semver.Version) ([]types.DetectedVulnerability, error)
}
// DriverFactory implements Factory
type DriverFactory struct{}
// NewDriver factory method for driver
func (d DriverFactory) NewDriver(filename string) (Driver, error) {
// TODO: use DI
var driver Driver
@@ -46,28 +50,31 @@ func (d DriverFactory) NewDriver(filename string) (Driver, error) {
return driver, nil
}
// Driver implements the advisory
type Driver struct {
lang string
advisories []advisory
}
// NewDriver is the factory method from drier
func NewDriver(lang string, advisories ...advisory) Driver {
return Driver{lang: lang, advisories: advisories}
}
// Detect scans and returns vulnerabilities
func (d *Driver) Detect(pkgName string, pkgVer *semver.Version) ([]types.DetectedVulnerability, error) {
var detectedVulnerabilities []types.DetectedVulnerability
uniqVulnIdMap := make(map[string]struct{})
uniqVulnIDMap := make(map[string]struct{})
for _, d := range append(d.advisories, NewAdvisory(d.lang)) {
vulns, err := d.DetectVulnerabilities(pkgName, pkgVer)
if err != nil {
return nil, xerrors.Errorf("failed to detect vulnerabilities: %w", err)
}
for _, vuln := range vulns {
if _, ok := uniqVulnIdMap[vuln.VulnerabilityID]; ok {
if _, ok := uniqVulnIDMap[vuln.VulnerabilityID]; ok {
continue
}
uniqVulnIdMap[vuln.VulnerabilityID] = struct{}{}
uniqVulnIDMap[vuln.VulnerabilityID] = struct{}{}
detectedVulnerabilities = append(detectedVulnerabilities, vuln)
}
}
@@ -75,6 +82,7 @@ func (d *Driver) Detect(pkgName string, pkgVer *semver.Version) ([]types.Detecte
return detectedVulnerabilities, nil
}
// Type returns the driver lang
func (d *Driver) Type() string {
return d.lang
}

View File

@@ -11,20 +11,24 @@ import (
"github.com/aquasecurity/trivy/pkg/types"
)
// VulnSrc defines the operations on vulnerability source
type VulnSrc interface {
Get(pkgName string) ([]ghsa.Advisory, error)
}
// Advisory implements VulnSrc
type Advisory struct {
vs VulnSrc
}
// NewAdvisory is the factory method to return advisory
func NewAdvisory(ecosystem ghsa.Ecosystem) *Advisory {
return &Advisory{
vs: ghsa.NewVulnSrc(ecosystem),
}
}
// DetectVulnerabilities scans package for vulnerabilities
func (s *Advisory) DetectVulnerabilities(pkgName string, pkgVer *semver.Version) ([]types.DetectedVulnerability, error) {
advisories, err := s.vs.Get(pkgName)
if err != nil {

View File

@@ -12,16 +12,19 @@ import (
"github.com/aquasecurity/trivy/pkg/types"
)
// Advisory encapsulate Node vulnerability source
type Advisory struct {
vs node.VulnSrc
}
// NewAdvisory is the factory method for Node Advisory
func NewAdvisory() *Advisory {
return &Advisory{
vs: node.NewVulnSrc(),
}
}
// DetectVulnerabilities scans and return vulnerability using Node package scanner
func (s *Advisory) DetectVulnerabilities(pkgName string, pkgVer *semver.Version) ([]types.DetectedVulnerability, error) {
replacer := strings.NewReplacer(".alpha", "-alpha", ".beta", "-beta", ".rc", "-rc", " <", ", <", " >", ", >")
advisories, err := s.vs.Get(pkgName)

View File

@@ -3,25 +3,27 @@ package python
import (
"strings"
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/python"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/Masterminds/semver/v3"
"golang.org/x/xerrors"
"github.com/Masterminds/semver/v3"
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/python"
"github.com/aquasecurity/trivy/pkg/scanner/utils"
"github.com/aquasecurity/trivy/pkg/types"
)
// Advisory encapsulates the python vulnerability scanner
type Advisory struct {
vs python.VulnSrc
}
// NewAdvisory is the factory method to reutrn Python Advisory
func NewAdvisory() *Advisory {
return &Advisory{
vs: python.NewVulnSrc(),
}
}
// DetectVulnerabilities scans and returns pythin vulnerabilities
func (s *Advisory) DetectVulnerabilities(pkgName string, pkgVer *semver.Version) ([]types.DetectedVulnerability, error) {
advisories, err := s.vs.Get(pkgName)
if err != nil {

View File

@@ -41,16 +41,19 @@ var (
}
)
// Scanner implements the Alpine scanner
type Scanner struct {
vs dbTypes.VulnSrc
}
// NewScanner is the factory method for Scanner
func NewScanner() *Scanner {
return &Scanner{
vs: alpine.NewVulnSrc(),
}
}
// Detect vulnerabilities in package using Alpine scanner
func (s *Scanner) Detect(osVer string, pkgs []ftypes.Package) ([]types.DetectedVulnerability, error) {
log.Logger.Info("Detecting Alpine vulnerabilities...")
if strings.Count(osVer, ".") > 1 {
@@ -94,6 +97,7 @@ func (s *Scanner) Detect(osVer string, pkgs []ftypes.Package) ([]types.DetectedV
return vulns, nil
}
// IsSupportedVersion checks the OSFamily can be scanned using Alpine scanner
func (s *Scanner) IsSupportedVersion(osFamily, osVer string) bool {
now := time.Now()
return s.isSupportedVersion(now, osFamily, osVer)

View File

@@ -15,11 +15,13 @@ import (
"github.com/aquasecurity/trivy/pkg/types"
)
// Scanner to scan amazon vulnerabilities
type Scanner struct {
l *zap.SugaredLogger
ac dbTypes.VulnSrc
}
// NewScanner is the factory method to return Amazon scanner
func NewScanner() *Scanner {
return &Scanner{
l: log.Logger,
@@ -27,6 +29,7 @@ func NewScanner() *Scanner {
}
}
// Detect scans the packages using amazon scanner
func (s *Scanner) Detect(osVer string, pkgs []ftypes.Package) ([]types.DetectedVulnerability, error) {
log.Logger.Info("Detecting Amazon Linux vulnerabilities...")
@@ -77,6 +80,7 @@ func (s *Scanner) Detect(osVer string, pkgs []ftypes.Package) ([]types.DetectedV
return vulns, nil
}
// IsSupportedVersion checks if os can be scanned using amazon scanner
func (s *Scanner) IsSupportedVersion(osFamily, osVer string) bool {
return true
}

View File

@@ -40,11 +40,13 @@ var (
}
)
// Scanner implements the Debian scanner
type Scanner struct {
ovalVs dbTypes.VulnSrc
vs dbTypes.VulnSrc
}
// NewScanner is the factory method to return Scanner
func NewScanner() *Scanner {
return &Scanner{
ovalVs: debianoval.NewVulnSrc(),
@@ -52,6 +54,7 @@ func NewScanner() *Scanner {
}
}
// Detect scans and return vulnerabilities using Debian scanner
func (s *Scanner) Detect(osVer string, pkgs []ftypes.Package) ([]types.DetectedVulnerability, error) {
log.Logger.Info("Detecting Debian vulnerabilities...")
@@ -78,7 +81,8 @@ func (s *Scanner) Detect(osVer string, pkgs []ftypes.Package) ([]types.DetectedV
}
for _, adv := range advisories {
fixedVersion, err := version.NewVersion(adv.FixedVersion)
var fixedVersion version.Version
fixedVersion, err = version.NewVersion(adv.FixedVersion)
if err != nil {
log.Logger.Debugf("failed to parse Debian package version: %w", err)
continue
@@ -115,6 +119,7 @@ func (s *Scanner) Detect(osVer string, pkgs []ftypes.Package) ([]types.DetectedV
return vulns, nil
}
// IsSupportedVersion checks is OSFamily can be scanned using Debian
func (s *Scanner) IsSupportedVersion(osFamily, osVer string) bool {
now := time.Now()
return s.isSupportedVersion(now, osFamily, osVer)

View File

@@ -21,25 +21,31 @@ import (
)
var (
// ErrUnsupportedOS defines error for unsupported OS
ErrUnsupportedOS = xerrors.New("unsupported os")
// SuperSet binds dependencies for OS scan
SuperSet = wire.NewSet(
wire.Struct(new(Detector)),
wire.Bind(new(Operation), new(Detector)),
)
)
// Operation defines operation of OSpkg scan
type Operation interface {
Detect(string, string, string, time.Time, []ftypes.Package) ([]types.DetectedVulnerability, bool, error)
}
// Driver defines operations for OS package scan
type Driver interface {
Detect(string, []ftypes.Package) ([]types.DetectedVulnerability, error)
IsSupportedVersion(string, string) bool
}
// Detector implements Operation
type Detector struct{}
// Detect detects the vulnerabilities
func (d Detector) Detect(_, osFamily, osName string, _ time.Time, pkgs []ftypes.Package) ([]types.DetectedVulnerability, bool, error) {
driver := newDriver(osFamily, osName)
if driver == nil {
@@ -56,31 +62,31 @@ func (d Detector) Detect(_, osFamily, osName string, _ time.Time, pkgs []ftypes.
return vulns, eosl, nil
}
// nolint: gocyclo
// TODO: fix cyclometic complexity by removing default
func newDriver(osFamily, osName string) Driver {
// TODO: use DI and change struct names
var d Driver
switch osFamily {
case fos.Alpine:
d = alpine.NewScanner()
return alpine.NewScanner()
case fos.Debian:
d = debian.NewScanner()
return debian.NewScanner()
case fos.Ubuntu:
d = ubuntu.NewScanner()
return ubuntu.NewScanner()
case fos.RedHat, fos.CentOS:
d = redhat.NewScanner()
return redhat.NewScanner()
case fos.Amazon:
d = amazon.NewScanner()
return amazon.NewScanner()
case fos.Oracle:
d = oracle.NewScanner()
return oracle.NewScanner()
case fos.OpenSUSELeap:
d = suse.NewScanner(suse.OpenSUSE)
return suse.NewScanner(suse.OpenSUSE)
case fos.SLES:
d = suse.NewScanner(suse.SUSEEnterpriseLinux)
return suse.NewScanner(suse.SUSEEnterpriseLinux)
case fos.Photon:
d = photon.NewScanner()
return photon.NewScanner()
default:
log.Logger.Warnf("unsupported os : %s", osFamily)
return nil
}
return d
}

View File

@@ -4,18 +4,16 @@ import (
"strings"
"time"
oracleoval "github.com/aquasecurity/trivy-db/pkg/vulnsrc/oracle-oval"
version "github.com/knqyf263/go-rpm-version"
"golang.org/x/xerrors"
"k8s.io/utils/clock"
ftypes "github.com/aquasecurity/fanal/types"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
oracleoval "github.com/aquasecurity/trivy-db/pkg/vulnsrc/oracle-oval"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/scanner/utils"
"github.com/aquasecurity/trivy/pkg/types"
"k8s.io/utils/clock"
)
var (
@@ -32,11 +30,13 @@ var (
}
)
// Scanner implements oracle vulnerability scanner
type Scanner struct {
vs dbTypes.VulnSrc
clock clock.Clock
}
// NewScanner is the factory method to return oracle vulnerabilities
func NewScanner() *Scanner {
return &Scanner{
vs: oracleoval.NewVulnSrc(),
@@ -44,6 +44,7 @@ func NewScanner() *Scanner {
}
}
// Detect scans and return vulnerability in Oracle scanner
func (s *Scanner) Detect(osVer string, pkgs []ftypes.Package) ([]types.DetectedVulnerability, error) {
log.Logger.Info("Detecting Oracle Linux vulnerabilities...")
@@ -81,6 +82,7 @@ func (s *Scanner) Detect(osVer string, pkgs []ftypes.Package) ([]types.DetectedV
return vulns, nil
}
// IsSupportedVersion checks is OSFamily can be scanned with Oracle scanner
func (s *Scanner) IsSupportedVersion(osFamily, osVer string) bool {
if strings.Count(osVer, ".") > 0 {
osVer = osVer[:strings.Index(osVer, ".")]

View File

@@ -1,31 +1,30 @@
package photon
import (
"time"
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/photon"
version "github.com/knqyf263/go-rpm-version"
"golang.org/x/xerrors"
"k8s.io/utils/clock"
ftypes "github.com/aquasecurity/fanal/types"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/photon"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/scanner/utils"
"github.com/aquasecurity/trivy/pkg/types"
"k8s.io/utils/clock"
)
var (
eolDates = map[string]time.Time{}
)
// EOL can't be found for photon https://github.com/vmware/photon/issues/1031
//var (
// eolDates = map[string]time.Time{}
//)
// Scanner implements Photon scanner
type Scanner struct {
vs dbTypes.VulnSrc
clock clock.Clock
}
// NewScanner is the factory method for Scanner
func NewScanner() *Scanner {
return &Scanner{
vs: photon.NewVulnSrc(),
@@ -33,6 +32,7 @@ func NewScanner() *Scanner {
}
}
// Detect scans and returns vulnerabilities using photon scanner
func (s *Scanner) Detect(osVer string, pkgs []ftypes.Package) ([]types.DetectedVulnerability, error) {
log.Logger.Info("Detecting Photon Linux vulnerabilities...")
log.Logger.Debugf("Photon Linux: os version: %s", osVer)
@@ -64,6 +64,7 @@ func (s *Scanner) Detect(osVer string, pkgs []ftypes.Package) ([]types.DetectedV
return vulns, nil
}
// IsSupportedVersion checks is OSFamily can be scanned
func (s *Scanner) IsSupportedVersion(osFamily, osVer string) bool {
return true
}

View File

@@ -4,14 +4,13 @@ import (
"strings"
"time"
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/redhat"
version "github.com/knqyf263/go-rpm-version"
"golang.org/x/xerrors"
"github.com/aquasecurity/fanal/analyzer/os"
ftypes "github.com/aquasecurity/fanal/types"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/redhat"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/scanner/utils"
"github.com/aquasecurity/trivy/pkg/types"
@@ -37,16 +36,19 @@ var (
}
)
// Scanner implements the Redhat scanner
type Scanner struct {
vs dbTypes.VulnSrc
}
// NewScanner is the factory method for Scanner
func NewScanner() *Scanner {
return &Scanner{
vs: redhat.NewVulnSrc(),
}
}
// Detect scans and returns redhat vulenrabilities
func (s *Scanner) Detect(osVer string, pkgs []ftypes.Package) ([]types.DetectedVulnerability, error) {
log.Logger.Info("Detecting RHEL/CentOS vulnerabilities...")
if strings.Count(osVer, ".") > 0 {
@@ -102,6 +104,7 @@ func (s *Scanner) Detect(osVer string, pkgs []ftypes.Package) ([]types.DetectedV
return vulns, nil
}
// IsSupportedVersion checks is OSFamily can be scanned with Redhat scanner
func (s *Scanner) IsSupportedVersion(osFamily, osVer string) bool {
now := time.Now()
return s.isSupportedVersion(now, osFamily, osVer)

View File

@@ -6,6 +6,8 @@ import (
"golang.org/x/xerrors"
"k8s.io/utils/clock"
version "github.com/knqyf263/go-rpm-version"
fos "github.com/aquasecurity/fanal/analyzer/os"
ftypes "github.com/aquasecurity/fanal/types"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
@@ -13,7 +15,6 @@ import (
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/scanner/utils"
"github.com/aquasecurity/trivy/pkg/types"
version "github.com/knqyf263/go-rpm-version"
)
var (
@@ -52,20 +53,24 @@ var (
}
)
// Scanner implements suse scanner
type Scanner struct {
vs dbTypes.VulnSrc
clock clock.Clock
family string
vs dbTypes.VulnSrc
clock clock.Clock
}
type SUSEType int
// Type to define SUSE type
type Type int
const (
SUSEEnterpriseLinux SUSEType = iota
// SUSEEnterpriseLinux is Linux Enterprise version
SUSEEnterpriseLinux Type = iota
// OpenSUSE for open versions
OpenSUSE
)
func NewScanner(t SUSEType) *Scanner {
// NewScanner is the factory method for Scanner
func NewScanner(t Type) *Scanner {
switch t {
case SUSEEnterpriseLinux:
return &Scanner{
@@ -81,6 +86,7 @@ func NewScanner(t SUSEType) *Scanner {
return nil
}
// Detect scans and returns the vulnerabilities
func (s *Scanner) Detect(osVer string, pkgs []ftypes.Package) ([]types.DetectedVulnerability, error) {
log.Logger.Info("Detecting SUSE vulnerabilities...")
log.Logger.Debugf("SUSE: os version: %s", osVer)
@@ -112,6 +118,7 @@ func (s *Scanner) Detect(osVer string, pkgs []ftypes.Package) ([]types.DetectedV
return vulns, nil
}
// IsSupportedVersion checks if OSFamily can be scanned using SUSE scanner
func (s *Scanner) IsSupportedVersion(osFamily, osVer string) bool {
var eolDate time.Time
var ok bool

View File

@@ -51,16 +51,19 @@ var (
}
)
// Scanner implements the Ubuntu scanner
type Scanner struct {
vs dbTypes.VulnSrc
}
// NewScanner is the factory method for Scanner
func NewScanner() *Scanner {
return &Scanner{
vs: ubuntu.NewVulnSrc(),
}
}
// Detect scans and returns the vulnerabilities
func (s *Scanner) Detect(osVer string, pkgs []ftypes.Package) ([]types.DetectedVulnerability, error) {
log.Logger.Info("Detecting Ubuntu vulnerabilities...")
log.Logger.Debugf("ubuntu: os version: %s", osVer)
@@ -108,6 +111,7 @@ func (s *Scanner) Detect(osVer string, pkgs []ftypes.Package) ([]types.DetectedV
return vulns, nil
}
// IsSupportedVersion checks is OSFamily can be scanned using Ubuntu scanner
func (s *Scanner) IsSupportedVersion(osFamily, osVer string) bool {
now := time.Now()
return s.isSupportedVersion(now, osFamily, osVer)

View File

@@ -10,11 +10,12 @@ import (
"strconv"
"strings"
"github.com/aquasecurity/trivy-db/pkg/db"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/google/go-github/v28/github"
"golang.org/x/oauth2"
"golang.org/x/xerrors"
"github.com/aquasecurity/trivy-db/pkg/db"
"github.com/aquasecurity/trivy/pkg/log"
)
const (
@@ -22,11 +23,13 @@ const (
repo = "trivy-db"
)
// RepositoryInterface defines the operations on repository
type RepositoryInterface interface {
ListReleases(ctx context.Context, opt *github.ListOptions) ([]*github.RepositoryRelease, *github.Response, error)
DownloadAsset(ctx context.Context, id int64) (io.ReadCloser, string, error)
}
// Repository implements RepositoryInterface
type Repository struct {
repository *github.RepositoriesService
git *github.GitService
@@ -34,22 +37,27 @@ type Repository struct {
repoName string
}
// ListReleases returns all github releases on repository
func (r Repository) ListReleases(ctx context.Context, opt *github.ListOptions) ([]*github.RepositoryRelease, *github.Response, error) {
return r.repository.ListReleases(ctx, r.owner, r.repoName, opt)
}
// DownloadAsset returns reader object of downloaded object
func (r Repository) DownloadAsset(ctx context.Context, id int64) (io.ReadCloser, string, error) {
return r.repository.DownloadReleaseAsset(ctx, r.owner, r.repoName, id)
}
// Operation defines the file operations
type Operation interface {
DownloadDB(ctx context.Context, fileName string) (io.ReadCloser, int, error)
}
// Client implements RepositoryInterface
type Client struct {
Repository RepositoryInterface
}
// NewClient is the factory method to return Client for RepositoryInterface operations
func NewClient() Client {
var client *http.Client
githubToken := os.Getenv("GITHUB_TOKEN")
@@ -73,6 +81,7 @@ func NewClient() Client {
}
}
// DownloadDB returns reader object of file content
func (c Client) DownloadDB(ctx context.Context, fileName string) (io.ReadCloser, int, error) {
options := github.ListOptions{}
releases, _, err := c.Repository.ListReleases(ctx, &options)
@@ -120,7 +129,7 @@ func (c Client) downloadAsset(ctx context.Context, asset github.ReleaseAsset, fi
}
log.Logger.Debugf("asset URL: %s", url)
resp, err := http.Get(url)
resp, err := http.Get(url) // nolint: gosec
if err != nil || resp.StatusCode != http.StatusOK {
return nil, 0, xerrors.Errorf("unable to download the asset via URL: %w", err)
}

View File

@@ -6,14 +6,17 @@ import (
"github.com/cheggaaa/pb/v3"
)
// ProgressBar exports method to track the progress of jobs
type ProgressBar struct {
quiet bool
}
// NewProgressBar is the factory method to return progressBar object
func NewProgressBar(quiet bool) ProgressBar {
return ProgressBar{quiet: quiet}
}
// Start starts the progress tracking
func (p ProgressBar) Start(total int64) Bar {
if p.quiet {
return Bar{}
@@ -22,16 +25,20 @@ func (p ProgressBar) Start(total int64) Bar {
return Bar{bar: bar}
}
// Bar is the proxy progress bar
type Bar struct {
bar *pb.ProgressBar
}
// NewProxyReader is the factory method to track the progress
func (b Bar) NewProxyReader(r io.Reader) io.Reader {
if b.bar == nil {
return r
}
return b.bar.NewProxyReader(r)
}
// Finish finishes the progress tracking
func (b Bar) Finish() {
if b.bar == nil {
return

View File

@@ -9,10 +9,12 @@ import (
)
var (
// Logger is the global variable for logging
Logger *zap.SugaredLogger
debugOption bool
)
// InitLogger initialize the logger variable
func InitLogger(debug, disable bool) (err error) {
debugOption = debug
Logger, err = NewLogger(debug, disable)
@@ -23,6 +25,7 @@ func InitLogger(debug, disable bool) (err error) {
}
// NewLogger is the factory method to return the instance of logger
func NewLogger(debug, disable bool) (*zap.SugaredLogger, error) {
// First, define our level-handling logic.
errorPriority := zap.LevelEnablerFunc(func(lvl zapcore.Level) bool {
@@ -78,6 +81,7 @@ func NewLogger(debug, disable bool) (*zap.SugaredLogger, error) {
return logger.Sugar(), nil
}
// Fatal for logging fatal errors
func Fatal(err error) {
if debugOption {
Logger.Fatalf("%+v", err)

View File

@@ -13,19 +13,22 @@ import (
"text/template"
"time"
"github.com/olekukonko/tablewriter"
"golang.org/x/xerrors"
ftypes "github.com/aquasecurity/fanal/types"
dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/aquasecurity/trivy/pkg/utils"
"github.com/olekukonko/tablewriter"
)
// Now returns the current time
var Now = time.Now
// Results to hold list of Result
type Results []Result
// Result to hold image scan results
type Result struct {
Target string `json:"Target"`
Type string `json:"Type,omitempty"`
@@ -33,13 +36,14 @@ type Result struct {
Vulnerabilities []types.DetectedVulnerability `json:"Vulnerabilities"`
}
// WriteResults writes the result to output, format as passed in argument
func WriteResults(format string, output io.Writer, severities []dbTypes.Severity, results Results, outputTemplate string, light bool) error {
var writer Writer
switch format {
case "table":
writer = &TableWriter{Output: output, Light: light, Severities: severities}
case "json":
writer = &JsonWriter{Output: output}
writer = &JSONWriter{Output: output}
case "template":
var err error
if writer, err = NewTemplateWriter(output, outputTemplate); err != nil {
@@ -55,22 +59,28 @@ func WriteResults(format string, output io.Writer, severities []dbTypes.Severity
return nil
}
// Writer defines the result write operation
type Writer interface {
Write(Results) error
}
// TableWriter implements Writer and output in tabular form
type TableWriter struct {
Severities []dbTypes.Severity
Output io.Writer
Light bool
}
// Write writes the result on standard output
func (tw TableWriter) Write(results Results) error {
for _, result := range results {
tw.write(result)
}
return nil
}
// nolint: gocyclo
// TODO: refactror and fix cyclometic complexity
func (tw TableWriter) write(result Result) {
table := tablewriter.NewWriter(tw.Output)
header := []string{"Library", "Vulnerability ID", "Severity", "Installed Version", "Fixed Version"}
@@ -134,11 +144,13 @@ func (tw TableWriter) write(result Result) {
return
}
type JsonWriter struct {
// JSONWriter implements result Writer
type JSONWriter struct {
Output io.Writer
}
func (jw JsonWriter) Write(results Results) error {
// Write writes the results in JSON format
func (jw JSONWriter) Write(results Results) error {
output, err := json.MarshalIndent(results, "", " ")
if err != nil {
return xerrors.Errorf("failed to marshal json: %w", err)
@@ -150,11 +162,13 @@ func (jw JsonWriter) Write(results Results) error {
return nil
}
// TemplateWriter write result in custom format defined by user's template
type TemplateWriter struct {
Output io.Writer
Template *template.Template
}
// NewTemplateWriter is the factory method to return TemplateWriter object
func NewTemplateWriter(output io.Writer, outputTemplate string) (*TemplateWriter, error) {
if strings.HasPrefix(outputTemplate, "@") {
buf, err := ioutil.ReadFile(strings.TrimPrefix(outputTemplate, "@"))
@@ -197,6 +211,7 @@ func NewTemplateWriter(output io.Writer, outputTemplate string) (*TemplateWriter
return &TemplateWriter{Output: output, Template: tmpl}, nil
}
// Write writes result
func (tw TemplateWriter) Write(results Results) error {
err := tw.Template.Execute(tw.Output, results)
if err != nil {

View File

@@ -175,7 +175,7 @@ func TestReportWriter_JSON(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
jw := report.JsonWriter{}
jw := report.JSONWriter{}
jsonWritten := bytes.Buffer{}
jw.Output = &jsonWritten

View File

@@ -16,28 +16,35 @@ import (
rpc "github.com/aquasecurity/trivy/rpc/scanner"
)
// SuperSet binds the dependencies for RPC client
var SuperSet = wire.NewSet(
NewProtobufClient,
NewScanner,
)
// RemoteURL for RPC remote host
type RemoteURL string
// NewProtobufClient is the factory method to return RPC scanner
func NewProtobufClient(remoteURL RemoteURL) rpc.Scanner {
return rpc.NewScannerProtobufClient(string(remoteURL), &http.Client{})
}
// CustomHeaders for holding HTTP headers
type CustomHeaders http.Header
// Scanner implements the RPC scanner
type Scanner struct {
customHeaders CustomHeaders
client rpc.Scanner
}
// NewScanner is the factory method to return RPC Scanner
func NewScanner(customHeaders CustomHeaders, s rpc.Scanner) Scanner {
return Scanner{customHeaders: customHeaders, client: s}
}
// Scan scans the image
func (s Scanner) Scan(target string, imageID string, layerIDs []string, options types.ScanOptions) (report.Results, *ftypes.OS, bool, error) {
ctx := WithCustomHeaders(context.Background(), http.Header(s.customHeaders))
@@ -58,5 +65,5 @@ func (s Scanner) Scan(target string, imageID string, layerIDs []string, options
return nil, nil, false, xerrors.Errorf("failed to detect vulnerabilities via RPC: %w", err)
}
return r.ConvertFromRpcResults(res.Results), r.ConvertFromRpcOS(res.Os), res.Eosl, nil
return r.ConvertFromRPCResults(res.Results), r.ConvertFromRPCOS(res.Os), res.Eosl, nil
}

View File

@@ -9,6 +9,7 @@ import (
"github.com/aquasecurity/trivy/pkg/log"
)
// WithCustomHeaders adds custom headers to request headers
func WithCustomHeaders(ctx context.Context, customHeaders http.Header) context.Context {
// Attach the headers to a context
ctxWithToken, err := twirp.WithHTTPRequestHeaders(ctx, customHeaders)

View File

@@ -14,7 +14,8 @@ import (
"github.com/aquasecurity/trivy/rpc/scanner"
)
func ConvertToRpcPkgs(pkgs []ftypes.Package) []*common.Package {
// ConvertToRPCPkgs returns the list of RPC package objects
func ConvertToRPCPkgs(pkgs []ftypes.Package) []*common.Package {
var rpcPkgs []*common.Package
for _, pkg := range pkgs {
rpcPkgs = append(rpcPkgs, &common.Package{
@@ -32,7 +33,8 @@ func ConvertToRpcPkgs(pkgs []ftypes.Package) []*common.Package {
return rpcPkgs
}
func ConvertFromRpcPkgs(rpcPkgs []*common.Package) []ftypes.Package {
// ConvertFromRPCPkgs returns list of Fanal package objects
func ConvertFromRPCPkgs(rpcPkgs []*common.Package) []ftypes.Package {
var pkgs []ftypes.Package
for _, pkg := range rpcPkgs {
pkgs = append(pkgs, ftypes.Package{
@@ -50,7 +52,8 @@ func ConvertFromRpcPkgs(rpcPkgs []*common.Package) []ftypes.Package {
return pkgs
}
func ConvertFromRpcLibraries(rpcLibs []*common.Library) []ftypes.LibraryInfo {
// ConvertFromRPCLibraries returns list of Fanal library
func ConvertFromRPCLibraries(rpcLibs []*common.Library) []ftypes.LibraryInfo {
var libs []ftypes.LibraryInfo
for _, l := range rpcLibs {
libs = append(libs, ftypes.LibraryInfo{
@@ -63,7 +66,8 @@ func ConvertFromRpcLibraries(rpcLibs []*common.Library) []ftypes.LibraryInfo {
return libs
}
func ConvertToRpcLibraries(libs []deptypes.Library) []*common.Library {
// ConvertToRPCLibraries returns list of libraries
func ConvertToRPCLibraries(libs []deptypes.Library) []*common.Library {
var rpcLibs []*common.Library
for _, l := range libs {
rpcLibs = append(rpcLibs, &common.Library{
@@ -74,7 +78,8 @@ func ConvertToRpcLibraries(libs []deptypes.Library) []*common.Library {
return rpcLibs
}
func ConvertFromRpcVulns(rpcVulns []*common.Vulnerability) []types.DetectedVulnerability {
// ConvertFromRPCVulns returns converted vulnerability from common vulnerability format
func ConvertFromRPCVulns(rpcVulns []*common.Vulnerability) []types.DetectedVulnerability {
var vulns []types.DetectedVulnerability
for _, vuln := range rpcVulns {
severity := dbTypes.Severity(vuln.Severity)
@@ -94,7 +99,8 @@ func ConvertFromRpcVulns(rpcVulns []*common.Vulnerability) []types.DetectedVulne
return vulns
}
func ConvertToRpcVulns(vulns []types.DetectedVulnerability) []*common.Vulnerability {
// ConvertToRPCVulns returns common.Vulnerability
func ConvertToRPCVulns(vulns []types.DetectedVulnerability) []*common.Vulnerability {
var rpcVulns []*common.Vulnerability
for _, vuln := range vulns {
severity, err := dbTypes.NewSeverity(vuln.Severity)
@@ -132,9 +138,9 @@ func ConvertToRpcVulns(vulns []types.DetectedVulnerability) []*common.Vulnerabil
return rpcVulns
}
func ConvertFromRpcResults(rpcResults []*scanner.Result) []report.Result {
// ConvertFromRPCResults converts scannel.Result to report.Result
func ConvertFromRPCResults(rpcResults []*scanner.Result) []report.Result {
var results []report.Result
for _, result := range rpcResults {
var vulns []types.DetectedVulnerability
for _, vuln := range result.Vulnerabilities {
@@ -178,7 +184,8 @@ func ConvertFromRpcResults(rpcResults []*scanner.Result) []report.Result {
return results
}
func ConvertFromRpcOS(rpcOS *common.OS) *ftypes.OS {
// ConvertFromRPCOS converts common.OS to fanal.OS
func ConvertFromRPCOS(rpcOS *common.OS) *ftypes.OS {
if rpcOS == nil {
return nil
}
@@ -188,55 +195,60 @@ func ConvertFromRpcOS(rpcOS *common.OS) *ftypes.OS {
}
}
func ConvertFromRpcPackageInfos(rpcPkgInfos []*common.PackageInfo) []ftypes.PackageInfo {
// ConvertFromRPCPackageInfos converts common.PackageInfo to fanal.PackageInfo
func ConvertFromRPCPackageInfos(rpcPkgInfos []*common.PackageInfo) []ftypes.PackageInfo {
var pkgInfos []ftypes.PackageInfo
for _, rpcPkgInfo := range rpcPkgInfos {
pkgInfos = append(pkgInfos, ftypes.PackageInfo{
FilePath: rpcPkgInfo.FilePath,
Packages: ConvertFromRpcPkgs(rpcPkgInfo.Packages),
Packages: ConvertFromRPCPkgs(rpcPkgInfo.Packages),
})
}
return pkgInfos
}
func ConvertFromRpcApplications(rpcApps []*common.Application) []ftypes.Application {
// ConvertFromRPCApplications converts common.Application to fanal.Application
func ConvertFromRPCApplications(rpcApps []*common.Application) []ftypes.Application {
var apps []ftypes.Application
for _, rpcApp := range rpcApps {
apps = append(apps, ftypes.Application{
Type: rpcApp.Type,
FilePath: rpcApp.FilePath,
Libraries: ConvertFromRpcLibraries(rpcApp.Libraries),
Libraries: ConvertFromRPCLibraries(rpcApp.Libraries),
})
}
return apps
}
func ConvertFromRpcPutArtifactRequest(req *cache.PutArtifactRequest) ftypes.ArtifactInfo {
created, _ := ptypes.Timestamp(req.ArtifactInfo.Created)
// ConvertFromRPCPutArtifactRequest converts cache.PutArtifactRequest to fanal.PutArtifactRequest
func ConvertFromRPCPutArtifactRequest(req *cache.PutArtifactRequest) ftypes.ArtifactInfo {
created, _ := ptypes.Timestamp(req.ArtifactInfo.Created) // nolint: errcheck
return ftypes.ArtifactInfo{
SchemaVersion: int(req.ArtifactInfo.SchemaVersion),
Architecture: req.ArtifactInfo.Architecture,
Created: created,
DockerVersion: req.ArtifactInfo.DockerVersion,
OS: req.ArtifactInfo.Os,
HistoryPackages: ConvertFromRpcPkgs(req.ArtifactInfo.HistoryPackages),
HistoryPackages: ConvertFromRPCPkgs(req.ArtifactInfo.HistoryPackages),
}
}
func ConvertFromRpcPutBlobRequest(req *cache.PutBlobRequest) ftypes.BlobInfo {
// ConvertFromRPCPutBlobRequest returns ftypes.BlobInfo
func ConvertFromRPCPutBlobRequest(req *cache.PutBlobRequest) ftypes.BlobInfo {
return ftypes.BlobInfo{
SchemaVersion: int(req.BlobInfo.SchemaVersion),
Digest: req.BlobInfo.Digest,
DiffID: req.BlobInfo.DiffId,
OS: ConvertFromRpcOS(req.BlobInfo.Os),
PackageInfos: ConvertFromRpcPackageInfos(req.BlobInfo.PackageInfos),
Applications: ConvertFromRpcApplications(req.BlobInfo.Applications),
OS: ConvertFromRPCOS(req.BlobInfo.Os),
PackageInfos: ConvertFromRPCPackageInfos(req.BlobInfo.PackageInfos),
Applications: ConvertFromRPCApplications(req.BlobInfo.Applications),
OpaqueDirs: req.BlobInfo.OpaqueDirs,
WhiteoutFiles: req.BlobInfo.WhiteoutFiles,
}
}
func ConvertToRpcOS(fos *ftypes.OS) *common.OS {
// ConvertToRPCOS returns common.OS
func ConvertToRPCOS(fos *ftypes.OS) *common.OS {
if fos == nil {
return nil
}
@@ -246,7 +258,8 @@ func ConvertToRpcOS(fos *ftypes.OS) *common.OS {
}
}
func ConvertToRpcArtifactInfo(imageID string, imageInfo ftypes.ArtifactInfo) *cache.PutArtifactRequest {
// ConvertToRPCArtifactInfo returns PutArtifactRequest
func ConvertToRPCArtifactInfo(imageID string, imageInfo ftypes.ArtifactInfo) *cache.PutArtifactRequest {
t, err := ptypes.TimestampProto(imageInfo.Created)
if err != nil {
log.Logger.Warnf("invalid timestamp: %s", err)
@@ -260,17 +273,18 @@ func ConvertToRpcArtifactInfo(imageID string, imageInfo ftypes.ArtifactInfo) *ca
Created: t,
DockerVersion: imageInfo.DockerVersion,
Os: imageInfo.OS,
HistoryPackages: ConvertToRpcPkgs(imageInfo.HistoryPackages),
HistoryPackages: ConvertToRPCPkgs(imageInfo.HistoryPackages),
},
}
}
func ConvertToRpcBlobInfo(diffID string, layerInfo ftypes.BlobInfo) *cache.PutBlobRequest {
// ConvertToRPCBlobInfo returns PutBlobRequest
func ConvertToRPCBlobInfo(diffID string, layerInfo ftypes.BlobInfo) *cache.PutBlobRequest {
var packageInfos []*common.PackageInfo
for _, pkgInfo := range layerInfo.PackageInfos {
packageInfos = append(packageInfos, &common.PackageInfo{
FilePath: pkgInfo.FilePath,
Packages: ConvertToRpcPkgs(pkgInfo.Packages),
Packages: ConvertToRPCPkgs(pkgInfo.Packages),
})
}
@@ -282,7 +296,6 @@ func ConvertToRpcBlobInfo(diffID string, layerInfo ftypes.BlobInfo) *cache.PutBl
Name: lib.Library.Name,
Version: lib.Library.Version,
})
}
applications = append(applications, &common.Application{
Type: app.Type,
@@ -297,7 +310,7 @@ func ConvertToRpcBlobInfo(diffID string, layerInfo ftypes.BlobInfo) *cache.PutBl
SchemaVersion: ftypes.BlobJSONSchemaVersion,
Digest: layerInfo.Digest,
DiffId: layerInfo.DiffID,
Os: ConvertToRpcOS(layerInfo.OS),
Os: ConvertToRPCOS(layerInfo.OS),
PackageInfos: packageInfos,
Applications: applications,
OpaqueDirs: layerInfo.OpaqueDirs,
@@ -306,6 +319,7 @@ func ConvertToRpcBlobInfo(diffID string, layerInfo ftypes.BlobInfo) *cache.PutBl
}
}
// ConvertToMissingBlobsRequest returns MissingBlobsRequest object
func ConvertToMissingBlobsRequest(imageID string, layerIDs []string) *cache.MissingBlobsRequest {
return &cache.MissingBlobsRequest{
ArtifactId: imageID,
@@ -313,7 +327,8 @@ func ConvertToMissingBlobsRequest(imageID string, layerIDs []string) *cache.Miss
}
}
func ConvertToRpcScanResponse(results report.Results, os *ftypes.OS, eosl bool) *scanner.ScanResponse {
// ConvertToRPCScanResponse converts report.Result to ScanResponse
func ConvertToRPCScanResponse(results report.Results, os *ftypes.OS, eosl bool) *scanner.ScanResponse {
rpcOS := &common.OS{}
if os != nil {
rpcOS.Family = os.Family
@@ -324,7 +339,7 @@ func ConvertToRpcScanResponse(results report.Results, os *ftypes.OS, eosl bool)
for _, result := range results {
rpcResults = append(rpcResults, &scanner.Result{
Target: result.Target,
Vulnerabilities: ConvertToRpcVulns(result.Vulnerabilities),
Vulnerabilities: ConvertToRPCVulns(result.Vulnerabilities),
Type: result.Type,
})
}

View File

@@ -65,7 +65,7 @@ func TestConvertToRpcPkgs(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := ConvertToRpcPkgs(tt.args.pkgs)
got := ConvertToRPCPkgs(tt.args.pkgs)
assert.Equal(t, tt.want, got, tt.name)
})
}
@@ -113,7 +113,7 @@ func TestConvertFromRpcPkgs(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := ConvertFromRpcPkgs(tt.args.rpcPkgs)
got := ConvertFromRPCPkgs(tt.args.rpcPkgs)
assert.Equal(t, tt.want, got, tt.name)
})
}
@@ -144,7 +144,7 @@ func TestConvertFromRpcLibraries(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := ConvertFromRpcLibraries(tt.args.rpcLibs)
got := ConvertFromRPCLibraries(tt.args.rpcLibs)
assert.Equal(t, got, tt.want, tt.name)
})
}
@@ -175,7 +175,7 @@ func TestConvertToRpcLibraries(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := ConvertToRpcLibraries(tt.args.libs)
got := ConvertToRPCLibraries(tt.args.libs)
assert.Equal(t, got, tt.want, tt.name)
})
}
@@ -224,7 +224,7 @@ func TestConvertFromRpcVulns(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := ConvertFromRpcVulns(tt.args.rpcVulns)
got := ConvertFromRPCVulns(tt.args.rpcVulns)
assert.Equal(t, got, tt.want, tt.name)
})
}
@@ -337,7 +337,7 @@ func TestConvertToRpcVulns(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := ConvertToRpcVulns(tt.args.vulns)
got := ConvertToRPCVulns(tt.args.vulns)
assert.Equal(t, got, tt.want, tt.name)
})
}

View File

@@ -3,15 +3,17 @@ package rpc
import (
"time"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/cenkalti/backoff"
"github.com/twitchtv/twirp"
"github.com/aquasecurity/trivy/pkg/log"
)
const (
maxRetries = 10
)
// Retry executes the function again using backoff until maxRetries or success
func Retry(f func() error) error {
operation := func() error {
err := f()

View File

@@ -14,6 +14,7 @@ import (
proto "github.com/aquasecurity/trivy/rpc/detector"
)
// SuperSet binds the dependencies for library RPC server
var SuperSet = wire.NewSet(
detector.SuperSet,
vulnerability.SuperSet,
@@ -26,13 +27,14 @@ type Server struct {
vulnClient vulnerability.Operation
}
// NewServer is the facotry method for Server
func NewServer(detector detector.Operation, vulnClient vulnerability.Operation) *Server {
return &Server{detector: detector, vulnClient: vulnClient}
}
// Detect is for backward compatibility
func (s *Server) Detect(_ context.Context, req *proto.LibDetectRequest) (res *proto.DetectResponse, err error) {
vulns, err := s.detector.Detect("", req.FilePath, time.Time{}, rpc.ConvertFromRpcLibraries(req.Libraries))
vulns, err := s.detector.Detect("", req.FilePath, time.Time{}, rpc.ConvertFromRPCLibraries(req.Libraries))
if err != nil {
err = xerrors.Errorf("failed to detect library vulnerabilities: %w", err)
log.Logger.Error(err)
@@ -41,5 +43,5 @@ func (s *Server) Detect(_ context.Context, req *proto.LibDetectRequest) (res *pr
s.vulnClient.FillInfo(vulns, "")
return &proto.DetectResponse{Vulnerabilities: rpc.ConvertToRpcVulns(vulns)}, nil
return &proto.DetectResponse{Vulnerabilities: rpc.ConvertToRPCVulns(vulns)}, nil
}

View File

@@ -24,11 +24,13 @@ import (
rpcScanner "github.com/aquasecurity/trivy/rpc/scanner"
)
// DBWorkerSuperSet binds the dependencies for Trivy DB worker
var DBWorkerSuperSet = wire.NewSet(
dbFile.SuperSet,
newDBWorker,
)
// ListenAndServe starts Trivy server
func ListenAndServe(c config.Config, fsCache cache.FSCache) error {
requestWg := &sync.WaitGroup{}
dbUpdateWg := &sync.WaitGroup{}
@@ -132,7 +134,7 @@ func (w dbWorker) hotUpdate(ctx context.Context, cacheDir string, dbUpdateWg, re
}
defer os.RemoveAll(tmpDir)
if err := w.dbClient.Download(ctx, tmpDir, false); err != nil {
if err = w.dbClient.Download(ctx, tmpDir, false); err != nil {
return xerrors.Errorf("failed to download vulnerability DB: %w", err)
}

View File

@@ -14,6 +14,7 @@ import (
proto "github.com/aquasecurity/trivy/rpc/detector"
)
// SuperSet binds dependencies for RPC server
var SuperSet = wire.NewSet(
detector.SuperSet,
vulnerability.SuperSet,
@@ -26,13 +27,14 @@ type Server struct {
vulnClient vulnerability.Operation
}
// NewServer is the factory method to return Server
func NewServer(detector detector.Operation, vulnClient vulnerability.Operation) *Server {
return &Server{detector: detector, vulnClient: vulnClient}
}
// Detect is for backward compatibility
func (s *Server) Detect(_ context.Context, req *proto.OSDetectRequest) (res *proto.DetectResponse, err error) {
vulns, eosl, err := s.detector.Detect("", req.OsFamily, req.OsName, time.Time{}, rpc.ConvertFromRpcPkgs(req.Packages))
vulns, eosl, err := s.detector.Detect("", req.OsFamily, req.OsName, time.Time{}, rpc.ConvertFromRPCPkgs(req.Packages))
if err != nil {
err = xerrors.Errorf("failed to detect vulnerabilities of OS packages: %w", err)
log.Logger.Error(err)
@@ -41,5 +43,5 @@ func (s *Server) Detect(_ context.Context, req *proto.OSDetectRequest) (res *pro
s.vulnClient.FillInfo(vulns, "")
return &proto.DetectResponse{Vulnerabilities: rpc.ConvertToRpcVulns(vulns), Eosl: eosl}, nil
return &proto.DetectResponse{Vulnerabilities: rpc.ConvertToRPCVulns(vulns), Eosl: eosl}, nil
}

View File

@@ -17,6 +17,7 @@ import (
rpcScanner "github.com/aquasecurity/trivy/rpc/scanner"
)
// ScanSuperSet binds the dependencies for server
var ScanSuperSet = wire.NewSet(
local.SuperSet,
wire.Bind(new(scanner.Driver), new(local.Scanner)),
@@ -24,15 +25,18 @@ var ScanSuperSet = wire.NewSet(
NewScanServer,
)
// ScanServer implements the scanner
type ScanServer struct {
localScanner scanner.Driver
vulnClient vulnerability.Operation
}
// NewScanServer is the factory method for scanner
func NewScanServer(s scanner.Driver, vulnClient vulnerability.Operation) *ScanServer {
return &ScanServer{localScanner: s, vulnClient: vulnClient}
}
// Scan scans and return response
func (s *ScanServer) Scan(_ context.Context, in *rpcScanner.ScanRequest) (*rpcScanner.ScanResponse, error) {
options := types.ScanOptions{VulnType: in.Options.VulnType}
results, os, eosl, err := s.localScanner.Scan(in.Target, in.ArtifactId, in.BlobIds, options)
@@ -43,39 +47,44 @@ func (s *ScanServer) Scan(_ context.Context, in *rpcScanner.ScanRequest) (*rpcSc
for i := range results {
s.vulnClient.FillInfo(results[i].Vulnerabilities, results[i].Type)
}
return rpc.ConvertToRpcScanResponse(results, os, eosl), nil
return rpc.ConvertToRPCScanResponse(results, os, eosl), nil
}
// CacheServer implements the cache
type CacheServer struct {
cache cache.Cache
}
// NewCacheServer is the facotry method for cacheServer
func NewCacheServer(c cache.Cache) *CacheServer {
return &CacheServer{cache: c}
}
// PutArtifact puts the artifacts in cache
func (s *CacheServer) PutArtifact(_ context.Context, in *rpcCache.PutArtifactRequest) (*google_protobuf.Empty, error) {
if in.ArtifactInfo == nil {
return nil, xerrors.Errorf("empty image info")
}
imageInfo := rpc.ConvertFromRpcPutArtifactRequest(in)
imageInfo := rpc.ConvertFromRPCPutArtifactRequest(in)
if err := s.cache.PutArtifact(in.ArtifactId, imageInfo); err != nil {
return nil, xerrors.Errorf("unable to store image info in cache: %w", err)
}
return &google_protobuf.Empty{}, nil
}
// PutBlob puts the blobs in cache
func (s *CacheServer) PutBlob(_ context.Context, in *rpcCache.PutBlobRequest) (*google_protobuf.Empty, error) {
if in.BlobInfo == nil {
return nil, xerrors.Errorf("empty layer info")
}
layerInfo := rpc.ConvertFromRpcPutBlobRequest(in)
layerInfo := rpc.ConvertFromRPCPutBlobRequest(in)
if err := s.cache.PutBlob(in.DiffId, layerInfo); err != nil {
return nil, xerrors.Errorf("unable to store layer info in cache: %w", err)
}
return &google_protobuf.Empty{}, nil
}
// MissingBlobs returns missing blobs from cache
func (s *CacheServer) MissingBlobs(_ context.Context, in *rpcCache.MissingBlobsRequest) (*rpcCache.MissingBlobsResponse, error) {
missingArtifact, blobIDs, err := s.cache.MissingBlobs(in.ArtifactId, in.BlobIds)
if err != nil {

View File

@@ -40,6 +40,7 @@ import (
"github.com/aquasecurity/trivy/pkg/utils"
)
// SuperSet binds dependencies for Local scan
var SuperSet = wire.NewSet(
applier.NewApplier,
wire.Bind(new(Applier), new(applier.Applier)),
@@ -50,28 +51,35 @@ var SuperSet = wire.NewSet(
NewScanner,
)
// Applier defines operation to scan image layers
type Applier interface {
ApplyLayers(artifactID string, blobIDs []string) (detail ftypes.ArtifactDetail, err error)
}
// OspkgDetector defines operation to detect OS vulnerabilities
type OspkgDetector interface {
Detect(imageName, osFamily, osName string, created time.Time, pkgs []ftypes.Package) (detectedVulns []types.DetectedVulnerability, eosl bool, err error)
}
// LibraryDetector defines operation to detect library vulnerabilities
type LibraryDetector interface {
Detect(imageName, filePath string, created time.Time, pkgs []ftypes.LibraryInfo) (detectedVulns []types.DetectedVulnerability, err error)
}
// Scanner implements the OspkgDetector and LibraryDetector
type Scanner struct {
applier Applier
ospkgDetector OspkgDetector
libDetector LibraryDetector
}
// NewScanner is the factory method for Scanner
func NewScanner(applier Applier, ospkgDetector OspkgDetector, libDetector LibraryDetector) Scanner {
return Scanner{applier: applier, ospkgDetector: ospkgDetector, libDetector: libDetector}
}
// Scan scans the local image and return results. TODO: fix cyclometic complexity
// nolint: gocyclo
func (s Scanner) Scan(target string, imageID string, layerIDs []string, options types.ScanOptions) (report.Results, *ftypes.OS, bool, error) {
imageDetail, err := s.applier.ApplyLayers(imageID, layerIDs)
if err != nil {

View File

@@ -26,6 +26,7 @@ var StandaloneSuperSet = wire.NewSet(
NewScanner,
)
// StandaloneDockerSet binds docker dependencies
var StandaloneDockerSet = wire.NewSet(
types.GetDockerOption,
image.NewDockerImage,
@@ -33,17 +34,20 @@ var StandaloneDockerSet = wire.NewSet(
StandaloneSuperSet,
)
// StandaloneArchiveSet binds archive scan dependencies
var StandaloneArchiveSet = wire.NewSet(
image.NewArchiveImage,
aimage.NewArtifact,
StandaloneSuperSet,
)
// StandaloneFilesystemSet binds filesystem dependencies
var StandaloneFilesystemSet = wire.NewSet(
flocal.NewArtifact,
StandaloneSuperSet,
)
// StandaloneRepositorySet binds repository dependencies
var StandaloneRepositorySet = wire.NewSet(
remote.NewArtifact,
StandaloneSuperSet,
@@ -57,30 +61,36 @@ var RemoteSuperSet = wire.NewSet(
NewScanner,
)
// RemoteDockerSet binds remote docker dependencies
var RemoteDockerSet = wire.NewSet(
types.GetDockerOption,
image.NewDockerImage,
RemoteSuperSet,
)
// RemoteArchiveSet binds remote archive dependencies
var RemoteArchiveSet = wire.NewSet(
image.NewArchiveImage,
RemoteSuperSet,
)
// Scanner implements the Artifact and Driver operations
type Scanner struct {
driver Driver
artifact artifact.Artifact
}
// Driver defines operations of scanner
type Driver interface {
Scan(target string, imageID string, layerIDs []string, options types.ScanOptions) (results report.Results, osFound *ftypes.OS, eols bool, err error)
}
// NewScanner is the factory method of Scanner
func NewScanner(driver Driver, ar artifact.Artifact) Scanner {
return Scanner{driver: driver, artifact: ar}
}
// ScanArtifact scans the artifacts and returns results
func (s Scanner) ScanArtifact(ctx context.Context, options types.ScanOptions) (report.Results, error) {
artifactInfo, err := s.artifact.Inspect(ctx)
if err != nil {

View File

@@ -18,6 +18,7 @@ var (
preReleaseSplitter = regexp.MustCompile(`(?P<Number>^[0-9]+)(?P<PreRelease>[a-z]*.*)`)
)
// MatchVersions runs comparison on currentVersion based on rangeVersions and return true/false
func MatchVersions(currentVersion *semver.Version, rangeVersions []string) bool {
for _, v := range rangeVersions {
v = replacer.Replace(v)
@@ -55,14 +56,17 @@ func MatchVersions(currentVersion *semver.Version, rangeVersions []string) bool
return false
}
// FormatVersion formats the package version based on epoch, version & release
func FormatVersion(pkg types.Package) string {
return formatVersion(pkg.Epoch, pkg.Version, pkg.Release)
}
// FormatSrcVersion formats the package version based on source epoch, version & release
func FormatSrcVersion(pkg types.Package) string {
return formatVersion(pkg.SrcEpoch, pkg.SrcVersion, pkg.SrcRelease)
}
// FormatPatchVersion returns the semver compatible version string given non-semver version
func FormatPatchVersion(version string) string {
part := strings.Split(version, ".")
if len(part) > 3 {

View File

@@ -9,6 +9,7 @@ import (
"github.com/aquasecurity/fanal/types"
)
// DockerConfig holds the config of Docker
type DockerConfig struct {
UserName string `env:"TRIVY_USERNAME"`
Password string `env:"TRIVY_PASSWORD"`
@@ -17,6 +18,7 @@ type DockerConfig struct {
NonSSL bool `env:"TRIVY_NON_SSL" envDefault:"false"`
}
// GetDockerOption returns the Docker scanning options using DockerConfig
func GetDockerOption(timeout time.Duration) (types.DockerOption, error) {
cfg := DockerConfig{}
if err := env.Parse(&cfg); err != nil {

View File

@@ -1,5 +1,6 @@
package types
// Library holds the attribute of a package library
type Library struct {
Name string
Version string

View File

@@ -1,5 +1,6 @@
package types
// ScanOptions holds the attributes for scanning vulnerabilities
type ScanOptions struct {
VulnType []string
ScanRemovedPackages bool

View File

@@ -5,6 +5,7 @@ import (
"github.com/aquasecurity/trivy-db/pkg/types"
)
// DetectedVulnerability holds the information of detected vulnerabilities
type DetectedVulnerability struct {
VulnerabilityID string `json:",omitempty"`
PkgName string `json:",omitempty"`
@@ -15,3 +16,26 @@ type DetectedVulnerability struct {
types.Vulnerability
}
// BySeverity implements sort.Interface based on the Severity field.
type BySeverity []DetectedVulnerability
// Len returns the length of DetectedVulnerabilities
func (v BySeverity) Len() int { return len(v) }
// Less compares 2 DetectedVulnerabilities based on package name, severity and vulnerabilityID
func (v BySeverity) Less(i, j int) bool {
if v[i].PkgName != v[j].PkgName {
return v[i].PkgName < v[j].PkgName
}
ret := types.CompareSeverityString(
v[j].Severity, v[i].Severity,
)
if ret != 0 {
return ret > 0
}
return v[i].VulnerabilityID < v[j].VulnerabilityID
}
// Swap swaps 2 vulnerability
func (v BySeverity) Swap(i, j int) { v[i], v[j] = v[j], v[i] }

View File

@@ -19,6 +19,7 @@ import (
var cacheDir string
// DefaultCacheDir returns/creates the cache-dir to be used for trivu operations
func DefaultCacheDir() string {
tmpDir, err := os.UserCacheDir()
if err != nil {
@@ -27,14 +28,17 @@ func DefaultCacheDir() string {
return filepath.Join(tmpDir, "trivy")
}
// CacheDir returns the directory used for caching
func CacheDir() string {
return cacheDir
}
// SetCacheDir sets the tricy cacheDir
func SetCacheDir(dir string) {
cacheDir = dir
}
// FileWalk walks the directory and performs operations on files defined by walkFn
func FileWalk(root string, targetFiles map[string]struct{}, walkFn func(r io.Reader, path string) error) error {
err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
if err != nil {
@@ -75,6 +79,7 @@ func FileWalk(root string, targetFiles map[string]struct{}, walkFn func(r io.Rea
return nil
}
// StringInSlice checks if strings exist in list of strings
func StringInSlice(a string, list []string) bool {
for _, b := range list {
if b == a {
@@ -84,6 +89,7 @@ func StringInSlice(a string, list []string) bool {
return false
}
// FilterTargets filters the target based on prefixPath
func FilterTargets(prefixPath string, targets map[string]struct{}) (map[string]struct{}, error) {
filtered := map[string]struct{}{}
for filename := range targets {
@@ -101,6 +107,7 @@ func FilterTargets(prefixPath string, targets map[string]struct{}) (map[string]s
return filtered, nil
}
// CopyFile copies the file content from scr to dst
func CopyFile(src, dst string) (int64, error) {
sourceFileStat, err := os.Stat(src)
if err != nil {

View File

@@ -21,29 +21,35 @@ import (
)
const (
// DefaultIgnoreFile is the file name to be ignored
DefaultIgnoreFile = ".trivyignore"
)
// SuperSet binds the dependencies
var SuperSet = wire.NewSet(
wire.Struct(new(db.Config)),
NewClient,
wire.Bind(new(Operation), new(Client)),
)
// Operation defines the vulnerability operations
type Operation interface {
FillInfo(vulns []types.DetectedVulnerability, reportType string)
Filter(ctx context.Context, vulns []types.DetectedVulnerability, severities []dbTypes.Severity,
ignoreUnfixed bool, ignoreFile string, policy string) ([]types.DetectedVulnerability, error)
}
// Client implements db operations
type Client struct {
dbc db.Operation
}
// NewClient is the factory method for Client
func NewClient(dbc db.Config) Client {
return Client{dbc: dbc}
}
// FillInfo fills extra info rin vulnerability objects
func (c Client) FillInfo(vulns []types.DetectedVulnerability, reportType string) {
var err error
@@ -91,6 +97,7 @@ func (c Client) getVendorSeverity(vuln *types.DetectedVulnerability, reportType
}
}
// Filter filter out the vulnerabilities
func (c Client) Filter(ctx context.Context, vulns []types.DetectedVulnerability, severities []dbTypes.Severity,
ignoreUnfixed bool, ignoreFile string, policyFile string) ([]types.DetectedVulnerability, error) {
ignoredIDs := getIgnoredIDs(ignoreFile)
@@ -118,19 +125,7 @@ func (c Client) Filter(ctx context.Context, vulns []types.DetectedVulnerability,
return nil, xerrors.Errorf("failed to apply the policy: %w", err)
}
}
sort.Slice(vulnerabilities, func(i, j int) bool {
if vulnerabilities[i].PkgName != vulnerabilities[j].PkgName {
return vulnerabilities[i].PkgName < vulnerabilities[j].PkgName
}
ret := dbTypes.CompareSeverityString(
vulnerabilities[j].Severity, vulnerabilities[i].Severity,
)
if ret != 0 {
return ret > 0
}
return vulnerabilities[i].VulnerabilityID < vulnerabilities[j].VulnerabilityID
})
sort.Sort(types.BySeverity(vulnerabilities))
return vulnerabilities, nil
}