github.com/devseccon/trivy@v0.47.1-0.20231123133102-bd902a0bd996/pkg/cloud/report/report.go (about) 1 package report 2 3 import ( 4 "context" 5 "io" 6 "os" 7 "sort" 8 "time" 9 10 "golang.org/x/xerrors" 11 12 "github.com/aquasecurity/defsec/pkg/scan" 13 "github.com/aquasecurity/tml" 14 "github.com/devseccon/trivy/pkg/clock" 15 cr "github.com/devseccon/trivy/pkg/compliance/report" 16 ftypes "github.com/devseccon/trivy/pkg/fanal/types" 17 "github.com/devseccon/trivy/pkg/flag" 18 pkgReport "github.com/devseccon/trivy/pkg/report" 19 "github.com/devseccon/trivy/pkg/result" 20 "github.com/devseccon/trivy/pkg/types" 21 ) 22 23 const ( 24 tableFormat = "table" 25 ) 26 27 // Report represents an AWS scan report 28 type Report struct { 29 Provider string 30 AccountID string 31 Region string 32 Results map[string]ResultsAtTime 33 ServicesInScope []string 34 } 35 36 type ResultsAtTime struct { 37 Results types.Results 38 CreationTime time.Time 39 } 40 41 func New(provider, accountID, region string, defsecResults scan.Results, scopedServices []string) *Report { 42 return &Report{ 43 Provider: provider, 44 AccountID: accountID, 45 Results: ConvertResults(defsecResults, provider, scopedServices), 46 ServicesInScope: scopedServices, 47 Region: region, 48 } 49 } 50 51 // Failed returns whether the aws report includes any "failed" results 52 func (r *Report) Failed() bool { 53 for _, set := range r.Results { 54 if set.Results.Failed() { 55 return true 56 } 57 } 58 return false 59 } 60 61 // Write writes the results in the give format 62 func Write(rep *Report, opt flag.Options, fromCache bool) error { 63 output, cleanup, err := opt.OutputWriter() 64 if err != nil { 65 return xerrors.Errorf("failed to create output file: %w", err) 66 } 67 defer cleanup() 68 69 if opt.Compliance.Spec.ID != "" { 70 return writeCompliance(rep, opt, output) 71 } 72 73 var filtered []types.Result 74 75 ctx := context.Background() 76 77 // filter results 78 for _, resultsAtTime := range rep.Results { 79 for _, res := range resultsAtTime.Results { 80 resCopy := res 81 if err := result.FilterResult(ctx, &resCopy, result.IgnoreConfig{}, result.FilterOption{ 82 Severities: opt.Severities, 83 IncludeNonFailures: opt.IncludeNonFailures, 84 }); err != nil { 85 return err 86 } 87 sort.Slice(resCopy.Misconfigurations, func(i, j int) bool { 88 return resCopy.Misconfigurations[i].CauseMetadata.Resource < resCopy.Misconfigurations[j].CauseMetadata.Resource 89 }) 90 filtered = append(filtered, resCopy) 91 } 92 } 93 sort.Slice(filtered, func(i, j int) bool { 94 return filtered[i].Target < filtered[j].Target 95 }) 96 97 base := types.Report{ 98 CreatedAt: clock.Now(), 99 ArtifactName: rep.AccountID, 100 ArtifactType: ftypes.ArtifactAWSAccount, 101 Results: filtered, 102 } 103 104 switch opt.Format { 105 case tableFormat: 106 107 // ensure color/formatting is disabled for pipes/non-pty 108 var useANSI bool 109 if output == os.Stdout { 110 if o, err := os.Stdout.Stat(); err == nil { 111 useANSI = (o.Mode() & os.ModeCharDevice) == os.ModeCharDevice 112 } 113 } 114 if !useANSI { 115 tml.DisableFormatting() 116 } 117 118 switch { 119 case len(opt.Services) == 1 && opt.ARN == "": 120 if err := writeResourceTable(rep, filtered, output, opt.Services[0]); err != nil { 121 return err 122 } 123 case len(opt.Services) == 1 && opt.ARN != "": 124 if err := writeResultsForARN(rep, filtered, output, opt.Services[0], opt.ARN, opt.Severities); err != nil { 125 return err 126 } 127 default: 128 if err := writeServiceTable(rep, filtered, output); err != nil { 129 return err 130 } 131 } 132 133 // render cache info 134 if fromCache { 135 _ = tml.Fprintf(output, "\n<blue>This scan report was loaded from cached results. If you'd like to run a fresh scan, use --update-cache.</blue>\n") 136 } 137 138 return nil 139 default: 140 return pkgReport.Write(base, opt) 141 } 142 } 143 144 func writeCompliance(rep *Report, opt flag.Options, output io.Writer) error { 145 var crr []types.Results 146 for _, r := range rep.Results { 147 crr = append(crr, r.Results) 148 } 149 150 complianceReport, err := cr.BuildComplianceReport(crr, opt.Compliance) 151 if err != nil { 152 return xerrors.Errorf("compliance report build error: %w", err) 153 } 154 155 return cr.Write(complianceReport, cr.Option{ 156 Format: opt.Format, 157 Report: opt.ReportFormat, 158 Output: output, 159 }) 160 }