diff --git a/cmd/scan/curl.go b/cmd/scan/curl.go index c243a4d..1455891 100644 --- a/cmd/scan/curl.go +++ b/cmd/scan/curl.go @@ -3,9 +3,13 @@ package scan import ( "log" "net/http" + "os" "strings" "github.com/cerberauth/vulnapi/scan" + "github.com/common-nighthawk/go-figure" + "github.com/fatih/color" + "github.com/olekukonko/tablewriter" "github.com/spf13/cobra" ) @@ -24,6 +28,9 @@ func NewCURLScanCmd() (scanCmd *cobra.Command) { FParseErrWhitelist: cobra.FParseErrWhitelist{ UnknownFlags: true, }, + PreRun: func(cmd *cobra.Command, args []string) { + figure.NewColorFigure("VulnAPI", "", "cyan", true).Print() + }, Run: func(cmd *cobra.Command, args []string) { url = args[0] @@ -47,20 +54,52 @@ func NewCURLScanCmd() (scanCmd *cobra.Command) { log.Fatal(err) } - rpr, _, err := scan.WithAllVulnsScans().WithAllBestPracticesScans().Execute() + reporter, _, err := scan.WithAllVulnsScans().WithAllBestPracticesScans().Execute() if err != nil { log.Fatal(err) } - for _, r := range rpr.GetVulnerabilityReports() { - log.Println(r) + var outputColor *color.Color + var outputMessage string + var outputStream *os.File + if !reporter.HasVulnerability() { + outputColor = color.New(color.FgGreen) + outputMessage = "Congratulations! No issues were found." + outputStream = os.Stdout + } else if reporter.HasHighRiskSeverityVulnerability() { + outputColor = color.New(color.FgRed) + outputMessage = "Warning: Critical vulnerabilities detected!" + outputStream = os.Stderr + } else { + outputColor = color.New(color.FgYellow) + outputMessage = "Advice: There are some low-risk issues. It's advised to take a look." + outputStream = os.Stderr } - if !rpr.HasVulnerability() { - log.Println("Congratulations! No vulnerability has been discovered!") - } else { - log.Fatalln("There is one or more vulnerabilies you should know.") + table := tablewriter.NewWriter(outputStream) + table.SetHeader([]string{"Risk Level", "Vulnerability", "Description"}) + + for _, v := range reporter.GetVulnerabilityReports() { + var lineColor int + if v.IsLowRiskSeverity() { + lineColor = tablewriter.BgBlueColor + } else if v.IsMediumRiskSeverity() { + lineColor = tablewriter.FgYellowColor + } else if v.IsHighRiskSeverity() { + lineColor = tablewriter.FgRedColor + } + + table.Rich( + []string{v.SeverityLevelString(), v.Name, v.Description}, + []tablewriter.Colors{ + {tablewriter.Bold, lineColor}, + {tablewriter.Normal, lineColor}, + {tablewriter.Normal, tablewriter.FgWhiteColor}}, + ) } + + table.Render() + outputColor.Fprintln(outputStream, outputMessage) }, } diff --git a/go.mod b/go.mod index 49ddf53..5484865 100644 --- a/go.mod +++ b/go.mod @@ -4,10 +4,12 @@ go 1.22 require ( github.com/brianvoe/gofakeit/v6 v6.28.0 - github.com/brianvoe/gofakeit/v7 v7.0.1 + github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be + github.com/fatih/color v1.16.0 github.com/getkin/kin-openapi v0.120.0 github.com/golang-jwt/jwt/v5 v5.2.0 github.com/jarcoal/httpmock v1.3.1 + github.com/olekukonko/tablewriter v0.0.5 github.com/spf13/cobra v1.8.0 github.com/std-uritemplate/std-uritemplate/go v0.0.52 github.com/stretchr/testify v1.8.4 @@ -21,9 +23,13 @@ require ( github.com/invopop/yaml v0.2.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/mailru/easyjson v0.7.7 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-runewidth v0.0.9 // indirect github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/perimeterx/marshmallow v1.1.5 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/spf13/pflag v1.0.5 // indirect + golang.org/x/sys v0.14.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index bf1fa84..f838fe5 100644 --- a/go.sum +++ b/go.sum @@ -1,11 +1,14 @@ github.com/brianvoe/gofakeit/v6 v6.28.0 h1:Xib46XXuQfmlLS2EXRuJpqcw8St6qSZz75OUo0tgAW4= github.com/brianvoe/gofakeit/v6 v6.28.0/go.mod h1:Xj58BMSnFqcn/fAQeSK+/PLtC5kSb7FJIq4JyGa8vEs= -github.com/brianvoe/gofakeit/v7 v7.0.1/go.mod h1:QXuPeBw164PJCzCUZVmgpgHJ3Llj49jSLVkKPMtxtxA= +github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be h1:J5BL2kskAlV9ckgEsNQXscjIaLiOYiZ75d4e94E6dcQ= +github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be/go.mod h1:mk5IQ+Y0ZeO87b858TlA645sVcEcbiX6YqP98kt+7+w= github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/getkin/kin-openapi v0.120.0 h1:MqJcNJFrMDFNc07iwE8iFC5eT2k/NPUFDIpNeiZv8Jg= github.com/getkin/kin-openapi v0.120.0/go.mod h1:PCWw/lfBrJY4HcdqE3jj+QFkaFK8ABoqo7PvqVhXXqw= github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= @@ -33,10 +36,19 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= +github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/maxatome/go-testdeep v1.12.0 h1:Ql7Go8Tg0C1D/uMMX59LAoYK7LffeJQ6X2T04nTH68g= github.com/maxatome/go-testdeep v1.12.0/go.mod h1:lPZc/HAcJMP92l7yI6TRz1aZN5URwUBUAfUNvrclaNM= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s= github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -58,6 +70,10 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/ugorji/go/codec v1.2.7 h1:YPXUKf7fYbp/y8xloBqZOw2qaVggbfwMlI8WM3wZUJ0= github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= +golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/report/vuln.go b/report/vuln.go index d9b047a..e8f0ac0 100644 --- a/report/vuln.go +++ b/report/vuln.go @@ -1,6 +1,8 @@ package report -import "fmt" +import ( + "fmt" +) type VulnerabilityReport struct { SeverityLevel float64 // https://nvd.nist.gov/vuln-metrics/cvss @@ -22,19 +24,19 @@ func (vr *VulnerabilityReport) IsHighRiskSeverity() bool { } func (vr *VulnerabilityReport) String() string { - return fmt.Sprintf("[%s][%s] %s: %s", severyLevelString(vr.SeverityLevel), vr.Name, vr.Url, vr.Description) + return fmt.Sprintf("[%s][%s] %s: %s", vr.SeverityLevelString(), vr.Name, vr.Url, vr.Description) } -func severyLevelString(severityLevel float64) string { - if severityLevel >= 9 { - return "critical" - } else if severityLevel < 9 && severityLevel >= 7 { - return "high" - } else if severityLevel < 7 && severityLevel >= 4 { - return "medium" - } else if severityLevel < 4 && severityLevel >= 0.1 { - return "low" +func (vr *VulnerabilityReport) SeverityLevelString() string { + if vr.SeverityLevel >= 9 { + return "Critical" + } else if vr.SeverityLevel < 9 && vr.SeverityLevel >= 7 { + return "High" + } else if vr.SeverityLevel < 7 && vr.SeverityLevel >= 4 { + return "Medium" + } else if vr.SeverityLevel < 4 && vr.SeverityLevel >= 0.1 { + return "Low" } else { - return "none" + return "None" } } diff --git a/report/vuln_test.go b/report/vuln_test.go index 8443b25..6972a41 100644 --- a/report/vuln_test.go +++ b/report/vuln_test.go @@ -29,6 +29,29 @@ func TestVulnerabilityReport_String(t *testing.T) { Description: "This is a test vulnerability", Url: "https://example.com/vulnerability", } - expected := "[high][Test Vulnerability] https://example.com/vulnerability: This is a test vulnerability" + expected := "[High][Test Vulnerability] https://example.com/vulnerability: This is a test vulnerability" assert.Equal(t, expected, vr.String()) } +func TestVulnerabilityReport_SeverityLevelString(t *testing.T) { + vr := &report.VulnerabilityReport{} + + // Test case for severity level >= 9 + vr.SeverityLevel = 9.5 + assert.Equal(t, "Critical", vr.SeverityLevelString()) + + // Test case for severity level < 9 and >= 7 + vr.SeverityLevel = 7.5 + assert.Equal(t, "High", vr.SeverityLevelString()) + + // Test case for severity level < 7 and >= 4 + vr.SeverityLevel = 4.5 + assert.Equal(t, "Medium", vr.SeverityLevelString()) + + // Test case for severity level < 4 and >= 0.1 + vr.SeverityLevel = 0.5 + assert.Equal(t, "Low", vr.SeverityLevelString()) + + // Test case for severity level < 0.1 + vr.SeverityLevel = 0.05 + assert.Equal(t, "None", vr.SeverityLevelString()) +}