Merge pull request #19 from safedep/develop

Sync Develop with Main for Multiple Fixes
This commit is contained in:
Abhisek Datta 2023-02-16 17:29:06 +05:30 committed by GitHub
commit 5939b9df10
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 155 additions and 109 deletions

View File

@ -4,7 +4,10 @@
helps engineering and security teams to identify potential issues in their open
source dependencies and evaluate them against organizational policies.
[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/safedep/vet/badge)](https://api.securityscorecards.dev/projects/github.com/safedep/vet)
[![CodeQL](https://github.com/safedep/vet/actions/workflows/codeql.yml/badge.svg?branch=main)](https://github.com/safedep/vet/actions/workflows/codeql.yml)
[![Scorecard supply-chain security](https://github.com/safedep/vet/actions/workflows/scorecard.yml/badge.svg)](https://github.com/safedep/vet/actions/workflows/scorecard.yml)
## TL;DR

View File

@ -1,6 +1,7 @@
package main
import (
"errors"
"fmt"
"os"
"syscall"
@ -22,9 +23,7 @@ func newAuthCommand() *cobra.Command {
Use: "auth",
Short: "Configure and verify Insights API authentication",
RunE: func(cmd *cobra.Command, args []string) error {
fmt.Printf("You must choose an appropriate command: configure, verify\n")
os.Exit(1)
return nil
return errors.New("a valid sub-command is required")
},
}

2
go.mod
View File

@ -8,7 +8,7 @@ require (
github.com/google/cel-go v0.13.0
github.com/google/osv-scanner v1.1.0
github.com/jedib0t/go-pretty/v6 v6.4.4
github.com/safedep/dry v0.0.0-20230203134955-367834d99b1c
github.com/safedep/dry v0.0.0-20230216112435-385c68e56634
github.com/sirupsen/logrus v1.9.0
github.com/spf13/cobra v1.6.1
github.com/stretchr/testify v1.8.1

2
go.sum
View File

@ -45,6 +45,8 @@ github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJ
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/safedep/dry v0.0.0-20230203134955-367834d99b1c h1:zbhTBT463mwcIuCq89GT8pFTU8UtGalBWFaa/wsgVXA=
github.com/safedep/dry v0.0.0-20230203134955-367834d99b1c/go.mod h1:yZ8R6kv4pR0yertVoxgBmnN4bvHT8TLubE7aahpWDDk=
github.com/safedep/dry v0.0.0-20230216112435-385c68e56634 h1:JRIzwT2Xo7TFH2O1gMJpHS5Fn6jDJdF+/+2JdyhzI3A=
github.com/safedep/dry v0.0.0-20230216112435-385c68e56634/go.mod h1:yZ8R6kv4pR0yertVoxgBmnN4bvHT8TLubE7aahpWDDk=
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA=

66
internal/ui/progress.go Normal file
View File

@ -0,0 +1,66 @@
package ui
import (
"os"
"time"
"github.com/jedib0t/go-pretty/v6/progress"
)
var progressWriter progress.Writer
func StartProgressWriter() {
pw := progress.NewWriter()
pw.SetTrackerLength(25)
pw.SetMessageWidth(20)
pw.SetSortBy(progress.SortByPercentDsc)
pw.SetStyle(progress.StyleDefault)
pw.SetOutputWriter(os.Stderr)
pw.SetTrackerPosition(progress.PositionRight)
pw.SetUpdateFrequency(time.Millisecond * 100)
pw.Style().Colors = progress.StyleColorsExample
pw.Style().Options.PercentFormat = "%4.1f%%"
pw.Style().Visibility.Pinned = true
pw.Style().Visibility.ETA = true
pw.Style().Visibility.Value = true
progressWriter = pw
go progressWriter.Render()
}
func StopProgressWriter() {
if progressWriter != nil {
progressWriter.Stop()
time.Sleep(1 * time.Second)
}
}
func TrackProgress(message string, total int) any {
tracker := progress.Tracker{Message: message, Total: int64(total),
Units: progress.UnitsDefault}
if progressWriter != nil {
progressWriter.AppendTracker(&tracker)
}
return &tracker
}
func MarkTrackerAsDone(i any) {
if tracker, ok := i.(*progress.Tracker); ok {
tracker.MarkAsDone()
}
}
func IncrementTrackerTotal(i any, count int) {
if tracker, ok := i.(*progress.Tracker); ok {
tracker.UpdateTotal(tracker.Total + int64(count))
}
}
func IncrementProgress(i any, count int) {
if tracker, ok := i.(*progress.Tracker); ok {
tracker.Increment(int64(count))
}
}

39
internal/ui/spinner.go Normal file
View File

@ -0,0 +1,39 @@
package ui
import (
"fmt"
"time"
)
var spinnerChan chan bool
func StartSpinner(msg string) {
style := `⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏`
frames := []rune(style)
length := len(frames)
spinnerChan = make(chan bool)
ticker := time.NewTicker(100 * time.Millisecond)
go func() {
pos := 0
for {
select {
case <-spinnerChan:
ticker.Stop()
return
case <-ticker.C:
fmt.Printf("\r%s ... %s", msg, string(frames[pos%length]))
pos += 1
}
}
}()
}
func StopSpinner() {
spinnerChan <- true
fmt.Printf("\r")
fmt.Println()
}

View File

@ -2,96 +2,9 @@ package ui
import (
"fmt"
"time"
"github.com/jedib0t/go-pretty/v6/progress"
"os"
)
var progressWriter progress.Writer
var spinnerChan chan bool
func StartSpinner(msg string) {
style := `⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏`
frames := []rune(style)
length := len(frames)
spinnerChan = make(chan bool)
ticker := time.NewTicker(100 * time.Millisecond)
go func() {
pos := 0
for {
select {
case <-spinnerChan:
ticker.Stop()
return
case <-ticker.C:
fmt.Printf("\r%s ... %s", msg, string(frames[pos%length]))
pos += 1
}
}
}()
}
func StopSpinner() {
spinnerChan <- true
fmt.Printf("\r")
fmt.Println()
}
func StartProgressWriter() {
pw := progress.NewWriter()
pw.SetTrackerLength(25)
pw.SetMessageWidth(20)
pw.SetSortBy(progress.SortByPercentDsc)
pw.SetStyle(progress.StyleDefault)
pw.SetTrackerPosition(progress.PositionRight)
pw.SetUpdateFrequency(time.Millisecond * 100)
pw.Style().Colors = progress.StyleColorsExample
pw.Style().Options.PercentFormat = "%4.1f%%"
pw.Style().Visibility.Pinned = true
pw.Style().Visibility.ETA = true
pw.Style().Visibility.Value = true
progressWriter = pw
go progressWriter.Render()
}
func StopProgressWriter() {
if progressWriter != nil {
progressWriter.Stop()
time.Sleep(1 * time.Second)
}
}
func TrackProgress(message string, total int) any {
tracker := progress.Tracker{Message: message, Total: int64(total),
Units: progress.UnitsDefault}
if progressWriter != nil {
progressWriter.AppendTracker(&tracker)
}
return &tracker
}
func MarkTrackerAsDone(i any) {
if tracker, ok := i.(*progress.Tracker); ok {
tracker.MarkAsDone()
}
}
func IncrementTrackerTotal(i any, count int) {
if tracker, ok := i.(*progress.Tracker); ok {
tracker.UpdateTotal(tracker.Total + int64(count))
}
}
func IncrementProgress(i any, count int) {
if tracker, ok := i.(*progress.Tracker); ok {
tracker.Increment(int64(count))
}
func PrintBanner(s string) {
fmt.Fprintf(os.Stderr, s)
}

View File

@ -7,6 +7,7 @@ import (
"strconv"
"github.com/safedep/dry/utils"
"github.com/safedep/vet/internal/ui"
"github.com/safedep/vet/pkg/common/logger"
"github.com/spf13/cobra"
)
@ -68,7 +69,7 @@ func main() {
func printBanner() {
bRet, err := strconv.ParseBool(os.Getenv("VET_DISABLE_BANNER"))
if (err != nil) || (!bRet) {
fmt.Print(banner)
ui.PrintBanner(banner)
}
}

View File

@ -85,6 +85,8 @@ func (pw *parserWrapper) Ecosystem() string {
return models.EcosystemPub
case "requirements.txt":
return models.EcosystemPyPI
case "Pipfile.lock":
return models.EcosystemPyPI
case "yarn.lock":
return models.EcosystemNpm
case "gradle.lockfile":

View File

@ -8,7 +8,7 @@ import (
func TestListParser(t *testing.T) {
parsers := List()
assert.Equal(t, 9, len(parsers))
assert.Equal(t, 10, len(parsers))
}
func TestInvalidEcosystemMapping(t *testing.T) {

View File

@ -26,12 +26,17 @@ const (
summaryWeightUnpopular = 2
summaryWeightMajorDrift = 2
tagVuln = "vulnerabiity"
tagUnpopular = "low popularity"
tagDrift = "drift"
summaryReportMaxUpgradeAdvice = 5
)
type summaryReporterRemediationData struct {
pkg *models.Package
score int
tags []string
}
type summaryReporter struct {
@ -100,7 +105,7 @@ func (r *summaryReporter) processForVersionDrift(pkg *models.Package) {
driftType, _ := semver.Diff(version, latestVersion)
if driftType.IsMajor() {
r.summary.metrics.drifts += 1
r.addPkgForRemediationAdvice(pkg, summaryWeightMajorDrift)
r.addPkgForRemediationAdvice(pkg, summaryWeightMajorDrift, tagDrift)
}
}
@ -121,7 +126,7 @@ func (r *summaryReporter) processForPopularity(pkg *models.Package) {
if (strings.EqualFold(projectType, "github")) && (starsCount < 10) {
r.summary.metrics.unpopular += 1
r.addPkgForRemediationAdvice(pkg, summaryWeightUnpopular)
r.addPkgForRemediationAdvice(pkg, summaryWeightUnpopular, tagUnpopular)
}
}
}
@ -141,33 +146,39 @@ func (r *summaryReporter) processForVulns(pkg *models.Package) {
switch risk {
case insightapi.PackageVulnerabilitySeveritiesRiskCRITICAL:
r.summary.vulns.critical += 1
r.addPkgForRemediationAdvice(pkg, summaryWeightCriticalVuln)
r.addPkgForRemediationAdvice(pkg, summaryWeightCriticalVuln, tagVuln)
break
case insightapi.PackageVulnerabilitySeveritiesRiskHIGH:
r.summary.vulns.high += 1
r.addPkgForRemediationAdvice(pkg, summaryWeightHighVuln)
r.addPkgForRemediationAdvice(pkg, summaryWeightHighVuln, tagVuln)
break
case insightapi.PackageVulnerabilitySeveritiesRiskMEDIUM:
r.summary.vulns.medium += 1
r.addPkgForRemediationAdvice(pkg, summaryWeightMediumVuln)
r.addPkgForRemediationAdvice(pkg, summaryWeightMediumVuln, tagVuln)
break
case insightapi.PackageVulnerabilitySeveritiesRiskLOW:
r.summary.vulns.low += 1
r.addPkgForRemediationAdvice(pkg, summaryWeightLowVuln)
r.addPkgForRemediationAdvice(pkg, summaryWeightLowVuln, tagVuln)
break
}
}
}
}
func (r *summaryReporter) addPkgForRemediationAdvice(pkg *models.Package, weight int) {
func (r *summaryReporter) addPkgForRemediationAdvice(pkg *models.Package,
weight int, tag string) {
if _, ok := r.remediationScores[pkg.Id()]; !ok {
r.remediationScores[pkg.Id()] = &summaryReporterRemediationData{
pkg: pkg,
pkg: pkg,
tags: []string{},
}
}
r.remediationScores[pkg.Id()].score += weight
if utils.FindInSlice(r.remediationScores[pkg.Id()].tags, tag) == -1 {
r.remediationScores[pkg.Id()].tags = append(r.remediationScores[pkg.Id()].tags, tag)
}
}
func (r *summaryReporter) Finish() error {
@ -181,11 +192,10 @@ func (r *summaryReporter) Finish() error {
fmt.Println()
fmt.Println(text.Faint.Sprint(summaryListPrependText, r.manifestCountStatement()))
fmt.Println()
r.renderRemediationAdvice()
fmt.Println()
fmt.Println("Install as a security gate in CI for incremental scan and blocking risky dependencies")
fmt.Println("Run `vet ci` to generate CI scripts")
fmt.Println()
fmt.Println("Run with `vet --filter=\"...\"` for custom filters to identify risky libraries")
fmt.Println()
fmt.Println("For more details", text.Bold.Sprint("https://github.com/safedep/vet"))
@ -221,7 +231,7 @@ func (r *summaryReporter) renderRemediationAdvice() {
tbl.SetOutputMirror(os.Stdout)
tbl.SetStyle(table.StyleLight)
tbl.AppendHeader(table.Row{"Package", "Update To", "Risk Score"})
tbl.AppendHeader(table.Row{"Package", "Update To", "Impact"})
for idx, sp := range sortedPackages {
if idx >= summaryReportMaxUpgradeAdvice {
break
@ -234,6 +244,17 @@ func (r *summaryReporter) renderRemediationAdvice() {
utils.SafelyGetValue(insight.PackageCurrentVersion),
sp.score,
})
tagText := ""
for _, t := range sp.tags {
tagText += text.BgMagenta.Sprint(" "+t+" ") + " "
}
tbl.AppendRow(table.Row{
tagText, "", "",
})
tbl.AppendSeparator()
}
tbl.Render()
@ -255,7 +276,7 @@ func (r *summaryReporter) packageNameForRemediationAdvice(pkg *models.Package) s
}
func (r *summaryReporter) vulnSummaryStatement() string {
return fmt.Sprintf("%d critical, %d high and %d other vulnerabilities were identifier",
return fmt.Sprintf("%d critical, %d high and %d other vulnerabilities were identified",
r.summary.vulns.critical, r.summary.vulns.high,
r.summary.vulns.medium+r.summary.vulns.low)
}