Merge branch 'master' into commandcontext

This commit is contained in:
Will Roden 2018-07-26 09:13:43 -05:00
commit 6a156e2695
54 changed files with 632 additions and 451 deletions

View file

@ -2,7 +2,7 @@
### Steps to reproduce the behavior ### Steps to reproduce the behavior
### Gas version ### gosec version
### Go version (output of 'go version') ### Go version (output of 'go version')

View file

@ -1,7 +1,6 @@
language: go language: go
go: go:
- 1.8
- 1.9 - 1.9
- "1.10" - "1.10"
- tip - tip
@ -12,7 +11,7 @@ install:
- go get -u github.com/onsi/ginkgo/ginkgo - go get -u github.com/onsi/ginkgo/ginkgo
- go get -u github.com/onsi/gomega - go get -u github.com/onsi/gomega
- go get -u golang.org/x/crypto/ssh - go get -u golang.org/x/crypto/ssh
- go get -u github.com/GoASTScanner/gas/cmd/gas/... - go get -u github.com/securego/gosec/cmd/gosec/...
- go get -v -t ./... - go get -v -t ./...
- export PATH=$PATH:$HOME/gopath/bin - export PATH=$PATH:$HOME/gopath/bin

View file

@ -1,6 +1,6 @@
FROM golang:1.9.4-alpine3.7 FROM golang:1.9.4-alpine3.7
ENV BIN=gas ENV BIN=gosec
COPY build/*-linux-amd64 /go/bin/$BIN COPY build/*-linux-amd64 /go/bin/$BIN
COPY docker-entrypoint.sh /usr/local/bin COPY docker-entrypoint.sh /usr/local/bin

View file

@ -1,7 +1,7 @@
GIT_TAG?= $(shell git describe --always --tags) GIT_TAG?= $(shell git describe --always --tags)
BUILD_DATE = $(shell date +%Y-%m-%d) BUILD_DATE = $(shell date +%Y-%m-%d)
BIN = 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/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/gosec/ &
FMT_CMD = $(gofmt -s -l -w $(find . -type f -name '*.go' -not -path './vendor/*') | tee /dev/stderr) FMT_CMD = $(gofmt -s -l -w $(find . -type f -name '*.go' -not -path './vendor/*') | tee /dev/stderr)
IMAGE_REPO = docker.io IMAGE_REPO = docker.io
@ -13,12 +13,12 @@ test: bootstrap
test -z '$(FMT_CMD)' test -z '$(FMT_CMD)'
go vet $(go list ./... | grep -v /vendor/) go vet $(go list ./... | grep -v /vendor/)
golint -set_exit_status $(shell go list ./... | grep -v vendor) golint -set_exit_status $(shell go list ./... | grep -v vendor)
gas ./... gosec ./...
ginkgo -r -v ginkgo -r -v
bootstrap: bootstrap:
dep ensure dep ensure
build: build:
go build -o $(BIN) ./cmd/gas/ go build -o $(BIN) ./cmd/gosec/
clean: clean:
rm -rf build vendor rm -rf build vendor
rm -f release image bootstrap $(BIN) rm -f release image bootstrap $(BIN)

View file

@ -1,6 +1,6 @@
## GAS - Go AST Scanner ## gosec -Golang Security Checker
Inspects source code for security problems by scanning the Go AST. Inspects source code for security problems by scanning the Go AST.
@ -12,26 +12,25 @@ You may obtain a copy of the License [here](http://www.apache.org/licenses/LICEN
### Project status ### Project status
[![Build Status](https://travis-ci.org/GoASTScanner/gas.svg?branch=master)](https://travis-ci.org/GoASTScanner/gas) [![Build Status](https://travis-ci.org/securego/gosec.svg?branch=master)](https://travis-ci.org/securego/gosec)
[![GoDoc](https://godoc.org/github.com/GoASTScanner/gas?status.svg)](https://godoc.org/github.com/GoASTScanner/gas) [![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)
Gas is still in alpha and accepting feedback from early adopters. We do
not consider it production ready at this time.
### Install ### Install
`$ go get github.com/GoASTScanner/gas/cmd/gas/...` `$ go get github.com/securego/gosec/cmd/gosec/...`
### Usage ### 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 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 run against the supplied input files. To recursively scan from the current
directory you can supply './...' as the input argument. directory you can supply './...' as the input argument.
#### Selecting rules #### 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. or to specify a set of rules to explicitly exclude using the '-exclude=' flag.
##### Available rules ##### Available rules
@ -50,6 +49,7 @@ or to specify a set of rules to explicitly exclude using the '-exclude=' flag.
- G302: Poor file permisions used with chmod - G302: Poor file permisions used with chmod
- G303: Creating tempfile using a predictable path - G303: Creating tempfile using a predictable path
- G304: File path provided as taint input - G304: File path provided as taint input
- G305: File traversal when extracting zip archive
- G401: Detect the usage of DES, RC4, or MD5 - G401: Detect the usage of DES, RC4, or MD5
- G402: Look for bad TLS connection settings - G402: Look for bad TLS connection settings
- G403: Ensure minimum RSA key length of 2048 bits - G403: Ensure minimum RSA key length of 2048 bits
@ -62,22 +62,22 @@ or to specify a set of rules to explicitly exclude using the '-exclude=' flag.
``` ```
# Run a specific set of rules # Run a specific set of rules
$ gas -include=G101,G203,G401 ./... $ gosec -include=G101,G203,G401 ./...
# Run everything except for rule G303 # Run everything except for rule G303
$ gas -exclude=G303 ./... $ gosec -exclude=G303 ./...
``` ```
#### Excluding files: #### 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). that are not considered build artifacts by the compiler (so test files).
#### Annotating code #### 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. AST so can apply to a whole block or more granularly to a single expression.
```go ```go
@ -101,26 +101,26 @@ have been used. To run the scanner and ignore any #nosec annotations you
can do the following: can do the following:
``` ```
$ gas -nosec=true ./... $ gosec -nosec=true ./...
``` ```
#### Build tags #### 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: They can be provided as a comma separated list as follows:
``` ```
$ gas -tag debug,ignore ./... $ gosec -tag debug,ignore ./...
``` ```
### Output formats ### 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 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: 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 # Write output in json format to results.json
$ gas -fmt=json -out=results.json *.go $ gosec -fmt=json -out=results.json *.go
``` ```
### Development ### Development
@ -143,7 +143,7 @@ make test
#### Release Build #### Release Build
Gas can be released as follows: gosec can be released as follows:
```bash ```bash
make release VERSION=2.0.0 make release VERSION=2.0.0
@ -152,11 +152,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. 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. can lead to security problems.
VERSION: 2.0.0 VERSION: 2.0.0
@ -173,10 +173,10 @@ You can execute a release and build the docker image as follows:
make image VERSION=2.0.0 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 <YOUR LOCAL WORKSPACE>:/workspace gas /workspace docker run -it -v <YOUR LOCAL WORKSPACE>:/workspace gosec /workspace
``` ```
#### Generate TLS rule #### Generate TLS rule
@ -187,7 +187,7 @@ The configuration of TLS rule can be generated from [Mozilla's TLS ciphers recom
First you need to install the generator tool: First you need to install the generator tool:
``` ```
go get github.com/GoASTScanner/gas/cmd/tlsconfig/... go get github.com/securego/gosec/cmd/tlsconfig/...
``` ```
You can invoke now the `go generate` in the root of the project: You can invoke now the `go generate` in the root of the project:

View file

@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
// Package gas holds the central scanning logic used by GAS // Package gosec holds the central scanning logic used by gosec security scanner
package gas package gosec
import ( import (
"go/ast" "go/ast"
@ -28,8 +28,6 @@ import (
"regexp" "regexp"
"strings" "strings"
"path/filepath"
"golang.org/x/tools/go/loader" "golang.org/x/tools/go/loader"
) )
@ -55,7 +53,7 @@ type Metrics struct {
NumFound int `json:"found"` 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. // and invoke the correct checking rules as on each node as required.
type Analyzer struct { type Analyzer struct {
ignoreNosec bool ignoreNosec bool
@ -74,7 +72,7 @@ func NewAnalyzer(conf Config, logger *log.Logger) *Analyzer {
ignoreNoSec = setting == "true" || setting == "enabled" ignoreNoSec = setting == "true" || setting == "enabled"
} }
if logger == nil { if logger == nil {
logger = log.New(os.Stderr, "[gas]", log.LstdFlags) logger = log.New(os.Stderr, "[gosec]", log.LstdFlags)
} }
return &Analyzer{ return &Analyzer{
ignoreNosec: ignoreNoSec, ignoreNosec: ignoreNoSec,
@ -89,15 +87,15 @@ func NewAnalyzer(conf Config, logger *log.Logger) *Analyzer {
// LoadRules instantiates all the rules to be used when analyzing source // LoadRules instantiates all the rules to be used when analyzing source
// packages // packages
func (gas *Analyzer) LoadRules(ruleDefinitions map[string]RuleBuilder) { func (gosec *Analyzer) LoadRules(ruleDefinitions map[string]RuleBuilder) {
for id, def := range ruleDefinitions { for id, def := range ruleDefinitions {
r, nodes := def(id, gas.config) r, nodes := def(id, gosec.config)
gas.ruleset.Register(r, nodes...) gosec.ruleset.Register(r, nodes...)
} }
} }
// Process kicks off the analysis process for a given package // 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 := build.Default
ctx.BuildTags = append(ctx.BuildTags, buildTags...) ctx.BuildTags = append(ctx.BuildTags, buildTags...)
packageConfig := loader.Config{ packageConfig := loader.Config{
@ -106,15 +104,12 @@ func (gas *Analyzer) Process(buildTags []string, packagePaths ...string) error {
AllowErrors: true, AllowErrors: true,
} }
for _, packagePath := range packagePaths { for _, packagePath := range packagePaths {
abspath, err := filepath.Abs(packagePath) abspath, err := GetPkgAbsPath(packagePath)
if err != nil { if err != nil {
return err gosec.logger.Printf("Skipping: %s. Path doesn't exist.", abspath)
}
if _, err := os.Stat(abspath); os.IsNotExist(err) {
gas.logger.Printf("Skipping: %s. Path doesn't exist.", abspath)
continue continue
} }
gas.logger.Println("Searching directory:", abspath) gosec.logger.Println("Searching directory:", abspath)
basePackage, err := build.Default.ImportDir(packagePath, build.ImportComment) basePackage, err := build.Default.ImportDir(packagePath, build.ImportComment)
if err != nil { if err != nil {
@ -135,31 +130,31 @@ func (gas *Analyzer) Process(buildTags []string, packagePaths ...string) error {
} }
for _, pkg := range builtPackage.Created { for _, pkg := range builtPackage.Created {
gas.logger.Println("Checking package:", pkg.String()) gosec.logger.Println("Checking package:", pkg.String())
for _, file := range pkg.Files { for _, file := range pkg.Files {
gas.logger.Println("Checking file:", builtPackage.Fset.File(file.Pos()).Name()) gosec.logger.Println("Checking file:", builtPackage.Fset.File(file.Pos()).Name())
gas.context.FileSet = builtPackage.Fset gosec.context.FileSet = builtPackage.Fset
gas.context.Config = gas.config gosec.context.Config = gosec.config
gas.context.Comments = ast.NewCommentMap(gas.context.FileSet, file, file.Comments) gosec.context.Comments = ast.NewCommentMap(gosec.context.FileSet, file, file.Comments)
gas.context.Root = file gosec.context.Root = file
gas.context.Info = &pkg.Info gosec.context.Info = &pkg.Info
gas.context.Pkg = pkg.Pkg gosec.context.Pkg = pkg.Pkg
gas.context.Imports = NewImportTracker() gosec.context.Imports = NewImportTracker()
gas.context.Imports.TrackPackages(gas.context.Pkg.Imports()...) gosec.context.Imports.TrackPackages(gosec.context.Pkg.Imports()...)
ast.Walk(gas, file) ast.Walk(gosec, file)
gas.stats.NumFiles++ gosec.stats.NumFiles++
gas.stats.NumLines += builtPackage.Fset.File(file.Pos()).LineCount() gosec.stats.NumLines += builtPackage.Fset.File(file.Pos()).LineCount()
} }
} }
return nil return nil
} }
// ignore a node (and sub-tree) if it is tagged with a "#nosec" comment // ignore a node (and sub-tree) if it is tagged with a "#nosec" comment
func (gas *Analyzer) ignore(n ast.Node) ([]string, bool) { func (gosec *Analyzer) ignore(n ast.Node) ([]string, bool) {
if groups, ok := gas.context.Comments[n]; ok && !gas.ignoreNosec { if groups, ok := gosec.context.Comments[n]; ok && !gosec.ignoreNosec {
for _, group := range groups { for _, group := range groups {
if strings.Contains(group.Text(), "#nosec") { if strings.Contains(group.Text(), "#nosec") {
gas.stats.NumNosec++ gosec.stats.NumNosec++
// Pull out the specific rules that are listed to be ignored. // Pull out the specific rules that are listed to be ignored.
re := regexp.MustCompile("(G\\d{3})") re := regexp.MustCompile("(G\\d{3})")
@ -182,27 +177,27 @@ func (gas *Analyzer) ignore(n ast.Node) ([]string, bool) {
return nil, false 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. // 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 we've reached the end of this branch, pop off the ignores stack.
if n == nil { if n == nil {
if len(gas.context.Ignores) > 0 { if len(gosec.context.Ignores) > 0 {
gas.context.Ignores = gas.context.Ignores[1:] gosec.context.Ignores = gosec.context.Ignores[1:]
} }
return gas return gosec
} }
// Get any new rule exclusions. // Get any new rule exclusions.
ignoredRules, ignoreAll := gas.ignore(n) ignoredRules, ignoreAll := gosec.ignore(n)
if ignoreAll { if ignoreAll {
return nil return nil
} }
// Now create the union of exclusions. // Now create the union of exclusions.
ignores := make(map[string]bool, 0) ignores := make(map[string]bool, 0)
if len(gas.context.Ignores) > 0 { if len(gosec.context.Ignores) > 0 {
for k, v := range gas.context.Ignores[0] { for k, v := range gosec.context.Ignores[0] {
ignores[k] = v ignores[k] = v
} }
} }
@ -212,37 +207,37 @@ func (gas *Analyzer) Visit(n ast.Node) ast.Visitor {
} }
// Push the new set onto the stack. // 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 // 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 { if _, ok := ignores[rule.ID()]; ok {
continue continue
} }
issue, err := rule.Match(n, gas.context) issue, err := rule.Match(n, gosec.context)
if err != nil { if err != nil {
file, line := GetLocation(n, gas.context) file, line := GetLocation(n, gosec.context)
file = path.Base(file) 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 { if issue != nil {
gas.issues = append(gas.issues, issue) gosec.issues = append(gosec.issues, issue)
gas.stats.NumFound++ gosec.stats.NumFound++
} }
} }
return gas return gosec
} }
// Report returns the current issues discovered and the metrics about the scan // Report returns the current issues discovered and the metrics about the scan
func (gas *Analyzer) Report() ([]*Issue, *Metrics) { func (gosec *Analyzer) Report() ([]*Issue, *Metrics) {
return gas.issues, gas.stats return gosec.issues, gosec.stats
} }
// Reset clears state such as context, issues and metrics from the configured analyzer // Reset clears state such as context, issues and metrics from the configured analyzer
func (gas *Analyzer) Reset() { func (gosec *Analyzer) Reset() {
gas.context = &Context{} gosec.context = &Context{}
gas.issues = make([]*Issue, 0, 16) gosec.issues = make([]*Issue, 0, 16)
gas.stats = &Metrics{} gosec.stats = &Metrics{}
} }

View file

@ -1,4 +1,4 @@
package gas_test package gosec_test
import ( import (
"io/ioutil" "io/ioutil"
@ -6,24 +6,24 @@ import (
"os" "os"
"strings" "strings"
"github.com/GoASTScanner/gas" "github.com/securego/gosec"
"github.com/GoASTScanner/gas/rules" "github.com/securego/gosec/rules"
"github.com/GoASTScanner/gas/testutils"
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo"
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
"github.com/securego/gosec/testutils"
) )
var _ = Describe("Analyzer", func() { var _ = Describe("Analyzer", func() {
var ( var (
analyzer *gas.Analyzer analyzer *gosec.Analyzer
logger *log.Logger logger *log.Logger
buildTags []string buildTags []string
) )
BeforeEach(func() { BeforeEach(func() {
logger, _ = testutils.NewLogger() logger, _ = testutils.NewLogger()
analyzer = gas.NewAnalyzer(nil, logger) analyzer = gosec.NewAnalyzer(nil, logger)
}) })
Context("when processing a package", func() { Context("when processing a package", func() {
@ -200,9 +200,9 @@ var _ = Describe("Analyzer", func() {
source := sample.Code source := sample.Code
// overwrite nosec option // overwrite nosec option
nosecIgnoreConfig := gas.NewConfig() nosecIgnoreConfig := gosec.NewConfig()
nosecIgnoreConfig.SetGlobal("nosec", "true") nosecIgnoreConfig.SetGlobal("nosec", "true")
customAnalyzer := gas.NewAnalyzer(nosecIgnoreConfig, logger) customAnalyzer := gosec.NewAnalyzer(nosecIgnoreConfig, logger)
customAnalyzer.LoadRules(rules.Generate(rules.NewRuleFilter(false, "G401")).Builders()) customAnalyzer.LoadRules(rules.Generate(rules.NewRuleFilter(false, "G401")).Builders())
nosecPackage := testutils.NewTestPackage() nosecPackage := testutils.NewTestPackage()

View file

@ -11,7 +11,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package gas package gosec
import ( import (
"go/ast" "go/ast"

View file

@ -1,20 +1,20 @@
package gas_test package gosec_test
import ( import (
"go/ast" "go/ast"
"github.com/GoASTScanner/gas"
"github.com/GoASTScanner/gas/testutils"
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo"
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
"github.com/securego/gosec"
"github.com/securego/gosec/testutils"
) )
var _ = Describe("call list", func() { var _ = Describe("call list", func() {
var ( var (
calls gas.CallList calls gosec.CallList
) )
BeforeEach(func() { BeforeEach(func() {
calls = gas.NewCallList() calls = gosec.NewCallList()
}) })
It("should not return any matches when empty", func() { It("should not return any matches when empty", func() {
@ -72,7 +72,7 @@ var _ = Describe("call list", func() {
matched := 0 matched := 0
v := testutils.NewMockVisitor() v := testutils.NewMockVisitor()
v.Context = ctx 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 { if _, ok := n.(*ast.CallExpr); ok && calls.ContainsCallExpr(n, ctx) != nil {
matched++ matched++
} }

View file

@ -20,24 +20,22 @@ import (
"io/ioutil" "io/ioutil"
"log" "log"
"os" "os"
"os/user"
"path/filepath" "path/filepath"
"regexp" "regexp"
"runtime"
"sort" "sort"
"strings" "strings"
"github.com/GoASTScanner/gas"
"github.com/GoASTScanner/gas/output"
"github.com/GoASTScanner/gas/rules"
"github.com/kisielk/gotool" "github.com/kisielk/gotool"
"github.com/securego/gosec"
"github.com/securego/gosec/output"
"github.com/securego/gosec/rules"
) )
const ( const (
usageText = ` 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. can lead to security problems.
VERSION: %s VERSION: %s
@ -47,17 +45,17 @@ BUILD DATE: %s
USAGE: USAGE:
# Check a single package # 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 # Check all packages under the current directory and save results in
# json format. # 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): # 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 # 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 +117,8 @@ func usage() {
fmt.Fprint(os.Stderr, "\n") fmt.Fprint(os.Stderr, "\n")
} }
func loadConfig(configFile string) (gas.Config, error) { func loadConfig(configFile string) (gosec.Config, error) {
config := gas.NewConfig() config := gosec.NewConfig()
if configFile != "" { if configFile != "" {
// #nosec // #nosec
file, err := os.Open(configFile) file, err := os.Open(configFile)
@ -158,7 +156,7 @@ func loadRules(include, exclude string) rules.RuleList {
return rules.Generate(filters...) 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 != "" { if filename != "" {
outfile, err := os.Create(filename) outfile, err := os.Create(filename)
if err != nil { if err != nil {
@ -178,36 +176,13 @@ func saveOutput(filename, format string, issues []*gas.Issue, metrics *gas.Metri
return nil return nil
} }
func getenv(key, userDefault string) string { func cleanPath(path string) (string, error) {
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) {
cleanFailed := fmt.Errorf("%s is not within the $GOPATH and cannot be processed", path) cleanFailed := fmt.Errorf("%s is not within the $GOPATH and cannot be processed", path)
nonRecursivePath := strings.TrimSuffix(path, "/...") nonRecursivePath := strings.TrimSuffix(path, "/...")
// do not attempt to clean directs that are resolvable on gopath // do not attempt to clean directs that are resolvable on gopath
if _, err := os.Stat(nonRecursivePath); err != nil && os.IsNotExist(err) { 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) 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) dir := filepath.Join(basedir, "src", nonRecursivePath)
if st, err := os.Stat(dir); err == nil && st.IsDir() { if st, err := os.Stat(dir); err == nil && st.IsDir() {
log.Printf("located %s in %s", path, dir) 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 // ensure we resolve package directory correctly based on $GOPATH
abspath, err := filepath.Abs(path) pkgPath, err := gosec.GetPkgRelativePath(path)
if err != nil { if err != nil {
abspath = path
}
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 "", cleanFailed
}
return pkgPath, nil
} }
func cleanPaths(paths []string) []string { func cleanPaths(paths []string) []string {
gopaths := gopath()
var clean []string var clean []string
for _, path := range paths { for _, path := range paths {
cleaned, err := cleanPath(path, gopaths) cleaned, err := cleanPath(path)
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
@ -283,7 +251,7 @@ func main() {
if *flagQuiet { if *flagQuiet {
logger = log.New(ioutil.Discard, "", 0) logger = log.New(ioutil.Discard, "", 0)
} else { } else {
logger = log.New(logWriter, "[gas] ", log.LstdFlags) logger = log.New(logWriter, "[gosec] ", log.LstdFlags)
} }
// Load config // Load config
@ -299,14 +267,14 @@ func main() {
} }
// Create the analyzer // Create the analyzer
analyzer := gas.NewAnalyzer(config, logger) analyzer := gosec.NewAnalyzer(config, logger)
analyzer.LoadRules(ruleDefinitions.Builders()) analyzer.LoadRules(ruleDefinitions.Builders())
vendor := regexp.MustCompile(`[\\/]vendor([\\/]|$)`) vendor := regexp.MustCompile(`[\\/]vendor([\\/]|$)`)
var packages []string var packages []string
// Iterate over packages on the import paths // Iterate over packages on the import paths
gopaths := gopath() gopaths := gosec.Gopath()
for _, pkg := range gotool.ImportPaths(cleanPaths(flag.Args())) { for _, pkg := range gotool.ImportPaths(cleanPaths(flag.Args())) {
// Skip vendor directory // Skip vendor directory

View file

@ -3,10 +3,10 @@ package main
import ( import (
"sort" "sort"
"github.com/GoASTScanner/gas" "github.com/securego/gosec"
) )
type sortBySeverity []*gas.Issue type sortBySeverity []*gosec.Issue
func (s sortBySeverity) Len() int { return len(s) } 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] } func (s sortBySeverity) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
// sortIssues sorts the issues by severity in descending order // sortIssues sorts the issues by severity in descending order
func sortIssues(issues []*gas.Issue) { func sortIssues(issues []*gosec.Issue) {
sort.Sort(sortBySeverity(issues)) sort.Sort(sortBySeverity(issues))
} }

View file

@ -8,6 +8,6 @@ package {{.}}
import ( import (
"go/ast" "go/ast"
"github.com/GoASTScanner/gas" "github.com/securego/gosec"
) )
`)) `))

View file

@ -5,9 +5,9 @@ import "text/template"
var generatedRuleTmpl = template.Must(template.New("generated").Parse(` var generatedRuleTmpl = template.Must(template.New("generated").Parse(`
// New{{.Name}}TLSCheck creates a check for {{.Name}} TLS ciphers // New{{.Name}}TLSCheck creates a check for {{.Name}} TLS ciphers
// DO NOT EDIT - generated by tlsconfig tool // 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{ return &insecureConfigTLS{
MetaData: gas.MetaData{ID: id}, MetaData: gosec.MetaData{ID: id},
requiredType: "crypto/tls.Config", requiredType: "crypto/tls.Config",
MinVersion: {{ .MinVersion }}, MinVersion: {{ .MinVersion }},
MaxVersion: {{ .MaxVersion }}, MaxVersion: {{ .MaxVersion }},

View file

@ -1,4 +1,4 @@
package gas package gosec
import ( import (
"bytes" "bytes"
@ -10,7 +10,7 @@ import (
const ( const (
// Globals are applicable to all rules and used for general // Globals are applicable to all rules and used for general
// configuration settings for gas. // configuration settings for gosec.
Globals = "global" Globals = "global"
) )

View file

@ -1,17 +1,17 @@
package gas_test package gosec_test
import ( import (
"bytes" "bytes"
"github.com/GoASTScanner/gas"
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo"
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
"github.com/securego/gosec"
) )
var _ = Describe("Configuration", func() { var _ = Describe("Configuration", func() {
var configuration gas.Config var configuration gosec.Config
BeforeEach(func() { BeforeEach(func() {
configuration = gas.NewConfig() configuration = gosec.NewConfig()
}) })
Context("when loading from disk", func() { Context("when loading from disk", func() {

View file

@ -1,4 +1,4 @@
package gas_test package gosec_test
import ( import (
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo"
@ -7,7 +7,7 @@ import (
"testing" "testing"
) )
func TestGas(t *testing.T) { func TestGosec(t *testing.T) {
RegisterFailHandler(Fail) RegisterFailHandler(Fail)
RunSpecs(t, "Gas Suite") RunSpecs(t, "gosec Suite")
} }

View file

@ -12,14 +12,20 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package gas package gosec
import ( import (
"errors"
"fmt" "fmt"
"go/ast" "go/ast"
"go/token" "go/token"
"go/types" "go/types"
"os"
"os/user"
"path/filepath"
"runtime"
"strconv" "strconv"
"strings"
) )
// MatchCallByPackage ensures that the specified package is imported, // 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()) fobj := ctx.FileSet.File(n.Pos())
return fobj.Name(), fobj.Line(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
}

View file

@ -1,4 +1,4 @@
package gas_test package gosec_test
import ( import (
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo"

View file

@ -10,7 +10,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package gas package gosec
import ( import (
"go/ast" "go/ast"

View file

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package gas package gosec
import ( import (
"encoding/json" "encoding/json"
@ -34,7 +34,7 @@ const (
High 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 { type Issue struct {
Severity Score `json:"severity"` // issue severity (how problematic it is) Severity Score `json:"severity"` // issue severity (how problematic it is)
Confidence Score `json:"confidence"` // issue confidence (how sure we are we found it) 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 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. // will be passed tbhrough to reported issues.
type MetaData struct { type MetaData struct {
ID string ID string

View file

@ -1,13 +1,13 @@
package gas_test package gosec_test
import ( import (
"go/ast" "go/ast"
"github.com/GoASTScanner/gas"
"github.com/GoASTScanner/gas/rules"
"github.com/GoASTScanner/gas/testutils"
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo"
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
"github.com/securego/gosec"
"github.com/securego/gosec/rules"
"github.com/securego/gosec/testutils"
) )
var _ = Describe("Issue", func() { var _ = Describe("Issue", func() {
@ -26,7 +26,7 @@ var _ = Describe("Issue", func() {
pkg.AddFile("foo.go", source) pkg.AddFile("foo.go", source)
ctx := pkg.CreateContext("foo.go") ctx := pkg.CreateContext("foo.go")
v := testutils.NewMockVisitor() 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 { if node, ok := n.(*ast.BasicLit); ok {
target = node target = node
return false return false
@ -37,7 +37,7 @@ var _ = Describe("Issue", func() {
ast.Walk(v, ctx.Root) ast.Walk(v, ctx.Root)
Expect(target).ShouldNot(BeNil()) 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).ShouldNot(BeNil())
Expect(issue.Code).Should(MatchRegexp(`"bar"`)) Expect(issue.Code).Should(MatchRegexp(`"bar"`))
Expect(issue.Line).Should(Equal("2")) Expect(issue.Line).Should(Equal("2"))
@ -58,7 +58,7 @@ var _ = Describe("Issue", func() {
source := `package main source := `package main
import "os" import "os"
func main(){` 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)}` source += `println(q)}`
pkg := testutils.NewTestPackage() pkg := testutils.NewTestPackage()
@ -66,7 +66,7 @@ var _ = Describe("Issue", func() {
pkg.AddFile("foo.go", source) pkg.AddFile("foo.go", source)
ctx := pkg.CreateContext("foo.go") ctx := pkg.CreateContext("foo.go")
v := testutils.NewMockVisitor() 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 { if node, ok := n.(*ast.BinaryExpr); ok {
target = node target = node
} }
@ -77,7 +77,7 @@ var _ = Describe("Issue", func() {
Expect(target).ShouldNot(BeNil()) Expect(target).ShouldNot(BeNil())
// Use SQL rule to check binary expr // Use SQL rule to check binary expr
cfg := gas.NewConfig() cfg := gosec.NewConfig()
rule, _ := rules.NewSQLStrConcat("TEST", cfg) rule, _ := rules.NewSQLStrConcat("TEST", cfg)
issue, err := rule.Match(target, ctx) issue, err := rule.Match(target, ctx)
Expect(err).ShouldNot(HaveOccurred()) Expect(err).ShouldNot(HaveOccurred())

View file

@ -22,7 +22,7 @@ import (
"io" "io"
plainTemplate "text/template" plainTemplate "text/template"
"github.com/GoASTScanner/gas" "github.com/securego/gosec"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
) )
@ -58,13 +58,13 @@ Summary:
` `
type reportInfo struct { type reportInfo struct {
Issues []*gas.Issue Issues []*gosec.Issue
Stats *gas.Metrics Stats *gosec.Metrics
} }
// CreateReport generates a report based for the supplied issues and metrics given // 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. // 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{ data := &reportInfo{
Issues: issues, Issues: issues,
Stats: metrics, Stats: metrics,
@ -150,7 +150,7 @@ func reportJUnitXML(w io.Writer, data *reportInfo) error {
} }
func reportFromPlaintextTemplate(w io.Writer, reportTemplate string, 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 { if e != nil {
return e 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 { 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 { if e != nil {
return e return e
} }

View file

@ -5,7 +5,7 @@ import (
htmlLib "html" htmlLib "html"
"strconv" "strconv"
"github.com/GoASTScanner/gas" "github.com/securego/gosec"
) )
type junitXMLReport struct { type junitXMLReport struct {
@ -32,26 +32,26 @@ type failure struct {
Text string `xml:",innerxml"` Text string `xml:",innerxml"`
} }
func generatePlaintext(issue *gas.Issue) string { func generatePlaintext(issue *gosec.Issue) string {
return "Results:\n" + return "Results:\n" +
"[" + issue.File + ":" + issue.Line + "] - " + "[" + issue.File + ":" + issue.Line + "] - " +
issue.What + " (Confidence: " + strconv.Itoa(int(issue.Confidence)) + issue.What + " (Confidence: " + strconv.Itoa(int(issue.Confidence)) +
", Severity: " + strconv.Itoa(int(issue.Severity)) + ")\n" + "> " + htmlLib.EscapeString(issue.Code) ", Severity: " + strconv.Itoa(int(issue.Severity)) + ")\n" + "> " + htmlLib.EscapeString(issue.Code)
} }
func groupDataByRules(data *reportInfo) map[string][]*gas.Issue { func groupDataByRules(data *reportInfo) map[string][]*gosec.Issue {
groupedData := make(map[string][]*gas.Issue) groupedData := make(map[string][]*gosec.Issue)
for _, issue := range data.Issues { for _, issue := range data.Issues {
if _, ok := groupedData[issue.What]; ok { if _, ok := groupedData[issue.What]; ok {
groupedData[issue.What] = append(groupedData[issue.What], issue) groupedData[issue.What] = append(groupedData[issue.What], issue)
} else { } else {
groupedData[issue.What] = []*gas.Issue{issue} groupedData[issue.What] = []*gosec.Issue{issue}
} }
} }
return groupedData return groupedData
} }
func createJUnitXMLStruct(groupedData map[string][]*gas.Issue) junitXMLReport { func createJUnitXMLStruct(groupedData map[string][]*gosec.Issue) junitXMLReport {
var xmlReport junitXMLReport var xmlReport junitXMLReport
for what, issues := range groupedData { for what, issues := range groupedData {
testsuite := testsuite{ testsuite := testsuite{

View file

@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package gas package gosec
import "go/ast" import "go/ast"

View file

@ -1,12 +1,12 @@
package gas_test package gosec_test
import ( import (
"go/ast" "go/ast"
"github.com/GoASTScanner/gas"
"github.com/GoASTScanner/gas/testutils"
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo"
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
"github.com/securego/gosec"
"github.com/securego/gosec/testutils"
) )
var _ = Describe("Resolve ast node to concrete value", func() { 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(){}`) pkg.AddFile("foo.go", `package main; const foo = "bar"; func main(){}`)
ctx := pkg.CreateContext("foo.go") ctx := pkg.CreateContext("foo.go")
v := testutils.NewMockVisitor() 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 { if node, ok := n.(*ast.BasicLit); ok {
basicLiteral = node basicLiteral = node
return false return false
@ -29,7 +29,7 @@ var _ = Describe("Resolve ast node to concrete value", func() {
v.Context = ctx v.Context = ctx
ast.Walk(v, ctx.Root) ast.Walk(v, ctx.Root)
Expect(basicLiteral).ShouldNot(BeNil()) Expect(basicLiteral).ShouldNot(BeNil())
Expect(gas.TryResolve(basicLiteral, ctx)).Should(BeTrue()) Expect(gosec.TryResolve(basicLiteral, ctx)).Should(BeTrue())
}) })
It("should successfully resolve identifier", func() { 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(){}`) pkg.AddFile("foo.go", `package main; var foo string = "bar"; func main(){}`)
ctx := pkg.CreateContext("foo.go") ctx := pkg.CreateContext("foo.go")
v := testutils.NewMockVisitor() 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 { if node, ok := n.(*ast.Ident); ok {
ident = node ident = node
return false return false
@ -49,7 +49,7 @@ var _ = Describe("Resolve ast node to concrete value", func() {
v.Context = ctx v.Context = ctx
ast.Walk(v, ctx.Root) ast.Walk(v, ctx.Root)
Expect(ident).ShouldNot(BeNil()) 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() { 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) }`) pkg.AddFile("foo.go", `package main; const x = "bar"; func main(){ y := x; println(y) }`)
ctx := pkg.CreateContext("foo.go") ctx := pkg.CreateContext("foo.go")
v := testutils.NewMockVisitor() 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 node, ok := n.(*ast.AssignStmt); ok {
if id, ok := node.Lhs[0].(*ast.Ident); ok && id.Name == "y" { if id, ok := node.Lhs[0].(*ast.Ident); ok && id.Name == "y" {
assign = node assign = node
@ -70,7 +70,7 @@ var _ = Describe("Resolve ast node to concrete value", func() {
v.Context = ctx v.Context = ctx
ast.Walk(v, ctx.Root) ast.Walk(v, ctx.Root)
Expect(assign).ShouldNot(BeNil()) 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() { 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) }`) pkg.AddFile("foo.go", `package main; const (x = "bar"; y = "baz"); func main(){ z := x + y; println(z) }`)
ctx := pkg.CreateContext("foo.go") ctx := pkg.CreateContext("foo.go")
v := testutils.NewMockVisitor() 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 { if node, ok := n.(*ast.BinaryExpr); ok {
target = node target = node
} }
@ -89,7 +89,7 @@ var _ = Describe("Resolve ast node to concrete value", func() {
v.Context = ctx v.Context = ctx
ast.Walk(v, ctx.Root) ast.Walk(v, ctx.Root)
Expect(target).ShouldNot(BeNil()) Expect(target).ShouldNot(BeNil())
Expect(gas.TryResolve(target, ctx)).Should(BeTrue()) Expect(gosec.TryResolve(target, ctx)).Should(BeTrue())
}) })
// TODO: It should resolve call expressions // TODO: It should resolve call expressions

View file

@ -10,14 +10,14 @@
// See the License for the specific language governing permissions and // See the License for the specific language governing permissions and
// limitations under the License. // limitations under the License.
package gas package gosec
import ( import (
"go/ast" "go/ast"
"reflect" "reflect"
) )
// The Rule interface used by all rules supported by GAS. // The Rule interface used by all rules supported by gosec.
type Rule interface { type Rule interface {
ID() string ID() string
Match(ast.Node, *Context) (*Issue, error) Match(ast.Node, *Context) (*Issue, error)

View file

@ -1,25 +1,25 @@
package gas_test package gosec_test
import ( import (
"fmt" "fmt"
"go/ast" "go/ast"
"github.com/GoASTScanner/gas"
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo"
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
"github.com/securego/gosec"
) )
type mockrule struct { type mockrule struct {
issue *gas.Issue issue *gosec.Issue
err error 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 { func (m *mockrule) ID() string {
return "MOCK" 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) { if m.callback(n, ctx) {
return m.issue, nil return m.issue, nil
} }
@ -31,29 +31,29 @@ var _ = Describe("Rule", func() {
Context("when using a ruleset", func() { Context("when using a ruleset", func() {
var ( var (
ruleset gas.RuleSet ruleset gosec.RuleSet
dummyErrorRule gas.Rule dummyErrorRule gosec.Rule
dummyIssueRule gas.Rule dummyIssueRule gosec.Rule
) )
JustBeforeEach(func() { JustBeforeEach(func() {
ruleset = gas.NewRuleSet() ruleset = gosec.NewRuleSet()
dummyErrorRule = &mockrule{ dummyErrorRule = &mockrule{
issue: nil, issue: nil,
err: fmt.Errorf("An unexpected error occurred"), 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{ dummyIssueRule = &mockrule{
issue: &gas.Issue{ issue: &gosec.Issue{
Severity: gas.High, Severity: gosec.High,
Confidence: gas.High, Confidence: gosec.High,
What: `Some explanation of the thing`, What: `Some explanation of the thing`,
File: "main.go", File: "main.go",
Code: `#include <stdio.h> int main(){ puts("hello world"); }`, Code: `#include <stdio.h> int main(){ puts("hello world"); }`,
Line: "42", Line: "42",
}, },
err: nil, 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() { It("should be possible to register a rule for multiple ast.Node", func() {

60
rules/archive.go Normal file
View file

@ -0,0 +1,60 @@
package rules
import (
"go/ast"
"go/types"
"github.com/securego/gosec"
)
type archive struct {
gosec.MetaData
calls gosec.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 *gosec.Context) (*gosec.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 gosec.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 gosec.Config) (gosec.Rule, []ast.Node) {
calls := gosec.NewCallList()
calls.Add("path/filepath", "Join")
return &archive{
calls: calls,
argType: "*archive/zip.File",
MetaData: gosec.MetaData{
ID: id,
Severity: gosec.Medium,
Confidence: gosec.High,
What: "File traversal when extracting zip archive",
},
}, []ast.Node{(*ast.CallExpr)(nil)}
}

View file

@ -17,11 +17,11 @@ package rules
import ( import (
"go/ast" "go/ast"
"github.com/GoASTScanner/gas" "github.com/securego/gosec"
) )
type usingBigExp struct { type usingBigExp struct {
gas.MetaData gosec.MetaData
pkg string pkg string
calls []string calls []string
} }
@ -30,23 +30,23 @@ func (r *usingBigExp) ID() string {
return r.MetaData.ID return r.MetaData.ID
} }
func (r *usingBigExp) Match(n ast.Node, c *gas.Context) (gi *gas.Issue, err error) { func (r *usingBigExp) Match(n ast.Node, c *gosec.Context) (gi *gosec.Issue, err error) {
if _, matched := gas.MatchCallByType(n, c, r.pkg, r.calls...); matched { if _, matched := gosec.MatchCallByType(n, c, r.pkg, r.calls...); matched {
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 return nil, nil
} }
// NewUsingBigExp detects issues with modulus == 0 for Bignum // 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{ return &usingBigExp{
pkg: "*math/big.Int", pkg: "*math/big.Int",
calls: []string{"Exp"}, calls: []string{"Exp"},
MetaData: gas.MetaData{ MetaData: gosec.MetaData{
ID: id, ID: id,
What: "Use of math/big.Int.Exp function should be audited for modulus == 0", What: "Use of math/big.Int.Exp function should be audited for modulus == 0",
Severity: gas.Low, Severity: gosec.Low,
Confidence: gas.High, Confidence: gosec.High,
}, },
}, []ast.Node{(*ast.CallExpr)(nil)} }, []ast.Node{(*ast.CallExpr)(nil)}
} }

View file

@ -18,13 +18,13 @@ import (
"go/ast" "go/ast"
"regexp" "regexp"
"github.com/GoASTScanner/gas" "github.com/securego/gosec"
) )
// Looks for net.Listen("0.0.0.0") or net.Listen(":8080") // Looks for net.Listen("0.0.0.0") or net.Listen(":8080")
type bindsToAllNetworkInterfaces struct { type bindsToAllNetworkInterfaces struct {
gas.MetaData gosec.MetaData
calls gas.CallList calls gosec.CallList
pattern *regexp.Regexp pattern *regexp.Regexp
} }
@ -32,14 +32,14 @@ func (r *bindsToAllNetworkInterfaces) ID() string {
return r.MetaData.ID 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) callExpr := r.calls.ContainsCallExpr(n, c)
if callExpr == nil { if callExpr == nil {
return nil, 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) { 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 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 // NewBindsToAllNetworkInterfaces detects socket connections that are setup to
// listen on all network interfaces. // listen on all network interfaces.
func NewBindsToAllNetworkInterfaces(id string, conf gas.Config) (gas.Rule, []ast.Node) { func NewBindsToAllNetworkInterfaces(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
calls := gas.NewCallList() calls := gosec.NewCallList()
calls.Add("net", "Listen") calls.Add("net", "Listen")
calls.Add("crypto/tls", "Listen") calls.Add("crypto/tls", "Listen")
return &bindsToAllNetworkInterfaces{ return &bindsToAllNetworkInterfaces{
calls: calls, calls: calls,
pattern: regexp.MustCompile(`^(0.0.0.0|:).*$`), pattern: regexp.MustCompile(`^(0.0.0.0|:).*$`),
MetaData: gas.MetaData{ MetaData: gosec.MetaData{
ID: id, ID: id,
Severity: gas.Medium, Severity: gosec.Medium,
Confidence: gas.High, Confidence: gosec.High,
What: "Binds to all network interfaces", What: "Binds to all network interfaces",
}, },
}, []ast.Node{(*ast.CallExpr)(nil)} }, []ast.Node{(*ast.CallExpr)(nil)}

View file

@ -18,11 +18,11 @@ import (
"go/ast" "go/ast"
"strings" "strings"
"github.com/GoASTScanner/gas" "github.com/securego/gosec"
) )
type blacklistedImport struct { type blacklistedImport struct {
gas.MetaData gosec.MetaData
Blacklisted map[string]string Blacklisted map[string]string
} }
@ -36,10 +36,10 @@ func (r *blacklistedImport) ID() string {
return r.MetaData.ID 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 node, ok := n.(*ast.ImportSpec); ok {
if description, ok := r.Blacklisted[unquote(node.Path.Value)]; 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 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. // NewBlacklistedImports reports when a blacklisted import is being used.
// Typically when a deprecated technology 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{ return &blacklistedImport{
MetaData: gas.MetaData{ MetaData: gosec.MetaData{
ID: id, ID: id,
Severity: gas.Medium, Severity: gosec.Medium,
Confidence: gas.High, Confidence: gosec.High,
}, },
Blacklisted: blacklist, Blacklisted: blacklist,
}, []ast.Node{(*ast.ImportSpec)(nil)} }, []ast.Node{(*ast.ImportSpec)(nil)}
} }
// NewBlacklistedImportMD5 fails if MD5 is imported // 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{ return NewBlacklistedImports(id, conf, map[string]string{
"crypto/md5": "Blacklisted import crypto/md5: weak cryptographic primitive", "crypto/md5": "Blacklisted import crypto/md5: weak cryptographic primitive",
}) })
} }
// NewBlacklistedImportDES fails if DES is imported // 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{ return NewBlacklistedImports(id, conf, map[string]string{
"crypto/des": "Blacklisted import crypto/des: weak cryptographic primitive", "crypto/des": "Blacklisted import crypto/des: weak cryptographic primitive",
}) })
} }
// NewBlacklistedImportRC4 fails if DES is imported // 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{ return NewBlacklistedImports(id, conf, map[string]string{
"crypto/rc4": "Blacklisted import crypto/rc4: weak cryptographic primitive", "crypto/rc4": "Blacklisted import crypto/rc4: weak cryptographic primitive",
}) })
} }
// NewBlacklistedImportCGI fails if CGI is imported // 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{ 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)", "net/http/cgi": "Blacklisted import net/http/cgi: Go versions < 1.6.3 are vulnerable to Httpoxy attack: (CVE-2016-5386)",
}) })

View file

@ -18,19 +18,19 @@ import (
"go/ast" "go/ast"
"go/types" "go/types"
"github.com/GoASTScanner/gas" "github.com/securego/gosec"
) )
type noErrorCheck struct { type noErrorCheck struct {
gas.MetaData gosec.MetaData
whitelist gas.CallList whitelist gosec.CallList
} }
func (r *noErrorCheck) ID() string { func (r *noErrorCheck) ID() string {
return r.MetaData.ID 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 { if tv := ctx.Info.TypeOf(callExpr); tv != nil {
switch t := tv.(type) { switch t := tv.(type) {
case *types.Tuple: case *types.Tuple:
@ -49,7 +49,7 @@ func returnsError(callExpr *ast.CallExpr, ctx *gas.Context) int {
return -1 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) { switch stmt := n.(type) {
case *ast.AssignStmt: case *ast.AssignStmt:
for _, expr := range stmt.Rhs { 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 return nil, nil
} }
if id, ok := stmt.Lhs[pos].(*ast.Ident); ok && id.Name == "_" { 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 { if callExpr, ok := stmt.X.(*ast.CallExpr); ok && r.whitelist.ContainsCallExpr(stmt.X, ctx) == nil {
pos := returnsError(callExpr, ctx) pos := returnsError(callExpr, ctx)
if pos >= 0 { 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 // 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 // TODO(gm) Come up with sensible defaults here. Or flip it to use a
// black list instead. // black list instead.
whitelist := gas.NewCallList() whitelist := gosec.NewCallList()
whitelist.AddAll("bytes.Buffer", "Write", "WriteByte", "WriteRune", "WriteString") whitelist.AddAll("bytes.Buffer", "Write", "WriteByte", "WriteRune", "WriteString")
whitelist.AddAll("fmt", "Print", "Printf", "Println", "Fprint", "Fprintf", "Fprintln") whitelist.AddAll("fmt", "Print", "Printf", "Println", "Fprint", "Fprintf", "Fprintln")
whitelist.Add("io.PipeWriter", "CloseWithError") whitelist.Add("io.PipeWriter", "CloseWithError")
@ -91,10 +91,10 @@ func NewNoErrorCheck(id string, conf gas.Config) (gas.Rule, []ast.Node) {
} }
} }
return &noErrorCheck{ return &noErrorCheck{
MetaData: gas.MetaData{ MetaData: gosec.MetaData{
ID: id, ID: id,
Severity: gas.Low, Severity: gosec.Low,
Confidence: gas.High, Confidence: gosec.High,
What: "Errors unhandled.", What: "Errors unhandled.",
}, },
whitelist: whitelist, whitelist: whitelist,

View file

@ -19,11 +19,11 @@ import (
"go/ast" "go/ast"
"strconv" "strconv"
"github.com/GoASTScanner/gas" "github.com/securego/gosec"
) )
type filePermissions struct { type filePermissions struct {
gas.MetaData gosec.MetaData
mode int64 mode int64
pkg string pkg string
calls []string calls []string
@ -50,11 +50,11 @@ func getConfiguredMode(conf map[string]interface{}, configKey string, defaultMod
return mode return mode
} }
func (r *filePermissions) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) { func (r *filePermissions) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) {
if callexpr, matched := gas.MatchCallByPackage(n, c, r.pkg, r.calls...); matched { if callexpr, matched := gosec.MatchCallByPackage(n, c, r.pkg, r.calls...); matched {
modeArg := callexpr.Args[len(callexpr.Args)-1] modeArg := callexpr.Args[len(callexpr.Args)-1]
if mode, err := gas.GetInt(modeArg); err == nil && mode > r.mode { if mode, err := gosec.GetInt(modeArg); err == nil && mode > r.mode {
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 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 // NewFilePerms creates a rule to detect file creation with a more permissive than configured
// permission mask. // 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) mode := getConfiguredMode(conf, "G302", 0600)
return &filePermissions{ return &filePermissions{
mode: mode, mode: mode,
pkg: "os", pkg: "os",
calls: []string{"OpenFile", "Chmod"}, calls: []string{"OpenFile", "Chmod"},
MetaData: gas.MetaData{ MetaData: gosec.MetaData{
ID: id, ID: id,
Severity: gas.Medium, Severity: gosec.Medium,
Confidence: gas.High, Confidence: gosec.High,
What: fmt.Sprintf("Expect file permissions to be %#o or less", mode), What: fmt.Sprintf("Expect file permissions to be %#o or less", mode),
}, },
}, []ast.Node{(*ast.CallExpr)(nil)} }, []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 // NewMkdirPerms creates a rule to detect directory creation with more permissive than
// configured permission mask. // 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) mode := getConfiguredMode(conf, "G301", 0750)
return &filePermissions{ return &filePermissions{
mode: mode, mode: mode,
pkg: "os", pkg: "os",
calls: []string{"Mkdir", "MkdirAll"}, calls: []string{"Mkdir", "MkdirAll"},
MetaData: gas.MetaData{ MetaData: gosec.MetaData{
ID: id, ID: id,
Severity: gas.Medium, Severity: gosec.Medium,
Confidence: gas.High, Confidence: gosec.High,
What: fmt.Sprintf("Expect directory permissions to be %#o or less", mode), What: fmt.Sprintf("Expect directory permissions to be %#o or less", mode),
}, },
}, []ast.Node{(*ast.CallExpr)(nil)} }, []ast.Node{(*ast.CallExpr)(nil)}

View file

@ -19,12 +19,12 @@ import (
"regexp" "regexp"
"strconv" "strconv"
"github.com/GoASTScanner/gas" zxcvbn "github.com/nbutton23/zxcvbn-go"
"github.com/nbutton23/zxcvbn-go" "github.com/securego/gosec"
) )
type credentials struct { type credentials struct {
gas.MetaData gosec.MetaData
pattern *regexp.Regexp pattern *regexp.Regexp
entropyThreshold float64 entropyThreshold float64
perCharThreshold float64 perCharThreshold float64
@ -52,7 +52,7 @@ func (r *credentials) isHighEntropyString(str string) bool {
entropyPerChar >= r.perCharThreshold)) 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) { switch node := n.(type) {
case *ast.AssignStmt: case *ast.AssignStmt:
return r.matchAssign(node, ctx) 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 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 { for _, i := range assign.Lhs {
if ident, ok := i.(*ast.Ident); ok { if ident, ok := i.(*ast.Ident); ok {
if r.pattern.MatchString(ident.Name) { if r.pattern.MatchString(ident.Name) {
for _, e := range assign.Rhs { 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)) { 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 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 { for index, ident := range valueSpec.Names {
if r.pattern.MatchString(ident.Name) && valueSpec.Values != nil { if r.pattern.MatchString(ident.Name) && valueSpec.Values != nil {
// const foo, bar = "same value" // const foo, bar = "same value"
if len(valueSpec.Values) <= index { if len(valueSpec.Values) <= index {
index = len(valueSpec.Values) - 1 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)) { 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 // NewHardcodedCredentials attempts to find high entropy string constants being
// assigned to variables that appear to be related to credentials. // 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` pattern := `(?i)passwd|pass|password|pwd|secret|token`
entropyThreshold := 80.0 entropyThreshold := 80.0
perCharThreshold := 3.0 perCharThreshold := 3.0
@ -137,11 +137,11 @@ func NewHardcodedCredentials(id string, conf gas.Config) (gas.Rule, []ast.Node)
perCharThreshold: perCharThreshold, perCharThreshold: perCharThreshold,
ignoreEntropy: ignoreEntropy, ignoreEntropy: ignoreEntropy,
truncate: truncateString, truncate: truncateString,
MetaData: gas.MetaData{ MetaData: gosec.MetaData{
ID: id, ID: id,
What: "Potential hardcoded credentials", What: "Potential hardcoded credentials",
Confidence: gas.Low, Confidence: gosec.Low,
Severity: gas.High, Severity: gosec.High,
}, },
}, []ast.Node{(*ast.AssignStmt)(nil), (*ast.ValueSpec)(nil)} }, []ast.Node{(*ast.AssignStmt)(nil), (*ast.ValueSpec)(nil)}
} }

View file

@ -17,11 +17,11 @@ package rules
import ( import (
"go/ast" "go/ast"
"github.com/GoASTScanner/gas" "github.com/securego/gosec"
) )
type weakRand struct { type weakRand struct {
gas.MetaData gosec.MetaData
funcNames []string funcNames []string
packagePath string packagePath string
} }
@ -30,10 +30,10 @@ func (w *weakRand) ID() string {
return w.MetaData.ID 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 { for _, funcName := range w.funcNames {
if _, matched := gas.MatchCallByPackage(n, c, w.packagePath, funcName); matched { if _, matched := gosec.MatchCallByPackage(n, c, w.packagePath, funcName); matched {
return gas.NewIssue(c, n, w.ID(), w.What, w.Severity, w.Confidence), nil 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 // 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{ return &weakRand{
funcNames: []string{"Read", "Int"}, funcNames: []string{"Read", "Int"},
packagePath: "math/rand", packagePath: "math/rand",
MetaData: gas.MetaData{ MetaData: gosec.MetaData{
ID: id, ID: id,
Severity: gas.High, Severity: gosec.High,
Confidence: gas.Medium, Confidence: gosec.Medium,
What: "Use of weak random number generator (math/rand instead of crypto/rand)", What: "Use of weak random number generator (math/rand instead of crypto/rand)",
}, },
}, []ast.Node{(*ast.CallExpr)(nil)} }, []ast.Node{(*ast.CallExpr)(nil)}

View file

@ -18,12 +18,12 @@ import (
"go/ast" "go/ast"
"go/types" "go/types"
"github.com/GoASTScanner/gas" "github.com/securego/gosec"
) )
type readfile struct { type readfile struct {
gas.MetaData gosec.MetaData
gas.CallList gosec.CallList
} }
// ID returns the identifier for this rule // 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` // 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 { if node := r.ContainsCallExpr(n, c); node != nil {
for _, arg := range node.Args { for _, arg := range node.Args {
if ident, ok := arg.(*ast.Ident); ok { if ident, ok := arg.(*ast.Ident); ok {
obj := c.Info.ObjectOf(ident) obj := c.Info.ObjectOf(ident)
if _, ok := obj.(*types.Var); ok && !gas.TryResolve(ident, c) { if _, ok := obj.(*types.Var); ok && !gosec.TryResolve(ident, c) {
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
} }
} }
} }
@ -47,14 +47,14 @@ func (r *readfile) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
} }
// NewReadFile detects cases where we read files // 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{ rule := &readfile{
CallList: gas.NewCallList(), CallList: gosec.NewCallList(),
MetaData: gas.MetaData{ MetaData: gosec.MetaData{
ID: id, ID: id,
What: "Potential file inclusion via variable", What: "Potential file inclusion via variable",
Severity: gas.Medium, Severity: gosec.Medium,
Confidence: gas.High, Confidence: gosec.High,
}, },
} }
rule.Add("io/ioutil", "ReadFile") rule.Add("io/ioutil", "ReadFile")

View file

@ -18,12 +18,12 @@ import (
"fmt" "fmt"
"go/ast" "go/ast"
"github.com/GoASTScanner/gas" "github.com/securego/gosec"
) )
type weakKeyStrength struct { type weakKeyStrength struct {
gas.MetaData gosec.MetaData
calls gas.CallList calls gosec.CallList
bits int bits int
} }
@ -31,27 +31,27 @@ func (w *weakKeyStrength) ID() string {
return w.MetaData.ID 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 callExpr := w.calls.ContainsCallExpr(n, c); callExpr != nil {
if bits, err := gas.GetInt(callExpr.Args[1]); err == nil && bits < (int64)(w.bits) { if bits, err := gosec.GetInt(callExpr.Args[1]); err == nil && bits < (int64)(w.bits) {
return gas.NewIssue(c, n, w.ID(), w.What, w.Severity, w.Confidence), nil return gosec.NewIssue(c, n, w.ID(), w.What, w.Severity, w.Confidence), nil
} }
} }
return nil, nil return nil, nil
} }
// NewWeakKeyStrength builds a rule that detects RSA keys < 2048 bits // NewWeakKeyStrength builds a rule that detects RSA keys < 2048 bits
func NewWeakKeyStrength(id string, conf gas.Config) (gas.Rule, []ast.Node) { func NewWeakKeyStrength(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
calls := gas.NewCallList() calls := gosec.NewCallList()
calls.Add("crypto/rsa", "GenerateKey") calls.Add("crypto/rsa", "GenerateKey")
bits := 2048 bits := 2048
return &weakKeyStrength{ return &weakKeyStrength{
calls: calls, calls: calls,
bits: bits, bits: bits,
MetaData: gas.MetaData{ MetaData: gosec.MetaData{
ID: id, ID: id,
Severity: gas.Medium, Severity: gosec.Medium,
Confidence: gas.High, Confidence: gosec.High,
What: fmt.Sprintf("RSA keys should be at least %d bits", bits), What: fmt.Sprintf("RSA keys should be at least %d bits", bits),
}, },
}, []ast.Node{(*ast.CallExpr)(nil)} }, []ast.Node{(*ast.CallExpr)(nil)}

View file

@ -14,24 +14,22 @@
package rules package rules
import ( import "github.com/securego/gosec"
"github.com/GoASTScanner/gas"
)
// RuleDefinition contains the description of a rule and a mechanism to // RuleDefinition contains the description of a rule and a mechanism to
// create it. // create it.
type RuleDefinition struct { type RuleDefinition struct {
ID string ID string
Description string Description string
Create gas.RuleBuilder Create gosec.RuleBuilder
} }
// RuleList is a mapping of rule ID's to rule definitions // RuleList is a mapping of rule ID's to rule definitions
type RuleList map[string]RuleDefinition type RuleList map[string]RuleDefinition
// Builders returns all the create methods for a given rule list // Builders returns all the create methods for a given rule list
func (rl RuleList) Builders() map[string]gas.RuleBuilder { func (rl RuleList) Builders() map[string]gosec.RuleBuilder {
builders := make(map[string]gas.RuleBuilder) builders := make(map[string]gosec.RuleBuilder)
for _, def := range rl { for _, def := range rl {
builders[def.ID] = def.Create builders[def.ID] = def.Create
} }
@ -79,6 +77,7 @@ func Generate(filters ...RuleFilter) RuleList {
{"G302", "Poor file permisions used when creation file or using chmod", NewFilePerms}, {"G302", "Poor file permisions used when creation file or using chmod", NewFilePerms},
{"G303", "Creating tempfile using a predictable path", NewBadTempFile}, {"G303", "Creating tempfile using a predictable path", NewBadTempFile},
{"G304", "File path provided as taint input", NewReadFile}, {"G304", "File path provided as taint input", NewReadFile},
{"G305", "File path traversal when extracting zip archive", NewArchive},
// crypto // crypto
{"G401", "Detect the usage of DES, RC4, or MD5", NewUsesWeakCryptography}, {"G401", "Detect the usage of DES, RC4, or MD5", NewUsesWeakCryptography},

View file

@ -4,28 +4,27 @@ import (
"fmt" "fmt"
"log" "log"
"github.com/GoASTScanner/gas"
"github.com/GoASTScanner/gas/rules"
"github.com/GoASTScanner/gas/testutils"
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo"
. "github.com/onsi/gomega" . "github.com/onsi/gomega"
"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 ( var (
logger *log.Logger logger *log.Logger
config gas.Config config gosec.Config
analyzer *gas.Analyzer analyzer *gosec.Analyzer
runner func(string, []testutils.CodeSample) runner func(string, []testutils.CodeSample)
buildTags []string buildTags []string
) )
BeforeEach(func() { BeforeEach(func() {
logger, _ = testutils.NewLogger() logger, _ = testutils.NewLogger()
config = gas.NewConfig() config = gosec.NewConfig()
analyzer = gas.NewAnalyzer(config, logger) analyzer = gosec.NewAnalyzer(config, logger)
runner = func(rule string, samples []testutils.CodeSample) { runner = func(rule string, samples []testutils.CodeSample) {
analyzer.LoadRules(rules.Generate(rules.NewRuleFilter(false, rule)).Builders()) analyzer.LoadRules(rules.Generate(rules.NewRuleFilter(false, rule)).Builders())
for n, sample := range samples { for n, sample := range samples {
@ -103,6 +102,10 @@ var _ = Describe("gas rules", func() {
runner("G304", testutils.SampleCodeG304) 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() { It("should detect weak crypto algorithms", func() {
runner("G401", testutils.SampleCodeG401) runner("G401", testutils.SampleCodeG401)
}) })

View file

@ -18,11 +18,11 @@ import (
"go/ast" "go/ast"
"regexp" "regexp"
"github.com/GoASTScanner/gas" "github.com/securego/gosec"
) )
type sqlStatement struct { type sqlStatement struct {
gas.MetaData gosec.MetaData
// Contains a list of patterns which must all match for the rule to match. // Contains a list of patterns which must all match for the rule to match.
patterns []*regexp.Regexp patterns []*regexp.Regexp
@ -59,10 +59,10 @@ func (s *sqlStrConcat) checkObject(n *ast.Ident) bool {
} }
// Look for "SELECT * FROM table WHERE " + " ' OR 1=1" // 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 node, ok := n.(*ast.BinaryExpr); ok {
if start, ok := node.X.(*ast.BasicLit); 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) { if !s.MatchPatterns(str) {
return nil, nil 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) { if second, ok := node.Y.(*ast.Ident); ok && s.checkObject(second) {
return nil, nil 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 // 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{ return &sqlStrConcat{
sqlStatement: sqlStatement{ sqlStatement: sqlStatement{
patterns: []*regexp.Regexp{ patterns: []*regexp.Regexp{
regexp.MustCompile(`(?)(SELECT|DELETE|INSERT|UPDATE|INTO|FROM|WHERE) `), regexp.MustCompile(`(?)(SELECT|DELETE|INSERT|UPDATE|INTO|FROM|WHERE) `),
}, },
MetaData: gas.MetaData{ MetaData: gosec.MetaData{
ID: id, ID: id,
Severity: gas.Medium, Severity: gosec.Medium,
Confidence: gas.High, Confidence: gosec.High,
What: "SQL string concatenation", What: "SQL string concatenation",
}, },
}, },
@ -98,34 +98,34 @@ func NewSQLStrConcat(id string, conf gas.Config) (gas.Rule, []ast.Node) {
type sqlStrFormat struct { type sqlStrFormat struct {
sqlStatement sqlStatement
calls gas.CallList calls gosec.CallList
} }
// Looks for "fmt.Sprintf("SELECT * FROM foo where '%s', userInput)" // 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 // TODO(gm) improve confidence if database/sql is being used
if node := s.calls.ContainsCallExpr(n, c); node != nil { if node := s.calls.ContainsCallExpr(n, c); node != nil {
if arg, e := gas.GetString(node.Args[0]); s.MatchPatterns(arg) && e == nil { if arg, e := gosec.GetString(node.Args[0]); s.MatchPatterns(arg) && e == 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
} }
} }
return nil, nil return nil, nil
} }
// NewSQLStrFormat looks for cases where we're building SQL query strings using format strings // 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{ rule := &sqlStrFormat{
calls: gas.NewCallList(), calls: gosec.NewCallList(),
sqlStatement: sqlStatement{ sqlStatement: sqlStatement{
patterns: []*regexp.Regexp{ patterns: []*regexp.Regexp{
regexp.MustCompile("(?)(SELECT|DELETE|INSERT|UPDATE|INTO|FROM|WHERE) "), regexp.MustCompile("(?)(SELECT|DELETE|INSERT|UPDATE|INTO|FROM|WHERE) "),
regexp.MustCompile("%[^bdoxXfFp]"), regexp.MustCompile("%[^bdoxXfFp]"),
}, },
MetaData: gas.MetaData{ MetaData: gosec.MetaData{
ID: id, ID: id,
Severity: gas.Medium, Severity: gosec.Medium,
Confidence: gas.High, Confidence: gosec.High,
What: "SQL string formatting", What: "SQL string formatting",
}, },
}, },

View file

@ -3,11 +3,11 @@ package rules
import ( import (
"go/ast" "go/ast"
"github.com/GoASTScanner/gas" "github.com/securego/gosec"
) )
type sshHostKey struct { type sshHostKey struct {
gas.MetaData gosec.MetaData
pkg string pkg string
calls []string calls []string
} }
@ -16,23 +16,23 @@ func (r *sshHostKey) ID() string {
return r.MetaData.ID return r.MetaData.ID
} }
func (r *sshHostKey) Match(n ast.Node, c *gas.Context) (gi *gas.Issue, err error) { func (r *sshHostKey) Match(n ast.Node, c *gosec.Context) (gi *gosec.Issue, err error) {
if _, matches := gas.MatchCallByPackage(n, c, r.pkg, r.calls...); matches { if _, matches := gosec.MatchCallByPackage(n, c, r.pkg, r.calls...); matches {
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 return nil, nil
} }
// NewSSHHostKey rule detects the use of insecure ssh HostKeyCallback. // 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{ return &sshHostKey{
pkg: "golang.org/x/crypto/ssh", pkg: "golang.org/x/crypto/ssh",
calls: []string{"InsecureIgnoreHostKey"}, calls: []string{"InsecureIgnoreHostKey"},
MetaData: gas.MetaData{ MetaData: gosec.MetaData{
ID: id, ID: id,
What: "Use of ssh InsecureIgnoreHostKey should be audited", What: "Use of ssh InsecureIgnoreHostKey should be audited",
Severity: gas.Medium, Severity: gosec.Medium,
Confidence: gas.High, Confidence: gosec.High,
}, },
}, []ast.Node{(*ast.CallExpr)(nil)} }, []ast.Node{(*ast.CallExpr)(nil)}
} }

View file

@ -18,12 +18,12 @@ import (
"go/ast" "go/ast"
"go/types" "go/types"
"github.com/GoASTScanner/gas" "github.com/securego/gosec"
) )
type subprocess struct { type subprocess struct {
gas.MetaData gosec.MetaData
gas.CallList gosec.CallList
} }
func (r *subprocess) ID() string { func (r *subprocess) ID() string {
@ -39,24 +39,24 @@ func (r *subprocess) ID() string {
// is unsafe. For example: // is unsafe. For example:
// //
// syscall.Exec("echo", "foobar" + tainted) // 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 { if node := r.ContainsCallExpr(n, c); node != nil {
for _, arg := range node.Args { for _, arg := range node.Args {
if ident, ok := arg.(*ast.Ident); ok { if ident, ok := arg.(*ast.Ident); ok {
obj := c.Info.ObjectOf(ident) obj := c.Info.ObjectOf(ident)
if _, ok := obj.(*types.Var); ok && !gas.TryResolve(ident, c) { if _, ok := obj.(*types.Var); ok && !gosec.TryResolve(ident, c) {
return gas.NewIssue(c, n, r.ID(), "Subprocess launched with variable", gas.Medium, gas.High), nil 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 return nil, nil
} }
// NewSubproc detects cases where we are forking out to an external process // NewSubproc detects cases where we are forking out to an external process
func NewSubproc(id string, conf gas.Config) (gas.Rule, []ast.Node) { func NewSubproc(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
rule := &subprocess{gas.MetaData{ID: id}, gas.NewCallList()} rule := &subprocess{gosec.MetaData{ID: id}, gosec.NewCallList()}
rule.Add("os/exec", "Command") rule.Add("os/exec", "Command")
rule.Add("os/exec", "CommandContext") rule.Add("os/exec", "CommandContext")
rule.Add("syscall", "Exec") rule.Add("syscall", "Exec")

View file

@ -18,12 +18,12 @@ import (
"go/ast" "go/ast"
"regexp" "regexp"
"github.com/GoASTScanner/gas" "github.com/securego/gosec"
) )
type badTempFile struct { type badTempFile struct {
gas.MetaData gosec.MetaData
calls gas.CallList calls gosec.CallList
args *regexp.Regexp args *regexp.Regexp
} }
@ -31,27 +31,27 @@ func (t *badTempFile) ID() string {
return t.MetaData.ID 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 node := t.calls.ContainsCallExpr(n, c); node != nil {
if arg, e := gas.GetString(node.Args[0]); t.args.MatchString(arg) && e == nil { if arg, e := gosec.GetString(node.Args[0]); t.args.MatchString(arg) && e == nil {
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
} }
} }
return nil, nil return nil, nil
} }
// NewBadTempFile detects direct writes to predictable path in temporary directory // NewBadTempFile detects direct writes to predictable path in temporary directory
func NewBadTempFile(id string, conf gas.Config) (gas.Rule, []ast.Node) { func NewBadTempFile(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
calls := gas.NewCallList() calls := gosec.NewCallList()
calls.Add("io/ioutil", "WriteFile") calls.Add("io/ioutil", "WriteFile")
calls.Add("os", "Create") calls.Add("os", "Create")
return &badTempFile{ return &badTempFile{
calls: calls, calls: calls,
args: regexp.MustCompile(`^/tmp/.*$|^/var/tmp/.*$`), args: regexp.MustCompile(`^/tmp/.*$|^/var/tmp/.*$`),
MetaData: gas.MetaData{ MetaData: gosec.MetaData{
ID: id, ID: id,
Severity: gas.Medium, Severity: gosec.Medium,
Confidence: gas.High, Confidence: gosec.High,
What: "File creation in shared tmp directory without using ioutil.Tempfile", What: "File creation in shared tmp directory without using ioutil.Tempfile",
}, },
}, []ast.Node{(*ast.CallExpr)(nil)} }, []ast.Node{(*ast.CallExpr)(nil)}

View file

@ -17,23 +17,23 @@ package rules
import ( import (
"go/ast" "go/ast"
"github.com/GoASTScanner/gas" "github.com/securego/gosec"
) )
type templateCheck struct { type templateCheck struct {
gas.MetaData gosec.MetaData
calls gas.CallList calls gosec.CallList
} }
func (t *templateCheck) ID() string { func (t *templateCheck) ID() string {
return t.MetaData.ID 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 { if node := t.calls.ContainsCallExpr(n, c); node != nil {
for _, arg := range node.Args { for _, arg := range node.Args {
if _, ok := arg.(*ast.BasicLit); !ok { // basic lits are safe 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 // NewTemplateCheck constructs the template check rule. This rule is used to
// find use of tempaltes where HTML/JS escaping is not being used // 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", "HTML")
calls.Add("html/template", "HTMLAttr") calls.Add("html/template", "HTMLAttr")
calls.Add("html/template", "JS") calls.Add("html/template", "JS")
calls.Add("html/template", "URL") calls.Add("html/template", "URL")
return &templateCheck{ return &templateCheck{
calls: calls, calls: calls,
MetaData: gas.MetaData{ MetaData: gosec.MetaData{
ID: id, ID: id,
Severity: gas.Medium, Severity: gosec.Medium,
Confidence: gas.Low, Confidence: gosec.Low,
What: "this method will not auto-escape HTML. Verify data is well formed.", What: "this method will not auto-escape HTML. Verify data is well formed.",
}, },
}, []ast.Node{(*ast.CallExpr)(nil)} }, []ast.Node{(*ast.CallExpr)(nil)}

View file

@ -20,11 +20,11 @@ import (
"fmt" "fmt"
"go/ast" "go/ast"
"github.com/GoASTScanner/gas" "github.com/securego/gosec"
) )
type insecureConfigTLS struct { type insecureConfigTLS struct {
gas.MetaData gosec.MetaData
MinVersion int16 MinVersion int16
MaxVersion int16 MaxVersion int16
requiredType string requiredType string
@ -44,14 +44,14 @@ func stringInSlice(a string, list []string) bool {
return false 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 { if ciphers, ok := n.(*ast.CompositeLit); ok {
for _, cipher := range ciphers.Elts { for _, cipher := range ciphers.Elts {
if ident, ok := cipher.(*ast.SelectorExpr); ok { if ident, ok := cipher.(*ast.SelectorExpr); ok {
if !stringInSlice(ident.Sel.Name, t.goodCiphers) { if !stringInSlice(ident.Sel.Name, t.goodCiphers) {
err := fmt.Sprintf("TLS Bad Cipher Suite: %s", ident.Sel.Name) 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 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 { if ident, ok := n.Key.(*ast.Ident); ok {
switch ident.Name { switch ident.Name {
case "InsecureSkipVerify": case "InsecureSkipVerify":
if node, ok := n.Value.(*ast.Ident); ok { if node, ok := n.Value.(*ast.Ident); ok {
if node.Name != "false" { 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 { } else {
// TODO(tk): symbol tab look up to get the actual value // 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": case "PreferServerCipherSuites":
if node, ok := n.Value.(*ast.Ident); ok { if node, ok := n.Value.(*ast.Ident); ok {
if node.Name == "false" { 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 { } else {
// TODO(tk): symbol tab look up to get the actual value // 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": 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 { 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 // 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": 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 { 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 // 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": case "CipherSuites":
@ -112,7 +112,7 @@ func (t *insecureConfigTLS) processTLSConfVal(n *ast.KeyValueExpr, c *gas.Contex
return nil 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 { if complit, ok := n.(*ast.CompositeLit); ok && complit.Type != nil {
actualType := c.Info.TypeOf(complit.Type) actualType := c.Info.TypeOf(complit.Type)
if actualType != nil && actualType.String() == t.requiredType { if actualType != nil && actualType.String() == t.requiredType {

View file

@ -3,14 +3,14 @@ package rules
import ( import (
"go/ast" "go/ast"
"github.com/GoASTScanner/gas" "github.com/securego/gosec"
) )
// NewModernTLSCheck creates a check for Modern TLS ciphers // NewModernTLSCheck creates a check for Modern TLS ciphers
// DO NOT EDIT - generated by tlsconfig tool // 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{ return &insecureConfigTLS{
MetaData: gas.MetaData{ID: id}, MetaData: gosec.MetaData{ID: id},
requiredType: "crypto/tls.Config", requiredType: "crypto/tls.Config",
MinVersion: 0x0303, MinVersion: 0x0303,
MaxVersion: 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 // NewIntermediateTLSCheck creates a check for Intermediate TLS ciphers
// DO NOT EDIT - generated by tlsconfig tool // 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{ return &insecureConfigTLS{
MetaData: gas.MetaData{ID: id}, MetaData: gosec.MetaData{ID: id},
requiredType: "crypto/tls.Config", requiredType: "crypto/tls.Config",
MinVersion: 0x0301, MinVersion: 0x0301,
MaxVersion: 0x0303, 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 // NewOldTLSCheck creates a check for Old TLS ciphers
// DO NOT EDIT - generated by tlsconfig tool // 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{ return &insecureConfigTLS{
MetaData: gas.MetaData{ID: id}, MetaData: gosec.MetaData{ID: id},
requiredType: "crypto/tls.Config", requiredType: "crypto/tls.Config",
MinVersion: 0x0300, MinVersion: 0x0300,
MaxVersion: 0x0303, MaxVersion: 0x0303,

View file

@ -17,11 +17,11 @@ package rules
import ( import (
"go/ast" "go/ast"
"github.com/GoASTScanner/gas" "github.com/securego/gosec"
) )
type usingUnsafe struct { type usingUnsafe struct {
gas.MetaData gosec.MetaData
pkg string pkg string
calls []string calls []string
} }
@ -30,24 +30,24 @@ func (r *usingUnsafe) ID() string {
return r.MetaData.ID return r.MetaData.ID
} }
func (r *usingUnsafe) Match(n ast.Node, c *gas.Context) (gi *gas.Issue, err error) { func (r *usingUnsafe) Match(n ast.Node, c *gosec.Context) (gi *gosec.Issue, err error) {
if _, matches := gas.MatchCallByPackage(n, c, r.pkg, r.calls...); matches { if _, matches := gosec.MatchCallByPackage(n, c, r.pkg, r.calls...); matches {
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 return nil, nil
} }
// NewUsingUnsafe rule detects the use of the unsafe package. This is only // NewUsingUnsafe rule detects the use of the unsafe package. This is only
// really useful for auditing purposes. // 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{ return &usingUnsafe{
pkg: "unsafe", pkg: "unsafe",
calls: []string{"Alignof", "Offsetof", "Sizeof", "Pointer"}, calls: []string{"Alignof", "Offsetof", "Sizeof", "Pointer"},
MetaData: gas.MetaData{ MetaData: gosec.MetaData{
ID: id, ID: id,
What: "Use of unsafe calls should be audited", What: "Use of unsafe calls should be audited",
Severity: gas.Low, Severity: gosec.Low,
Confidence: gas.High, Confidence: gosec.High,
}, },
}, []ast.Node{(*ast.CallExpr)(nil)} }, []ast.Node{(*ast.CallExpr)(nil)}
} }

View file

@ -17,11 +17,11 @@ package rules
import ( import (
"go/ast" "go/ast"
"github.com/GoASTScanner/gas" "github.com/securego/gosec"
) )
type usesWeakCryptography struct { type usesWeakCryptography struct {
gas.MetaData gosec.MetaData
blacklist map[string][]string blacklist map[string][]string
} }
@ -29,27 +29,27 @@ func (r *usesWeakCryptography) ID() string {
return r.MetaData.ID 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 { for pkg, funcs := range r.blacklist {
if _, matched := gas.MatchCallByPackage(n, c, pkg, funcs...); matched { if _, matched := gosec.MatchCallByPackage(n, c, pkg, funcs...); matched {
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 return nil, nil
} }
// NewUsesWeakCryptography detects uses of des.* md5.* or rc4.* // 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 := make(map[string][]string)
calls["crypto/des"] = []string{"NewCipher", "NewTripleDESCipher"} calls["crypto/des"] = []string{"NewCipher", "NewTripleDESCipher"}
calls["crypto/md5"] = []string{"New", "Sum"} calls["crypto/md5"] = []string{"New", "Sum"}
calls["crypto/rc4"] = []string{"NewCipher"} calls["crypto/rc4"] = []string{"NewCipher"}
rule := &usesWeakCryptography{ rule := &usesWeakCryptography{
blacklist: calls, blacklist: calls,
MetaData: gas.MetaData{ MetaData: gosec.MetaData{
ID: id, ID: id,
Severity: gas.Medium, Severity: gosec.Medium,
Confidence: gas.High, Confidence: gosec.High,
What: "Use of weak cryptographic primitive", What: "Use of weak cryptographic primitive",
}, },
} }

View file

@ -10,7 +10,7 @@ import (
"path" "path"
"strings" "strings"
"github.com/GoASTScanner/gas" "github.com/securego/gosec"
"golang.org/x/tools/go/loader" "golang.org/x/tools/go/loader"
) )
@ -33,7 +33,7 @@ type TestPackage struct {
func NewTestPackage() *TestPackage { func NewTestPackage() *TestPackage {
// Files must exist in $GOPATH // Files must exist in $GOPATH
sourceDir := path.Join(os.Getenv("GOPATH"), "src") sourceDir := path.Join(os.Getenv("GOPATH"), "src")
workingDir, err := ioutil.TempDir(sourceDir, "gas_test") workingDir, err := ioutil.TempDir(sourceDir, "gosecs_test")
if err != nil { if err != nil {
return nil return nil
} }
@ -97,7 +97,7 @@ func (p *TestPackage) Build() error {
} }
// CreateContext builds a context out of supplied package context // 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 { if err := p.Build(); err != nil {
log.Fatal(err) log.Fatal(err)
return nil return nil
@ -109,13 +109,13 @@ func (p *TestPackage) CreateContext(filename string) *gas.Context {
strip := fmt.Sprintf("%s%c", p.Path, os.PathSeparator) strip := fmt.Sprintf("%s%c", p.Path, os.PathSeparator)
pkgFile = strings.TrimPrefix(pkgFile, strip) pkgFile = strings.TrimPrefix(pkgFile, strip)
if pkgFile == filename { if pkgFile == filename {
ctx := &gas.Context{ ctx := &gosec.Context{
FileSet: p.build.program.Fset, FileSet: p.build.program.Fset,
Root: file, Root: file,
Config: gas.NewConfig(), Config: gosec.NewConfig(),
Info: &pkg.Info, Info: &pkg.Info,
Pkg: pkg.Pkg, Pkg: pkg.Pkg,
Imports: gas.NewImportTracker(), Imports: gosec.NewImportTracker(),
} }
ctx.Imports.TrackPackages(ctx.Pkg.Imports()...) ctx.Imports.TrackPackages(ctx.Pkg.Imports()...)
return ctx return ctx

View file

@ -515,6 +515,100 @@ func main() {
log.Fatal(http.ListenAndServe(":3000", nil)) log.Fatal(http.ListenAndServe(":3000", nil))
}`, 1}} }`, 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 - Use of weak crypto MD5
SampleCodeG401 = []CodeSample{ SampleCodeG401 = []CodeSample{
{` {`

View file

@ -3,14 +3,14 @@ package testutils
import ( import (
"go/ast" "go/ast"
"github.com/GoASTScanner/gas" "github.com/securego/gosec"
) )
// MockVisitor is useful for stubbing out ast.Visitor with callback // MockVisitor is useful for stubbing out ast.Visitor with callback
// and looking for specific conditions to exist. // and looking for specific conditions to exist.
type MockVisitor struct { type MockVisitor struct {
Context *gas.Context Context *gosec.Context
Callback func(n ast.Node, ctx *gas.Context) bool Callback func(n ast.Node, ctx *gosec.Context) bool
} }
// NewMockVisitor creates a new empty struct, the Context and // NewMockVisitor creates a new empty struct, the Context and