Skip to content

Commit f7d6cdc

Browse files
committed
Report for Golang errors
Right now if you use Gosec to scan invalid go file and if you report the result in a text, JSON, CSV or another file format you will always receive 0 issues. The reason for that is that Gosec can't parse the AST of invalid go files and thus will not report anything. The real problem here is that the user will never know about the issue if he generates the output in a file. I fix this problem in this patch and I added those Golang errors in the output. Signed-off-by: Martin Vrachev <[email protected]>
1 parent e2752bc commit f7d6cdc

File tree

5 files changed

+82
-17
lines changed

5 files changed

+82
-17
lines changed

analyzer.go

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ type Analyzer struct {
6464
logger *log.Logger
6565
issues []*Issue
6666
stats *Metrics
67+
errors map[string][]Error
6768
}
6869

6970
// NewAnalyzer builds a new analyzer.
@@ -83,6 +84,7 @@ func NewAnalyzer(conf Config, logger *log.Logger) *Analyzer {
8384
logger: logger,
8485
issues: make([]*Issue, 0, 16),
8586
stats: &Metrics{},
87+
errors: make(map[string][]Error),
8688
}
8789
}
8890

@@ -129,6 +131,27 @@ func (gosec *Analyzer) Process(buildTags []string, packagePaths ...string) error
129131
if err != nil {
130132
return err
131133
}
134+
for _, packageInfo := range builtPackage.AllPackages {
135+
if len(packageInfo.Errors) != 0 {
136+
for _, packErr := range packageInfo.Errors {
137+
// infoErr contains information about the error
138+
// at index 0 is the file path
139+
// at index 1 is the line; index 2 is for column
140+
// at index 3 is the actual error
141+
infoErr := strings.Split(packErr.Error(), ":")
142+
newErr := NewError(infoErr[1], infoErr[2], strings.TrimSpace(infoErr[3]))
143+
filePath := infoErr[0]
144+
145+
if errSlice, ok := gosec.errors[filePath]; ok {
146+
gosec.errors[filePath] = append(errSlice, *newErr)
147+
} else {
148+
errSlice = make([]Error, 0)
149+
gosec.errors[filePath] = append(errSlice, *newErr)
150+
}
151+
}
152+
}
153+
}
154+
sortErrors(gosec.errors) // sorts errors by line and column in the file
132155

133156
for _, pkg := range builtPackage.Created {
134157
gosec.logger.Println("Checking package:", pkg.String())
@@ -233,8 +256,8 @@ func (gosec *Analyzer) Visit(n ast.Node) ast.Visitor {
233256
}
234257

235258
// Report returns the current issues discovered and the metrics about the scan
236-
func (gosec *Analyzer) Report() ([]*Issue, *Metrics) {
237-
return gosec.issues, gosec.stats
259+
func (gosec *Analyzer) Report() ([]*Issue, *Metrics, map[string][]Error) {
260+
return gosec.issues, gosec.stats, gosec.errors
238261
}
239262

240263
// Reset clears state such as context, issues and metrics from the configured analyzer

analyzer_test.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ var _ = Describe("Analyzer", func() {
6868
pkg.Build()
6969
err := analyzer.Process(buildTags, pkg.Path)
7070
Expect(err).ShouldNot(HaveOccurred())
71-
_, metrics := analyzer.Report()
71+
_, metrics, _ := analyzer.Report()
7272
Expect(metrics.NumFiles).To(Equal(2))
7373
})
7474

@@ -90,7 +90,7 @@ var _ = Describe("Analyzer", func() {
9090
pkg2.Build()
9191
err := analyzer.Process(buildTags, pkg1.Path, pkg2.Path)
9292
Expect(err).ShouldNot(HaveOccurred())
93-
_, metrics := analyzer.Report()
93+
_, metrics, _ := analyzer.Report()
9494
Expect(metrics.NumFiles).To(Equal(2))
9595
})
9696

@@ -106,7 +106,7 @@ var _ = Describe("Analyzer", func() {
106106
controlPackage.AddFile("md5.go", source)
107107
controlPackage.Build()
108108
analyzer.Process(buildTags, controlPackage.Path)
109-
controlIssues, _ := analyzer.Report()
109+
controlIssues, _, _ := analyzer.Report()
110110
Expect(controlIssues).Should(HaveLen(sample.Errors))
111111

112112
})
@@ -124,7 +124,7 @@ var _ = Describe("Analyzer", func() {
124124
nosecPackage.Build()
125125

126126
analyzer.Process(buildTags, nosecPackage.Path)
127-
nosecIssues, _ := analyzer.Report()
127+
nosecIssues, _, _ := analyzer.Report()
128128
Expect(nosecIssues).Should(BeEmpty())
129129
})
130130

@@ -141,7 +141,7 @@ var _ = Describe("Analyzer", func() {
141141
nosecPackage.Build()
142142

143143
analyzer.Process(buildTags, nosecPackage.Path)
144-
nosecIssues, _ := analyzer.Report()
144+
nosecIssues, _, _ := analyzer.Report()
145145
Expect(nosecIssues).Should(BeEmpty())
146146
})
147147

@@ -158,7 +158,7 @@ var _ = Describe("Analyzer", func() {
158158
nosecPackage.Build()
159159

160160
analyzer.Process(buildTags, nosecPackage.Path)
161-
nosecIssues, _ := analyzer.Report()
161+
nosecIssues, _, _ := analyzer.Report()
162162
Expect(nosecIssues).Should(HaveLen(sample.Errors))
163163
})
164164

@@ -175,7 +175,7 @@ var _ = Describe("Analyzer", func() {
175175
nosecPackage.Build()
176176

177177
analyzer.Process(buildTags, nosecPackage.Path)
178-
nosecIssues, _ := analyzer.Report()
178+
nosecIssues, _, _ := analyzer.Report()
179179
Expect(nosecIssues).Should(BeEmpty())
180180
})
181181

@@ -212,7 +212,7 @@ var _ = Describe("Analyzer", func() {
212212
nosecPackage.Build()
213213

214214
customAnalyzer.Process(buildTags, nosecPackage.Path)
215-
nosecIssues, _ := customAnalyzer.Report()
215+
nosecIssues, _, _ := customAnalyzer.Report()
216216
Expect(nosecIssues).Should(HaveLen(sample.Errors))
217217

218218
})

cmd/gosec/main.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -165,19 +165,19 @@ func loadRules(include, exclude string) rules.RuleList {
165165
return rules.Generate(filters...)
166166
}
167167

168-
func saveOutput(filename, format string, issues []*gosec.Issue, metrics *gosec.Metrics) error {
168+
func saveOutput(filename, format string, issues []*gosec.Issue, metrics *gosec.Metrics, errors map[string][]gosec.Error) error {
169169
if filename != "" {
170170
outfile, err := os.Create(filename)
171171
if err != nil {
172172
return err
173173
}
174174
defer outfile.Close()
175-
err = output.CreateReport(outfile, format, issues, metrics)
175+
err = output.CreateReport(outfile, format, issues, metrics, errors)
176176
if err != nil {
177177
return err
178178
}
179179
} else {
180-
err := output.CreateReport(os.Stdout, format, issues, metrics)
180+
err := output.CreateReport(os.Stdout, format, issues, metrics, errors)
181181
if err != nil {
182182
return err
183183
}
@@ -323,7 +323,7 @@ func main() {
323323
}
324324

325325
// Collect the results
326-
issues, metrics := analyzer.Report()
326+
issues, metrics, errors := analyzer.Report()
327327

328328
// Sort the issue by severity
329329
if *flagSortIssues {
@@ -344,15 +344,15 @@ func main() {
344344
}
345345

346346
// Create output report
347-
if err := saveOutput(*flagOutput, *flagFormat, issues, metrics); err != nil {
347+
if err := saveOutput(*flagOutput, *flagFormat, issues, metrics, errors); err != nil {
348348
logger.Fatal(err)
349349
}
350350

351351
// Finalize logging
352352
logWriter.Close() // #nosec
353353

354354
// Do we have an issue? If so exit 1 unless NoFail is set
355-
if issuesFound && !*flagNoFail {
355+
if issuesFound && !*flagNoFail || len(errors) != 0 {
356356
os.Exit(1)
357357
}
358358
}

errors.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package gosec
2+
3+
import (
4+
"sort"
5+
)
6+
7+
// Error is used when there are golang errors while parsing the AST
8+
type Error struct {
9+
Line string `json:"line"`
10+
Column string `json:"column"`
11+
Err string `json:"error"`
12+
}
13+
14+
// NewError creates Error object
15+
func NewError(line, column, err string) *Error {
16+
return &Error{
17+
Line: line,
18+
Column: column,
19+
Err: err,
20+
}
21+
}
22+
23+
// sortErros sorts the golang erros by line
24+
func sortErrors(allErrors map[string][]Error) {
25+
26+
for _, errors := range allErrors {
27+
sort.Slice(errors, func(i, j int) bool {
28+
if errors[i].Line == errors[j].Line {
29+
return errors[i].Column <= errors[j].Column
30+
}
31+
return errors[i].Line < errors[j].Line
32+
})
33+
}
34+
}

output/formatter.go

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,12 @@ const (
4444
)
4545

4646
var text = `Results:
47+
{{range $filePath,$fileErrors := .Errors}}
48+
Golang errors in file: [{{ $filePath }}]:
49+
{{range $index, $error := $fileErrors}}
50+
> [line{{$error.Line}} : column{{$error.Column}}] - {{$error.Err}}
51+
{{end}}
52+
{{end}}
4753
{{ range $index, $issue := .Issues }}
4854
[{{ $issue.File }}:{{ $issue.Line }}] - {{ $issue.RuleID }}: {{ $issue.What }} (Confidence: {{ $issue.Confidence}}, Severity: {{ $issue.Severity }})
4955
> {{ $issue.Code }}
@@ -58,14 +64,16 @@ Summary:
5864
`
5965

6066
type reportInfo struct {
67+
Errors map[string][]gosec.Error `json:"Golang errors"`
6168
Issues []*gosec.Issue
6269
Stats *gosec.Metrics
6370
}
6471

6572
// CreateReport generates a report based for the supplied issues and metrics given
6673
// the specified format. The formats currently accepted are: json, csv, html and text.
67-
func CreateReport(w io.Writer, format string, issues []*gosec.Issue, metrics *gosec.Metrics) error {
74+
func CreateReport(w io.Writer, format string, issues []*gosec.Issue, metrics *gosec.Metrics, errors map[string][]gosec.Error) error {
6875
data := &reportInfo{
76+
Errors: errors,
6977
Issues: issues,
7078
Stats: metrics,
7179
}

0 commit comments

Comments
 (0)