goscan/cmd/root.go

185 lines
4.6 KiB
Go
Raw Normal View History

/*
Package cmd
Copyright © 2024 Shane C. <shane@scaffoe.com>
*/
package cmd
import (
"fmt"
"github.com/go-git/go-git/v5"
"github.com/kr/pretty"
"github.com/nao1215/markdown"
"github.com/owenrumney/go-sarif/sarif"
2024-09-06 15:03:37 +01:00
"github.com/sethvargo/go-githubactions"
"log"
"os"
"strconv"
"strings"
"github.com/spf13/cobra"
)
var isAction bool
var isGithub bool
// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
Use: "goscan",
Short: "A brief description of your application",
Run: func(cmd *cobra.Command, args []string) {
2024-09-06 15:03:37 +01:00
action := githubactions.New()
cwd, err := os.Getwd()
if err != nil {
log.Fatalln(err)
}
repo, err := git.PlainOpen(cwd)
if err != nil {
log.Fatalln(err)
}
ref, err := repo.Head()
if err != nil {
log.Fatalln(err)
}
pretty.Logln(ref.Name().Short())
pretty.Logln(ref.Hash().String())
report, err := sarif.Open("output.sarif")
if err != nil {
log.Fatal(err)
}
if len(report.Runs) != 1 {
log.Fatalf("expected 1 result, got %d", len(report.Runs))
}
run := report.Runs[0]
var rows [][]string
sevCountMap := map[string]int{
"high": 0,
"medium": 0,
"low": 0,
}
for _, result := range run.Results {
rule, err := run.GetRuleById(*result.RuleID)
if err != nil {
log.Fatal(err)
}
if len(result.Locations) != 1 {
log.Fatalf("expected 1 result, got %d", len(result.Locations))
}
location := result.Locations[0]
severity := rule.Properties["tags"].([]interface{})[1].(string)
confidence := rule.Properties["precision"].(string)
var severityEmoji string
switch strings.ToLower(severity) {
case "low":
sevCountMap["low"] = sevCountMap["low"] + 1
severityEmoji = "🟨"
case "medium":
sevCountMap["medium"] = sevCountMap["medium"] + 1
severityEmoji = "🟧"
case "high":
sevCountMap["high"] = sevCountMap["high"] + 1
severityEmoji = "🟥"
}
var confidenceEmoji string
switch strings.ToLower(confidence) {
case "low":
confidenceEmoji = "🟧"
case "medium":
confidenceEmoji = "🟨"
case "high":
confidenceEmoji = "🟩"
}
var linkToFile strings.Builder
if isGithub {
linkToFile.WriteString("./blob/commit/")
} else {
linkToFile.WriteString("./src/commit/")
}
linkToFile.WriteString(ref.Hash().String() + "/" + *location.PhysicalLocation.ArtifactLocation.URI + "#L" + strconv.Itoa(*location.PhysicalLocation.Region.StartLine))
rows = append(rows, []string{
fmt.Sprintf("`%s` - %s", *result.RuleID, *rule.Name),
fmt.Sprintf("%s **%c**", severityEmoji, severity[0]),
fmt.Sprintf("%s **%s**", confidenceEmoji, strings.ToUpper(string(confidence[0]))),
fmt.Sprintf("[%s:%d](%s)", *location.PhysicalLocation.ArtifactLocation.URI, *location.PhysicalLocation.Region.StartLine, linkToFile.String()),
})
}
2024-09-06 15:03:37 +01:00
var markdownOutput strings.Builder
markdownHandler := markdown.NewMarkdown(&markdownOutput)
markdownHandler.H1("GoSec Results:")
markdownHandler.PlainText("<details>")
markdownHandler.PlainText("<summary>Results:</summary>\n")
if len(rows) == 0 {
markdownHandler.PlainText("**Nothing Found! 🥳**")
} else {
if sevCountMap["high"] != 0 {
markdownHandler.PlainText(fmt.Sprintf("🟥 **%d** high severity issues\n", sevCountMap["high"]))
}
if sevCountMap["medium"] != 0 {
markdownHandler.PlainText(fmt.Sprintf("🟧 **%d** medium severity issues\n", sevCountMap["medium"]))
}
if sevCountMap["low"] != 0 {
markdownHandler.PlainText(fmt.Sprintf("🟨 **%d** low severity issues\n", sevCountMap["low"]))
}
markdownHandler.PlainTextf("Total of **%d** issues.\n", sevCountMap["high"]+sevCountMap["medium"]+sevCountMap["low"])
}
if len(rows) != 0 {
markdownHandler.CustomTable(markdown.TableSet{
Header: []string{"Rule", "Severity", "Confidence", "Location"},
Rows: rows,
}, markdown.TableOptions{
AutoWrapText: false,
AutoFormatHeaders: false,
})
}
markdownHandler.PlainText("</details>")
err = markdownHandler.Build()
if err != nil {
log.Fatalln(err)
}
2024-09-06 15:03:37 +01:00
action.AddStepSummary(markdownOutput.String())
},
}
// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
err := rootCmd.Execute()
if err != nil {
os.Exit(1)
}
}
func init() {
rootCmd.Flags().BoolVar(&isAction, "is-action", false, "If set, will run some things specific to git actions")
rootCmd.Flags().BoolVar(&isGithub, "is-github", false, "If set, will change some outputs to support github")
}