mirror of
https://github.com/anchore/syft.git
synced 2026-10-11 21:57:22 +02:00
Compare commits
4
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b00c492b0b | ||
|
|
34f9e6fec9 | ||
|
|
552cf45224 | ||
|
|
1af07a4775 |
@@ -0,0 +1,6 @@
|
||||
version: v1.57.0
|
||||
destination: ./.tool/
|
||||
plugins:
|
||||
- module: 'github.com/anchore/syft'
|
||||
import: 'github.com/anchore/syft/test/linters/ensuredefer'
|
||||
path: .
|
||||
@@ -14,6 +14,7 @@ linters:
|
||||
- bodyclose
|
||||
- dogsled
|
||||
- dupl
|
||||
- ensuredefer
|
||||
- errcheck
|
||||
- exportloopref
|
||||
- funlen
|
||||
@@ -52,6 +53,13 @@ linters-settings:
|
||||
gocritic:
|
||||
enabled-checks:
|
||||
- deferInLoop
|
||||
custom:
|
||||
ensuredefer:
|
||||
type: "module"
|
||||
description: Ensure that all important closers are called with defer
|
||||
settings:
|
||||
symbols:
|
||||
- github.com/anchore/syft/internal.CloseAndLogError
|
||||
output:
|
||||
uniq-by-line: false
|
||||
run:
|
||||
|
||||
+12
-5
@@ -128,12 +128,19 @@ tasks:
|
||||
- "{{ .TOOL_DIR }}/gosimports -local github.com/anchore -w ."
|
||||
- go mod tidy
|
||||
|
||||
lint-fix:
|
||||
desc: Auto-format all source code + run golangci lint fixers
|
||||
custom-linter:
|
||||
desc: Compile a version of golangci-lint that includes our plugins
|
||||
deps: [tools]
|
||||
cmds:
|
||||
# build custom linter executable
|
||||
- "{{ .TOOL_DIR }}/golangci-lint custom -v"
|
||||
|
||||
lint-fix:
|
||||
desc: Auto-format all source code + run golangci lint fixers
|
||||
deps: [tools, custom-linter]
|
||||
cmds:
|
||||
- task: format
|
||||
- "{{ .TOOL_DIR }}/golangci-lint run --tests=false --fix"
|
||||
- "{{ .TOOL_DIR }}/custom-gcl run --tests=false --fix"
|
||||
|
||||
lint:
|
||||
desc: Run gofmt + golangci lint checks
|
||||
@@ -142,7 +149,7 @@ tasks:
|
||||
sh: gofmt -l -s .
|
||||
BAD_FILE_NAMES:
|
||||
sh: "find . | grep -e ':' || true"
|
||||
deps: [tools]
|
||||
deps: [tools, custom-linter]
|
||||
cmds:
|
||||
# ensure there are no go fmt differences
|
||||
- cmd: 'test -z "{{ .BAD_FMT_FILES }}" || (echo "files with gofmt issues: [{{ .BAD_FMT_FILES }}]"; exit 1)'
|
||||
@@ -151,7 +158,7 @@ tasks:
|
||||
- cmd: 'test -z "{{ .BAD_FILE_NAMES }}" || (echo "files with bad names: [{{ .BAD_FILE_NAMES }}]"; exit 1)'
|
||||
silent: true
|
||||
# run linting
|
||||
- "{{ .TOOL_DIR }}/golangci-lint run --tests=false"
|
||||
- "{{ .TOOL_DIR }}/custom-gcl run --tests=false"
|
||||
|
||||
|
||||
check-licenses:
|
||||
|
||||
@@ -83,6 +83,11 @@ require (
|
||||
modernc.org/sqlite v1.29.8
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/golangci/plugin-module-register v0.1.1
|
||||
golang.org/x/tools v0.19.0
|
||||
)
|
||||
|
||||
require (
|
||||
dario.cat/mergo v1.0.0 // indirect
|
||||
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect
|
||||
@@ -224,7 +229,6 @@ require (
|
||||
golang.org/x/sys v0.19.0 // indirect
|
||||
golang.org/x/term v0.19.0 // indirect
|
||||
golang.org/x/text v0.14.0 // indirect
|
||||
golang.org/x/tools v0.19.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||
google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f // indirect
|
||||
|
||||
@@ -357,6 +357,8 @@ github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEW
|
||||
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golangci/plugin-module-register v0.1.1 h1:TCmesur25LnyJkpsVrupv1Cdzo+2f7zX0H6Jkw1Ol6c=
|
||||
github.com/golangci/plugin-module-register v0.1.1/go.mod h1:TTpqoB6KkwOJMV8u7+NyXMrkwwESJLOkfl9TxR1DGFc=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
package ensuredefer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"strings"
|
||||
|
||||
"github.com/golangci/plugin-module-register/register"
|
||||
"golang.org/x/tools/go/analysis"
|
||||
"golang.org/x/tools/go/analysis/passes/inspect"
|
||||
"golang.org/x/tools/go/ast/inspector"
|
||||
)
|
||||
|
||||
func init() {
|
||||
register.Plugin("ensuredefer", func(conf any) (register.LinterPlugin, error) {
|
||||
m, ok := conf.(map[string]any)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected map[string]any but got %+v (%T)", conf, conf)
|
||||
}
|
||||
|
||||
var toCheck []mustDeferSymbol
|
||||
symbols := m["symbols"]
|
||||
if symbols != nil {
|
||||
s, ok := symbols.([]any)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected slice but got %v (%T)", symbols, symbols)
|
||||
}
|
||||
for _, maybeSym := range s {
|
||||
sym, ok := maybeSym.(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expect slice of string but found %v (%T)", maybeSym, maybeSym)
|
||||
}
|
||||
|
||||
splitAt := strings.LastIndex(sym, ".")
|
||||
if splitAt == -1 {
|
||||
return nil, fmt.Errorf("symbols must have form example.com/some/package.SomeMethod, but got %s", sym)
|
||||
}
|
||||
toCheck = append(toCheck, mustDeferSymbol{
|
||||
pkg: sym[:splitAt],
|
||||
funName: sym[splitAt+1:],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return &analyzerPlugin{
|
||||
symbols: toCheck,
|
||||
}, nil
|
||||
})
|
||||
}
|
||||
|
||||
func makeRun(toCheck map[string]mustDeferSymbol) func(pass *analysis.Pass) (any, error) {
|
||||
return func(pass *analysis.Pass) (any, error) {
|
||||
insp := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector)
|
||||
nodeFilter := []ast.Node{
|
||||
(*ast.ExprStmt)(nil),
|
||||
}
|
||||
insp.Preorder(nodeFilter, func(node ast.Node) {
|
||||
// if we have a *ast.ExprStmt that calls internal.CloseAndLogError, report a problem.
|
||||
// (if the function is correctly called in a defer statement, the statement will have type
|
||||
// *ast.DeferStmt.)
|
||||
if t, ok := node.(*ast.ExprStmt); ok {
|
||||
if !isExprStmtAllowed(t, pass, toCheck) {
|
||||
pass.Reportf(t.Pos(), "internal.CloseAndLogError must be called in defer")
|
||||
}
|
||||
}
|
||||
})
|
||||
return nil, nil
|
||||
}
|
||||
}
|
||||
|
||||
func isExprStmtAllowed(e *ast.ExprStmt, pass *analysis.Pass, toCheck map[string]mustDeferSymbol) bool {
|
||||
call, ok := e.X.(*ast.CallExpr)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
sel, ok := call.Fun.(*ast.SelectorExpr)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
candidate, ok := toCheck[sel.Sel.Name]
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
|
||||
obj := pass.TypesInfo.Uses[sel.Sel]
|
||||
if obj == nil {
|
||||
return true
|
||||
}
|
||||
pkg := obj.Pkg()
|
||||
if pkg == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
if pkg.Path() == candidate.pkg && sel.Sel.Name == candidate.funName {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
type mustDeferSymbol struct {
|
||||
pkg string
|
||||
funName string
|
||||
}
|
||||
|
||||
type analyzerPlugin struct {
|
||||
symbols []mustDeferSymbol
|
||||
}
|
||||
|
||||
func (p *analyzerPlugin) BuildAnalyzers() ([]*analysis.Analyzer, error) {
|
||||
analyzer := &analysis.Analyzer{
|
||||
Name: "ensuredefer",
|
||||
Doc: "enforce that specified functions are called in defer statements",
|
||||
Requires: []*analysis.Analyzer{inspect.Analyzer},
|
||||
}
|
||||
|
||||
callsToCheck := make(map[string]mustDeferSymbol)
|
||||
for _, s := range p.symbols {
|
||||
callsToCheck[s.funName] = s
|
||||
}
|
||||
analyzer.Run = makeRun(callsToCheck)
|
||||
|
||||
return []*analysis.Analyzer{
|
||||
analyzer,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (p *analyzerPlugin) GetLoadMode() string {
|
||||
return register.LoadModeSyntax
|
||||
}
|
||||
Reference in New Issue
Block a user