├── third_party └── .gitkeep ├── .gitignore ├── design ├── analyzers.dot.png └── analyzers.dot ├── internal └── pkg │ ├── config │ ├── testdata │ │ ├── src │ │ │ └── config_analysistest │ │ │ │ ├── go.mod │ │ │ │ ├── example │ │ │ │ ├── exclusion │ │ │ │ │ └── test.go │ │ │ │ ├── notcore │ │ │ │ │ └── notcore.go │ │ │ │ ├── core │ │ │ │ │ └── core.go │ │ │ │ └── crosspkg │ │ │ │ │ └── crosspkg.go │ │ │ │ └── notexample │ │ │ │ ├── core │ │ │ │ └── core.go │ │ │ │ └── exclusion │ │ │ │ └── test.go │ │ ├── empty-config.yaml │ │ └── test-config.yaml │ ├── set_config_bytes_test.go │ ├── regexp │ │ ├── regexp_test.go │ │ └── regexp.go │ ├── fieldtags_test.go │ └── config_test.go │ ├── levee │ ├── testdata │ │ ├── src │ │ │ └── levee_analysistest │ │ │ │ ├── go.mod │ │ │ │ ├── example │ │ │ │ ├── core │ │ │ │ │ ├── sanitize.go │ │ │ │ │ ├── sink.go │ │ │ │ │ └── source.go │ │ │ │ └── tests │ │ │ │ │ ├── booleans │ │ │ │ │ └── tests.go │ │ │ │ │ ├── recover │ │ │ │ │ └── tests.go │ │ │ │ │ ├── store │ │ │ │ │ └── tests.go │ │ │ │ │ ├── typealias │ │ │ │ │ └── tests.go │ │ │ │ │ ├── propagation │ │ │ │ │ ├── functions.go │ │ │ │ │ └── builtins.go │ │ │ │ │ ├── eface │ │ │ │ │ └── tests.go │ │ │ │ │ ├── basictypes │ │ │ │ │ └── tests.go │ │ │ │ │ ├── declarations │ │ │ │ │ └── tests.go │ │ │ │ │ ├── closures │ │ │ │ │ └── tests.go │ │ │ │ │ ├── binop │ │ │ │ │ └── tests.go │ │ │ │ │ ├── go │ │ │ │ │ └── tests.go │ │ │ │ │ ├── excludedpackage │ │ │ │ │ └── tests.go │ │ │ │ │ ├── namedreturn │ │ │ │ │ └── tests.go │ │ │ │ │ ├── colocation │ │ │ │ │ └── tests.go │ │ │ │ │ ├── callorder │ │ │ │ │ ├── singleblock.go │ │ │ │ │ ├── colocation.go │ │ │ │ │ ├── beforesource.go │ │ │ │ │ └── multiblock.go │ │ │ │ │ ├── pointers │ │ │ │ │ └── tests.go │ │ │ │ │ ├── phi │ │ │ │ │ └── tests.go │ │ │ │ │ ├── includedpackage │ │ │ │ │ └── tests.go │ │ │ │ │ ├── panic │ │ │ │ │ └── tests.go │ │ │ │ │ ├── receivers │ │ │ │ │ └── tests.go │ │ │ │ │ ├── typeassert │ │ │ │ │ ├── tests.go │ │ │ │ │ └── assertion_inference_tests.go │ │ │ │ │ ├── collections │ │ │ │ │ ├── chans.go │ │ │ │ │ ├── arrays.go │ │ │ │ │ ├── slices.go │ │ │ │ │ └── maps.go │ │ │ │ │ ├── arguments │ │ │ │ │ └── tests.go │ │ │ │ │ ├── inlining │ │ │ │ │ └── tests.go │ │ │ │ │ ├── embedding │ │ │ │ │ └── tests.go │ │ │ │ │ ├── stdlib │ │ │ │ │ └── interface_funcs.go │ │ │ │ │ ├── position │ │ │ │ │ └── tests.go │ │ │ │ │ ├── extracts │ │ │ │ │ └── tests.go │ │ │ │ │ ├── sinks │ │ │ │ │ └── tests.go │ │ │ │ │ └── loops │ │ │ │ │ └── tests.go │ │ │ │ ├── custom.message.com │ │ │ │ ├── nocustom │ │ │ │ │ └── nocustom.go │ │ │ │ └── withcustom │ │ │ │ │ └── withcustom.go │ │ │ │ ├── nopanic.com │ │ │ │ └── core │ │ │ │ │ └── core.go │ │ │ │ └── ear │ │ │ │ └── tests │ │ │ │ └── call │ │ │ │ ├── callee-src.go │ │ │ │ └── callee-src-sink.go │ │ ├── no-custom-message.yaml │ │ ├── with-custom-message.yaml │ │ ├── allowpanicontaintedvalues-config.yaml │ │ ├── test-ear-config.yaml │ │ └── test-config.yaml │ └── levee_test.go │ ├── fieldtags │ ├── testdata │ │ ├── src │ │ │ └── fieldtags_analysistest │ │ │ │ ├── go.mod │ │ │ │ ├── crosspkg │ │ │ │ └── crosspkg.go │ │ │ │ └── core │ │ │ │ └── core.go │ │ └── test-config.yaml │ └── analyzer_test.go │ ├── propagation │ ├── summary │ │ ├── testdata │ │ │ └── src │ │ │ │ └── summary_analysistest │ │ │ │ ├── go.mod │ │ │ │ └── tests │ │ │ │ └── test.go │ │ └── api_test.go │ └── stdlib.go │ ├── suppression │ ├── testdata │ │ └── src │ │ │ └── suppression_analysistest │ │ │ ├── go.mod │ │ │ └── comments │ │ │ └── comments.go │ ├── analyzer_test.go │ └── analyzer.go │ ├── fieldpropagator │ ├── testdata │ │ ├── src │ │ │ └── fieldpropagator_analysistest │ │ │ │ ├── go.mod │ │ │ │ ├── proto │ │ │ │ └── proto.go │ │ │ │ └── source │ │ │ │ └── test.go │ │ └── test-config.yaml │ └── analyzer_test.go │ ├── debug │ ├── render │ │ ├── testdata │ │ │ ├── TestClosure.dot │ │ │ ├── TestClosure.ssa │ │ │ ├── TestDisconnected.ssa │ │ │ ├── TestParams.ssa │ │ │ ├── TestSingleBlock.ssa │ │ │ ├── tests.go │ │ │ ├── TestMultiBlock.ssa │ │ │ ├── TestDisconnected.dot │ │ │ ├── TestParams.dot │ │ │ └── TestSingleBlock.dot │ │ ├── cfg.go │ │ ├── ssa.go │ │ ├── render_test.go │ │ └── dot.go │ ├── debug.go │ ├── node │ │ └── node.go │ └── dump │ │ └── dump.go │ ├── earpointer │ └── config.yaml │ ├── sourceinfer │ ├── testdata │ │ ├── src │ │ │ └── example.com │ │ │ │ ├── tests │ │ │ │ ├── nosource │ │ │ │ │ └── test.go │ │ │ │ ├── samepkg │ │ │ │ │ └── test.go │ │ │ │ ├── crosspkg │ │ │ │ │ └── test.go │ │ │ │ └── core │ │ │ │ │ ├── mixed.go │ │ │ │ │ ├── recursive.go │ │ │ │ │ ├── typedef.go │ │ │ │ │ └── field.go │ │ │ │ └── source │ │ │ │ └── source.go │ │ └── test-config.yaml │ └── analyzer_test.go │ ├── utils │ ├── testdata │ │ └── src │ │ │ ├── dereference │ │ │ ├── struct │ │ │ │ └── struct.go │ │ │ └── pointer_to_struct │ │ │ │ └── pointer_to_struct.go │ │ │ └── fields │ │ │ ├── regular │ │ │ └── test.go │ │ │ └── embedded │ │ │ └── test.go │ ├── utils_go118.go │ └── utils.go │ ├── source │ ├── testdata │ │ └── src │ │ │ └── analyzertest │ │ │ ├── sourcetest │ │ │ ├── position.go │ │ │ └── source.go │ │ │ └── test-config.yaml │ ├── analyzer_test.go │ └── analyzer.go │ ├── sourcetype │ ├── testdata │ │ └── test_stackoverflow.go │ ├── sourcetype_test.go │ └── sourcetype.go │ └── sanitizer │ ├── testdata │ └── tests.go │ ├── sanitizer.go │ └── sanitizer_test.go ├── guides └── quickstart │ ├── analyzer_configuration.yaml │ └── quickstart.go ├── go.mod ├── .github ├── ISSUE_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE.md ├── ISSUE_TEMPLATE │ ├── bug_report.md │ ├── false-negative.md │ └── false-positive.md └── workflows │ └── ci.yaml ├── configuration ├── example.sh └── example-config.yaml ├── cmd └── levee │ └── main.go ├── hack └── verify-kubernetes.sh ├── GIT_WORKFLOW.md ├── pkg └── levee │ └── levee.go ├── CONTRIBUTING.md └── README.md /third_party/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /.idea 3 | 4 | # Debugging output 5 | output 6 | -------------------------------------------------------------------------------- /design/analyzers.dot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/google/go-flow-levee/HEAD/design/analyzers.dot.png -------------------------------------------------------------------------------- /internal/pkg/config/testdata/src/config_analysistest/go.mod: -------------------------------------------------------------------------------- 1 | module config_analysistest 2 | 3 | go 1.15 4 | -------------------------------------------------------------------------------- /internal/pkg/levee/testdata/src/levee_analysistest/go.mod: -------------------------------------------------------------------------------- 1 | module levee_analysistest 2 | 3 | go 1.15 4 | -------------------------------------------------------------------------------- /internal/pkg/fieldtags/testdata/src/fieldtags_analysistest/go.mod: -------------------------------------------------------------------------------- 1 | module fieldtags_analysistest 2 | 3 | go 1.15 4 | -------------------------------------------------------------------------------- /internal/pkg/propagation/summary/testdata/src/summary_analysistest/go.mod: -------------------------------------------------------------------------------- 1 | module summary_analysistest 2 | 3 | go 1.15 4 | -------------------------------------------------------------------------------- /internal/pkg/suppression/testdata/src/suppression_analysistest/go.mod: -------------------------------------------------------------------------------- 1 | module suppression_analysistest 2 | 3 | go 1.15 4 | -------------------------------------------------------------------------------- /internal/pkg/fieldpropagator/testdata/src/fieldpropagator_analysistest/go.mod: -------------------------------------------------------------------------------- 1 | module fieldpropagator_analysistest 2 | 3 | go 1.15 4 | -------------------------------------------------------------------------------- /guides/quickstart/analyzer_configuration.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | FieldTags: 3 | - Key: datapolicy 4 | Value: password 5 | Sinks: 6 | - Package: log 7 | Method: Printf 8 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/google/go-flow-levee 2 | 3 | go 1.14 4 | 5 | require ( 6 | github.com/google/go-cmp v0.5.2 7 | golang.org/x/tools v0.1.12 8 | sigs.k8s.io/yaml v1.2.0 9 | ) 10 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Expected Behavior 2 | 3 | 4 | ## Actual Behavior 5 | 6 | 7 | ## Steps to Reproduce the Problem 8 | 9 | 1. 10 | 1. 11 | 1. 12 | 13 | ## Specifications 14 | 15 | - Version: 16 | - Platform: -------------------------------------------------------------------------------- /internal/pkg/debug/render/testdata/TestClosure.dot: -------------------------------------------------------------------------------- 1 | digraph { 2 | subgraph cluster_0 { 3 | color=black; 4 | label="entry"; 5 | "t0 = TestClosure$1(0:int)\n(Call)" [shape=rectangle]; 6 | "return\n(Return)" [shape=diamond]; 7 | } 8 | "TestClosure$1\n(Function)" -> "t0 = TestClosure$1(0:int)\n(Call)" [color=orange]; 9 | "0:int\n(Const)" -> "t0 = TestClosure$1(0:int)\n(Call)" [color=orange]; 10 | } 11 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Fixes # 2 | 3 | > It's a good idea to open an issue first for discussion. 4 | 5 | - [ ] Running against a large codebase such as [Kubernetes](https://github.com/kubernetes/kubernetes) does not error out. (See [DEVELOPING.md](https://github.com/google/go-flow-levee/blob/master/DEVELOPING.md) for instructions on how to do that.) 6 | - [ ] Appropriate changes to README are included in PR 7 | -------------------------------------------------------------------------------- /configuration/example.sh: -------------------------------------------------------------------------------- 1 | echo "Working directory: $(pwd)" 2 | 3 | # Get go bin 4 | GB=$(go env GOBIN) GB=${GB:-$(go env GOPATH)/bin} 5 | # go vet dislikes relative pathing. 6 | SCRIPT_DIR=$( cd "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P ) 7 | # analysis arguments 8 | CONFIG_FILE=${SCRIPT_DIR}/example-config.yaml 9 | PKGS=$(pwd)/... 10 | 11 | set -x 12 | 13 | go install github.com/google/go-flow-levee/cmd/levee 14 | go vet -vettool ${GB}/levee -config ${CONFIG_FILE} ${PKGS} 15 | -------------------------------------------------------------------------------- /internal/pkg/debug/render/testdata/TestClosure.ssa: -------------------------------------------------------------------------------- 1 | func TestClosure() 2 | 0: entry 3 | 0(*ssa.Call ): t0 = TestClosure$1(0:int) 4 | 1(*ssa.Return ): return 5 | func TestClosure$1(x int) 6 | 0: entry 7 | 0(*ssa.Alloc ): t0 = new [1]interface{} (varargs) 8 | 1(*ssa.IndexAddr ): t1 = &t0[0:int] 9 | 2(*ssa.MakeInterface ): t2 = make interface{} <- int (x) 10 | 3(*ssa.Store ): *t1 = t2 11 | 4(*ssa.Slice ): t3 = slice t0[:] 12 | 5(*ssa.Call ): t4 = fmt.Println(t3...) 13 | 6(*ssa.Return ): return 14 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | labels: bug 5 | 6 | --- 7 | 8 | ## Bug report 9 | 10 | **Describe the bug** 11 | Please include a clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Please make it as easy as possible for us to reproduce what you observed. If possible, provide the exact configuration and code on which you were using when you encountered the bug. If the code cannot be shared, please provide a simplified example and confirm that it also exposes the bug. 15 | 16 | **Additional context** 17 | Add any other context about the problem here. 18 | -------------------------------------------------------------------------------- /internal/pkg/earpointer/config.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | --- 15 | UseEAR: true 16 | -------------------------------------------------------------------------------- /internal/pkg/debug/render/testdata/TestDisconnected.ssa: -------------------------------------------------------------------------------- 1 | func TestDisconnected() 2 | 0: entry 3 | 0(*ssa.Jump ): jump 3 4 | 1: for.loop 5 | 0(*ssa.Phi ): t4 = phi [0: 0:int, 1: t1] #i 6 | 1(*ssa.BinOp ): t5 = 1:int * 2:int 7 | 2(*ssa.BinOp ): t6 = t4 < t5 8 | 3(*ssa.If ): if t6 goto 1 else 2 9 | 2: for.body 10 | 0(*ssa.BinOp ): t0 = t4 - 1:int 11 | 1(*ssa.BinOp ): t1 = t0 + 1:int 12 | 2(*ssa.Jump ): jump 3 13 | 3: for.done 14 | 0(*ssa.BinOp ): t2 = "error: ":string + "unreachable code":string 15 | 1(*ssa.Call ): t3 = fmt.Printf(t2, nil:[]interface{}...) 16 | 2(*ssa.Return ): return 17 | -------------------------------------------------------------------------------- /internal/pkg/config/testdata/empty-config.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | --- 15 | 16 | # This config left intentionally empty. 17 | -------------------------------------------------------------------------------- /internal/pkg/fieldtags/testdata/test-config.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | --- 15 | FieldTags: 16 | - Key: example 17 | Value: sensitive 18 | -------------------------------------------------------------------------------- /internal/pkg/levee/testdata/no-custom-message.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | --- 15 | Sources: 16 | - Type: Source 17 | 18 | Sinks: 19 | - Method: Sink 20 | -------------------------------------------------------------------------------- /internal/pkg/debug/render/testdata/TestParams.ssa: -------------------------------------------------------------------------------- 1 | func TestParams(a int, b int, c string) 2 | 0: entry 3 | 0(*ssa.Alloc ): t0 = new [3]interface{} (varargs) 4 | 1(*ssa.IndexAddr ): t1 = &t0[0:int] 5 | 2(*ssa.MakeInterface ): t2 = make interface{} <- int (a) 6 | 3(*ssa.Store ): *t1 = t2 7 | 4(*ssa.IndexAddr ): t3 = &t0[1:int] 8 | 5(*ssa.MakeInterface ): t4 = make interface{} <- int (b) 9 | 6(*ssa.Store ): *t3 = t4 10 | 7(*ssa.IndexAddr ): t5 = &t0[2:int] 11 | 8(*ssa.MakeInterface ): t6 = make interface{} <- string (c) 12 | 9(*ssa.Store ): *t5 = t6 13 | 10(*ssa.Slice ): t7 = slice t0[:] 14 | 11(*ssa.Call ): t8 = fmt.Println(t7...) 15 | 12(*ssa.Return ): return 16 | -------------------------------------------------------------------------------- /internal/pkg/config/testdata/src/config_analysistest/example/exclusion/test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package test 16 | 17 | func Foo() {} // want "excluded" 18 | 19 | func Bar() {} 20 | -------------------------------------------------------------------------------- /internal/pkg/sourceinfer/testdata/src/example.com/tests/nosource/test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package nosource 16 | 17 | type NotSource struct{} 18 | 19 | type NotSourceEither struct{} 20 | -------------------------------------------------------------------------------- /internal/pkg/config/testdata/src/config_analysistest/notexample/core/core.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package core 16 | 17 | func Sink() {} 18 | 19 | type Sinker struct{} 20 | 21 | func (s Sinker) Do() {} 22 | -------------------------------------------------------------------------------- /internal/pkg/config/testdata/src/config_analysistest/notexample/exclusion/test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package exclusion 16 | 17 | func Foo() {} // want "excluded" 18 | 19 | func Bar() {} // want "excluded" 20 | -------------------------------------------------------------------------------- /internal/pkg/levee/testdata/with-custom-message.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2021 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | --- 15 | ReportMessage: This custom message is included with report. 16 | 17 | Sources: 18 | - Type: Source 19 | 20 | Sinks: 21 | - Method: Sink 22 | -------------------------------------------------------------------------------- /internal/pkg/sourceinfer/testdata/src/example.com/tests/samepkg/test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package samepkg 16 | 17 | type Source struct{} 18 | 19 | type DefinedSource Source // want DefinedSource:"inferred source" 20 | -------------------------------------------------------------------------------- /internal/pkg/sourceinfer/testdata/test-config.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | --- 15 | Sources: 16 | - PackageRE: "^example.com/source$" 17 | TypeRE: "^Source$" 18 | - PackageRE: "^example.com/tests/samepkg$" 19 | TypeRE: "^Source$" 20 | -------------------------------------------------------------------------------- /design/analyzers.dot: -------------------------------------------------------------------------------- 1 | digraph { 2 | rankdir=LR; 3 | node [ shape = "record" ]; 4 | node [ color = "darkgoldenrod1" ]; 5 | 6 | subgraph cluster_1 { 7 | label = "Identify by types.Object" 8 | sourceObj; 9 | sinkObj; 10 | propagatorObj; 11 | sanitizerObj; 12 | } 13 | 14 | node [ color = "lightblue" ]; 15 | 16 | subgraph cluster_2 { 17 | label = "Identify by ssa.Value and ssa.Instruction" 18 | source; 19 | sink; 20 | sanitizer; 21 | propagator; 22 | taint; 23 | } 24 | 25 | subgraph cluster_3 { 26 | levee; 27 | } 28 | 29 | 30 | sourceObj:e -> source:w; 31 | sinkObj:e -> sink:w; 32 | sanitizerObj:e -> sanitizer:w; 33 | propagatorObj:e -> propagator:w; 34 | 35 | { source:e propagator:e } -> taint:w; 36 | 37 | { source:e sanitizer:e sink:e taint:e } -> levee:w; 38 | } 39 | -------------------------------------------------------------------------------- /internal/pkg/utils/testdata/src/dereference/struct/struct.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package pointer_to_struct 16 | 17 | import "fmt" 18 | 19 | type foo struct { 20 | } 21 | 22 | func f() { 23 | bar := foo{} 24 | fmt.Printf("%v", bar) 25 | } 26 | -------------------------------------------------------------------------------- /internal/pkg/utils/utils_go118.go: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | //go:build go1.18 16 | // +build go1.18 17 | 18 | package utils 19 | 20 | func init() { 21 | // After 1.18 default rendering of interface{} is any. 22 | DefaultEmptyInterface = "any" 23 | } 24 | -------------------------------------------------------------------------------- /internal/pkg/source/testdata/src/analyzertest/sourcetest/position.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package sourcetest 16 | 17 | func TestSourcePointerExtract() { 18 | s, _ := NewSource() // want "source identified at .*position.go:18:19" 19 | _ = s 20 | } 21 | -------------------------------------------------------------------------------- /cmd/levee/main.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "github.com/google/go-flow-levee/pkg/levee" 19 | "golang.org/x/tools/go/analysis/singlechecker" 20 | ) 21 | 22 | func main() { 23 | singlechecker.Main(levee.Analyzer) 24 | } 25 | -------------------------------------------------------------------------------- /internal/pkg/utils/testdata/src/fields/regular/test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package regular 16 | 17 | import "fmt" 18 | 19 | type foo struct { 20 | name string 21 | } 22 | 23 | func f() { 24 | bar := &foo{name: "n"} 25 | fmt.Printf("%v", bar) 26 | } 27 | -------------------------------------------------------------------------------- /guides/quickstart/quickstart.go: -------------------------------------------------------------------------------- 1 | // full path: github.com/google/go-flow-levee/guides/quickstart 2 | package quickstart 3 | 4 | import "log" 5 | 6 | type Authentication struct { 7 | Username string 8 | Password string `datapolicy:"password"` 9 | } 10 | 11 | func authenticate(auth Authentication) (*AuthenticationResponse, error) { 12 | response, err := makeAuthenticationRequest(auth) 13 | if err != nil { 14 | log.Printf("unable to make authenticated request: incorrect authentication? %v", auth) 15 | return nil, err 16 | } 17 | return response, nil 18 | } 19 | 20 | // just a stub, to allow the code to compile 21 | type AuthenticationResponse struct{} 22 | 23 | // just a stub, to allow the code to compile 24 | func makeAuthenticationRequest(Authentication) (*AuthenticationResponse, error) { return nil, nil } 25 | 26 | //lint:file-ignore U1000 ignore unused functions 27 | -------------------------------------------------------------------------------- /internal/pkg/utils/testdata/src/dereference/pointer_to_struct/pointer_to_struct.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package pointer_to_struct 16 | 17 | import "fmt" 18 | 19 | type foo struct { 20 | } 21 | 22 | func f() { 23 | bar := &foo{} 24 | fmt.Printf("%v", bar) 25 | } 26 | -------------------------------------------------------------------------------- /internal/pkg/sourceinfer/testdata/src/example.com/source/source.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package source 16 | 17 | type Source struct{} 18 | 19 | type Tagged struct { // want Tagged:"inferred source" 20 | secret string `levee:"source"` 21 | } 22 | 23 | type NotSource struct{} 24 | -------------------------------------------------------------------------------- /internal/pkg/source/testdata/src/analyzertest/test-config.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | --- 15 | Sources: 16 | - PackageRE: "" 17 | TypeRE: "^Source$" 18 | FieldRE: "^Data" 19 | # The below cannot actually be a Source, because it is an interface type 20 | - PackageRE: "" 21 | Type: "SourceInterface" 22 | -------------------------------------------------------------------------------- /internal/pkg/suppression/analyzer_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package suppression 16 | 17 | import ( 18 | "testing" 19 | 20 | "golang.org/x/tools/go/analysis/analysistest" 21 | ) 22 | 23 | func TestAnalyzer(t *testing.T) { 24 | analysistest.Run(t, analysistest.TestData(), Analyzer, "./...") 25 | } 26 | -------------------------------------------------------------------------------- /internal/pkg/utils/testdata/src/fields/embedded/test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package embedded 16 | 17 | import "fmt" 18 | 19 | type foo struct { 20 | name string 21 | } 22 | 23 | type bar struct { 24 | *foo 25 | } 26 | 27 | func f() { 28 | bar := &bar{foo: &foo{}} 29 | fmt.Printf("%v", bar) 30 | } 31 | -------------------------------------------------------------------------------- /internal/pkg/fieldtags/testdata/src/fieldtags_analysistest/crosspkg/crosspkg.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package crosspkg 16 | 17 | import "fieldtags_analysistest/core" 18 | 19 | type PersonWrapper struct { 20 | p core.Person 21 | crossField string `levee:"source"` // want crossField:"tagged field" 22 | } 23 | -------------------------------------------------------------------------------- /internal/pkg/levee/testdata/allowpanicontaintedvalues-config.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | --- 15 | AllowPanicOnTaintedValues: true 16 | Sources: 17 | - Package: "levee_analysistest/nopanic.com/core" 18 | Type: "Source" 19 | Field: "Data" 20 | Sinks: 21 | - Package: "levee_analysistest/nopanic.com/core" 22 | Method: "Sink" 23 | -------------------------------------------------------------------------------- /internal/pkg/sourceinfer/testdata/src/example.com/tests/crosspkg/test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package infer 16 | 17 | import ( 18 | "example.com/tests/core" 19 | ) 20 | 21 | type ( 22 | RedefinedSource core.DefinedSource // want RedefinedSource:"inferred source" 23 | ) 24 | 25 | type ( 26 | NotRedefinedSource core.NotDefinedSource 27 | ) 28 | -------------------------------------------------------------------------------- /internal/pkg/levee/testdata/src/levee_analysistest/example/core/sanitize.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package core 16 | 17 | func Sanitize(args ...interface{}) []interface{} { 18 | return args 19 | } 20 | 21 | func SanitizeSource(s Source) Source { 22 | return Source{ID: s.ID} 23 | } 24 | 25 | func SanitizePtr(s *Source) { 26 | s.Data = "" 27 | } 28 | -------------------------------------------------------------------------------- /internal/pkg/fieldpropagator/testdata/test-config.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | --- 15 | Sources: 16 | - PackageRE: source 17 | TypeRE: "^Source$" 18 | FieldRE: "^data" 19 | - PackageRE: proto 20 | Type: KeyPair 21 | Field: Private 22 | - PackageRE: proto 23 | Type: TLS 24 | Field: Cert 25 | - PackageRE: proto 26 | Type: BasicAuth 27 | Field: Password 28 | -------------------------------------------------------------------------------- /internal/pkg/levee/testdata/src/levee_analysistest/example/tests/booleans/tests.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package inlining 16 | 17 | import ( 18 | "levee_analysistest/example/core" 19 | ) 20 | 21 | func HasSecret(s core.Source) bool { 22 | return s.Data != "" 23 | } 24 | 25 | func TestDoNotTraverseToBoolean(s core.Source) { 26 | ok := HasSecret(s) 27 | core.Sink(ok) 28 | } 29 | -------------------------------------------------------------------------------- /internal/pkg/config/testdata/src/config_analysistest/example/notcore/notcore.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package notcore 16 | 17 | func Sink() {} 18 | 19 | func NotSink() {} 20 | 21 | type Sinker struct{} 22 | 23 | func (s Sinker) Do() {} 24 | 25 | func (s Sinker) DoNot() {} 26 | 27 | func Calls() { 28 | Sink() 29 | NotSink() 30 | s := Sinker{} 31 | s.Do() 32 | s.DoNot() 33 | } 34 | -------------------------------------------------------------------------------- /internal/pkg/levee/testdata/src/levee_analysistest/example/tests/recover/tests.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package recover 16 | 17 | import ( 18 | "levee_analysistest/example/core" 19 | ) 20 | 21 | func TestSinkInRecoverBlock(source core.Source) { 22 | defer func() { 23 | if r := recover(); r != nil { 24 | core.Sink(source) // want "a source has reached a sink" 25 | } 26 | }() 27 | } 28 | -------------------------------------------------------------------------------- /internal/pkg/levee/testdata/src/levee_analysistest/custom.message.com/nocustom/nocustom.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package nocustom 16 | 17 | type Source struct { 18 | Data string 19 | ID int 20 | } 21 | 22 | func Sink(interface{}) {} 23 | 24 | func TestReportMessage() { 25 | s := Source{Data: "password", ID: 1337} 26 | Sink(s) // want "^a source has reached a sink\n source: .*custom.go:25:2$" 27 | } 28 | -------------------------------------------------------------------------------- /internal/pkg/levee/testdata/src/levee_analysistest/example/tests/store/tests.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package store 16 | 17 | import ( 18 | "levee_analysistest/example/core" 19 | ) 20 | 21 | func TestStoringToTaintedAddrDoesNotTaintStoredValue() { 22 | myChan := make(chan string) 23 | s := core.Source{Data: "foo"} 24 | recv := <-myChan 25 | s.Data = recv 26 | core.Sink(recv) 27 | core.Sink(myChan) 28 | } 29 | -------------------------------------------------------------------------------- /internal/pkg/levee/testdata/src/levee_analysistest/example/tests/typealias/tests.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package typealias 16 | 17 | import ( 18 | "levee_analysistest/example/core" 19 | ) 20 | 21 | type Foo core.Source 22 | 23 | type Bar = core.Source 24 | 25 | func TestTypeDefinition() { 26 | core.Sink(Foo{}) 27 | } 28 | 29 | func TestTypeAlias() { 30 | core.Sink(Bar{}) // want "a source has reached a sink" 31 | } 32 | -------------------------------------------------------------------------------- /internal/pkg/config/testdata/test-config.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | --- 15 | sinks: 16 | - package: "config_analysistest/example/core" 17 | method: "Sink" 18 | - package: "config_analysistest/example/core" 19 | method: "Do" 20 | receiver: "Sinker" 21 | fieldTags: 22 | - key: example 23 | value: sensitive 24 | exclude: 25 | - package: "config_analysistest/example/exclusion" 26 | method: "Foo" 27 | - package: "config_analysistest/notexample/exclusion" 28 | -------------------------------------------------------------------------------- /internal/pkg/levee/testdata/src/levee_analysistest/custom.message.com/withcustom/withcustom.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package nocustom 16 | 17 | type Source struct { 18 | Data string 19 | ID int 20 | } 21 | 22 | func Sink(interface{}) {} 23 | 24 | func TestReportMessage() { 25 | s := Source{Data: "password", ID: 1337} 26 | Sink(s) // want "^a source has reached a sink\n source: .*custom.go:25:2\n This custom message is included with report.$" 27 | } 28 | -------------------------------------------------------------------------------- /internal/pkg/levee/testdata/src/levee_analysistest/example/tests/propagation/functions.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package propagation 16 | 17 | import ( 18 | "errors" 19 | "levee_analysistest/example/core" 20 | "os" 21 | ) 22 | 23 | func TestPropagationViaFunctionReturningBool(s core.Source, err *os.PathError) { 24 | if ok := errors.As(errors.New(s.Data), err); !ok { 25 | core.Sinkf("not a PathError: %v", err) // want "a source has reached a sink" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /internal/pkg/levee/testdata/src/levee_analysistest/example/tests/eface/tests.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package eface 16 | 17 | import ( 18 | "levee_analysistest/example/core" 19 | ) 20 | 21 | func TestEfaceSource(s core.Source) { 22 | var ss interface{} = s 23 | core.Sink(ss) // want "a source has reached a sink" 24 | } 25 | 26 | func TestEfaceSourcePointer(s *core.Source) { 27 | var ss interface{} = s 28 | core.Sink(ss) // want "a source has reached a sink" 29 | } 30 | -------------------------------------------------------------------------------- /internal/pkg/debug/render/testdata/TestSingleBlock.ssa: -------------------------------------------------------------------------------- 1 | func TestSingleBlock() 2 | 0: entry 3 | 0(*ssa.Alloc ): t0 = local image.Point (p) 4 | 1(*ssa.FieldAddr ): t1 = &t0.X [#0] 5 | 2(*ssa.FieldAddr ): t2 = &t0.Y [#1] 6 | 3(*ssa.Store ): *t1 = 1:int 7 | 4(*ssa.Store ): *t2 = 2:int 8 | 5(*ssa.FieldAddr ): t3 = &t0.X [#0] 9 | 6(*ssa.Store ): *t3 = 3:int 10 | 7(*ssa.FieldAddr ): t4 = &t0.Y [#1] 11 | 8(*ssa.Store ): *t4 = 4:int 12 | 9(*ssa.FieldAddr ): t5 = &t0.X [#0] 13 | 10(*ssa.UnOp ): t6 = *t5 14 | 11(*ssa.FieldAddr ): t7 = &t0.Y [#1] 15 | 12(*ssa.UnOp ): t8 = *t7 16 | 13(*ssa.BinOp ): t9 = t6 + t8 17 | 14(*ssa.Alloc ): t10 = new [1]interface{} (varargs) 18 | 15(*ssa.IndexAddr ): t11 = &t10[0:int] 19 | 16(*ssa.MakeInterface ): t12 = make interface{} <- int (t9) 20 | 17(*ssa.Store ): *t11 = t12 21 | 18(*ssa.Slice ): t13 = slice t10[:] 22 | 19(*ssa.Call ): t14 = fmt.Println(t13...) 23 | 20(*ssa.Return ): return 24 | -------------------------------------------------------------------------------- /internal/pkg/sourcetype/testdata/test_stackoverflow.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package test 16 | 17 | type SelfReferential map[string]SelfReferential 18 | 19 | func selfReferential(s SelfReferential) { 20 | } 21 | 22 | type CoReferentialA CoReferentialB 23 | type CoReferentialB *CoReferentialA 24 | 25 | func coReferential(c CoReferentialA) { 26 | } 27 | 28 | type DeepSelfReferential [1][]chan DeepSelfReferential 29 | 30 | func deepSelfReferential(d DeepSelfReferential) { 31 | } 32 | -------------------------------------------------------------------------------- /internal/pkg/levee/testdata/src/levee_analysistest/example/tests/basictypes/tests.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package basic 16 | 17 | import ( 18 | "levee_analysistest/example/core" 19 | ) 20 | 21 | func BooleansDontPropagateTaint(s *core.Source) { 22 | is := isSourcey(s) 23 | core.Sink(is) 24 | } 25 | 26 | func isSourcey(x interface{}) bool { 27 | // not taking any chances 28 | return true 29 | } 30 | 31 | func IntegersDontPropagateTaint(sources []core.Source) { 32 | core.Sink(len(sources)) 33 | } 34 | -------------------------------------------------------------------------------- /internal/pkg/levee/testdata/src/levee_analysistest/nopanic.com/core/core.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package core 16 | 17 | type Source struct { 18 | Data string 19 | ID int 20 | } 21 | 22 | func Sink(...interface{}) {} 23 | 24 | func TestTaintedValuesAreNotAllowedToReachSinks() { 25 | s := Source{Data: "password", ID: 1337} 26 | Sink(s) // want "a source has reached a sink" 27 | } 28 | 29 | func TestPanickingOnTaintedValueIsAllowed() { 30 | s := Source{Data: "password", ID: 1337} 31 | panic(s) 32 | } 33 | -------------------------------------------------------------------------------- /internal/pkg/levee/testdata/src/levee_analysistest/example/core/sink.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package core 16 | 17 | import "io" 18 | 19 | type Sinker struct{} 20 | 21 | func (s Sinker) Sink(args ...interface{}) {} 22 | 23 | func Sink(args ...interface{}) {} 24 | 25 | func SinkAndReturn(args ...interface{}) []interface{} { 26 | return args 27 | } 28 | 29 | func Sinkf(format string, args ...interface{}) {} 30 | 31 | func FSinkf(writer io.Writer, args ...interface{}) {} 32 | 33 | func OneArgSink(interface{}) {} 34 | -------------------------------------------------------------------------------- /internal/pkg/levee/testdata/src/levee_analysistest/example/tests/declarations/tests.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package declarations contains test-cases for testing PII leak detection when sources are introduced via declarations. 16 | package declarations 17 | 18 | import ( 19 | "levee_analysistest/example/core" 20 | ) 21 | 22 | func TestSourceDeclaredInBody() { 23 | s := &core.Source{} 24 | core.Sinkf("%v", s) // want "a source has reached a sink" 25 | 26 | i := &core.Innocuous{} 27 | core.Sinkf("%v", i) 28 | } 29 | -------------------------------------------------------------------------------- /internal/pkg/source/analyzer_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package source 16 | 17 | import ( 18 | "testing" 19 | 20 | "github.com/google/go-flow-levee/internal/pkg/config" 21 | "golang.org/x/tools/go/analysis/analysistest" 22 | ) 23 | 24 | func TestSourceAnalysis(t *testing.T) { 25 | testdata := analysistest.TestData() 26 | if err := config.FlagSet.Set("config", testdata+"/src/analyzertest/test-config.yaml"); err != nil { 27 | t.Error(err) 28 | } 29 | 30 | analysistest.Run(t, testdata, Analyzer, "analyzertest/sourcetest") 31 | } 32 | -------------------------------------------------------------------------------- /internal/pkg/suppression/testdata/src/suppression_analysistest/comments/comments.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package comments 16 | 17 | func TestSuppressionComments() { 18 | // levee.DoNotReport 19 | println() // want "suppressed" 20 | 21 | println() 22 | 23 | // This call has a non-suppressing comment 24 | // associated with it 25 | println() 26 | 27 | /* 28 | This comment is suppressing too. 29 | levee.DoNotReport 30 | */ 31 | println() // want "suppressed" 32 | 33 | println() // levee.DoNotReport // want "suppressed" 34 | } 35 | -------------------------------------------------------------------------------- /internal/pkg/sourceinfer/analyzer_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package infer 16 | 17 | import ( 18 | "path/filepath" 19 | "testing" 20 | 21 | "github.com/google/go-flow-levee/internal/pkg/config" 22 | "golang.org/x/tools/go/analysis/analysistest" 23 | ) 24 | 25 | func TestInferAnalysis(t *testing.T) { 26 | testdata := analysistest.TestData() 27 | 28 | if err := config.FlagSet.Set("config", filepath.Join(testdata, "test-config.yaml")); err != nil { 29 | t.Error(err) 30 | } 31 | 32 | analysistest.Run(t, testdata, Analyzer, "./...") 33 | } 34 | -------------------------------------------------------------------------------- /internal/pkg/config/testdata/src/config_analysistest/example/core/core.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package core 16 | 17 | func Sink() {} // want "sink" 18 | 19 | func NotSink() {} 20 | 21 | type Sinker struct{} 22 | 23 | func (s Sinker) Do() {} // want "sink" 24 | 25 | func (s Sinker) DoNot() {} 26 | 27 | type NotSinker struct{} 28 | 29 | func (ns NotSinker) Do() {} 30 | 31 | func Calls() { 32 | Sink() // want "sink call" 33 | NotSink() 34 | s := Sinker{} 35 | s.Do() // want "sink call" 36 | s.DoNot() 37 | ns := NotSinker{} 38 | ns.Do() 39 | } 40 | -------------------------------------------------------------------------------- /internal/pkg/levee/testdata/src/levee_analysistest/example/tests/closures/tests.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package closures 16 | 17 | import ( 18 | "levee_analysistest/example/core" 19 | ) 20 | 21 | func TesCapturedSourceReachesSinkInClosure() func() { 22 | s := &core.Source{} 23 | return func() { 24 | core.Sinkf("%v", s) // want "a source has reached a sink" 25 | } 26 | } 27 | 28 | func TestSourceReachesSinkInClosure() func() { 29 | return func() { 30 | s := &core.Source{} 31 | core.Sinkf("%v", s) // want "a source has reached a sink" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /internal/pkg/fieldpropagator/analyzer_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package fieldpropagator 16 | 17 | import ( 18 | "path/filepath" 19 | "testing" 20 | 21 | "github.com/google/go-flow-levee/internal/pkg/config" 22 | "golang.org/x/tools/go/analysis/analysistest" 23 | ) 24 | 25 | func TestFieldPropagatorAnalysis(t *testing.T) { 26 | testdata := analysistest.TestData() 27 | if err := config.FlagSet.Set("config", filepath.Join(testdata, "test-config.yaml")); err != nil { 28 | t.Error(err) 29 | } 30 | analysistest.Run(t, testdata, Analyzer, "./...") 31 | } 32 | -------------------------------------------------------------------------------- /internal/pkg/levee/testdata/src/levee_analysistest/example/tests/binop/tests.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package binop 16 | 17 | import ( 18 | "fmt" 19 | 20 | "levee_analysistest/example/core" 21 | ) 22 | 23 | func TestConcatenatingTaintedAndNonTaintedStrings(prefix string) { 24 | s := core.Source{Data: "password1234"} 25 | message := fmt.Sprintf("source: %v", s) 26 | fullMessage := prefix + message 27 | core.Sink(prefix) 28 | core.Sink(message) // want "a source has reached a sink" 29 | core.Sink(fullMessage) // want "a source has reached a sink" 30 | } 31 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/false-negative.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: False negative 3 | about: Report a false negative. 4 | labels: false negative 5 | 6 | --- 7 | 8 | ## False negative report 9 | 10 | Use this issue template to describe a situation where the analyzer failed to recognize that a piece of unsafe code is unsafe. For example, if the analyzer did not produce a report on the following piece of code, then that would be a false negative: 11 | 12 | ```go 13 | func Oops() { 14 | Sink(Source{"sensitive data"}) 15 | } 16 | ``` 17 | (We are assuming that `Source` has been configured as a source and `Sink` has been configured as a sink.) 18 | 19 | **Describe the issue** 20 | Please include a clear and concise description of what happened and why you think it is a false negative. 21 | 22 | **To Reproduce** 23 | Please make it as easy as possible for us to reproduce what you observed. If possible, provide the exact configuration and code on which the analyzer failed to produce a report. If the code cannot be shared, please provide a simplified example and confirm that it also contains the false negative. 24 | 25 | **Additional context** 26 | Add any other context about the problem here. 27 | -------------------------------------------------------------------------------- /internal/pkg/source/testdata/src/analyzertest/sourcetest/source.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package sourcetest 16 | 17 | // source container 18 | type Source struct { 19 | Data string // source field 20 | ID int // public 21 | } 22 | 23 | func CreateSource() (Source, error) { 24 | return Source{}, nil // want "source identified" 25 | } 26 | 27 | func NewSource() (*Source, error) { 28 | return &Source{}, nil // want "source identified" 29 | } 30 | 31 | type TaggedSource struct { 32 | Data string `levee:"source"` 33 | ID int 34 | } 35 | 36 | type SourceInterface interface{} 37 | -------------------------------------------------------------------------------- /hack/verify-kubernetes.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -ex 4 | 5 | install_and_run_levee_against_k8s_from_branch() { 6 | local branch_name=$1 7 | git checkout "$branch_name" 8 | local levee_output 9 | levee_output="$(mktemp -d)/levee" 10 | go build -o "${levee_output}" "${LEVEE_DIR}/cmd/levee" 11 | local cfg_path 12 | cfg_path="$(realpath "${K8S_SRC_PATH}"/hack/testdata/levee/levee-config.yaml)" 13 | make -C "${K8S_SRC_PATH}" clean 14 | FINDINGS=$(mktemp) 15 | set +e 16 | make -C "${K8S_SRC_PATH}" vet WHAT="-vettool=$levee_output -config=$cfg_path" 2>"${FINDINGS}" 17 | set -e 18 | } 19 | 20 | LEVEE_DIR="$(dirname "$(dirname "$(realpath "$0")")")" 21 | K8S_SRC_PATH="$(go env GOPATH)/src/k8s.io/kubernetes" 22 | FEATURE_BRANCH=$(git branch --show-current) 23 | 24 | if [[ "${FEATURE_BRANCH}" == "master" ]]; then 25 | echo "Please run from feature branch" 26 | exit 1 27 | fi 28 | 29 | install_and_run_levee_against_k8s_from_branch "master" 30 | MASTER_FINDINGS=$FINDINGS 31 | 32 | install_and_run_levee_against_k8s_from_branch "$FEATURE_BRANCH" 33 | FEATURE_BRANCH_FINDINGS=$FINDINGS 34 | 35 | diff <(sort "${FEATURE_BRANCH_FINDINGS}") <(sort "${MASTER_FINDINGS}") 36 | -------------------------------------------------------------------------------- /internal/pkg/config/set_config_bytes_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package config 16 | 17 | import ( 18 | "testing" 19 | 20 | "github.com/google/go-cmp/cmp" 21 | ) 22 | 23 | func TestSetConfigBytes(t *testing.T) { 24 | set := &Config{ReportMessage: "test"} 25 | bytes := []byte(`ReportMessage: "test"`) 26 | 27 | SetBytes(bytes) 28 | 29 | read, err := ReadConfig() 30 | if err != nil { 31 | t.Fatalf("ReadConfig returned an unexpected error: %v", err) 32 | } 33 | 34 | if diff := cmp.Diff(set, read); diff != "" { 35 | t.Errorf("set config differs from read config (-set, +read):\n%s", diff) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /internal/pkg/levee/testdata/src/levee_analysistest/ear/tests/call/callee-src.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package call 16 | 17 | import ( 18 | "levee_analysistest/example/core" 19 | ) 20 | 21 | func sinkf1(i interface{}) { 22 | core.Sink(i) // want "a source has reached a sink" 23 | } 24 | 25 | func f1(s string) { 26 | sinkf1(s) 27 | } 28 | 29 | func createData() string { 30 | src := &core.Source{} 31 | return src.Data 32 | } 33 | 34 | // Test the case where the source is introduced in callees. 35 | func TestSrcInCallees() { 36 | s := createData() 37 | core.Sink(s) // want "a source has reached a sink" 38 | f1(s) 39 | } 40 | -------------------------------------------------------------------------------- /configuration/example-config.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2020 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | --- 15 | Sources: 16 | - TypeRE: "^Secret$|Token" 17 | - PackageRE: "k8s.io/client-go/tools/clientcmd/api(?:/v1)?" 18 | TypeRE: "^(?:Named)?AuthInfo$" 19 | FieldRE: "" 20 | - PackageRE: "^k8s.io/client-go/rest$" 21 | TypeRE: "^TLSClientConfig$" 22 | FieldRE: "Password|BearerToken$|" 23 | - PackageRE: "^k8s.io/client-go/rest$" 24 | TypeRE: "^Config$" 25 | FieldRE: "Password|BearerToken$|" 26 | Sinks: 27 | - PackageRE: "log" 28 | ReceiverRE: "" 29 | MethodRE: "Info|Warning|Error|Fatal|Exit" 30 | Sanitizers: [] 31 | Exclude: 32 | - PackageRE: "^k8s.io/kubernetes/test" 33 | -------------------------------------------------------------------------------- /internal/pkg/levee/testdata/src/levee_analysistest/example/tests/go/tests.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package callcommon contains tests for Defer and Go instructions, 16 | // which should be treated similarly to Call instructions. 17 | package callcommon 18 | 19 | import ( 20 | "fmt" 21 | "io" 22 | "levee_analysistest/example/core" 23 | ) 24 | 25 | func TestGoStdlib(w io.Writer, s core.Source) { 26 | go fmt.Fprint(w, s.Data) 27 | core.Sink(w) // want "a source has reached a sink" 28 | } 29 | 30 | func TestGoUnknownFunction(i *core.Innocuous, s core.Source) { 31 | go baz(i, s) 32 | core.Sink(i) 33 | } 34 | 35 | func baz(a, b interface{}) {} 36 | -------------------------------------------------------------------------------- /internal/pkg/sourceinfer/testdata/src/example.com/tests/core/mixed.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package core 16 | 17 | import ( 18 | "example.com/source" 19 | ) 20 | 21 | type SourceByDefinition source.Source // want SourceByDefinition:"inferred source" 22 | 23 | type SourceByDefinitionHolder struct { // want SourceByDefinitionHolder:"inferred source" 24 | sbd SourceByDefinition 25 | } 26 | 27 | type SourceByDefinitionHolderAlias SourceByDefinitionHolder // want SourceByDefinitionHolderAlias:"inferred source" 28 | 29 | type SourceByDefinitionHolderAliasHolder struct { // want SourceByDefinitionHolderAliasHolder:"inferred source" 30 | sbdha SourceByDefinitionHolderAlias 31 | } 32 | -------------------------------------------------------------------------------- /internal/pkg/config/testdata/src/config_analysistest/example/crosspkg/crosspkg.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package crosspkg 16 | 17 | import ( 18 | "config_analysistest/example/core" 19 | "config_analysistest/example/notcore" 20 | necore "config_analysistest/notexample/core" 21 | ) 22 | 23 | func CoreCalls() { 24 | core.Sink() // want "sink call" 25 | core.NotSink() 26 | s := core.Sinker{} 27 | s.Do() // want "sink call" 28 | s.DoNot() 29 | } 30 | 31 | func NotCoreCalls() { 32 | notcore.Sink() 33 | notcore.NotSink() 34 | s := notcore.Sinker{} 35 | s.Do() 36 | s.DoNot() 37 | } 38 | 39 | func NotExampleComCalls() { 40 | necore.Sink() 41 | s := necore.Sinker{} 42 | s.Do() 43 | } 44 | -------------------------------------------------------------------------------- /internal/pkg/levee/testdata/src/levee_analysistest/example/tests/excludedpackage/tests.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package excludedpackage 16 | 17 | import ( 18 | "levee_analysistest/example/core" 19 | ) 20 | 21 | func Oops(s core.Source) { 22 | core.Sink(s) // we do not expect a report here, because this package is excluded from analysis 23 | } 24 | 25 | func OopsIDidItAgain(s core.Source) { 26 | core.Sink(s) // we do not expect a report here, because this package is excluded from analysis 27 | } 28 | 29 | type Receiver int 30 | 31 | func (Receiver) OopsWithReceiver(s core.Source) { 32 | core.Sink(s) // we do not expect a report here, because this package is excluded from analysis 33 | } 34 | -------------------------------------------------------------------------------- /internal/pkg/levee/testdata/src/levee_analysistest/example/tests/namedreturn/tests.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package namedreturn 16 | 17 | import ( 18 | "levee_analysistest/example/core" 19 | ) 20 | 21 | func TestNamedReturnValue() (s core.Source) { 22 | core.Sink(s) // want "a source has reached a sink" 23 | return 24 | } 25 | 26 | func TestNamedReturnValuePointer() (s *core.Source) { 27 | core.Sink(s) // we do not report here, because no Source value has been created, so s must be nil 28 | return 29 | } 30 | 31 | func TestNamedReturnValuePointerWithInitialization() (s *core.Source) { 32 | s = &core.Source{} 33 | core.Sink(s) // want "a source has reached a sink" 34 | return 35 | } 36 | -------------------------------------------------------------------------------- /internal/pkg/levee/testdata/src/levee_analysistest/example/tests/colocation/tests.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package colocation 16 | 17 | import ( 18 | "encoding/json" 19 | 20 | "levee_analysistest/example/core" 21 | ) 22 | 23 | // CVE-2020-8564 24 | func TestTaintIsPropagatedToDataBeingUnmarshalled(contents []byte) (src core.Source, err error) { 25 | if err = json.Unmarshal(contents, &src); err != nil { 26 | core.Sink(src) // want "a source has reached a sink" 27 | core.Sink(contents) // want "a source has reached a sink" 28 | return 29 | } 30 | core.Sink(src) // want "a source has reached a sink" 31 | core.Sink(contents) // want "a source has reached a sink" 32 | return 33 | } 34 | -------------------------------------------------------------------------------- /internal/pkg/levee/testdata/src/levee_analysistest/example/tests/callorder/singleblock.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package callorder 16 | 17 | import ( 18 | "fmt" 19 | "io" 20 | 21 | "levee_analysistest/example/core" 22 | ) 23 | 24 | func TestTaintBeforeSinking(s core.Source, w io.Writer) { 25 | _, _ = fmt.Fprintf(w, "%v", s) 26 | core.Sink(w) // want "a source has reached a sink" 27 | } 28 | 29 | func TestSinkBeforeTainting(s core.Source, w io.Writer) { 30 | core.Sink(w) 31 | _, _ = fmt.Fprintf(w, "%v", s) 32 | } 33 | 34 | func TestSinkBeforeAndAfterTainting(s core.Source, w io.Writer) { 35 | core.Sink(w) 36 | _, _ = fmt.Fprintf(w, "%v", s) 37 | core.Sink(w) // want "a source has reached a sink" 38 | } 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/false-positive.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: False positive 3 | about: Report a false positive. 4 | labels: false positive 5 | 6 | --- 7 | 8 | ## False positive report 9 | 10 | Use this issue template to describe a false positive report produced by the analyzer. A false positive report is a report produced by the analyzer on code that you consider to be safe. For example, if the analyzer were to produce a report on the following piece of code, then that would be a false positive: 11 | 12 | ```go 13 | func NothingWrongHere() { 14 | Sink(Safe{"not a secret"}) // we don't want a report here, since the value that reached the sink is not sensitive 15 | } 16 | ``` 17 | (We are assuming that `Sink` has been configured as a sink and that `Safe` has *not* been configured as a source.) 18 | 19 | **Describe the issue** 20 | Please include a clear and concise description of what happened and why you think it is a false positive. 21 | 22 | **To Reproduce** 23 | Please make it as easy as possible for us to reproduce what you observed. If possible, provide the exact configuration and code on which the analyzer failed to produce a report. If the code cannot be shared, please provide a simplified example and confirm that it also yields the false positive report. 24 | 25 | **Additional context** 26 | Add any other context about the problem here. 27 | -------------------------------------------------------------------------------- /internal/pkg/levee/testdata/src/levee_analysistest/example/tests/pointers/tests.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package pointers 16 | 17 | import ( 18 | "levee_analysistest/example/core" 19 | ) 20 | 21 | func TestNew() { 22 | s := new(core.Source) 23 | core.Sink(s) // want "a source has reached a sink" 24 | } 25 | 26 | func TestDoublePointer(s **core.Source) { 27 | core.Sink(s) // want "a source has reached a sink" 28 | core.Sink(*s) // want "a source has reached a sink" 29 | core.Sink(**s) // want "a source has reached a sink" 30 | } 31 | 32 | func TestDoubleReference(s core.Source) { 33 | ref := &s 34 | core.Sink(ref) // want "a source has reached a sink" 35 | refRef := &ref 36 | core.Sink(refRef) // want "a source has reached a sink" 37 | } 38 | -------------------------------------------------------------------------------- /internal/pkg/levee/testdata/test-ear-config.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | --- 15 | Sources: 16 | - Package: "levee_analysistest/example/core" 17 | Type: "Source" 18 | FieldRE: "^Data" 19 | - Package: "levee_analysistest/example/core" 20 | Type: "SourceManipulator" 21 | Sinks: 22 | - Package: "levee_analysistest/example/core" 23 | MethodRE: Sinkf?$ 24 | - Package: "levee_analysistest/example/core" 25 | Method: SinkAndReturn 26 | Exclude: 27 | - Package: "levee_analysistest/example/tests/excludedpackage" 28 | - Package: "levee_analysistest/example/tests/includedpackage" 29 | Method: "Oops" 30 | - Package: "levee_analysistest/example/tests/includedpackage" 31 | Method: "OopsWithReceiverExcluded" 32 | Receiver: "Receiver" 33 | UseEAR: true 34 | EARTaintCallSpan: 8 35 | -------------------------------------------------------------------------------- /internal/pkg/levee/testdata/src/levee_analysistest/example/tests/phi/tests.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package phi 16 | 17 | import ( 18 | "levee_analysistest/example/core" 19 | ) 20 | 21 | func TestPhiNodeDoesntPropagateTaintToOperands(i *core.Innocuous) { 22 | for { 23 | s := core.Source{} 24 | var ss, ii interface{} = s, i 25 | if true { 26 | ss = ii 27 | } 28 | core.Sink(ii) 29 | core.Sink(i) 30 | core.Sink(ss) // want "a source has reached a sink" 31 | } 32 | } 33 | 34 | func TestPhiNodeIsTaintedByTaintedOperand(s core.Source, i *core.Innocuous) { 35 | var ss, ii interface{} = s, i 36 | if true { 37 | ii = ss 38 | } 39 | core.Sink(ss) // want "a source has reached a sink" 40 | core.Sink(ii) // want "a source has reached a sink" 41 | core.Sink(i) 42 | } 43 | -------------------------------------------------------------------------------- /internal/pkg/debug/debug.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package debug 16 | 17 | import ( 18 | "github.com/google/go-flow-levee/internal/pkg/debug/dump" 19 | "golang.org/x/tools/go/analysis" 20 | "golang.org/x/tools/go/analysis/passes/buildssa" 21 | ) 22 | 23 | var Analyzer = &analysis.Analyzer{ 24 | Name: "debug", 25 | Run: run, 26 | Doc: "dumps SSA and DOT source for every function", 27 | Requires: []*analysis.Analyzer{buildssa.Analyzer}, 28 | } 29 | 30 | func run(pass *analysis.Pass) (interface{}, error) { 31 | ssaInput := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA) 32 | 33 | for _, f := range ssaInput.SrcFuncs { 34 | pkgName := f.Pkg.Pkg.Name() 35 | dump.SSA(pkgName, f) 36 | dump.DOT(pkgName, f) 37 | dump.CFG(pkgName, f) 38 | } 39 | 40 | return nil, nil 41 | } 42 | -------------------------------------------------------------------------------- /internal/pkg/levee/testdata/src/levee_analysistest/example/tests/includedpackage/tests.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package includedpackage 16 | 17 | import ( 18 | "levee_analysistest/example/core" 19 | ) 20 | 21 | func Oops(s core.Source) { 22 | core.Sink(s) // we do not expect a report here, because this specific function is excluded from analysis 23 | } 24 | 25 | func OopsIDidItAgain(s core.Source) { 26 | core.Sink(s) // want "a source has reached a sink" 27 | } 28 | 29 | type Receiver int 30 | 31 | func (Receiver) OopsWithReceiver(s core.Source) { 32 | core.Sink(s) // want "a source has reached a sink" 33 | } 34 | 35 | func (Receiver) OopsWithReceiverExcluded(s core.Source) { 36 | core.Sink(s) // we do not expect a report here, because this specific function is excluded from analysis 37 | } 38 | -------------------------------------------------------------------------------- /internal/pkg/levee/testdata/src/levee_analysistest/ear/tests/call/callee-src-sink.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package call 16 | 17 | import ( 18 | "levee_analysistest/example/core" 19 | ) 20 | 21 | func sinkf2(i interface{}) { 22 | core.Sink(i) // want "a source has reached a sink" 23 | } 24 | 25 | func createSource2() interface{} { 26 | return &core.Source{} 27 | } 28 | 29 | func identity(arg interface{}) interface{} { 30 | return arg 31 | } 32 | 33 | // Test the case where: 34 | // (1) the sink function doesn't embed the source function; and 35 | // (2) the source function doesn't embed the sink function. 36 | func TestSrcSinkInDifferentCallees() { 37 | // The source is created in a callee. 38 | s := createSource2() 39 | i := identity(s) 40 | // The sink is within another callee. 41 | sinkf2(i) 42 | } 43 | -------------------------------------------------------------------------------- /internal/pkg/levee/testdata/src/levee_analysistest/example/tests/panic/tests.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package panic 16 | 17 | import ( 18 | "levee_analysistest/example/core" 19 | ) 20 | 21 | func TestPanicIsASink(source core.Source) { 22 | panic(source) // want "a source has reached a sink" 23 | } 24 | 25 | func TestPanicIsASinkSanitized(source core.Source) { 26 | sanitized := core.Sanitize(source)[0] 27 | panic(sanitized) 28 | } 29 | 30 | func TestGoPanicIsASink(source core.Source) { 31 | go panic(source) // TODO(#231): want "a source has reached a sink" 32 | } 33 | 34 | func TestDeferPanicIsASink(source core.Source) { 35 | defer panic(source) // TODO(#231): want "a source has reached a sink" 36 | } 37 | 38 | func TestPanicOnNonSourceDoesNotProduceReport(source core.Source) { 39 | y := 42 40 | panic(y) 41 | } 42 | -------------------------------------------------------------------------------- /internal/pkg/levee/testdata/test-config.yaml: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Google LLC 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # https://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | --- 15 | Sources: 16 | - Package: "levee_analysistest/example/core" 17 | Type: "Source" 18 | FieldRE: "^Data" 19 | - Package: "levee_analysistest/example/core" 20 | Type: "SourceManipulator" 21 | Sinks: 22 | - Package: "levee_analysistest/example/core" 23 | MethodRE: Sinkf?$ 24 | - Package: "levee_analysistest/example/core" 25 | Method: SinkAndReturn 26 | Sanitizers: 27 | - Package: "levee_analysistest/example/core" 28 | MethodRE: "^Sanitize" 29 | Exclude: 30 | - Package: "levee_analysistest/example/tests/excludedpackage" 31 | - Package: "levee_analysistest/example/tests/includedpackage" 32 | Method: "Oops" 33 | - Package: "levee_analysistest/example/tests/includedpackage" 34 | Method: "OopsWithReceiverExcluded" 35 | Receiver: "Receiver" 36 | -------------------------------------------------------------------------------- /internal/pkg/levee/testdata/src/levee_analysistest/example/tests/receivers/tests.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package receivers contains test-cases for testing PII leak detection when sources are introduced via receivers. 16 | package receivers 17 | 18 | import ( 19 | "levee_analysistest/example/core" 20 | ) 21 | 22 | type SourceBuilder struct { 23 | sourcePtr *core.Source 24 | sourceVal core.Source 25 | } 26 | 27 | func (b *SourceBuilder) buildP() { 28 | core.Sinkf("Building cluster %v", b.sourcePtr) // want "a source has reached a sink" 29 | core.Sinkf("Building cluster %v", b.sourceVal) // want "a source has reached a sink" 30 | } 31 | 32 | func (b SourceBuilder) buildV() { 33 | core.Sinkf("Building cluster %v", b.sourcePtr) // want "a source has reached a sink" 34 | core.Sinkf("Building cluster %v", b.sourceVal) // want "a source has reached a sink" 35 | } 36 | -------------------------------------------------------------------------------- /internal/pkg/levee/testdata/src/levee_analysistest/example/tests/typeassert/tests.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package typeassert 16 | 17 | import ( 18 | "levee_analysistest/example/core" 19 | ) 20 | 21 | func TestSourceAssertedFromTaintedEface(s core.Source) { 22 | var e interface{} = s 23 | core.Sink(e.(core.Source)) // want "a source has reached a sink" 24 | } 25 | 26 | func TestSourcePointerAssertedFromTaintedEface(s *core.Source) { 27 | var e interface{} = s 28 | core.Sink(e.(*core.Source)) // want "a source has reached a sink" 29 | } 30 | 31 | func TestSourcePointerAssertedFromParameterEface(e interface{}) { 32 | s := e.(*core.Source) 33 | core.Sink(s) // want "a source has reached a sink" 34 | } 35 | 36 | func TestSourcePointerAssertedFromParameterEfaceCommaOk(e interface{}) { 37 | s, ok := e.(*core.Source) 38 | core.Sink(s) // want "a source has reached a sink" 39 | core.Sink(ok) 40 | } 41 | -------------------------------------------------------------------------------- /internal/pkg/debug/render/testdata/tests.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package main 16 | 17 | import ( 18 | "fmt" 19 | "image" 20 | ) 21 | 22 | func TestSingleBlock() { 23 | p := image.Point{1, 2} 24 | p.X = 3 25 | p.Y = 4 26 | fmt.Println(p.X + p.Y) 27 | } 28 | 29 | func TestMultiBlock() { 30 | p := image.Point{1, 2} 31 | if p.X > 0 { 32 | if p.Y > 0 { 33 | fmt.Printf("in top right quadrant, at (%d, %d)\n", p.X, p.Y) 34 | } 35 | } else { 36 | fmt.Println("somewhere") 37 | } 38 | } 39 | 40 | func TestParams(a, b int, c string) { 41 | fmt.Println(a, b, c) 42 | } 43 | 44 | func TestClosure() { 45 | x := 0 46 | f := func(x int) { 47 | fmt.Println(x) 48 | } 49 | f(x) 50 | } 51 | 52 | func TestDisconnected() { 53 | x, y := 1, 2 54 | for i := 0; i < x*y; i++ { 55 | i-- 56 | } 57 | 58 | prefix := "error: " 59 | message := "unreachable code" 60 | fmt.Printf(prefix + message) 61 | } 62 | -------------------------------------------------------------------------------- /internal/pkg/sourceinfer/testdata/src/example.com/tests/core/recursive.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package core 16 | 17 | import ( 18 | "example.com/source" 19 | ) 20 | 21 | type ( 22 | A struct { // want A:"inferred source" 23 | source.Source 24 | b *B 25 | } 26 | 27 | B struct { // want B:"inferred source" 28 | a *A 29 | } 30 | ) 31 | 32 | type ( 33 | C struct { // want C:"inferred source" 34 | source.Source 35 | d *D 36 | } 37 | 38 | D struct { // want D:"inferred source" 39 | e *E 40 | } 41 | 42 | E struct { // want E:"inferred source" 43 | c *C 44 | } 45 | ) 46 | 47 | type ( 48 | F struct { 49 | g *G 50 | } 51 | 52 | G struct { 53 | f *F 54 | } 55 | ) 56 | 57 | type SelfRecursive struct { 58 | *SelfRecursive 59 | } 60 | 61 | type SelfRecursiveWithSource struct { // want SelfRecursiveWithSource:"inferred source" 62 | *SelfRecursiveWithSource 63 | s source.Source 64 | } 65 | -------------------------------------------------------------------------------- /internal/pkg/levee/testdata/src/levee_analysistest/example/tests/callorder/colocation.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package callorder 16 | 17 | import ( 18 | "fmt" 19 | "io" 20 | "levee_analysistest/example/core" 21 | ) 22 | 23 | func TestTaintedColocatedArgumentDoesNotReachSinkThatPrecedesColocation(w io.Writer, src core.Source) { 24 | if true { 25 | core.Sink(w) 26 | } 27 | fmt.Fprint(w, src) 28 | } 29 | 30 | func TestTaintedColocatedArgumentReachesSinkThatFollowsColocation(w io.Writer, src core.Source) { 31 | if _, err := fmt.Fprint(w, src); err != nil { 32 | core.Sink(w) // want "a source has reached a sink" 33 | } 34 | } 35 | 36 | func TestAvoidingIncorrectPropagationFromColocationDoesNotPreventCorrectReport(w io.Writer, src core.Source) { 37 | _, err := fmt.Fprint(w, src) 38 | if err != nil { 39 | core.Sink(w) // want "a source has reached a sink" 40 | } 41 | 42 | if true { 43 | fmt.Fprint(w, src) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /internal/pkg/sanitizer/testdata/tests.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package dominance contains test-cases for testing PII sanitization. 16 | package tests 17 | 18 | import ( 19 | "log" 20 | "time" 21 | ) 22 | 23 | func dominatedSameBlock() { 24 | pwd := "password" 25 | pwd = scrub(pwd) 26 | log.Print(pwd) // want "dominated" 27 | } 28 | 29 | func notDominatedSameBlock() { 30 | pwd := "password" 31 | log.Print(pwd) 32 | pwd = scrub(pwd) 33 | } 34 | 35 | func dominatedDifferentBlocks() { 36 | pwd := "password" 37 | pwd = scrub(pwd) 38 | if time.Now().Weekday() == time.Monday { 39 | log.Print(pwd) // want "dominated" 40 | } 41 | } 42 | 43 | func notDominatedDifferentBlocks() { 44 | pwd := "P@ssword1" 45 | if time.Now().Weekday() == time.Monday { 46 | pwd = scrub(pwd) 47 | log.Print(pwd) // want "dominated" 48 | } 49 | 50 | log.Print(pwd) 51 | } 52 | 53 | func scrub(in string) string { 54 | return "scrubbed" 55 | } 56 | -------------------------------------------------------------------------------- /internal/pkg/debug/render/cfg.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package render 16 | 17 | import ( 18 | "fmt" 19 | "strings" 20 | 21 | "golang.org/x/tools/go/ssa" 22 | ) 23 | 24 | // CFG renders DOT source representing the function's control flow graph (CFG). 25 | func CFG(f *ssa.Function) string { 26 | blockIndex := map[*ssa.BasicBlock]int{} 27 | for i, b := range f.Blocks { 28 | blockIndex[b] = i 29 | } 30 | 31 | var b strings.Builder 32 | b.WriteString("digraph {\n") 33 | for _, block := range f.Blocks { 34 | b.WriteString(fmt.Sprintf("\t%q\n", blockLabel(block, blockIndex))) 35 | for _, succ := range block.Succs { 36 | b.WriteString(fmt.Sprintf("\t%q -> %q;\n", blockLabel(block, blockIndex), blockLabel(succ, blockIndex))) 37 | } 38 | } 39 | b.WriteString("}") 40 | return b.String() 41 | } 42 | 43 | func blockLabel(b *ssa.BasicBlock, blockIndex map[*ssa.BasicBlock]int) string { 44 | return fmt.Sprintf("%d %s", blockIndex[b], b.Comment) 45 | } 46 | -------------------------------------------------------------------------------- /GIT_WORKFLOW.md: -------------------------------------------------------------------------------- 1 | ## Git Workflow 2 | 1. Fork in the cloud 3 | 1. Visit https://github.com/google/go-flow-levee 4 | 2. Click `Fork` button (top right) to establish a cloud-based fork. 5 | 6 | 2. Clone fork to local storage 7 | 8 | ```bash 9 | cd $(go env GOPATH)/src/ 10 | git clone https://github.com//go-flow-levee.git 11 | 12 | cd go-flow-levee 13 | git remote add upstream https://github.com/google/go-flow-levee.git 14 | 15 | # Never push to upstream master 16 | git remote set-url --push upstream no_push 17 | 18 | # Confirm that your remotes make sense: 19 | git remote -v 20 | ``` 21 | 22 | 3. Branch 23 | 24 | Get your local master up to date: 25 | 26 | ```bash 27 | cd $(go env GOPATH)/src/go-flow-levee 28 | git fetch upstream 29 | git checkout master 30 | git merge upstream/master 31 | ``` 32 | 33 | Branch from it: 34 | ```bash 35 | git checkout -b featurebranch 36 | ``` 37 | 38 | 4. Keep your branch in sync 39 | ```bash 40 | git fetch upstream 41 | git merge upstream/master 42 | ``` 43 | 44 | 5. Pushing your commits 45 | ```bash 46 | git push origin featurebranch 47 | ``` 48 | 49 | **Note:** Please avoid force-pushing, as it can break links to commits and cause GitHub to lose track of comment threads. 50 | 51 | 6. Create a Pull Request 52 | 1. Go to https://github.com/google/go-flow-levee 53 | 2. Click on the "Compare & pull request" button. 54 | 55 | **Note:** When merging your PR, please use a squash merge. 56 | -------------------------------------------------------------------------------- /internal/pkg/debug/node/node.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package node contains utility functions for working with SSA nodes. 16 | package node 17 | 18 | import ( 19 | "fmt" 20 | "strings" 21 | 22 | "golang.org/x/tools/go/ssa" 23 | ) 24 | 25 | // CanonicalName produces a canonical string representation for an SSA node. 26 | func CanonicalName(n ssa.Node) string { 27 | value, isValue := n.(ssa.Value) 28 | instr, isInstr := n.(ssa.Instruction) 29 | switch { 30 | case isValue && isInstr: 31 | return fmt.Sprintf("%s = %s", value.Name(), instr.String()) 32 | case isValue: 33 | return value.Name() 34 | case isInstr: 35 | return instr.String() 36 | default: 37 | member, isMember := n.(ssa.Member) 38 | if !isMember { 39 | return "" 40 | } 41 | return member.Name() 42 | } 43 | } 44 | 45 | // TrimmedType returns the type of a node without the "*.ssa" prefix. 46 | func TrimmedType(n ssa.Node) string { 47 | t := fmt.Sprintf("%T", n) 48 | return strings.TrimPrefix(t, "*ssa.") 49 | } 50 | -------------------------------------------------------------------------------- /internal/pkg/fieldtags/testdata/src/fieldtags_analysistest/core/core.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package core 16 | 17 | type Person struct { 18 | password, creds string `levee:"source"` // want password:"tagged field" creds:"tagged field" 19 | secret string `json:"secret" levee:"source"` // want secret:"tagged field" 20 | another interface{} "levee:\"source\"" // want another:"tagged field" 21 | hasCustomFieldTag string `example:"sensitive"` // want hasCustomFieldTag:"tagged field" 22 | hasTagWithMultipleValues string `example:"val,sensitive,long"` // want hasTagWithMultipleValues:"tagged field" 23 | name string `some_key:"non_secret"` 24 | spaceAfterFinalQuote string `key:"value" ` 25 | someNotTaggedField int 26 | } 27 | 28 | type Nester struct { 29 | Nested struct { 30 | adminSecret string `levee:"source"` // want adminSecret:"tagged field" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /pkg/levee/levee.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package levee exports the levee Analyzer. 16 | package levee 17 | 18 | import ( 19 | "github.com/google/go-flow-levee/internal/pkg/config" 20 | "github.com/google/go-flow-levee/internal/pkg/levee" 21 | "github.com/google/go-flow-levee/internal/pkg/propagation/summary" 22 | ) 23 | 24 | // Analyzer reports instances of source data reaching a sink. 25 | var Analyzer = levee.Analyzer 26 | 27 | // SetBytes is a wrapper around the config package's SetBytes function. 28 | var SetBytes = config.SetBytes 29 | 30 | // Summary is a wrapper around the propagation/summary 31 | // package's Summary type. 32 | type Summary = summary.Summary 33 | 34 | // FuncSummaries is a wrapper around the propagation/summary 35 | // package's map of regular function summaries. 36 | var FuncSummaries = summary.FuncSummaries 37 | 38 | // InterfaceFuncSummaries is a wrapper around the propagation/summary 39 | // package's map of interface function summaries. 40 | var InterfaceFuncSummaries = summary.InterfaceFuncSummaries 41 | -------------------------------------------------------------------------------- /internal/pkg/levee/testdata/src/levee_analysistest/example/tests/collections/chans.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package collections 16 | 17 | import ( 18 | "levee_analysistest/example/core" 19 | ) 20 | 21 | func TestSourceReceivedFromChannelIsTainted(sources <-chan core.Source) { 22 | s := <-sources 23 | core.Sink(s) // want "a source has reached a sink" 24 | } 25 | 26 | func TestChannelIsTaintedWhenSourceIsPlacedOnIt(sources chan<- core.Source) { 27 | sources <- core.Source{} 28 | core.Sink(sources) // want "a source has reached a sink" 29 | } 30 | 31 | func TestValueObtainedFromTaintedChannelIsTainted(c chan interface{}) { 32 | c <- core.Source{} 33 | s := <-c 34 | core.Sink(s) // want "a source has reached a sink" 35 | } 36 | 37 | func TestChannelIsNoLongerTaintedWhenNilledOut(sources chan core.Source) { 38 | sources <- core.Source{} 39 | sources = nil 40 | core.Sink(sources) 41 | } 42 | 43 | func TestRangeOverChan(sources chan core.Source) { 44 | for s := range sources { 45 | core.Sink(s) // want "a source has reached a sink" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /internal/pkg/config/regexp/regexp_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package regexp 16 | 17 | import ( 18 | "encoding/json" 19 | "testing" 20 | ) 21 | 22 | func TestFlagParser(t *testing.T) { 23 | testCases := []struct { 24 | desc string 25 | in []byte 26 | wantMatch string 27 | wantErr bool 28 | }{ 29 | { 30 | desc: "valid regex", 31 | in: []byte(`"^hello$"`), 32 | wantMatch: "hello", 33 | }, 34 | { 35 | desc: "empty regex", 36 | in: []byte(""), 37 | wantErr: true, 38 | }, 39 | { 40 | desc: "invalid regex", 41 | in: []byte("["), 42 | wantErr: true, 43 | }, 44 | } 45 | 46 | for _, tt := range testCases { 47 | t.Run(tt.desc, func(t *testing.T) { 48 | got := &Regexp{} 49 | err := json.Unmarshal(tt.in, &got) 50 | 51 | if tt.wantErr && err == nil { 52 | t.Fatalf("Got nil, wanted error %v", tt.wantErr) 53 | } 54 | 55 | if !got.MatchString(tt.wantMatch) { 56 | t.Fatalf("Got false, wanted true for got.MatchStrng(%s)", tt.wantMatch) 57 | } 58 | }) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /internal/pkg/levee/testdata/src/levee_analysistest/example/core/source.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package core 16 | 17 | // Source will be configured to be detected as a source struct, with Source.Data as the source field. 18 | type Source struct { 19 | Data string 20 | ID int 21 | } 22 | 23 | func (s Source) GetID() int { 24 | return s.ID 25 | } 26 | 27 | func (s Source) GetData() string { 28 | return s.Data 29 | } 30 | 31 | func (s Source) ShowData() string { 32 | return "Data: " + s.Data 33 | } 34 | 35 | func (s Source) Copy() (Source, error) { 36 | return s, nil 37 | } 38 | 39 | func (s Source) CopyPointer() (*Source, error) { 40 | return &s, nil 41 | } 42 | 43 | type TaggedSource struct { 44 | Data string `levee:"source"` 45 | ID int 46 | } 47 | 48 | // Innocuous will _not_ be configured to be a source, even though underlying types are equal. 49 | type Innocuous struct { 50 | Data string 51 | ID int 52 | } 53 | 54 | func (i Innocuous) GetID() int { 55 | return i.ID 56 | } 57 | 58 | func (i Innocuous) GetData() string { 59 | return i.Data 60 | } 61 | -------------------------------------------------------------------------------- /internal/pkg/config/regexp/regexp.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package regexp contains functionality for unmarshalling regular expressions from a config. 16 | package regexp 17 | 18 | import ( 19 | "encoding/json" 20 | "regexp" 21 | ) 22 | 23 | // Regexp delegates to a Regexp while enabling unmarshalling. 24 | // Any unspecified / nil matcher will return vacuous truth in MatchString 25 | type Regexp struct { 26 | r *regexp.Regexp 27 | } 28 | 29 | func New(s string) (*Regexp, error) { 30 | r, err := regexp.Compile(s) 31 | return &Regexp{r: r}, err 32 | } 33 | 34 | // MatchString delegates matching to the regex package. 35 | func (mr *Regexp) MatchString(s string) bool { 36 | return mr.r == nil || mr.r.MatchString(s) 37 | } 38 | 39 | // UnmarshalJSON implementation of json.UnmarshalJSON interface. 40 | func (mr *Regexp) UnmarshalJSON(data []byte) error { 41 | var matcher string 42 | if err := json.Unmarshal(data, &matcher); err != nil { 43 | return err 44 | } 45 | 46 | var err error 47 | if mr.r, err = regexp.Compile(matcher); err != nil { 48 | return err 49 | } 50 | return nil 51 | } 52 | -------------------------------------------------------------------------------- /internal/pkg/levee/testdata/src/levee_analysistest/example/tests/arguments/tests.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package arguments 16 | 17 | import ( 18 | "levee_analysistest/example/core" 19 | ) 20 | 21 | func TestSourceFromParamByReference(s *core.Source) { 22 | core.Sink("Source in the parameter %v", s) // want "a source has reached a sink" 23 | } 24 | 25 | func TestSourceMethodFromParamByReference(s *core.Source) { 26 | core.Sink("Source in the parameter %v", s.Data) // want "a source has reached a sink" 27 | } 28 | 29 | func TestSourceFromParamByReferenceInfo(s *core.Source) { 30 | core.Sink(s) // want "a source has reached a sink" 31 | } 32 | 33 | func TestSourceFromParamByValue(s core.Source) { 34 | core.Sink("Source in the parameter %v", s) // want "a source has reached a sink" 35 | } 36 | 37 | func TestUpdatedSource(s *core.Source) { 38 | s.Data = "updated" 39 | core.Sink("Updated %v", s) // want "a source has reached a sink" 40 | } 41 | 42 | func TestSourceFromAPointerCopy(s *core.Source) { 43 | cp := s 44 | core.Sink("Pointer copy of the source %v", cp) // want "a source has reached a sink" 45 | } 46 | -------------------------------------------------------------------------------- /internal/pkg/levee/testdata/src/levee_analysistest/example/tests/inlining/tests.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package inlining 16 | 17 | import ( 18 | "levee_analysistest/example/core" 19 | ) 20 | 21 | func NewSource() *core.Source { 22 | return &core.Source{} 23 | } 24 | 25 | func MaybeSource() (core.Source, error) { 26 | return core.Source{}, nil 27 | } 28 | 29 | func MaybeSourcePtr() (*core.Source, error) { 30 | return &core.Source{}, nil 31 | } 32 | 33 | func TestInlinedCall() { 34 | core.Sink(NewSource()) // want "a source has reached a sink" 35 | } 36 | 37 | func TestInlinedTupleCall() { 38 | core.Sink(MaybeSource()) // want "a source has reached a sink" 39 | core.Sink(MaybeSourcePtr()) // want "a source has reached a sink" 40 | } 41 | 42 | func TestInlinedRecv(sources <-chan core.Source) { 43 | core.Sink(<-sources) // want "a source has reached a sink" 44 | } 45 | 46 | func TestInlinedArrayIndex(sources [1]core.Source) { 47 | core.Sink(sources[0]) // want "a source has reached a sink" 48 | } 49 | 50 | func TestInlinedMapKey(sources map[string]core.Source) { 51 | core.Sink(sources["source"]) // want "a source has reached a sink" 52 | } 53 | -------------------------------------------------------------------------------- /internal/pkg/levee/testdata/src/levee_analysistest/example/tests/embedding/tests.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package embedding 16 | 17 | import ( 18 | "levee_analysistest/example/core" 19 | ) 20 | 21 | type EmbedsSource struct { 22 | core.Source 23 | } 24 | 25 | type EmbedsSourcePointer struct { 26 | *core.Source 27 | } 28 | 29 | func TestStructThatEmbedsSourceIsSource() { 30 | core.Sink(EmbedsSource{}) // TODO(#96) want "a source has reached a sink" 31 | } 32 | 33 | func TestStructThatEmbedsSourcePointerIsSource() { 34 | core.Sink(EmbedsSourcePointer{}) // TODO(#96) want "a source has reached a sink" 35 | } 36 | 37 | func TestEmbeddedSourceIsSource() { 38 | core.Sink(EmbedsSource{}.Source) // want "a source has reached a sink" 39 | } 40 | 41 | func TestEmbeddedSourcePointerIsSource() { 42 | core.Sink(EmbedsSource{}.Source) // want "a source has reached a sink" 43 | } 44 | 45 | func TestEmbeddedSourceFieldIsSourceField() { 46 | core.Sink(EmbedsSource{}.Data) // want "a source has reached a sink" 47 | } 48 | 49 | func TestEmbeddedSourcePointerFieldIsSourceField() { 50 | core.Sink(EmbedsSourcePointer{}.Data) // want "a source has reached a sink" 51 | } 52 | -------------------------------------------------------------------------------- /internal/pkg/sourcetype/sourcetype_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package sourcetype 16 | 17 | import ( 18 | "testing" 19 | 20 | "github.com/google/go-flow-levee/internal/pkg/config" 21 | "github.com/google/go-flow-levee/internal/pkg/fieldtags" 22 | "golang.org/x/tools/go/analysis" 23 | "golang.org/x/tools/go/analysis/analysistest" 24 | "golang.org/x/tools/go/analysis/passes/buildssa" 25 | ) 26 | 27 | var Analyzer = &analysis.Analyzer{ 28 | Name: "sourcetype_test", 29 | Doc: "This analyzer is used to test the sourcetype package.", 30 | Run: run, 31 | Requires: []*analysis.Analyzer{buildssa.Analyzer}, 32 | } 33 | 34 | func run(pass *analysis.Pass) (interface{}, error) { 35 | ssaInput := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA) 36 | 37 | c := &config.Config{} 38 | tf := make(fieldtags.ResultType) 39 | 40 | for _, fn := range ssaInput.SrcFuncs { 41 | for _, p := range fn.Params { 42 | _ = IsSourceType(c, tf, p.Type()) 43 | } 44 | } 45 | 46 | return nil, nil 47 | } 48 | 49 | func TestSourceTypeDoesNotStackOverflow(t *testing.T) { 50 | testdata := analysistest.TestData() 51 | analysistest.Run(t, testdata, Analyzer, "./...") 52 | } 53 | -------------------------------------------------------------------------------- /internal/pkg/levee/testdata/src/levee_analysistest/example/tests/collections/arrays.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package collections 16 | 17 | import ( 18 | "levee_analysistest/example/core" 19 | ) 20 | 21 | func TestArrayLiteralContainingSourceIsTainted(s core.Source) { 22 | tainted := [1]core.Source{s} 23 | core.Sink(tainted) // want "a source has reached a sink" 24 | } 25 | 26 | func TestArrayIsTaintedWhenSourceIsInserted(s core.Source) { 27 | arr := [2]interface{}{nil, nil} 28 | arr[0] = s 29 | core.Sink(arr) // want "a source has reached a sink" 30 | } 31 | 32 | func TestValueObtainedFromTaintedArrayIsTainted(s core.Source) { 33 | arr := [2]interface{}{nil, nil} 34 | arr[0] = s 35 | core.Sink(arr[1]) // want "a source has reached a sink" 36 | } 37 | 38 | func TestArrayRemainsTaintedWhenSourceIsOverwritten(s core.Source) { 39 | arr := [2]interface{}{s, nil} 40 | arr[0] = nil 41 | core.Sink(arr) // want "a source has reached a sink" 42 | } 43 | 44 | func TestRangeOverArray() { 45 | sources := [1]core.Source{core.Source{Data: "password1234"}} 46 | for i, s := range sources { 47 | core.Sink(s) // want "a source has reached a sink" 48 | core.Sink(i) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /internal/pkg/propagation/summary/testdata/src/summary_analysistest/tests/test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package test 16 | 17 | import ( 18 | "bytes" 19 | "encoding/json" 20 | "fmt" 21 | "io" 22 | ) 23 | 24 | func testStaticFuncName(w io.Writer) { 25 | // positive cases 26 | var b bytes.Buffer 27 | b.Write(nil) // want `^\Q(*bytes.Buffer).Write\E$` 28 | fmt.Println() // want "^fmt.Println$" 29 | json.Unmarshal(nil, &b) // want "^encoding/json.Unmarshal$" 30 | 31 | // negative cases 32 | w.Write(nil) // want "^$" 33 | println() // want "^$" 34 | } 35 | 36 | func testFuncNameWithoutReceiver(w io.Writer) { 37 | // positive cases 38 | var b bytes.Buffer 39 | b.Write(nil) // want "^Write$" 40 | w.Write(nil) // want "^Write$" 41 | 42 | // negative cases 43 | fmt.Println() // want "^$" 44 | println() // want "^$" 45 | } 46 | 47 | func testFuncSignature(slice *[]*interface{}, m map[foo]string, r io.Reader) (err error, oops bool) { // want `^\Q(*[]*interface{},map[foo]string,Reader)(error,bool)\E$` 48 | return nil, false 49 | } 50 | 51 | func (f *foo) testMethodSignature(i int, d float64) *foo { // want `^\Q(int,float64)(*foo)\E$` 52 | return f 53 | } 54 | 55 | type foo struct{} 56 | -------------------------------------------------------------------------------- /internal/pkg/levee/testdata/src/levee_analysistest/example/tests/stdlib/interface_funcs.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package stdlib 16 | 17 | import ( 18 | "context" 19 | "io" 20 | "levee_analysistest/example/core" 21 | "strings" 22 | ) 23 | 24 | func TestPropagateToAndFromConcreteReceiver(b *strings.Builder, s core.Source) { 25 | b.WriteString(s.Data) 26 | core.Sink(b) // want "a source has reached a sink" 27 | core.Sink(b.String()) // want "a source has reached a sink" 28 | } 29 | 30 | func TestPropagateToAndFromAbstractReceiver(w io.ReadWriter, b []byte, s core.Source) { 31 | w.Write([]byte(s.Data)) 32 | w.Read(b) 33 | core.Sink(w) // want "a source has reached a sink" 34 | core.Sink(b) // want "a source has reached a sink" 35 | } 36 | 37 | func TestPropagationInvolvingFuncWithInterfaceParameter(rf io.ReaderFrom, s core.Source) { 38 | rf.ReadFrom(strings.NewReader(s.Data)) 39 | core.Sink(rf) // want "a source has reached a sink" 40 | } 41 | 42 | func TestPropagateThroughContext(c context.Context, s core.Source) { 43 | cc := context.WithValue(c, "data", s.Data) 44 | core.Sink(cc.Err()) // want "a source has reached a sink" 45 | core.Sink(cc.Value("data")) // want "a source has reached a sink" 46 | } 47 | -------------------------------------------------------------------------------- /internal/pkg/debug/render/testdata/TestMultiBlock.ssa: -------------------------------------------------------------------------------- 1 | func TestMultiBlock() 2 | 0: entry 3 | 0(*ssa.Alloc ): t0 = local image.Point (p) 4 | 1(*ssa.FieldAddr ): t1 = &t0.X [#0] 5 | 2(*ssa.FieldAddr ): t2 = &t0.Y [#1] 6 | 3(*ssa.Store ): *t1 = 1:int 7 | 4(*ssa.Store ): *t2 = 2:int 8 | 5(*ssa.FieldAddr ): t3 = &t0.X [#0] 9 | 6(*ssa.UnOp ): t4 = *t3 10 | 7(*ssa.BinOp ): t5 = t4 > 0:int 11 | 8(*ssa.If ): if t5 goto 1 else 3 12 | 1: if.then 13 | 0(*ssa.FieldAddr ): t6 = &t0.Y [#1] 14 | 1(*ssa.UnOp ): t7 = *t6 15 | 2(*ssa.BinOp ): t8 = t7 > 0:int 16 | 3(*ssa.If ): if t8 goto 4 else 2 17 | 2: if.then 18 | 0(*ssa.FieldAddr ): t14 = &t0.X [#0] 19 | 1(*ssa.UnOp ): t15 = *t14 20 | 2(*ssa.FieldAddr ): t16 = &t0.Y [#1] 21 | 3(*ssa.UnOp ): t17 = *t16 22 | 4(*ssa.Alloc ): t18 = new [2]interface{} (varargs) 23 | 5(*ssa.IndexAddr ): t19 = &t18[0:int] 24 | 6(*ssa.MakeInterface ): t20 = make interface{} <- int (t15) 25 | 7(*ssa.Store ): *t19 = t20 26 | 8(*ssa.IndexAddr ): t21 = &t18[1:int] 27 | 9(*ssa.MakeInterface ): t22 = make interface{} <- int (t17) 28 | 10(*ssa.Store ): *t21 = t22 29 | 11(*ssa.Slice ): t23 = slice t18[:] 30 | 12(*ssa.Call ): t24 = fmt.Printf("in top right quad...":string, t23...) 31 | 13(*ssa.Jump ): jump 2 32 | 3: if.done 33 | 0(*ssa.Return ): return 34 | 4: if.else 35 | 0(*ssa.Alloc ): t9 = new [1]interface{} (varargs) 36 | 1(*ssa.IndexAddr ): t10 = &t9[0:int] 37 | 2(*ssa.MakeInterface ): t11 = make interface{} <- string ("somewhere":string) 38 | 3(*ssa.Store ): *t10 = t11 39 | 4(*ssa.Slice ): t12 = slice t9[:] 40 | 5(*ssa.Call ): t13 = fmt.Println(t12...) 41 | 6(*ssa.Jump ): jump 2 42 | -------------------------------------------------------------------------------- /internal/pkg/levee/testdata/src/levee_analysistest/example/tests/callorder/beforesource.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package callorder 16 | 17 | import ( 18 | "levee_analysistest/example/core" 19 | ) 20 | 21 | // This type should *not* be identified as a Source. 22 | type key struct { 23 | name string 24 | } 25 | 26 | func (k *key) Name() string { 27 | return k.name 28 | } 29 | 30 | func newKey() *key { 31 | return &key{ 32 | name: "", 33 | } 34 | } 35 | 36 | func TestDoesNotReachSinkAfterSourceThroughValueCreatedBeforeSource() { 37 | // Taint should not propagate to this value. 38 | k := newKey() 39 | 40 | _ = map[string]core.Source{}[k.name] 41 | 42 | core.Sink(k.Name()) 43 | } 44 | 45 | func TestDoesNotReachSinkInIfBeforeSourceThroughValueCreatedBeforeSource() { 46 | // Taint should not propagate to this value. 47 | k := newKey() 48 | 49 | if true { 50 | core.Sink(k.Name()) 51 | } 52 | 53 | _ = map[string]core.Source{}[k.name] 54 | } 55 | 56 | func TestValueDeclaredBeforeSourceIsTainted() { 57 | var x interface{} = core.Innocuous{} 58 | x = core.Source{} 59 | core.Sink(x) // want "a source has reached a sink" 60 | } 61 | 62 | func TestSliceDeclaredBeforeSourceIsTainted() { 63 | xs := []interface{}{nil} 64 | xs[0] = core.Source{} 65 | core.Sink(xs) // want "a source has reached a sink" 66 | } 67 | -------------------------------------------------------------------------------- /internal/pkg/sanitizer/sanitizer.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package sanitizer contains the logic responsible for determining whether sources are sanitized 16 | // before they are being sent to sinks. 17 | package sanitizer 18 | 19 | import ( 20 | "math" 21 | 22 | "golang.org/x/tools/go/ssa" 23 | ) 24 | 25 | // Sanitizer removes the taint. 26 | type Sanitizer struct { 27 | // Call is the underlying call that performs sanitization 28 | Call *ssa.Call 29 | } 30 | 31 | // Dominates returns true if the Sanitizer dominates the supplied instruction. 32 | // In the context of SSA, domination implies that 33 | // if instructions X executes and X dominates Y, then Y is guaranteed to execute and to be 34 | // executed after X. 35 | func (s Sanitizer) Dominates(target ssa.Instruction) bool { 36 | if s.Call.Parent() != target.Parent() { 37 | // Instructions are in different functions. 38 | return false 39 | } 40 | 41 | if s.Call.Block() == target.Block() { 42 | sanitizationIdx := math.MaxInt64 43 | targetIdx := 0 44 | for i, instr := range s.Call.Block().Instrs { 45 | if instr == s.Call { 46 | sanitizationIdx = i 47 | } 48 | if instr == target { 49 | targetIdx = i 50 | break 51 | } 52 | } 53 | return sanitizationIdx < targetIdx 54 | } 55 | 56 | return s.Call.Block().Dominates(target.Block()) 57 | } 58 | -------------------------------------------------------------------------------- /internal/pkg/fieldpropagator/testdata/src/fieldpropagator_analysistest/proto/proto.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package proto 16 | 17 | type KeyPair struct { 18 | Public []byte 19 | Private []byte 20 | } 21 | 22 | func (kp *KeyPair) GetPublic() []byte { 23 | if kp != nil { 24 | return kp.Public 25 | } 26 | return nil 27 | } 28 | 29 | func (kp *KeyPair) GetPrivate() []byte { // want GetPrivate:"field propagator identified" 30 | if kp != nil { 31 | return kp.Private 32 | } 33 | return nil 34 | } 35 | 36 | type Certificate struct{} 37 | 38 | type TLS struct { 39 | Flag *bool 40 | Cert *Certificate 41 | } 42 | 43 | func (t *TLS) GetFlag() bool { 44 | if t != nil && t.Flag != nil { 45 | return *t.Flag 46 | } 47 | return false 48 | } 49 | 50 | func (t *TLS) GetCert() *Certificate { // want GetCert:"field propagator identified" 51 | if t != nil { 52 | return t.Cert 53 | } 54 | return nil 55 | } 56 | 57 | type BasicAuth struct { 58 | Username, Password *string 59 | } 60 | 61 | func (ba *BasicAuth) GetUsername() string { 62 | if ba != nil && ba.Username != nil { 63 | return *ba.Username 64 | } 65 | return "" 66 | } 67 | 68 | func (ba *BasicAuth) GetPassword() string { // want GetPassword:"field propagator identified" 69 | if ba != nil && ba.Password != nil { 70 | return *ba.Password 71 | } 72 | return "" 73 | } 74 | -------------------------------------------------------------------------------- /internal/pkg/sourceinfer/testdata/src/example.com/tests/core/typedef.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package core 16 | 17 | import ( 18 | "example.com/source" 19 | ) 20 | 21 | type ( 22 | DefinedSource source.Source // want DefinedSource:"inferred source" 23 | RedefinedSource DefinedSource // want RedefinedSource:"inferred source" 24 | SourcePointer *source.Source // want SourcePointer:"inferred source" 25 | SourceSlice []source.Source // want SourceSlice:"inferred source" 26 | SourceMap map[string]source.Source // want SourceMap:"inferred source" 27 | SourceNested map[string][]map[string][]source.Source // want SourceNested:"inferred source" 28 | DefinedSourceSlice []DefinedSource // want DefinedSourceSlice:"inferred source" 29 | DefinedFromTagged source.Tagged // want DefinedFromTagged:"inferred source" 30 | ) 31 | 32 | type ( 33 | NotDefinedSource source.NotSource 34 | FunctionsAreNotSources func() map[string]source.Source 35 | ) 36 | 37 | func Typedef() { 38 | type DefinedInFunc source.Source // want DefinedInFunc:"inferred source" 39 | } 40 | 41 | func TypedefAnonymousFunc() { 42 | _ = func() { 43 | type DefinedInAnonymousFunc source.Source // want DefinedInAnonymousFunc:"inferred source" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /internal/pkg/debug/render/ssa.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package render 16 | 17 | import ( 18 | "fmt" 19 | "strconv" 20 | "strings" 21 | 22 | "github.com/google/go-flow-levee/internal/pkg/debug/node" 23 | "golang.org/x/tools/go/ssa" 24 | ) 25 | 26 | // SSA produces a human-readable representation of the SSA code for a function. 27 | func SSA(f *ssa.Function) string { 28 | var b strings.Builder 29 | 30 | b.WriteString("func ") 31 | b.WriteString(f.Name()) 32 | b.WriteString(strings.TrimPrefix(f.Signature.String(), "func")) 33 | b.WriteByte('\n') 34 | 35 | maxBlockLength := 1 36 | for _, blk := range f.Blocks { 37 | length := len(strconv.Itoa(len(blk.Instrs))) 38 | if length > maxBlockLength { 39 | maxBlockLength = length 40 | } 41 | } 42 | 43 | for i, blk := range f.DomPreorder() { 44 | renderBlock(&b, i, blk, maxBlockLength) 45 | } 46 | 47 | for _, af := range f.AnonFuncs { 48 | b.WriteString(SSA(af)) 49 | } 50 | 51 | return b.String() 52 | } 53 | 54 | func renderBlock(b *strings.Builder, i int, blk *ssa.BasicBlock, instructionIndexWidth int) { 55 | b.WriteString(fmt.Sprintf("%d: %s\n", i, blk.Comment)) 56 | for j, instr := range blk.Instrs { 57 | s := node.CanonicalName(instr.(ssa.Node)) 58 | b.WriteByte('\t') 59 | b.WriteString(fmt.Sprintf("%"+strconv.Itoa(instructionIndexWidth)+"d", j)) 60 | b.WriteString(fmt.Sprintf("(%-20T): ", instr)) 61 | b.WriteString(s) 62 | b.WriteByte('\n') 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /internal/pkg/sanitizer/sanitizer_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package sanitizer 16 | 17 | import ( 18 | "testing" 19 | 20 | "golang.org/x/tools/go/analysis" 21 | "golang.org/x/tools/go/analysis/analysistest" 22 | "golang.org/x/tools/go/analysis/passes/buildssa" 23 | "golang.org/x/tools/go/ssa" 24 | ) 25 | 26 | var testAnalyzer = &analysis.Analyzer{ 27 | Name: "domination", 28 | Run: run, 29 | Doc: "test harness for domination logic", 30 | Requires: []*analysis.Analyzer{buildssa.Analyzer}, 31 | } 32 | 33 | func run(pass *analysis.Pass) (interface{}, error) { 34 | in := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA) 35 | var sanitizers []Sanitizer 36 | var sinks []*ssa.Call 37 | for _, fn := range in.SrcFuncs { 38 | for _, b := range fn.Blocks { 39 | for _, i := range b.Instrs { 40 | if c, ok := i.(*ssa.Call); ok { 41 | name := c.Call.StaticCallee().Name() 42 | switch name { 43 | case "scrub": 44 | sanitizers = append(sanitizers, Sanitizer{c}) 45 | case "Print": 46 | sinks = append(sinks, c) 47 | } 48 | } 49 | } 50 | } 51 | } 52 | 53 | for _, sink := range sinks { 54 | for _, san := range sanitizers { 55 | if san.Dominates(sink) { 56 | pass.Reportf(sink.Pos(), "dominated") 57 | } 58 | } 59 | } 60 | return nil, nil 61 | } 62 | 63 | func TestDomination(t *testing.T) { 64 | dir := analysistest.TestData() 65 | analysistest.Run(t, dir, testAnalyzer) 66 | } 67 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | # This workflow is triggered on push or pull request for the master branch. 2 | # It verifies that the code is safe to merge. 3 | --- 4 | name: CI 5 | 6 | # yamllint disable-line rule:truthy 7 | on: 8 | push: 9 | branches: [master] 10 | pull_request: 11 | branches: [master] 12 | 13 | env: 14 | GO_VERSION: "1.18" 15 | 16 | jobs: 17 | lint: 18 | name: Lint 19 | runs-on: ubuntu-latest 20 | steps: 21 | - name: Setup Go 22 | uses: actions/setup-go@v2 23 | with: 24 | go-version: ${{ env.GO_VERSION }} 25 | 26 | - name: Checkout code 27 | uses: actions/checkout@v1 28 | 29 | - name: Check goimports 30 | if: ${{ always() }} 31 | run: | 32 | go install golang.org/x/tools/cmd/goimports 33 | if [[ $(goimports -l cmd internal pkg) ]]; then 34 | echo 'Please run `goimports -w cmd internal pkg`.' 35 | false 36 | fi 37 | 38 | - name: Check go mod 39 | if: ${{ always() }} 40 | run: | 41 | go mod tidy 42 | if ! git diff --quiet; then 43 | echo 'Please run `go mod tidy`.' 44 | false 45 | fi 46 | 47 | - name: Check for license headers 48 | if: ${{ always() }} 49 | run: | 50 | go install github.com/google/addlicense@latest 51 | addlicense -check cmd internal pkg 52 | 53 | - name: Run staticcheck 54 | if: ${{ always() }} 55 | run: | 56 | go install honnef.co/go/tools/cmd/staticcheck@v0.3.3 57 | staticcheck ./... 58 | 59 | - name: Check YAML 60 | if: ${{ always() }} 61 | run: | 62 | yamllint . 63 | 64 | test: 65 | name: Test 66 | runs-on: ubuntu-latest 67 | steps: 68 | - name: Setup Go 69 | uses: actions/setup-go@v2 70 | with: 71 | go-version: ${{ env.GO_VERSION }} 72 | 73 | - name: Checkout code 74 | uses: actions/checkout@v1 75 | 76 | - name: Run tests 77 | run: | 78 | go test -v ./... 79 | -------------------------------------------------------------------------------- /internal/pkg/debug/dump/dump.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package dump contains functions for writing a function's SSA as SSA or DOT source to a file. 16 | package dump 17 | 18 | import ( 19 | "fmt" 20 | "io/ioutil" 21 | "os" 22 | "path/filepath" 23 | "runtime" 24 | "strings" 25 | 26 | "github.com/google/go-flow-levee/internal/pkg/debug/render" 27 | "golang.org/x/tools/go/ssa" 28 | ) 29 | 30 | // SSA dumps a function's SSA to a file. 31 | func SSA(fileName string, f *ssa.Function) { 32 | save(fileName, f.Name(), render.SSA(f), "ssa") 33 | } 34 | 35 | // DOT dumps DOT source representing the function's SSA graph to a file. 36 | func DOT(fileName string, f *ssa.Function) { 37 | save(fileName, f.Name(), render.DOT(f), "dot") 38 | } 39 | 40 | // CFG dumps DOT source representing the function's control flow graph (CFG) to a file. 41 | func CFG(fileName string, f *ssa.Function) { 42 | save(fileName, f.Name()+"-cfg", render.CFG(f), "dot") 43 | } 44 | 45 | func save(fileName, funcName, s, ending string) { 46 | baseName := strings.TrimSuffix(fileName, ".go") 47 | outFile := fmt.Sprintf("%s_%s.%s", baseName, funcName, ending) 48 | err := ioutil.WriteFile(filepath.Join(outDir(), outFile), []byte(s), 0666) 49 | if err != nil { 50 | fmt.Fprintf(os.Stderr, "could not write to file: %s, error: %v\n", outFile, err) 51 | } 52 | } 53 | 54 | func outDir() string { 55 | _, currentFile, _, _ := runtime.Caller(0) 56 | d := filepath.Join(filepath.Dir(currentFile), "../../../../output") 57 | os.MkdirAll(d, 0755) 58 | return d 59 | } 60 | -------------------------------------------------------------------------------- /internal/pkg/levee/testdata/src/levee_analysistest/example/tests/propagation/builtins.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package propagation 16 | 17 | import ( 18 | "levee_analysistest/example/core" 19 | ) 20 | 21 | func TestCopyPropagatesTaintFromSrcToDst(s core.Source) { 22 | b := make([]byte, len(s.Data)) 23 | bytesCopied := copy(b, s.Data) 24 | core.Sink(bytesCopied) 25 | core.Sink(b) // want "a source has reached a sink" 26 | } 27 | 28 | func TestCopyDoesNotPropagateTaintFromDstToSrc(s core.Source) { 29 | data := []byte(s.Data) 30 | redacted := []byte("") 31 | copy(data, redacted) 32 | core.Sink(redacted) 33 | } 34 | 35 | func TestCopyDoesNotPropagateTaintToReturnedCount(s core.Source) { 36 | var b []byte 37 | count := copy(b, s.Data) 38 | core.Sink(count) 39 | } 40 | 41 | func TestAppendPropagatesTaintFromInputValueToInputAndOutputSlices(s core.Source, in, out []string) { 42 | out = append(in, s.Data) 43 | core.Sink(in) // want "a source has reached a sink" 44 | core.Sink(out) // want "a source has reached a sink" 45 | } 46 | 47 | func TestAppendPropagatesTaintFromInputSliceToOutputSlice(s core.Source, safe interface{}, out []interface{}) { 48 | in := []interface{}{s.Data} 49 | out = append(in, safe) 50 | core.Sink(out) // want "a source has reached a sink" 51 | core.Sink(safe) 52 | } 53 | 54 | func TestSpreadIntoAppendPropagatesTaintFromValueToSlices(s core.Source, in, out []byte) { 55 | out = append(in, s.Data...) 56 | core.Sink(in) // want "a source has reached a sink" 57 | core.Sink(out) // want "a source has reached a sink" 58 | } 59 | -------------------------------------------------------------------------------- /internal/pkg/levee/testdata/src/levee_analysistest/example/tests/collections/slices.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package collections 16 | 17 | import ( 18 | "levee_analysistest/example/core" 19 | ) 20 | 21 | func TestSlices(s core.Source) { 22 | slice := []core.Source{s} 23 | core.Sink(slice) // want "a source has reached a sink" 24 | core.Sink([]core.Source{s}) // want "a source has reached a sink" 25 | core.Sink([]interface{}{s}) // want "a source has reached a sink" 26 | core.Sink([]interface{}{0, "", s}) // want "a source has reached a sink" 27 | core.Sink([]interface{}{0, "", s}...) // want "a source has reached a sink" 28 | } 29 | 30 | func TestRangeOverSlice() { 31 | sources := []core.Source{core.Source{Data: "password1234"}} 32 | for i, s := range sources { 33 | core.Sink(s) // want "a source has reached a sink" 34 | core.Sink(i) 35 | } 36 | } 37 | 38 | func TestRangeOverInterfaceSlice() { 39 | for i, s := range []interface{}{core.Source{Data: "password1235"}} { 40 | core.Sink(s) // want "a source has reached a sink" 41 | core.Sink(i) 42 | } 43 | } 44 | 45 | func TestSliceBoundariesAreNotTainted(lo, hi, max int) { 46 | sources := [1]core.Source{{Data: "secret"}} 47 | slice := sources[lo:hi:max] 48 | core.Sink(lo) 49 | core.Sink(hi) 50 | core.Sink(max) 51 | _ = slice 52 | } 53 | 54 | func TestSlicedArrayIsTainted() { 55 | innocs := [1]interface{}{nil} 56 | slice := innocs[:] 57 | slice[0] = core.Source{Data: "secret"} 58 | core.Sink(innocs) // want "a source has reached a sink" 59 | _ = slice 60 | } 61 | -------------------------------------------------------------------------------- /internal/pkg/levee/testdata/src/levee_analysistest/example/tests/position/tests.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package position 16 | 17 | import ( 18 | "levee_analysistest/example/core" 19 | ) 20 | 21 | func TestSourcePointerExtract() { 22 | s, _ := NewSource() 23 | core.Sink(s) // want "a source has reached a sink\n source: .*tests.go:22:19" 24 | } 25 | 26 | // In order for the SSA to contain a FieldAddr, the EmbedsSource instance's fields have to be addressable. 27 | // We use a local variable here so that the fields will be addressable. 28 | // The field is extracted on its own line so we can differentiate between the struct's position 29 | // and the field's position. 30 | // The Source in this function is created by a FieldAddr that is not represented explicitly in the code. 31 | // Indeed, es.Data is actually es.Source.Data. 32 | // Because of this, we expect the report to be produced at the struct's position. 33 | func TestEmbeddedSourceFieldAddr() { 34 | es := EmbedsSource{} 35 | d := es.Data 36 | core.Sink(d) // want "a source has reached a sink\n source: .*tests.go:35:7" 37 | } 38 | 39 | // In order for the SSA to contain a Field, the EmbedsSource instance's fields must not be addressable. 40 | // One way to do this is to create a literal and to access the field directly, as part of the same expression. 41 | func TestEmbeddedSourceField() { 42 | core.Sink(EmbedsSource{}.Data) // want "a source has reached a sink\n source: .*tests.go:42:12" 43 | } 44 | 45 | type EmbedsSource struct { 46 | core.Source 47 | } 48 | 49 | func NewSource() (*core.Source, error) { 50 | return &core.Source{}, nil 51 | } 52 | -------------------------------------------------------------------------------- /internal/pkg/source/analyzer.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package source 16 | 17 | import ( 18 | "go/token" 19 | "reflect" 20 | 21 | "github.com/google/go-flow-levee/internal/pkg/config" 22 | "github.com/google/go-flow-levee/internal/pkg/fieldpropagator" 23 | "github.com/google/go-flow-levee/internal/pkg/fieldtags" 24 | "golang.org/x/tools/go/analysis" 25 | "golang.org/x/tools/go/analysis/passes/buildssa" 26 | "golang.org/x/tools/go/ssa" 27 | ) 28 | 29 | type ResultType = map[*ssa.Function][]*Source 30 | 31 | var Analyzer = &analysis.Analyzer{ 32 | Name: "source", 33 | Doc: "This analyzer identifies ssa.Values that are sources.", 34 | Flags: config.FlagSet, 35 | Run: run, 36 | Requires: []*analysis.Analyzer{buildssa.Analyzer, fieldtags.Analyzer, fieldpropagator.Analyzer}, 37 | ResultType: reflect.TypeOf(new(ResultType)).Elem(), 38 | } 39 | 40 | func run(pass *analysis.Pass) (interface{}, error) { 41 | ssaInput := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA) 42 | taggedFields := pass.ResultOf[fieldtags.Analyzer].(fieldtags.ResultType) 43 | fieldPropagators := pass.ResultOf[fieldpropagator.Analyzer].(fieldpropagator.ResultType) 44 | 45 | conf, err := config.ReadConfig() 46 | if err != nil { 47 | return nil, err 48 | } 49 | 50 | sourceMap := identify(conf, ssaInput, taggedFields, fieldPropagators) 51 | 52 | for _, srcs := range sourceMap { 53 | for _, s := range srcs { 54 | report(pass, s.Pos()) 55 | } 56 | } 57 | 58 | return sourceMap, nil 59 | } 60 | 61 | func report(pass *analysis.Pass, pos token.Pos) { 62 | pass.Reportf(pos, "source identified at %s", pass.Fset.Position(pos)) 63 | } 64 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to Contribute 2 | 3 | We'd love to accept your patches and contributions to this project. There are 4 | just a few small guidelines you need to follow. 5 | 6 | ## Contributor License Agreement 7 | 8 | Contributions to this project must be accompanied by a Contributor License 9 | Agreement. You (or your employer) retain the copyright to your contribution; 10 | this simply gives us permission to use and redistribute your contributions as 11 | part of the project. Head over to to see 12 | your current agreements on file or to sign a new one. 13 | 14 | You generally only need to submit a CLA once, so if you've already submitted one 15 | (even if it was for a different project), you probably don't need to do it 16 | again. 17 | 18 | ## Code reviews 19 | 20 | All submissions, including submissions by project members, require review. We 21 | use GitHub pull requests for this purpose. Consult 22 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more 23 | information on using pull requests. 24 | 25 | ## Community Guidelines 26 | 27 | This project follows [Google's Open Source Community 28 | Guidelines](https://opensource.google/conduct/). 29 | 30 | ## Project Conventions 31 | 32 | ### Use Go Modules in `testdata` 33 | 34 | The analyzer testing package `analysistest` executes a build of the Go code it tests. 35 | This treats the `testdata` folder as a synthetic GOPATH, and Go code tested within will look for imported packages in `testdata/src/...`. 36 | Unfortunately, this tends to break IDE linking, making test development difficult. 37 | 38 | To ameliorate this issue, we group a given test's `testdata` into a synthetic root package and use Go modules to identify it for the IDE. 39 | This allows your IDE to correctly link imports while adhering to the expectation of `analysistest`. 40 | 41 | Specifically, `testdata` should be grouped under `testdata/src/NAME_analysistest`, where `NAME` is the name of the analyzer being tested. 42 | Initialize a trivial Go module file with `go mod init NAME_analysistest`. 43 | Any imports used within this `testdata` will begin with `NAME_analysistest`. 44 | See [`levee_analysistest`](internal/pkg/levee/testdata/src/levee_analysistest) for an example. 45 | -------------------------------------------------------------------------------- /internal/pkg/fieldpropagator/testdata/src/fieldpropagator_analysistest/source/test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package source 16 | 17 | type Source struct { 18 | secret string `levee:"source"` 19 | data string 20 | dataPtr *string 21 | id int 22 | } 23 | 24 | func (s Source) ID() int { 25 | return s.id 26 | } 27 | 28 | func (s Source) Secret() string { // want Secret:"field propagator identified" 29 | return s.secret 30 | } 31 | 32 | func (s Source) DataValueReceiver() string { // want DataValueReceiver:"field propagator identified" 33 | return s.data 34 | } 35 | 36 | func (s *Source) DataPointerReceiver() string { // want DataPointerReceiver:"field propagator identified" 37 | return s.data 38 | } 39 | 40 | func (s Source) DataRef() *string { // want DataRef:"field propagator identified" 41 | return &s.data 42 | } 43 | 44 | func (s Source) DataPtr() *string { // want DataPtr:"field propagator identified" 45 | return s.dataPtr 46 | } 47 | 48 | func (s Source) DataDeref() string { // want DataDeref:"field propagator identified" 49 | return *s.dataPtr 50 | } 51 | 52 | func (s Source) ShowData() string { // want ShowData:"field propagator identified" 53 | return "Data: " + s.data 54 | } 55 | 56 | var isAdmin bool 57 | 58 | func (s Source) MaybeData() string { // want MaybeData:"field propagator identified" 59 | if isAdmin { 60 | return s.data 61 | } 62 | return "" 63 | } 64 | 65 | func (s Source) TryGetData() (string, error) { // want TryGetData:"field propagator identified" 66 | return s.data, nil 67 | } 68 | 69 | func New(data string) Source { 70 | return Source{data: data, id: 0} 71 | } 72 | 73 | type NotSource struct { 74 | data string 75 | } 76 | 77 | func (n NotSource) Data() string { 78 | return n.data 79 | } 80 | -------------------------------------------------------------------------------- /internal/pkg/fieldtags/analyzer_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package fieldtags 16 | 17 | import ( 18 | "path/filepath" 19 | "testing" 20 | 21 | "github.com/google/go-cmp/cmp" 22 | "github.com/google/go-cmp/cmp/cmpopts" 23 | "github.com/google/go-flow-levee/internal/pkg/config" 24 | "golang.org/x/tools/go/analysis/analysistest" 25 | ) 26 | 27 | func TestFieldTagsAnalysis(t *testing.T) { 28 | testdata := analysistest.TestData() 29 | 30 | if err := config.FlagSet.Set("config", filepath.Join(testdata, "test-config.yaml")); err != nil { 31 | t.Error(err) 32 | } 33 | 34 | results := analysistest.Run(t, testdata, Analyzer, "fieldtags_analysistest/core", "fieldtags_analysistest/crosspkg") 35 | 36 | if len(results) != 2 { 37 | t.Fatalf("expected 2 results, got %d", len(results)) 38 | } 39 | 40 | var gotCoreResults []string 41 | for obj := range results[0].Result.(ResultType) { 42 | gotCoreResults = append(gotCoreResults, obj.Name()) 43 | } 44 | wantCoreResults := []string{ 45 | "adminSecret", 46 | "another", 47 | "creds", 48 | "hasCustomFieldTag", 49 | "hasTagWithMultipleValues", 50 | "password", 51 | "secret", 52 | } 53 | if diff := cmp.Diff(wantCoreResults, gotCoreResults, cmpopts.SortSlices(func(a, b string) bool { return a < b })); diff != "" { 54 | t.Errorf("core results diff (-want +got):\n%s", diff) 55 | } 56 | 57 | var gotCrosspkgResults []string 58 | for obj := range results[1].Result.(ResultType) { 59 | gotCrosspkgResults = append(gotCrosspkgResults, obj.Name()) 60 | } 61 | wantCrosspkgResults := append(wantCoreResults, "crossField") 62 | if diff := cmp.Diff(wantCrosspkgResults, gotCrosspkgResults, cmpopts.SortSlices(func(a, b string) bool { return a < b })); diff != "" { 63 | t.Errorf("crosspkg results diff (-want +got):\n%s", diff) 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /internal/pkg/levee/testdata/src/levee_analysistest/example/tests/typeassert/assertion_inference_tests.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package typeassert 16 | 17 | import ( 18 | "levee_analysistest/example/core" 19 | ) 20 | 21 | func TestTypeSwitch(i interface{}) { 22 | switch t := i.(type) { 23 | case core.Innocuous: 24 | core.Sink(i) 25 | core.Sink(t) 26 | case *core.Source: 27 | // The type of i is definitively known within this block 28 | core.Sink(i) // TODO(#161) want "a source has reached a sink" 29 | core.Sink(t) // want "a source has reached a sink" 30 | case core.Source: 31 | // The type of i is definitively known within this block 32 | core.Sink(i) // TODO(#161) want "a source has reached a sink" 33 | core.Sink(t) // want "a source has reached a sink" 34 | default: 35 | core.Sink(i) 36 | core.Sink(t) 37 | } 38 | } 39 | 40 | func TestTypeSwitchInline(i interface{}) { 41 | switch i.(type) { 42 | case core.Innocuous, *core.Source, core.Source: 43 | // While not definitively known, the type of i may be asserted to be a source type 44 | core.Sink(i) // TODO(#161) want "a source has reached a sink" 45 | default: 46 | // do nothing 47 | } 48 | } 49 | 50 | func TestPanicTypeAssertSource(i interface{}) { 51 | s := i.(core.Source) 52 | _ = s 53 | // The dominating type assertion would panic if i were not a source type. 54 | core.Sink(i) // want "a source has reached a sink" 55 | } 56 | 57 | func TestPanicTypeAssertInnocuous(i interface{}) { 58 | // TypeAssert instructions should not cause reports by themselves. 59 | innoc := i.(core.Innocuous) 60 | _ = innoc 61 | core.Sink(i) 62 | } 63 | 64 | func TestOkayTypeAssert(i interface{}) { 65 | s, ok := i.(core.Source) 66 | _, _ = s, ok 67 | // The dominating type assertion will not panic. 68 | core.Sink(i) 69 | } 70 | -------------------------------------------------------------------------------- /internal/pkg/debug/render/testdata/TestDisconnected.dot: -------------------------------------------------------------------------------- 1 | digraph { 2 | subgraph cluster_0 { 3 | color=black; 4 | label="entry"; 5 | "jump 3\n(Jump)" [shape=diamond]; 6 | } 7 | subgraph cluster_1 { 8 | color=black; 9 | label="for.body"; 10 | "t0 = t4 - 1:int\n(BinOp)" [shape=rectangle]; 11 | "t1 = t0 + 1:int\n(BinOp)" [shape=rectangle]; 12 | "jump 3\n(Jump)" [shape=diamond]; 13 | } 14 | subgraph cluster_2 { 15 | color=black; 16 | label="for.done"; 17 | "t2 = \"error: \":string + \"unreachable code\":string\n(BinOp)" [shape=rectangle]; 18 | "t3 = fmt.Printf(t2, nil:[]interface{}...)\n(Call)" [shape=rectangle]; 19 | "return\n(Return)" [shape=diamond]; 20 | } 21 | subgraph cluster_3 { 22 | color=black; 23 | label="for.loop"; 24 | "t4 = phi [0: 0:int, 1: t1] #i\n(Phi)" [shape=rectangle]; 25 | "t5 = 1:int * 2:int\n(BinOp)" [shape=rectangle]; 26 | "t6 = t4 < t5\n(BinOp)" [shape=rectangle]; 27 | "if t6 goto 1 else 2\n(If)" [shape=diamond]; 28 | } 29 | "t4 = phi [0: 0:int, 1: t1] #i\n(Phi)" -> "t0 = t4 - 1:int\n(BinOp)" [color=orange]; 30 | "1:int\n(Const)" -> "t0 = t4 - 1:int\n(BinOp)" [color=orange]; 31 | "t0 = t4 - 1:int\n(BinOp)" -> "t1 = t0 + 1:int\n(BinOp)" [color=orange]; 32 | "1:int\n(Const)" -> "t1 = t0 + 1:int\n(BinOp)" [color=orange]; 33 | "\"error: \":string\n(Const)" -> "t2 = \"error: \":string + \"unreachable code\":string\n(BinOp)" [color=orange]; 34 | "\"unreachable code\":string\n(Const)" -> "t2 = \"error: \":string + \"unreachable code\":string\n(BinOp)" [color=orange]; 35 | "Printf\n(Function)" -> "t3 = fmt.Printf(t2, nil:[]interface{}...)\n(Call)" [color=orange]; 36 | "t2 = \"error: \":string + \"unreachable code\":string\n(BinOp)" -> "t3 = fmt.Printf(t2, nil:[]interface{}...)\n(Call)" [color=orange]; 37 | "nil:[]interface{}\n(Const)" -> "t3 = fmt.Printf(t2, nil:[]interface{}...)\n(Call)" [color=orange]; 38 | "0:int\n(Const)" -> "t4 = phi [0: 0:int, 1: t1] #i\n(Phi)" [color=orange]; 39 | "t1 = t0 + 1:int\n(BinOp)" -> "t4 = phi [0: 0:int, 1: t1] #i\n(Phi)" [color=orange]; 40 | "1:int\n(Const)" -> "t5 = 1:int * 2:int\n(BinOp)" [color=orange]; 41 | "2:int\n(Const)" -> "t5 = 1:int * 2:int\n(BinOp)" [color=orange]; 42 | "t4 = phi [0: 0:int, 1: t1] #i\n(Phi)" -> "t6 = t4 < t5\n(BinOp)" [color=orange]; 43 | "t5 = 1:int * 2:int\n(BinOp)" -> "t6 = t4 < t5\n(BinOp)" [color=orange]; 44 | "t6 = t4 < t5\n(BinOp)" -> "if t6 goto 1 else 2\n(If)" [color=orange]; 45 | } 46 | -------------------------------------------------------------------------------- /internal/pkg/debug/render/testdata/TestParams.dot: -------------------------------------------------------------------------------- 1 | digraph { 2 | subgraph cluster_0 { 3 | color=black; 4 | label="entry"; 5 | "t0 = new [3]interface{} (varargs)\n(Alloc)" [shape=rectangle]; 6 | "t1 = &t0[0:int]\n(IndexAddr)" [shape=rectangle]; 7 | "t2 = make interface{} <- int (a)\n(MakeInterface)" [shape=rectangle]; 8 | "*t1 = t2\n(Store)" [shape=diamond]; 9 | "t3 = &t0[1:int]\n(IndexAddr)" [shape=rectangle]; 10 | "t4 = make interface{} <- int (b)\n(MakeInterface)" [shape=rectangle]; 11 | "*t3 = t4\n(Store)" [shape=diamond]; 12 | "t5 = &t0[2:int]\n(IndexAddr)" [shape=rectangle]; 13 | "t6 = make interface{} <- string (c)\n(MakeInterface)" [shape=rectangle]; 14 | "*t5 = t6\n(Store)" [shape=diamond]; 15 | "t7 = slice t0[:]\n(Slice)" [shape=rectangle]; 16 | "t8 = fmt.Println(t7...)\n(Call)" [shape=rectangle]; 17 | "return\n(Return)" [shape=diamond]; 18 | } 19 | "t0 = new [3]interface{} (varargs)\n(Alloc)" -> "t1 = &t0[0:int]\n(IndexAddr)" [color=orange]; 20 | "0:int\n(Const)" -> "t1 = &t0[0:int]\n(IndexAddr)" [color=orange]; 21 | "a\n(Parameter)" -> "t2 = make interface{} <- int (a)\n(MakeInterface)" [color=orange]; 22 | "t1 = &t0[0:int]\n(IndexAddr)" -> "*t1 = t2\n(Store)" [color=orange]; 23 | "t2 = make interface{} <- int (a)\n(MakeInterface)" -> "*t1 = t2\n(Store)" [color=orange]; 24 | "t0 = new [3]interface{} (varargs)\n(Alloc)" -> "t3 = &t0[1:int]\n(IndexAddr)" [color=orange]; 25 | "1:int\n(Const)" -> "t3 = &t0[1:int]\n(IndexAddr)" [color=orange]; 26 | "b\n(Parameter)" -> "t4 = make interface{} <- int (b)\n(MakeInterface)" [color=orange]; 27 | "t3 = &t0[1:int]\n(IndexAddr)" -> "*t3 = t4\n(Store)" [color=orange]; 28 | "t4 = make interface{} <- int (b)\n(MakeInterface)" -> "*t3 = t4\n(Store)" [color=orange]; 29 | "t0 = new [3]interface{} (varargs)\n(Alloc)" -> "t5 = &t0[2:int]\n(IndexAddr)" [color=orange]; 30 | "2:int\n(Const)" -> "t5 = &t0[2:int]\n(IndexAddr)" [color=orange]; 31 | "c\n(Parameter)" -> "t6 = make interface{} <- string (c)\n(MakeInterface)" [color=orange]; 32 | "t5 = &t0[2:int]\n(IndexAddr)" -> "*t5 = t6\n(Store)" [color=orange]; 33 | "t6 = make interface{} <- string (c)\n(MakeInterface)" -> "*t5 = t6\n(Store)" [color=orange]; 34 | "t0 = new [3]interface{} (varargs)\n(Alloc)" -> "t7 = slice t0[:]\n(Slice)" [color=orange]; 35 | "Println\n(Function)" -> "t8 = fmt.Println(t7...)\n(Call)" [color=orange]; 36 | "t7 = slice t0[:]\n(Slice)" -> "t8 = fmt.Println(t7...)\n(Call)" [color=orange]; 37 | } 38 | -------------------------------------------------------------------------------- /internal/pkg/levee/levee_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package levee 16 | 17 | import ( 18 | "flag" 19 | "testing" 20 | 21 | "github.com/google/go-flow-levee/internal/pkg/debug" 22 | "golang.org/x/tools/go/analysis/analysistest" 23 | ) 24 | 25 | var debugging *bool = flag.Bool("debug", false, "run the debug analyzer") 26 | 27 | func TestLevee(t *testing.T) { 28 | dataDir := analysistest.TestData() 29 | if err := Analyzer.Flags.Set("config", dataDir+"/test-config.yaml"); err != nil { 30 | t.Error(err) 31 | } 32 | if *debugging { 33 | Analyzer.Requires = append(Analyzer.Requires, debug.Analyzer) 34 | } 35 | analysistest.Run(t, dataDir, Analyzer, "./src/levee_analysistest/example/...") 36 | } 37 | 38 | func TestLeveeDoesNotCreateReportsForPanicIfPanicingOnTaintedValuesIsAllowed(t *testing.T) { 39 | dataDir := analysistest.TestData() 40 | if err := Analyzer.Flags.Set("config", dataDir+"/allowpanicontaintedvalues-config.yaml"); err != nil { 41 | t.Error(err) 42 | } 43 | if *debugging { 44 | Analyzer.Requires = append(Analyzer.Requires, debug.Analyzer) 45 | } 46 | analysistest.Run(t, dataDir, Analyzer, "./src/levee_analysistest/nopanic.com/...") 47 | } 48 | 49 | func TestFormattingWithCustomReportMessage(t *testing.T) { 50 | dataDir := analysistest.TestData() 51 | if err := Analyzer.Flags.Set("config", dataDir+"/with-custom-message.yaml"); err != nil { 52 | t.Error(err) 53 | } 54 | analysistest.Run(t, dataDir, Analyzer, "./src/levee_analysistest/custom.message.com/withcustom") 55 | } 56 | 57 | func TestFormattingWithoutCustomReportMessage(t *testing.T) { 58 | dataDir := analysistest.TestData() 59 | if err := Analyzer.Flags.Set("config", dataDir+"/no-custom-message.yaml"); err != nil { 60 | t.Error(err) 61 | } 62 | analysistest.Run(t, dataDir, Analyzer, "./src/levee_analysistest/custom.message.com/nocustom") 63 | } 64 | -------------------------------------------------------------------------------- /internal/pkg/suppression/analyzer.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package suppression defines an analyzer that identifies calls 16 | // suppressed by a comment. 17 | package suppression 18 | 19 | import ( 20 | "go/ast" 21 | "reflect" 22 | "strings" 23 | 24 | "golang.org/x/tools/go/analysis" 25 | ) 26 | 27 | const suppressionString = "levee.DoNotReport" 28 | 29 | // ResultType is a set of nodes that are suppressed by a comment. 30 | type ResultType map[ast.Node]bool 31 | 32 | // IsSuppressed determines whether the given node is suppressed. 33 | func (rt ResultType) IsSuppressed(n ast.Node) bool { 34 | _, ok := rt[n] 35 | return ok 36 | } 37 | 38 | var Analyzer = &analysis.Analyzer{ 39 | Name: "suppression", 40 | Doc: "This analyzer identifies ast nodes that are suppressed by comments.", 41 | Run: run, 42 | ResultType: reflect.TypeOf(new(ResultType)).Elem(), 43 | } 44 | 45 | func run(pass *analysis.Pass) (interface{}, error) { 46 | result := map[ast.Node]bool{} 47 | 48 | for _, f := range pass.Files { 49 | for node, commentGroups := range ast.NewCommentMap(pass.Fset, f, f.Comments) { 50 | for _, cg := range commentGroups { 51 | if isSuppressingCommentGroup(cg) { 52 | result[node] = true 53 | pass.Reportf(node.Pos(), "suppressed") 54 | } 55 | } 56 | } 57 | } 58 | 59 | return ResultType(result), nil 60 | } 61 | 62 | // isSuppressingCommentGroup determines whether a comment group contains 63 | // the suppression string at the beginning of a line in the comment text. 64 | func isSuppressingCommentGroup(commentGroup *ast.CommentGroup) bool { 65 | for _, line := range strings.Split(commentGroup.Text(), "\n") { 66 | trimmed := strings.TrimSpace(strings.TrimPrefix(strings.TrimPrefix(line, "//"), "/*")) 67 | if strings.HasPrefix(trimmed, suppressionString) { 68 | return true 69 | } 70 | } 71 | return false 72 | } 73 | -------------------------------------------------------------------------------- /internal/pkg/propagation/summary/api_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package summary 16 | 17 | import ( 18 | "go/types" 19 | "testing" 20 | 21 | "golang.org/x/tools/go/analysis" 22 | "golang.org/x/tools/go/analysis/analysistest" 23 | "golang.org/x/tools/go/analysis/passes/buildssa" 24 | "golang.org/x/tools/go/ssa" 25 | ) 26 | 27 | func Test(t *testing.T) { 28 | analysistest.Run(t, analysistest.TestData(), analyzer, "./...") 29 | } 30 | 31 | var analyzer = &analysis.Analyzer{ 32 | Name: "summarytest", 33 | Doc: "This analyzer is a test harness for functions in the summary package.", 34 | Run: run, 35 | Requires: []*analysis.Analyzer{buildssa.Analyzer}, 36 | } 37 | 38 | func run(pass *analysis.Pass) (interface{}, error) { 39 | builtSSA := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA) 40 | 41 | // produce reports for sigTypeString test cases 42 | for _, f := range builtSSA.SrcFuncs { 43 | if f.Name() == "testStaticFuncName" || f.Name() == "testFuncNameWithoutReceiver" { 44 | continue 45 | } 46 | sig := f.Type().(*types.Signature) 47 | pass.Reportf(f.Pos(), sigTypeString(sig)) 48 | } 49 | 50 | // produce reports for testFuncName test cases 51 | for _, f := range builtSSA.SrcFuncs { 52 | if f.Name() != "testStaticFuncName" { 53 | continue 54 | } 55 | for _, instr := range f.Blocks[0].Instrs { 56 | if call, ok := instr.(*ssa.Call); ok { 57 | pass.Reportf(call.Pos(), staticFuncName(call)) 58 | } 59 | } 60 | } 61 | 62 | // produce reports for testFuncNameWithoutReceiver test cases 63 | for _, f := range builtSSA.SrcFuncs { 64 | if f.Name() != "testFuncNameWithoutReceiver" { 65 | continue 66 | } 67 | for _, instr := range f.Blocks[0].Instrs { 68 | if call, ok := instr.(*ssa.Call); ok { 69 | pass.Reportf(call.Pos(), methodNameWithoutReceiver(call)) 70 | } 71 | } 72 | } 73 | 74 | return nil, nil 75 | } 76 | -------------------------------------------------------------------------------- /internal/pkg/config/fieldtags_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package config 16 | 17 | import ( 18 | "testing" 19 | ) 20 | 21 | func TestFieldTagsIdentification(t *testing.T) { 22 | if err := FlagSet.Set("config", "testdata/test-config.yaml"); err != nil { 23 | t.Error(err) 24 | } 25 | 26 | config, err := ReadConfig() 27 | if err != nil { 28 | t.Error(err) 29 | } 30 | 31 | cases := []struct { 32 | desc string 33 | tag string 34 | want bool 35 | }{ 36 | { 37 | "built-in field tag", 38 | "`levee:\"source\"`", 39 | true, 40 | }, 41 | { 42 | "custom field tag", 43 | "`example:\"sensitive\"`", 44 | true, 45 | }, 46 | { 47 | "different tag key", 48 | "`notexample:\"sensitive\"`", 49 | false, 50 | }, 51 | { 52 | "different tag value", 53 | "`example:\"notsensitive\"`", 54 | false, 55 | }, 56 | { 57 | "escaped tag accepted", 58 | `"levee:\"source\""`, 59 | true, 60 | }, 61 | { 62 | "multiple values, no target", 63 | "`example:\"foo,bar,baz\"`", 64 | false, 65 | }, 66 | { 67 | "multiple values, with target", 68 | "`example:\"foo,sensitive,bar\"`", 69 | true, 70 | }, 71 | { 72 | "multiple key value sets, no target", 73 | "`foo:\"bar,baz\" example:\"foo,bar,baz\" fizz:\"bang\"`", 74 | false, 75 | }, 76 | { 77 | "multiple key value sets, with target", 78 | "`foo:\"bar,baz\" example:\"foo,sensitive,bar\"` fizz:\"bang\"", 79 | true, 80 | }, 81 | { 82 | "empty", 83 | "", 84 | false, 85 | }, 86 | { 87 | "malformed", 88 | "`noEndQuote:\"malform", 89 | false, 90 | }, 91 | } 92 | 93 | for _, tt := range cases { 94 | t.Run(tt.desc, func(t *testing.T) { 95 | got := config.IsSourceFieldTag(tt.tag) 96 | if got != tt.want { 97 | t.Errorf("config.IsSourceFieldTag(%q) == %v, want %v", tt.tag, got, tt.want) 98 | } 99 | }) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /internal/pkg/sourceinfer/testdata/src/example.com/tests/core/field.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package core 16 | 17 | import ( 18 | "example.com/source" 19 | ) 20 | 21 | type ( 22 | SourceEmbedder struct { // want SourceEmbedder:"inferred source" 23 | source.Source 24 | } 25 | 26 | SourceHolder struct { // want SourceHolder:"inferred source" 27 | s source.Source 28 | } 29 | 30 | SourcePointerHolder struct { // want SourcePointerHolder:"inferred source" 31 | s *source.Source 32 | } 33 | 34 | SourceHolderAmongOtherThings struct { // want SourceHolderAmongOtherThings:"inferred source" 35 | s source.Source 36 | ns source.NotSource 37 | } 38 | 39 | SourceSliceHolder struct { // want SourceSliceHolder:"inferred source" 40 | ss []source.Source 41 | } 42 | 43 | SourceNestedHolder struct { // want SourceNestedHolder:"inferred source" 44 | sn map[string][]map[string][]source.Source 45 | } 46 | 47 | SourceHolderHolder struct { // want SourceHolderHolder:"inferred source" 48 | sh SourceHolder 49 | } 50 | 51 | SourceWrapperHolder struct { // want SourceWrapperHolder:"inferred source" 52 | Wrapped struct { 53 | s source.Source 54 | } 55 | } 56 | 57 | TaggedWrapperHolder struct { // want TaggedWrapperHolder:"inferred source" 58 | Wrapped struct { 59 | s string `levee:"source"` 60 | } 61 | } 62 | 63 | TaggedHolder struct { // want TaggedHolder:"inferred source" 64 | t source.Tagged 65 | } 66 | ) 67 | 68 | type ( 69 | NotSourceHolder struct { 70 | ns source.NotSource 71 | } 72 | 73 | FieldFuncWithSourceArg struct { 74 | f func(s source.Source) 75 | } 76 | 77 | FieldFuncWithSourceRetval struct { 78 | f func() source.Source 79 | } 80 | ) 81 | 82 | func Field() { 83 | type HoldingInFunc struct { // want HoldingInFunc:"inferred source" 84 | s source.Source 85 | } 86 | } 87 | 88 | func FieldAnonymousFunc() { 89 | _ = func() { 90 | type HoldingInAnonymousFunc struct { // want HoldingInAnonymousFunc:"inferred source" 91 | s source.Source 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /internal/pkg/levee/testdata/src/levee_analysistest/example/tests/extracts/tests.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package extracts 16 | 17 | import ( 18 | "levee_analysistest/example/core" 19 | ) 20 | 21 | func CreateSource() (core.Source, error) { 22 | return core.Source{}, nil 23 | } 24 | 25 | func TryUpdateSource(s core.Source) (core.Source, bool) { 26 | return s, true 27 | } 28 | 29 | func CreateSourceFlipped() (error, core.Source) { 30 | return nil, core.Source{} 31 | } 32 | 33 | func TestOnlySourceExtractIsTaintedFromCall() { 34 | s, ok := TryUpdateSource(core.Source{}) 35 | core.Sink(s) // want "a source has reached a sink" 36 | core.Sink(ok) 37 | } 38 | 39 | func TestOnlySourceExtractIsTaintedFromTypeAssert(s core.Source) { 40 | var p interface{} = s 41 | s, ok := p.(core.Source) 42 | core.Sink(s) // want "a source has reached a sink" 43 | core.Sink(ok) 44 | } 45 | 46 | func TestOnlySourceExtractIsTaintedFromLookup() { 47 | s, ok := map[string]core.Source{}[""] 48 | core.Sink(s) // want "a source has reached a sink" 49 | core.Sink(ok) 50 | } 51 | 52 | func TestOnlySourceExtractIsTaintedFromChanRecv() { 53 | s, ok := <-make(chan core.Source) 54 | core.Sink(s) // want "a source has reached a sink" 55 | core.Sink(ok) 56 | } 57 | 58 | func TestOnlySourceExtractIsTaintedInstructionOrder() { 59 | s, err := CreateSource() 60 | core.Sink(err) 61 | core.Sink(s) // want "a source has reached a sink" 62 | } 63 | 64 | func TestOnlySourceExtractIsTaintedFlipped() { 65 | err, s := CreateSourceFlipped() 66 | core.Sink(s) // want "a source has reached a sink" 67 | core.Sink(err) 68 | } 69 | 70 | func TestOnlySourceExtractIsTaintedInstructionOrderFlipped() { 71 | err, s := CreateSourceFlipped() 72 | core.Sink(err) 73 | core.Sink(s) // want "a source has reached a sink" 74 | } 75 | 76 | func NewSource() (*core.Source, error) { 77 | return &core.Source{}, nil 78 | } 79 | 80 | func TestNewSource() { 81 | s, err := NewSource() 82 | core.Sink(s) // want "a source has reached a sink" 83 | core.Sink(err) 84 | } 85 | 86 | func TestCopy() { 87 | s := core.Source{} 88 | cpy, err := s.Copy() 89 | core.Sink(cpy) // want "a source has reached a sink" 90 | core.Sink(err) 91 | } 92 | 93 | func TestCopyPointer() { 94 | s := core.Source{} 95 | cpy, err := s.CopyPointer() 96 | core.Sink(cpy) // want "a source has reached a sink" 97 | core.Sink(err) 98 | } 99 | -------------------------------------------------------------------------------- /internal/pkg/levee/testdata/src/levee_analysistest/example/tests/sinks/tests.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package sinks 16 | 17 | import ( 18 | "io" 19 | 20 | "levee_analysistest/example/core" 21 | ) 22 | 23 | func TestSinks(s core.Source, writer io.Writer) { 24 | core.Sinker{}.Sink(s) // want "a source has reached a sink" 25 | core.Sink(s) // want "a source has reached a sink" 26 | core.Sinkf("a source: %v", s) // want "a source has reached a sink" 27 | core.FSinkf(writer, s) // want "a source has reached a sink" 28 | core.OneArgSink(s) // want "a source has reached a sink" 29 | } 30 | 31 | func TestSinksWithRef(s *core.Source, writer io.Writer) { 32 | core.Sinker{}.Sink(s) // want "a source has reached a sink" 33 | core.Sink(s) // want "a source has reached a sink" 34 | core.Sinkf("a source: %v", s) // want "a source has reached a sink" 35 | core.FSinkf(writer, s) // want "a source has reached a sink" 36 | core.OneArgSink(s) // want "a source has reached a sink" 37 | } 38 | 39 | func TestSinksInnocuous(innoc core.Innocuous, writer io.Writer) { 40 | core.Sinker{}.Sink(innoc) 41 | core.Sink(innoc) 42 | core.Sinkf("a source: %v", innoc) 43 | core.FSinkf(writer, innoc) 44 | core.OneArgSink(innoc) 45 | 46 | core.Sink([]interface{}{innoc, innoc, innoc}...) 47 | core.Sink([]interface{}{innoc, innoc, innoc}) 48 | } 49 | 50 | func TestSinksWithInnocuousRef(innoc *core.Innocuous, writer io.Writer) { 51 | core.Sinker{}.Sink(innoc) 52 | core.Sink(innoc) 53 | core.Sinkf("a source: %v", innoc) 54 | core.FSinkf(writer, innoc) 55 | core.OneArgSink(innoc) 56 | 57 | core.Sink([]interface{}{innoc, innoc, innoc}...) 58 | core.Sink([]interface{}{innoc, innoc, innoc}) 59 | } 60 | 61 | // TestSinksSourceAndInnocuous covers the situations in which both a Source and 62 | // a non-Source value are in scope, as well as variadic calls involving both 63 | // Source and non-Source arguments. 64 | func TestSinksSourceAndInnocuous(source core.Source, innoc core.Innocuous) { 65 | core.Sink(source) // want "a source has reached a sink" 66 | core.OneArgSink(source) // want "a source has reached a sink" 67 | core.Sink(innoc, source) // want "a source has reached a sink" 68 | core.Sink(source, innoc) // want "a source has reached a sink" 69 | core.Sink(innoc) 70 | core.OneArgSink(innoc) 71 | } 72 | -------------------------------------------------------------------------------- /internal/pkg/config/config_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package config 16 | 17 | import ( 18 | "path/filepath" 19 | "testing" 20 | 21 | "github.com/google/go-flow-levee/internal/pkg/utils" 22 | "golang.org/x/tools/go/analysis" 23 | "golang.org/x/tools/go/analysis/analysistest" 24 | "golang.org/x/tools/go/analysis/passes/buildssa" 25 | "golang.org/x/tools/go/ssa" 26 | ) 27 | 28 | var testAnalyzer = &analysis.Analyzer{ 29 | Name: "config", 30 | Run: runTest, 31 | Doc: "test harness for the logic related to config", 32 | Requires: []*analysis.Analyzer{buildssa.Analyzer}, 33 | } 34 | 35 | func runTest(pass *analysis.Pass) (interface{}, error) { 36 | in := pass.ResultOf[buildssa.Analyzer].(*buildssa.SSA) 37 | 38 | conf, err := ReadConfig() 39 | if err != nil { 40 | return nil, err 41 | } 42 | 43 | for _, f := range in.SrcFuncs { 44 | if conf.IsSink(utils.DecomposeFunction(f)) { 45 | pass.Reportf(f.Pos(), "sink") 46 | } 47 | if conf.IsExcluded(utils.DecomposeFunction(f)) { 48 | pass.Reportf(f.Pos(), "excluded") 49 | } 50 | for _, b := range f.Blocks { 51 | for _, i := range b.Instrs { 52 | if c, ok := i.(*ssa.Call); ok { 53 | if callee := c.Call.StaticCallee(); callee != nil && conf.IsSink(utils.DecomposeFunction(callee)) { 54 | pass.Reportf(i.Pos(), "sink call") 55 | } 56 | } 57 | } 58 | } 59 | } 60 | 61 | return nil, nil 62 | } 63 | 64 | func TestConfigCache(t *testing.T) { 65 | testdata := analysistest.TestData() 66 | if err := FlagSet.Set("config", filepath.Join(testdata, "empty-config.yaml")); err != nil { 67 | t.Fatal(err) 68 | } 69 | 70 | emptyConfig, err := ReadConfig() 71 | if err != nil { 72 | t.Errorf("failed to read config: %v", err) 73 | } 74 | 75 | if err := FlagSet.Set("config", filepath.Join(testdata, "test-config.yaml")); err != nil { 76 | t.Fatal(err) 77 | } 78 | 79 | testConfig, err := ReadConfig() 80 | if err != nil { 81 | t.Errorf("failed to read config: %v", err) 82 | } 83 | 84 | if testConfig == emptyConfig { 85 | t.Error("Returned the same config for separate config source files") 86 | } 87 | } 88 | 89 | func TestConfig(t *testing.T) { 90 | testdata := analysistest.TestData() 91 | if err := FlagSet.Set("config", filepath.Join(testdata, "test-config.yaml")); err != nil { 92 | t.Fatal(err) 93 | } 94 | analysistest.Run(t, testdata, testAnalyzer, "./...") 95 | } 96 | -------------------------------------------------------------------------------- /internal/pkg/debug/render/render_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package render 16 | 17 | import ( 18 | "go/ast" 19 | "go/importer" 20 | "go/parser" 21 | "go/token" 22 | "go/types" 23 | "io/ioutil" 24 | "path/filepath" 25 | "strings" 26 | "testing" 27 | 28 | "github.com/google/go-cmp/cmp" 29 | "github.com/google/go-flow-levee/internal/pkg/utils" 30 | "golang.org/x/tools/go/ssa" 31 | "golang.org/x/tools/go/ssa/ssautil" 32 | ) 33 | 34 | func TestDOT(t *testing.T) { 35 | testGoldenFiles(t, DOT, "DOT", ".dot") 36 | } 37 | 38 | func TestSSA(t *testing.T) { 39 | testGoldenFiles(t, SSA, "SSA", ".ssa") 40 | } 41 | 42 | // testGoldenFiles tests the output of a rendering function against the golden files under testdata. 43 | func testGoldenFiles(t *testing.T, fn func(f *ssa.Function) string, fnName, ext string) { 44 | testdata, err := filepath.Abs("testdata") 45 | if err != nil { 46 | t.Fatal(err) 47 | } 48 | testfile := filepath.Join(testdata, "tests.go") 49 | ssaFuncs := extractSSAFuncs(t, testfile) 50 | for _, f := range ssaFuncs { 51 | goldenFilename := filepath.Join(testdata, f.Name()) + ext 52 | bytes, err := ioutil.ReadFile(goldenFilename) 53 | if err != nil { 54 | t.Fatal(err) 55 | } 56 | want := string(bytes) 57 | 58 | // Replace interface{} in golden files if necessary. 59 | if utils.DefaultEmptyInterface != "interface{}" { 60 | want = strings.ReplaceAll(want, "interface{}", utils.DefaultEmptyInterface) 61 | } 62 | 63 | got := fn(f) 64 | 65 | if diff := cmp.Diff(want, got); diff != "" { 66 | t.Errorf("render.%s(%s) diff (-want +got):\n%s", fnName, f.Name(), diff) 67 | } 68 | } 69 | } 70 | 71 | func extractSSAFuncs(t *testing.T, testfile string) []*ssa.Function { 72 | t.Helper() 73 | 74 | fset := token.NewFileSet() 75 | file, err := parser.ParseFile(fset, testfile, nil, parser.ParseComments) 76 | if err != nil { 77 | t.Fatal(err) 78 | } 79 | files := []*ast.File{file} 80 | 81 | pkg := types.NewPackage(file.Name.Name, "") 82 | ssaPkg, _, err := ssautil.BuildPackage( 83 | &types.Config{Importer: importer.Default()}, fset, pkg, files, ssa.SanityCheckFunctions) 84 | if err != nil { 85 | t.Fatal(err) 86 | } 87 | 88 | var functions []*ssa.Function 89 | for _, m := range ssaPkg.Members { 90 | if f, ok := m.(*ssa.Function); ok && !strings.HasPrefix(f.Name(), "init") { 91 | functions = append(functions, f) 92 | } 93 | } 94 | return functions 95 | } 96 | -------------------------------------------------------------------------------- /internal/pkg/debug/render/dot.go: -------------------------------------------------------------------------------- 1 | // Copyright 2019 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package render 16 | 17 | import ( 18 | "fmt" 19 | "strings" 20 | 21 | "github.com/google/go-flow-levee/internal/pkg/debug/node" 22 | "golang.org/x/tools/go/ssa" 23 | ) 24 | 25 | // DOT produces DOT source code representing the SSA graph for a function. 26 | func DOT(f *ssa.Function) string { 27 | return (&renderer{strings.Builder{}, f}).Render() 28 | } 29 | 30 | type renderer struct { 31 | strings.Builder 32 | f *ssa.Function 33 | } 34 | 35 | func (r *renderer) Render() string { 36 | r.init() 37 | r.writeSubgraphs() 38 | r.writeEdges() 39 | r.finish() 40 | return r.String() 41 | } 42 | 43 | func (r *renderer) init() { 44 | r.WriteString("digraph {\n") 45 | } 46 | 47 | func (r *renderer) writeSubgraphs() { 48 | for bi, b := range r.f.Blocks { 49 | r.WriteString(fmt.Sprintf("\tsubgraph cluster_%d {\n\t\tcolor=black;\n\t\tlabel=%q;\n", bi, b.Comment)) 50 | for _, i := range b.Instrs { 51 | n := i.(ssa.Node) 52 | r.WriteString(fmt.Sprintf("\t\t%q [shape=%s];\n", renderNode(n), nodeShape(n))) 53 | } 54 | r.WriteString("\t}\n") 55 | } 56 | } 57 | 58 | func (r *renderer) writeEdges() { 59 | for _, b := range r.f.Blocks { 60 | for _, i := range b.Instrs { 61 | // we only need to write the operands, since as per the ssa package docs, 62 | // the referrers relation is a subset of the operands relation 63 | r.writeOperands(i.(ssa.Node)) 64 | } 65 | } 66 | } 67 | 68 | func (r *renderer) writeOperands(n ssa.Node) { 69 | for _, o := range n.Operands(nil) { 70 | if *o == nil { 71 | continue 72 | } 73 | // Orange as in O-perand 74 | r.writeEdge((*o).(ssa.Node), n, "orange") 75 | } 76 | } 77 | 78 | func (r *renderer) writeEdge(from ssa.Node, to ssa.Node, color string) { 79 | r.WriteString(fmt.Sprintf("\t%q -> %q [color=%s];\n", renderNode(from), renderNode(to), color)) 80 | } 81 | 82 | func renderNode(n ssa.Node) string { 83 | return fmt.Sprintf("%s\n(%s)", node.CanonicalName(n), node.TrimmedType(n)) 84 | } 85 | 86 | func (r *renderer) finish() { 87 | r.WriteString("}\n") 88 | } 89 | 90 | func nodeShape(n ssa.Node) string { 91 | _, isValue := n.(ssa.Value) 92 | _, isInstr := n.(ssa.Instruction) 93 | switch { 94 | case isValue && isInstr: 95 | return "rectangle" 96 | case isInstr: 97 | return "diamond" 98 | default: 99 | return "ellipse" 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /internal/pkg/levee/testdata/src/levee_analysistest/example/tests/collections/maps.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package collections 16 | 17 | import ( 18 | "levee_analysistest/example/core" 19 | ) 20 | 21 | func TestMapLiteralContainingSourceKeyIsTainted(s core.Source) { 22 | m := map[core.Source]string{s: "source"} 23 | core.Sink(m) // want "a source has reached a sink" 24 | } 25 | 26 | func TestMapLiteralContainingSourceValueIsTainted(s core.Source) { 27 | m := map[string]core.Source{"source": s} 28 | core.Sink(m) // want "a source has reached a sink" 29 | } 30 | 31 | func TestMapIsTaintedWhenSourceIsInserted(s core.Source) { 32 | m := map[core.Source]core.Source{} 33 | m[s] = s 34 | core.Sink(m) // want "a source has reached a sink" 35 | } 36 | 37 | func TestTaintIsNotPropagatedwhenMapIsOverwritten(s core.Source) { 38 | m := map[string]interface{}{"source": s} 39 | core.Sink(m) // want "a source has reached a sink" 40 | m = nil 41 | core.Sink(m) 42 | } 43 | 44 | func TestValueObtainedFromTaintedMapIsTainted(s core.Source) { 45 | m := map[interface{}]string{s: "source"} 46 | v := m[0] 47 | core.Sink(v) // want "a source has reached a sink" 48 | } 49 | 50 | func TestMapRemainsTaintedWhenSourceIsDeleted(s core.Source) { 51 | m := map[interface{}]string{s: "source"} 52 | delete(m, s) 53 | core.Sink(m) // want "a source has reached a sink" 54 | } 55 | 56 | func TestDeletingFromTaintedMapDoesNotTaintKey(key *string, sources map[*string]core.Source) { 57 | // The key needs to be a pointer parameter, because we don't traverse to non-pointer 58 | // arguments of a call, and we don't traverse to Allocs. 59 | delete(sources, key) 60 | core.Sink(key) 61 | } 62 | 63 | func TestMapUpdateWithTaintedValueDoesNotTaintTheKey(key string, value core.Source, sources map[string]core.Source) { 64 | sources[key] = value 65 | core.Sink(key) 66 | } 67 | 68 | func TestMapUpdateWithTaintedKeyDoesNotTaintTheValue(key core.Source, value string, sources map[core.Source]string) { 69 | sources[key] = value 70 | core.Sink(value) 71 | } 72 | 73 | func TestRangeOverMapWithSourceAsValue() { 74 | m := map[string]core.Source{"secret": core.Source{Data: "password1234"}} 75 | for k, s := range m { 76 | core.Sink(s) // want "a source has reached a sink" 77 | core.Sink(k) 78 | } 79 | } 80 | 81 | func TestRangeOverMapWithSourceAsKey() { 82 | m := map[core.Source]string{core.Source{Data: "password1234"}: "don't sink me"} 83 | for src, str := range m { 84 | core.Sink(src) // want "a source has reached a sink" 85 | core.Sink(str) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Go Flow Levee 2 | 3 | This static analysis tool works to ensure your program's data flow does not spill beyond its banks. 4 | 5 | An input program's data flow is explored using a combination of pointer analysis, 6 | static single assignment analysis, and taint analysis. 7 | "Sources" must not reach "sinks" without first passing through a "sanitizer." 8 | Additionally, source data can "taint" neighboring variables during a "propagation" function call, 9 | such as writing a source to a string. 10 | Such tainted variables also must not reach any sink. 11 | 12 | Such analysis can be used to prevent the accidental logging of credentials or personally identifying information, 13 | defend against maliciously constructed user input, and enforce data communication restrictions between processes. 14 | 15 | ## User Guide 16 | 17 | See `guides/` for guided introductions on: 18 | * [How to configure and run the analyzer](guides/quickstart.md) 19 | * More topics coming soon! 20 | 21 | ## Motivation 22 | 23 | Much data should not be freely shared. 24 | For instance, secrets (e.g, OAuth tokens, passwords), 25 | personally identifiable information (e.g., name, email or mailing address), 26 | and other sensitive information (e.g., user payment info, information regulated by law) 27 | should typically be serialized only when necessary and should almost never be logged. 28 | However, as a program's type hierarchy becomes more complex or 29 | as program logic grows to warrant increasingly detailed logging, 30 | it is easy to overlook when a class might contain these sensitive data and 31 | which log statements might accidentally expose them. 32 | 33 | ## Technical design 34 | 35 | See [design/](design/README.md). 36 | 37 | ## Configuration 38 | 39 | See [configuration/](configuration/README.md) for configuration details. 40 | 41 | ## Reporting bugs 42 | 43 | Static taint propagation analysis is a hard problem. In fact, it is [undecidable](https://en.wikipedia.org/wiki/Rice%27s_theorem). Concretely, this means two things: 44 | * False negatives: the analyzer may fail to recognize that a piece of code is unsafe. 45 | * False positives: the analyzer may incorrectly claim that a safe piece of code is unsafe. 46 | 47 | Since taint propagation is often used as a security safeguard, we care more deeply about false negatives. If you discover unsafe code that the analyzer is not recognizing as unsafe, please open an issue [here](https://github.com/google/go-flow-levee/issues/new?template=false-negative.md). Conversely, false positives waste developer time and should also be addressed. If the analyzer produces a report for code that you consider to be safe, please open an issue [here](https://github.com/google/go-flow-levee/issues/new?template=false-positive.md). 48 | 49 | For general bug reports (e.g. crashes), please open an issue [here](https://github.com/google/go-flow-levee/issues/new?template=bug_report.md). 50 | 51 | ## Contributing 52 | 53 | See [CONTRIBUTING.md](CONTRIBUTING.md) for details. 54 | 55 | ## Developing 56 | 57 | See [DEVELOPING.md](DEVELOPING.md) for details. 58 | 59 | ## Disclaimer 60 | 61 | This is not an officially supported Google product. 62 | -------------------------------------------------------------------------------- /internal/pkg/utils/utils.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package utils contains various utility functions. 16 | package utils 17 | 18 | import ( 19 | "go/types" 20 | 21 | "golang.org/x/tools/go/ssa" 22 | ) 23 | 24 | // Dereference returns the underlying type of a pointer. 25 | // If the input is not a pointer, then the type of the input is returned. 26 | func Dereference(t types.Type) types.Type { 27 | for { 28 | tt, ok := t.Underlying().(*types.Pointer) 29 | if !ok { 30 | return t 31 | } 32 | t = tt.Elem() 33 | } 34 | } 35 | 36 | // DecomposeType returns the path and name of a Named type 37 | // Returns empty strings if the type is not *types.Named 38 | func DecomposeType(t types.Type) (path, name string) { 39 | n, ok := t.(*types.Named) 40 | if !ok { 41 | return 42 | } 43 | 44 | if pkg := n.Obj().Pkg(); pkg != nil { 45 | path = pkg.Path() 46 | } 47 | 48 | return path, n.Obj().Name() 49 | } 50 | 51 | // DecomposeField returns the decomposed type of the 52 | // struct containing the field, as well as the field's name. 53 | // If the referenced struct's type is not a named type, 54 | // the type path and name will both be empty strings. 55 | func DecomposeField(t types.Type, field int) (typePath, typeName, fieldName string) { 56 | deref := Dereference(t) 57 | typePath, typeName = DecomposeType(deref) 58 | fieldName = deref.Underlying().(*types.Struct).Field(field).Name() 59 | return 60 | } 61 | 62 | // UnqualifiedName returns the name of the given type, without the qualifying 63 | // prefix containing the package in which it was declared. 64 | // Example: for a type named T declared in package p, the returned string will 65 | // be just `T` instead of `p.T`. 66 | func UnqualifiedName(t *types.Var) string { 67 | return types.TypeString(t.Type(), func(*types.Package) string { return "" }) 68 | } 69 | 70 | // DecomposeFunction returns the path, receiver, and name strings of a ssa.Function. 71 | // For functions that have no receiver, returns an empty string for recv. 72 | // For shared functions (wrappers and error.Error), returns an empty string for path. 73 | // Panics if provided a nil argument. 74 | func DecomposeFunction(f *ssa.Function) (path, recv, name string) { 75 | if f.Pkg != nil { 76 | path = f.Pkg.Pkg.Path() 77 | } 78 | name = f.Name() 79 | if recvVar := f.Signature.Recv(); recvVar != nil { 80 | recv = UnqualifiedName(recvVar) 81 | } 82 | return 83 | } 84 | 85 | // EmptyInterfaceString is the string rendering of an empty interface, interface{}. 86 | // Changes based on the go version. 87 | var DefaultEmptyInterface = "interface{}" 88 | -------------------------------------------------------------------------------- /internal/pkg/propagation/stdlib.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package propagation 16 | 17 | import ( 18 | "github.com/google/go-flow-levee/internal/pkg/propagation/summary" 19 | "golang.org/x/tools/go/ssa" 20 | ) 21 | 22 | // taintStdlibCall propagates taint through a static call to a standard 23 | // library function, or through an implementation of a standard library 24 | // interface function, provided that the function's taint propagation behavior 25 | // is known (i.e. the function has a summary). 26 | func (prop *Propagation) taintStdlibCall(callInstr ssa.CallInstruction, maxInstrReached map[*ssa.BasicBlock]int, lastBlockVisited *ssa.BasicBlock) { 27 | summ := summary.For(callInstr) 28 | if summ == nil { 29 | return 30 | } 31 | 32 | var args []ssa.Value 33 | // For "invoke" calls, Value is the receiver 34 | if callInstr.Common().IsInvoke() { 35 | args = append(args, callInstr.Common().Value) 36 | } 37 | args = append(args, callInstr.Common().Args...) 38 | 39 | // Determine whether we need to propagate taint. 40 | tainted := int64(0) 41 | for i, a := range args { 42 | if prop.tainted[a.(ssa.Node)] { 43 | tainted |= 1 << i 44 | } 45 | } 46 | if (tainted & summ.IfTainted) == 0 { 47 | return 48 | } 49 | 50 | // Taint call arguments. 51 | for _, i := range summ.TaintedArgs { 52 | prop.taint(args[i].(ssa.Node), maxInstrReached, lastBlockVisited, false) 53 | } 54 | 55 | // Only actual Call instructions can have Referrers. 56 | call, ok := callInstr.(*ssa.Call) 57 | if !ok { 58 | return 59 | } 60 | 61 | // If there are no referrers, exit early. 62 | if call.Referrers() == nil { 63 | return 64 | } 65 | 66 | // If the call has a single return value, the return value is the call 67 | // instruction itself, so if the call's return value is tainted, taint 68 | // the Referrers. 69 | if call.Common().Signature().Results().Len() == 1 { 70 | if len(summ.TaintedRets) > 0 { 71 | prop.taintReferrers(call, maxInstrReached, lastBlockVisited) 72 | } 73 | return 74 | } 75 | 76 | // If the call has more than one return value, the call's Referrers will 77 | // contain one Extract for each returned value. There is no guarantee that 78 | // these will appear in order, so we create a map from the index of 79 | // each returned value to the corresponding Extract (the extracted value), 80 | // then we taint the Extracts. 81 | indexToExtract := map[int]*ssa.Extract{} 82 | for _, r := range *call.Referrers() { 83 | e := r.(*ssa.Extract) 84 | indexToExtract[e.Index] = e 85 | } 86 | for i := range summ.TaintedRets { 87 | prop.taint(indexToExtract[i], maxInstrReached, lastBlockVisited, true) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /internal/pkg/sourcetype/sourcetype.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // Package sourcetype handles identification of sources based on their type. 16 | package sourcetype 17 | 18 | import ( 19 | "fmt" 20 | "go/types" 21 | 22 | "github.com/google/go-flow-levee/internal/pkg/config" 23 | "github.com/google/go-flow-levee/internal/pkg/fieldtags" 24 | "github.com/google/go-flow-levee/internal/pkg/utils" 25 | ) 26 | 27 | // IsSourceType determines whether a Type is a Source Type. 28 | // A Source Type is either: 29 | // - A Named Struct Type that is configured as a Source 30 | // - A Struct Type that contains a tagged field 31 | // - A composite type that contains a Source Type 32 | func IsSourceType(c *config.Config, tf fieldtags.ResultType, t types.Type) bool { 33 | seen := map[types.Type]bool{} 34 | return isSourceType(c, tf, t, seen) 35 | } 36 | 37 | // isSourceType is a helper method for IsSourceType. 38 | // The set of seen types is kept track of to prevent infinite recursion on 39 | // types such as `type A map[string]A`, which refer to themselves. 40 | func isSourceType(c *config.Config, tf fieldtags.ResultType, t types.Type, seen map[types.Type]bool) bool { 41 | // If a type has been seen, then its status as a Source has already 42 | // been evaluated. Return to avoid infinite recursion. 43 | if seen[t] { 44 | return false 45 | } 46 | seen[t] = true 47 | 48 | switch tt := t.(type) { 49 | case *types.Named: 50 | return c.IsSourceType(utils.DecomposeType(tt)) || isSourceType(c, tf, tt.Underlying(), seen) 51 | case *types.Array: 52 | return isSourceType(c, tf, tt.Elem(), seen) 53 | case *types.Slice: 54 | return isSourceType(c, tf, tt.Elem(), seen) 55 | case *types.Chan: 56 | return isSourceType(c, tf, tt.Elem(), seen) 57 | case *types.Map: 58 | key := isSourceType(c, tf, tt.Key(), seen) 59 | elem := isSourceType(c, tf, tt.Elem(), seen) 60 | return key || elem 61 | case *types.Pointer: 62 | return isSourceType(c, tf, tt.Elem(), seen) 63 | case *types.Struct: 64 | return hasTaggedField(tf, tt) 65 | case *types.Basic, *types.Tuple, *types.Interface, *types.Signature: 66 | // These types do not currently represent possible source types 67 | return false 68 | default: 69 | // The above should be exhaustive. Reaching this default case is an error. 70 | fmt.Printf("unexpected type received: %T %v; please report this issue\n", tt, tt) 71 | return false 72 | } 73 | } 74 | 75 | func hasTaggedField(taggedFields fieldtags.ResultType, s *types.Struct) bool { 76 | for i := 0; i < s.NumFields(); i++ { 77 | f := s.Field(i) 78 | if taggedFields.IsSource(f) { 79 | return true 80 | } 81 | } 82 | return false 83 | } 84 | -------------------------------------------------------------------------------- /internal/pkg/levee/testdata/src/levee_analysistest/example/tests/callorder/multiblock.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package callorder 16 | 17 | import ( 18 | "fmt" 19 | "io" 20 | 21 | "levee_analysistest/example/core" 22 | ) 23 | 24 | func TestSinkInIfBeforeTaint(s core.Source, w io.Writer) { 25 | if true { 26 | core.Sink(w) 27 | } 28 | fmt.Fprintf(w, "%v", s) 29 | } 30 | 31 | func TestTaintInIfBeforeSink(s core.Source, w io.Writer) { 32 | if true { 33 | fmt.Fprintf(w, "%v", s) 34 | } 35 | core.Sink(w) // want "a source has reached a sink" 36 | } 37 | 38 | func TestSinkAndTaintInDifferentIfBranches(s core.Source, w io.Writer) { 39 | if true { 40 | fmt.Fprintf(w, "%v", s) 41 | } else { 42 | core.Sink(w) 43 | } 44 | } 45 | 46 | func TestSinkInIfBeforeTaintInIf(s core.Source, w io.Writer) { 47 | if true { 48 | core.Sink(w) 49 | } 50 | if true { 51 | fmt.Fprintf(w, "%v", s) 52 | } 53 | } 54 | 55 | func TestTaintInIfBeforeSinkInIf(s core.Source, w io.Writer) { 56 | if true { 57 | fmt.Fprintf(w, "%v", s) 58 | } 59 | if true { 60 | core.Sink(w) // want "a source has reached a sink" 61 | } 62 | } 63 | 64 | func TestSinkBeforeTaintInSameIfBlock(s core.Source, w io.Writer) { 65 | if true { 66 | core.Sink(w) 67 | fmt.Fprintf(w, "%v", s) 68 | } 69 | } 70 | 71 | func TestTaintBeforeSinkInSameIfBlock(s core.Source, w io.Writer) { 72 | if true { 73 | fmt.Fprintf(w, "%v", s) 74 | core.Sink(w) // want "a source has reached a sink" 75 | } 76 | } 77 | 78 | func TestSinkInNestedIfBeforeTaint(s core.Source, w io.Writer) { 79 | if true { 80 | if true { 81 | core.Sink(w) 82 | } 83 | } 84 | fmt.Fprintf(w, "%v", s) 85 | } 86 | 87 | func TestTaintInNestedIfBeforeSink(s core.Source, w io.Writer) { 88 | if true { 89 | if true { 90 | fmt.Fprintf(w, "%v", s) 91 | core.Sink(w) // want "a source has reached a sink" 92 | } 93 | core.Sink(w) // want "a source has reached a sink" 94 | } 95 | core.Sink(w) // want "a source has reached a sink" 96 | } 97 | 98 | func TestSinkAndTaintInSeparateSwitchCases(s core.Source, w io.Writer) { 99 | switch "true" { 100 | case "true": 101 | core.Sink(w) 102 | case "false": 103 | fmt.Fprintf(w, "%v", s) 104 | } 105 | } 106 | 107 | func TestSinkAfterTaintInSwitch(s core.Source, w io.Writer) { 108 | switch "true" { 109 | case "true": 110 | fmt.Fprintf(w, "%v", s) 111 | } 112 | core.Sink(w) // want "a source has reached a sink" 113 | } 114 | 115 | func TestSinkAfterTaintInFor(sources []core.Source, w io.Writer) { 116 | for i := 0; i < len(sources); i++ { 117 | fmt.Fprintf(w, "%v", sources[i]) 118 | } 119 | core.Sink(w) // want "a source has reached a sink" 120 | } 121 | -------------------------------------------------------------------------------- /internal/pkg/levee/testdata/src/levee_analysistest/example/tests/loops/tests.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Google LLC 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // https://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package loops 16 | 17 | import ( 18 | "levee_analysistest/example/core" 19 | ) 20 | 21 | func TestTaintInThenBlockInLoopSinkAfterLoop() { 22 | var e interface{} 23 | for true { 24 | if true { 25 | e = core.Source{} 26 | } else { 27 | e = nil 28 | } 29 | } 30 | core.Sink(e) // want "a source has reached a sink" 31 | } 32 | 33 | func TestTaintInElseBlockInLoopSinkAfterLoop() { 34 | var e interface{} 35 | for true { 36 | if true { 37 | e = nil 38 | } else { 39 | e = core.Source{} 40 | } 41 | } 42 | core.Sink(e) // want "a source has reached a sink" 43 | } 44 | 45 | func TestTaintInThenBlockSinkInElseBlockInLoop() { 46 | var e interface{} 47 | for true { 48 | if true { 49 | e = core.Source{} 50 | } else { 51 | core.Sink(e) // want "a source has reached a sink" 52 | } 53 | } 54 | } 55 | 56 | func TestTaintInElseBlockSinkInThenBlockInLoop() { 57 | var e interface{} 58 | for true { 59 | if true { 60 | e = core.Source{} 61 | } else { 62 | core.Sink(e) // want "a source has reached a sink" 63 | } 64 | } 65 | } 66 | 67 | func TestTaintInNestedConditionalInLoop() { 68 | var e interface{} 69 | for true { 70 | if true { 71 | if true { 72 | e = nil 73 | } else { 74 | e = core.Source{} 75 | } 76 | } else { 77 | e = nil 78 | } 79 | } 80 | core.Sink(e) // want "a source has reached a sink" 81 | } 82 | 83 | func TestTaintPropagationOverMultipleIterations() { 84 | var e1 interface{} 85 | var e2 interface{} 86 | for true { 87 | if true { 88 | e1 = core.Source{} 89 | } else { 90 | e2 = e1 91 | } 92 | } 93 | core.Sink(e1) // want "a source has reached a sink" 94 | core.Sink(e2) // want "a source has reached a sink" 95 | } 96 | 97 | func TestTaintPropagationOverMultipleIterationsWithNestedConditionals() { 98 | var e1 interface{} 99 | var e2 interface{} 100 | var e3 interface{} 101 | var e4 interface{} 102 | for true { 103 | if true { 104 | e1 = core.Source{} 105 | } else { 106 | if true { 107 | e4 = e3 108 | } else { 109 | e3 = e2 110 | } 111 | e2 = e1 112 | } 113 | } 114 | core.Sink(e1) // want "a source has reached a sink" 115 | core.Sink(e2) // want "a source has reached a sink" 116 | core.Sink(e3) // want "a source has reached a sink" 117 | core.Sink(e4) // want "a source has reached a sink" 118 | } 119 | 120 | func TestSourceOverwrittenBeforeLoopExit() { 121 | var e interface{} 122 | for true { 123 | if true { 124 | e = nil 125 | } else { 126 | e = core.Source{} 127 | } 128 | e = nil 129 | } 130 | core.Sink(e) 131 | } 132 | -------------------------------------------------------------------------------- /internal/pkg/debug/render/testdata/TestSingleBlock.dot: -------------------------------------------------------------------------------- 1 | digraph { 2 | subgraph cluster_0 { 3 | color=black; 4 | label="entry"; 5 | "t0 = local image.Point (p)\n(Alloc)" [shape=rectangle]; 6 | "t1 = &t0.X [#0]\n(FieldAddr)" [shape=rectangle]; 7 | "t2 = &t0.Y [#1]\n(FieldAddr)" [shape=rectangle]; 8 | "*t1 = 1:int\n(Store)" [shape=diamond]; 9 | "*t2 = 2:int\n(Store)" [shape=diamond]; 10 | "t3 = &t0.X [#0]\n(FieldAddr)" [shape=rectangle]; 11 | "*t3 = 3:int\n(Store)" [shape=diamond]; 12 | "t4 = &t0.Y [#1]\n(FieldAddr)" [shape=rectangle]; 13 | "*t4 = 4:int\n(Store)" [shape=diamond]; 14 | "t5 = &t0.X [#0]\n(FieldAddr)" [shape=rectangle]; 15 | "t6 = *t5\n(UnOp)" [shape=rectangle]; 16 | "t7 = &t0.Y [#1]\n(FieldAddr)" [shape=rectangle]; 17 | "t8 = *t7\n(UnOp)" [shape=rectangle]; 18 | "t9 = t6 + t8\n(BinOp)" [shape=rectangle]; 19 | "t10 = new [1]interface{} (varargs)\n(Alloc)" [shape=rectangle]; 20 | "t11 = &t10[0:int]\n(IndexAddr)" [shape=rectangle]; 21 | "t12 = make interface{} <- int (t9)\n(MakeInterface)" [shape=rectangle]; 22 | "*t11 = t12\n(Store)" [shape=diamond]; 23 | "t13 = slice t10[:]\n(Slice)" [shape=rectangle]; 24 | "t14 = fmt.Println(t13...)\n(Call)" [shape=rectangle]; 25 | "return\n(Return)" [shape=diamond]; 26 | } 27 | "t0 = local image.Point (p)\n(Alloc)" -> "t1 = &t0.X [#0]\n(FieldAddr)" [color=orange]; 28 | "t0 = local image.Point (p)\n(Alloc)" -> "t2 = &t0.Y [#1]\n(FieldAddr)" [color=orange]; 29 | "t1 = &t0.X [#0]\n(FieldAddr)" -> "*t1 = 1:int\n(Store)" [color=orange]; 30 | "1:int\n(Const)" -> "*t1 = 1:int\n(Store)" [color=orange]; 31 | "t2 = &t0.Y [#1]\n(FieldAddr)" -> "*t2 = 2:int\n(Store)" [color=orange]; 32 | "2:int\n(Const)" -> "*t2 = 2:int\n(Store)" [color=orange]; 33 | "t0 = local image.Point (p)\n(Alloc)" -> "t3 = &t0.X [#0]\n(FieldAddr)" [color=orange]; 34 | "t3 = &t0.X [#0]\n(FieldAddr)" -> "*t3 = 3:int\n(Store)" [color=orange]; 35 | "3:int\n(Const)" -> "*t3 = 3:int\n(Store)" [color=orange]; 36 | "t0 = local image.Point (p)\n(Alloc)" -> "t4 = &t0.Y [#1]\n(FieldAddr)" [color=orange]; 37 | "t4 = &t0.Y [#1]\n(FieldAddr)" -> "*t4 = 4:int\n(Store)" [color=orange]; 38 | "4:int\n(Const)" -> "*t4 = 4:int\n(Store)" [color=orange]; 39 | "t0 = local image.Point (p)\n(Alloc)" -> "t5 = &t0.X [#0]\n(FieldAddr)" [color=orange]; 40 | "t5 = &t0.X [#0]\n(FieldAddr)" -> "t6 = *t5\n(UnOp)" [color=orange]; 41 | "t0 = local image.Point (p)\n(Alloc)" -> "t7 = &t0.Y [#1]\n(FieldAddr)" [color=orange]; 42 | "t7 = &t0.Y [#1]\n(FieldAddr)" -> "t8 = *t7\n(UnOp)" [color=orange]; 43 | "t6 = *t5\n(UnOp)" -> "t9 = t6 + t8\n(BinOp)" [color=orange]; 44 | "t8 = *t7\n(UnOp)" -> "t9 = t6 + t8\n(BinOp)" [color=orange]; 45 | "t10 = new [1]interface{} (varargs)\n(Alloc)" -> "t11 = &t10[0:int]\n(IndexAddr)" [color=orange]; 46 | "0:int\n(Const)" -> "t11 = &t10[0:int]\n(IndexAddr)" [color=orange]; 47 | "t9 = t6 + t8\n(BinOp)" -> "t12 = make interface{} <- int (t9)\n(MakeInterface)" [color=orange]; 48 | "t11 = &t10[0:int]\n(IndexAddr)" -> "*t11 = t12\n(Store)" [color=orange]; 49 | "t12 = make interface{} <- int (t9)\n(MakeInterface)" -> "*t11 = t12\n(Store)" [color=orange]; 50 | "t10 = new [1]interface{} (varargs)\n(Alloc)" -> "t13 = slice t10[:]\n(Slice)" [color=orange]; 51 | "Println\n(Function)" -> "t14 = fmt.Println(t13...)\n(Call)" [color=orange]; 52 | "t13 = slice t10[:]\n(Slice)" -> "t14 = fmt.Println(t13...)\n(Call)" [color=orange]; 53 | } 54 | --------------------------------------------------------------------------------