Skip to content

Commit

Permalink
Declare result types for analyzers.
Browse files Browse the repository at this point in the history
This change declares each of the analyzers in the analyzers
package to have a result type of
`[]github.com/praetorian-inc/gokart/util.Finding`.

A test has also been included that runs a minimal `*analysis.Pass`
through each analyzer's `Run` function, reflectively determines the
type of the result value, and compares it to the declared result type
of the analyzer.

Resolves praetorian-inc#76.
  • Loading branch information
hxtk committed Jul 30, 2022
1 parent 2a2120f commit 4f5e697
Show file tree
Hide file tree
Showing 9 changed files with 71 additions and 24 deletions.
9 changes: 5 additions & 4 deletions analyzers/cmdi.go
Expand Up @@ -25,10 +25,11 @@ import (
// converts all variables to SSA form to construct a call graph and performs
// recursive taint analysis to search for input sources of user-controllable data
var CommandInjectionAnalyzer = &analysis.Analyzer{
Name: "command_injection",
Doc: "reports when command injection can occur",
Run: cmdInjectionRun,
Requires: []*analysis.Analyzer{buildssa.Analyzer},
Name: "command_injection",
Doc: "reports when command injection can occur",
Run: cmdInjectionRun,
Requires: []*analysis.Analyzer{buildssa.Analyzer},
ResultType: resultType,
}

// vulnCmdInjectionFuncs() returns a map of command injection functions that may be vulnerable when used with user controlled input
Expand Down
9 changes: 5 additions & 4 deletions analyzers/generic.go
Expand Up @@ -71,10 +71,11 @@ func LoadGenericAnalyzers() []*analysis.Analyzer {
return genericFunctionRun(pass, vulnCalls, analyzerName, message)
}
analysisRun := analysis.Analyzer{
Name: analyzerName,
Doc: analyzerDict.Doc,
Run: analyzerFunc,
Requires: []*analysis.Analyzer{buildssa.Analyzer},
Name: analyzerName,
Doc: analyzerDict.Doc,
Run: analyzerFunc,
Requires: []*analysis.Analyzer{buildssa.Analyzer},
ResultType: resultType,
}
analyzers = append(analyzers, &analysisRun)
}
Expand Down
26 changes: 26 additions & 0 deletions analyzers/result_type_test.go
@@ -0,0 +1,26 @@
package analyzers

import (
"reflect"
"testing"

"github.com/praetorian-inc/gokart/test/testutil"
)

func TestResultTypes(t *testing.T) {
for _, a := range Analyzers {
t.Run(a.Name, func(t *testing.T) {
in := testutil.MinimalPass(a)
x, _ := a.Run(in)
if got := reflect.TypeOf(x); got != a.ResultType {
t.Errorf(
"x, _ := %v.Run(%v); reflect.TypeOf(x) = %v, want %v",
a.Name,
in,
got,
a.ResultType,
)
}
})
}
}
9 changes: 5 additions & 4 deletions analyzers/rsa.go
Expand Up @@ -31,10 +31,11 @@ import (
// all variables are converted to SSA form and a call graph is constructed
// recursive analysis is then used to resolve variables used as a key length to a final constant value at the callsite
var RsaKeylenAnalyzer = &analysis.Analyzer{
Name: "rsa_keylen",
Doc: "reports when rsa keys are too short",
Run: rsaRun,
Requires: []*analysis.Analyzer{buildssa.Analyzer},
Name: "rsa_keylen",
Doc: "reports when rsa keys are too short",
Run: rsaRun,
Requires: []*analysis.Analyzer{buildssa.Analyzer},
ResultType: resultType,
}

const RECOMMENDED_KEYLEN = 2048
Expand Down
3 changes: 3 additions & 0 deletions analyzers/scan.go
Expand Up @@ -25,6 +25,7 @@ import (
"log"
"os"
"path/filepath"
"reflect"
"strings"
"time"

Expand All @@ -33,6 +34,8 @@ import (
"golang.org/x/tools/go/analysis"
)

var resultType = reflect.TypeOf([]util.Finding(nil))

var Analyzers = []*analysis.Analyzer{
RsaKeylenAnalyzer,
PathTraversalAnalyzer,
Expand Down
9 changes: 5 additions & 4 deletions analyzers/sqli.go
Expand Up @@ -26,10 +26,11 @@ import (
// all variables are converted to SSA form and a call graph is constructed
// recursive taint analysis is then used to search from a given Sink up the callgraph for Sources of user-controllable data
var SQLInjectionAnalyzer = &analysis.Analyzer{
Name: "sql_injection",
Doc: "reports when SQL injection can occur",
Run: sqlRun,
Requires: []*analysis.Analyzer{buildssa.Analyzer},
Name: "sql_injection",
Doc: "reports when SQL injection can occur",
Run: sqlRun,
Requires: []*analysis.Analyzer{buildssa.Analyzer},
ResultType: resultType,
}

// grab_vulnerable_sql_functions() creates map of vulnerable functions that the scanner will check
Expand Down
9 changes: 5 additions & 4 deletions analyzers/ssrf.go
Expand Up @@ -26,10 +26,11 @@ import (
// converts all variables to SSA form to construct a call graph and performs
// recursive taint analysis to search for input sources of user-controllable data
var SSRFAnalyzer = &analysis.Analyzer{
Name: "SSRF",
Doc: "reports when SSRF vulnerabilities can occur",
Run: ssrfRun,
Requires: []*analysis.Analyzer{buildssa.Analyzer},
Name: "SSRF",
Doc: "reports when SSRF vulnerabilities can occur",
Run: ssrfRun,
Requires: []*analysis.Analyzer{buildssa.Analyzer},
ResultType: resultType,
}

// vulnerable_ssrf_funcs() returns a map of networking functions that may be vulnerable when used with user controlled input
Expand Down
9 changes: 5 additions & 4 deletions analyzers/traversal.go
Expand Up @@ -24,10 +24,11 @@ import (
// all variables are converted to SSA form and a call graph is constructed
// recursive taint analysis is then used to search from a given Sink up the callgraph for Sources of user-controllable data
var PathTraversalAnalyzer = &analysis.Analyzer{
Name: "path_traversal",
Doc: "reports when path traversal can occur",
Run: traversalRun,
Requires: []*analysis.Analyzer{buildssa.Analyzer},
Name: "path_traversal",
Doc: "reports when path traversal can occur",
Run: traversalRun,
Requires: []*analysis.Analyzer{buildssa.Analyzer},
ResultType: resultType,
}

// getVulnerableInjectionFunctions() returns a map of functions that may be vulnerable to path traversal when used with user controlled input
Expand Down
12 changes: 12 additions & 0 deletions test/testutil/run.go
Expand Up @@ -15,14 +15,26 @@
package testutil

import (
"go/types"
"path/filepath"
"runtime"
"testing"

"github.com/praetorian-inc/gokart/run"
"golang.org/x/tools/go/analysis"
"golang.org/x/tools/go/analysis/passes/buildssa"
)

func MinimalPass(a *analysis.Analyzer) *analysis.Pass {
return &analysis.Pass{
Analyzer: a,
ResultOf: map[*analysis.Analyzer]interface{}{
buildssa.Analyzer: new(buildssa.SSA),
},
Pkg: types.NewPackage("./foo", "bar"),
}
}

func RunTest(file string, numResults int, resultsType string, analyzer *analysis.Analyzer, t *testing.T) {
_, b, _, _ := runtime.Caller(0)
basepath := filepath.Dir(b)
Expand Down

0 comments on commit 4f5e697

Please sign in to comment.