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 helps engineering and security teams to identify potential issues in their open
source dependencies and evaluate them against organizational policies. 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) [![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 ## TL;DR

View File

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

2
go.mod
View File

@ -8,7 +8,7 @@ require (
github.com/google/cel-go v0.13.0 github.com/google/cel-go v0.13.0
github.com/google/osv-scanner v1.1.0 github.com/google/osv-scanner v1.1.0
github.com/jedib0t/go-pretty/v6 v6.4.4 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/sirupsen/logrus v1.9.0
github.com/spf13/cobra v1.6.1 github.com/spf13/cobra v1.6.1
github.com/stretchr/testify v1.8.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/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 h1:zbhTBT463mwcIuCq89GT8pFTU8UtGalBWFaa/wsgVXA=
github.com/safedep/dry v0.0.0-20230203134955-367834d99b1c/go.mod h1:yZ8R6kv4pR0yertVoxgBmnN4bvHT8TLubE7aahpWDDk= 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 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= 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 ( import (
"fmt" "fmt"
"time" "os"
"github.com/jedib0t/go-pretty/v6/progress"
) )
var progressWriter progress.Writer func PrintBanner(s string) {
var spinnerChan chan bool fmt.Fprintf(os.Stderr, s)
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))
}
} }

View File

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

View File

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

View File

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

View File

@ -26,12 +26,17 @@ const (
summaryWeightUnpopular = 2 summaryWeightUnpopular = 2
summaryWeightMajorDrift = 2 summaryWeightMajorDrift = 2
tagVuln = "vulnerabiity"
tagUnpopular = "low popularity"
tagDrift = "drift"
summaryReportMaxUpgradeAdvice = 5 summaryReportMaxUpgradeAdvice = 5
) )
type summaryReporterRemediationData struct { type summaryReporterRemediationData struct {
pkg *models.Package pkg *models.Package
score int score int
tags []string
} }
type summaryReporter struct { type summaryReporter struct {
@ -100,7 +105,7 @@ func (r *summaryReporter) processForVersionDrift(pkg *models.Package) {
driftType, _ := semver.Diff(version, latestVersion) driftType, _ := semver.Diff(version, latestVersion)
if driftType.IsMajor() { if driftType.IsMajor() {
r.summary.metrics.drifts += 1 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) { if (strings.EqualFold(projectType, "github")) && (starsCount < 10) {
r.summary.metrics.unpopular += 1 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 { switch risk {
case insightapi.PackageVulnerabilitySeveritiesRiskCRITICAL: case insightapi.PackageVulnerabilitySeveritiesRiskCRITICAL:
r.summary.vulns.critical += 1 r.summary.vulns.critical += 1
r.addPkgForRemediationAdvice(pkg, summaryWeightCriticalVuln) r.addPkgForRemediationAdvice(pkg, summaryWeightCriticalVuln, tagVuln)
break break
case insightapi.PackageVulnerabilitySeveritiesRiskHIGH: case insightapi.PackageVulnerabilitySeveritiesRiskHIGH:
r.summary.vulns.high += 1 r.summary.vulns.high += 1
r.addPkgForRemediationAdvice(pkg, summaryWeightHighVuln) r.addPkgForRemediationAdvice(pkg, summaryWeightHighVuln, tagVuln)
break break
case insightapi.PackageVulnerabilitySeveritiesRiskMEDIUM: case insightapi.PackageVulnerabilitySeveritiesRiskMEDIUM:
r.summary.vulns.medium += 1 r.summary.vulns.medium += 1
r.addPkgForRemediationAdvice(pkg, summaryWeightMediumVuln) r.addPkgForRemediationAdvice(pkg, summaryWeightMediumVuln, tagVuln)
break break
case insightapi.PackageVulnerabilitySeveritiesRiskLOW: case insightapi.PackageVulnerabilitySeveritiesRiskLOW:
r.summary.vulns.low += 1 r.summary.vulns.low += 1
r.addPkgForRemediationAdvice(pkg, summaryWeightLowVuln) r.addPkgForRemediationAdvice(pkg, summaryWeightLowVuln, tagVuln)
break 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 { if _, ok := r.remediationScores[pkg.Id()]; !ok {
r.remediationScores[pkg.Id()] = &summaryReporterRemediationData{ r.remediationScores[pkg.Id()] = &summaryReporterRemediationData{
pkg: pkg, pkg: pkg,
tags: []string{},
} }
} }
r.remediationScores[pkg.Id()].score += weight 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 { func (r *summaryReporter) Finish() error {
@ -181,11 +192,10 @@ func (r *summaryReporter) Finish() error {
fmt.Println() fmt.Println()
fmt.Println(text.Faint.Sprint(summaryListPrependText, r.manifestCountStatement())) fmt.Println(text.Faint.Sprint(summaryListPrependText, r.manifestCountStatement()))
fmt.Println() fmt.Println()
r.renderRemediationAdvice() r.renderRemediationAdvice()
fmt.Println() 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("Run with `vet --filter=\"...\"` for custom filters to identify risky libraries")
fmt.Println() fmt.Println()
fmt.Println("For more details", text.Bold.Sprint("https://github.com/safedep/vet")) 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.SetOutputMirror(os.Stdout)
tbl.SetStyle(table.StyleLight) 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 { for idx, sp := range sortedPackages {
if idx >= summaryReportMaxUpgradeAdvice { if idx >= summaryReportMaxUpgradeAdvice {
break break
@ -234,6 +244,17 @@ func (r *summaryReporter) renderRemediationAdvice() {
utils.SafelyGetValue(insight.PackageCurrentVersion), utils.SafelyGetValue(insight.PackageCurrentVersion),
sp.score, sp.score,
}) })
tagText := ""
for _, t := range sp.tags {
tagText += text.BgMagenta.Sprint(" "+t+" ") + " "
}
tbl.AppendRow(table.Row{
tagText, "", "",
})
tbl.AppendSeparator()
} }
tbl.Render() tbl.Render()
@ -255,7 +276,7 @@ func (r *summaryReporter) packageNameForRemediationAdvice(pkg *models.Package) s
} }
func (r *summaryReporter) vulnSummaryStatement() string { 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.critical, r.summary.vulns.high,
r.summary.vulns.medium+r.summary.vulns.low) r.summary.vulns.medium+r.summary.vulns.low)
} }