github.com/khulnasoft-lab/defsec@v1.0.5-0.20230827010352-5e9f46893d95/internal/lint/adapter/analyzer.go (about) 1 package adapter 2 3 import ( 4 "fmt" 5 "go/ast" 6 "go/types" 7 "strings" 8 9 "golang.org/x/tools/go/analysis" 10 "golang.org/x/tools/go/analysis/passes/inspect" 11 "golang.org/x/tools/go/ast/inspector" 12 ) 13 14 const ( 15 defaultProviderPackage = "github.com/khulnasoft-lab/defsec/pkg/providers" 16 defaultTypesPackage = "github.com/khulnasoft-lab/defsec/pkg/types" 17 ) 18 19 func DefaultAnalyzer() *analysis.Analyzer { 20 return CreateAnalyzer(defaultProviderPackage, defaultTypesPackage) 21 } 22 23 func CreateAnalyzer(providerPackage, typesPackage string) *analysis.Analyzer { 24 return &analysis.Analyzer{ 25 Name: "adapter", 26 Doc: "reports provider struct initialisations with omitted 'types' fields", 27 Requires: []*analysis.Analyzer{inspect.Analyzer}, 28 Run: func(pass *analysis.Pass) (interface{}, error) { 29 30 if err := detectNamedFunctionReturns(pass, providerPackage); err != nil { 31 return nil, err 32 } 33 34 if err := detectEmptyStructInit(pass, providerPackage); err != nil { 35 return nil, err 36 } 37 38 if err := detectMissingTypesFields(pass, providerPackage, typesPackage); err != nil { 39 return nil, err 40 } 41 42 if err := detectMetadataLiterals(pass, typesPackage); err != nil { 43 return nil, err 44 } 45 46 return nil, nil 47 }, 48 } 49 } 50 51 func detectMetadataLiterals(pass *analysis.Pass, typesPackage string) error { 52 insp := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) 53 insp.Preorder([]ast.Node{ 54 (*ast.CompositeLit)(nil), 55 }, func(n ast.Node) { 56 57 if strings.HasSuffix(pass.Fset.File(n.Pos()).Name(), "_test.go") { 58 return 59 } 60 61 lit := n.(*ast.CompositeLit) 62 if lit.Type == nil { 63 return 64 } 65 66 tx := pass.TypesInfo.TypeOf(lit.Type) 67 if tx == nil { 68 return 69 } 70 71 if !strings.HasPrefix(tx.String(), typesPackage) { 72 return 73 } 74 75 named, ok := tx.(*types.Named) 76 if !ok { 77 return 78 } 79 80 if named.String() == fmt.Sprintf("%s.Metadata", typesPackage) { 81 pass.Reportf(lit.Pos(), "Metadata instances should not be initialised using literals") 82 } 83 }) 84 return nil 85 86 } 87 88 func detectNamedFunctionReturns(pass *analysis.Pass, providerPackage string) error { 89 for _, f := range pass.Files { 90 for _, d := range f.Decls { 91 switch fnc := d.(type) { 92 case *ast.FuncDecl: 93 if fnc.Type.Results == nil { 94 continue 95 } 96 for _, result := range fnc.Type.Results.List { 97 for _, name := range result.Names { 98 if field, ok := name.Obj.Decl.(*ast.Field); ok { 99 tx := pass.TypesInfo.TypeOf(field.Type) 100 if tx == nil { 101 continue 102 } 103 104 if !strings.HasPrefix(tx.String(), providerPackage) { 105 continue 106 } 107 108 if strings.HasSuffix(pass.Fset.File(field.Pos()).Name(), "_test.go") { 109 continue 110 } 111 112 pass.Reportf(field.Pos(), "Provider struct %s should not be initialised via a named function return type", tx.String()) 113 } 114 } 115 } 116 } 117 } 118 } 119 return nil 120 } 121 122 func detectEmptyStructInit(pass *analysis.Pass, providerPackage string) error { 123 insp := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) 124 insp.Preorder([]ast.Node{ 125 (*ast.DeclStmt)(nil), 126 }, func(n ast.Node) { 127 128 if strings.HasSuffix(pass.Fset.File(n.Pos()).Name(), "_test.go") { 129 return 130 } 131 132 decl := n.(*ast.DeclStmt) 133 gen, ok := decl.Decl.(*ast.GenDecl) 134 if !ok { 135 return 136 } 137 for _, spec := range gen.Specs { 138 u, ok := spec.(*ast.ValueSpec) 139 if !ok { 140 continue 141 } 142 143 tx := pass.TypesInfo.TypeOf(u.Type) 144 if tx == nil { 145 return 146 } 147 148 if !strings.HasPrefix(tx.String(), providerPackage) { 149 continue 150 } 151 pass.Reportf(n.Pos(), "Provider struct %s should be explicitly initialised with all fields provided", tx.String()) 152 } 153 }) 154 return nil 155 } 156 func detectMissingTypesFields(pass *analysis.Pass, providerPackage, typesPackage string) error { 157 insp := pass.ResultOf[inspect.Analyzer].(*inspector.Inspector) 158 insp.Preorder([]ast.Node{ 159 (*ast.CompositeLit)(nil), 160 }, func(n ast.Node) { 161 162 if strings.HasSuffix(pass.Fset.File(n.Pos()).Name(), "_test.go") { 163 return 164 } 165 166 lit := n.(*ast.CompositeLit) 167 if lit.Type == nil { 168 return 169 } 170 171 tx := pass.TypesInfo.TypeOf(lit.Type) 172 if tx == nil { 173 return 174 } 175 176 if !strings.HasPrefix(tx.String(), providerPackage) { 177 return 178 } 179 180 named, ok := tx.(*types.Named) 181 if !ok { 182 return 183 } 184 185 switch u := named.Underlying().(type) { 186 case *types.Struct: 187 for i := 0; i < u.NumFields(); i++ { 188 field := u.Field(i) 189 if !strings.HasPrefix(field.Type().String(), typesPackage) { 190 continue 191 } 192 var found bool 193 for j, included := range lit.Elts { 194 switch et := included.(type) { 195 case *ast.KeyValueExpr: 196 if et.Key.(*ast.Ident).Name == field.Name() { 197 found = true 198 } 199 default: 200 found = found || i == j 201 } 202 if found { 203 break 204 } 205 } 206 if !found { 207 pass.Reportf(lit.Pos(), "Provider struct %s is missing an initialised value for field '%s'", named.String(), field.Name()) 208 } 209 } 210 } 211 }) 212 return nil 213 }