github.com/devseccon/trivy@v0.47.1-0.20231123133102-bd902a0bd996/pkg/rpc/client/client_test.go (about)

     1  package client
     2  
     3  import (
     4  	"context"
     5  	"crypto/tls"
     6  	"encoding/json"
     7  	"fmt"
     8  	"net/http"
     9  	"net/http/httptest"
    10  	"testing"
    11  
    12  	"github.com/golang/protobuf/ptypes/timestamp"
    13  	"github.com/stretchr/testify/assert"
    14  	"github.com/stretchr/testify/require"
    15  	"google.golang.org/protobuf/encoding/protojson"
    16  
    17  	dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
    18  	"github.com/aquasecurity/trivy-db/pkg/utils"
    19  	"github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability"
    20  	ftypes "github.com/devseccon/trivy/pkg/fanal/types"
    21  	"github.com/devseccon/trivy/pkg/types"
    22  	"github.com/devseccon/trivy/rpc/common"
    23  	rpc "github.com/devseccon/trivy/rpc/scanner"
    24  )
    25  
    26  func TestScanner_Scan(t *testing.T) {
    27  	type args struct {
    28  		target   string
    29  		imageID  string
    30  		layerIDs []string
    31  		options  types.ScanOptions
    32  	}
    33  	tests := []struct {
    34  		name          string
    35  		customHeaders http.Header
    36  		args          args
    37  		expectation   *rpc.ScanResponse
    38  		wantResults   types.Results
    39  		wantOS        ftypes.OS
    40  		wantEosl      bool
    41  		wantErr       string
    42  	}{
    43  		{
    44  			name: "happy path",
    45  			customHeaders: http.Header{
    46  				"Trivy-Token": []string{"foo"},
    47  			},
    48  			args: args{
    49  				target:   "alpine:3.11",
    50  				imageID:  "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
    51  				layerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
    52  				options: types.ScanOptions{
    53  					VulnType: []string{"os"},
    54  				},
    55  			},
    56  			expectation: &rpc.ScanResponse{
    57  				Os: &common.OS{
    58  					Family: "alpine",
    59  					Name:   "3.11",
    60  					Eosl:   true,
    61  				},
    62  				Results: []*rpc.Result{
    63  					{
    64  						Target: "alpine:3.11",
    65  						Vulnerabilities: []*common.Vulnerability{
    66  							{
    67  								VulnerabilityId:  "CVE-2020-0001",
    68  								PkgName:          "musl",
    69  								InstalledVersion: "1.2.3",
    70  								FixedVersion:     "1.2.4",
    71  								Title:            "DoS",
    72  								Description:      "Denial os Service",
    73  								Severity:         common.Severity_CRITICAL,
    74  								References:       []string{"http://example.com"},
    75  								SeveritySource:   "nvd",
    76  								VendorSeverity: map[string]common.Severity{
    77  									string(vulnerability.NVD):    common.Severity_MEDIUM,
    78  									string(vulnerability.RedHat): common.Severity_MEDIUM,
    79  								},
    80  								Cvss: map[string]*common.CVSS{
    81  									"nvd": {
    82  										V2Vector: "AV:L/AC:L/Au:N/C:C/I:C/A:C",
    83  										V3Vector: "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
    84  										V2Score:  7.2,
    85  										V3Score:  7.8,
    86  									},
    87  									"redhat": {
    88  										V2Vector: "AV:H/AC:L/Au:N/C:C/I:C/A:C",
    89  										V3Vector: "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
    90  										V2Score:  4.2,
    91  										V3Score:  2.8,
    92  									},
    93  								},
    94  								CweIds: []string{"CWE-78"},
    95  								Layer: &common.Layer{
    96  									DiffId: "sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10",
    97  								},
    98  								LastModifiedDate: &timestamp.Timestamp{
    99  									Seconds: 1577840460,
   100  								},
   101  								PublishedDate: &timestamp.Timestamp{
   102  									Seconds: 978310860,
   103  								},
   104  							},
   105  						},
   106  					},
   107  				},
   108  			},
   109  			wantResults: types.Results{
   110  				{
   111  					Target: "alpine:3.11",
   112  					Vulnerabilities: []types.DetectedVulnerability{
   113  						{
   114  							VulnerabilityID:  "CVE-2020-0001",
   115  							PkgName:          "musl",
   116  							InstalledVersion: "1.2.3",
   117  							FixedVersion:     "1.2.4",
   118  							Vulnerability: dbTypes.Vulnerability{
   119  								Title:       "DoS",
   120  								Description: "Denial os Service",
   121  								Severity:    "CRITICAL",
   122  								References:  []string{"http://example.com"},
   123  								VendorSeverity: dbTypes.VendorSeverity{
   124  									vulnerability.NVD:    dbTypes.SeverityMedium,
   125  									vulnerability.RedHat: dbTypes.SeverityMedium,
   126  								},
   127  								CVSS: dbTypes.VendorCVSS{
   128  									"nvd": {
   129  										V2Vector: "AV:L/AC:L/Au:N/C:C/I:C/A:C",
   130  										V3Vector: "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
   131  										V2Score:  7.2,
   132  										V3Score:  7.8,
   133  									},
   134  									"redhat": {
   135  										V2Vector: "AV:H/AC:L/Au:N/C:C/I:C/A:C",
   136  										V3Vector: "CVSS:3.1/AV:L/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H",
   137  										V2Score:  4.2,
   138  										V3Score:  2.8,
   139  									},
   140  								},
   141  								CweIDs:           []string{"CWE-78"},
   142  								LastModifiedDate: utils.MustTimeParse("2020-01-01T01:01:00Z"),
   143  								PublishedDate:    utils.MustTimeParse("2001-01-01T01:01:00Z"),
   144  							},
   145  							SeveritySource: "nvd",
   146  							Layer: ftypes.Layer{
   147  								DiffID: "sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10",
   148  							},
   149  						},
   150  					},
   151  				},
   152  			},
   153  			wantOS: ftypes.OS{
   154  				Family: "alpine",
   155  				Name:   "3.11",
   156  				Eosl:   true,
   157  			},
   158  		},
   159  		{
   160  			name: "sad path: Scan returns an error",
   161  			customHeaders: http.Header{
   162  				"Trivy-Token": []string{"foo"},
   163  			},
   164  			args: args{
   165  				target:   "alpine:3.11",
   166  				imageID:  "sha256:e7d92cdc71feacf90708cb59182d0df1b911f8ae022d29e8e95d75ca6a99776a",
   167  				layerIDs: []string{"sha256:5216338b40a7b96416b8b9858974bbe4acc3096ee60acbc4dfb1ee02aecceb10"},
   168  				options: types.ScanOptions{
   169  					VulnType: []string{"os"},
   170  				},
   171  			},
   172  			wantErr: "failed to detect vulnerabilities via RPC",
   173  		},
   174  	}
   175  	for _, tt := range tests {
   176  		t.Run(tt.name, func(t *testing.T) {
   177  			ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   178  				if tt.expectation == nil {
   179  					e := map[string]interface{}{
   180  						"code": "not_found",
   181  						"msg":  "expectation is empty",
   182  					}
   183  					b, _ := json.Marshal(e)
   184  					w.WriteHeader(http.StatusBadGateway)
   185  					w.Write(b)
   186  					return
   187  				}
   188  				b, err := protojson.Marshal(tt.expectation)
   189  				if err != nil {
   190  					w.WriteHeader(http.StatusInternalServerError)
   191  					fmt.Fprintf(w, "json marshaling error: %v", err)
   192  					return
   193  				}
   194  				w.Header().Set("Content-Type", "application/json")
   195  				w.Write(b)
   196  			}))
   197  			client := rpc.NewScannerJSONClient(ts.URL, ts.Client())
   198  
   199  			s := NewScanner(ScannerOption{CustomHeaders: tt.customHeaders}, WithRPCClient(client))
   200  
   201  			gotResults, gotOS, err := s.Scan(context.Background(), tt.args.target, tt.args.imageID, tt.args.layerIDs, tt.args.options)
   202  
   203  			if tt.wantErr != "" {
   204  				require.NotNil(t, err, tt.name)
   205  				require.Contains(t, err.Error(), tt.wantErr, tt.name)
   206  				return
   207  			}
   208  
   209  			require.NoError(t, err, tt.name)
   210  			assert.Equal(t, tt.wantResults, gotResults)
   211  			assert.Equal(t, tt.wantOS, gotOS)
   212  		})
   213  	}
   214  }
   215  
   216  func TestScanner_ScanServerInsecure(t *testing.T) {
   217  	ts := httptest.NewTLSServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {}))
   218  	defer ts.Close()
   219  
   220  	tests := []struct {
   221  		name     string
   222  		insecure bool
   223  		wantErr  string
   224  	}{
   225  		{
   226  			name:     "happy path",
   227  			insecure: true,
   228  		},
   229  		{
   230  			name:     "sad path",
   231  			insecure: false,
   232  			wantErr:  "failed to do request",
   233  		},
   234  	}
   235  	for _, tt := range tests {
   236  		t.Run(tt.name, func(t *testing.T) {
   237  			c := rpc.NewScannerProtobufClient(ts.URL, &http.Client{
   238  				Transport: &http.Transport{
   239  					TLSClientConfig: &tls.Config{
   240  						InsecureSkipVerify: tt.insecure,
   241  					},
   242  				},
   243  			})
   244  			s := NewScanner(ScannerOption{Insecure: tt.insecure}, WithRPCClient(c))
   245  			_, _, err := s.Scan(context.Background(), "dummy", "", nil, types.ScanOptions{})
   246  
   247  			if tt.wantErr != "" {
   248  				require.Error(t, err)
   249  				require.Contains(t, err.Error(), tt.wantErr)
   250  				return
   251  			}
   252  			require.NoError(t, err)
   253  		})
   254  	}
   255  }