From 0616ff0067133f6f5e014eea7fa3fca7f3b78403 Mon Sep 17 00:00:00 2001 From: mlevesquedion Date: Wed, 30 Dec 2020 16:09:41 -0500 Subject: [PATCH] Use propagation in fieldpropagator (#248) * move propagation to its own package * move sourcetype identification logic to sourcetype package * use propagation in fieldpropagator --- cmd/sourcetype/main.go | 26 --- internal/pkg/fieldpropagator/analyzer.go | 67 ++++--- .../testdata/src/source/test.go | 4 + internal/pkg/levee/levee.go | 2 +- .../testdata/src/example.com/core/source.go | 4 + .../src/example.com/tests/fields/tests.go | 2 + .../{levee => }/propagation/propagation.go | 4 +- internal/pkg/source/source.go | 63 +------ internal/pkg/sourcetype/analyzer.go | 164 ------------------ internal/pkg/sourcetype/analyzer_test.go | 32 ---- internal/pkg/sourcetype/sourcetype.go | 70 ++++++++ .../sourcetype/testdata/src/crosspkg/test.go | 36 ---- .../testdata/src/sourcetype/test.go | 43 ----- .../pkg/sourcetype/testdata/test-config.yaml | 18 -- 14 files changed, 125 insertions(+), 410 deletions(-) delete mode 100644 cmd/sourcetype/main.go rename internal/pkg/{levee => }/propagation/propagation.go (98%) delete mode 100644 internal/pkg/sourcetype/analyzer.go delete mode 100644 internal/pkg/sourcetype/analyzer_test.go create mode 100644 internal/pkg/sourcetype/sourcetype.go delete mode 100644 internal/pkg/sourcetype/testdata/src/crosspkg/test.go delete mode 100644 internal/pkg/sourcetype/testdata/src/sourcetype/test.go delete mode 100644 internal/pkg/sourcetype/testdata/test-config.yaml diff --git a/cmd/sourcetype/main.go b/cmd/sourcetype/main.go deleted file mode 100644 index 2a41e543..00000000 --- a/cmd/sourcetype/main.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2020 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package main - -import ( - "github.com/google/go-flow-levee/internal/pkg/sourcetype" - "golang.org/x/tools/go/analysis/singlechecker" -) - -func main() { - sourcetype.Report = true - - singlechecker.Main(sourcetype.Analyzer) -} diff --git a/internal/pkg/fieldpropagator/analyzer.go b/internal/pkg/fieldpropagator/analyzer.go index 4d9fc0eb..1883f62b 100644 --- a/internal/pkg/fieldpropagator/analyzer.go +++ b/internal/pkg/fieldpropagator/analyzer.go @@ -22,6 +22,7 @@ import ( "github.com/google/go-flow-levee/internal/pkg/config" "github.com/google/go-flow-levee/internal/pkg/fieldtags" + "github.com/google/go-flow-levee/internal/pkg/propagation" "github.com/google/go-flow-levee/internal/pkg/utils" "golang.org/x/tools/go/analysis" "golang.org/x/tools/go/analysis/passes/buildssa" @@ -49,7 +50,7 @@ var Analyzer = &analysis.Analyzer{ Name: "fieldpropagator", Doc: `This analyzer identifies field propagators. -A field propagator is a function that returns a source field.`, +A field propagator is a function that returns a value that is tainted by a source field.`, Flags: config.FlagSet, Run: run, Requires: []*analysis.Analyzer{buildssa.Analyzer, fieldtags.Analyzer}, @@ -105,43 +106,41 @@ func methodValues(ssaProg *ssa.Program, t types.Type) []*ssa.Function { } func analyzeBlocks(pass *analysis.Pass, conf *config.Config, tf fieldtags.ResultType, meth *ssa.Function) { - // Function does not return anything - if res := meth.Signature.Results(); res == nil || (*res).Len() == 0 { - return - } + var propagations []propagation.Propagation + for _, b := range meth.Blocks { - if len(b.Instrs) == 0 { - continue + for _, instr := range b.Instrs { + var ( + txType types.Type + field int + ) + switch t := instr.(type) { + case *ssa.Field: + txType = t.X.Type() + field = t.Field + case *ssa.FieldAddr: + txType = t.X.Type() + field = t.Field + default: + continue + } + if conf.IsSourceField(utils.DecomposeField(txType, field)) || tf.IsSourceField(txType, field) { + propagations = append(propagations, propagation.Dfs(instr.(ssa.Node), conf, tf)) + } } - lastInstr := b.Instrs[len(b.Instrs)-1] - ret, ok := lastInstr.(*ssa.Return) - if !ok { - continue - } - analyzeResults(pass, conf, tf, meth, ret.Results) } -} -func analyzeResults(pass *analysis.Pass, conf *config.Config, tf fieldtags.ResultType, meth *ssa.Function, results []ssa.Value) { - for _, r := range results { - fa, ok := fieldAddr(r) - if !ok { - continue - } - - xt, field := fa.X.Type(), fa.Field - if conf.IsSourceField(utils.DecomposeField(xt, field)) || tf.IsSourceField(xt, field) { - pass.ExportObjectFact(meth.Object(), &isFieldPropagator{}) + for _, b := range meth.Blocks { + for _, instr := range b.Instrs { + ret, ok := instr.(*ssa.Return) + if !ok { + continue + } + for _, prop := range propagations { + if prop.IsTainted(ret) { + pass.ExportObjectFact(meth.Object(), &isFieldPropagator{}) + } + } } } } - -func fieldAddr(x ssa.Value) (*ssa.FieldAddr, bool) { - switch t := x.(type) { - case *ssa.FieldAddr: - return t, true - case *ssa.UnOp: - return fieldAddr(t.X) - } - return nil, false -} diff --git a/internal/pkg/fieldpropagator/testdata/src/source/test.go b/internal/pkg/fieldpropagator/testdata/src/source/test.go index 493e45d9..d6a080db 100644 --- a/internal/pkg/fieldpropagator/testdata/src/source/test.go +++ b/internal/pkg/fieldpropagator/testdata/src/source/test.go @@ -49,6 +49,10 @@ func (s Source) DataDeref() string { // want DataDeref:"field propagator identif return *s.dataPtr } +func (s Source) ShowData() string { // want ShowData:"field propagator identified" + return "Data: " + s.data +} + var isAdmin bool func (s Source) MaybeData() string { // want MaybeData:"field propagator identified" diff --git a/internal/pkg/levee/levee.go b/internal/pkg/levee/levee.go index ec7649ca..d244c560 100644 --- a/internal/pkg/levee/levee.go +++ b/internal/pkg/levee/levee.go @@ -20,7 +20,7 @@ import ( "github.com/google/go-flow-levee/internal/pkg/config" "github.com/google/go-flow-levee/internal/pkg/fieldtags" - "github.com/google/go-flow-levee/internal/pkg/levee/propagation" + "github.com/google/go-flow-levee/internal/pkg/propagation" "github.com/google/go-flow-levee/internal/pkg/utils" "golang.org/x/tools/go/analysis" "golang.org/x/tools/go/ssa" diff --git a/internal/pkg/levee/testdata/src/example.com/core/source.go b/internal/pkg/levee/testdata/src/example.com/core/source.go index ed780118..ba19f85c 100644 --- a/internal/pkg/levee/testdata/src/example.com/core/source.go +++ b/internal/pkg/levee/testdata/src/example.com/core/source.go @@ -28,6 +28,10 @@ func (s Source) GetData() string { return s.Data } +func (s Source) ShowData() string { + return "Data: " + s.Data +} + func (s Source) Copy() (Source, error) { return s, nil } diff --git a/internal/pkg/levee/testdata/src/example.com/tests/fields/tests.go b/internal/pkg/levee/testdata/src/example.com/tests/fields/tests.go index 5ac85456..94cf5744 100644 --- a/internal/pkg/levee/testdata/src/example.com/tests/fields/tests.go +++ b/internal/pkg/levee/testdata/src/example.com/tests/fields/tests.go @@ -23,9 +23,11 @@ import ( func TestFieldAccessors(s core.Source, ptr *core.Source) { core.Sinkf("Data: %v", s.GetData()) // want "a source has reached a sink" + core.Sinkf(s.ShowData()) // want "a source has reached a sink" core.Sinkf("ID: %v", s.GetID()) core.Sinkf("Data: %v", ptr.GetData()) // want "a source has reached a sink" + core.Sinkf(ptr.ShowData()) // want "a source has reached a sink" core.Sinkf("ID: %v", ptr.GetID()) } diff --git a/internal/pkg/levee/propagation/propagation.go b/internal/pkg/propagation/propagation.go similarity index 98% rename from internal/pkg/levee/propagation/propagation.go rename to internal/pkg/propagation/propagation.go index 53d1fc8b..10fcdd46 100644 --- a/internal/pkg/levee/propagation/propagation.go +++ b/internal/pkg/propagation/propagation.go @@ -24,7 +24,7 @@ import ( "github.com/google/go-flow-levee/internal/pkg/config" "github.com/google/go-flow-levee/internal/pkg/fieldtags" "github.com/google/go-flow-levee/internal/pkg/sanitizer" - "github.com/google/go-flow-levee/internal/pkg/source" + "github.com/google/go-flow-levee/internal/pkg/sourcetype" "github.com/google/go-flow-levee/internal/pkg/utils" "golang.org/x/tools/go/pointer" "golang.org/x/tools/go/ssa" @@ -156,7 +156,7 @@ func (prop *Propagation) visit(n ssa.Node, maxInstrReached map[*ssa.BasicBlock]i // This is to avoid attaching calls where the source is the receiver, ex: // core.Sinkf("Source id: %v", wrapper.Source.GetID()) - if recv := t.Call.Signature().Recv(); recv != nil && source.IsSourceType(prop.config, prop.taggedFields, recv.Type()) { + if recv := t.Call.Signature().Recv(); recv != nil && sourcetype.IsSourceType(prop.config, prop.taggedFields, recv.Type()) { return } diff --git a/internal/pkg/source/source.go b/internal/pkg/source/source.go index 176efe8e..8cced107 100644 --- a/internal/pkg/source/source.go +++ b/internal/pkg/source/source.go @@ -16,13 +16,13 @@ package source import ( - "fmt" "go/token" "go/types" "github.com/google/go-flow-levee/internal/pkg/config" "github.com/google/go-flow-levee/internal/pkg/fieldpropagator" "github.com/google/go-flow-levee/internal/pkg/fieldtags" + "github.com/google/go-flow-levee/internal/pkg/sourcetype" "github.com/google/go-flow-levee/internal/pkg/utils" "golang.org/x/tools/go/analysis/passes/buildssa" "golang.org/x/tools/go/ssa" @@ -90,7 +90,7 @@ func identify(conf *config.Config, ssaInput *buildssa.SSA, taggedFields fieldtag func sourcesFromParams(fn *ssa.Function, conf *config.Config, taggedFields fieldtags.ResultType) []*Source { var sources []*Source for _, p := range fn.Params { - if IsSourceType(conf, taggedFields, p.Type()) { + if sourcetype.IsSourceType(conf, taggedFields, p.Type()) { sources = append(sources, New(p)) } } @@ -106,7 +106,7 @@ func sourcesFromClosures(fn *ssa.Function, conf *config.Config, taggedFields fie for _, p := range fn.FreeVars { switch t := p.Type().(type) { case *types.Pointer: - if IsSourceType(conf, taggedFields, t) { + if sourcetype.IsSourceType(conf, taggedFields, t) { sources = append(sources, New(p)) } } @@ -135,19 +135,19 @@ func isSourceNode(n ssa.Node, conf *config.Config, propagators fieldpropagator.R // Values produced by sanitizers are not sources. case *ssa.Alloc: - return !isProducedBySanitizer(v, conf) && IsSourceType(conf, taggedFields, n.(ssa.Value).Type()) + return !isProducedBySanitizer(v, conf) && sourcetype.IsSourceType(conf, taggedFields, n.(ssa.Value).Type()) // Values produced by sanitizers are not sources. // Values produced by field propagators are. case *ssa.Call: return !isProducedBySanitizer(v, conf) && - (propagators.IsFieldPropagator(v) || IsSourceType(conf, taggedFields, n.(ssa.Value).Type())) + (propagators.IsFieldPropagator(v) || sourcetype.IsSourceType(conf, taggedFields, n.(ssa.Value).Type())) // A type assertion can assert that an interface is of a source type. // Only panicky type asserts will refer to the source Value. // The typed value returned in (value, ok) type assertions are examined in the case for ssa.Extract instructions. case *ssa.TypeAssert: - return !v.CommaOk && IsSourceType(conf, taggedFields, v.AssertedType) + return !v.CommaOk && sourcetype.IsSourceType(conf, taggedFields, v.AssertedType) // An Extract is used to obtain a value from an instruction that returns multiple values. // If the extracted value is a Pointer to a Source, it won't have an Alloc, so we need to @@ -155,11 +155,11 @@ func isSourceNode(n ssa.Node, conf *config.Config, propagators fieldpropagator.R case *ssa.Extract: t := v.Tuple.Type().(*types.Tuple).At(v.Index).Type() _, ok := t.(*types.Pointer) - return ok && IsSourceType(conf, taggedFields, t) + return ok && sourcetype.IsSourceType(conf, taggedFields, t) // Unary operator <- can receive sources from a channel. case *ssa.UnOp: - return v.Op == token.ARROW && IsSourceType(conf, taggedFields, n.(ssa.Value).Type()) + return v.Op == token.ARROW && sourcetype.IsSourceType(conf, taggedFields, n.(ssa.Value).Type()) // Field access (Field, FieldAddr), // collection access (Index, IndexAddr, Lookup), @@ -168,55 +168,10 @@ func isSourceNode(n ssa.Node, conf *config.Config, propagators fieldpropagator.R case *ssa.Field, *ssa.FieldAddr, *ssa.Index, *ssa.IndexAddr, *ssa.Lookup, *ssa.MakeMap, *ssa.MakeChan: - return IsSourceType(conf, taggedFields, n.(ssa.Value).Type()) + return sourcetype.IsSourceType(conf, taggedFields, n.(ssa.Value).Type()) } } -// IsSourceType determines whether a Type is a Source Type. -// A Source Type is either: -// - A Named Type that is classified as a Source -// - A composite type that contains a Source Type -// - A Struct Type that contains a tagged field -func IsSourceType(c *config.Config, tf fieldtags.ResultType, t types.Type) bool { - deref := utils.Dereference(t) - switch tt := deref.(type) { - case *types.Named: - return c.IsSourceType(utils.DecomposeType(tt)) || IsSourceType(c, tf, tt.Underlying()) - case *types.Array: - return IsSourceType(c, tf, tt.Elem()) - case *types.Slice: - return IsSourceType(c, tf, tt.Elem()) - case *types.Chan: - return IsSourceType(c, tf, tt.Elem()) - case *types.Map: - key := IsSourceType(c, tf, tt.Key()) - elem := IsSourceType(c, tf, tt.Elem()) - return key || elem - case *types.Struct: - return hasTaggedField(tf, tt) - case *types.Basic, *types.Tuple, *types.Interface, *types.Signature: - // These types do not currently represent possible source types - return false - case *types.Pointer: - // This should be unreachable due to the dereference above - return false - default: - // The above should be exhaustive. Reaching this default case is an error. - fmt.Printf("unexpected type received: %T %v; please report this issue\n", tt, tt) - return false - } -} - -func hasTaggedField(taggedFields fieldtags.ResultType, s *types.Struct) bool { - for i := 0; i < s.NumFields(); i++ { - f := s.Field(i) - if taggedFields.IsSource(f) { - return true - } - } - return false -} - func isProducedBySanitizer(v ssa.Value, conf *config.Config) bool { for _, instr := range *v.Referrers() { store, ok := instr.(*ssa.Store) diff --git a/internal/pkg/sourcetype/analyzer.go b/internal/pkg/sourcetype/analyzer.go deleted file mode 100644 index 4d9afb68..00000000 --- a/internal/pkg/sourcetype/analyzer.go +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright 2020 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package sourcetpye handles identification of source types and fields at the type declaration. -// This can be consumed downstream, e.g., by the sources package to identify source data at instantiation. -// This package concerns itself with ssa.Member and types.Object, as opposed to ssa.Value and ssa.Instruction more typically used in other analysis packages. -package sourcetype - -import ( - "fmt" - "go/types" - "reflect" - "sort" - - "github.com/google/go-flow-levee/internal/pkg/config" - "github.com/google/go-flow-levee/internal/pkg/fieldtags" - "github.com/google/go-flow-levee/internal/pkg/utils" - "golang.org/x/tools/go/analysis" - "golang.org/x/tools/go/analysis/passes/buildssa" - "golang.org/x/tools/go/ssa" -) - -type typeDeclFact struct{} - -func (t typeDeclFact) AFact() { -} - -func (t typeDeclFact) String() string { - return "source type" -} - -type fieldDeclFact struct{} - -func (f fieldDeclFact) AFact() { -} - -func (f fieldDeclFact) String() string { - return "source field" -} - -type sourceClassifier struct { - passObjFacts []analysis.ObjectFact -} - -func (s sourceClassifier) IsSource(named *types.Named) bool { - if named == nil { - return false - } - - for _, fct := range s.passObjFacts { - if fct.Object == named.Obj() { - return true - } - } - return false -} - -func (s sourceClassifier) IsSourceField(v *types.Var) bool { - for _, fct := range s.passObjFacts { - if fct.Object == v { - return true - } - } - return false -} - -var Analyzer = &analysis.Analyzer{ - Name: "sourcetypes", - Doc: "This analyzer identifies types.Types values which contain dataflow sources.", - Flags: config.FlagSet, - Run: run, - Requires: []*analysis.Analyzer{buildssa.Analyzer, fieldtags.Analyzer}, - ResultType: reflect.TypeOf(new(sourceClassifier)), - FactTypes: []analysis.Fact{new(typeDeclFact), new(fieldDeclFact)}, -} - -var Report bool - -func run(pass *analysis.Pass) (interface{}, error) { - taggedFields := pass.ResultOf[fieldtags.Analyzer].(fieldtags.ResultType) - ssaInput := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA) - conf, err := config.ReadConfig() - if err != nil { - return nil, err - } - - // Members contains all named entities - for _, mem := range ssaInput.Pkg.Members { - if ssaType, ok := mem.(*ssa.Type); ok && - (conf.IsSourceType(utils.DecomposeType(ssaType.Type())) || hasTaggedField(mem, taggedFields)) { - exportSourceFacts(pass, ssaType, conf, taggedFields) - } - } - - classifier := &sourceClassifier{pass.AllObjectFacts()} - if Report { - makeReport(classifier, pass) - } - - return classifier, nil -} - -func hasTaggedField(mem ssa.Member, taggedFields fieldtags.ResultType) bool { - n, ok := mem.Type().(*types.Named) - if !ok { - return false - } - s, ok := n.Underlying().(*types.Struct) - if !ok { - return false - } - var has bool - for i := 0; i < s.NumFields(); i++ { - f := s.Field(i) - has = has || taggedFields.IsSource(f) - } - return has -} - -func exportSourceFacts(pass *analysis.Pass, ssaType *ssa.Type, conf *config.Config, taggedFields fieldtags.ResultType) { - pass.ExportObjectFact(ssaType.Object(), &typeDeclFact{}) - if under, ok := ssaType.Type().Underlying().(*types.Struct); ok { - for i := 0; i < under.NumFields(); i++ { - fld := under.Field(i) - if fld.Pkg() != pass.Pkg { - continue - } - - typPath, typName := utils.DecomposeType(ssaType.Type()) - if conf.IsSourceField(typPath, typName, fld.Name()) || taggedFields.IsSource(fld) { - pass.ExportObjectFact(fld, &fieldDeclFact{}) - } - } - } -} - -func makeReport(classifier *sourceClassifier, pass *analysis.Pass) { - // Aggregate diagnostics first in order to sort report by position. - var diags []analysis.Diagnostic - for _, objFact := range classifier.passObjFacts { - // A pass should only report within its package. - if objFact.Object.Pkg() == pass.Pkg { - diags = append(diags, analysis.Diagnostic{ - Pos: objFact.Object.Pos(), - Message: fmt.Sprintf("%v: %v", objFact.Fact, objFact.Object.Name()), - }) - } - } - sort.Slice(diags, func(i, j int) bool { return diags[i].Pos < diags[j].Pos }) - for _, d := range diags { - pass.Reportf(d.Pos, d.Message) - } -} diff --git a/internal/pkg/sourcetype/analyzer_test.go b/internal/pkg/sourcetype/analyzer_test.go deleted file mode 100644 index 6a69e2fc..00000000 --- a/internal/pkg/sourcetype/analyzer_test.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2020 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package sourcetype - -import ( - "testing" - - "golang.org/x/tools/go/analysis/analysistest" -) - -func TestSourceAnalysis(t *testing.T) { - testdata := analysistest.TestData() - - if err := Analyzer.Flags.Set("config", testdata+"/test-config.yaml"); err != nil { - t.Error(err) - return - } - - analysistest.Run(t, testdata, Analyzer, "./...") -} diff --git a/internal/pkg/sourcetype/sourcetype.go b/internal/pkg/sourcetype/sourcetype.go new file mode 100644 index 00000000..1e210250 --- /dev/null +++ b/internal/pkg/sourcetype/sourcetype.go @@ -0,0 +1,70 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// https://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// Package sourcetype handles identification of sources based on their type. +package sourcetype + +import ( + "fmt" + "go/types" + + "github.com/google/go-flow-levee/internal/pkg/config" + "github.com/google/go-flow-levee/internal/pkg/fieldtags" + "github.com/google/go-flow-levee/internal/pkg/utils" +) + +// IsSourceType determines whether a Type is a Source Type. +// A Source Type is either: +// - A Named Type that is classified as a Source +// - A composite type that contains a Source Type +// - A Struct Type that contains a tagged field +func IsSourceType(c *config.Config, tf fieldtags.ResultType, t types.Type) bool { + deref := utils.Dereference(t) + switch tt := deref.(type) { + case *types.Named: + return c.IsSourceType(utils.DecomposeType(tt)) || IsSourceType(c, tf, tt.Underlying()) + case *types.Array: + return IsSourceType(c, tf, tt.Elem()) + case *types.Slice: + return IsSourceType(c, tf, tt.Elem()) + case *types.Chan: + return IsSourceType(c, tf, tt.Elem()) + case *types.Map: + key := IsSourceType(c, tf, tt.Key()) + elem := IsSourceType(c, tf, tt.Elem()) + return key || elem + case *types.Struct: + return hasTaggedField(tf, tt) + case *types.Basic, *types.Tuple, *types.Interface, *types.Signature: + // These types do not currently represent possible source types + return false + case *types.Pointer: + // This should be unreachable due to the dereference above + return false + default: + // The above should be exhaustive. Reaching this default case is an error. + fmt.Printf("unexpected type received: %T %v; please report this issue\n", tt, tt) + return false + } +} + +func hasTaggedField(taggedFields fieldtags.ResultType, s *types.Struct) bool { + for i := 0; i < s.NumFields(); i++ { + f := s.Field(i) + if taggedFields.IsSource(f) { + return true + } + } + return false +} diff --git a/internal/pkg/sourcetype/testdata/src/crosspkg/test.go b/internal/pkg/sourcetype/testdata/src/crosspkg/test.go deleted file mode 100644 index 1a8cc572..00000000 --- a/internal/pkg/sourcetype/testdata/src/crosspkg/test.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright 2020 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package crosspkg - -import "sourcetype" - -type AliasStruct = sourcetype.Source // want AliasStruct:"source type" - -type NamedType sourcetype.Source // want NamedType:"source type" - -// TODO(#96,#97) Consider automatic detection of the following types. - -type SliceContainer []sourcetype.Source -type ArrayContainer [5]sourcetype.Source -type MapKeyContainer map[sourcetype.Source]interface{} -type MapValueContainer map[string]sourcetype.Source - -type EmbeddedWrapper struct { - sourcetype.Source -} - -type FieldWrapper struct { - s sourcetype.Source -} diff --git a/internal/pkg/sourcetype/testdata/src/sourcetype/test.go b/internal/pkg/sourcetype/testdata/src/sourcetype/test.go deleted file mode 100644 index 58e197db..00000000 --- a/internal/pkg/sourcetype/testdata/src/sourcetype/test.go +++ /dev/null @@ -1,43 +0,0 @@ -// Copyright 2020 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// https://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package sourcetype - -type Source struct { // want Source:"source type" - Data string // want Data:"source field" - Tagged string `levee:"source"` // want Tagged:"source field" - Name1, Name2 string `levee:"source"` // want Name1:"source field" Name2:"source field" - ID int -} - -type SourceContainingTaggedField struct { // want SourceContainingTaggedField:"source type" - Tagged string `levee:"source"` // want Tagged:"source field" -} - -type AliasStruct = Source // want AliasStruct:"source type" - -type NamedType Source // want NamedType:"source type" - -type SliceContainer []Source -type ArrayContainer [5]Source -type MapKeyContainer map[Source]interface{} -type MapValueContainer map[string]Source - -type EmbeddedWrapper struct { - Source -} - -type FieldWrapper struct { - s Source -} diff --git a/internal/pkg/sourcetype/testdata/test-config.yaml b/internal/pkg/sourcetype/testdata/test-config.yaml deleted file mode 100644 index 043f7ae3..00000000 --- a/internal/pkg/sourcetype/testdata/test-config.yaml +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright 2019 Google LLC -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. ---- -Sources: -- PackageRE: "" - TypeRE: "^Source$" - FieldRE: "^Data"