Skip to content

Commit

Permalink
Exit purge (#137)
Browse files Browse the repository at this point in the history
* remove os.Exit calls. instead, return error as needed. oh wait, CLF:
REMOVE OS.EXIT CALLS.
  • Loading branch information
bhamail authored Apr 24, 2020
1 parent 38b5485 commit 1b89a61
Show file tree
Hide file tree
Showing 17 changed files with 639 additions and 163 deletions.
55 changes: 40 additions & 15 deletions audit/csvformatter.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ import (
"encoding/csv"
"encoding/json"
"errors"
"github.com/sonatype-nexus-community/nancy/customerrors"
"strconv"

. "github.com/sirupsen/logrus"
"github.com/sonatype-nexus-community/nancy/customerrors"
"github.com/sonatype-nexus-community/nancy/types"
)

Expand Down Expand Up @@ -57,33 +57,56 @@ func (f *CsvFormatter) Format(entry *Entry) ([]byte, error) {
var buf bytes.Buffer
w := csv.NewWriter(&buf)

f.write(w, []string{"Summary"})
f.write(w, summaryHeader)
f.write(w, summaryRow)
var err error
if err = f.write(w, []string{"Summary"}); err != nil {
return nil, err
}
if err = f.write(w, summaryHeader); err != nil {
return nil, err
}
if err = f.write(w, summaryRow); err != nil {
return nil, err
}

if !*f.Quiet {
invalidCount := len(invalidEntries)
if invalidCount > 0 {
f.write(w, []string{""})
f.write(w, []string{"Invalid Package(s)"})
f.write(w, invalidHeader)
if err = f.write(w, []string{""}); err != nil {
return nil, err
}
if err = f.write(w, []string{"Invalid Package(s)"}); err != nil {
return nil, err
}
if err = f.write(w, invalidHeader); err != nil {
return nil, err
}
for i := 1; i <= invalidCount; i++ {
invalidEntry := invalidEntries[i-1]
f.write(w, []string{"[" + strconv.Itoa(i) + "/" + strconv.Itoa(invalidCount) + "]", invalidEntry.Coordinates, "Does not use SemVer"})
if err = f.write(w, []string{"[" + strconv.Itoa(i) + "/" + strconv.Itoa(invalidCount) + "]", invalidEntry.Coordinates, "Does not use SemVer"}); err != nil {
return nil, err
}
}
}
}

if !*f.Quiet || numVulnerable > 0 {
f.write(w, []string{""})
f.write(w, []string{"Audited Package(s)"})
f.write(w, auditedHeader)
if err = f.write(w, []string{""}); err != nil {
return nil, err
}
if err = f.write(w, []string{"Audited Package(s)"}); err != nil {
return nil, err
}
if err = f.write(w, auditedHeader); err != nil {
return nil, err
}
}
for i := 1; i <= len(auditedEntries); i++ {
auditEntry := auditedEntries[i-1]
if auditEntry.IsVulnerable() || !*f.Quiet {
jsonVulns, _ := json.Marshal(auditEntry.Vulnerabilities)
f.write(w, []string{"[" + strconv.Itoa(i) + "/" + strconv.Itoa(packageCount) + "]", auditEntry.Coordinates, strconv.FormatBool(auditEntry.IsVulnerable()), strconv.Itoa(len(auditEntry.Vulnerabilities)), string(jsonVulns)})
if err = f.write(w, []string{"[" + strconv.Itoa(i) + "/" + strconv.Itoa(packageCount) + "]", auditEntry.Coordinates, strconv.FormatBool(auditEntry.IsVulnerable()), strconv.Itoa(len(auditEntry.Vulnerabilities)), string(jsonVulns)}); err != nil {
return nil, err
}
}
}

Expand All @@ -96,7 +119,9 @@ func (f *CsvFormatter) Format(entry *Entry) ([]byte, error) {

}

func (f *CsvFormatter) write(w *csv.Writer, line []string) {
err := w.Write(line)
customerrors.Check(err, "Failed to write data to csv")
func (f *CsvFormatter) write(w *csv.Writer, line []string) error {
if err := w.Write(line); err != nil {
return customerrors.NewErrorExitPrintHelp(err, "Failed to write data to csv")
}
return nil
}
20 changes: 20 additions & 0 deletions audit/csvformatter_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,23 @@ func TestCsvOutputWhenNotAuditLog(t *testing.T) {
assert.NotNil(t, e)
assert.Equal(t, errors.New("fields passed did not match the expected values for an audit log. You should probably look at setting the formatter to something else"), e)
}

func TestCsvFormatter_FormatNoError(t *testing.T) {
quiet := true
formatter := CsvFormatter{Quiet: &quiet}

data := map[string]interface{}{
"audited": []types.Coordinate{
{Coordinates: "auditedCoordinates"},
},
"invalid": []types.Coordinate{
{Coordinates: "invalidCoordinates"},
},
"num_audited": 0,
"num_vulnerable": 0,
"version": "theBuildVersion",
}
buf, err := formatter.Format(&Entry{Data: data})
assert.NoError(t, err)
assert.Equal(t, "Summary\nAudited Count,Vulnerable Count,Build Version\n0,0,theBuildVersion\n", string(buf))
}
6 changes: 2 additions & 4 deletions configuration/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ var unixComments = regexp.MustCompile(`#.*$`)
var untilComment = regexp.MustCompile(`(until=)(.*)`)

func ParseIQ(args []string) (config IqConfiguration, err error) {
iqCommand := flag.NewFlagSet("iq", flag.ExitOnError)
iqCommand := flag.NewFlagSet("iq", flag.ContinueOnError)
iqCommand.BoolVar(&config.Info, "v", false, "Set log level to Info")
iqCommand.BoolVar(&config.Debug, "vv", false, "Set log level to Debug")
iqCommand.BoolVar(&config.Trace, "vvv", false, "Set log level to Trace")
Expand All @@ -88,7 +88,6 @@ func ParseIQ(args []string) (config IqConfiguration, err error) {
Options:
`)
iqCommand.PrintDefaults()
os.Exit(2)
}

ConfigLocation = filepath.Join(HomeDir, types.IQServerDirName, types.IQServerConfigFileName)
Expand All @@ -103,7 +102,7 @@ Options:
return config, err
}

return config, nil
return config, err
}

func loadConfigFromFile(configLocation string, config *Configuration) error {
Expand Down Expand Up @@ -173,7 +172,6 @@ func Parse(args []string) (Configuration, error) {
Options:
`)
flag.PrintDefaults()
os.Exit(2)
}

ConfigLocation = filepath.Join(HomeDir, types.OssIndexDirName, types.OssIndexConfigFileName)
Expand Down
16 changes: 16 additions & 0 deletions configuration/parse_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -147,3 +147,19 @@ func setup() {
HomeDir = "/doesnt/exist"
flag.CommandLine = flag.NewFlagSet("", flag.ContinueOnError)
}

func TestParseUsage(t *testing.T) {
setup()
_, err := Parse([]string{""})
assert.NoError(t, err)
// should NOT call os.Exit
flag.Usage()
}

func TestParseIQUsage(t *testing.T) {
setup()
_, err := ParseIQ([]string{""})
assert.NoError(t, err)
// should NOT call os.Exit
flag.Usage()
}
5 changes: 3 additions & 2 deletions configuration/set.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,9 +72,10 @@ func GetConfigFromCommandLine(stdin io.Reader) (err error) {
ConfigLocation = filepath.Join(HomeDir, types.OssIndexDirName, types.OssIndexConfigFileName)
err = getAndSetOSSIndexConfig(reader)
case "":
// TODO should this return an error, because it means config setup was not completed?
return
default:
LogLady.Info("User chose to set OSS Index config, moving forward")
LogLady.Infof("User chose invalid config type: %s, will retry", str)
fmt.Println("Invalid value, 'iq' and 'ossindex' are accepted values, try again!")
err = GetConfigFromCommandLine(stdin)
}
Expand Down Expand Up @@ -179,7 +180,7 @@ func marshallAndWriteToDisk(config interface{}) (err error) {
}

LogLady.WithField("config_location", ConfigLocation).Info("Successfully wrote config to disk")
fmt.Printf("Successfully wrote config to: %s", ConfigLocation)
fmt.Printf("Successfully wrote config to: %s\n", ConfigLocation)
return
}

Expand Down
43 changes: 24 additions & 19 deletions customerrors/error.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,33 +18,38 @@ package customerrors

import (
"fmt"
"os"

"github.com/sonatype-nexus-community/nancy/buildversion"
. "github.com/sonatype-nexus-community/nancy/logger"
)

type SwError struct {
Message string
Err error
type ErrorExit struct {
Message string
Err error
ExitCode int
}

func (sw SwError) Error() string {
return fmt.Sprintf("%s - error: %s", sw.Message, sw.Err.Error())
func (ee ErrorExit) Error() string {
var errString string
if ee.Err != nil {
errString = ee.Err.Error()
} else {
errString = ""
}
return fmt.Sprintf("exit code: %d - %s - error: %s", ee.ExitCode, ee.Message, errString)
}

func (sw SwError) Exit() {
os.Exit(3)
}
func NewErrorExitPrintHelp(errCause error, message string) ErrorExit {
myErr := ErrorExit{message, errCause, 3}
LogLady.WithField("error", errCause).Error(message)
fmt.Println(myErr.Error())

func Check(err error, message string) {
if err != nil {
location, _ := LogFileLocation()
myErr := SwError{Message: message, Err: err}
LogLady.WithField("error", err).Error(message)
fmt.Println(myErr.Error())
fmt.Printf("For more information, check the log file at %s\n", location)
fmt.Println("nancy version:", buildversion.BuildVersion)
myErr.Exit()
var logFile string
var logFileErr error
if logFile, logFileErr = LogFileLocation(); logFileErr != nil {
logFile = "unknown"
}

fmt.Printf("For more information, check the log file at %s\n", logFile)
fmt.Println("nancy version:", buildversion.BuildVersion)
return myErr
}
43 changes: 43 additions & 0 deletions customerrors/error_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//
// Copyright 2018-present Sonatype Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

package customerrors

import (
"fmt"
"github.com/stretchr/testify/assert"
"testing"
)

func TestError(t *testing.T) {
assert.Equal(t, "exit code: 2 - MyMessage - error: MyError",
ErrorExit{Message: "MyMessage", Err: fmt.Errorf("MyError"), ExitCode: 2}.Error())
assert.Equal(t, "exit code: 0 - MyMessage - error: MyError",
ErrorExit{Message: "MyMessage", Err: fmt.Errorf("MyError")}.Error())
assert.Equal(t, "exit code: 0 - MyMessage - error: ",
ErrorExit{Message: "MyMessage"}.Error())
assert.Equal(t, "exit code: 0 - - error: ",
ErrorExit{}.Error())
}

func TestNewErrorExitPrintHelp(t *testing.T) {
assert.Equal(t, "exit code: 3 - MyMessage - error: MyError",
NewErrorExitPrintHelp(fmt.Errorf("MyError"), "MyMessage").Error())
assert.Equal(t, "exit code: 3 - MyMessage - error: ",
NewErrorExitPrintHelp(nil, "MyMessage").Error())
assert.Equal(t, "exit code: 3 - - error: ",
NewErrorExitPrintHelp(nil, "").Error())
}
7 changes: 5 additions & 2 deletions cyclonedx/cyclonedx.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ package cyclonedx

import (
"encoding/xml"
"github.com/sonatype-nexus-community/nancy/customerrors"

"github.com/package-url/packageurl-go"
"github.com/sonatype-nexus-community/nancy/customerrors"
. "github.com/sonatype-nexus-community/nancy/logger"
"github.com/sonatype-nexus-community/nancy/types"
)
Expand Down Expand Up @@ -91,7 +91,10 @@ func processPurlsIntoSBOMSchema1_1(results []types.Coordinate) string {
sbom := createSbomDocument()
for _, v := range results {
purl, err := packageurl.FromString(v.Coordinates)
customerrors.Check(err, "Error parsing purl from given coordinate")
if err != nil {
_ = customerrors.NewErrorExitPrintHelp(err, "Error parsing purl from given coordinate")
return ""
}

component := types.Component{
Type: "library",
Expand Down
37 changes: 33 additions & 4 deletions cyclonedx/cyclonedx_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,11 @@ import (
"github.com/package-url/packageurl-go"
"github.com/shopspring/decimal"
"github.com/sonatype-nexus-community/nancy/types"
assert "gopkg.in/go-playground/assert.v1"
"gopkg.in/go-playground/assert.v1"
)

func TestCreateSBOMFromPackageURLs(t *testing.T) {
results := []packageurl.PackageURL{}
var results []packageurl.PackageURL
uno, _ := packageurl.FromString("pkg:golang/github.com/test/test@1.0.0")
results = append(results, uno)

Expand Down Expand Up @@ -65,7 +65,7 @@ func TestCreateSBOMFromPackageURLs(t *testing.T) {
}

func TestCreateSBOMFromSHA1s(t *testing.T) {
results := []types.Sha1SBOM{}
var results []types.Sha1SBOM
uno := types.Sha1SBOM{Location: "/path/on/disk", Sha1: "c2843e01d9a2"}
results = append(results, uno)

Expand Down Expand Up @@ -104,7 +104,7 @@ func TestCreateSBOMFromSHA1s(t *testing.T) {
}

func TestProcessPurlsIntoSBOM(t *testing.T) {
results := []types.Coordinate{}
var results []types.Coordinate
crypto := types.Coordinate{
Coordinates: "pkg:golang/golang.org/x/crypto@v0.0.0-20190308221718-c2843e01d9a2",
Reference: "https://ossindex.sonatype.org/component/pkg:golang/golang.org/x/crypto@v0.0.0-20190308221718-c2843e01d9a2",
Expand Down Expand Up @@ -212,3 +212,32 @@ func assertBaseXMLValid(doc *etree.Element, t *testing.T) {
assert.Equal(t, doc.Attr[2].Key, "version")
assert.Equal(t, doc.Attr[2].Value, "1")
}

func TestProcess1_1NoError(t *testing.T) {
var results []types.Coordinate
sbom := processPurlsIntoSBOMSchema1_1(results)
assert.Equal(t, `<?xml version="1.0" encoding="UTF-8"?>
<bom xmlns="http://cyclonedx.org/schema/bom/1.1" xmlns:v="http://cyclonedx.org/schema/ext/vulnerability/1.0" version="1">
<components></components>
</bom>`, sbom)
}

func TestProcess1_1WithCoordinate(t *testing.T) {
results := []types.Coordinate{
{
Coordinates: "BADpkg:golang/golang.org/x/crypto@v0.0.0-20190308221718-c2843e01d9a2",
},
}

sbom := processPurlsIntoSBOMSchema1_1(results)
assert.Equal(t, "", sbom)
}

func TestProcessWithError(t *testing.T) {
var results []types.Coordinate
sbom := ProcessPurlsIntoSBOM(results)
assert.Equal(t, `<?xml version="1.0" encoding="UTF-8"?>
<bom xmlns="http://cyclonedx.org/schema/bom/1.1" xmlns:v="http://cyclonedx.org/schema/ext/vulnerability/1.0" version="1">
<components></components>
</bom>`, sbom)
}
Loading

0 comments on commit 1b89a61

Please sign in to comment.