Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge experimental / refactor #146

Merged
merged 26 commits into from
Jan 5, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
9b08174
Process via packages instead of files
gcmurphy Apr 25, 2017
8df48f9
Fix to reporting to use output formats
gcmurphy Apr 26, 2017
cacf21f
Restructure to focus on lib rather than cli
gcmurphy Apr 26, 2017
bf78d02
Restructure and introduce a standalone config
gcmurphy Apr 28, 2017
50bbc53
Isolate import tracking functionality
gcmurphy May 10, 2017
5160048
Move rule definitions into own file
gcmurphy May 10, 2017
65b18da
Hack to address circular dependency in rulelist
gcmurphy May 10, 2017
026fe4c
Simplify analyzer and command line interface
gcmurphy May 10, 2017
f4b705a
Use glide to manage vendored dependencies
gcmurphy May 10, 2017
6943f9e
Major rework of codebase
gcmurphy Jul 19, 2017
3caf7c3
Add test cases
gcmurphy Sep 16, 2017
9c959ca
Issue.Line is already a string
lanzafame Oct 1, 2017
5a11336
remove commited binary
lanzafame Oct 1, 2017
27b2fd9
Merge pull request #136 from lanzafame/experimental
gcmurphy Oct 4, 2017
67dc432
use godep instead of glide
gcmurphy Dec 13, 2017
d4311c9
make it clear that these tests have not been implemented yet
gcmurphy Dec 13, 2017
02901b9
actually skip tests until implementation exists
gcmurphy Dec 13, 2017
e3b6fd9
update readme to provide info regarding package level scans
gcmurphy Dec 13, 2017
97cde35
update travis-ci to use ginkgo tests
gcmurphy Dec 13, 2017
cfa4327
fix hound-ci errors
gcmurphy Dec 13, 2017
af25ac1
fix golint errors picked up by hound-ci
gcmurphy Dec 13, 2017
25d74c6
address review comments
gcmurphy Dec 14, 2017
e925d3c
Migrated old test cases.
gcmurphy Dec 28, 2017
4c49716
move utils to separate executable
gcmurphy Dec 28, 2017
d452dcb
Fix ginko invocation
gcmurphy Jan 5, 2018
867d300
Fix lint issues
gcmurphy Jan 5, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
fix golint errors picked up by hound-ci
  • Loading branch information
gcmurphy committed Dec 13, 2017
commit af25ac1f6ef6f7c6be2526fcca9521a7fbeefeb7
5 changes: 4 additions & 1 deletion analyzer.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ type Metrics struct {
NumFound int `json:"found"`
}

// The Analyzer object is the main object of GAS. It has methods traverse an AST
// Analyzer object is the main object of GAS. It has methods traverse an AST
// and invoke the correct checking rules as on each node as required.
type Analyzer struct {
ignoreNosec bool
Expand Down Expand Up @@ -83,13 +83,16 @@ func NewAnalyzer(conf Config, logger *log.Logger) *Analyzer {
}
}

// LoadRules instantiates all the rules to be used when analyzing source
// packages
func (gas *Analyzer) LoadRules(ruleDefinitions ...RuleBuilder) {
for _, builder := range ruleDefinitions {
r, nodes := builder(gas.config)
gas.ruleset.Register(r, nodes...)
}
}

// Process kicks off the analysis process for a given package
func (gas *Analyzer) Process(packagePath string) error {

basePackage, err := build.Default.ImportDir(packagePath, build.ImportComment)
Expand Down
14 changes: 7 additions & 7 deletions call_list.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,31 +19,31 @@ import (

type set map[string]bool

/// CallList is used to check for usage of specific packages
/// and functions.
// CallList is used to check for usage of specific packages
// and functions.
type CallList map[string]set

/// NewCallList creates a new empty CallList
// NewCallList creates a new empty CallList
func NewCallList() CallList {
return make(CallList)
}

/// AddAll will add several calls to the call list at once
// AddAll will add several calls to the call list at once
func (c CallList) AddAll(selector string, idents ...string) {
for _, ident := range idents {
c.Add(selector, ident)
}
}

/// Add a selector and call to the call list
// Add a selector and call to the call list
func (c CallList) Add(selector, ident string) {
if _, ok := c[selector]; !ok {
c[selector] = make(set)
}
c[selector][ident] = true
}

/// Contains returns true if the package and function are
// Contains returns true if the package and function are
/// members of this call list.
func (c CallList) Contains(selector, ident string) bool {
if idents, ok := c[selector]; ok {
Expand All @@ -53,7 +53,7 @@ func (c CallList) Contains(selector, ident string) bool {
return false
}

/// ContainsCallExpr resolves the call expression name and type
// ContainsCallExpr resolves the call expression name and type
/// or package and determines if it exists within the CallList
func (c CallList) ContainsCallExpr(n ast.Node, ctx *Context) *ast.CallExpr {
selector, ident, err := GetCallInfo(n, ctx)
Expand Down
12 changes: 8 additions & 4 deletions issue.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package gas

import (
Expand All @@ -25,12 +26,15 @@ import (
type Score int

const (
Low Score = iota // Low value
Medium // Medium value
High // High value
// Low severity or confidence
Low Score = iota
// Medium severity or confidence
Medium
// High severity or confidence
High
)

// An Issue is returnd by a GAS rule if it discovers an issue with the scanned code.
// Issue is returnd by a GAS rule if it discovers an issue with the scanned code.
type Issue struct {
Severity Score `json:"severity"` // issue severity (how problematic it is)
Confidence Score `json:"confidence"` // issue confidence (how sure we are we found it)
Expand Down
2 changes: 1 addition & 1 deletion issue_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ var _ = Describe("Issue", func() {

// Use SQL rule to check binary expr
cfg := gas.NewConfig()
rule, _ := rules.NewSqlStrConcat(cfg)
rule, _ := rules.NewSQLStrConcat(cfg)
issue, err := rule.Match(target, ctx)
Expect(err).ShouldNot(HaveOccurred())
Expect(issue).ShouldNot(BeNil())
Expand Down
11 changes: 8 additions & 3 deletions output/formatter.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,18 @@ import (
"github.com/GoASTScanner/gas"
)

// The output format for reported issues
// ReportFormat enumrates the output format for reported issues
type ReportFormat int

const (
// ReportText is the default format that writes to stdout
ReportText ReportFormat = iota // Plain text format
ReportJSON // Json format
ReportCSV // CSV format

// ReportJSON set the output format to json
ReportJSON // Json format

// ReportCSV set the output format to csv
ReportCSV // CSV format
)

var text = `Results:
Expand Down
8 changes: 5 additions & 3 deletions rules/big.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,20 +20,22 @@ import (
"github.com/GoASTScanner/gas"
)

type UsingBigExp struct {
type usingBigExp struct {
gas.MetaData
pkg string
calls []string
}

func (r *UsingBigExp) Match(n ast.Node, c *gas.Context) (gi *gas.Issue, err error) {
func (r *usingBigExp) Match(n ast.Node, c *gas.Context) (gi *gas.Issue, err error) {
if _, matched := gas.MatchCallByType(n, c, r.pkg, r.calls...); matched {
return gas.NewIssue(c, n, r.What, r.Severity, r.Confidence), nil
}
return nil, nil
}

// NewUsingBigExp detects issues with modulus == 0 for Bignum
func NewUsingBigExp(conf gas.Config) (gas.Rule, []ast.Node) {
return &UsingBigExp{
return &usingBigExp{
pkg: "*math/big.Int",
calls: []string{"Exp"},
MetaData: gas.MetaData{
Expand Down
8 changes: 5 additions & 3 deletions rules/bind.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,13 @@ import (
)

// Looks for net.Listen("0.0.0.0") or net.Listen(":8080")
type BindsToAllNetworkInterfaces struct {
type bindsToAllNetworkInterfaces struct {
gas.MetaData
calls gas.CallList
pattern *regexp.Regexp
}

func (r *BindsToAllNetworkInterfaces) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
func (r *bindsToAllNetworkInterfaces) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
callExpr := r.calls.ContainsCallExpr(n, c)
if callExpr == nil {
return nil, nil
Expand All @@ -41,11 +41,13 @@ func (r *BindsToAllNetworkInterfaces) Match(n ast.Node, c *gas.Context) (*gas.Is
return nil, nil
}

// NewBindsToAllNetworkInterfaces detects socket connections that are setup to
// listen on all network interfaces.
func NewBindsToAllNetworkInterfaces(conf gas.Config) (gas.Rule, []ast.Node) {
calls := gas.NewCallList()
calls.Add("net", "Listen")
calls.Add("tls", "Listen")
return &BindsToAllNetworkInterfaces{
return &bindsToAllNetworkInterfaces{
calls: calls,
pattern: regexp.MustCompile(`^(0.0.0.0|:).*$`),
MetaData: gas.MetaData{
Expand Down
71 changes: 34 additions & 37 deletions rules/blacklist.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,60 +20,57 @@ import (
"github.com/GoASTScanner/gas"
)

type BlacklistImport struct {
type blacklistedImport struct {
gas.MetaData
Path string
Blacklisted map[string]string
}

func (r *BlacklistImport) Match(n ast.Node, c *gas.Context) (gi *gas.Issue, err error) {
func (r *blacklistedImport) Match(n ast.Node, c *gas.Context) (gi *gas.Issue, err error) {
if node, ok := n.(*ast.ImportSpec); ok {
if r.Path == node.Path.Value && node.Name.String() != "_" {
return gas.NewIssue(c, n, r.What, r.Severity, r.Confidence), nil
description, ok := r.Blacklisted[node.Path.Value]
if ok && node.Name.String() != "_" {
return gas.NewIssue(c, n, description, r.Severity, r.Confidence), nil
}
}
return nil, nil
}

func NewBlacklist_crypto_md5(conf gas.Config) (gas.Rule, []ast.Node) {
return &BlacklistImport{
// NewBlacklistedImports reports when a blacklisted import is being used.
// Typically when a deprecated technology is being used.
func NewBlacklistedImports(conf gas.Config, blacklist map[string]string) (gas.Rule, []ast.Node) {
return &blacklistedImport{
MetaData: gas.MetaData{
Severity: gas.High,
Severity: gas.Medium,
Confidence: gas.High,
What: "Use of weak cryptographic primitive",
},
Path: `"crypto/md5"`,
Blacklisted: blacklist,
}, []ast.Node{(*ast.ImportSpec)(nil)}
}

func NewBlacklist_crypto_des(conf gas.Config) (gas.Rule, []ast.Node) {
return &BlacklistImport{
MetaData: gas.MetaData{
Severity: gas.High,
Confidence: gas.High,
What: "Use of weak cryptographic primitive",
},
Path: `"crypto/des"`,
}, []ast.Node{(*ast.ImportSpec)(nil)}
// NewBlacklistedImportMD5 fails if MD5 is imported
func NewBlacklistedImportMD5(conf gas.Config) (gas.Rule, []ast.Node) {
return NewBlacklistedImports(conf, map[string]string{
"crypto/md5": "Use of weak cryptographic primitive",
})
}

func NewBlacklist_crypto_rc4(conf gas.Config) (gas.Rule, []ast.Node) {
return &BlacklistImport{
MetaData: gas.MetaData{
Severity: gas.High,
Confidence: gas.High,
What: "Use of weak cryptographic primitive",
},
Path: `"crypto/rc4"`,
}, []ast.Node{(*ast.ImportSpec)(nil)}
// NewBlacklistedImportDES fails if DES is imported
func NewBlacklistedImportDES(conf gas.Config) (gas.Rule, []ast.Node) {
return NewBlacklistedImports(conf, map[string]string{
"crypto/des": "Use of weak cryptographic primitive",
})
}

func NewBlacklist_net_http_cgi(conf gas.Config) (gas.Rule, []ast.Node) {
return &BlacklistImport{
MetaData: gas.MetaData{
Severity: gas.High,
Confidence: gas.High,
What: "Go versions < 1.6.3 are vulnerable to Httpoxy attack: (CVE-2016-5386)",
},
Path: `"net/http/cgi"`,
}, []ast.Node{(*ast.ImportSpec)(nil)}
// NewBlacklistedImportRC4 fails if DES is imported
func NewBlacklistedImportRC4(conf gas.Config) (gas.Rule, []ast.Node) {
return NewBlacklistedImports(conf, map[string]string{
"crypto/rc4": "Use of weak cryptographic primitive",
})
}

// NewBlacklistedImportCGI fails if CGI is imported
func NewBlacklistedImportCGI(conf gas.Config) (gas.Rule, []ast.Node) {
return NewBlacklistedImports(conf, map[string]string{
"net/http/cgi": "Go versions < 1.6.3 are vulnerable to Httpoxy attack: (CVE-2016-5386)",
})
}
9 changes: 5 additions & 4 deletions rules/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import (
"github.com/GoASTScanner/gas"
)

type NoErrorCheck struct {
type noErrorCheck struct {
gas.MetaData
whitelist gas.CallList
}
Expand All @@ -30,7 +30,7 @@ func returnsError(callExpr *ast.CallExpr, ctx *gas.Context) int {
if tv := ctx.Info.TypeOf(callExpr); tv != nil {
switch t := tv.(type) {
case *types.Tuple:
for pos := 0; pos < t.Len(); pos += 1 {
for pos := 0; pos < t.Len(); pos++ {
variable := t.At(pos)
if variable != nil && variable.Type().String() == "error" {
return pos
Expand All @@ -45,7 +45,7 @@ func returnsError(callExpr *ast.CallExpr, ctx *gas.Context) int {
return -1
}

func (r *NoErrorCheck) Match(n ast.Node, ctx *gas.Context) (*gas.Issue, error) {
func (r *noErrorCheck) Match(n ast.Node, ctx *gas.Context) (*gas.Issue, error) {
switch stmt := n.(type) {
case *ast.AssignStmt:
for _, expr := range stmt.Rhs {
Expand All @@ -70,6 +70,7 @@ func (r *NoErrorCheck) Match(n ast.Node, ctx *gas.Context) (*gas.Issue, error) {
return nil, nil
}

// NewNoErrorCheck detects if the returned error is unchecked
func NewNoErrorCheck(conf gas.Config) (gas.Rule, []ast.Node) {

// TODO(gm) Come up with sensible defaults here. Or flip it to use a
Expand All @@ -86,7 +87,7 @@ func NewNoErrorCheck(conf gas.Config) (gas.Rule, []ast.Node) {
}
}
}
return &NoErrorCheck{
return &noErrorCheck{
MetaData: gas.MetaData{
Severity: gas.Low,
Confidence: gas.High,
Expand Down
14 changes: 9 additions & 5 deletions rules/fileperms.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ import (
"github.com/GoASTScanner/gas"
)

type FilePermissions struct {
type filePermissions struct {
gas.MetaData
mode int64
pkg string
calls []string
}

func getConfiguredMode(conf map[string]interface{}, configKey string, defaultMode int64) int64 {
var mode int64 = defaultMode
var mode = defaultMode
if value, ok := conf[configKey]; ok {
switch value.(type) {
case int64:
Expand All @@ -46,7 +46,7 @@ func getConfiguredMode(conf map[string]interface{}, configKey string, defaultMod
return mode
}

func (r *FilePermissions) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
func (r *filePermissions) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
if callexpr, matched := gas.MatchCallByPackage(n, c, r.pkg, r.calls...); matched {
modeArg := callexpr.Args[len(callexpr.Args)-1]
if mode, err := gas.GetInt(modeArg); err == nil && mode > r.mode {
Expand All @@ -56,9 +56,11 @@ func (r *FilePermissions) Match(n ast.Node, c *gas.Context) (*gas.Issue, error)
return nil, nil
}

// NewFilePerms creates a rule to detect file creation with a more permissive than configured
// permission mask.
func NewFilePerms(conf gas.Config) (gas.Rule, []ast.Node) {
mode := getConfiguredMode(conf, "G302", 0600)
return &FilePermissions{
return &filePermissions{
mode: mode,
pkg: "os",
calls: []string{"OpenFile", "Chmod"},
Expand All @@ -70,9 +72,11 @@ func NewFilePerms(conf gas.Config) (gas.Rule, []ast.Node) {
}, []ast.Node{(*ast.CallExpr)(nil)}
}

// NewMkdirPerms creates a rule to detect directory creation with more permissive than
// configured permission mask.
func NewMkdirPerms(conf gas.Config) (gas.Rule, []ast.Node) {
mode := getConfiguredMode(conf, "G301", 0700)
return &FilePermissions{
return &filePermissions{
mode: mode,
pkg: "os",
calls: []string{"Mkdir", "MkdirAll"},
Expand Down
Loading