github.com/elek/golangci-lint@v1.42.2-0.20211208090441-c05b7fcb3a9a/pkg/golinters/gosec.go (about)

     1  package golinters
     2  
     3  import (
     4  	"fmt"
     5  	"go/token"
     6  	"io/ioutil"
     7  	"log"
     8  	"strconv"
     9  	"strings"
    10  	"sync"
    11  
    12  	"github.com/securego/gosec/v2"
    13  	"github.com/securego/gosec/v2/rules"
    14  	"golang.org/x/tools/go/analysis"
    15  	"golang.org/x/tools/go/packages"
    16  
    17  	"github.com/elek/golangci-lint/pkg/config"
    18  	"github.com/elek/golangci-lint/pkg/golinters/goanalysis"
    19  	"github.com/elek/golangci-lint/pkg/lint/linter"
    20  	"github.com/elek/golangci-lint/pkg/result"
    21  )
    22  
    23  const gosecName = "gosec"
    24  
    25  func NewGosec(settings *config.GoSecSettings) *goanalysis.Linter {
    26  	var mu sync.Mutex
    27  	var resIssues []goanalysis.Issue
    28  
    29  	gasConfig := gosec.NewConfig()
    30  
    31  	var filters []rules.RuleFilter
    32  	if settings != nil {
    33  		filters = gosecRuleFilters(settings.Includes, settings.Excludes)
    34  
    35  		for k, v := range settings.Config {
    36  			// Uses ToUpper because the parsing of the map's key change the key to lowercase.
    37  			// The value is not impacted by that: the case is respected.
    38  			gasConfig.Set(strings.ToUpper(k), v)
    39  		}
    40  	}
    41  
    42  	ruleDefinitions := rules.Generate(filters...)
    43  
    44  	logger := log.New(ioutil.Discard, "", 0)
    45  
    46  	analyzer := &analysis.Analyzer{
    47  		Name: gosecName,
    48  		Doc:  goanalysis.TheOnlyanalyzerDoc,
    49  	}
    50  	return goanalysis.NewLinter(
    51  		gosecName,
    52  		"Inspects source code for security problems",
    53  		[]*analysis.Analyzer{analyzer},
    54  		nil,
    55  	).WithContextSetter(func(lintCtx *linter.Context) {
    56  		analyzer.Run = func(pass *analysis.Pass) (interface{}, error) {
    57  			gosecAnalyzer := gosec.NewAnalyzer(gasConfig, true, logger)
    58  			gosecAnalyzer.LoadRules(ruleDefinitions.Builders())
    59  
    60  			pkg := &packages.Package{
    61  				Fset:      pass.Fset,
    62  				Syntax:    pass.Files,
    63  				Types:     pass.Pkg,
    64  				TypesInfo: pass.TypesInfo,
    65  			}
    66  			gosecAnalyzer.Check(pkg)
    67  			issues, _, _ := gosecAnalyzer.Report()
    68  			if len(issues) == 0 {
    69  				return nil, nil
    70  			}
    71  
    72  			res := make([]goanalysis.Issue, 0, len(issues))
    73  			for _, i := range issues {
    74  				text := fmt.Sprintf("%s: %s", i.RuleID, i.What) // TODO: use severity and confidence
    75  				var r *result.Range
    76  				line, err := strconv.Atoi(i.Line)
    77  				if err != nil {
    78  					r = &result.Range{}
    79  					if n, rerr := fmt.Sscanf(i.Line, "%d-%d", &r.From, &r.To); rerr != nil || n != 2 {
    80  						lintCtx.Log.Warnf("Can't convert gosec line number %q of %v to int: %s", i.Line, i, err)
    81  						continue
    82  					}
    83  					line = r.From
    84  				}
    85  
    86  				column, err := strconv.Atoi(i.Col)
    87  				if err != nil {
    88  					lintCtx.Log.Warnf("Can't convert gosec column number %q of %v to int: %s", i.Col, i, err)
    89  					continue
    90  				}
    91  
    92  				res = append(res, goanalysis.NewIssue(&result.Issue{
    93  					Pos: token.Position{
    94  						Filename: i.File,
    95  						Line:     line,
    96  						Column:   column,
    97  					},
    98  					Text:       text,
    99  					LineRange:  r,
   100  					FromLinter: gosecName,
   101  				}, pass))
   102  			}
   103  
   104  			mu.Lock()
   105  			resIssues = append(resIssues, res...)
   106  			mu.Unlock()
   107  
   108  			return nil, nil
   109  		}
   110  	}).WithIssuesReporter(func(*linter.Context) []goanalysis.Issue {
   111  		return resIssues
   112  	}).WithLoadMode(goanalysis.LoadModeTypesInfo)
   113  }
   114  
   115  // based on https://github.com/securego/gosec/blob/569328eade2ccbad4ce2d0f21ee158ab5356a5cf/cmd/gosec/main.go#L170-L188
   116  func gosecRuleFilters(includes, excludes []string) []rules.RuleFilter {
   117  	var filters []rules.RuleFilter
   118  
   119  	if len(includes) > 0 {
   120  		filters = append(filters, rules.NewRuleFilter(false, includes...))
   121  	}
   122  
   123  	if len(excludes) > 0 {
   124  		filters = append(filters, rules.NewRuleFilter(true, excludes...))
   125  	}
   126  
   127  	return filters
   128  }