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  }