Refactor the analyzer to process one package at the time

This avoids loading all packages in memory before running the checks.

Signed-off-by: Cosmin Cojocar <cosmin.cojocar@gmx.ch>
This commit is contained in:
Cosmin Cojocar 2019-04-27 10:01:27 +02:00 committed by Cosmin Cojocar
parent adcfe94257
commit 4dfaf0a997
2 changed files with 88 additions and 74 deletions

View file

@ -100,71 +100,59 @@ func (gosec *Analyzer) LoadRules(ruleDefinitions map[string]RuleBuilder) {
// Process kicks off the analysis process for a given package // Process kicks off the analysis process for a given package
func (gosec *Analyzer) Process(buildTags []string, packagePaths ...string) error { func (gosec *Analyzer) Process(buildTags []string, packagePaths ...string) error {
ctx := build.Default config := gosec.pkgConfig(buildTags)
ctx.BuildTags = append(ctx.BuildTags, buildTags...) for _, pkgPath := range packagePaths {
conf := &packages.Config{ pkgs, err := gosec.load(pkgPath, config)
Mode: packages.LoadSyntax,
Tests: true,
}
pkgs := []*packages.Package{}
for _, packagePath := range packagePaths {
abspath, err := GetPkgAbsPath(packagePath)
if err != nil {
gosec.logger.Printf("Skipping: %s. Path doesn't exist.", abspath)
continue
}
gosec.logger.Println("Import directory:", abspath)
basePackage, err := build.Default.ImportDir(packagePath, build.ImportComment)
if err != nil { if err != nil {
return err return err
} }
for _, pkg := range pkgs {
err := gosec.parseErrors(pkg)
if err != nil {
return err
}
gosec.check(pkg)
}
}
sortErrors(gosec.errors)
return nil
}
func (gosec *Analyzer) pkgConfig(buildTags []string) *packages.Config {
tagsFlag := "-tags=" + strings.Join(buildTags, " ")
return &packages.Config{
Mode: packages.LoadSyntax,
BuildFlags: []string{tagsFlag},
Tests: true,
}
}
func (gosec *Analyzer) load(pkgPath string, conf *packages.Config) ([]*packages.Package, error) {
abspath, err := GetPkgAbsPath(pkgPath)
if err != nil {
gosec.logger.Printf("Skipping: %s. Path doesn't exist.", abspath)
return []*packages.Package{}, nil
}
gosec.logger.Println("Import directory:", abspath)
basePackage, err := build.Default.ImportDir(pkgPath, build.ImportComment)
if err != nil {
return []*packages.Package{}, err
}
var packageFiles []string var packageFiles []string
for _, filename := range basePackage.GoFiles { for _, filename := range basePackage.GoFiles {
packageFiles = append(packageFiles, path.Join(packagePath, filename)) packageFiles = append(packageFiles, path.Join(pkgPath, filename))
} }
_pkgs, err := packages.Load(conf, packageFiles...) pkgs, err := packages.Load(conf, packageFiles...)
if err != nil { if err != nil {
return err return []*packages.Package{}, err
}
pkgs = append(pkgs, _pkgs...)
} }
return pkgs, nil
}
for _, packageInfo := range pkgs { func (gosec *Analyzer) check(pkg *packages.Package) {
if len(packageInfo.Errors) != 0 {
for _, packErr := range packageInfo.Errors {
// infoErr contains information about the error
// at index 0 is the file path
// at index 1 is the line; index 2 is for column
// at index 3 is the actual error
infoErr := strings.Split(packErr.Error(), ":")
filePath := infoErr[0]
line, err := strconv.Atoi(infoErr[1])
if err != nil {
return err
}
column, err := strconv.Atoi(infoErr[2])
if err != nil {
return err
}
newErr := NewError(line, column, strings.TrimSpace(infoErr[3]))
if errSlice, ok := gosec.errors[filePath]; ok {
gosec.errors[filePath] = append(errSlice, *newErr)
} else {
errSlice = make([]Error, 0)
gosec.errors[filePath] = append(errSlice, *newErr)
}
}
}
}
sortErrors(gosec.errors) // sorts errors by line and column in the file
for _, pkg := range pkgs {
gosec.logger.Println("Checking package:", pkg.Name) gosec.logger.Println("Checking package:", pkg.Name)
for _, file := range pkg.Syntax { for _, file := range pkg.Syntax {
gosec.logger.Println("Checking file:", pkg.Fset.File(file.Pos()).Name()) gosec.logger.Println("Checking file:", pkg.Fset.File(file.Pos()).Name())
@ -181,8 +169,35 @@ func (gosec *Analyzer) Process(buildTags []string, packagePaths ...string) error
gosec.stats.NumFiles++ gosec.stats.NumFiles++
gosec.stats.NumLines += pkg.Fset.File(file.Pos()).LineCount() gosec.stats.NumLines += pkg.Fset.File(file.Pos()).LineCount()
} }
} }
func (gosec *Analyzer) parseErrors(pkg *packages.Package) error {
if len(pkg.Errors) == 0 {
return nil
}
for _, pkgErr := range pkg.Errors {
// infoErr contains information about the error
// at index 0 is the file path
// at index 1 is the line; index 2 is for column
// at index 3 is the actual error
infoErr := strings.Split(pkgErr.Error(), ":")
filePath := infoErr[0]
line, err := strconv.Atoi(infoErr[1])
if err != nil {
return err
}
column, err := strconv.Atoi(infoErr[2])
if err != nil {
return err
}
newErr := NewError(line, column, strings.TrimSpace(infoErr[3]))
if errSlice, ok := gosec.errors[filePath]; ok {
gosec.errors[filePath] = append(errSlice, *newErr)
} else {
errSlice = make([]Error, 0)
gosec.errors[filePath] = append(errSlice, *newErr)
}
}
return nil return nil
} }

View file

@ -219,7 +219,6 @@ var _ = Describe("Analyzer", func() {
}) })
It("should be possible to overwrite nosec comments, and report issues", func() { It("should be possible to overwrite nosec comments, and report issues", func() {
// Rule for MD5 weak crypto usage // Rule for MD5 weak crypto usage
sample := testutils.SampleCodeG401[0] sample := testutils.SampleCodeG401[0]
source := sample.Code[0] source := sample.Code[0]