github.com/khulnasoft-lab/defsec@v1.0.5-0.20230827010352-5e9f46893d95/pkg/formatters/formatter.go (about) 1 package formatters 2 3 import ( 4 "fmt" 5 "io" 6 "os" 7 "sort" 8 9 "github.com/khulnasoft-lab/defsec/pkg/types" 10 11 "github.com/khulnasoft-lab/defsec/pkg/severity" 12 13 "github.com/khulnasoft-lab/defsec/pkg/scan" 14 15 "github.com/liamg/tml" 16 ) 17 18 type Formatter interface { 19 Output(scan.Results) error 20 } 21 22 type ConfigurableFormatter interface { 23 Writer() io.Writer 24 GetLinks(scan.Result) []string 25 BaseDir() string 26 DebugEnabled() bool 27 GroupResults([]scan.Result) ([]GroupedResult, error) 28 IncludePassed() bool 29 IncludeIgnored() bool 30 Path(result scan.Result, metadata types.Metadata) string 31 } 32 33 type Base struct { 34 enableGrouping bool 35 enableMetrics bool 36 enableColours bool 37 enableDebug bool 38 includePassed bool 39 includeIgnored bool 40 fsRoot string 41 baseDir string 42 writer io.Writer 43 relative bool 44 outputOverride func(ConfigurableFormatter, scan.Results) error 45 linksOverride func(result scan.Result) []string 46 } 47 48 func NewBase() *Base { 49 return &Base{ 50 enableGrouping: true, 51 enableMetrics: true, 52 enableColours: true, 53 enableDebug: false, 54 includePassed: false, 55 includeIgnored: false, 56 fsRoot: "", 57 baseDir: ".", 58 relative: true, 59 writer: os.Stdout, 60 outputOverride: outputSARIF, 61 linksOverride: func(result scan.Result) []string { 62 return result.Rule().Links 63 }, 64 } 65 } 66 67 func (b *Base) Path(result scan.Result, metadata types.Metadata) string { 68 if b.relative { 69 return result.RelativePathTo(b.fsRoot, b.baseDir, metadata) 70 } 71 return result.AbsolutePath(b.fsRoot, metadata) 72 } 73 74 func (b *Base) IncludePassed() bool { 75 return b.includePassed 76 } 77 78 func (b *Base) IncludeIgnored() bool { 79 return b.includeIgnored 80 } 81 82 func (b *Base) Writer() io.Writer { 83 return b.writer 84 } 85 86 func (b *Base) DebugEnabled() bool { 87 return b.enableDebug 88 } 89 90 func (b *Base) GetLinks(result scan.Result) []string { 91 return b.linksOverride(result) 92 } 93 94 func (b *Base) BaseDir() string { 95 return b.baseDir 96 } 97 98 func (b *Base) Output(results scan.Results) error { 99 if !b.enableColours { 100 tml.DisableFormatting() 101 } 102 return b.outputOverride(b, results) 103 } 104 105 func key(result scan.Result) string { 106 var severityInt int 107 switch result.Severity() { 108 case severity.Critical: 109 severityInt = 1 110 case severity.High: 111 severityInt = 2 112 case severity.Medium: 113 severityInt = 3 114 case severity.Low: 115 severityInt = 4 116 } 117 return fmt.Sprintf("%d:%s:%s:%d", severityInt, result.Range(), result.Rule().AVDID, result.Status()) 118 } 119 120 func (b *Base) GroupResults(results []scan.Result) ([]GroupedResult, error) { 121 122 // sort by key first 123 sort.Slice(results, func(i, j int) bool { 124 return key(results[i]) < key(results[j]) 125 }) 126 127 var output []GroupedResult 128 var lastKey string 129 var group GroupedResult 130 for i, result := range results { 131 currentKey := key(result) 132 if !b.enableGrouping || lastKey != currentKey { 133 if group.Len() > 0 { 134 output = append(output, group) 135 } 136 group = GroupedResult{} 137 } 138 if err := group.Add(i+1, result); err != nil { 139 return nil, err 140 } 141 lastKey = currentKey 142 } 143 if group.Len() > 0 { 144 output = append(output, group) 145 } 146 147 return output, nil 148 } 149 150 type GroupedResult struct { 151 start int 152 end int 153 results []scan.Result 154 } 155 156 func (g *GroupedResult) Add(i int, res scan.Result) error { 157 if g.end > 0 { 158 if i != g.end+1 { 159 return fmt.Errorf("expecting result #%d, found #%d", g.end+1, i) 160 } 161 } 162 if g.start == 0 { 163 g.start = i 164 } 165 g.end = i 166 g.results = append(g.results, res) 167 return nil 168 } 169 170 func (g *GroupedResult) String() string { 171 if g.start == g.end { 172 return fmt.Sprintf("#%d", g.start) 173 } 174 return fmt.Sprintf("#%d-%d", g.start, g.end) 175 } 176 177 func (g *GroupedResult) Len() int { 178 return len(g.results) 179 } 180 181 func (g *GroupedResult) Results() []scan.Result { 182 return g.results 183 }