github.com/devseccon/trivy@v0.47.1-0.20231123133102-bd902a0bd996/pkg/cloud/report/service_test.go (about) 1 package report 2 3 import ( 4 "bytes" 5 "github.com/devseccon/trivy/pkg/clock" 6 "testing" 7 "time" 8 9 "github.com/aws/aws-sdk-go-v2/aws/arn" 10 "github.com/stretchr/testify/assert" 11 "github.com/stretchr/testify/require" 12 13 "github.com/aquasecurity/defsec/pkg/scan" 14 defsecTypes "github.com/aquasecurity/defsec/pkg/types" 15 "github.com/aquasecurity/trivy-db/pkg/types" 16 "github.com/devseccon/trivy/pkg/flag" 17 ) 18 19 func Test_ServiceReport(t *testing.T) { 20 tests := []struct { 21 name string 22 options flag.Options 23 fromCache bool 24 expected string 25 }{ 26 { 27 name: "simple table output", 28 options: flag.Options{ 29 ReportOptions: flag.ReportOptions{ 30 Format: tableFormat, 31 Severities: []types.Severity{ 32 types.SeverityLow, 33 types.SeverityMedium, 34 types.SeverityHigh, 35 types.SeverityCritical, 36 }, 37 }, 38 }, 39 fromCache: false, 40 expected: ` 41 Scan Overview for AWS Account 42 ┌─────────┬──────────────────────────────────────────────────┬──────────────┐ 43 │ │ Misconfigurations │ │ 44 │ ├──────────┬──────────────┬────────┬─────┬─────────┤ │ 45 │ Service │ Critical │ High │ Medium │ Low │ Unknown │ Last Scanned │ 46 ├─────────┼──────────┼──────────────┼────────┼─────┼─────────┼──────────────┤ 47 │ ec2 │ 0 │ 1 │ 0 │ 0 │ 0 │ just now │ 48 │ s3 │ 0 │ 3 │ 0 │ 0 │ 0 │ just now │ 49 └─────────┴──────────┴──────────────┴────────┴─────┴─────────┴──────────────┘ 50 `, 51 }, 52 { 53 name: "results from cache", 54 options: flag.Options{ 55 ReportOptions: flag.ReportOptions{ 56 Format: tableFormat, 57 Severities: []types.Severity{ 58 types.SeverityLow, 59 types.SeverityMedium, 60 types.SeverityHigh, 61 types.SeverityCritical, 62 }, 63 }, 64 }, 65 fromCache: true, 66 expected: ` 67 Scan Overview for AWS Account 68 ┌─────────┬──────────────────────────────────────────────────┬──────────────┐ 69 │ │ Misconfigurations │ │ 70 │ ├──────────┬──────────────┬────────┬─────┬─────────┤ │ 71 │ Service │ Critical │ High │ Medium │ Low │ Unknown │ Last Scanned │ 72 ├─────────┼──────────┼──────────────┼────────┼─────┼─────────┼──────────────┤ 73 │ ec2 │ 0 │ 1 │ 0 │ 0 │ 0 │ just now │ 74 │ s3 │ 0 │ 3 │ 0 │ 0 │ 0 │ just now │ 75 └─────────┴──────────┴──────────────┴────────┴─────┴─────────┴──────────────┘ 76 77 This scan report was loaded from cached results. If you'd like to run a fresh scan, use --update-cache. 78 `, 79 }, 80 { 81 name: "filter severities", 82 options: flag.Options{ 83 ReportOptions: flag.ReportOptions{ 84 Format: tableFormat, 85 Severities: []types.Severity{ 86 types.SeverityMedium, 87 }, 88 }, 89 AWSOptions: flag.AWSOptions{ 90 Services: []string{"s3", "ec2"}, 91 }, 92 }, 93 fromCache: false, 94 expected: ` 95 Scan Overview for AWS Account 96 ┌─────────┬──────────────────────────────────────────────────┬──────────────┐ 97 │ │ Misconfigurations │ │ 98 │ ├──────────┬──────────────┬────────┬─────┬─────────┤ │ 99 │ Service │ Critical │ High │ Medium │ Low │ Unknown │ Last Scanned │ 100 ├─────────┼──────────┼──────────────┼────────┼─────┼─────────┼──────────────┤ 101 │ ec2 │ 0 │ 0 │ 0 │ 0 │ 0 │ just now │ 102 │ s3 │ 0 │ 0 │ 0 │ 0 │ 0 │ just now │ 103 └─────────┴──────────┴──────────────┴────────┴─────┴─────────┴──────────────┘ 104 `, 105 }, 106 { 107 name: "scoped services without results", 108 options: flag.Options{ 109 ReportOptions: flag.ReportOptions{ 110 Format: tableFormat, 111 Severities: []types.Severity{ 112 types.SeverityLow, 113 types.SeverityMedium, 114 types.SeverityHigh, 115 types.SeverityCritical, 116 }, 117 }, 118 AWSOptions: flag.AWSOptions{ 119 Services: []string{"ec2", "s3", "iam"}, 120 }, 121 }, 122 fromCache: false, 123 expected: ` 124 Scan Overview for AWS Account 125 ┌─────────┬──────────────────────────────────────────────────┬──────────────┐ 126 │ │ Misconfigurations │ │ 127 │ ├──────────┬──────────────┬────────┬─────┬─────────┤ │ 128 │ Service │ Critical │ High │ Medium │ Low │ Unknown │ Last Scanned │ 129 ├─────────┼──────────┼──────────────┼────────┼─────┼─────────┼──────────────┤ 130 │ ec2 │ 0 │ 1 │ 0 │ 0 │ 0 │ just now │ 131 │ iam │ 0 │ 0 │ 0 │ 0 │ 0 │ just now │ 132 │ s3 │ 0 │ 3 │ 0 │ 0 │ 0 │ just now │ 133 └─────────┴──────────┴──────────────┴────────┴─────┴─────────┴──────────────┘ 134 `, 135 }, 136 { 137 name: "json output", 138 options: flag.Options{ 139 ReportOptions: flag.ReportOptions{ 140 Format: "json", 141 Severities: []types.Severity{ 142 types.SeverityLow, 143 types.SeverityMedium, 144 types.SeverityHigh, 145 types.SeverityCritical, 146 }, 147 }, 148 }, 149 fromCache: false, 150 expected: `{ 151 "CreatedAt": "2021-08-25T12:20:30.000000005Z", 152 "ArtifactType": "aws_account", 153 "Metadata": { 154 "ImageConfig": { 155 "architecture": "", 156 "created": "0001-01-01T00:00:00Z", 157 "os": "", 158 "rootfs": { 159 "type": "", 160 "diff_ids": null 161 }, 162 "config": {} 163 } 164 }, 165 "Results": [ 166 { 167 "Target": "arn:aws:ec2:us-east-1:1234567890:instance1", 168 "Class": "config", 169 "Type": "cloud", 170 "MisconfSummary": { 171 "Successes": 0, 172 "Failures": 1, 173 "Exceptions": 0 174 }, 175 "Misconfigurations": [ 176 { 177 "Type": "AWS", 178 "ID": "AVD-AWS-9999", 179 "AVDID": "AVD-AWS-9999", 180 "Title": "Do not use bad stuff", 181 "Description": "Bad stuff is... bad", 182 "Message": "instance is bad", 183 "Resolution": "Remove bad stuff", 184 "Severity": "HIGH", 185 "PrimaryURL": "https://avd.aquasec.com/misconfig/avd-aws-9999", 186 "References": [ 187 "https://avd.aquasec.com/misconfig/avd-aws-9999" 188 ], 189 "Status": "FAIL", 190 "Layer": {}, 191 "CauseMetadata": { 192 "Resource": "arn:aws:ec2:us-east-1:1234567890:instance1", 193 "Provider": "AWS", 194 "Service": "ec2", 195 "Code": { 196 "Lines": null 197 } 198 } 199 } 200 ] 201 }, 202 { 203 "Target": "arn:aws:s3:us-east-1:1234567890:bucket1", 204 "Class": "config", 205 "Type": "cloud", 206 "MisconfSummary": { 207 "Successes": 0, 208 "Failures": 1, 209 "Exceptions": 0 210 }, 211 "Misconfigurations": [ 212 { 213 "Type": "AWS", 214 "ID": "AVD-AWS-9999", 215 "AVDID": "AVD-AWS-9999", 216 "Title": "Do not use bad stuff", 217 "Description": "Bad stuff is... bad", 218 "Message": "something failed", 219 "Resolution": "Remove bad stuff", 220 "Severity": "HIGH", 221 "PrimaryURL": "https://avd.aquasec.com/misconfig/avd-aws-9999", 222 "References": [ 223 "https://avd.aquasec.com/misconfig/avd-aws-9999" 224 ], 225 "Status": "FAIL", 226 "Layer": {}, 227 "CauseMetadata": { 228 "Resource": "arn:aws:s3:us-east-1:1234567890:bucket1", 229 "Provider": "AWS", 230 "Service": "s3", 231 "Code": { 232 "Lines": null 233 } 234 } 235 } 236 ] 237 }, 238 { 239 "Target": "arn:aws:s3:us-east-1:1234567890:bucket2", 240 "Class": "config", 241 "Type": "cloud", 242 "MisconfSummary": { 243 "Successes": 0, 244 "Failures": 2, 245 "Exceptions": 0 246 }, 247 "Misconfigurations": [ 248 { 249 "Type": "AWS", 250 "ID": "AVD-AWS-9999", 251 "AVDID": "AVD-AWS-9999", 252 "Title": "Do not use bad stuff", 253 "Description": "Bad stuff is... bad", 254 "Message": "something else failed", 255 "Resolution": "Remove bad stuff", 256 "Severity": "HIGH", 257 "PrimaryURL": "https://avd.aquasec.com/misconfig/avd-aws-9999", 258 "References": [ 259 "https://avd.aquasec.com/misconfig/avd-aws-9999" 260 ], 261 "Status": "FAIL", 262 "Layer": {}, 263 "CauseMetadata": { 264 "Resource": "arn:aws:s3:us-east-1:1234567890:bucket2", 265 "Provider": "AWS", 266 "Service": "s3", 267 "Code": { 268 "Lines": null 269 } 270 } 271 }, 272 { 273 "Type": "AWS", 274 "ID": "AVD-AWS-9999", 275 "AVDID": "AVD-AWS-9999", 276 "Title": "Do not use bad stuff", 277 "Description": "Bad stuff is... bad", 278 "Message": "something else failed again", 279 "Resolution": "Remove bad stuff", 280 "Severity": "HIGH", 281 "PrimaryURL": "https://avd.aquasec.com/misconfig/avd-aws-9999", 282 "References": [ 283 "https://avd.aquasec.com/misconfig/avd-aws-9999" 284 ], 285 "Status": "FAIL", 286 "Layer": {}, 287 "CauseMetadata": { 288 "Resource": "arn:aws:s3:us-east-1:1234567890:bucket2", 289 "Provider": "AWS", 290 "Service": "s3", 291 "Code": { 292 "Lines": null 293 } 294 } 295 } 296 ] 297 }, 298 { 299 "Target": "arn:aws:s3:us-east-1:1234567890:bucket3", 300 "Class": "config", 301 "Type": "cloud", 302 "MisconfSummary": { 303 "Successes": 1, 304 "Failures": 0, 305 "Exceptions": 0 306 } 307 } 308 ] 309 }`, 310 }, 311 } 312 clock.SetFakeTime(t, time.Date(2021, 8, 25, 12, 20, 30, 5, time.UTC)) 313 for _, tt := range tests { 314 t.Run(tt.name, func(t *testing.T) { 315 report := New( 316 "AWS", 317 tt.options.AWSOptions.Account, 318 tt.options.AWSOptions.Region, 319 createTestResults(), 320 tt.options.AWSOptions.Services, 321 ) 322 323 output := bytes.NewBuffer(nil) 324 tt.options.SetOutputWriter(output) 325 require.NoError(t, Write(report, tt.options, tt.fromCache)) 326 327 assert.Equal(t, "AWS", report.Provider) 328 assert.Equal(t, tt.options.AWSOptions.Account, report.AccountID) 329 assert.Equal(t, tt.options.AWSOptions.Region, report.Region) 330 assert.ElementsMatch(t, tt.options.AWSOptions.Services, report.ServicesInScope) 331 332 if tt.options.Format == "json" { 333 // json output can be formatted/ordered differently - we just care that the data matches 334 assert.JSONEq(t, tt.expected, output.String()) 335 } else { 336 assert.Equal(t, tt.expected, output.String()) 337 } 338 }) 339 } 340 } 341 342 func createTestResults() scan.Results { 343 344 baseRule := scan.Rule{ 345 AVDID: "AVD-AWS-9999", 346 Aliases: []string{"AWS999"}, 347 ShortCode: "no-bad-stuff", 348 Summary: "Do not use bad stuff", 349 Explanation: "Bad stuff is... bad", 350 Impact: "Bad things", 351 Resolution: "Remove bad stuff", 352 Provider: "AWS", 353 Severity: "HIGH", 354 } 355 356 var s3Results scan.Results 357 s3Results.Add( 358 "something failed", 359 defsecTypes.NewRemoteMetadata((arn.ARN{ 360 Partition: "aws", 361 Service: "s3", 362 Region: "us-east-1", 363 AccountID: "1234567890", 364 Resource: "bucket1", 365 }).String()), 366 ) 367 s3Results.Add( 368 "something else failed", 369 defsecTypes.NewRemoteMetadata((arn.ARN{ 370 Partition: "aws", 371 Service: "s3", 372 Region: "us-east-1", 373 AccountID: "1234567890", 374 Resource: "bucket2", 375 }).String()), 376 ) 377 s3Results.Add( 378 "something else failed again", 379 defsecTypes.NewRemoteMetadata((arn.ARN{ 380 Partition: "aws", 381 Service: "s3", 382 Region: "us-east-1", 383 AccountID: "1234567890", 384 Resource: "bucket2", 385 }).String()), 386 ) 387 s3Results.AddPassed( 388 defsecTypes.NewRemoteMetadata((arn.ARN{ 389 Partition: "aws", 390 Service: "s3", 391 Region: "us-east-1", 392 AccountID: "1234567890", 393 Resource: "bucket3", 394 }).String()), 395 ) 396 baseRule.Service = "s3" 397 s3Results.SetRule(baseRule) 398 var ec2Results scan.Results 399 ec2Results.Add( 400 "instance is bad", 401 defsecTypes.NewRemoteMetadata((arn.ARN{ 402 Partition: "aws", 403 Service: "ec2", 404 Region: "us-east-1", 405 AccountID: "1234567890", 406 Resource: "instance1", 407 }).String()), 408 ) 409 baseRule.Service = "ec2" 410 ec2Results.SetRule(baseRule) 411 return append(s3Results, ec2Results...) 412 }