From 1923b6d18e761dd94f14defea76163698b1f35af Mon Sep 17 00:00:00 2001 From: Cosmin Cojocar Date: Wed, 18 Jul 2018 14:31:07 +0200 Subject: [PATCH 1/7] Rule which detects a potential path traversal when extracting zip archives (#208) * Add a rule which detects file path traversal when extracting zip archive * Detect if any argument is derived from zip.File * Drop support for Go version 1.8 --- .travis.yml | 1 - README.md | 1 + rules/archive.go | 60 +++++++++++++++++++++++++++++ rules/rulelist.go | 1 + rules/rules_test.go | 4 ++ testutils/source.go | 94 +++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 rules/archive.go diff --git a/.travis.yml b/.travis.yml index 99640c0..7d7958f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,6 @@ language: go go: - - 1.8 - 1.9 - "1.10" - tip diff --git a/README.md b/README.md index 5646d9b..dff0b0e 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,7 @@ or to specify a set of rules to explicitly exclude using the '-exclude=' flag. - G302: Poor file permisions used with chmod - G303: Creating tempfile using a predictable path - G304: File path provided as taint input + - G305: File traversal when extracting zip archive - G401: Detect the usage of DES, RC4, or MD5 - G402: Look for bad TLS connection settings - G403: Ensure minimum RSA key length of 2048 bits diff --git a/rules/archive.go b/rules/archive.go new file mode 100644 index 0000000..1b388f5 --- /dev/null +++ b/rules/archive.go @@ -0,0 +1,60 @@ +package rules + +import ( + "go/ast" + "go/types" + + "github.com/GoASTScanner/gas" +) + +type archive struct { + gas.MetaData + calls gas.CallList + argType string +} + +func (a *archive) ID() string { + return a.MetaData.ID +} + +// Match inspects AST nodes to determine if the filepath.Joins uses any argument derived from type zip.File +func (a *archive) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) { + if node := a.calls.ContainsCallExpr(n, c); node != nil { + for _, arg := range node.Args { + var argType types.Type + if selector, ok := arg.(*ast.SelectorExpr); ok { + argType = c.Info.TypeOf(selector.X) + } else if ident, ok := arg.(*ast.Ident); ok { + if ident.Obj != nil && ident.Obj.Kind == ast.Var { + decl := ident.Obj.Decl + if assign, ok := decl.(*ast.AssignStmt); ok { + if selector, ok := assign.Rhs[0].(*ast.SelectorExpr); ok { + argType = c.Info.TypeOf(selector.X) + } + } + } + } + + if argType != nil && argType.String() == a.argType { + return gas.NewIssue(c, n, a.ID(), a.What, a.Severity, a.Confidence), nil + } + } + } + return nil, nil +} + +// NewArchive creates a new rule which detects the file traversal when extracting zip archives +func NewArchive(id string, conf gas.Config) (gas.Rule, []ast.Node) { + calls := gas.NewCallList() + calls.Add("path/filepath", "Join") + return &archive{ + calls: calls, + argType: "*archive/zip.File", + MetaData: gas.MetaData{ + ID: id, + Severity: gas.Medium, + Confidence: gas.High, + What: "File traversal when extracting zip archive", + }, + }, []ast.Node{(*ast.CallExpr)(nil)} +} diff --git a/rules/rulelist.go b/rules/rulelist.go index f6f21af..e06b7cc 100644 --- a/rules/rulelist.go +++ b/rules/rulelist.go @@ -79,6 +79,7 @@ func Generate(filters ...RuleFilter) RuleList { {"G302", "Poor file permisions used when creation file or using chmod", NewFilePerms}, {"G303", "Creating tempfile using a predictable path", NewBadTempFile}, {"G304", "File path provided as taint input", NewReadFile}, + {"G305", "File path traversal when extracting zip archive", NewArchive}, // crypto {"G401", "Detect the usage of DES, RC4, or MD5", NewUsesWeakCryptography}, diff --git a/rules/rules_test.go b/rules/rules_test.go index 78a2619..4a9c910 100644 --- a/rules/rules_test.go +++ b/rules/rules_test.go @@ -103,6 +103,10 @@ var _ = Describe("gas rules", func() { runner("G304", testutils.SampleCodeG304) }) + It("should detect file path traversal when extracting zip archive", func() { + runner("G305", testutils.SampleCodeG305) + }) + It("should detect weak crypto algorithms", func() { runner("G401", testutils.SampleCodeG401) }) diff --git a/testutils/source.go b/testutils/source.go index a0ba5bf..00a2a93 100644 --- a/testutils/source.go +++ b/testutils/source.go @@ -501,6 +501,100 @@ func main() { log.Fatal(http.ListenAndServe(":3000", nil)) }`, 1}} + // SampleCodeG305 - File path traversal when extracting zip archives + SampleCodeG305 = []CodeSample{{` +package unzip + +import ( + "archive/zip" + "io" + "os" + "path/filepath" +) + +func unzip(archive, target string) error { + reader, err := zip.OpenReader(archive) + if err != nil { + return err + } + + if err := os.MkdirAll(target, 0750); err != nil { + return err + } + + for _, file := range reader.File { + path := filepath.Join(target, file.Name) + if file.FileInfo().IsDir() { + os.MkdirAll(path, file.Mode()) // #nosec + continue + } + + fileReader, err := file.Open() + if err != nil { + return err + } + defer fileReader.Close() + + targetFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode()) + if err != nil { + return err + } + defer targetFile.Close() + + if _, err := io.Copy(targetFile, fileReader); err != nil { + return err + } + } + + return nil +}`, 1}, {` +package unzip + +import ( + "archive/zip" + "io" + "os" + "path/filepath" +) + +func unzip(archive, target string) error { + reader, err := zip.OpenReader(archive) + if err != nil { + return err + } + + if err := os.MkdirAll(target, 0750); err != nil { + return err + } + + for _, file := range reader.File { + archiveFile := file.Name + path := filepath.Join(target, archiveFile) + if file.FileInfo().IsDir() { + os.MkdirAll(path, file.Mode()) // #nosec + continue + } + + fileReader, err := file.Open() + if err != nil { + return err + } + defer fileReader.Close() + + targetFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode()) + if err != nil { + return err + } + defer targetFile.Close() + + if _, err := io.Copy(targetFile, fileReader); err != nil { + return err + } + } + + return nil +}`, 1}} + // SampleCodeG401 - Use of weak crypto MD5 SampleCodeG401 = []CodeSample{ {` From da26f64208ab995b527b9e43bebab3ece1b68261 Mon Sep 17 00:00:00 2001 From: Grant Murphy Date: Thu, 19 Jul 2018 17:40:28 +1000 Subject: [PATCH 2/7] Rename github org (#214) --- .travis.yml | 2 +- README.md | 10 +++++----- analyzer_test.go | 6 +++--- call_list_test.go | 4 ++-- cmd/gas/main.go | 6 +++--- cmd/gas/sort_issues.go | 2 +- cmd/tlsconfig/header_template.go | 2 +- config_test.go | 2 +- issue_test.go | 6 +++--- output/formatter.go | 2 +- output/junit_xml_format.go | 2 +- resolve_test.go | 4 ++-- rule_test.go | 2 +- rules/archive.go | 2 +- rules/big.go | 2 +- rules/bind.go | 2 +- rules/blacklist.go | 2 +- rules/errors.go | 2 +- rules/fileperms.go | 2 +- rules/hardcoded_credentials.go | 2 +- rules/rand.go | 2 +- rules/readfile.go | 2 +- rules/rsa.go | 2 +- rules/rulelist.go | 2 +- rules/rules_test.go | 6 +++--- rules/sql.go | 2 +- rules/ssh.go | 2 +- rules/subproc.go | 2 +- rules/tempfiles.go | 2 +- rules/templates.go | 2 +- rules/tls.go | 2 +- rules/tls_config.go | 2 +- rules/unsafe.go | 2 +- rules/weakcrypto.go | 2 +- testutils/pkg.go | 2 +- testutils/visitor.go | 2 +- 36 files changed, 50 insertions(+), 50 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7d7958f..4b1c9c4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,7 @@ install: - go get -u github.com/onsi/ginkgo/ginkgo - go get -u github.com/onsi/gomega - go get -u golang.org/x/crypto/ssh - - go get -u github.com/GoASTScanner/gas/cmd/gas/... + - go get -u github.com/securego/gas/cmd/gas/... - go get -v -t ./... - export PATH=$PATH:$HOME/gopath/bin diff --git a/README.md b/README.md index dff0b0e..253fbb0 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -## GAS - Go AST Scanner +## GAS - Go Application Security Inspects source code for security problems by scanning the Go AST. @@ -12,15 +12,15 @@ You may obtain a copy of the License [here](http://www.apache.org/licenses/LICEN ### Project status -[![Build Status](https://travis-ci.org/GoASTScanner/gas.svg?branch=master)](https://travis-ci.org/GoASTScanner/gas) -[![GoDoc](https://godoc.org/github.com/GoASTScanner/gas?status.svg)](https://godoc.org/github.com/GoASTScanner/gas) +[![Build Status](https://travis-ci.org/securego/gas.svg?branch=master)](https://travis-ci.org/securego/gas) +[![GoDoc](https://godoc.org/github.com/securego/gas?status.svg)](https://godoc.org/github.com/securego/gas) Gas is still in alpha and accepting feedback from early adopters. We do not consider it production ready at this time. ### Install -`$ go get github.com/GoASTScanner/gas/cmd/gas/...` +`$ go get github.com/securego/gas/cmd/gas/...` ### Usage @@ -188,7 +188,7 @@ The configuration of TLS rule can be generated from [Mozilla's TLS ciphers recom First you need to install the generator tool: ``` -go get github.com/GoASTScanner/gas/cmd/tlsconfig/... +go get github.com/securego/gas/cmd/tlsconfig/... ``` You can invoke now the `go generate` in the root of the project: diff --git a/analyzer_test.go b/analyzer_test.go index c527d0e..5f75db1 100644 --- a/analyzer_test.go +++ b/analyzer_test.go @@ -6,12 +6,12 @@ import ( "os" "strings" - "github.com/GoASTScanner/gas" - "github.com/GoASTScanner/gas/rules" + "github.com/securego/gas" + "github.com/securego/gas/rules" - "github.com/GoASTScanner/gas/testutils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + "github.com/securego/gas/testutils" ) var _ = Describe("Analyzer", func() { diff --git a/call_list_test.go b/call_list_test.go index f949cfd..3b01b68 100644 --- a/call_list_test.go +++ b/call_list_test.go @@ -3,10 +3,10 @@ package gas_test import ( "go/ast" - "github.com/GoASTScanner/gas" - "github.com/GoASTScanner/gas/testutils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + "github.com/securego/gas" + "github.com/securego/gas/testutils" ) var _ = Describe("call list", func() { diff --git a/cmd/gas/main.go b/cmd/gas/main.go index ccc002e..0a1db68 100644 --- a/cmd/gas/main.go +++ b/cmd/gas/main.go @@ -27,10 +27,10 @@ import ( "sort" "strings" - "github.com/GoASTScanner/gas" - "github.com/GoASTScanner/gas/output" - "github.com/GoASTScanner/gas/rules" "github.com/kisielk/gotool" + "github.com/securego/gas" + "github.com/securego/gas/output" + "github.com/securego/gas/rules" ) const ( diff --git a/cmd/gas/sort_issues.go b/cmd/gas/sort_issues.go index 5557f96..208253c 100644 --- a/cmd/gas/sort_issues.go +++ b/cmd/gas/sort_issues.go @@ -3,7 +3,7 @@ package main import ( "sort" - "github.com/GoASTScanner/gas" + "github.com/securego/gas" ) type sortBySeverity []*gas.Issue diff --git a/cmd/tlsconfig/header_template.go b/cmd/tlsconfig/header_template.go index 618221b..99da96e 100644 --- a/cmd/tlsconfig/header_template.go +++ b/cmd/tlsconfig/header_template.go @@ -8,6 +8,6 @@ package {{.}} import ( "go/ast" - "github.com/GoASTScanner/gas" + "github.com/securego/gas" ) `)) diff --git a/config_test.go b/config_test.go index a1ff0f5..e1e2ff3 100644 --- a/config_test.go +++ b/config_test.go @@ -3,9 +3,9 @@ package gas_test import ( "bytes" - "github.com/GoASTScanner/gas" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + "github.com/securego/gas" ) var _ = Describe("Configuration", func() { diff --git a/issue_test.go b/issue_test.go index 1203697..bb3e162 100644 --- a/issue_test.go +++ b/issue_test.go @@ -3,11 +3,11 @@ package gas_test import ( "go/ast" - "github.com/GoASTScanner/gas" - "github.com/GoASTScanner/gas/rules" - "github.com/GoASTScanner/gas/testutils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + "github.com/securego/gas" + "github.com/securego/gas/rules" + "github.com/securego/gas/testutils" ) var _ = Describe("Issue", func() { diff --git a/output/formatter.go b/output/formatter.go index 3d9d248..9ab13db 100644 --- a/output/formatter.go +++ b/output/formatter.go @@ -22,7 +22,7 @@ import ( "io" plainTemplate "text/template" - "github.com/GoASTScanner/gas" + "github.com/securego/gas" "gopkg.in/yaml.v2" ) diff --git a/output/junit_xml_format.go b/output/junit_xml_format.go index 2fd5c39..a3b0739 100644 --- a/output/junit_xml_format.go +++ b/output/junit_xml_format.go @@ -5,7 +5,7 @@ import ( htmlLib "html" "strconv" - "github.com/GoASTScanner/gas" + "github.com/securego/gas" ) type junitXMLReport struct { diff --git a/resolve_test.go b/resolve_test.go index f17a825..1589c10 100644 --- a/resolve_test.go +++ b/resolve_test.go @@ -3,10 +3,10 @@ package gas_test import ( "go/ast" - "github.com/GoASTScanner/gas" - "github.com/GoASTScanner/gas/testutils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + "github.com/securego/gas" + "github.com/securego/gas/testutils" ) var _ = Describe("Resolve ast node to concrete value", func() { diff --git a/rule_test.go b/rule_test.go index 196e575..df5f21e 100644 --- a/rule_test.go +++ b/rule_test.go @@ -4,9 +4,9 @@ import ( "fmt" "go/ast" - "github.com/GoASTScanner/gas" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + "github.com/securego/gas" ) type mockrule struct { diff --git a/rules/archive.go b/rules/archive.go index 1b388f5..09648fb 100644 --- a/rules/archive.go +++ b/rules/archive.go @@ -4,7 +4,7 @@ import ( "go/ast" "go/types" - "github.com/GoASTScanner/gas" + "github.com/securego/gas" ) type archive struct { diff --git a/rules/big.go b/rules/big.go index f4aeb3e..10270a0 100644 --- a/rules/big.go +++ b/rules/big.go @@ -17,7 +17,7 @@ package rules import ( "go/ast" - "github.com/GoASTScanner/gas" + "github.com/securego/gas" ) type usingBigExp struct { diff --git a/rules/bind.go b/rules/bind.go index 1cd8bf2..c669951 100644 --- a/rules/bind.go +++ b/rules/bind.go @@ -18,7 +18,7 @@ import ( "go/ast" "regexp" - "github.com/GoASTScanner/gas" + "github.com/securego/gas" ) // Looks for net.Listen("0.0.0.0") or net.Listen(":8080") diff --git a/rules/blacklist.go b/rules/blacklist.go index 92d8ed4..6ab3b8f 100644 --- a/rules/blacklist.go +++ b/rules/blacklist.go @@ -18,7 +18,7 @@ import ( "go/ast" "strings" - "github.com/GoASTScanner/gas" + "github.com/securego/gas" ) type blacklistedImport struct { diff --git a/rules/errors.go b/rules/errors.go index 03ededf..73f5f87 100644 --- a/rules/errors.go +++ b/rules/errors.go @@ -18,7 +18,7 @@ import ( "go/ast" "go/types" - "github.com/GoASTScanner/gas" + "github.com/securego/gas" ) type noErrorCheck struct { diff --git a/rules/fileperms.go b/rules/fileperms.go index 6276c85..883f6a4 100644 --- a/rules/fileperms.go +++ b/rules/fileperms.go @@ -19,7 +19,7 @@ import ( "go/ast" "strconv" - "github.com/GoASTScanner/gas" + "github.com/securego/gas" ) type filePermissions struct { diff --git a/rules/hardcoded_credentials.go b/rules/hardcoded_credentials.go index 0040710..6b811b6 100644 --- a/rules/hardcoded_credentials.go +++ b/rules/hardcoded_credentials.go @@ -19,8 +19,8 @@ import ( "regexp" "strconv" - "github.com/GoASTScanner/gas" "github.com/nbutton23/zxcvbn-go" + "github.com/securego/gas" ) type credentials struct { diff --git a/rules/rand.go b/rules/rand.go index c85f10f..1e9e055 100644 --- a/rules/rand.go +++ b/rules/rand.go @@ -17,7 +17,7 @@ package rules import ( "go/ast" - "github.com/GoASTScanner/gas" + "github.com/securego/gas" ) type weakRand struct { diff --git a/rules/readfile.go b/rules/readfile.go index d6c2186..9e573be 100644 --- a/rules/readfile.go +++ b/rules/readfile.go @@ -18,7 +18,7 @@ import ( "go/ast" "go/types" - "github.com/GoASTScanner/gas" + "github.com/securego/gas" ) type readfile struct { diff --git a/rules/rsa.go b/rules/rsa.go index 99c6e82..e11cc99 100644 --- a/rules/rsa.go +++ b/rules/rsa.go @@ -18,7 +18,7 @@ import ( "fmt" "go/ast" - "github.com/GoASTScanner/gas" + "github.com/securego/gas" ) type weakKeyStrength struct { diff --git a/rules/rulelist.go b/rules/rulelist.go index e06b7cc..63d98ab 100644 --- a/rules/rulelist.go +++ b/rules/rulelist.go @@ -15,7 +15,7 @@ package rules import ( - "github.com/GoASTScanner/gas" + "github.com/securego/gas" ) // RuleDefinition contains the description of a rule and a mechanism to diff --git a/rules/rules_test.go b/rules/rules_test.go index 4a9c910..7e9324b 100644 --- a/rules/rules_test.go +++ b/rules/rules_test.go @@ -4,12 +4,12 @@ import ( "fmt" "log" - "github.com/GoASTScanner/gas" + "github.com/securego/gas" - "github.com/GoASTScanner/gas/rules" - "github.com/GoASTScanner/gas/testutils" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" + "github.com/securego/gas/rules" + "github.com/securego/gas/testutils" ) var _ = Describe("gas rules", func() { diff --git a/rules/sql.go b/rules/sql.go index a76f580..5e37c58 100644 --- a/rules/sql.go +++ b/rules/sql.go @@ -18,7 +18,7 @@ import ( "go/ast" "regexp" - "github.com/GoASTScanner/gas" + "github.com/securego/gas" ) type sqlStatement struct { diff --git a/rules/ssh.go b/rules/ssh.go index f4f18cc..17ed71b 100644 --- a/rules/ssh.go +++ b/rules/ssh.go @@ -3,7 +3,7 @@ package rules import ( "go/ast" - "github.com/GoASTScanner/gas" + "github.com/securego/gas" ) type sshHostKey struct { diff --git a/rules/subproc.go b/rules/subproc.go index 8c0f675..d34cfb1 100644 --- a/rules/subproc.go +++ b/rules/subproc.go @@ -18,7 +18,7 @@ import ( "go/ast" "go/types" - "github.com/GoASTScanner/gas" + "github.com/securego/gas" ) type subprocess struct { diff --git a/rules/tempfiles.go b/rules/tempfiles.go index 664f774..c2f587b 100644 --- a/rules/tempfiles.go +++ b/rules/tempfiles.go @@ -18,7 +18,7 @@ import ( "go/ast" "regexp" - "github.com/GoASTScanner/gas" + "github.com/securego/gas" ) type badTempFile struct { diff --git a/rules/templates.go b/rules/templates.go index 66c37d6..45ea3c0 100644 --- a/rules/templates.go +++ b/rules/templates.go @@ -17,7 +17,7 @@ package rules import ( "go/ast" - "github.com/GoASTScanner/gas" + "github.com/securego/gas" ) type templateCheck struct { diff --git a/rules/tls.go b/rules/tls.go index c437930..62366bc 100644 --- a/rules/tls.go +++ b/rules/tls.go @@ -20,7 +20,7 @@ import ( "fmt" "go/ast" - "github.com/GoASTScanner/gas" + "github.com/securego/gas" ) type insecureConfigTLS struct { diff --git a/rules/tls_config.go b/rules/tls_config.go index 7242513..a756386 100644 --- a/rules/tls_config.go +++ b/rules/tls_config.go @@ -3,7 +3,7 @@ package rules import ( "go/ast" - "github.com/GoASTScanner/gas" + "github.com/securego/gas" ) // NewModernTLSCheck creates a check for Modern TLS ciphers diff --git a/rules/unsafe.go b/rules/unsafe.go index 8742dbc..bb88aa6 100644 --- a/rules/unsafe.go +++ b/rules/unsafe.go @@ -17,7 +17,7 @@ package rules import ( "go/ast" - "github.com/GoASTScanner/gas" + "github.com/securego/gas" ) type usingUnsafe struct { diff --git a/rules/weakcrypto.go b/rules/weakcrypto.go index 2fb9686..26b03df 100644 --- a/rules/weakcrypto.go +++ b/rules/weakcrypto.go @@ -17,7 +17,7 @@ package rules import ( "go/ast" - "github.com/GoASTScanner/gas" + "github.com/securego/gas" ) type usesWeakCryptography struct { diff --git a/testutils/pkg.go b/testutils/pkg.go index a7dbdb0..b74c211 100644 --- a/testutils/pkg.go +++ b/testutils/pkg.go @@ -10,7 +10,7 @@ import ( "path" "strings" - "github.com/GoASTScanner/gas" + "github.com/securego/gas" "golang.org/x/tools/go/loader" ) diff --git a/testutils/visitor.go b/testutils/visitor.go index df9275b..b2c6a50 100644 --- a/testutils/visitor.go +++ b/testutils/visitor.go @@ -3,7 +3,7 @@ package testutils import ( "go/ast" - "github.com/GoASTScanner/gas" + "github.com/securego/gas" ) // MockVisitor is useful for stubbing out ast.Visitor with callback From 893b87b34342eadd448aba7638c5cc25f7ad26dd Mon Sep 17 00:00:00 2001 From: Cosmin Cojocar Date: Thu, 19 Jul 2018 18:42:25 +0200 Subject: [PATCH 3/7] Replace gas with gosec everywhere in the project --- .github/issue_template.md | 2 +- .travis.yml | 2 +- Dockerfile | 2 +- Makefile | 8 +- analyzer.go | 98 ++++++++++++------------ analyzer_test.go | 16 ++-- call_list.go | 2 +- call_list_test.go | 12 +-- cmd/{gas => gosec}/filelist.go | 0 cmd/{gas => gosec}/main.go | 28 +++---- cmd/{gas => gosec}/sort_issues.go | 6 +- cmd/{gas => gosec}/version.go | 0 cmd/{gasutil => gosecutil}/tools.go | 0 cmd/tlsconfig/header_template.go | 2 +- cmd/tlsconfig/rule_template.go | 4 +- config.go | 4 +- config_test.go | 8 +- gas_suite_test.go => gosec_suite_test.go | 6 +- helpers.go | 2 +- helpers_test.go | 2 +- import_tracker.go | 2 +- issue.go | 6 +- issue_test.go | 18 ++--- output/formatter.go | 12 +-- output/junit_xml_format.go | 12 +-- resolve.go | 2 +- resolve_test.go | 22 +++--- rule.go | 4 +- rule_test.go | 28 +++---- rules/archive.go | 20 ++--- rules/big.go | 18 ++--- rules/bind.go | 22 +++--- rules/blacklist.go | 24 +++--- rules/errors.go | 24 +++--- rules/fileperms.go | 28 +++---- rules/hardcoded_credentials.go | 28 +++---- rules/rand.go | 18 ++--- rules/readfile.go | 22 +++--- rules/rsa.go | 22 +++--- rules/rulelist.go | 10 +-- rules/rules_test.go | 17 ++-- rules/sql.go | 36 ++++----- rules/ssh.go | 18 ++--- rules/subproc.go | 18 ++--- rules/tempfiles.go | 22 +++--- rules/templates.go | 20 ++--- rules/tls.go | 32 ++++---- rules/tls_config.go | 14 ++-- rules/unsafe.go | 18 ++--- rules/weakcrypto.go | 18 ++--- testutils/pkg.go | 12 +-- testutils/visitor.go | 6 +- 52 files changed, 387 insertions(+), 390 deletions(-) rename cmd/{gas => gosec}/filelist.go (100%) rename cmd/{gas => gosec}/main.go (92%) rename cmd/{gas => gosec}/sort_issues.go (76%) rename cmd/{gas => gosec}/version.go (100%) rename cmd/{gasutil => gosecutil}/tools.go (100%) rename gas_suite_test.go => gosec_suite_test.go (58%) diff --git a/.github/issue_template.md b/.github/issue_template.md index 14aec56..9c3ef02 100644 --- a/.github/issue_template.md +++ b/.github/issue_template.md @@ -2,7 +2,7 @@ ### Steps to reproduce the behavior -### Gas version +### gosec version ### Go version (output of 'go version') diff --git a/.travis.yml b/.travis.yml index 4b1c9c4..d12ef10 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,7 +11,7 @@ install: - go get -u github.com/onsi/ginkgo/ginkgo - go get -u github.com/onsi/gomega - go get -u golang.org/x/crypto/ssh - - go get -u github.com/securego/gas/cmd/gas/... + - go get -u github.com/securego/gosec/cmd/gosec/... - go get -v -t ./... - export PATH=$PATH:$HOME/gopath/bin diff --git a/Dockerfile b/Dockerfile index bbe12ca..e2ff565 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM golang:1.9.4-alpine3.7 -ENV BIN=gas +ENV BIN=gosec COPY build/*-linux-amd64 /go/bin/$BIN COPY docker-entrypoint.sh /usr/local/bin diff --git a/Makefile b/Makefile index db55f0e..fc6002b 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ GIT_TAG?= $(shell git describe --always --tags) BUILD_DATE = $(shell date +%Y-%m-%d) -BIN = gas -BUILD_CMD = go build -ldflags "-X main.Version=${VERSION} -X main.GitTag=${GIT_TAG} -X main.BuildDate=${BUILD_DATE}" -o build/$(BIN)-$(VERSION)-$${GOOS}-$${GOARCH} ./cmd/gas/ & +BIN = gosec +BUILD_CMD = go build -ldflags "-X main.Version=${VERSION} -X main.GitTag=${GIT_TAG} -X main.BuildDate=${BUILD_DATE}" -o build/$(BIN)-$(VERSION)-$${GOOS}-$${GOARCH} ./cmd/gosec/ & FMT_CMD = $(gofmt -s -l -w $(find . -type f -name '*.go' -not -path './vendor/*') | tee /dev/stderr) IMAGE_REPO = docker.io @@ -13,12 +13,12 @@ test: bootstrap test -z '$(FMT_CMD)' go vet $(go list ./... | grep -v /vendor/) golint -set_exit_status $(shell go list ./... | grep -v vendor) - gas ./... + gosec ./... ginkgo -r -v bootstrap: dep ensure build: - go build -o $(BIN) ./cmd/gas/ + go build -o $(BIN) ./cmd/gosec/ clean: rm -rf build vendor rm -f release image bootstrap $(BIN) diff --git a/analyzer.go b/analyzer.go index d3a47ec..e21722f 100644 --- a/analyzer.go +++ b/analyzer.go @@ -12,8 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -// Package gas holds the central scanning logic used by GAS -package gas +// Package gosec holds the central scanning logic used by gosec security scanner +package gosec import ( "go/ast" @@ -55,7 +55,7 @@ type Metrics struct { NumFound int `json:"found"` } -// Analyzer object is the main object of GAS. It has methods traverse an AST +// Analyzer object is the main object of gosec. It has methods traverse an AST // and invoke the correct checking rules as on each node as required. type Analyzer struct { ignoreNosec bool @@ -74,7 +74,7 @@ func NewAnalyzer(conf Config, logger *log.Logger) *Analyzer { ignoreNoSec = setting == "true" || setting == "enabled" } if logger == nil { - logger = log.New(os.Stderr, "[gas]", log.LstdFlags) + logger = log.New(os.Stderr, "[gosec]", log.LstdFlags) } return &Analyzer{ ignoreNosec: ignoreNoSec, @@ -89,15 +89,15 @@ 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 map[string]RuleBuilder) { +func (gosec *Analyzer) LoadRules(ruleDefinitions map[string]RuleBuilder) { for id, def := range ruleDefinitions { - r, nodes := def(id, gas.config) - gas.ruleset.Register(r, nodes...) + r, nodes := def(id, gosec.config) + gosec.ruleset.Register(r, nodes...) } } // Process kicks off the analysis process for a given package -func (gas *Analyzer) Process(buildTags []string, packagePaths ...string) error { +func (gosec *Analyzer) Process(buildTags []string, packagePaths ...string) error { ctx := build.Default ctx.BuildTags = append(ctx.BuildTags, buildTags...) packageConfig := loader.Config{ @@ -111,10 +111,10 @@ func (gas *Analyzer) Process(buildTags []string, packagePaths ...string) error { return err } if _, err := os.Stat(abspath); os.IsNotExist(err) { - gas.logger.Printf("Skipping: %s. Path doesn't exist.", abspath) + gosec.logger.Printf("Skipping: %s. Path doesn't exist.", abspath) continue } - gas.logger.Println("Searching directory:", abspath) + gosec.logger.Println("Searching directory:", abspath) basePackage, err := build.Default.ImportDir(packagePath, build.ImportComment) if err != nil { @@ -135,31 +135,31 @@ func (gas *Analyzer) Process(buildTags []string, packagePaths ...string) error { } for _, pkg := range builtPackage.Created { - gas.logger.Println("Checking package:", pkg.String()) + gosec.logger.Println("Checking package:", pkg.String()) for _, file := range pkg.Files { - gas.logger.Println("Checking file:", builtPackage.Fset.File(file.Pos()).Name()) - gas.context.FileSet = builtPackage.Fset - gas.context.Config = gas.config - gas.context.Comments = ast.NewCommentMap(gas.context.FileSet, file, file.Comments) - gas.context.Root = file - gas.context.Info = &pkg.Info - gas.context.Pkg = pkg.Pkg - gas.context.Imports = NewImportTracker() - gas.context.Imports.TrackPackages(gas.context.Pkg.Imports()...) - ast.Walk(gas, file) - gas.stats.NumFiles++ - gas.stats.NumLines += builtPackage.Fset.File(file.Pos()).LineCount() + gosec.logger.Println("Checking file:", builtPackage.Fset.File(file.Pos()).Name()) + gosec.context.FileSet = builtPackage.Fset + gosec.context.Config = gosec.config + gosec.context.Comments = ast.NewCommentMap(gosec.context.FileSet, file, file.Comments) + gosec.context.Root = file + gosec.context.Info = &pkg.Info + gosec.context.Pkg = pkg.Pkg + gosec.context.Imports = NewImportTracker() + gosec.context.Imports.TrackPackages(gosec.context.Pkg.Imports()...) + ast.Walk(gosec, file) + gosec.stats.NumFiles++ + gosec.stats.NumLines += builtPackage.Fset.File(file.Pos()).LineCount() } } return nil } // ignore a node (and sub-tree) if it is tagged with a "#nosec" comment -func (gas *Analyzer) ignore(n ast.Node) ([]string, bool) { - if groups, ok := gas.context.Comments[n]; ok && !gas.ignoreNosec { +func (gosec *Analyzer) ignore(n ast.Node) ([]string, bool) { + if groups, ok := gosec.context.Comments[n]; ok && !gosec.ignoreNosec { for _, group := range groups { if strings.Contains(group.Text(), "#nosec") { - gas.stats.NumNosec++ + gosec.stats.NumNosec++ // Pull out the specific rules that are listed to be ignored. re := regexp.MustCompile("(G\\d{3})") @@ -182,27 +182,27 @@ func (gas *Analyzer) ignore(n ast.Node) ([]string, bool) { return nil, false } -// Visit runs the GAS visitor logic over an AST created by parsing go code. +// Visit runs the gosec visitor logic over an AST created by parsing go code. // Rule methods added with AddRule will be invoked as necessary. -func (gas *Analyzer) Visit(n ast.Node) ast.Visitor { +func (gosec *Analyzer) Visit(n ast.Node) ast.Visitor { // If we've reached the end of this branch, pop off the ignores stack. if n == nil { - if len(gas.context.Ignores) > 0 { - gas.context.Ignores = gas.context.Ignores[1:] + if len(gosec.context.Ignores) > 0 { + gosec.context.Ignores = gosec.context.Ignores[1:] } - return gas + return gosec } // Get any new rule exclusions. - ignoredRules, ignoreAll := gas.ignore(n) + ignoredRules, ignoreAll := gosec.ignore(n) if ignoreAll { return nil } // Now create the union of exclusions. ignores := make(map[string]bool, 0) - if len(gas.context.Ignores) > 0 { - for k, v := range gas.context.Ignores[0] { + if len(gosec.context.Ignores) > 0 { + for k, v := range gosec.context.Ignores[0] { ignores[k] = v } } @@ -212,37 +212,37 @@ func (gas *Analyzer) Visit(n ast.Node) ast.Visitor { } // Push the new set onto the stack. - gas.context.Ignores = append([]map[string]bool{ignores}, gas.context.Ignores...) + gosec.context.Ignores = append([]map[string]bool{ignores}, gosec.context.Ignores...) // Track aliased and initialization imports - gas.context.Imports.TrackImport(n) + gosec.context.Imports.TrackImport(n) - for _, rule := range gas.ruleset.RegisteredFor(n) { + for _, rule := range gosec.ruleset.RegisteredFor(n) { if _, ok := ignores[rule.ID()]; ok { continue } - issue, err := rule.Match(n, gas.context) + issue, err := rule.Match(n, gosec.context) if err != nil { - file, line := GetLocation(n, gas.context) + file, line := GetLocation(n, gosec.context) file = path.Base(file) - gas.logger.Printf("Rule error: %v => %s (%s:%d)\n", reflect.TypeOf(rule), err, file, line) + gosec.logger.Printf("Rule error: %v => %s (%s:%d)\n", reflect.TypeOf(rule), err, file, line) } if issue != nil { - gas.issues = append(gas.issues, issue) - gas.stats.NumFound++ + gosec.issues = append(gosec.issues, issue) + gosec.stats.NumFound++ } } - return gas + return gosec } // Report returns the current issues discovered and the metrics about the scan -func (gas *Analyzer) Report() ([]*Issue, *Metrics) { - return gas.issues, gas.stats +func (gosec *Analyzer) Report() ([]*Issue, *Metrics) { + return gosec.issues, gosec.stats } // Reset clears state such as context, issues and metrics from the configured analyzer -func (gas *Analyzer) Reset() { - gas.context = &Context{} - gas.issues = make([]*Issue, 0, 16) - gas.stats = &Metrics{} +func (gosec *Analyzer) Reset() { + gosec.context = &Context{} + gosec.issues = make([]*Issue, 0, 16) + gosec.stats = &Metrics{} } diff --git a/analyzer_test.go b/analyzer_test.go index 5f75db1..5252af1 100644 --- a/analyzer_test.go +++ b/analyzer_test.go @@ -1,4 +1,4 @@ -package gas_test +package gosec_test import ( "io/ioutil" @@ -6,24 +6,24 @@ import ( "os" "strings" - "github.com/securego/gas" - "github.com/securego/gas/rules" + "github.com/securego/gosec" + "github.com/securego/gosec/rules" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "github.com/securego/gas/testutils" + "github.com/securego/gosec/testutils" ) var _ = Describe("Analyzer", func() { var ( - analyzer *gas.Analyzer + analyzer *gosec.Analyzer logger *log.Logger buildTags []string ) BeforeEach(func() { logger, _ = testutils.NewLogger() - analyzer = gas.NewAnalyzer(nil, logger) + analyzer = gosec.NewAnalyzer(nil, logger) }) Context("when processing a package", func() { @@ -200,9 +200,9 @@ var _ = Describe("Analyzer", func() { source := sample.Code // overwrite nosec option - nosecIgnoreConfig := gas.NewConfig() + nosecIgnoreConfig := gosec.NewConfig() nosecIgnoreConfig.SetGlobal("nosec", "true") - customAnalyzer := gas.NewAnalyzer(nosecIgnoreConfig, logger) + customAnalyzer := gosec.NewAnalyzer(nosecIgnoreConfig, logger) customAnalyzer.LoadRules(rules.Generate(rules.NewRuleFilter(false, "G401")).Builders()) nosecPackage := testutils.NewTestPackage() diff --git a/call_list.go b/call_list.go index e277950..8370f8f 100644 --- a/call_list.go +++ b/call_list.go @@ -11,7 +11,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package gas +package gosec import ( "go/ast" diff --git a/call_list_test.go b/call_list_test.go index 3b01b68..41aa51d 100644 --- a/call_list_test.go +++ b/call_list_test.go @@ -1,20 +1,20 @@ -package gas_test +package gosec_test import ( "go/ast" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "github.com/securego/gas" - "github.com/securego/gas/testutils" + "github.com/securego/gosec" + "github.com/securego/gosec/testutils" ) var _ = Describe("call list", func() { var ( - calls gas.CallList + calls gosec.CallList ) BeforeEach(func() { - calls = gas.NewCallList() + calls = gosec.NewCallList() }) It("should not return any matches when empty", func() { @@ -72,7 +72,7 @@ var _ = Describe("call list", func() { matched := 0 v := testutils.NewMockVisitor() v.Context = ctx - v.Callback = func(n ast.Node, ctx *gas.Context) bool { + v.Callback = func(n ast.Node, ctx *gosec.Context) bool { if _, ok := n.(*ast.CallExpr); ok && calls.ContainsCallExpr(n, ctx) != nil { matched++ } diff --git a/cmd/gas/filelist.go b/cmd/gosec/filelist.go similarity index 100% rename from cmd/gas/filelist.go rename to cmd/gosec/filelist.go diff --git a/cmd/gas/main.go b/cmd/gosec/main.go similarity index 92% rename from cmd/gas/main.go rename to cmd/gosec/main.go index 0a1db68..df88a19 100644 --- a/cmd/gas/main.go +++ b/cmd/gosec/main.go @@ -28,16 +28,16 @@ import ( "strings" "github.com/kisielk/gotool" - "github.com/securego/gas" - "github.com/securego/gas/output" - "github.com/securego/gas/rules" + "github.com/securego/gosec" + "github.com/securego/gosec/output" + "github.com/securego/gosec/rules" ) const ( usageText = ` -GAS - Go AST Scanner +gosec - Golang security checker -Gas analyzes Go source code to look for common programming mistakes that +gosec analyzes Go source code to look for common programming mistakes that can lead to security problems. VERSION: %s @@ -47,17 +47,17 @@ BUILD DATE: %s USAGE: # Check a single package - $ gas $GOPATH/src/github.com/example/project + $ gosec $GOPATH/src/github.com/example/project # Check all packages under the current directory and save results in # json format. - $ gas -fmt=json -out=results.json ./... + $ gosec -fmt=json -out=results.json ./... # Run a specific set of rules (by default all rules will be run): - $ gas -include=G101,G203,G401 ./... + $ gosec -include=G101,G203,G401 ./... # Run all rules except the provided - $ gas -exclude=G101 $GOPATH/src/github.com/example/project/... + $ gosec -exclude=G101 $GOPATH/src/github.com/example/project/... ` ) @@ -119,8 +119,8 @@ func usage() { fmt.Fprint(os.Stderr, "\n") } -func loadConfig(configFile string) (gas.Config, error) { - config := gas.NewConfig() +func loadConfig(configFile string) (gosec.Config, error) { + config := gosec.NewConfig() if configFile != "" { // #nosec file, err := os.Open(configFile) @@ -158,7 +158,7 @@ func loadRules(include, exclude string) rules.RuleList { return rules.Generate(filters...) } -func saveOutput(filename, format string, issues []*gas.Issue, metrics *gas.Metrics) error { +func saveOutput(filename, format string, issues []*gosec.Issue, metrics *gosec.Metrics) error { if filename != "" { outfile, err := os.Create(filename) if err != nil { @@ -283,7 +283,7 @@ func main() { if *flagQuiet { logger = log.New(ioutil.Discard, "", 0) } else { - logger = log.New(logWriter, "[gas] ", log.LstdFlags) + logger = log.New(logWriter, "[gosec] ", log.LstdFlags) } // Load config @@ -299,7 +299,7 @@ func main() { } // Create the analyzer - analyzer := gas.NewAnalyzer(config, logger) + analyzer := gosec.NewAnalyzer(config, logger) analyzer.LoadRules(ruleDefinitions.Builders()) vendor := regexp.MustCompile(`[\\/]vendor([\\/]|$)`) diff --git a/cmd/gas/sort_issues.go b/cmd/gosec/sort_issues.go similarity index 76% rename from cmd/gas/sort_issues.go rename to cmd/gosec/sort_issues.go index 208253c..10a86eb 100644 --- a/cmd/gas/sort_issues.go +++ b/cmd/gosec/sort_issues.go @@ -3,10 +3,10 @@ package main import ( "sort" - "github.com/securego/gas" + "github.com/securego/gosec" ) -type sortBySeverity []*gas.Issue +type sortBySeverity []*gosec.Issue func (s sortBySeverity) Len() int { return len(s) } @@ -15,6 +15,6 @@ func (s sortBySeverity) Less(i, j int) bool { return s[i].Severity > s[i].Severi func (s sortBySeverity) Swap(i, j int) { s[i], s[j] = s[j], s[i] } // sortIssues sorts the issues by severity in descending order -func sortIssues(issues []*gas.Issue) { +func sortIssues(issues []*gosec.Issue) { sort.Sort(sortBySeverity(issues)) } diff --git a/cmd/gas/version.go b/cmd/gosec/version.go similarity index 100% rename from cmd/gas/version.go rename to cmd/gosec/version.go diff --git a/cmd/gasutil/tools.go b/cmd/gosecutil/tools.go similarity index 100% rename from cmd/gasutil/tools.go rename to cmd/gosecutil/tools.go diff --git a/cmd/tlsconfig/header_template.go b/cmd/tlsconfig/header_template.go index 99da96e..3593645 100644 --- a/cmd/tlsconfig/header_template.go +++ b/cmd/tlsconfig/header_template.go @@ -8,6 +8,6 @@ package {{.}} import ( "go/ast" - "github.com/securego/gas" + "github.com/securego/gosec" ) `)) diff --git a/cmd/tlsconfig/rule_template.go b/cmd/tlsconfig/rule_template.go index bea9c39..952be29 100644 --- a/cmd/tlsconfig/rule_template.go +++ b/cmd/tlsconfig/rule_template.go @@ -5,9 +5,9 @@ import "text/template" var generatedRuleTmpl = template.Must(template.New("generated").Parse(` // New{{.Name}}TLSCheck creates a check for {{.Name}} TLS ciphers // DO NOT EDIT - generated by tlsconfig tool -func New{{.Name}}TLSCheck(id string, conf gas.Config) (gas.Rule, []ast.Node) { +func New{{.Name}}TLSCheck(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { return &insecureConfigTLS{ - MetaData: gas.MetaData{ID: id}, + MetaData: gosec.MetaData{ID: id}, requiredType: "crypto/tls.Config", MinVersion: {{ .MinVersion }}, MaxVersion: {{ .MaxVersion }}, diff --git a/config.go b/config.go index 09b97d3..a19937f 100644 --- a/config.go +++ b/config.go @@ -1,4 +1,4 @@ -package gas +package gosec import ( "bytes" @@ -10,7 +10,7 @@ import ( const ( // Globals are applicable to all rules and used for general - // configuration settings for gas. + // configuration settings for gosec. Globals = "global" ) diff --git a/config_test.go b/config_test.go index e1e2ff3..724717a 100644 --- a/config_test.go +++ b/config_test.go @@ -1,17 +1,17 @@ -package gas_test +package gosec_test import ( "bytes" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "github.com/securego/gas" + "github.com/securego/gosec" ) var _ = Describe("Configuration", func() { - var configuration gas.Config + var configuration gosec.Config BeforeEach(func() { - configuration = gas.NewConfig() + configuration = gosec.NewConfig() }) Context("when loading from disk", func() { diff --git a/gas_suite_test.go b/gosec_suite_test.go similarity index 58% rename from gas_suite_test.go rename to gosec_suite_test.go index 649d89a..7475c35 100644 --- a/gas_suite_test.go +++ b/gosec_suite_test.go @@ -1,4 +1,4 @@ -package gas_test +package gosec_test import ( . "github.com/onsi/ginkgo" @@ -7,7 +7,7 @@ import ( "testing" ) -func TestGas(t *testing.T) { +func TestGosec(t *testing.T) { RegisterFailHandler(Fail) - RunSpecs(t, "Gas Suite") + RunSpecs(t, "gosec Suite") } diff --git a/helpers.go b/helpers.go index 8bd1f5c..515ee8b 100644 --- a/helpers.go +++ b/helpers.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package gas +package gosec import ( "fmt" diff --git a/helpers_test.go b/helpers_test.go index 5676707..2f683cb 100644 --- a/helpers_test.go +++ b/helpers_test.go @@ -1,4 +1,4 @@ -package gas_test +package gosec_test import ( . "github.com/onsi/ginkgo" diff --git a/import_tracker.go b/import_tracker.go index 0f948fb..b56b65a 100644 --- a/import_tracker.go +++ b/import_tracker.go @@ -10,7 +10,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package gas +package gosec import ( "go/ast" diff --git a/issue.go b/issue.go index 5ec39bc..40bfa3d 100644 --- a/issue.go +++ b/issue.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package gas +package gosec import ( "encoding/json" @@ -34,7 +34,7 @@ const ( High ) -// Issue is returnd by a GAS rule if it discovers an issue with the scanned code. +// Issue is returnd by a gosec 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) @@ -45,7 +45,7 @@ type Issue struct { Line string `json:"line"` // Line number in file } -// MetaData is embedded in all GAS rules. The Severity, Confidence and What message +// MetaData is embedded in all gosec rules. The Severity, Confidence and What message // will be passed tbhrough to reported issues. type MetaData struct { ID string diff --git a/issue_test.go b/issue_test.go index bb3e162..68c4a5f 100644 --- a/issue_test.go +++ b/issue_test.go @@ -1,13 +1,13 @@ -package gas_test +package gosec_test import ( "go/ast" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "github.com/securego/gas" - "github.com/securego/gas/rules" - "github.com/securego/gas/testutils" + "github.com/securego/gosec" + "github.com/securego/gosec/rules" + "github.com/securego/gosec/testutils" ) var _ = Describe("Issue", func() { @@ -26,7 +26,7 @@ var _ = Describe("Issue", func() { pkg.AddFile("foo.go", source) ctx := pkg.CreateContext("foo.go") v := testutils.NewMockVisitor() - v.Callback = func(n ast.Node, ctx *gas.Context) bool { + v.Callback = func(n ast.Node, ctx *gosec.Context) bool { if node, ok := n.(*ast.BasicLit); ok { target = node return false @@ -37,7 +37,7 @@ var _ = Describe("Issue", func() { ast.Walk(v, ctx.Root) Expect(target).ShouldNot(BeNil()) - issue := gas.NewIssue(ctx, target, "TEST", "", gas.High, gas.High) + issue := gosec.NewIssue(ctx, target, "TEST", "", gosec.High, gosec.High) Expect(issue).ShouldNot(BeNil()) Expect(issue.Code).Should(MatchRegexp(`"bar"`)) Expect(issue.Line).Should(Equal("2")) @@ -58,7 +58,7 @@ var _ = Describe("Issue", func() { source := `package main import "os" func main(){` - source += "q := `SELECT * FROM table WHERE` + \n os.Args[1] + `= ?` // nolint: gas\n" + source += "q := `SELECT * FROM table WHERE` + \n os.Args[1] + `= ?` // nolint: gosec\n" source += `println(q)}` pkg := testutils.NewTestPackage() @@ -66,7 +66,7 @@ var _ = Describe("Issue", func() { pkg.AddFile("foo.go", source) ctx := pkg.CreateContext("foo.go") v := testutils.NewMockVisitor() - v.Callback = func(n ast.Node, ctx *gas.Context) bool { + v.Callback = func(n ast.Node, ctx *gosec.Context) bool { if node, ok := n.(*ast.BinaryExpr); ok { target = node } @@ -77,7 +77,7 @@ var _ = Describe("Issue", func() { Expect(target).ShouldNot(BeNil()) // Use SQL rule to check binary expr - cfg := gas.NewConfig() + cfg := gosec.NewConfig() rule, _ := rules.NewSQLStrConcat("TEST", cfg) issue, err := rule.Match(target, ctx) Expect(err).ShouldNot(HaveOccurred()) diff --git a/output/formatter.go b/output/formatter.go index 9ab13db..ee98de4 100644 --- a/output/formatter.go +++ b/output/formatter.go @@ -22,7 +22,7 @@ import ( "io" plainTemplate "text/template" - "github.com/securego/gas" + "github.com/securego/gosec" "gopkg.in/yaml.v2" ) @@ -58,13 +58,13 @@ Summary: ` type reportInfo struct { - Issues []*gas.Issue - Stats *gas.Metrics + Issues []*gosec.Issue + Stats *gosec.Metrics } // CreateReport generates a report based for the supplied issues and metrics given // the specified format. The formats currently accepted are: json, csv, html and text. -func CreateReport(w io.Writer, format string, issues []*gas.Issue, metrics *gas.Metrics) error { +func CreateReport(w io.Writer, format string, issues []*gosec.Issue, metrics *gosec.Metrics) error { data := &reportInfo{ Issues: issues, Stats: metrics, @@ -150,7 +150,7 @@ func reportJUnitXML(w io.Writer, data *reportInfo) error { } func reportFromPlaintextTemplate(w io.Writer, reportTemplate string, data *reportInfo) error { - t, e := plainTemplate.New("gas").Parse(reportTemplate) + t, e := plainTemplate.New("gosec").Parse(reportTemplate) if e != nil { return e } @@ -159,7 +159,7 @@ func reportFromPlaintextTemplate(w io.Writer, reportTemplate string, data *repor } func reportFromHTMLTemplate(w io.Writer, reportTemplate string, data *reportInfo) error { - t, e := htmlTemplate.New("gas").Parse(reportTemplate) + t, e := htmlTemplate.New("gosec").Parse(reportTemplate) if e != nil { return e } diff --git a/output/junit_xml_format.go b/output/junit_xml_format.go index a3b0739..547d5b2 100644 --- a/output/junit_xml_format.go +++ b/output/junit_xml_format.go @@ -5,7 +5,7 @@ import ( htmlLib "html" "strconv" - "github.com/securego/gas" + "github.com/securego/gosec" ) type junitXMLReport struct { @@ -32,26 +32,26 @@ type failure struct { Text string `xml:",innerxml"` } -func generatePlaintext(issue *gas.Issue) string { +func generatePlaintext(issue *gosec.Issue) string { return "Results:\n" + "[" + issue.File + ":" + issue.Line + "] - " + issue.What + " (Confidence: " + strconv.Itoa(int(issue.Confidence)) + ", Severity: " + strconv.Itoa(int(issue.Severity)) + ")\n" + "> " + htmlLib.EscapeString(issue.Code) } -func groupDataByRules(data *reportInfo) map[string][]*gas.Issue { - groupedData := make(map[string][]*gas.Issue) +func groupDataByRules(data *reportInfo) map[string][]*gosec.Issue { + groupedData := make(map[string][]*gosec.Issue) for _, issue := range data.Issues { if _, ok := groupedData[issue.What]; ok { groupedData[issue.What] = append(groupedData[issue.What], issue) } else { - groupedData[issue.What] = []*gas.Issue{issue} + groupedData[issue.What] = []*gosec.Issue{issue} } } return groupedData } -func createJUnitXMLStruct(groupedData map[string][]*gas.Issue) junitXMLReport { +func createJUnitXMLStruct(groupedData map[string][]*gosec.Issue) junitXMLReport { var xmlReport junitXMLReport for what, issues := range groupedData { testsuite := testsuite{ diff --git a/resolve.go b/resolve.go index d7c6dce..b563e7d 100644 --- a/resolve.go +++ b/resolve.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package gas +package gosec import "go/ast" diff --git a/resolve_test.go b/resolve_test.go index 1589c10..afc0316 100644 --- a/resolve_test.go +++ b/resolve_test.go @@ -1,12 +1,12 @@ -package gas_test +package gosec_test import ( "go/ast" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "github.com/securego/gas" - "github.com/securego/gas/testutils" + "github.com/securego/gosec" + "github.com/securego/gosec/testutils" ) var _ = Describe("Resolve ast node to concrete value", func() { @@ -19,7 +19,7 @@ var _ = Describe("Resolve ast node to concrete value", func() { pkg.AddFile("foo.go", `package main; const foo = "bar"; func main(){}`) ctx := pkg.CreateContext("foo.go") v := testutils.NewMockVisitor() - v.Callback = func(n ast.Node, ctx *gas.Context) bool { + v.Callback = func(n ast.Node, ctx *gosec.Context) bool { if node, ok := n.(*ast.BasicLit); ok { basicLiteral = node return false @@ -29,7 +29,7 @@ var _ = Describe("Resolve ast node to concrete value", func() { v.Context = ctx ast.Walk(v, ctx.Root) Expect(basicLiteral).ShouldNot(BeNil()) - Expect(gas.TryResolve(basicLiteral, ctx)).Should(BeTrue()) + Expect(gosec.TryResolve(basicLiteral, ctx)).Should(BeTrue()) }) It("should successfully resolve identifier", func() { @@ -39,7 +39,7 @@ var _ = Describe("Resolve ast node to concrete value", func() { pkg.AddFile("foo.go", `package main; var foo string = "bar"; func main(){}`) ctx := pkg.CreateContext("foo.go") v := testutils.NewMockVisitor() - v.Callback = func(n ast.Node, ctx *gas.Context) bool { + v.Callback = func(n ast.Node, ctx *gosec.Context) bool { if node, ok := n.(*ast.Ident); ok { ident = node return false @@ -49,7 +49,7 @@ var _ = Describe("Resolve ast node to concrete value", func() { v.Context = ctx ast.Walk(v, ctx.Root) Expect(ident).ShouldNot(BeNil()) - Expect(gas.TryResolve(ident, ctx)).Should(BeTrue()) + Expect(gosec.TryResolve(ident, ctx)).Should(BeTrue()) }) It("should successfully resolve assign statement", func() { @@ -59,7 +59,7 @@ var _ = Describe("Resolve ast node to concrete value", func() { pkg.AddFile("foo.go", `package main; const x = "bar"; func main(){ y := x; println(y) }`) ctx := pkg.CreateContext("foo.go") v := testutils.NewMockVisitor() - v.Callback = func(n ast.Node, ctx *gas.Context) bool { + v.Callback = func(n ast.Node, ctx *gosec.Context) bool { if node, ok := n.(*ast.AssignStmt); ok { if id, ok := node.Lhs[0].(*ast.Ident); ok && id.Name == "y" { assign = node @@ -70,7 +70,7 @@ var _ = Describe("Resolve ast node to concrete value", func() { v.Context = ctx ast.Walk(v, ctx.Root) Expect(assign).ShouldNot(BeNil()) - Expect(gas.TryResolve(assign, ctx)).Should(BeTrue()) + Expect(gosec.TryResolve(assign, ctx)).Should(BeTrue()) }) It("should successfully resolve a binary statement", func() { @@ -80,7 +80,7 @@ var _ = Describe("Resolve ast node to concrete value", func() { pkg.AddFile("foo.go", `package main; const (x = "bar"; y = "baz"); func main(){ z := x + y; println(z) }`) ctx := pkg.CreateContext("foo.go") v := testutils.NewMockVisitor() - v.Callback = func(n ast.Node, ctx *gas.Context) bool { + v.Callback = func(n ast.Node, ctx *gosec.Context) bool { if node, ok := n.(*ast.BinaryExpr); ok { target = node } @@ -89,7 +89,7 @@ var _ = Describe("Resolve ast node to concrete value", func() { v.Context = ctx ast.Walk(v, ctx.Root) Expect(target).ShouldNot(BeNil()) - Expect(gas.TryResolve(target, ctx)).Should(BeTrue()) + Expect(gosec.TryResolve(target, ctx)).Should(BeTrue()) }) // TODO: It should resolve call expressions diff --git a/rule.go b/rule.go index 95c6562..415c708 100644 --- a/rule.go +++ b/rule.go @@ -10,14 +10,14 @@ // See the License for the specific language governing permissions and // limitations under the License. -package gas +package gosec import ( "go/ast" "reflect" ) -// The Rule interface used by all rules supported by GAS. +// The Rule interface used by all rules supported by gosec. type Rule interface { ID() string Match(ast.Node, *Context) (*Issue, error) diff --git a/rule_test.go b/rule_test.go index df5f21e..dbbf38f 100644 --- a/rule_test.go +++ b/rule_test.go @@ -1,4 +1,4 @@ -package gas_test +package gosec_test import ( "fmt" @@ -6,20 +6,20 @@ import ( . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "github.com/securego/gas" + "github.com/securego/gosec" ) type mockrule struct { - issue *gas.Issue + issue *gosec.Issue err error - callback func(n ast.Node, ctx *gas.Context) bool + callback func(n ast.Node, ctx *gosec.Context) bool } func (m *mockrule) ID() string { return "MOCK" } -func (m *mockrule) Match(n ast.Node, ctx *gas.Context) (*gas.Issue, error) { +func (m *mockrule) Match(n ast.Node, ctx *gosec.Context) (*gosec.Issue, error) { if m.callback(n, ctx) { return m.issue, nil } @@ -31,29 +31,29 @@ var _ = Describe("Rule", func() { Context("when using a ruleset", func() { var ( - ruleset gas.RuleSet - dummyErrorRule gas.Rule - dummyIssueRule gas.Rule + ruleset gosec.RuleSet + dummyErrorRule gosec.Rule + dummyIssueRule gosec.Rule ) JustBeforeEach(func() { - ruleset = gas.NewRuleSet() + ruleset = gosec.NewRuleSet() dummyErrorRule = &mockrule{ issue: nil, err: fmt.Errorf("An unexpected error occurred"), - callback: func(n ast.Node, ctx *gas.Context) bool { return false }, + callback: func(n ast.Node, ctx *gosec.Context) bool { return false }, } dummyIssueRule = &mockrule{ - issue: &gas.Issue{ - Severity: gas.High, - Confidence: gas.High, + issue: &gosec.Issue{ + Severity: gosec.High, + Confidence: gosec.High, What: `Some explanation of the thing`, File: "main.go", Code: `#include int main(){ puts("hello world"); }`, Line: "42", }, err: nil, - callback: func(n ast.Node, ctx *gas.Context) bool { return true }, + callback: func(n ast.Node, ctx *gosec.Context) bool { return true }, } }) It("should be possible to register a rule for multiple ast.Node", func() { diff --git a/rules/archive.go b/rules/archive.go index 09648fb..1fa2b40 100644 --- a/rules/archive.go +++ b/rules/archive.go @@ -4,12 +4,12 @@ import ( "go/ast" "go/types" - "github.com/securego/gas" + "github.com/securego/gosec" ) type archive struct { - gas.MetaData - calls gas.CallList + gosec.MetaData + calls gosec.CallList argType string } @@ -18,7 +18,7 @@ func (a *archive) ID() string { } // Match inspects AST nodes to determine if the filepath.Joins uses any argument derived from type zip.File -func (a *archive) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) { +func (a *archive) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) { if node := a.calls.ContainsCallExpr(n, c); node != nil { for _, arg := range node.Args { var argType types.Type @@ -36,7 +36,7 @@ func (a *archive) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) { } if argType != nil && argType.String() == a.argType { - return gas.NewIssue(c, n, a.ID(), a.What, a.Severity, a.Confidence), nil + return gosec.NewIssue(c, n, a.ID(), a.What, a.Severity, a.Confidence), nil } } } @@ -44,16 +44,16 @@ func (a *archive) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) { } // NewArchive creates a new rule which detects the file traversal when extracting zip archives -func NewArchive(id string, conf gas.Config) (gas.Rule, []ast.Node) { - calls := gas.NewCallList() +func NewArchive(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { + calls := gosec.NewCallList() calls.Add("path/filepath", "Join") return &archive{ calls: calls, argType: "*archive/zip.File", - MetaData: gas.MetaData{ + MetaData: gosec.MetaData{ ID: id, - Severity: gas.Medium, - Confidence: gas.High, + Severity: gosec.Medium, + Confidence: gosec.High, What: "File traversal when extracting zip archive", }, }, []ast.Node{(*ast.CallExpr)(nil)} diff --git a/rules/big.go b/rules/big.go index 10270a0..8c45a53 100644 --- a/rules/big.go +++ b/rules/big.go @@ -17,11 +17,11 @@ package rules import ( "go/ast" - "github.com/securego/gas" + "github.com/securego/gosec" ) type usingBigExp struct { - gas.MetaData + gosec.MetaData pkg string calls []string } @@ -30,23 +30,23 @@ func (r *usingBigExp) ID() string { return r.MetaData.ID } -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.ID(), r.What, r.Severity, r.Confidence), nil +func (r *usingBigExp) Match(n ast.Node, c *gosec.Context) (gi *gosec.Issue, err error) { + if _, matched := gosec.MatchCallByType(n, c, r.pkg, r.calls...); matched { + return gosec.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil } return nil, nil } // NewUsingBigExp detects issues with modulus == 0 for Bignum -func NewUsingBigExp(id string, conf gas.Config) (gas.Rule, []ast.Node) { +func NewUsingBigExp(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { return &usingBigExp{ pkg: "*math/big.Int", calls: []string{"Exp"}, - MetaData: gas.MetaData{ + MetaData: gosec.MetaData{ ID: id, What: "Use of math/big.Int.Exp function should be audited for modulus == 0", - Severity: gas.Low, - Confidence: gas.High, + Severity: gosec.Low, + Confidence: gosec.High, }, }, []ast.Node{(*ast.CallExpr)(nil)} } diff --git a/rules/bind.go b/rules/bind.go index c669951..a7d599b 100644 --- a/rules/bind.go +++ b/rules/bind.go @@ -18,13 +18,13 @@ import ( "go/ast" "regexp" - "github.com/securego/gas" + "github.com/securego/gosec" ) // Looks for net.Listen("0.0.0.0") or net.Listen(":8080") type bindsToAllNetworkInterfaces struct { - gas.MetaData - calls gas.CallList + gosec.MetaData + calls gosec.CallList pattern *regexp.Regexp } @@ -32,14 +32,14 @@ func (r *bindsToAllNetworkInterfaces) ID() string { return r.MetaData.ID } -func (r *bindsToAllNetworkInterfaces) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) { +func (r *bindsToAllNetworkInterfaces) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) { callExpr := r.calls.ContainsCallExpr(n, c) if callExpr == nil { return nil, nil } - if arg, err := gas.GetString(callExpr.Args[1]); err == nil { + if arg, err := gosec.GetString(callExpr.Args[1]); err == nil { if r.pattern.MatchString(arg) { - return gas.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil + return gosec.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil } } return nil, nil @@ -47,17 +47,17 @@ func (r *bindsToAllNetworkInterfaces) Match(n ast.Node, c *gas.Context) (*gas.Is // NewBindsToAllNetworkInterfaces detects socket connections that are setup to // listen on all network interfaces. -func NewBindsToAllNetworkInterfaces(id string, conf gas.Config) (gas.Rule, []ast.Node) { - calls := gas.NewCallList() +func NewBindsToAllNetworkInterfaces(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { + calls := gosec.NewCallList() calls.Add("net", "Listen") calls.Add("crypto/tls", "Listen") return &bindsToAllNetworkInterfaces{ calls: calls, pattern: regexp.MustCompile(`^(0.0.0.0|:).*$`), - MetaData: gas.MetaData{ + MetaData: gosec.MetaData{ ID: id, - Severity: gas.Medium, - Confidence: gas.High, + Severity: gosec.Medium, + Confidence: gosec.High, What: "Binds to all network interfaces", }, }, []ast.Node{(*ast.CallExpr)(nil)} diff --git a/rules/blacklist.go b/rules/blacklist.go index 6ab3b8f..74a769c 100644 --- a/rules/blacklist.go +++ b/rules/blacklist.go @@ -18,11 +18,11 @@ import ( "go/ast" "strings" - "github.com/securego/gas" + "github.com/securego/gosec" ) type blacklistedImport struct { - gas.MetaData + gosec.MetaData Blacklisted map[string]string } @@ -36,10 +36,10 @@ func (r *blacklistedImport) ID() string { return r.MetaData.ID } -func (r *blacklistedImport) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) { +func (r *blacklistedImport) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) { if node, ok := n.(*ast.ImportSpec); ok { if description, ok := r.Blacklisted[unquote(node.Path.Value)]; ok { - return gas.NewIssue(c, node, r.ID(), description, r.Severity, r.Confidence), nil + return gosec.NewIssue(c, node, r.ID(), description, r.Severity, r.Confidence), nil } } return nil, nil @@ -47,40 +47,40 @@ func (r *blacklistedImport) Match(n ast.Node, c *gas.Context) (*gas.Issue, error // NewBlacklistedImports reports when a blacklisted import is being used. // Typically when a deprecated technology is being used. -func NewBlacklistedImports(id string, conf gas.Config, blacklist map[string]string) (gas.Rule, []ast.Node) { +func NewBlacklistedImports(id string, conf gosec.Config, blacklist map[string]string) (gosec.Rule, []ast.Node) { return &blacklistedImport{ - MetaData: gas.MetaData{ + MetaData: gosec.MetaData{ ID: id, - Severity: gas.Medium, - Confidence: gas.High, + Severity: gosec.Medium, + Confidence: gosec.High, }, Blacklisted: blacklist, }, []ast.Node{(*ast.ImportSpec)(nil)} } // NewBlacklistedImportMD5 fails if MD5 is imported -func NewBlacklistedImportMD5(id string, conf gas.Config) (gas.Rule, []ast.Node) { +func NewBlacklistedImportMD5(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { return NewBlacklistedImports(id, conf, map[string]string{ "crypto/md5": "Blacklisted import crypto/md5: weak cryptographic primitive", }) } // NewBlacklistedImportDES fails if DES is imported -func NewBlacklistedImportDES(id string, conf gas.Config) (gas.Rule, []ast.Node) { +func NewBlacklistedImportDES(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { return NewBlacklistedImports(id, conf, map[string]string{ "crypto/des": "Blacklisted import crypto/des: weak cryptographic primitive", }) } // NewBlacklistedImportRC4 fails if DES is imported -func NewBlacklistedImportRC4(id string, conf gas.Config) (gas.Rule, []ast.Node) { +func NewBlacklistedImportRC4(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { return NewBlacklistedImports(id, conf, map[string]string{ "crypto/rc4": "Blacklisted import crypto/rc4: weak cryptographic primitive", }) } // NewBlacklistedImportCGI fails if CGI is imported -func NewBlacklistedImportCGI(id string, conf gas.Config) (gas.Rule, []ast.Node) { +func NewBlacklistedImportCGI(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { return NewBlacklistedImports(id, conf, map[string]string{ "net/http/cgi": "Blacklisted import net/http/cgi: Go versions < 1.6.3 are vulnerable to Httpoxy attack: (CVE-2016-5386)", }) diff --git a/rules/errors.go b/rules/errors.go index 73f5f87..5aea57d 100644 --- a/rules/errors.go +++ b/rules/errors.go @@ -18,19 +18,19 @@ import ( "go/ast" "go/types" - "github.com/securego/gas" + "github.com/securego/gosec" ) type noErrorCheck struct { - gas.MetaData - whitelist gas.CallList + gosec.MetaData + whitelist gosec.CallList } func (r *noErrorCheck) ID() string { return r.MetaData.ID } -func returnsError(callExpr *ast.CallExpr, ctx *gas.Context) int { +func returnsError(callExpr *ast.CallExpr, ctx *gosec.Context) int { if tv := ctx.Info.TypeOf(callExpr); tv != nil { switch t := tv.(type) { case *types.Tuple: @@ -49,7 +49,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 *gosec.Context) (*gosec.Issue, error) { switch stmt := n.(type) { case *ast.AssignStmt: for _, expr := range stmt.Rhs { @@ -59,7 +59,7 @@ func (r *noErrorCheck) Match(n ast.Node, ctx *gas.Context) (*gas.Issue, error) { return nil, nil } if id, ok := stmt.Lhs[pos].(*ast.Ident); ok && id.Name == "_" { - return gas.NewIssue(ctx, n, r.ID(), r.What, r.Severity, r.Confidence), nil + return gosec.NewIssue(ctx, n, r.ID(), r.What, r.Severity, r.Confidence), nil } } } @@ -67,7 +67,7 @@ func (r *noErrorCheck) Match(n ast.Node, ctx *gas.Context) (*gas.Issue, error) { if callExpr, ok := stmt.X.(*ast.CallExpr); ok && r.whitelist.ContainsCallExpr(stmt.X, ctx) == nil { pos := returnsError(callExpr, ctx) if pos >= 0 { - return gas.NewIssue(ctx, n, r.ID(), r.What, r.Severity, r.Confidence), nil + return gosec.NewIssue(ctx, n, r.ID(), r.What, r.Severity, r.Confidence), nil } } } @@ -75,10 +75,10 @@ func (r *noErrorCheck) Match(n ast.Node, ctx *gas.Context) (*gas.Issue, error) { } // NewNoErrorCheck detects if the returned error is unchecked -func NewNoErrorCheck(id string, conf gas.Config) (gas.Rule, []ast.Node) { +func NewNoErrorCheck(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { // TODO(gm) Come up with sensible defaults here. Or flip it to use a // black list instead. - whitelist := gas.NewCallList() + whitelist := gosec.NewCallList() whitelist.AddAll("bytes.Buffer", "Write", "WriteByte", "WriteRune", "WriteString") whitelist.AddAll("fmt", "Print", "Printf", "Println", "Fprint", "Fprintf", "Fprintln") whitelist.Add("io.PipeWriter", "CloseWithError") @@ -91,10 +91,10 @@ func NewNoErrorCheck(id string, conf gas.Config) (gas.Rule, []ast.Node) { } } return &noErrorCheck{ - MetaData: gas.MetaData{ + MetaData: gosec.MetaData{ ID: id, - Severity: gas.Low, - Confidence: gas.High, + Severity: gosec.Low, + Confidence: gosec.High, What: "Errors unhandled.", }, whitelist: whitelist, diff --git a/rules/fileperms.go b/rules/fileperms.go index 883f6a4..8e94369 100644 --- a/rules/fileperms.go +++ b/rules/fileperms.go @@ -19,11 +19,11 @@ import ( "go/ast" "strconv" - "github.com/securego/gas" + "github.com/securego/gosec" ) type filePermissions struct { - gas.MetaData + gosec.MetaData mode int64 pkg string calls []string @@ -50,11 +50,11 @@ func getConfiguredMode(conf map[string]interface{}, configKey string, defaultMod return mode } -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 { +func (r *filePermissions) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) { + if callexpr, matched := gosec.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 { - return gas.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil + if mode, err := gosec.GetInt(modeArg); err == nil && mode > r.mode { + return gosec.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil } } return nil, nil @@ -62,16 +62,16 @@ func (r *filePermissions) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) // NewFilePerms creates a rule to detect file creation with a more permissive than configured // permission mask. -func NewFilePerms(id string, conf gas.Config) (gas.Rule, []ast.Node) { +func NewFilePerms(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { mode := getConfiguredMode(conf, "G302", 0600) return &filePermissions{ mode: mode, pkg: "os", calls: []string{"OpenFile", "Chmod"}, - MetaData: gas.MetaData{ + MetaData: gosec.MetaData{ ID: id, - Severity: gas.Medium, - Confidence: gas.High, + Severity: gosec.Medium, + Confidence: gosec.High, What: fmt.Sprintf("Expect file permissions to be %#o or less", mode), }, }, []ast.Node{(*ast.CallExpr)(nil)} @@ -79,16 +79,16 @@ func NewFilePerms(id string, conf gas.Config) (gas.Rule, []ast.Node) { // NewMkdirPerms creates a rule to detect directory creation with more permissive than // configured permission mask. -func NewMkdirPerms(id string, conf gas.Config) (gas.Rule, []ast.Node) { +func NewMkdirPerms(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { mode := getConfiguredMode(conf, "G301", 0750) return &filePermissions{ mode: mode, pkg: "os", calls: []string{"Mkdir", "MkdirAll"}, - MetaData: gas.MetaData{ + MetaData: gosec.MetaData{ ID: id, - Severity: gas.Medium, - Confidence: gas.High, + Severity: gosec.Medium, + Confidence: gosec.High, What: fmt.Sprintf("Expect directory permissions to be %#o or less", mode), }, }, []ast.Node{(*ast.CallExpr)(nil)} diff --git a/rules/hardcoded_credentials.go b/rules/hardcoded_credentials.go index 6b811b6..17a5cdf 100644 --- a/rules/hardcoded_credentials.go +++ b/rules/hardcoded_credentials.go @@ -19,12 +19,12 @@ import ( "regexp" "strconv" - "github.com/nbutton23/zxcvbn-go" - "github.com/securego/gas" + zxcvbn "github.com/nbutton23/zxcvbn-go" + "github.com/securego/gosec" ) type credentials struct { - gas.MetaData + gosec.MetaData pattern *regexp.Regexp entropyThreshold float64 perCharThreshold float64 @@ -52,7 +52,7 @@ func (r *credentials) isHighEntropyString(str string) bool { entropyPerChar >= r.perCharThreshold)) } -func (r *credentials) Match(n ast.Node, ctx *gas.Context) (*gas.Issue, error) { +func (r *credentials) Match(n ast.Node, ctx *gosec.Context) (*gosec.Issue, error) { switch node := n.(type) { case *ast.AssignStmt: return r.matchAssign(node, ctx) @@ -62,14 +62,14 @@ func (r *credentials) Match(n ast.Node, ctx *gas.Context) (*gas.Issue, error) { return nil, nil } -func (r *credentials) matchAssign(assign *ast.AssignStmt, ctx *gas.Context) (*gas.Issue, error) { +func (r *credentials) matchAssign(assign *ast.AssignStmt, ctx *gosec.Context) (*gosec.Issue, error) { for _, i := range assign.Lhs { if ident, ok := i.(*ast.Ident); ok { if r.pattern.MatchString(ident.Name) { for _, e := range assign.Rhs { - if val, err := gas.GetString(e); err == nil { + if val, err := gosec.GetString(e); err == nil { if r.ignoreEntropy || (!r.ignoreEntropy && r.isHighEntropyString(val)) { - return gas.NewIssue(ctx, assign, r.ID(), r.What, r.Severity, r.Confidence), nil + return gosec.NewIssue(ctx, assign, r.ID(), r.What, r.Severity, r.Confidence), nil } } } @@ -79,16 +79,16 @@ func (r *credentials) matchAssign(assign *ast.AssignStmt, ctx *gas.Context) (*ga return nil, nil } -func (r *credentials) matchValueSpec(valueSpec *ast.ValueSpec, ctx *gas.Context) (*gas.Issue, error) { +func (r *credentials) matchValueSpec(valueSpec *ast.ValueSpec, ctx *gosec.Context) (*gosec.Issue, error) { for index, ident := range valueSpec.Names { if r.pattern.MatchString(ident.Name) && valueSpec.Values != nil { // const foo, bar = "same value" if len(valueSpec.Values) <= index { index = len(valueSpec.Values) - 1 } - if val, err := gas.GetString(valueSpec.Values[index]); err == nil { + if val, err := gosec.GetString(valueSpec.Values[index]); err == nil { if r.ignoreEntropy || (!r.ignoreEntropy && r.isHighEntropyString(val)) { - return gas.NewIssue(ctx, valueSpec, r.ID(), r.What, r.Severity, r.Confidence), nil + return gosec.NewIssue(ctx, valueSpec, r.ID(), r.What, r.Severity, r.Confidence), nil } } } @@ -98,7 +98,7 @@ func (r *credentials) matchValueSpec(valueSpec *ast.ValueSpec, ctx *gas.Context) // NewHardcodedCredentials attempts to find high entropy string constants being // assigned to variables that appear to be related to credentials. -func NewHardcodedCredentials(id string, conf gas.Config) (gas.Rule, []ast.Node) { +func NewHardcodedCredentials(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { pattern := `(?i)passwd|pass|password|pwd|secret|token` entropyThreshold := 80.0 perCharThreshold := 3.0 @@ -137,11 +137,11 @@ func NewHardcodedCredentials(id string, conf gas.Config) (gas.Rule, []ast.Node) perCharThreshold: perCharThreshold, ignoreEntropy: ignoreEntropy, truncate: truncateString, - MetaData: gas.MetaData{ + MetaData: gosec.MetaData{ ID: id, What: "Potential hardcoded credentials", - Confidence: gas.Low, - Severity: gas.High, + Confidence: gosec.Low, + Severity: gosec.High, }, }, []ast.Node{(*ast.AssignStmt)(nil), (*ast.ValueSpec)(nil)} } diff --git a/rules/rand.go b/rules/rand.go index 1e9e055..a2bdabe 100644 --- a/rules/rand.go +++ b/rules/rand.go @@ -17,11 +17,11 @@ package rules import ( "go/ast" - "github.com/securego/gas" + "github.com/securego/gosec" ) type weakRand struct { - gas.MetaData + gosec.MetaData funcNames []string packagePath string } @@ -30,10 +30,10 @@ func (w *weakRand) ID() string { return w.MetaData.ID } -func (w *weakRand) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) { +func (w *weakRand) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) { for _, funcName := range w.funcNames { - if _, matched := gas.MatchCallByPackage(n, c, w.packagePath, funcName); matched { - return gas.NewIssue(c, n, w.ID(), w.What, w.Severity, w.Confidence), nil + if _, matched := gosec.MatchCallByPackage(n, c, w.packagePath, funcName); matched { + return gosec.NewIssue(c, n, w.ID(), w.What, w.Severity, w.Confidence), nil } } @@ -41,14 +41,14 @@ func (w *weakRand) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) { } // NewWeakRandCheck detects the use of random number generator that isn't cryptographically secure -func NewWeakRandCheck(id string, conf gas.Config) (gas.Rule, []ast.Node) { +func NewWeakRandCheck(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { return &weakRand{ funcNames: []string{"Read", "Int"}, packagePath: "math/rand", - MetaData: gas.MetaData{ + MetaData: gosec.MetaData{ ID: id, - Severity: gas.High, - Confidence: gas.Medium, + Severity: gosec.High, + Confidence: gosec.Medium, What: "Use of weak random number generator (math/rand instead of crypto/rand)", }, }, []ast.Node{(*ast.CallExpr)(nil)} diff --git a/rules/readfile.go b/rules/readfile.go index 9e573be..61e1c85 100644 --- a/rules/readfile.go +++ b/rules/readfile.go @@ -18,12 +18,12 @@ import ( "go/ast" "go/types" - "github.com/securego/gas" + "github.com/securego/gosec" ) type readfile struct { - gas.MetaData - gas.CallList + gosec.MetaData + gosec.CallList } // ID returns the identifier for this rule @@ -32,13 +32,13 @@ func (r *readfile) ID() string { } // Match inspects AST nodes to determine if the match the methods `os.Open` or `ioutil.ReadFile` -func (r *readfile) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) { +func (r *readfile) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) { if node := r.ContainsCallExpr(n, c); node != nil { for _, arg := range node.Args { if ident, ok := arg.(*ast.Ident); ok { obj := c.Info.ObjectOf(ident) - if _, ok := obj.(*types.Var); ok && !gas.TryResolve(ident, c) { - return gas.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil + if _, ok := obj.(*types.Var); ok && !gosec.TryResolve(ident, c) { + return gosec.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil } } } @@ -47,14 +47,14 @@ func (r *readfile) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) { } // NewReadFile detects cases where we read files -func NewReadFile(id string, conf gas.Config) (gas.Rule, []ast.Node) { +func NewReadFile(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { rule := &readfile{ - CallList: gas.NewCallList(), - MetaData: gas.MetaData{ + CallList: gosec.NewCallList(), + MetaData: gosec.MetaData{ ID: id, What: "Potential file inclusion via variable", - Severity: gas.Medium, - Confidence: gas.High, + Severity: gosec.Medium, + Confidence: gosec.High, }, } rule.Add("io/ioutil", "ReadFile") diff --git a/rules/rsa.go b/rules/rsa.go index e11cc99..4a42905 100644 --- a/rules/rsa.go +++ b/rules/rsa.go @@ -18,12 +18,12 @@ import ( "fmt" "go/ast" - "github.com/securego/gas" + "github.com/securego/gosec" ) type weakKeyStrength struct { - gas.MetaData - calls gas.CallList + gosec.MetaData + calls gosec.CallList bits int } @@ -31,27 +31,27 @@ func (w *weakKeyStrength) ID() string { return w.MetaData.ID } -func (w *weakKeyStrength) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) { +func (w *weakKeyStrength) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) { if callExpr := w.calls.ContainsCallExpr(n, c); callExpr != nil { - if bits, err := gas.GetInt(callExpr.Args[1]); err == nil && bits < (int64)(w.bits) { - return gas.NewIssue(c, n, w.ID(), w.What, w.Severity, w.Confidence), nil + if bits, err := gosec.GetInt(callExpr.Args[1]); err == nil && bits < (int64)(w.bits) { + return gosec.NewIssue(c, n, w.ID(), w.What, w.Severity, w.Confidence), nil } } return nil, nil } // NewWeakKeyStrength builds a rule that detects RSA keys < 2048 bits -func NewWeakKeyStrength(id string, conf gas.Config) (gas.Rule, []ast.Node) { - calls := gas.NewCallList() +func NewWeakKeyStrength(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { + calls := gosec.NewCallList() calls.Add("crypto/rsa", "GenerateKey") bits := 2048 return &weakKeyStrength{ calls: calls, bits: bits, - MetaData: gas.MetaData{ + MetaData: gosec.MetaData{ ID: id, - Severity: gas.Medium, - Confidence: gas.High, + Severity: gosec.Medium, + Confidence: gosec.High, What: fmt.Sprintf("RSA keys should be at least %d bits", bits), }, }, []ast.Node{(*ast.CallExpr)(nil)} diff --git a/rules/rulelist.go b/rules/rulelist.go index 63d98ab..16cb273 100644 --- a/rules/rulelist.go +++ b/rules/rulelist.go @@ -14,24 +14,22 @@ package rules -import ( - "github.com/securego/gas" -) +import "github.com/securego/gosec" // RuleDefinition contains the description of a rule and a mechanism to // create it. type RuleDefinition struct { ID string Description string - Create gas.RuleBuilder + Create gosec.RuleBuilder } // RuleList is a mapping of rule ID's to rule definitions type RuleList map[string]RuleDefinition // Builders returns all the create methods for a given rule list -func (rl RuleList) Builders() map[string]gas.RuleBuilder { - builders := make(map[string]gas.RuleBuilder) +func (rl RuleList) Builders() map[string]gosec.RuleBuilder { + builders := make(map[string]gosec.RuleBuilder) for _, def := range rl { builders[def.ID] = def.Create } diff --git a/rules/rules_test.go b/rules/rules_test.go index 7e9324b..958932f 100644 --- a/rules/rules_test.go +++ b/rules/rules_test.go @@ -4,28 +4,27 @@ import ( "fmt" "log" - "github.com/securego/gas" - . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" - "github.com/securego/gas/rules" - "github.com/securego/gas/testutils" + "github.com/securego/gosec" + "github.com/securego/gosec/rules" + "github.com/securego/gosec/testutils" ) -var _ = Describe("gas rules", func() { +var _ = Describe("gosec rules", func() { var ( logger *log.Logger - config gas.Config - analyzer *gas.Analyzer + config gosec.Config + analyzer *gosec.Analyzer runner func(string, []testutils.CodeSample) buildTags []string ) BeforeEach(func() { logger, _ = testutils.NewLogger() - config = gas.NewConfig() - analyzer = gas.NewAnalyzer(config, logger) + config = gosec.NewConfig() + analyzer = gosec.NewAnalyzer(config, logger) runner = func(rule string, samples []testutils.CodeSample) { analyzer.LoadRules(rules.Generate(rules.NewRuleFilter(false, rule)).Builders()) for n, sample := range samples { diff --git a/rules/sql.go b/rules/sql.go index 5e37c58..655f4b0 100644 --- a/rules/sql.go +++ b/rules/sql.go @@ -18,11 +18,11 @@ import ( "go/ast" "regexp" - "github.com/securego/gas" + "github.com/securego/gosec" ) type sqlStatement struct { - gas.MetaData + gosec.MetaData // Contains a list of patterns which must all match for the rule to match. patterns []*regexp.Regexp @@ -59,10 +59,10 @@ func (s *sqlStrConcat) checkObject(n *ast.Ident) bool { } // Look for "SELECT * FROM table WHERE " + " ' OR 1=1" -func (s *sqlStrConcat) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) { +func (s *sqlStrConcat) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) { if node, ok := n.(*ast.BinaryExpr); ok { if start, ok := node.X.(*ast.BasicLit); ok { - if str, e := gas.GetString(start); e == nil { + if str, e := gosec.GetString(start); e == nil { if !s.MatchPatterns(str) { return nil, nil } @@ -72,7 +72,7 @@ func (s *sqlStrConcat) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) { if second, ok := node.Y.(*ast.Ident); ok && s.checkObject(second) { return nil, nil } - return gas.NewIssue(c, n, s.ID(), s.What, s.Severity, s.Confidence), nil + return gosec.NewIssue(c, n, s.ID(), s.What, s.Severity, s.Confidence), nil } } } @@ -80,16 +80,16 @@ func (s *sqlStrConcat) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) { } // NewSQLStrConcat looks for cases where we are building SQL strings via concatenation -func NewSQLStrConcat(id string, conf gas.Config) (gas.Rule, []ast.Node) { +func NewSQLStrConcat(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { return &sqlStrConcat{ sqlStatement: sqlStatement{ patterns: []*regexp.Regexp{ regexp.MustCompile(`(?)(SELECT|DELETE|INSERT|UPDATE|INTO|FROM|WHERE) `), }, - MetaData: gas.MetaData{ + MetaData: gosec.MetaData{ ID: id, - Severity: gas.Medium, - Confidence: gas.High, + Severity: gosec.Medium, + Confidence: gosec.High, What: "SQL string concatenation", }, }, @@ -98,34 +98,34 @@ func NewSQLStrConcat(id string, conf gas.Config) (gas.Rule, []ast.Node) { type sqlStrFormat struct { sqlStatement - calls gas.CallList + calls gosec.CallList } // Looks for "fmt.Sprintf("SELECT * FROM foo where '%s', userInput)" -func (s *sqlStrFormat) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) { +func (s *sqlStrFormat) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) { // TODO(gm) improve confidence if database/sql is being used if node := s.calls.ContainsCallExpr(n, c); node != nil { - if arg, e := gas.GetString(node.Args[0]); s.MatchPatterns(arg) && e == nil { - return gas.NewIssue(c, n, s.ID(), s.What, s.Severity, s.Confidence), nil + if arg, e := gosec.GetString(node.Args[0]); s.MatchPatterns(arg) && e == nil { + return gosec.NewIssue(c, n, s.ID(), s.What, s.Severity, s.Confidence), nil } } return nil, nil } // NewSQLStrFormat looks for cases where we're building SQL query strings using format strings -func NewSQLStrFormat(id string, conf gas.Config) (gas.Rule, []ast.Node) { +func NewSQLStrFormat(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { rule := &sqlStrFormat{ - calls: gas.NewCallList(), + calls: gosec.NewCallList(), sqlStatement: sqlStatement{ patterns: []*regexp.Regexp{ regexp.MustCompile("(?)(SELECT|DELETE|INSERT|UPDATE|INTO|FROM|WHERE) "), regexp.MustCompile("%[^bdoxXfFp]"), }, - MetaData: gas.MetaData{ + MetaData: gosec.MetaData{ ID: id, - Severity: gas.Medium, - Confidence: gas.High, + Severity: gosec.Medium, + Confidence: gosec.High, What: "SQL string formatting", }, }, diff --git a/rules/ssh.go b/rules/ssh.go index 17ed71b..7496b5f 100644 --- a/rules/ssh.go +++ b/rules/ssh.go @@ -3,11 +3,11 @@ package rules import ( "go/ast" - "github.com/securego/gas" + "github.com/securego/gosec" ) type sshHostKey struct { - gas.MetaData + gosec.MetaData pkg string calls []string } @@ -16,23 +16,23 @@ func (r *sshHostKey) ID() string { return r.MetaData.ID } -func (r *sshHostKey) Match(n ast.Node, c *gas.Context) (gi *gas.Issue, err error) { - if _, matches := gas.MatchCallByPackage(n, c, r.pkg, r.calls...); matches { - return gas.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil +func (r *sshHostKey) Match(n ast.Node, c *gosec.Context) (gi *gosec.Issue, err error) { + if _, matches := gosec.MatchCallByPackage(n, c, r.pkg, r.calls...); matches { + return gosec.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil } return nil, nil } // NewSSHHostKey rule detects the use of insecure ssh HostKeyCallback. -func NewSSHHostKey(id string, conf gas.Config) (gas.Rule, []ast.Node) { +func NewSSHHostKey(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { return &sshHostKey{ pkg: "golang.org/x/crypto/ssh", calls: []string{"InsecureIgnoreHostKey"}, - MetaData: gas.MetaData{ + MetaData: gosec.MetaData{ ID: id, What: "Use of ssh InsecureIgnoreHostKey should be audited", - Severity: gas.Medium, - Confidence: gas.High, + Severity: gosec.Medium, + Confidence: gosec.High, }, }, []ast.Node{(*ast.CallExpr)(nil)} } diff --git a/rules/subproc.go b/rules/subproc.go index d34cfb1..b214ed9 100644 --- a/rules/subproc.go +++ b/rules/subproc.go @@ -18,12 +18,12 @@ import ( "go/ast" "go/types" - "github.com/securego/gas" + "github.com/securego/gosec" ) type subprocess struct { - gas.MetaData - gas.CallList + gosec.MetaData + gosec.CallList } func (r *subprocess) ID() string { @@ -39,24 +39,24 @@ func (r *subprocess) ID() string { // is unsafe. For example: // // syscall.Exec("echo", "foobar" + tainted) -func (r *subprocess) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) { +func (r *subprocess) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) { if node := r.ContainsCallExpr(n, c); node != nil { for _, arg := range node.Args { if ident, ok := arg.(*ast.Ident); ok { obj := c.Info.ObjectOf(ident) - if _, ok := obj.(*types.Var); ok && !gas.TryResolve(ident, c) { - return gas.NewIssue(c, n, r.ID(), "Subprocess launched with variable", gas.Medium, gas.High), nil + if _, ok := obj.(*types.Var); ok && !gosec.TryResolve(ident, c) { + return gosec.NewIssue(c, n, r.ID(), "Subprocess launched with variable", gosec.Medium, gosec.High), nil } } } - return gas.NewIssue(c, n, r.ID(), "Subprocess launching should be audited", gas.Low, gas.High), nil + return gosec.NewIssue(c, n, r.ID(), "Subprocess launching should be audited", gosec.Low, gosec.High), nil } return nil, nil } // NewSubproc detects cases where we are forking out to an external process -func NewSubproc(id string, conf gas.Config) (gas.Rule, []ast.Node) { - rule := &subprocess{gas.MetaData{ID: id}, gas.NewCallList()} +func NewSubproc(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { + rule := &subprocess{gosec.MetaData{ID: id}, gosec.NewCallList()} rule.Add("os/exec", "Command") rule.Add("syscall", "Exec") return rule, []ast.Node{(*ast.CallExpr)(nil)} diff --git a/rules/tempfiles.go b/rules/tempfiles.go index c2f587b..6963404 100644 --- a/rules/tempfiles.go +++ b/rules/tempfiles.go @@ -18,12 +18,12 @@ import ( "go/ast" "regexp" - "github.com/securego/gas" + "github.com/securego/gosec" ) type badTempFile struct { - gas.MetaData - calls gas.CallList + gosec.MetaData + calls gosec.CallList args *regexp.Regexp } @@ -31,27 +31,27 @@ func (t *badTempFile) ID() string { return t.MetaData.ID } -func (t *badTempFile) Match(n ast.Node, c *gas.Context) (gi *gas.Issue, err error) { +func (t *badTempFile) Match(n ast.Node, c *gosec.Context) (gi *gosec.Issue, err error) { if node := t.calls.ContainsCallExpr(n, c); node != nil { - if arg, e := gas.GetString(node.Args[0]); t.args.MatchString(arg) && e == nil { - return gas.NewIssue(c, n, t.ID(), t.What, t.Severity, t.Confidence), nil + if arg, e := gosec.GetString(node.Args[0]); t.args.MatchString(arg) && e == nil { + return gosec.NewIssue(c, n, t.ID(), t.What, t.Severity, t.Confidence), nil } } return nil, nil } // NewBadTempFile detects direct writes to predictable path in temporary directory -func NewBadTempFile(id string, conf gas.Config) (gas.Rule, []ast.Node) { - calls := gas.NewCallList() +func NewBadTempFile(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { + calls := gosec.NewCallList() calls.Add("io/ioutil", "WriteFile") calls.Add("os", "Create") return &badTempFile{ calls: calls, args: regexp.MustCompile(`^/tmp/.*$|^/var/tmp/.*$`), - MetaData: gas.MetaData{ + MetaData: gosec.MetaData{ ID: id, - Severity: gas.Medium, - Confidence: gas.High, + Severity: gosec.Medium, + Confidence: gosec.High, What: "File creation in shared tmp directory without using ioutil.Tempfile", }, }, []ast.Node{(*ast.CallExpr)(nil)} diff --git a/rules/templates.go b/rules/templates.go index 45ea3c0..30a964b 100644 --- a/rules/templates.go +++ b/rules/templates.go @@ -17,23 +17,23 @@ package rules import ( "go/ast" - "github.com/securego/gas" + "github.com/securego/gosec" ) type templateCheck struct { - gas.MetaData - calls gas.CallList + gosec.MetaData + calls gosec.CallList } func (t *templateCheck) ID() string { return t.MetaData.ID } -func (t *templateCheck) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) { +func (t *templateCheck) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) { if node := t.calls.ContainsCallExpr(n, c); node != nil { for _, arg := range node.Args { if _, ok := arg.(*ast.BasicLit); !ok { // basic lits are safe - return gas.NewIssue(c, n, t.ID(), t.What, t.Severity, t.Confidence), nil + return gosec.NewIssue(c, n, t.ID(), t.What, t.Severity, t.Confidence), nil } } } @@ -42,19 +42,19 @@ func (t *templateCheck) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) { // NewTemplateCheck constructs the template check rule. This rule is used to // find use of tempaltes where HTML/JS escaping is not being used -func NewTemplateCheck(id string, conf gas.Config) (gas.Rule, []ast.Node) { +func NewTemplateCheck(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { - calls := gas.NewCallList() + calls := gosec.NewCallList() calls.Add("html/template", "HTML") calls.Add("html/template", "HTMLAttr") calls.Add("html/template", "JS") calls.Add("html/template", "URL") return &templateCheck{ calls: calls, - MetaData: gas.MetaData{ + MetaData: gosec.MetaData{ ID: id, - Severity: gas.Medium, - Confidence: gas.Low, + Severity: gosec.Medium, + Confidence: gosec.Low, What: "this method will not auto-escape HTML. Verify data is well formed.", }, }, []ast.Node{(*ast.CallExpr)(nil)} diff --git a/rules/tls.go b/rules/tls.go index 62366bc..d4b7fa2 100644 --- a/rules/tls.go +++ b/rules/tls.go @@ -20,11 +20,11 @@ import ( "fmt" "go/ast" - "github.com/securego/gas" + "github.com/securego/gosec" ) type insecureConfigTLS struct { - gas.MetaData + gosec.MetaData MinVersion int16 MaxVersion int16 requiredType string @@ -44,14 +44,14 @@ func stringInSlice(a string, list []string) bool { return false } -func (t *insecureConfigTLS) processTLSCipherSuites(n ast.Node, c *gas.Context) *gas.Issue { +func (t *insecureConfigTLS) processTLSCipherSuites(n ast.Node, c *gosec.Context) *gosec.Issue { if ciphers, ok := n.(*ast.CompositeLit); ok { for _, cipher := range ciphers.Elts { if ident, ok := cipher.(*ast.SelectorExpr); ok { if !stringInSlice(ident.Sel.Name, t.goodCiphers) { err := fmt.Sprintf("TLS Bad Cipher Suite: %s", ident.Sel.Name) - return gas.NewIssue(c, ident, t.ID(), err, gas.High, gas.High) + return gosec.NewIssue(c, ident, t.ID(), err, gosec.High, gosec.High) } } } @@ -59,46 +59,46 @@ func (t *insecureConfigTLS) processTLSCipherSuites(n ast.Node, c *gas.Context) * return nil } -func (t *insecureConfigTLS) processTLSConfVal(n *ast.KeyValueExpr, c *gas.Context) *gas.Issue { +func (t *insecureConfigTLS) processTLSConfVal(n *ast.KeyValueExpr, c *gosec.Context) *gosec.Issue { if ident, ok := n.Key.(*ast.Ident); ok { switch ident.Name { case "InsecureSkipVerify": if node, ok := n.Value.(*ast.Ident); ok { if node.Name != "false" { - return gas.NewIssue(c, n, t.ID(), "TLS InsecureSkipVerify set true.", gas.High, gas.High) + return gosec.NewIssue(c, n, t.ID(), "TLS InsecureSkipVerify set true.", gosec.High, gosec.High) } } else { // TODO(tk): symbol tab look up to get the actual value - return gas.NewIssue(c, n, t.ID(), "TLS InsecureSkipVerify may be true.", gas.High, gas.Low) + return gosec.NewIssue(c, n, t.ID(), "TLS InsecureSkipVerify may be true.", gosec.High, gosec.Low) } case "PreferServerCipherSuites": if node, ok := n.Value.(*ast.Ident); ok { if node.Name == "false" { - return gas.NewIssue(c, n, t.ID(), "TLS PreferServerCipherSuites set false.", gas.Medium, gas.High) + return gosec.NewIssue(c, n, t.ID(), "TLS PreferServerCipherSuites set false.", gosec.Medium, gosec.High) } } else { // TODO(tk): symbol tab look up to get the actual value - return gas.NewIssue(c, n, t.ID(), "TLS PreferServerCipherSuites may be false.", gas.Medium, gas.Low) + return gosec.NewIssue(c, n, t.ID(), "TLS PreferServerCipherSuites may be false.", gosec.Medium, gosec.Low) } case "MinVersion": - if ival, ierr := gas.GetInt(n.Value); ierr == nil { + if ival, ierr := gosec.GetInt(n.Value); ierr == nil { if (int16)(ival) < t.MinVersion { - return gas.NewIssue(c, n, t.ID(), "TLS MinVersion too low.", gas.High, gas.High) + return gosec.NewIssue(c, n, t.ID(), "TLS MinVersion too low.", gosec.High, gosec.High) } // TODO(tk): symbol tab look up to get the actual value - return gas.NewIssue(c, n, t.ID(), "TLS MinVersion may be too low.", gas.High, gas.Low) + return gosec.NewIssue(c, n, t.ID(), "TLS MinVersion may be too low.", gosec.High, gosec.Low) } case "MaxVersion": - if ival, ierr := gas.GetInt(n.Value); ierr == nil { + if ival, ierr := gosec.GetInt(n.Value); ierr == nil { if (int16)(ival) < t.MaxVersion { - return gas.NewIssue(c, n, t.ID(), "TLS MaxVersion too low.", gas.High, gas.High) + return gosec.NewIssue(c, n, t.ID(), "TLS MaxVersion too low.", gosec.High, gosec.High) } // TODO(tk): symbol tab look up to get the actual value - return gas.NewIssue(c, n, t.ID(), "TLS MaxVersion may be too low.", gas.High, gas.Low) + return gosec.NewIssue(c, n, t.ID(), "TLS MaxVersion may be too low.", gosec.High, gosec.Low) } case "CipherSuites": @@ -112,7 +112,7 @@ func (t *insecureConfigTLS) processTLSConfVal(n *ast.KeyValueExpr, c *gas.Contex return nil } -func (t *insecureConfigTLS) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) { +func (t *insecureConfigTLS) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) { if complit, ok := n.(*ast.CompositeLit); ok && complit.Type != nil { actualType := c.Info.TypeOf(complit.Type) if actualType != nil && actualType.String() == t.requiredType { diff --git a/rules/tls_config.go b/rules/tls_config.go index a756386..a629917 100644 --- a/rules/tls_config.go +++ b/rules/tls_config.go @@ -3,14 +3,14 @@ package rules import ( "go/ast" - "github.com/securego/gas" + "github.com/securego/gosec" ) // NewModernTLSCheck creates a check for Modern TLS ciphers // DO NOT EDIT - generated by tlsconfig tool -func NewModernTLSCheck(id string, conf gas.Config) (gas.Rule, []ast.Node) { +func NewModernTLSCheck(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { return &insecureConfigTLS{ - MetaData: gas.MetaData{ID: id}, + MetaData: gosec.MetaData{ID: id}, requiredType: "crypto/tls.Config", MinVersion: 0x0303, MaxVersion: 0x0303, @@ -31,9 +31,9 @@ func NewModernTLSCheck(id string, conf gas.Config) (gas.Rule, []ast.Node) { // NewIntermediateTLSCheck creates a check for Intermediate TLS ciphers // DO NOT EDIT - generated by tlsconfig tool -func NewIntermediateTLSCheck(id string, conf gas.Config) (gas.Rule, []ast.Node) { +func NewIntermediateTLSCheck(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { return &insecureConfigTLS{ - MetaData: gas.MetaData{ID: id}, + MetaData: gosec.MetaData{ID: id}, requiredType: "crypto/tls.Config", MinVersion: 0x0301, MaxVersion: 0x0303, @@ -74,9 +74,9 @@ func NewIntermediateTLSCheck(id string, conf gas.Config) (gas.Rule, []ast.Node) // NewOldTLSCheck creates a check for Old TLS ciphers // DO NOT EDIT - generated by tlsconfig tool -func NewOldTLSCheck(id string, conf gas.Config) (gas.Rule, []ast.Node) { +func NewOldTLSCheck(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { return &insecureConfigTLS{ - MetaData: gas.MetaData{ID: id}, + MetaData: gosec.MetaData{ID: id}, requiredType: "crypto/tls.Config", MinVersion: 0x0300, MaxVersion: 0x0303, diff --git a/rules/unsafe.go b/rules/unsafe.go index bb88aa6..f4a38be 100644 --- a/rules/unsafe.go +++ b/rules/unsafe.go @@ -17,11 +17,11 @@ package rules import ( "go/ast" - "github.com/securego/gas" + "github.com/securego/gosec" ) type usingUnsafe struct { - gas.MetaData + gosec.MetaData pkg string calls []string } @@ -30,24 +30,24 @@ func (r *usingUnsafe) ID() string { return r.MetaData.ID } -func (r *usingUnsafe) Match(n ast.Node, c *gas.Context) (gi *gas.Issue, err error) { - if _, matches := gas.MatchCallByPackage(n, c, r.pkg, r.calls...); matches { - return gas.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil +func (r *usingUnsafe) Match(n ast.Node, c *gosec.Context) (gi *gosec.Issue, err error) { + if _, matches := gosec.MatchCallByPackage(n, c, r.pkg, r.calls...); matches { + return gosec.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil } return nil, nil } // NewUsingUnsafe rule detects the use of the unsafe package. This is only // really useful for auditing purposes. -func NewUsingUnsafe(id string, conf gas.Config) (gas.Rule, []ast.Node) { +func NewUsingUnsafe(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { return &usingUnsafe{ pkg: "unsafe", calls: []string{"Alignof", "Offsetof", "Sizeof", "Pointer"}, - MetaData: gas.MetaData{ + MetaData: gosec.MetaData{ ID: id, What: "Use of unsafe calls should be audited", - Severity: gas.Low, - Confidence: gas.High, + Severity: gosec.Low, + Confidence: gosec.High, }, }, []ast.Node{(*ast.CallExpr)(nil)} } diff --git a/rules/weakcrypto.go b/rules/weakcrypto.go index 26b03df..db1ada7 100644 --- a/rules/weakcrypto.go +++ b/rules/weakcrypto.go @@ -17,11 +17,11 @@ package rules import ( "go/ast" - "github.com/securego/gas" + "github.com/securego/gosec" ) type usesWeakCryptography struct { - gas.MetaData + gosec.MetaData blacklist map[string][]string } @@ -29,27 +29,27 @@ func (r *usesWeakCryptography) ID() string { return r.MetaData.ID } -func (r *usesWeakCryptography) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) { +func (r *usesWeakCryptography) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) { for pkg, funcs := range r.blacklist { - if _, matched := gas.MatchCallByPackage(n, c, pkg, funcs...); matched { - return gas.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil + if _, matched := gosec.MatchCallByPackage(n, c, pkg, funcs...); matched { + return gosec.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil } } return nil, nil } // NewUsesWeakCryptography detects uses of des.* md5.* or rc4.* -func NewUsesWeakCryptography(id string, conf gas.Config) (gas.Rule, []ast.Node) { +func NewUsesWeakCryptography(id string, conf gosec.Config) (gosec.Rule, []ast.Node) { calls := make(map[string][]string) calls["crypto/des"] = []string{"NewCipher", "NewTripleDESCipher"} calls["crypto/md5"] = []string{"New", "Sum"} calls["crypto/rc4"] = []string{"NewCipher"} rule := &usesWeakCryptography{ blacklist: calls, - MetaData: gas.MetaData{ + MetaData: gosec.MetaData{ ID: id, - Severity: gas.Medium, - Confidence: gas.High, + Severity: gosec.Medium, + Confidence: gosec.High, What: "Use of weak cryptographic primitive", }, } diff --git a/testutils/pkg.go b/testutils/pkg.go index b74c211..ee85ac7 100644 --- a/testutils/pkg.go +++ b/testutils/pkg.go @@ -10,7 +10,7 @@ import ( "path" "strings" - "github.com/securego/gas" + "github.com/securego/gosec" "golang.org/x/tools/go/loader" ) @@ -33,7 +33,7 @@ type TestPackage struct { func NewTestPackage() *TestPackage { // Files must exist in $GOPATH sourceDir := path.Join(os.Getenv("GOPATH"), "src") - workingDir, err := ioutil.TempDir(sourceDir, "gas_test") + workingDir, err := ioutil.TempDir(sourceDir, "gosecs_test") if err != nil { return nil } @@ -97,7 +97,7 @@ func (p *TestPackage) Build() error { } // CreateContext builds a context out of supplied package context -func (p *TestPackage) CreateContext(filename string) *gas.Context { +func (p *TestPackage) CreateContext(filename string) *gosec.Context { if err := p.Build(); err != nil { log.Fatal(err) return nil @@ -109,13 +109,13 @@ func (p *TestPackage) CreateContext(filename string) *gas.Context { strip := fmt.Sprintf("%s%c", p.Path, os.PathSeparator) pkgFile = strings.TrimPrefix(pkgFile, strip) if pkgFile == filename { - ctx := &gas.Context{ + ctx := &gosec.Context{ FileSet: p.build.program.Fset, Root: file, - Config: gas.NewConfig(), + Config: gosec.NewConfig(), Info: &pkg.Info, Pkg: pkg.Pkg, - Imports: gas.NewImportTracker(), + Imports: gosec.NewImportTracker(), } ctx.Imports.TrackPackages(ctx.Pkg.Imports()...) return ctx diff --git a/testutils/visitor.go b/testutils/visitor.go index b2c6a50..775829b 100644 --- a/testutils/visitor.go +++ b/testutils/visitor.go @@ -3,14 +3,14 @@ package testutils import ( "go/ast" - "github.com/securego/gas" + "github.com/securego/gosec" ) // MockVisitor is useful for stubbing out ast.Visitor with callback // and looking for specific conditions to exist. type MockVisitor struct { - Context *gas.Context - Callback func(n ast.Node, ctx *gas.Context) bool + Context *gosec.Context + Callback func(n ast.Node, ctx *gosec.Context) bool } // NewMockVisitor creates a new empty struct, the Context and From e6641c626581f7e53d7966e74785aed14e8e530e Mon Sep 17 00:00:00 2001 From: Cosmin Cojocar Date: Thu, 19 Jul 2018 18:46:26 +0200 Subject: [PATCH 4/7] Replace gas with gosec in the README file --- README.md | 49 +++++++++++++++++++++++-------------------------- 1 file changed, 23 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 253fbb0..ceb925e 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -## GAS - Go Application Security +## gosec -Golang Security Checker Inspects source code for security problems by scanning the Go AST. @@ -12,26 +12,23 @@ You may obtain a copy of the License [here](http://www.apache.org/licenses/LICEN ### Project status -[![Build Status](https://travis-ci.org/securego/gas.svg?branch=master)](https://travis-ci.org/securego/gas) -[![GoDoc](https://godoc.org/github.com/securego/gas?status.svg)](https://godoc.org/github.com/securego/gas) - -Gas is still in alpha and accepting feedback from early adopters. We do -not consider it production ready at this time. +[![Build Status](https://travis-ci.org/securego/gosec.svg?branch=master)](https://travis-ci.org/securego/gosec) +[![GoDoc](https://godoc.org/github.com/securego/gosec?status.svg)](https://godoc.org/github.com/securego/gosec) ### Install -`$ go get github.com/securego/gas/cmd/gas/...` +`$ go get github.com/securego/gosec/cmd/gosec/...` ### Usage -Gas can be configured to only run a subset of rules, to exclude certain file +Gosec can be configured to only run a subset of rules, to exclude certain file paths, and produce reports in different formats. By default all rules will be run against the supplied input files. To recursively scan from the current directory you can supply './...' as the input argument. #### Selecting rules -By default Gas will run all rules against the supplied file paths. It is however possible to select a subset of rules to run via the '-include=' flag, +By default gosec will run all rules against the supplied file paths. It is however possible to select a subset of rules to run via the '-include=' flag, or to specify a set of rules to explicitly exclude using the '-exclude=' flag. ##### Available rules @@ -63,22 +60,22 @@ or to specify a set of rules to explicitly exclude using the '-exclude=' flag. ``` # Run a specific set of rules -$ gas -include=G101,G203,G401 ./... +$ gosec -include=G101,G203,G401 ./... # Run everything except for rule G303 -$ gas -exclude=G303 ./... +$ gosec -exclude=G303 ./... ``` #### Excluding files: -Gas will ignore dependencies in your vendor directory any files +gosec will ignore dependencies in your vendor directory any files that are not considered build artifacts by the compiler (so test files). #### Annotating code -As with all automated detection tools there will be cases of false positives. In cases where Gas reports a failure that has been manually verified as being safe it is possible to annotate the code with a '#nosec' comment. +As with all automated detection tools there will be cases of false positives. In cases where gosec reports a failure that has been manually verified as being safe it is possible to annotate the code with a '#nosec' comment. -The annotation causes Gas to stop processing any further nodes within the +The annotation causes gosec to stop processing any further nodes within the AST so can apply to a whole block or more granularly to a single expression. ```go @@ -102,26 +99,26 @@ have been used. To run the scanner and ignore any #nosec annotations you can do the following: ``` -$ gas -nosec=true ./... +$ gosec -nosec=true ./... ``` #### Build tags -Gas is able to pass your [Go build tags](https://golang.org/pkg/go/build/) to the analyzer. +gosec is able to pass your [Go build tags](https://golang.org/pkg/go/build/) to the analyzer. They can be provided as a comma separated list as follows: ``` -$ gas -tag debug,ignore ./... +$ gosec -tag debug,ignore ./... ``` ### Output formats -Gas currently supports text, json, yaml, csv and JUnit XML output formats. By default +gosec currently supports text, json, yaml, csv and JUnit XML output formats. By default results will be reported to stdout, but can also be written to an output file. The output format is controlled by the '-fmt' flag, and the output file is controlled by the '-out' flag as follows: ``` # Write output in json format to results.json -$ gas -fmt=json -out=results.json *.go +$ gosec -fmt=json -out=results.json *.go ``` ### Development @@ -144,7 +141,7 @@ make test #### Release Build -Gas can be released as follows: +gosec can be released as follows: ```bash make release VERSION=2.0.0 @@ -153,11 +150,11 @@ make release VERSION=2.0.0 The released version of the tool is available in the `build` folder. The build information should be displayed in the usage text. ``` -./build/gas-2.0.0-linux-amd64 -h +./build/gosec-2.0.0-linux-amd64 -h -GAS - Go AST Scanner +gosec - Golang security checker -Gas analyzes Go source code to look for common programming mistakes that +gosec analyzes Go source code to look for common programming mistakes that can lead to security problems. VERSION: 2.0.0 @@ -174,10 +171,10 @@ You can execute a release and build the docker image as follows: make image VERSION=2.0.0 ``` -Now you can run the gas tool in a container against your local workspace: +Now you can run the gosec tool in a container against your local workspace: ``` -docker run -it -v :/workspace gas /workspace +docker run -it -v :/workspace gosec /workspace ``` #### Generate TLS rule @@ -188,7 +185,7 @@ The configuration of TLS rule can be generated from [Mozilla's TLS ciphers recom First you need to install the generator tool: ``` -go get github.com/securego/gas/cmd/tlsconfig/... +go get github.com/securego/gosec/cmd/tlsconfig/... ``` You can invoke now the `go generate` in the root of the project: From 138e6decee8e6cd044bc12c5396e41c1315b03e6 Mon Sep 17 00:00:00 2001 From: Grant Murphy Date: Fri, 20 Jul 2018 09:22:43 +1000 Subject: [PATCH 5/7] Add slack community link (#215) Add slack community link --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index ceb925e..5c5d357 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,8 @@ You may obtain a copy of the License [here](http://www.apache.org/licenses/LICEN [![Build Status](https://travis-ci.org/securego/gosec.svg?branch=master)](https://travis-ci.org/securego/gosec) [![GoDoc](https://godoc.org/github.com/securego/gosec?status.svg)](https://godoc.org/github.com/securego/gosec) +![Slack](http://securego.herokuapp.com/badge.svg)](http://securego.herokuapp.com) + ### Install From 3f2b81461f928d1933d9afbce153cbbb1c061ba2 Mon Sep 17 00:00:00 2001 From: Grant Murphy Date: Fri, 20 Jul 2018 09:23:46 +1000 Subject: [PATCH 6/7] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5c5d357..830d330 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ You may obtain a copy of the License [here](http://www.apache.org/licenses/LICEN [![Build Status](https://travis-ci.org/securego/gosec.svg?branch=master)](https://travis-ci.org/securego/gosec) [![GoDoc](https://godoc.org/github.com/securego/gosec?status.svg)](https://godoc.org/github.com/securego/gosec) -![Slack](http://securego.herokuapp.com/badge.svg)](http://securego.herokuapp.com) +[![Slack](http://securego.herokuapp.com/badge.svg)](http://securego.herokuapp.com) ### Install From 4c6396b7d4e2cea5e9332d5802f7a31b6bb29133 Mon Sep 17 00:00:00 2001 From: Cosmin Cojocar Date: Mon, 23 Jul 2018 15:16:47 +0200 Subject: [PATCH 7/7] Derive the package from given files Move some utility functions into the helper --- analyzer.go | 7 +----- cmd/gosec/main.go | 46 ++++++---------------------------- helpers.go | 63 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 71 insertions(+), 45 deletions(-) diff --git a/analyzer.go b/analyzer.go index e21722f..231b718 100644 --- a/analyzer.go +++ b/analyzer.go @@ -28,8 +28,6 @@ import ( "regexp" "strings" - "path/filepath" - "golang.org/x/tools/go/loader" ) @@ -106,11 +104,8 @@ func (gosec *Analyzer) Process(buildTags []string, packagePaths ...string) error AllowErrors: true, } for _, packagePath := range packagePaths { - abspath, err := filepath.Abs(packagePath) + abspath, err := GetPkgAbsPath(packagePath) if err != nil { - return err - } - if _, err := os.Stat(abspath); os.IsNotExist(err) { gosec.logger.Printf("Skipping: %s. Path doesn't exist.", abspath) continue } diff --git a/cmd/gosec/main.go b/cmd/gosec/main.go index df88a19..3d6479a 100644 --- a/cmd/gosec/main.go +++ b/cmd/gosec/main.go @@ -20,10 +20,8 @@ import ( "io/ioutil" "log" "os" - "os/user" "path/filepath" "regexp" - "runtime" "sort" "strings" @@ -178,36 +176,13 @@ func saveOutput(filename, format string, issues []*gosec.Issue, metrics *gosec.M return nil } -func getenv(key, userDefault string) string { - if val := os.Getenv(key); val != "" { - return val - } - return userDefault -} - -func gopath() []string { - defaultGoPath := runtime.GOROOT() - if u, err := user.Current(); err == nil { - defaultGoPath = filepath.Join(u.HomeDir, "go") - } - path := getenv("GOPATH", defaultGoPath) - paths := strings.Split(path, string(os.PathListSeparator)) - for idx, path := range paths { - if abs, err := filepath.Abs(path); err == nil { - paths[idx] = abs - } - } - return paths -} - -func cleanPath(path string, gopaths []string) (string, error) { - +func cleanPath(path string) (string, error) { cleanFailed := fmt.Errorf("%s is not within the $GOPATH and cannot be processed", path) nonRecursivePath := strings.TrimSuffix(path, "/...") // do not attempt to clean directs that are resolvable on gopath if _, err := os.Stat(nonRecursivePath); err != nil && os.IsNotExist(err) { log.Printf("directory %s doesn't exist, checking if is a package on $GOPATH", path) - for _, basedir := range gopaths { + for _, basedir := range gosec.Gopath() { dir := filepath.Join(basedir, "src", nonRecursivePath) if st, err := os.Stat(dir); err == nil && st.IsDir() { log.Printf("located %s in %s", path, dir) @@ -218,24 +193,17 @@ func cleanPath(path string, gopaths []string) (string, error) { } // ensure we resolve package directory correctly based on $GOPATH - abspath, err := filepath.Abs(path) + pkgPath, err := gosec.GetPkgRelativePath(path) if err != nil { - abspath = path + return "", cleanFailed } - for _, base := range gopaths { - projectRoot := filepath.FromSlash(fmt.Sprintf("%s/src/", base)) - if strings.HasPrefix(abspath, projectRoot) { - return strings.TrimPrefix(abspath, projectRoot), nil - } - } - return "", cleanFailed + return pkgPath, nil } func cleanPaths(paths []string) []string { - gopaths := gopath() var clean []string for _, path := range paths { - cleaned, err := cleanPath(path, gopaths) + cleaned, err := cleanPath(path) if err != nil { log.Fatal(err) } @@ -306,7 +274,7 @@ func main() { var packages []string // Iterate over packages on the import paths - gopaths := gopath() + gopaths := gosec.Gopath() for _, pkg := range gotool.ImportPaths(cleanPaths(flag.Args())) { // Skip vendor directory diff --git a/helpers.go b/helpers.go index 515ee8b..abe4c15 100644 --- a/helpers.go +++ b/helpers.go @@ -15,11 +15,17 @@ package gosec import ( + "errors" "fmt" "go/ast" "go/token" "go/types" + "os" + "os/user" + "path/filepath" + "runtime" "strconv" + "strings" ) // MatchCallByPackage ensures that the specified package is imported, @@ -193,3 +199,60 @@ func GetLocation(n ast.Node, ctx *Context) (string, int) { fobj := ctx.FileSet.File(n.Pos()) return fobj.Name(), fobj.Line(n.Pos()) } + +// Gopath returns all GOPATHs +func Gopath() []string { + defaultGoPath := runtime.GOROOT() + if u, err := user.Current(); err == nil { + defaultGoPath = filepath.Join(u.HomeDir, "go") + } + path := Getenv("GOPATH", defaultGoPath) + paths := strings.Split(path, string(os.PathListSeparator)) + for idx, path := range paths { + if abs, err := filepath.Abs(path); err == nil { + paths[idx] = abs + } + } + return paths +} + +// Getenv returns the values of the environment variable, otherwise +//returns the default if variable is not set +func Getenv(key, userDefault string) string { + if val := os.Getenv(key); val != "" { + return val + } + return userDefault +} + +// GetPkgRelativePath returns the Go relative relative path derived +// form the given path +func GetPkgRelativePath(path string) (string, error) { + abspath, err := filepath.Abs(path) + if err != nil { + abspath = path + } + if strings.HasSuffix(abspath, ".go") { + abspath = filepath.Dir(abspath) + } + for _, base := range Gopath() { + projectRoot := filepath.FromSlash(fmt.Sprintf("%s/src/", base)) + if strings.HasPrefix(abspath, projectRoot) { + return strings.TrimPrefix(abspath, projectRoot), nil + } + } + return "", errors.New("no project relative path found") +} + +// GetPkgAbsPath returns the Go package absolute path derived from +// the given path +func GetPkgAbsPath(pkgPath string) (string, error) { + absPath, err := filepath.Abs(pkgPath) + if err != nil { + return "", err + } + if _, err := os.Stat(absPath); os.IsNotExist(err) { + return "", errors.New("no project absolute path found") + } + return absPath, nil +}