diff --git a/.golangci.yaml b/.golangci.yaml index 591d6cbb42..a21384c153 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -19,6 +19,10 @@ linters-settings: locale: US goimports: local-prefixes: github.com/aquasecurity + gosec: + excludes: + - G204 + - G402 linters: disable-all: true @@ -53,9 +57,6 @@ issues: - linters: - gosec text: "Deferring unsafe method" - - linters: - - gosec - text: "G204: Subprocess launched with variable" - linters: - errcheck text: "Close` is not checked" diff --git a/pkg/cache/remote.go b/pkg/cache/remote.go index c703338e86..888d464bf1 100644 --- a/pkg/cache/remote.go +++ b/pkg/cache/remote.go @@ -2,6 +2,7 @@ package cache import ( "context" + "crypto/tls" "net/http" "golang.org/x/xerrors" @@ -19,13 +20,19 @@ type RemoteCache struct { 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 { +func NewRemoteCache(url string, customHeaders http.Header, insecure bool) cache.ArtifactCache { ctx := client.WithCustomHeaders(context.Background(), customHeaders) - c := rpcCache.NewCacheProtobufClient(string(url), &http.Client{}) + + httpClient := &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: insecure, + }, + }, + } + + c := rpcCache.NewCacheProtobufClient(url, httpClient) return &RemoteCache{ctx: ctx, client: c} } diff --git a/pkg/cache/remote_test.go b/pkg/cache/remote_test.go index 12db8d1c1f..9c903e8ab4 100644 --- a/pkg/cache/remote_test.go +++ b/pkg/cache/remote_test.go @@ -135,7 +135,7 @@ func TestRemoteCache_PutArtifact(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - c := cache.NewRemoteCache(cache.RemoteURL(ts.URL), tt.args.customHeaders) + c := cache.NewRemoteCache(ts.URL, tt.args.customHeaders, false) err := c.PutArtifact(tt.args.imageID, tt.args.imageInfo) if tt.wantErr != "" { require.NotNil(t, err, tt.name) @@ -196,7 +196,7 @@ func TestRemoteCache_PutBlob(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - c := cache.NewRemoteCache(cache.RemoteURL(ts.URL), tt.args.customHeaders) + c := cache.NewRemoteCache(ts.URL, tt.args.customHeaders, false) err := c.PutBlob(tt.args.diffID, tt.args.layerInfo) if tt.wantErr != "" { require.NotNil(t, err, tt.name) @@ -274,7 +274,7 @@ func TestRemoteCache_MissingBlobs(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - c := cache.NewRemoteCache(cache.RemoteURL(ts.URL), tt.args.customHeaders) + c := cache.NewRemoteCache(ts.URL, tt.args.customHeaders, false) gotMissingImage, gotMissingLayerIDs, err := c.MissingBlobs(tt.args.imageID, tt.args.layerIDs) if tt.wantErr != "" { require.NotNil(t, err, tt.name) @@ -289,3 +289,49 @@ func TestRemoteCache_MissingBlobs(t *testing.T) { }) } } + +func TestRemoteCache_PutArtifactInsecure(t *testing.T) { + ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) + defer ts.Close() + + type args struct { + imageID string + imageInfo types.ArtifactInfo + insecure bool + } + tests := []struct { + name string + args args + wantErr string + }{ + { + name: "happy path", + args: args{ + imageID: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a", + imageInfo: types.ArtifactInfo{}, + insecure: true, + }, + }, + { + name: "sad path", + args: args{ + imageID: "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a", + imageInfo: types.ArtifactInfo{}, + insecure: false, + }, + wantErr: "certificate signed by unknown authority", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := cache.NewRemoteCache(ts.URL, nil, tt.args.insecure) + err := c.PutArtifact(tt.args.imageID, tt.args.imageInfo) + if tt.wantErr != "" { + require.Error(t, err) + assert.Contains(t, err.Error(), tt.wantErr) + return + } + assert.NoError(t, err, tt.name) + }) + } +} diff --git a/pkg/commands/app.go b/pkg/commands/app.go index 73626018e8..a7d87be248 100644 --- a/pkg/commands/app.go +++ b/pkg/commands/app.go @@ -309,6 +309,7 @@ var ( insecureFlag = cli.BoolFlag{ Name: "insecure", Usage: "allow insecure server connections when using SSL", + Value: false, EnvVars: []string{"TRIVY_INSECURE"}, } @@ -585,6 +586,7 @@ func NewClientCommand() *cli.Command { stringSliceFlag(configPolicy), &listAllPackages, &offlineScan, + &insecureFlag, // original flags &token, diff --git a/pkg/commands/client/inject.go b/pkg/commands/client/inject.go index 65a61f28f5..697bef93cc 100644 --- a/pkg/commands/client/inject.go +++ b/pkg/commands/client/inject.go @@ -18,14 +18,14 @@ import ( ) func initializeDockerScanner(ctx context.Context, imageName string, artifactCache cache.ArtifactCache, customHeaders client.CustomHeaders, - url client.RemoteURL, dockerOpt types.DockerOption, artifactOption artifact.Option, configScannerOption config.ScannerOption) ( + url client.RemoteURL, insecure client.Insecure, dockerOpt types.DockerOption, artifactOption artifact.Option, configScannerOption config.ScannerOption) ( scanner.Scanner, func(), error) { wire.Build(scanner.RemoteDockerSet) return scanner.Scanner{}, nil, nil } func initializeArchiveScanner(ctx context.Context, filePath string, artifactCache cache.ArtifactCache, - customHeaders client.CustomHeaders, url client.RemoteURL, artifactOption artifact.Option, + customHeaders client.CustomHeaders, url client.RemoteURL, insecure client.Insecure, artifactOption artifact.Option, configScannerOption config.ScannerOption) (scanner.Scanner, error) { wire.Build(scanner.RemoteArchiveSet) return scanner.Scanner{}, nil diff --git a/pkg/commands/client/run.go b/pkg/commands/client/run.go index 343da566f0..b4a41acb19 100644 --- a/pkg/commands/client/run.go +++ b/pkg/commands/client/run.go @@ -138,7 +138,7 @@ func disabledAnalyzers(opt Option) []analyzer.Type { } func initializeScanner(ctx context.Context, opt Option) (scanner.Scanner, func(), error) { - remoteCache := cache.NewRemoteCache(cache.RemoteURL(opt.RemoteAddr), opt.CustomHeaders) + remoteCache := cache.NewRemoteCache(opt.RemoteAddr, opt.CustomHeaders, opt.Insecure) // ScannerOptions is filled only when config scanning is enabled. var configScannerOptions config.ScannerOption @@ -168,7 +168,7 @@ func initializeScanner(ctx context.Context, opt Option) (scanner.Scanner, func() if opt.Input != "" { // Scan tar file s, err := initializeArchiveScanner(ctx, opt.Input, remoteCache, client.CustomHeaders(opt.CustomHeaders), - client.RemoteURL(opt.RemoteAddr), artifactOpt, configScannerOptions) + client.RemoteURL(opt.RemoteAddr), client.Insecure(opt.Insecure), artifactOpt, configScannerOptions) if err != nil { return scanner.Scanner{}, nil, xerrors.Errorf("unable to initialize the archive scanner: %w", err) } @@ -182,7 +182,7 @@ func initializeScanner(ctx context.Context, opt Option) (scanner.Scanner, func() } s, cleanup, err := initializeDockerScanner(ctx, opt.Target, remoteCache, client.CustomHeaders(opt.CustomHeaders), - client.RemoteURL(opt.RemoteAddr), dockerOpt, artifactOpt, configScannerOptions) + client.RemoteURL(opt.RemoteAddr), client.Insecure(opt.Insecure), dockerOpt, artifactOpt, configScannerOptions) if err != nil { return scanner.Scanner{}, nil, xerrors.Errorf("unable to initialize the docker scanner: %w", err) } diff --git a/pkg/commands/client/wire_gen.go b/pkg/commands/client/wire_gen.go index a5f1671d19..4e1a77cefc 100644 --- a/pkg/commands/client/wire_gen.go +++ b/pkg/commands/client/wire_gen.go @@ -1,6 +1,6 @@ // Code generated by Wire. DO NOT EDIT. -//go:generate go run github.com/google/wire/cmd/wire +//go:generate wire //go:build !wireinject // +build !wireinject @@ -22,8 +22,8 @@ import ( // Injectors from inject.go: -func initializeDockerScanner(ctx context.Context, imageName string, artifactCache cache.ArtifactCache, customHeaders client.CustomHeaders, url client.RemoteURL, dockerOpt types.DockerOption, artifactOption artifact.Option, configScannerOption config.ScannerOption) (scanner.Scanner, func(), error) { - scannerScanner := client.NewProtobufClient(url) +func initializeDockerScanner(ctx context.Context, imageName string, artifactCache cache.ArtifactCache, customHeaders client.CustomHeaders, url client.RemoteURL, insecure client.Insecure, dockerOpt types.DockerOption, artifactOption artifact.Option, configScannerOption config.ScannerOption) (scanner.Scanner, func(), error) { + scannerScanner := client.NewProtobufClient(url, insecure) clientScanner := client.NewScanner(customHeaders, scannerScanner) typesImage, cleanup, err := image.NewDockerImage(ctx, imageName, dockerOpt) if err != nil { @@ -40,8 +40,8 @@ func initializeDockerScanner(ctx context.Context, imageName string, artifactCach }, nil } -func initializeArchiveScanner(ctx context.Context, filePath string, artifactCache cache.ArtifactCache, customHeaders client.CustomHeaders, url client.RemoteURL, artifactOption artifact.Option, configScannerOption config.ScannerOption) (scanner.Scanner, error) { - scannerScanner := client.NewProtobufClient(url) +func initializeArchiveScanner(ctx context.Context, filePath string, artifactCache cache.ArtifactCache, customHeaders client.CustomHeaders, url client.RemoteURL, insecure client.Insecure, artifactOption artifact.Option, configScannerOption config.ScannerOption) (scanner.Scanner, error) { + scannerScanner := client.NewProtobufClient(url, insecure) clientScanner := client.NewScanner(customHeaders, scannerScanner) typesImage, err := image.NewArchiveImage(filePath) if err != nil { diff --git a/pkg/rpc/client/client.go b/pkg/rpc/client/client.go index 61d201d63a..985a0511b8 100644 --- a/pkg/rpc/client/client.go +++ b/pkg/rpc/client/client.go @@ -2,15 +2,15 @@ package client import ( "context" + "crypto/tls" "net/http" - ftypes "github.com/aquasecurity/fanal/types" - "github.com/aquasecurity/trivy/pkg/types" "github.com/google/wire" "golang.org/x/xerrors" + ftypes "github.com/aquasecurity/fanal/types" r "github.com/aquasecurity/trivy/pkg/rpc" rpc "github.com/aquasecurity/trivy/rpc/scanner" ) @@ -24,9 +24,20 @@ var SuperSet = wire.NewSet( // RemoteURL for RPC remote host type RemoteURL string +// Insecure for RPC remote host +type Insecure bool + // NewProtobufClient is the factory method to return RPC scanner -func NewProtobufClient(remoteURL RemoteURL) rpc.Scanner { - return rpc.NewScannerProtobufClient(string(remoteURL), &http.Client{}) +func NewProtobufClient(remoteURL RemoteURL, insecure Insecure) rpc.Scanner { + httpClient := &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: bool(insecure), + }, + }, + } + + return rpc.NewScannerProtobufClient(string(remoteURL), httpClient) } // CustomHeaders for holding HTTP headers diff --git a/pkg/rpc/client/client_test.go b/pkg/rpc/client/client_test.go index bf60fc6a74..ebcef0e2e8 100644 --- a/pkg/rpc/client/client_test.go +++ b/pkg/rpc/client/client_test.go @@ -3,6 +3,8 @@ package client import ( "context" "errors" + "net/http" + "net/http/httptest" "testing" "github.com/golang/protobuf/ptypes/timestamp" @@ -283,3 +285,48 @@ func TestScanner_Scan(t *testing.T) { }) } } + +func TestScanner_ScanServerInsecure(t *testing.T) { + ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})) + defer ts.Close() + + type args struct { + request *scanner.ScanRequest + insecure bool + } + tests := []struct { + name string + args args + wantErr string + }{ + { + name: "happy path", + args: args{ + request: &scanner.ScanRequest{}, + insecure: true, + }, + }, + { + name: "sad path", + args: args{ + request: &scanner.ScanRequest{}, + insecure: false, + }, + wantErr: "certificate signed by unknown authority", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + + s := NewProtobufClient(RemoteURL(ts.URL), Insecure(tt.args.insecure)) + _, err := s.Scan(context.Background(), tt.args.request) + + if tt.wantErr != "" { + require.Error(t, err) + require.Contains(t, err.Error(), tt.wantErr) + return + } + require.NoError(t, err) + }) + } +}