Skip to content

Commit

Permalink
Exclude them vulns (#20)
Browse files Browse the repository at this point in the history
🐶
  • Loading branch information
DarthHater authored Jul 23, 2020
1 parent 22acddd commit a2a57f3
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 11 deletions.
38 changes: 38 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,44 @@ Flags:
--user string Specify your OSS Index Username
```

#### Exclude vulnerabilities

Sometimes you'll run into a dependency that after taking a look at, you either aren't affected by, or cannot resolve for some reason. Ahab understands, and will let you
exclude these vulnerabilities so you can get back to a passing build:

Vulnerabilities excluded will then be silenced and not show up in the output or fail your build.

We support exclusion of vulnerability either by CVE-ID (ex: `CVE-2018-20303`) or via the OSS Index ID (ex: `a8c20c84-1f6a-472a-ba1b-3eaedb2a2a14`) as not all vulnerabilities have a CVE-ID.

##### Via CLI flag
* `./ahab --exclude-vulnerability CVE-789,bcb0c38d-0d35-44ee-b7a7-8f77183d1ae2`
* `./ahab --exclude-vulnerability CVE-789,bcb0c38d-0d35-44ee-b7a7-8f77183d1ae2`

##### Via file
By default if a file named `.ahab-ignore` exists in the same directory that ahab is run it will use it, will no other options need to be passed.

If you would like to define the path to the file you can use the following
* `./ahab --exclude-vulnerability-file=/path/to/your/exclude-file`
* `./ahab --exclude-vulnerability-file=/path/to/your/exclude-file`

The file format requires each vulnerability that you want to exclude to be on a separate line. Comments are allowed in the file as well to help provide context when needed. See an example file below.

```
# This vulnerability is coming from package xyz, we are ok with this for now
CVN-111
CVN-123 # Mitigated the risk of this since we only use one method in this package and the affected code doesn't matter
CVN-543
```

It's also possible to define expiring ignores. Meaning that if you define a date on a vulnerability ignore until that date it will be ignored and once that
date is passed it will now be reported by ahab if its still an issue. Format to add an expiring ignore looks as follows. They can also be followed up by comments
to provide context to as why its been ignored until that date.

```
CVN-111 until=2021-01-01
CVN-543 until=2018-02-12 #Waiting on release from third party. Should be out before this date but gives us a little time to fix it.
```

#### Nexus IQ Server Usage

```
Expand Down
8 changes: 7 additions & 1 deletion audit/audit.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,13 @@ func init() {
four, _ = decimal.NewFromString("4")
}

func LogResults(noColor bool, loud bool, output string, projects []types.Coordinate) (vulnerableCount int, results string, err error) {
// LogResults will given a number of expected results and the results themselves, log the
// results.
func LogResults(noColor bool, loud bool, output string, projects []types.Coordinate, exclusions []string) (vulnerableCount int, results string, err error) {
for _, c := range projects {
c.ExcludeVulnerabilities(exclusions)
}

switch output {
case "json":
vulnerableCount, results, err = outputJSON(loud, projects)
Expand Down
99 changes: 89 additions & 10 deletions cmd/chase.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import (
"bufio"
"fmt"
"os"
"regexp"
"strings"
"time"

"github.com/common-nighthawk/go-figure"
Expand All @@ -34,16 +36,39 @@ import (
"github.com/spf13/cobra"
)

type CveListFlag struct {
Cves []string
}

func (cve *CveListFlag) String() string {
return fmt.Sprint(cve.Cves)
}

func (cve *CveListFlag) Set(value string) error {
if len(cve.Cves) > 0 {
return fmt.Errorf("The CVE Exclude Flag is already set")
}
cve.Cves = strings.Split(strings.ReplaceAll(value, " ", ""), ",")

return nil
}

func (cve *CveListFlag) Type() string { return "CveListFlag" }

var (
operating string
cleanCache bool
ossIndexUser string
ossIndexToken string
output string
loud bool
quiet bool
noColor bool
ossi *ossindex.Server
operating string
cleanCache bool
ossIndexUser string
ossIndexToken string
output string
loud bool
quiet bool
noColor bool
excludeVulnerabilityFilePath string
cveList CveListFlag
unixComments = regexp.MustCompile(`#.*$`)
untilComment = regexp.MustCompile(`(until=)(.*)`)
ossi *ossindex.Server
)

func init() {
Expand All @@ -53,10 +78,12 @@ func init() {
chaseCmd.PersistentFlags().StringVar(&ossIndexUser, "user", "", "Specify your OSS Index Username")
chaseCmd.PersistentFlags().StringVar(&ossIndexToken, "token", "", "Specify your OSS Index API Token")
chaseCmd.PersistentFlags().StringVar(&output, "output", "text", "Specify the output type you want (json, text, csv)")
chaseCmd.Flags().VarP(&cveList, "exclude-vulnerability", "e", "Comma separated list of CVEs to exclude")
chaseCmd.PersistentFlags().BoolVar(&loud, "loud", false, "Specify if you want non vulnerable packages included in your output")
chaseCmd.PersistentFlags().BoolVar(&quiet, "quiet", false, "Quiet removes the header from being printed")
chaseCmd.PersistentFlags().BoolVar(&noColor, "no-color", false, "Specify if you want no color in your results")
chaseCmd.PersistentFlags().CountVarP(&verbose, "", "v", "Set log level, higher is more verbose")
chaseCmd.Flags().StringVarP(&excludeVulnerabilityFilePath, "exclude-vulnerability-file", "x", "./.ahab-ignore", "Path to a file containing newline separated CVEs to be excluded")
}

var chaseCmd = &cobra.Command{
Expand Down Expand Up @@ -111,6 +138,8 @@ var chaseCmd = &cobra.Command{
return
}

err = getCVEExcludesFromFile(excludeVulnerabilityFilePath)

logLady.Trace("Attempting to audit list of strings from standard in")
pkgs, err := parseStdIn(&operating)
if err != nil {
Expand All @@ -129,7 +158,7 @@ var chaseCmd = &cobra.Command{
}

logLady.Trace("Attempting to output audited packages results")
count, results, err := audit.LogResults(noColor, loud, output, coordinates)
count, results, err := audit.LogResults(noColor, loud, output, coordinates, cveList.Cves)
if err != nil {
logLady.Error(err)
panic(err)
Expand Down Expand Up @@ -204,3 +233,53 @@ func printHeader() {
figure.NewFigure("By Sonatype & Friends", "pepper", true).Print()
fmt.Println("Ahab version: " + buildversion.BuildVersion)
}

func getCVEExcludesFromFile(excludeVulnerabilityFilePath string) error {
fi, err := os.Stat(excludeVulnerabilityFilePath)
if (fi != nil && fi.IsDir()) || (err != nil && os.IsNotExist(err)) {
return nil
}
file, err := os.Open(excludeVulnerabilityFilePath)
if err != nil {
return err
}
defer file.Close()

scanner := bufio.NewScanner(file)
for scanner.Scan() {
ogLine := scanner.Text()
err := determineIfLineIsExclusion(ogLine)
if err != nil {
return err
}
}

if err := scanner.Err(); err != nil {
return err
}

return nil
}

func determineIfLineIsExclusion(ogLine string) error {
line := unixComments.ReplaceAllString(ogLine, "")
until := untilComment.FindStringSubmatch(line)
line = untilComment.ReplaceAllString(line, "")
cveOnly := strings.TrimSpace(line)

if len(cveOnly) > 0 {
if until != nil {
parseDate, err := time.Parse("2006-01-02", strings.TrimSpace(until[2]))
if err != nil {
return fmt.Errorf("failed to parse until at line %q. Expected format is 'until=yyyy-MM-dd'", ogLine)
}
if parseDate.After(time.Now()) {
cveList.Cves = append(cveList.Cves, cveOnly)
}
} else {
cveList.Cves = append(cveList.Cves, cveOnly)
}
}

return nil
}

0 comments on commit a2a57f3

Please sign in to comment.