2021-05-06 08:31:51 +01:00
|
|
|
package text
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bufio"
|
|
|
|
"bytes"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"text/template"
|
2021-05-20 09:16:42 +01:00
|
|
|
|
|
|
|
"github.com/gookit/color"
|
|
|
|
"github.com/securego/gosec/v2"
|
2021-05-06 08:31:51 +01:00
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
errorTheme = color.New(color.FgLightWhite, color.BgRed)
|
|
|
|
warningTheme = color.New(color.FgBlack, color.BgYellow)
|
|
|
|
defaultTheme = color.New(color.FgWhite, color.BgBlack)
|
|
|
|
)
|
|
|
|
|
2021-05-31 09:44:12 +01:00
|
|
|
// WriteReport write a (colorized) report in text format
|
2021-05-20 09:16:42 +01:00
|
|
|
func WriteReport(w io.Writer, data *gosec.ReportInfo, enableColor bool) error {
|
2021-05-06 08:31:51 +01:00
|
|
|
t, e := template.
|
|
|
|
New("gosec").
|
|
|
|
Funcs(plainTextFuncMap(enableColor)).
|
|
|
|
Parse(templateContent)
|
|
|
|
if e != nil {
|
|
|
|
return e
|
|
|
|
}
|
|
|
|
|
|
|
|
return t.Execute(w, data)
|
|
|
|
}
|
|
|
|
|
|
|
|
func plainTextFuncMap(enableColor bool) template.FuncMap {
|
|
|
|
if enableColor {
|
|
|
|
return template.FuncMap{
|
|
|
|
"highlight": highlight,
|
|
|
|
"danger": color.Danger.Render,
|
|
|
|
"notice": color.Notice.Render,
|
|
|
|
"success": color.Success.Render,
|
|
|
|
"printCode": printCodeSnippet,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// by default those functions return the given content untouched
|
|
|
|
return template.FuncMap{
|
2021-08-18 12:00:38 +01:00
|
|
|
"highlight": func(t string, s gosec.Score, ignored bool) string {
|
2021-05-06 08:31:51 +01:00
|
|
|
return t
|
|
|
|
},
|
|
|
|
"danger": fmt.Sprint,
|
|
|
|
"notice": fmt.Sprint,
|
|
|
|
"success": fmt.Sprint,
|
|
|
|
"printCode": printCodeSnippet,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// highlight returns content t colored based on Score
|
2021-08-18 12:00:38 +01:00
|
|
|
func highlight(t string, s gosec.Score, ignored bool) string {
|
|
|
|
if ignored {
|
|
|
|
return defaultTheme.Sprint(t)
|
|
|
|
}
|
2021-05-06 08:31:51 +01:00
|
|
|
switch s {
|
|
|
|
case gosec.High:
|
|
|
|
return errorTheme.Sprint(t)
|
|
|
|
case gosec.Medium:
|
|
|
|
return warningTheme.Sprint(t)
|
|
|
|
default:
|
|
|
|
return defaultTheme.Sprint(t)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// printCodeSnippet prints the code snippet from the issue by adding a marker to the affected line
|
|
|
|
func printCodeSnippet(issue *gosec.Issue) string {
|
|
|
|
start, end := parseLine(issue.Line)
|
|
|
|
scanner := bufio.NewScanner(strings.NewReader(issue.Code))
|
|
|
|
var buf bytes.Buffer
|
|
|
|
line := start
|
|
|
|
for scanner.Scan() {
|
|
|
|
codeLine := scanner.Text()
|
|
|
|
if strings.HasPrefix(codeLine, strconv.Itoa(line)) && line <= end {
|
|
|
|
codeLine = " > " + codeLine + "\n"
|
|
|
|
line++
|
|
|
|
} else {
|
|
|
|
codeLine = " " + codeLine + "\n"
|
|
|
|
}
|
|
|
|
buf.WriteString(codeLine)
|
|
|
|
}
|
|
|
|
return buf.String()
|
|
|
|
}
|
|
|
|
|
|
|
|
// parseLine extract the start and the end line numbers from a issue line
|
|
|
|
func parseLine(line string) (int, int) {
|
|
|
|
parts := strings.Split(line, "-")
|
|
|
|
start := parts[0]
|
|
|
|
end := start
|
|
|
|
if len(parts) > 1 {
|
|
|
|
end = parts[1]
|
|
|
|
}
|
|
|
|
s, err := strconv.Atoi(start)
|
|
|
|
if err != nil {
|
|
|
|
return -1, -1
|
|
|
|
}
|
|
|
|
e, err := strconv.Atoi(end)
|
|
|
|
if err != nil {
|
|
|
|
return -1, -1
|
|
|
|
}
|
|
|
|
return s, e
|
|
|
|
}
|