diff --git a/analyzer.go b/analyzer.go index 76f3e7f..830d338 100644 --- a/analyzer.go +++ b/analyzer.go @@ -377,8 +377,8 @@ func (gosec *Analyzer) CheckAnalyzers(pkg *packages.Package) { continue } if result != nil { - if aissue, ok := result.(*analyzers.Issue); ok { - gosec.updateIssues(toGosecIssue(aissue), false, []issue.SuppressionInfo{}) + if aissue, ok := result.(*issue.Issue); ok { + gosec.updateIssues(aissue, false, []issue.SuppressionInfo{}) } } } @@ -596,18 +596,6 @@ func (gosec *Analyzer) updateIssues(issue *issue.Issue, ignored bool, suppressio } } -func toGosecIssue(aissue *analyzers.Issue) *issue.Issue { - return &issue.Issue{ - File: aissue.File, - Line: aissue.Line, - Col: aissue.Col, - RuleID: aissue.AnalyzerID, - What: aissue.What, - Confidence: issue.Score(aissue.Confidence), - Severity: issue.Score(aissue.Severity), - } -} - // Report returns the current issues discovered and the metrics about the scan func (gosec *Analyzer) Report() ([]*issue.Issue, *Metrics, map[string][]Error) { return gosec.issues, gosec.stats, gosec.errors diff --git a/analyzers/ssrf.go b/analyzers/ssrf.go index 909a5b0..a5a935f 100644 --- a/analyzers/ssrf.go +++ b/analyzers/ssrf.go @@ -15,6 +15,7 @@ package analyzers import ( + "github.com/securego/gosec/v2/issue" "golang.org/x/tools/go/analysis" "golang.org/x/tools/go/analysis/passes/buildssa" "golang.org/x/tools/go/ssa" @@ -45,7 +46,7 @@ func runSSRF(pass *analysis.Pass) (interface{}, error) { ssaResult.Logger.Printf("callee: %s\n", callee) return newIssue(pass.Analyzer.Name, "not implemeted", - pass.Fset, instr.Call.Pos(), Low, High), nil + pass.Fset, instr.Call.Pos(), issue.Low, issue.High), nil } } } diff --git a/analyzers/util.go b/analyzers/util.go index 6989f44..3312e73 100644 --- a/analyzers/util.go +++ b/analyzers/util.go @@ -18,8 +18,10 @@ import ( "fmt" "go/token" "log" + "os" "strconv" + "github.com/securego/gosec/v2/issue" "golang.org/x/tools/go/analysis" "golang.org/x/tools/go/analysis/passes/buildssa" ) @@ -32,32 +34,6 @@ type SSAAnalyzerResult struct { SSA *buildssa.SSA } -// Score type used by severity and confidence values -// TODO: remove this duplicated type -type Score int - -const ( - // Low severity or confidence - Low Score = iota - // Medium severity or confidence - Medium - // High severity or confidence - High -) - -// Issue is returned by a gosec rule if it discovers an issue with the scanned code. -// TODO: remove this duplicated type -type Issue struct { - Severity Score `json:"severity"` // issue severity (how problematic it is) - Confidence Score `json:"confidence"` // issue confidence (how sure we are we found it) - AnalyzerID string `json:"analyzer_id"` // Human readable explanation - What string `json:"details"` // Human readable explanation - File string `json:"file"` // File name we found it in - Code string `json:"code"` // Impacted code line - Line string `json:"line"` // Line number in file - Col string `json:"column"` // Column number in line -} - // BuildDefaultAnalyzers returns the default list of analyzers func BuildDefaultAnalyzers() []*analysis.Analyzer { return []*analysis.Analyzer{ @@ -78,18 +54,43 @@ func getSSAResult(pass *analysis.Pass) (*SSAAnalyzerResult, error) { return ssaResult, nil } -func newIssue(analyzerID string, desc string, fileSet *token.FileSet, pos token.Pos, severity Score, confidence Score) *Issue { +// newIssue creates a new gosec issue +func newIssue(analyzerID string, desc string, fileSet *token.FileSet, + pos token.Pos, severity, confidence issue.Score) *issue.Issue { file := fileSet.File(pos) line := file.Line(pos) col := file.Position(pos).Column - // TODO: extract the code snippet and map the CWE - return &Issue{ + + return &issue.Issue{ + RuleID: analyzerID, File: file.Name(), Line: strconv.Itoa(line), Col: strconv.Itoa(col), Severity: severity, Confidence: confidence, - AnalyzerID: analyzerID, What: desc, + Cwe: issue.GetCweByRule(analyzerID), + Code: issueCodeSnippet(fileSet, pos), } } + +func issueCodeSnippet(fileSet *token.FileSet, pos token.Pos) string { + file := fileSet.File(pos) + + start := (int64)(file.Line(pos)) + if start-issue.SnippetOffset > 0 { + start = start - issue.SnippetOffset + } + end := (int64)(file.Line(pos)) + end = end + issue.SnippetOffset + + var code string + if file, err := os.Open(file.Name()); err == nil { + defer file.Close() // #nosec + code, err = issue.CodeSnippet(file, start, end) + if err != nil { + return err.Error() + } + } + return code +} diff --git a/issue/issue.go b/issue/issue.go index 75d2a7d..731f42f 100644 --- a/issue/issue.go +++ b/issue/issue.go @@ -144,11 +144,8 @@ func (c Score) String() string { return "UNDEFINED" } -// codeSnippet extracts a code snippet based on the ast reference -func codeSnippet(file *os.File, start int64, end int64, n ast.Node) (string, error) { - if n == nil { - return "", fmt.Errorf("invalid AST node provided") - } +// CodeSnippet extracts a code snippet based on the ast reference +func CodeSnippet(file *os.File, start int64, end int64) (string, error) { var pos int64 var buf bytes.Buffer scanner := bufio.NewScanner(file) @@ -189,11 +186,14 @@ func New(fobj *token.File, node ast.Node, ruleID, desc string, severity, confide col := strconv.Itoa(fobj.Position(node.Pos()).Column) var code string - if file, err := os.Open(fobj.Name()); err == nil { + if node == nil { + code = "invalid AST node provided" + } + if file, err := os.Open(fobj.Name()); err == nil && node != nil { defer file.Close() // #nosec s := codeSnippetStartLine(node, fobj) e := codeSnippetEndLine(node, fobj) - code, err = codeSnippet(file, s, e, node) + code, err = CodeSnippet(file, s, e) if err != nil { code = err.Error() }