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: ×tamp.Timestamp{ 99 Seconds: 1577840460, 100 }, 101 PublishedDate: ×tamp.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 }