mirror of
https://github.com/securego/gosec.git
synced 2024-12-25 03:55:54 +00:00
Merge branch 'master' into commandcontext
This commit is contained in:
commit
6a156e2695
54 changed files with 632 additions and 451 deletions
2
.github/issue_template.md
vendored
2
.github/issue_template.md
vendored
|
@ -2,7 +2,7 @@
|
|||
|
||||
### Steps to reproduce the behavior
|
||||
|
||||
### Gas version
|
||||
### gosec version
|
||||
|
||||
### Go version (output of 'go version')
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
language: go
|
||||
|
||||
go:
|
||||
- 1.8
|
||||
- 1.9
|
||||
- "1.10"
|
||||
- tip
|
||||
|
@ -12,7 +11,7 @@ install:
|
|||
- go get -u github.com/onsi/ginkgo/ginkgo
|
||||
- go get -u github.com/onsi/gomega
|
||||
- go get -u golang.org/x/crypto/ssh
|
||||
- go get -u github.com/GoASTScanner/gas/cmd/gas/...
|
||||
- go get -u github.com/securego/gosec/cmd/gosec/...
|
||||
- go get -v -t ./...
|
||||
- export PATH=$PATH:$HOME/gopath/bin
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
FROM golang:1.9.4-alpine3.7
|
||||
|
||||
ENV BIN=gas
|
||||
ENV BIN=gosec
|
||||
|
||||
COPY build/*-linux-amd64 /go/bin/$BIN
|
||||
COPY docker-entrypoint.sh /usr/local/bin
|
||||
|
|
8
Makefile
8
Makefile
|
@ -1,7 +1,7 @@
|
|||
GIT_TAG?= $(shell git describe --always --tags)
|
||||
BUILD_DATE = $(shell date +%Y-%m-%d)
|
||||
BIN = gas
|
||||
BUILD_CMD = go build -ldflags "-X main.Version=${VERSION} -X main.GitTag=${GIT_TAG} -X main.BuildDate=${BUILD_DATE}" -o build/$(BIN)-$(VERSION)-$${GOOS}-$${GOARCH} ./cmd/gas/ &
|
||||
BIN = gosec
|
||||
BUILD_CMD = go build -ldflags "-X main.Version=${VERSION} -X main.GitTag=${GIT_TAG} -X main.BuildDate=${BUILD_DATE}" -o build/$(BIN)-$(VERSION)-$${GOOS}-$${GOARCH} ./cmd/gosec/ &
|
||||
FMT_CMD = $(gofmt -s -l -w $(find . -type f -name '*.go' -not -path './vendor/*') | tee /dev/stderr)
|
||||
IMAGE_REPO = docker.io
|
||||
|
||||
|
@ -13,12 +13,12 @@ test: bootstrap
|
|||
test -z '$(FMT_CMD)'
|
||||
go vet $(go list ./... | grep -v /vendor/)
|
||||
golint -set_exit_status $(shell go list ./... | grep -v vendor)
|
||||
gas ./...
|
||||
gosec ./...
|
||||
ginkgo -r -v
|
||||
bootstrap:
|
||||
dep ensure
|
||||
build:
|
||||
go build -o $(BIN) ./cmd/gas/
|
||||
go build -o $(BIN) ./cmd/gosec/
|
||||
clean:
|
||||
rm -rf build vendor
|
||||
rm -f release image bootstrap $(BIN)
|
||||
|
|
50
README.md
50
README.md
|
@ -1,6 +1,6 @@
|
|||
|
||||
|
||||
## GAS - Go AST Scanner
|
||||
## gosec -Golang Security Checker
|
||||
|
||||
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
|
||||
|
||||
[![Build Status](https://travis-ci.org/GoASTScanner/gas.svg?branch=master)](https://travis-ci.org/GoASTScanner/gas)
|
||||
[![GoDoc](https://godoc.org/github.com/GoASTScanner/gas?status.svg)](https://godoc.org/github.com/GoASTScanner/gas)
|
||||
[![Build Status](https://travis-ci.org/securego/gosec.svg?branch=master)](https://travis-ci.org/securego/gosec)
|
||||
[![GoDoc](https://godoc.org/github.com/securego/gosec?status.svg)](https://godoc.org/github.com/securego/gosec)
|
||||
[![Slack](http://securego.herokuapp.com/badge.svg)](http://securego.herokuapp.com)
|
||||
|
||||
Gas is still in alpha and accepting feedback from early adopters. We do
|
||||
not consider it production ready at this time.
|
||||
|
||||
### Install
|
||||
|
||||
`$ go get github.com/GoASTScanner/gas/cmd/gas/...`
|
||||
`$ go get github.com/securego/gosec/cmd/gosec/...`
|
||||
|
||||
### Usage
|
||||
|
||||
Gas can be configured to only run a subset of rules, to exclude certain file
|
||||
Gosec can be configured to only run a subset of rules, to exclude certain file
|
||||
paths, and produce reports in different formats. By default all rules will be
|
||||
run against the supplied input files. To recursively scan from the current
|
||||
directory you can supply './...' as the input argument.
|
||||
|
||||
#### Selecting rules
|
||||
|
||||
By default Gas will run all rules against the supplied file paths. It is however possible to select a subset of rules to run via the '-include=' flag,
|
||||
By default gosec will run all rules against the supplied file paths. It is however possible to select a subset of rules to run via the '-include=' flag,
|
||||
or to specify a set of rules to explicitly exclude using the '-exclude=' flag.
|
||||
|
||||
##### Available rules
|
||||
|
@ -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
|
||||
- G303: Creating tempfile using a predictable path
|
||||
- G304: File path provided as taint input
|
||||
- G305: File traversal when extracting zip archive
|
||||
- G401: Detect the usage of DES, RC4, or MD5
|
||||
- G402: Look for bad TLS connection settings
|
||||
- G403: Ensure minimum RSA key length of 2048 bits
|
||||
|
@ -62,22 +62,22 @@ or to specify a set of rules to explicitly exclude using the '-exclude=' flag.
|
|||
|
||||
```
|
||||
# Run a specific set of rules
|
||||
$ gas -include=G101,G203,G401 ./...
|
||||
$ gosec -include=G101,G203,G401 ./...
|
||||
|
||||
# Run everything except for rule G303
|
||||
$ gas -exclude=G303 ./...
|
||||
$ gosec -exclude=G303 ./...
|
||||
```
|
||||
|
||||
#### Excluding files:
|
||||
|
||||
Gas will ignore dependencies in your vendor directory any files
|
||||
gosec will ignore dependencies in your vendor directory any files
|
||||
that are not considered build artifacts by the compiler (so test files).
|
||||
|
||||
#### Annotating code
|
||||
|
||||
As with all automated detection tools there will be cases of false positives. In cases where Gas reports a failure that has been manually verified as being safe it is possible to annotate the code with a '#nosec' comment.
|
||||
As with all automated detection tools there will be cases of false positives. In cases where gosec reports a failure that has been manually verified as being safe it is possible to annotate the code with a '#nosec' comment.
|
||||
|
||||
The annotation causes Gas to stop processing any further nodes within the
|
||||
The annotation causes gosec to stop processing any further nodes within the
|
||||
AST so can apply to a whole block or more granularly to a single expression.
|
||||
|
||||
```go
|
||||
|
@ -101,26 +101,26 @@ have been used. To run the scanner and ignore any #nosec annotations you
|
|||
can do the following:
|
||||
|
||||
```
|
||||
$ gas -nosec=true ./...
|
||||
$ gosec -nosec=true ./...
|
||||
```
|
||||
#### Build tags
|
||||
|
||||
Gas is able to pass your [Go build tags](https://golang.org/pkg/go/build/) to the analyzer.
|
||||
gosec is able to pass your [Go build tags](https://golang.org/pkg/go/build/) to the analyzer.
|
||||
They can be provided as a comma separated list as follows:
|
||||
|
||||
```
|
||||
$ gas -tag debug,ignore ./...
|
||||
$ gosec -tag debug,ignore ./...
|
||||
```
|
||||
|
||||
### Output formats
|
||||
|
||||
Gas currently supports text, json, yaml, csv and JUnit XML output formats. By default
|
||||
gosec currently supports text, json, yaml, csv and JUnit XML output formats. By default
|
||||
results will be reported to stdout, but can also be written to an output
|
||||
file. The output format is controlled by the '-fmt' flag, and the output file is controlled by the '-out' flag as follows:
|
||||
|
||||
```
|
||||
# Write output in json format to results.json
|
||||
$ gas -fmt=json -out=results.json *.go
|
||||
$ gosec -fmt=json -out=results.json *.go
|
||||
```
|
||||
### Development
|
||||
|
||||
|
@ -143,7 +143,7 @@ make test
|
|||
|
||||
#### Release Build
|
||||
|
||||
Gas can be released as follows:
|
||||
gosec can be released as follows:
|
||||
|
||||
```bash
|
||||
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.
|
||||
|
||||
```
|
||||
./build/gas-2.0.0-linux-amd64 -h
|
||||
./build/gosec-2.0.0-linux-amd64 -h
|
||||
|
||||
GAS - Go AST Scanner
|
||||
gosec - Golang security checker
|
||||
|
||||
Gas analyzes Go source code to look for common programming mistakes that
|
||||
gosec analyzes Go source code to look for common programming mistakes that
|
||||
can lead to security problems.
|
||||
|
||||
VERSION: 2.0.0
|
||||
|
@ -173,10 +173,10 @@ You can execute a release and build the docker image as follows:
|
|||
make image VERSION=2.0.0
|
||||
```
|
||||
|
||||
Now you can run the gas tool in a container against your local workspace:
|
||||
Now you can run the gosec tool in a container against your local workspace:
|
||||
|
||||
```
|
||||
docker run -it -v <YOUR LOCAL WORKSPACE>:/workspace gas /workspace
|
||||
docker run -it -v <YOUR LOCAL WORKSPACE>:/workspace gosec /workspace
|
||||
```
|
||||
|
||||
#### 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:
|
||||
|
||||
```
|
||||
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:
|
||||
|
|
105
analyzer.go
105
analyzer.go
|
@ -12,8 +12,8 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package gas holds the central scanning logic used by GAS
|
||||
package gas
|
||||
// Package gosec holds the central scanning logic used by gosec security scanner
|
||||
package gosec
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
|
@ -28,8 +28,6 @@ import (
|
|||
"regexp"
|
||||
"strings"
|
||||
|
||||
"path/filepath"
|
||||
|
||||
"golang.org/x/tools/go/loader"
|
||||
)
|
||||
|
||||
|
@ -55,7 +53,7 @@ type Metrics struct {
|
|||
NumFound int `json:"found"`
|
||||
}
|
||||
|
||||
// Analyzer object is the main object of GAS. It has methods traverse an AST
|
||||
// Analyzer object is the main object of gosec. It has methods traverse an AST
|
||||
// and invoke the correct checking rules as on each node as required.
|
||||
type Analyzer struct {
|
||||
ignoreNosec bool
|
||||
|
@ -74,7 +72,7 @@ func NewAnalyzer(conf Config, logger *log.Logger) *Analyzer {
|
|||
ignoreNoSec = setting == "true" || setting == "enabled"
|
||||
}
|
||||
if logger == nil {
|
||||
logger = log.New(os.Stderr, "[gas]", log.LstdFlags)
|
||||
logger = log.New(os.Stderr, "[gosec]", log.LstdFlags)
|
||||
}
|
||||
return &Analyzer{
|
||||
ignoreNosec: ignoreNoSec,
|
||||
|
@ -89,15 +87,15 @@ func NewAnalyzer(conf Config, logger *log.Logger) *Analyzer {
|
|||
|
||||
// LoadRules instantiates all the rules to be used when analyzing source
|
||||
// packages
|
||||
func (gas *Analyzer) LoadRules(ruleDefinitions map[string]RuleBuilder) {
|
||||
func (gosec *Analyzer) LoadRules(ruleDefinitions map[string]RuleBuilder) {
|
||||
for id, def := range ruleDefinitions {
|
||||
r, nodes := def(id, gas.config)
|
||||
gas.ruleset.Register(r, nodes...)
|
||||
r, nodes := def(id, gosec.config)
|
||||
gosec.ruleset.Register(r, nodes...)
|
||||
}
|
||||
}
|
||||
|
||||
// Process kicks off the analysis process for a given package
|
||||
func (gas *Analyzer) Process(buildTags []string, packagePaths ...string) error {
|
||||
func (gosec *Analyzer) Process(buildTags []string, packagePaths ...string) error {
|
||||
ctx := build.Default
|
||||
ctx.BuildTags = append(ctx.BuildTags, buildTags...)
|
||||
packageConfig := loader.Config{
|
||||
|
@ -106,15 +104,12 @@ func (gas *Analyzer) Process(buildTags []string, packagePaths ...string) error {
|
|||
AllowErrors: true,
|
||||
}
|
||||
for _, packagePath := range packagePaths {
|
||||
abspath, err := filepath.Abs(packagePath)
|
||||
abspath, err := GetPkgAbsPath(packagePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := os.Stat(abspath); os.IsNotExist(err) {
|
||||
gas.logger.Printf("Skipping: %s. Path doesn't exist.", abspath)
|
||||
gosec.logger.Printf("Skipping: %s. Path doesn't exist.", abspath)
|
||||
continue
|
||||
}
|
||||
gas.logger.Println("Searching directory:", abspath)
|
||||
gosec.logger.Println("Searching directory:", abspath)
|
||||
|
||||
basePackage, err := build.Default.ImportDir(packagePath, build.ImportComment)
|
||||
if err != nil {
|
||||
|
@ -135,31 +130,31 @@ func (gas *Analyzer) Process(buildTags []string, packagePaths ...string) error {
|
|||
}
|
||||
|
||||
for _, pkg := range builtPackage.Created {
|
||||
gas.logger.Println("Checking package:", pkg.String())
|
||||
gosec.logger.Println("Checking package:", pkg.String())
|
||||
for _, file := range pkg.Files {
|
||||
gas.logger.Println("Checking file:", builtPackage.Fset.File(file.Pos()).Name())
|
||||
gas.context.FileSet = builtPackage.Fset
|
||||
gas.context.Config = gas.config
|
||||
gas.context.Comments = ast.NewCommentMap(gas.context.FileSet, file, file.Comments)
|
||||
gas.context.Root = file
|
||||
gas.context.Info = &pkg.Info
|
||||
gas.context.Pkg = pkg.Pkg
|
||||
gas.context.Imports = NewImportTracker()
|
||||
gas.context.Imports.TrackPackages(gas.context.Pkg.Imports()...)
|
||||
ast.Walk(gas, file)
|
||||
gas.stats.NumFiles++
|
||||
gas.stats.NumLines += builtPackage.Fset.File(file.Pos()).LineCount()
|
||||
gosec.logger.Println("Checking file:", builtPackage.Fset.File(file.Pos()).Name())
|
||||
gosec.context.FileSet = builtPackage.Fset
|
||||
gosec.context.Config = gosec.config
|
||||
gosec.context.Comments = ast.NewCommentMap(gosec.context.FileSet, file, file.Comments)
|
||||
gosec.context.Root = file
|
||||
gosec.context.Info = &pkg.Info
|
||||
gosec.context.Pkg = pkg.Pkg
|
||||
gosec.context.Imports = NewImportTracker()
|
||||
gosec.context.Imports.TrackPackages(gosec.context.Pkg.Imports()...)
|
||||
ast.Walk(gosec, file)
|
||||
gosec.stats.NumFiles++
|
||||
gosec.stats.NumLines += builtPackage.Fset.File(file.Pos()).LineCount()
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ignore a node (and sub-tree) if it is tagged with a "#nosec" comment
|
||||
func (gas *Analyzer) ignore(n ast.Node) ([]string, bool) {
|
||||
if groups, ok := gas.context.Comments[n]; ok && !gas.ignoreNosec {
|
||||
func (gosec *Analyzer) ignore(n ast.Node) ([]string, bool) {
|
||||
if groups, ok := gosec.context.Comments[n]; ok && !gosec.ignoreNosec {
|
||||
for _, group := range groups {
|
||||
if strings.Contains(group.Text(), "#nosec") {
|
||||
gas.stats.NumNosec++
|
||||
gosec.stats.NumNosec++
|
||||
|
||||
// Pull out the specific rules that are listed to be ignored.
|
||||
re := regexp.MustCompile("(G\\d{3})")
|
||||
|
@ -182,27 +177,27 @@ func (gas *Analyzer) ignore(n ast.Node) ([]string, bool) {
|
|||
return nil, false
|
||||
}
|
||||
|
||||
// Visit runs the GAS visitor logic over an AST created by parsing go code.
|
||||
// Visit runs the gosec visitor logic over an AST created by parsing go code.
|
||||
// Rule methods added with AddRule will be invoked as necessary.
|
||||
func (gas *Analyzer) Visit(n ast.Node) ast.Visitor {
|
||||
func (gosec *Analyzer) Visit(n ast.Node) ast.Visitor {
|
||||
// If we've reached the end of this branch, pop off the ignores stack.
|
||||
if n == nil {
|
||||
if len(gas.context.Ignores) > 0 {
|
||||
gas.context.Ignores = gas.context.Ignores[1:]
|
||||
if len(gosec.context.Ignores) > 0 {
|
||||
gosec.context.Ignores = gosec.context.Ignores[1:]
|
||||
}
|
||||
return gas
|
||||
return gosec
|
||||
}
|
||||
|
||||
// Get any new rule exclusions.
|
||||
ignoredRules, ignoreAll := gas.ignore(n)
|
||||
ignoredRules, ignoreAll := gosec.ignore(n)
|
||||
if ignoreAll {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Now create the union of exclusions.
|
||||
ignores := make(map[string]bool, 0)
|
||||
if len(gas.context.Ignores) > 0 {
|
||||
for k, v := range gas.context.Ignores[0] {
|
||||
if len(gosec.context.Ignores) > 0 {
|
||||
for k, v := range gosec.context.Ignores[0] {
|
||||
ignores[k] = v
|
||||
}
|
||||
}
|
||||
|
@ -212,37 +207,37 @@ func (gas *Analyzer) Visit(n ast.Node) ast.Visitor {
|
|||
}
|
||||
|
||||
// Push the new set onto the stack.
|
||||
gas.context.Ignores = append([]map[string]bool{ignores}, gas.context.Ignores...)
|
||||
gosec.context.Ignores = append([]map[string]bool{ignores}, gosec.context.Ignores...)
|
||||
|
||||
// Track aliased and initialization imports
|
||||
gas.context.Imports.TrackImport(n)
|
||||
gosec.context.Imports.TrackImport(n)
|
||||
|
||||
for _, rule := range gas.ruleset.RegisteredFor(n) {
|
||||
for _, rule := range gosec.ruleset.RegisteredFor(n) {
|
||||
if _, ok := ignores[rule.ID()]; ok {
|
||||
continue
|
||||
}
|
||||
issue, err := rule.Match(n, gas.context)
|
||||
issue, err := rule.Match(n, gosec.context)
|
||||
if err != nil {
|
||||
file, line := GetLocation(n, gas.context)
|
||||
file, line := GetLocation(n, gosec.context)
|
||||
file = path.Base(file)
|
||||
gas.logger.Printf("Rule error: %v => %s (%s:%d)\n", reflect.TypeOf(rule), err, file, line)
|
||||
gosec.logger.Printf("Rule error: %v => %s (%s:%d)\n", reflect.TypeOf(rule), err, file, line)
|
||||
}
|
||||
if issue != nil {
|
||||
gas.issues = append(gas.issues, issue)
|
||||
gas.stats.NumFound++
|
||||
gosec.issues = append(gosec.issues, issue)
|
||||
gosec.stats.NumFound++
|
||||
}
|
||||
}
|
||||
return gas
|
||||
return gosec
|
||||
}
|
||||
|
||||
// Report returns the current issues discovered and the metrics about the scan
|
||||
func (gas *Analyzer) Report() ([]*Issue, *Metrics) {
|
||||
return gas.issues, gas.stats
|
||||
func (gosec *Analyzer) Report() ([]*Issue, *Metrics) {
|
||||
return gosec.issues, gosec.stats
|
||||
}
|
||||
|
||||
// Reset clears state such as context, issues and metrics from the configured analyzer
|
||||
func (gas *Analyzer) Reset() {
|
||||
gas.context = &Context{}
|
||||
gas.issues = make([]*Issue, 0, 16)
|
||||
gas.stats = &Metrics{}
|
||||
func (gosec *Analyzer) Reset() {
|
||||
gosec.context = &Context{}
|
||||
gosec.issues = make([]*Issue, 0, 16)
|
||||
gosec.stats = &Metrics{}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package gas_test
|
||||
package gosec_test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
|
@ -6,24 +6,24 @@ import (
|
|||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
"github.com/GoASTScanner/gas/rules"
|
||||
"github.com/securego/gosec"
|
||||
"github.com/securego/gosec/rules"
|
||||
|
||||
"github.com/GoASTScanner/gas/testutils"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/securego/gosec/testutils"
|
||||
)
|
||||
|
||||
var _ = Describe("Analyzer", func() {
|
||||
|
||||
var (
|
||||
analyzer *gas.Analyzer
|
||||
analyzer *gosec.Analyzer
|
||||
logger *log.Logger
|
||||
buildTags []string
|
||||
)
|
||||
BeforeEach(func() {
|
||||
logger, _ = testutils.NewLogger()
|
||||
analyzer = gas.NewAnalyzer(nil, logger)
|
||||
analyzer = gosec.NewAnalyzer(nil, logger)
|
||||
})
|
||||
|
||||
Context("when processing a package", func() {
|
||||
|
@ -200,9 +200,9 @@ var _ = Describe("Analyzer", func() {
|
|||
source := sample.Code
|
||||
|
||||
// overwrite nosec option
|
||||
nosecIgnoreConfig := gas.NewConfig()
|
||||
nosecIgnoreConfig := gosec.NewConfig()
|
||||
nosecIgnoreConfig.SetGlobal("nosec", "true")
|
||||
customAnalyzer := gas.NewAnalyzer(nosecIgnoreConfig, logger)
|
||||
customAnalyzer := gosec.NewAnalyzer(nosecIgnoreConfig, logger)
|
||||
customAnalyzer.LoadRules(rules.Generate(rules.NewRuleFilter(false, "G401")).Builders())
|
||||
|
||||
nosecPackage := testutils.NewTestPackage()
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package gas
|
||||
package gosec
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
package gas_test
|
||||
package gosec_test
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
"github.com/GoASTScanner/gas/testutils"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/securego/gosec"
|
||||
"github.com/securego/gosec/testutils"
|
||||
)
|
||||
|
||||
var _ = Describe("call list", func() {
|
||||
var (
|
||||
calls gas.CallList
|
||||
calls gosec.CallList
|
||||
)
|
||||
BeforeEach(func() {
|
||||
calls = gas.NewCallList()
|
||||
calls = gosec.NewCallList()
|
||||
})
|
||||
|
||||
It("should not return any matches when empty", func() {
|
||||
|
@ -72,7 +72,7 @@ var _ = Describe("call list", func() {
|
|||
matched := 0
|
||||
v := testutils.NewMockVisitor()
|
||||
v.Context = ctx
|
||||
v.Callback = func(n ast.Node, ctx *gas.Context) bool {
|
||||
v.Callback = func(n ast.Node, ctx *gosec.Context) bool {
|
||||
if _, ok := n.(*ast.CallExpr); ok && calls.ContainsCallExpr(n, ctx) != nil {
|
||||
matched++
|
||||
}
|
||||
|
|
|
@ -20,24 +20,22 @@ import (
|
|||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
"github.com/GoASTScanner/gas/output"
|
||||
"github.com/GoASTScanner/gas/rules"
|
||||
"github.com/kisielk/gotool"
|
||||
"github.com/securego/gosec"
|
||||
"github.com/securego/gosec/output"
|
||||
"github.com/securego/gosec/rules"
|
||||
)
|
||||
|
||||
const (
|
||||
usageText = `
|
||||
GAS - Go AST Scanner
|
||||
gosec - Golang security checker
|
||||
|
||||
Gas analyzes Go source code to look for common programming mistakes that
|
||||
gosec analyzes Go source code to look for common programming mistakes that
|
||||
can lead to security problems.
|
||||
|
||||
VERSION: %s
|
||||
|
@ -47,17 +45,17 @@ BUILD DATE: %s
|
|||
USAGE:
|
||||
|
||||
# Check a single package
|
||||
$ gas $GOPATH/src/github.com/example/project
|
||||
$ gosec $GOPATH/src/github.com/example/project
|
||||
|
||||
# Check all packages under the current directory and save results in
|
||||
# json format.
|
||||
$ gas -fmt=json -out=results.json ./...
|
||||
$ gosec -fmt=json -out=results.json ./...
|
||||
|
||||
# Run a specific set of rules (by default all rules will be run):
|
||||
$ gas -include=G101,G203,G401 ./...
|
||||
$ gosec -include=G101,G203,G401 ./...
|
||||
|
||||
# Run all rules except the provided
|
||||
$ gas -exclude=G101 $GOPATH/src/github.com/example/project/...
|
||||
$ gosec -exclude=G101 $GOPATH/src/github.com/example/project/...
|
||||
|
||||
`
|
||||
)
|
||||
|
@ -119,8 +117,8 @@ func usage() {
|
|||
fmt.Fprint(os.Stderr, "\n")
|
||||
}
|
||||
|
||||
func loadConfig(configFile string) (gas.Config, error) {
|
||||
config := gas.NewConfig()
|
||||
func loadConfig(configFile string) (gosec.Config, error) {
|
||||
config := gosec.NewConfig()
|
||||
if configFile != "" {
|
||||
// #nosec
|
||||
file, err := os.Open(configFile)
|
||||
|
@ -158,7 +156,7 @@ func loadRules(include, exclude string) rules.RuleList {
|
|||
return rules.Generate(filters...)
|
||||
}
|
||||
|
||||
func saveOutput(filename, format string, issues []*gas.Issue, metrics *gas.Metrics) error {
|
||||
func saveOutput(filename, format string, issues []*gosec.Issue, metrics *gosec.Metrics) error {
|
||||
if filename != "" {
|
||||
outfile, err := os.Create(filename)
|
||||
if err != nil {
|
||||
|
@ -178,36 +176,13 @@ func saveOutput(filename, format string, issues []*gas.Issue, metrics *gas.Metri
|
|||
return nil
|
||||
}
|
||||
|
||||
func getenv(key, userDefault string) string {
|
||||
if val := os.Getenv(key); val != "" {
|
||||
return val
|
||||
}
|
||||
return userDefault
|
||||
}
|
||||
|
||||
func gopath() []string {
|
||||
defaultGoPath := runtime.GOROOT()
|
||||
if u, err := user.Current(); err == nil {
|
||||
defaultGoPath = filepath.Join(u.HomeDir, "go")
|
||||
}
|
||||
path := getenv("GOPATH", defaultGoPath)
|
||||
paths := strings.Split(path, string(os.PathListSeparator))
|
||||
for idx, path := range paths {
|
||||
if abs, err := filepath.Abs(path); err == nil {
|
||||
paths[idx] = abs
|
||||
}
|
||||
}
|
||||
return paths
|
||||
}
|
||||
|
||||
func cleanPath(path string, gopaths []string) (string, error) {
|
||||
|
||||
func cleanPath(path string) (string, error) {
|
||||
cleanFailed := fmt.Errorf("%s is not within the $GOPATH and cannot be processed", path)
|
||||
nonRecursivePath := strings.TrimSuffix(path, "/...")
|
||||
// do not attempt to clean directs that are resolvable on gopath
|
||||
if _, err := os.Stat(nonRecursivePath); err != nil && os.IsNotExist(err) {
|
||||
log.Printf("directory %s doesn't exist, checking if is a package on $GOPATH", path)
|
||||
for _, basedir := range gopaths {
|
||||
for _, basedir := range gosec.Gopath() {
|
||||
dir := filepath.Join(basedir, "src", nonRecursivePath)
|
||||
if st, err := os.Stat(dir); err == nil && st.IsDir() {
|
||||
log.Printf("located %s in %s", path, dir)
|
||||
|
@ -218,24 +193,17 @@ func cleanPath(path string, gopaths []string) (string, error) {
|
|||
}
|
||||
|
||||
// ensure we resolve package directory correctly based on $GOPATH
|
||||
abspath, err := filepath.Abs(path)
|
||||
pkgPath, err := gosec.GetPkgRelativePath(path)
|
||||
if err != nil {
|
||||
abspath = path
|
||||
return "", cleanFailed
|
||||
}
|
||||
for _, base := range gopaths {
|
||||
projectRoot := filepath.FromSlash(fmt.Sprintf("%s/src/", base))
|
||||
if strings.HasPrefix(abspath, projectRoot) {
|
||||
return strings.TrimPrefix(abspath, projectRoot), nil
|
||||
}
|
||||
}
|
||||
return "", cleanFailed
|
||||
return pkgPath, nil
|
||||
}
|
||||
|
||||
func cleanPaths(paths []string) []string {
|
||||
gopaths := gopath()
|
||||
var clean []string
|
||||
for _, path := range paths {
|
||||
cleaned, err := cleanPath(path, gopaths)
|
||||
cleaned, err := cleanPath(path)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
@ -283,7 +251,7 @@ func main() {
|
|||
if *flagQuiet {
|
||||
logger = log.New(ioutil.Discard, "", 0)
|
||||
} else {
|
||||
logger = log.New(logWriter, "[gas] ", log.LstdFlags)
|
||||
logger = log.New(logWriter, "[gosec] ", log.LstdFlags)
|
||||
}
|
||||
|
||||
// Load config
|
||||
|
@ -299,14 +267,14 @@ func main() {
|
|||
}
|
||||
|
||||
// Create the analyzer
|
||||
analyzer := gas.NewAnalyzer(config, logger)
|
||||
analyzer := gosec.NewAnalyzer(config, logger)
|
||||
analyzer.LoadRules(ruleDefinitions.Builders())
|
||||
|
||||
vendor := regexp.MustCompile(`[\\/]vendor([\\/]|$)`)
|
||||
|
||||
var packages []string
|
||||
// Iterate over packages on the import paths
|
||||
gopaths := gopath()
|
||||
gopaths := gosec.Gopath()
|
||||
for _, pkg := range gotool.ImportPaths(cleanPaths(flag.Args())) {
|
||||
|
||||
// Skip vendor directory
|
|
@ -3,10 +3,10 @@ package main
|
|||
import (
|
||||
"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) }
|
||||
|
||||
|
@ -15,6 +15,6 @@ func (s sortBySeverity) Less(i, j int) bool { return s[i].Severity > s[i].Severi
|
|||
func (s sortBySeverity) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
|
||||
// sortIssues sorts the issues by severity in descending order
|
||||
func sortIssues(issues []*gas.Issue) {
|
||||
func sortIssues(issues []*gosec.Issue) {
|
||||
sort.Sort(sortBySeverity(issues))
|
||||
}
|
|
@ -8,6 +8,6 @@ package {{.}}
|
|||
import (
|
||||
"go/ast"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
"github.com/securego/gosec"
|
||||
)
|
||||
`))
|
||||
|
|
|
@ -5,9 +5,9 @@ import "text/template"
|
|||
var generatedRuleTmpl = template.Must(template.New("generated").Parse(`
|
||||
// New{{.Name}}TLSCheck creates a check for {{.Name}} TLS ciphers
|
||||
// DO NOT EDIT - generated by tlsconfig tool
|
||||
func New{{.Name}}TLSCheck(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
func New{{.Name}}TLSCheck(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
|
||||
return &insecureConfigTLS{
|
||||
MetaData: gas.MetaData{ID: id},
|
||||
MetaData: gosec.MetaData{ID: id},
|
||||
requiredType: "crypto/tls.Config",
|
||||
MinVersion: {{ .MinVersion }},
|
||||
MaxVersion: {{ .MaxVersion }},
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package gas
|
||||
package gosec
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
@ -10,7 +10,7 @@ import (
|
|||
|
||||
const (
|
||||
// Globals are applicable to all rules and used for general
|
||||
// configuration settings for gas.
|
||||
// configuration settings for gosec.
|
||||
Globals = "global"
|
||||
)
|
||||
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
package gas_test
|
||||
package gosec_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/securego/gosec"
|
||||
)
|
||||
|
||||
var _ = Describe("Configuration", func() {
|
||||
var configuration gas.Config
|
||||
var configuration gosec.Config
|
||||
BeforeEach(func() {
|
||||
configuration = gas.NewConfig()
|
||||
configuration = gosec.NewConfig()
|
||||
})
|
||||
|
||||
Context("when loading from disk", func() {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package gas_test
|
||||
package gosec_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
|
@ -7,7 +7,7 @@ import (
|
|||
"testing"
|
||||
)
|
||||
|
||||
func TestGas(t *testing.T) {
|
||||
func TestGosec(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "Gas Suite")
|
||||
RunSpecs(t, "gosec Suite")
|
||||
}
|
65
helpers.go
65
helpers.go
|
@ -12,14 +12,20 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package gas
|
||||
package gosec
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/token"
|
||||
"go/types"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// MatchCallByPackage ensures that the specified package is imported,
|
||||
|
@ -193,3 +199,60 @@ func GetLocation(n ast.Node, ctx *Context) (string, int) {
|
|||
fobj := ctx.FileSet.File(n.Pos())
|
||||
return fobj.Name(), fobj.Line(n.Pos())
|
||||
}
|
||||
|
||||
// Gopath returns all GOPATHs
|
||||
func Gopath() []string {
|
||||
defaultGoPath := runtime.GOROOT()
|
||||
if u, err := user.Current(); err == nil {
|
||||
defaultGoPath = filepath.Join(u.HomeDir, "go")
|
||||
}
|
||||
path := Getenv("GOPATH", defaultGoPath)
|
||||
paths := strings.Split(path, string(os.PathListSeparator))
|
||||
for idx, path := range paths {
|
||||
if abs, err := filepath.Abs(path); err == nil {
|
||||
paths[idx] = abs
|
||||
}
|
||||
}
|
||||
return paths
|
||||
}
|
||||
|
||||
// Getenv returns the values of the environment variable, otherwise
|
||||
//returns the default if variable is not set
|
||||
func Getenv(key, userDefault string) string {
|
||||
if val := os.Getenv(key); val != "" {
|
||||
return val
|
||||
}
|
||||
return userDefault
|
||||
}
|
||||
|
||||
// GetPkgRelativePath returns the Go relative relative path derived
|
||||
// form the given path
|
||||
func GetPkgRelativePath(path string) (string, error) {
|
||||
abspath, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
abspath = path
|
||||
}
|
||||
if strings.HasSuffix(abspath, ".go") {
|
||||
abspath = filepath.Dir(abspath)
|
||||
}
|
||||
for _, base := range Gopath() {
|
||||
projectRoot := filepath.FromSlash(fmt.Sprintf("%s/src/", base))
|
||||
if strings.HasPrefix(abspath, projectRoot) {
|
||||
return strings.TrimPrefix(abspath, projectRoot), nil
|
||||
}
|
||||
}
|
||||
return "", errors.New("no project relative path found")
|
||||
}
|
||||
|
||||
// GetPkgAbsPath returns the Go package absolute path derived from
|
||||
// the given path
|
||||
func GetPkgAbsPath(pkgPath string) (string, error) {
|
||||
absPath, err := filepath.Abs(pkgPath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if _, err := os.Stat(absPath); os.IsNotExist(err) {
|
||||
return "", errors.New("no project absolute path found")
|
||||
}
|
||||
return absPath, nil
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package gas_test
|
||||
package gosec_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package gas
|
||||
package gosec
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
|
|
6
issue.go
6
issue.go
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package gas
|
||||
package gosec
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
@ -34,7 +34,7 @@ const (
|
|||
High
|
||||
)
|
||||
|
||||
// Issue is returnd by a GAS rule if it discovers an issue with the scanned code.
|
||||
// Issue is returnd by a gosec rule if it discovers an issue with the scanned code.
|
||||
type Issue struct {
|
||||
Severity Score `json:"severity"` // issue severity (how problematic it is)
|
||||
Confidence Score `json:"confidence"` // issue confidence (how sure we are we found it)
|
||||
|
@ -45,7 +45,7 @@ type Issue struct {
|
|||
Line string `json:"line"` // Line number in file
|
||||
}
|
||||
|
||||
// MetaData is embedded in all GAS rules. The Severity, Confidence and What message
|
||||
// MetaData is embedded in all gosec rules. The Severity, Confidence and What message
|
||||
// will be passed tbhrough to reported issues.
|
||||
type MetaData struct {
|
||||
ID string
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
package gas_test
|
||||
package gosec_test
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
"github.com/GoASTScanner/gas/rules"
|
||||
"github.com/GoASTScanner/gas/testutils"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/securego/gosec"
|
||||
"github.com/securego/gosec/rules"
|
||||
"github.com/securego/gosec/testutils"
|
||||
)
|
||||
|
||||
var _ = Describe("Issue", func() {
|
||||
|
@ -26,7 +26,7 @@ var _ = Describe("Issue", func() {
|
|||
pkg.AddFile("foo.go", source)
|
||||
ctx := pkg.CreateContext("foo.go")
|
||||
v := testutils.NewMockVisitor()
|
||||
v.Callback = func(n ast.Node, ctx *gas.Context) bool {
|
||||
v.Callback = func(n ast.Node, ctx *gosec.Context) bool {
|
||||
if node, ok := n.(*ast.BasicLit); ok {
|
||||
target = node
|
||||
return false
|
||||
|
@ -37,7 +37,7 @@ var _ = Describe("Issue", func() {
|
|||
ast.Walk(v, ctx.Root)
|
||||
Expect(target).ShouldNot(BeNil())
|
||||
|
||||
issue := gas.NewIssue(ctx, target, "TEST", "", gas.High, gas.High)
|
||||
issue := gosec.NewIssue(ctx, target, "TEST", "", gosec.High, gosec.High)
|
||||
Expect(issue).ShouldNot(BeNil())
|
||||
Expect(issue.Code).Should(MatchRegexp(`"bar"`))
|
||||
Expect(issue.Line).Should(Equal("2"))
|
||||
|
@ -58,7 +58,7 @@ var _ = Describe("Issue", func() {
|
|||
source := `package main
|
||||
import "os"
|
||||
func main(){`
|
||||
source += "q := `SELECT * FROM table WHERE` + \n os.Args[1] + `= ?` // nolint: gas\n"
|
||||
source += "q := `SELECT * FROM table WHERE` + \n os.Args[1] + `= ?` // nolint: gosec\n"
|
||||
source += `println(q)}`
|
||||
|
||||
pkg := testutils.NewTestPackage()
|
||||
|
@ -66,7 +66,7 @@ var _ = Describe("Issue", func() {
|
|||
pkg.AddFile("foo.go", source)
|
||||
ctx := pkg.CreateContext("foo.go")
|
||||
v := testutils.NewMockVisitor()
|
||||
v.Callback = func(n ast.Node, ctx *gas.Context) bool {
|
||||
v.Callback = func(n ast.Node, ctx *gosec.Context) bool {
|
||||
if node, ok := n.(*ast.BinaryExpr); ok {
|
||||
target = node
|
||||
}
|
||||
|
@ -77,7 +77,7 @@ var _ = Describe("Issue", func() {
|
|||
Expect(target).ShouldNot(BeNil())
|
||||
|
||||
// Use SQL rule to check binary expr
|
||||
cfg := gas.NewConfig()
|
||||
cfg := gosec.NewConfig()
|
||||
rule, _ := rules.NewSQLStrConcat("TEST", cfg)
|
||||
issue, err := rule.Match(target, ctx)
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
|
|
|
@ -22,7 +22,7 @@ import (
|
|||
"io"
|
||||
plainTemplate "text/template"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
"github.com/securego/gosec"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
|
@ -58,13 +58,13 @@ Summary:
|
|||
`
|
||||
|
||||
type reportInfo struct {
|
||||
Issues []*gas.Issue
|
||||
Stats *gas.Metrics
|
||||
Issues []*gosec.Issue
|
||||
Stats *gosec.Metrics
|
||||
}
|
||||
|
||||
// CreateReport generates a report based for the supplied issues and metrics given
|
||||
// the specified format. The formats currently accepted are: json, csv, html and text.
|
||||
func CreateReport(w io.Writer, format string, issues []*gas.Issue, metrics *gas.Metrics) error {
|
||||
func CreateReport(w io.Writer, format string, issues []*gosec.Issue, metrics *gosec.Metrics) error {
|
||||
data := &reportInfo{
|
||||
Issues: issues,
|
||||
Stats: metrics,
|
||||
|
@ -150,7 +150,7 @@ func reportJUnitXML(w io.Writer, data *reportInfo) error {
|
|||
}
|
||||
|
||||
func reportFromPlaintextTemplate(w io.Writer, reportTemplate string, data *reportInfo) error {
|
||||
t, e := plainTemplate.New("gas").Parse(reportTemplate)
|
||||
t, e := plainTemplate.New("gosec").Parse(reportTemplate)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
|
@ -159,7 +159,7 @@ func reportFromPlaintextTemplate(w io.Writer, reportTemplate string, data *repor
|
|||
}
|
||||
|
||||
func reportFromHTMLTemplate(w io.Writer, reportTemplate string, data *reportInfo) error {
|
||||
t, e := htmlTemplate.New("gas").Parse(reportTemplate)
|
||||
t, e := htmlTemplate.New("gosec").Parse(reportTemplate)
|
||||
if e != nil {
|
||||
return e
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@ import (
|
|||
htmlLib "html"
|
||||
"strconv"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
"github.com/securego/gosec"
|
||||
)
|
||||
|
||||
type junitXMLReport struct {
|
||||
|
@ -32,26 +32,26 @@ type failure struct {
|
|||
Text string `xml:",innerxml"`
|
||||
}
|
||||
|
||||
func generatePlaintext(issue *gas.Issue) string {
|
||||
func generatePlaintext(issue *gosec.Issue) string {
|
||||
return "Results:\n" +
|
||||
"[" + issue.File + ":" + issue.Line + "] - " +
|
||||
issue.What + " (Confidence: " + strconv.Itoa(int(issue.Confidence)) +
|
||||
", Severity: " + strconv.Itoa(int(issue.Severity)) + ")\n" + "> " + htmlLib.EscapeString(issue.Code)
|
||||
}
|
||||
|
||||
func groupDataByRules(data *reportInfo) map[string][]*gas.Issue {
|
||||
groupedData := make(map[string][]*gas.Issue)
|
||||
func groupDataByRules(data *reportInfo) map[string][]*gosec.Issue {
|
||||
groupedData := make(map[string][]*gosec.Issue)
|
||||
for _, issue := range data.Issues {
|
||||
if _, ok := groupedData[issue.What]; ok {
|
||||
groupedData[issue.What] = append(groupedData[issue.What], issue)
|
||||
} else {
|
||||
groupedData[issue.What] = []*gas.Issue{issue}
|
||||
groupedData[issue.What] = []*gosec.Issue{issue}
|
||||
}
|
||||
}
|
||||
return groupedData
|
||||
}
|
||||
|
||||
func createJUnitXMLStruct(groupedData map[string][]*gas.Issue) junitXMLReport {
|
||||
func createJUnitXMLStruct(groupedData map[string][]*gosec.Issue) junitXMLReport {
|
||||
var xmlReport junitXMLReport
|
||||
for what, issues := range groupedData {
|
||||
testsuite := testsuite{
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package gas
|
||||
package gosec
|
||||
|
||||
import "go/ast"
|
||||
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
package gas_test
|
||||
package gosec_test
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
"github.com/GoASTScanner/gas/testutils"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/securego/gosec"
|
||||
"github.com/securego/gosec/testutils"
|
||||
)
|
||||
|
||||
var _ = Describe("Resolve ast node to concrete value", func() {
|
||||
|
@ -19,7 +19,7 @@ var _ = Describe("Resolve ast node to concrete value", func() {
|
|||
pkg.AddFile("foo.go", `package main; const foo = "bar"; func main(){}`)
|
||||
ctx := pkg.CreateContext("foo.go")
|
||||
v := testutils.NewMockVisitor()
|
||||
v.Callback = func(n ast.Node, ctx *gas.Context) bool {
|
||||
v.Callback = func(n ast.Node, ctx *gosec.Context) bool {
|
||||
if node, ok := n.(*ast.BasicLit); ok {
|
||||
basicLiteral = node
|
||||
return false
|
||||
|
@ -29,7 +29,7 @@ var _ = Describe("Resolve ast node to concrete value", func() {
|
|||
v.Context = ctx
|
||||
ast.Walk(v, ctx.Root)
|
||||
Expect(basicLiteral).ShouldNot(BeNil())
|
||||
Expect(gas.TryResolve(basicLiteral, ctx)).Should(BeTrue())
|
||||
Expect(gosec.TryResolve(basicLiteral, ctx)).Should(BeTrue())
|
||||
})
|
||||
|
||||
It("should successfully resolve identifier", func() {
|
||||
|
@ -39,7 +39,7 @@ var _ = Describe("Resolve ast node to concrete value", func() {
|
|||
pkg.AddFile("foo.go", `package main; var foo string = "bar"; func main(){}`)
|
||||
ctx := pkg.CreateContext("foo.go")
|
||||
v := testutils.NewMockVisitor()
|
||||
v.Callback = func(n ast.Node, ctx *gas.Context) bool {
|
||||
v.Callback = func(n ast.Node, ctx *gosec.Context) bool {
|
||||
if node, ok := n.(*ast.Ident); ok {
|
||||
ident = node
|
||||
return false
|
||||
|
@ -49,7 +49,7 @@ var _ = Describe("Resolve ast node to concrete value", func() {
|
|||
v.Context = ctx
|
||||
ast.Walk(v, ctx.Root)
|
||||
Expect(ident).ShouldNot(BeNil())
|
||||
Expect(gas.TryResolve(ident, ctx)).Should(BeTrue())
|
||||
Expect(gosec.TryResolve(ident, ctx)).Should(BeTrue())
|
||||
})
|
||||
|
||||
It("should successfully resolve assign statement", func() {
|
||||
|
@ -59,7 +59,7 @@ var _ = Describe("Resolve ast node to concrete value", func() {
|
|||
pkg.AddFile("foo.go", `package main; const x = "bar"; func main(){ y := x; println(y) }`)
|
||||
ctx := pkg.CreateContext("foo.go")
|
||||
v := testutils.NewMockVisitor()
|
||||
v.Callback = func(n ast.Node, ctx *gas.Context) bool {
|
||||
v.Callback = func(n ast.Node, ctx *gosec.Context) bool {
|
||||
if node, ok := n.(*ast.AssignStmt); ok {
|
||||
if id, ok := node.Lhs[0].(*ast.Ident); ok && id.Name == "y" {
|
||||
assign = node
|
||||
|
@ -70,7 +70,7 @@ var _ = Describe("Resolve ast node to concrete value", func() {
|
|||
v.Context = ctx
|
||||
ast.Walk(v, ctx.Root)
|
||||
Expect(assign).ShouldNot(BeNil())
|
||||
Expect(gas.TryResolve(assign, ctx)).Should(BeTrue())
|
||||
Expect(gosec.TryResolve(assign, ctx)).Should(BeTrue())
|
||||
})
|
||||
|
||||
It("should successfully resolve a binary statement", func() {
|
||||
|
@ -80,7 +80,7 @@ var _ = Describe("Resolve ast node to concrete value", func() {
|
|||
pkg.AddFile("foo.go", `package main; const (x = "bar"; y = "baz"); func main(){ z := x + y; println(z) }`)
|
||||
ctx := pkg.CreateContext("foo.go")
|
||||
v := testutils.NewMockVisitor()
|
||||
v.Callback = func(n ast.Node, ctx *gas.Context) bool {
|
||||
v.Callback = func(n ast.Node, ctx *gosec.Context) bool {
|
||||
if node, ok := n.(*ast.BinaryExpr); ok {
|
||||
target = node
|
||||
}
|
||||
|
@ -89,7 +89,7 @@ var _ = Describe("Resolve ast node to concrete value", func() {
|
|||
v.Context = ctx
|
||||
ast.Walk(v, ctx.Root)
|
||||
Expect(target).ShouldNot(BeNil())
|
||||
Expect(gas.TryResolve(target, ctx)).Should(BeTrue())
|
||||
Expect(gosec.TryResolve(target, ctx)).Should(BeTrue())
|
||||
})
|
||||
|
||||
// TODO: It should resolve call expressions
|
||||
|
|
4
rule.go
4
rule.go
|
@ -10,14 +10,14 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package gas
|
||||
package gosec
|
||||
|
||||
import (
|
||||
"go/ast"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// The Rule interface used by all rules supported by GAS.
|
||||
// The Rule interface used by all rules supported by gosec.
|
||||
type Rule interface {
|
||||
ID() string
|
||||
Match(ast.Node, *Context) (*Issue, error)
|
||||
|
|
28
rule_test.go
28
rule_test.go
|
@ -1,25 +1,25 @@
|
|||
package gas_test
|
||||
package gosec_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/securego/gosec"
|
||||
)
|
||||
|
||||
type mockrule struct {
|
||||
issue *gas.Issue
|
||||
issue *gosec.Issue
|
||||
err error
|
||||
callback func(n ast.Node, ctx *gas.Context) bool
|
||||
callback func(n ast.Node, ctx *gosec.Context) bool
|
||||
}
|
||||
|
||||
func (m *mockrule) ID() string {
|
||||
return "MOCK"
|
||||
}
|
||||
|
||||
func (m *mockrule) Match(n ast.Node, ctx *gas.Context) (*gas.Issue, error) {
|
||||
func (m *mockrule) Match(n ast.Node, ctx *gosec.Context) (*gosec.Issue, error) {
|
||||
if m.callback(n, ctx) {
|
||||
return m.issue, nil
|
||||
}
|
||||
|
@ -31,29 +31,29 @@ var _ = Describe("Rule", func() {
|
|||
Context("when using a ruleset", func() {
|
||||
|
||||
var (
|
||||
ruleset gas.RuleSet
|
||||
dummyErrorRule gas.Rule
|
||||
dummyIssueRule gas.Rule
|
||||
ruleset gosec.RuleSet
|
||||
dummyErrorRule gosec.Rule
|
||||
dummyIssueRule gosec.Rule
|
||||
)
|
||||
|
||||
JustBeforeEach(func() {
|
||||
ruleset = gas.NewRuleSet()
|
||||
ruleset = gosec.NewRuleSet()
|
||||
dummyErrorRule = &mockrule{
|
||||
issue: nil,
|
||||
err: fmt.Errorf("An unexpected error occurred"),
|
||||
callback: func(n ast.Node, ctx *gas.Context) bool { return false },
|
||||
callback: func(n ast.Node, ctx *gosec.Context) bool { return false },
|
||||
}
|
||||
dummyIssueRule = &mockrule{
|
||||
issue: &gas.Issue{
|
||||
Severity: gas.High,
|
||||
Confidence: gas.High,
|
||||
issue: &gosec.Issue{
|
||||
Severity: gosec.High,
|
||||
Confidence: gosec.High,
|
||||
What: `Some explanation of the thing`,
|
||||
File: "main.go",
|
||||
Code: `#include <stdio.h> int main(){ puts("hello world"); }`,
|
||||
Line: "42",
|
||||
},
|
||||
err: nil,
|
||||
callback: func(n ast.Node, ctx *gas.Context) bool { return true },
|
||||
callback: func(n ast.Node, ctx *gosec.Context) bool { return true },
|
||||
}
|
||||
})
|
||||
It("should be possible to register a rule for multiple ast.Node", func() {
|
||||
|
|
60
rules/archive.go
Normal file
60
rules/archive.go
Normal 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)}
|
||||
}
|
18
rules/big.go
18
rules/big.go
|
@ -17,11 +17,11 @@ package rules
|
|||
import (
|
||||
"go/ast"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
"github.com/securego/gosec"
|
||||
)
|
||||
|
||||
type usingBigExp struct {
|
||||
gas.MetaData
|
||||
gosec.MetaData
|
||||
pkg string
|
||||
calls []string
|
||||
}
|
||||
|
@ -30,23 +30,23 @@ func (r *usingBigExp) ID() string {
|
|||
return r.MetaData.ID
|
||||
}
|
||||
|
||||
func (r *usingBigExp) Match(n ast.Node, c *gas.Context) (gi *gas.Issue, err error) {
|
||||
if _, matched := gas.MatchCallByType(n, c, r.pkg, r.calls...); matched {
|
||||
return gas.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil
|
||||
func (r *usingBigExp) Match(n ast.Node, c *gosec.Context) (gi *gosec.Issue, err error) {
|
||||
if _, matched := gosec.MatchCallByType(n, c, r.pkg, r.calls...); matched {
|
||||
return gosec.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// NewUsingBigExp detects issues with modulus == 0 for Bignum
|
||||
func NewUsingBigExp(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
func NewUsingBigExp(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
|
||||
return &usingBigExp{
|
||||
pkg: "*math/big.Int",
|
||||
calls: []string{"Exp"},
|
||||
MetaData: gas.MetaData{
|
||||
MetaData: gosec.MetaData{
|
||||
ID: id,
|
||||
What: "Use of math/big.Int.Exp function should be audited for modulus == 0",
|
||||
Severity: gas.Low,
|
||||
Confidence: gas.High,
|
||||
Severity: gosec.Low,
|
||||
Confidence: gosec.High,
|
||||
},
|
||||
}, []ast.Node{(*ast.CallExpr)(nil)}
|
||||
}
|
||||
|
|
|
@ -18,13 +18,13 @@ import (
|
|||
"go/ast"
|
||||
"regexp"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
"github.com/securego/gosec"
|
||||
)
|
||||
|
||||
// Looks for net.Listen("0.0.0.0") or net.Listen(":8080")
|
||||
type bindsToAllNetworkInterfaces struct {
|
||||
gas.MetaData
|
||||
calls gas.CallList
|
||||
gosec.MetaData
|
||||
calls gosec.CallList
|
||||
pattern *regexp.Regexp
|
||||
}
|
||||
|
||||
|
@ -32,14 +32,14 @@ func (r *bindsToAllNetworkInterfaces) ID() string {
|
|||
return r.MetaData.ID
|
||||
}
|
||||
|
||||
func (r *bindsToAllNetworkInterfaces) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
||||
func (r *bindsToAllNetworkInterfaces) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) {
|
||||
callExpr := r.calls.ContainsCallExpr(n, c)
|
||||
if callExpr == nil {
|
||||
return nil, nil
|
||||
}
|
||||
if arg, err := gas.GetString(callExpr.Args[1]); err == nil {
|
||||
if arg, err := gosec.GetString(callExpr.Args[1]); err == nil {
|
||||
if r.pattern.MatchString(arg) {
|
||||
return gas.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil
|
||||
return gosec.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
|
@ -47,17 +47,17 @@ func (r *bindsToAllNetworkInterfaces) Match(n ast.Node, c *gas.Context) (*gas.Is
|
|||
|
||||
// NewBindsToAllNetworkInterfaces detects socket connections that are setup to
|
||||
// listen on all network interfaces.
|
||||
func NewBindsToAllNetworkInterfaces(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
calls := gas.NewCallList()
|
||||
func NewBindsToAllNetworkInterfaces(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
|
||||
calls := gosec.NewCallList()
|
||||
calls.Add("net", "Listen")
|
||||
calls.Add("crypto/tls", "Listen")
|
||||
return &bindsToAllNetworkInterfaces{
|
||||
calls: calls,
|
||||
pattern: regexp.MustCompile(`^(0.0.0.0|:).*$`),
|
||||
MetaData: gas.MetaData{
|
||||
MetaData: gosec.MetaData{
|
||||
ID: id,
|
||||
Severity: gas.Medium,
|
||||
Confidence: gas.High,
|
||||
Severity: gosec.Medium,
|
||||
Confidence: gosec.High,
|
||||
What: "Binds to all network interfaces",
|
||||
},
|
||||
}, []ast.Node{(*ast.CallExpr)(nil)}
|
||||
|
|
|
@ -18,11 +18,11 @@ import (
|
|||
"go/ast"
|
||||
"strings"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
"github.com/securego/gosec"
|
||||
)
|
||||
|
||||
type blacklistedImport struct {
|
||||
gas.MetaData
|
||||
gosec.MetaData
|
||||
Blacklisted map[string]string
|
||||
}
|
||||
|
||||
|
@ -36,10 +36,10 @@ func (r *blacklistedImport) ID() string {
|
|||
return r.MetaData.ID
|
||||
}
|
||||
|
||||
func (r *blacklistedImport) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
||||
func (r *blacklistedImport) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) {
|
||||
if node, ok := n.(*ast.ImportSpec); ok {
|
||||
if description, ok := r.Blacklisted[unquote(node.Path.Value)]; ok {
|
||||
return gas.NewIssue(c, node, r.ID(), description, r.Severity, r.Confidence), nil
|
||||
return gosec.NewIssue(c, node, r.ID(), description, r.Severity, r.Confidence), nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
|
@ -47,40 +47,40 @@ func (r *blacklistedImport) Match(n ast.Node, c *gas.Context) (*gas.Issue, error
|
|||
|
||||
// NewBlacklistedImports reports when a blacklisted import is being used.
|
||||
// Typically when a deprecated technology is being used.
|
||||
func NewBlacklistedImports(id string, conf gas.Config, blacklist map[string]string) (gas.Rule, []ast.Node) {
|
||||
func NewBlacklistedImports(id string, conf gosec.Config, blacklist map[string]string) (gosec.Rule, []ast.Node) {
|
||||
return &blacklistedImport{
|
||||
MetaData: gas.MetaData{
|
||||
MetaData: gosec.MetaData{
|
||||
ID: id,
|
||||
Severity: gas.Medium,
|
||||
Confidence: gas.High,
|
||||
Severity: gosec.Medium,
|
||||
Confidence: gosec.High,
|
||||
},
|
||||
Blacklisted: blacklist,
|
||||
}, []ast.Node{(*ast.ImportSpec)(nil)}
|
||||
}
|
||||
|
||||
// NewBlacklistedImportMD5 fails if MD5 is imported
|
||||
func NewBlacklistedImportMD5(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
func NewBlacklistedImportMD5(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
|
||||
return NewBlacklistedImports(id, conf, map[string]string{
|
||||
"crypto/md5": "Blacklisted import crypto/md5: weak cryptographic primitive",
|
||||
})
|
||||
}
|
||||
|
||||
// NewBlacklistedImportDES fails if DES is imported
|
||||
func NewBlacklistedImportDES(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
func NewBlacklistedImportDES(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
|
||||
return NewBlacklistedImports(id, conf, map[string]string{
|
||||
"crypto/des": "Blacklisted import crypto/des: weak cryptographic primitive",
|
||||
})
|
||||
}
|
||||
|
||||
// NewBlacklistedImportRC4 fails if DES is imported
|
||||
func NewBlacklistedImportRC4(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
func NewBlacklistedImportRC4(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
|
||||
return NewBlacklistedImports(id, conf, map[string]string{
|
||||
"crypto/rc4": "Blacklisted import crypto/rc4: weak cryptographic primitive",
|
||||
})
|
||||
}
|
||||
|
||||
// NewBlacklistedImportCGI fails if CGI is imported
|
||||
func NewBlacklistedImportCGI(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
func NewBlacklistedImportCGI(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
|
||||
return NewBlacklistedImports(id, conf, map[string]string{
|
||||
"net/http/cgi": "Blacklisted import net/http/cgi: Go versions < 1.6.3 are vulnerable to Httpoxy attack: (CVE-2016-5386)",
|
||||
})
|
||||
|
|
|
@ -18,19 +18,19 @@ import (
|
|||
"go/ast"
|
||||
"go/types"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
"github.com/securego/gosec"
|
||||
)
|
||||
|
||||
type noErrorCheck struct {
|
||||
gas.MetaData
|
||||
whitelist gas.CallList
|
||||
gosec.MetaData
|
||||
whitelist gosec.CallList
|
||||
}
|
||||
|
||||
func (r *noErrorCheck) ID() string {
|
||||
return r.MetaData.ID
|
||||
}
|
||||
|
||||
func returnsError(callExpr *ast.CallExpr, ctx *gas.Context) int {
|
||||
func returnsError(callExpr *ast.CallExpr, ctx *gosec.Context) int {
|
||||
if tv := ctx.Info.TypeOf(callExpr); tv != nil {
|
||||
switch t := tv.(type) {
|
||||
case *types.Tuple:
|
||||
|
@ -49,7 +49,7 @@ func returnsError(callExpr *ast.CallExpr, ctx *gas.Context) int {
|
|||
return -1
|
||||
}
|
||||
|
||||
func (r *noErrorCheck) Match(n ast.Node, ctx *gas.Context) (*gas.Issue, error) {
|
||||
func (r *noErrorCheck) Match(n ast.Node, ctx *gosec.Context) (*gosec.Issue, error) {
|
||||
switch stmt := n.(type) {
|
||||
case *ast.AssignStmt:
|
||||
for _, expr := range stmt.Rhs {
|
||||
|
@ -59,7 +59,7 @@ func (r *noErrorCheck) Match(n ast.Node, ctx *gas.Context) (*gas.Issue, error) {
|
|||
return nil, nil
|
||||
}
|
||||
if id, ok := stmt.Lhs[pos].(*ast.Ident); ok && id.Name == "_" {
|
||||
return gas.NewIssue(ctx, n, r.ID(), r.What, r.Severity, r.Confidence), nil
|
||||
return gosec.NewIssue(ctx, n, r.ID(), r.What, r.Severity, r.Confidence), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -67,7 +67,7 @@ func (r *noErrorCheck) Match(n ast.Node, ctx *gas.Context) (*gas.Issue, error) {
|
|||
if callExpr, ok := stmt.X.(*ast.CallExpr); ok && r.whitelist.ContainsCallExpr(stmt.X, ctx) == nil {
|
||||
pos := returnsError(callExpr, ctx)
|
||||
if pos >= 0 {
|
||||
return gas.NewIssue(ctx, n, r.ID(), r.What, r.Severity, r.Confidence), nil
|
||||
return gosec.NewIssue(ctx, n, r.ID(), r.What, r.Severity, r.Confidence), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -75,10 +75,10 @@ func (r *noErrorCheck) Match(n ast.Node, ctx *gas.Context) (*gas.Issue, error) {
|
|||
}
|
||||
|
||||
// NewNoErrorCheck detects if the returned error is unchecked
|
||||
func NewNoErrorCheck(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
func NewNoErrorCheck(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
|
||||
// TODO(gm) Come up with sensible defaults here. Or flip it to use a
|
||||
// black list instead.
|
||||
whitelist := gas.NewCallList()
|
||||
whitelist := gosec.NewCallList()
|
||||
whitelist.AddAll("bytes.Buffer", "Write", "WriteByte", "WriteRune", "WriteString")
|
||||
whitelist.AddAll("fmt", "Print", "Printf", "Println", "Fprint", "Fprintf", "Fprintln")
|
||||
whitelist.Add("io.PipeWriter", "CloseWithError")
|
||||
|
@ -91,10 +91,10 @@ func NewNoErrorCheck(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
|||
}
|
||||
}
|
||||
return &noErrorCheck{
|
||||
MetaData: gas.MetaData{
|
||||
MetaData: gosec.MetaData{
|
||||
ID: id,
|
||||
Severity: gas.Low,
|
||||
Confidence: gas.High,
|
||||
Severity: gosec.Low,
|
||||
Confidence: gosec.High,
|
||||
What: "Errors unhandled.",
|
||||
},
|
||||
whitelist: whitelist,
|
||||
|
|
|
@ -19,11 +19,11 @@ import (
|
|||
"go/ast"
|
||||
"strconv"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
"github.com/securego/gosec"
|
||||
)
|
||||
|
||||
type filePermissions struct {
|
||||
gas.MetaData
|
||||
gosec.MetaData
|
||||
mode int64
|
||||
pkg string
|
||||
calls []string
|
||||
|
@ -50,11 +50,11 @@ func getConfiguredMode(conf map[string]interface{}, configKey string, defaultMod
|
|||
return mode
|
||||
}
|
||||
|
||||
func (r *filePermissions) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
||||
if callexpr, matched := gas.MatchCallByPackage(n, c, r.pkg, r.calls...); matched {
|
||||
func (r *filePermissions) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) {
|
||||
if callexpr, matched := gosec.MatchCallByPackage(n, c, r.pkg, r.calls...); matched {
|
||||
modeArg := callexpr.Args[len(callexpr.Args)-1]
|
||||
if mode, err := gas.GetInt(modeArg); err == nil && mode > r.mode {
|
||||
return gas.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil
|
||||
if mode, err := gosec.GetInt(modeArg); err == nil && mode > r.mode {
|
||||
return gosec.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
|
@ -62,16 +62,16 @@ func (r *filePermissions) Match(n ast.Node, c *gas.Context) (*gas.Issue, error)
|
|||
|
||||
// NewFilePerms creates a rule to detect file creation with a more permissive than configured
|
||||
// permission mask.
|
||||
func NewFilePerms(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
func NewFilePerms(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
|
||||
mode := getConfiguredMode(conf, "G302", 0600)
|
||||
return &filePermissions{
|
||||
mode: mode,
|
||||
pkg: "os",
|
||||
calls: []string{"OpenFile", "Chmod"},
|
||||
MetaData: gas.MetaData{
|
||||
MetaData: gosec.MetaData{
|
||||
ID: id,
|
||||
Severity: gas.Medium,
|
||||
Confidence: gas.High,
|
||||
Severity: gosec.Medium,
|
||||
Confidence: gosec.High,
|
||||
What: fmt.Sprintf("Expect file permissions to be %#o or less", mode),
|
||||
},
|
||||
}, []ast.Node{(*ast.CallExpr)(nil)}
|
||||
|
@ -79,16 +79,16 @@ func NewFilePerms(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
|||
|
||||
// NewMkdirPerms creates a rule to detect directory creation with more permissive than
|
||||
// configured permission mask.
|
||||
func NewMkdirPerms(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
func NewMkdirPerms(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
|
||||
mode := getConfiguredMode(conf, "G301", 0750)
|
||||
return &filePermissions{
|
||||
mode: mode,
|
||||
pkg: "os",
|
||||
calls: []string{"Mkdir", "MkdirAll"},
|
||||
MetaData: gas.MetaData{
|
||||
MetaData: gosec.MetaData{
|
||||
ID: id,
|
||||
Severity: gas.Medium,
|
||||
Confidence: gas.High,
|
||||
Severity: gosec.Medium,
|
||||
Confidence: gosec.High,
|
||||
What: fmt.Sprintf("Expect directory permissions to be %#o or less", mode),
|
||||
},
|
||||
}, []ast.Node{(*ast.CallExpr)(nil)}
|
||||
|
|
|
@ -19,12 +19,12 @@ import (
|
|||
"regexp"
|
||||
"strconv"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
"github.com/nbutton23/zxcvbn-go"
|
||||
zxcvbn "github.com/nbutton23/zxcvbn-go"
|
||||
"github.com/securego/gosec"
|
||||
)
|
||||
|
||||
type credentials struct {
|
||||
gas.MetaData
|
||||
gosec.MetaData
|
||||
pattern *regexp.Regexp
|
||||
entropyThreshold float64
|
||||
perCharThreshold float64
|
||||
|
@ -52,7 +52,7 @@ func (r *credentials) isHighEntropyString(str string) bool {
|
|||
entropyPerChar >= r.perCharThreshold))
|
||||
}
|
||||
|
||||
func (r *credentials) Match(n ast.Node, ctx *gas.Context) (*gas.Issue, error) {
|
||||
func (r *credentials) Match(n ast.Node, ctx *gosec.Context) (*gosec.Issue, error) {
|
||||
switch node := n.(type) {
|
||||
case *ast.AssignStmt:
|
||||
return r.matchAssign(node, ctx)
|
||||
|
@ -62,14 +62,14 @@ func (r *credentials) Match(n ast.Node, ctx *gas.Context) (*gas.Issue, error) {
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
func (r *credentials) matchAssign(assign *ast.AssignStmt, ctx *gas.Context) (*gas.Issue, error) {
|
||||
func (r *credentials) matchAssign(assign *ast.AssignStmt, ctx *gosec.Context) (*gosec.Issue, error) {
|
||||
for _, i := range assign.Lhs {
|
||||
if ident, ok := i.(*ast.Ident); ok {
|
||||
if r.pattern.MatchString(ident.Name) {
|
||||
for _, e := range assign.Rhs {
|
||||
if val, err := gas.GetString(e); err == nil {
|
||||
if val, err := gosec.GetString(e); err == nil {
|
||||
if r.ignoreEntropy || (!r.ignoreEntropy && r.isHighEntropyString(val)) {
|
||||
return gas.NewIssue(ctx, assign, r.ID(), r.What, r.Severity, r.Confidence), nil
|
||||
return gosec.NewIssue(ctx, assign, r.ID(), r.What, r.Severity, r.Confidence), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -79,16 +79,16 @@ func (r *credentials) matchAssign(assign *ast.AssignStmt, ctx *gas.Context) (*ga
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
func (r *credentials) matchValueSpec(valueSpec *ast.ValueSpec, ctx *gas.Context) (*gas.Issue, error) {
|
||||
func (r *credentials) matchValueSpec(valueSpec *ast.ValueSpec, ctx *gosec.Context) (*gosec.Issue, error) {
|
||||
for index, ident := range valueSpec.Names {
|
||||
if r.pattern.MatchString(ident.Name) && valueSpec.Values != nil {
|
||||
// const foo, bar = "same value"
|
||||
if len(valueSpec.Values) <= index {
|
||||
index = len(valueSpec.Values) - 1
|
||||
}
|
||||
if val, err := gas.GetString(valueSpec.Values[index]); err == nil {
|
||||
if val, err := gosec.GetString(valueSpec.Values[index]); err == nil {
|
||||
if r.ignoreEntropy || (!r.ignoreEntropy && r.isHighEntropyString(val)) {
|
||||
return gas.NewIssue(ctx, valueSpec, r.ID(), r.What, r.Severity, r.Confidence), nil
|
||||
return gosec.NewIssue(ctx, valueSpec, r.ID(), r.What, r.Severity, r.Confidence), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -98,7 +98,7 @@ func (r *credentials) matchValueSpec(valueSpec *ast.ValueSpec, ctx *gas.Context)
|
|||
|
||||
// NewHardcodedCredentials attempts to find high entropy string constants being
|
||||
// assigned to variables that appear to be related to credentials.
|
||||
func NewHardcodedCredentials(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
func NewHardcodedCredentials(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
|
||||
pattern := `(?i)passwd|pass|password|pwd|secret|token`
|
||||
entropyThreshold := 80.0
|
||||
perCharThreshold := 3.0
|
||||
|
@ -137,11 +137,11 @@ func NewHardcodedCredentials(id string, conf gas.Config) (gas.Rule, []ast.Node)
|
|||
perCharThreshold: perCharThreshold,
|
||||
ignoreEntropy: ignoreEntropy,
|
||||
truncate: truncateString,
|
||||
MetaData: gas.MetaData{
|
||||
MetaData: gosec.MetaData{
|
||||
ID: id,
|
||||
What: "Potential hardcoded credentials",
|
||||
Confidence: gas.Low,
|
||||
Severity: gas.High,
|
||||
Confidence: gosec.Low,
|
||||
Severity: gosec.High,
|
||||
},
|
||||
}, []ast.Node{(*ast.AssignStmt)(nil), (*ast.ValueSpec)(nil)}
|
||||
}
|
||||
|
|
|
@ -17,11 +17,11 @@ package rules
|
|||
import (
|
||||
"go/ast"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
"github.com/securego/gosec"
|
||||
)
|
||||
|
||||
type weakRand struct {
|
||||
gas.MetaData
|
||||
gosec.MetaData
|
||||
funcNames []string
|
||||
packagePath string
|
||||
}
|
||||
|
@ -30,10 +30,10 @@ func (w *weakRand) ID() string {
|
|||
return w.MetaData.ID
|
||||
}
|
||||
|
||||
func (w *weakRand) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
||||
func (w *weakRand) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) {
|
||||
for _, funcName := range w.funcNames {
|
||||
if _, matched := gas.MatchCallByPackage(n, c, w.packagePath, funcName); matched {
|
||||
return gas.NewIssue(c, n, w.ID(), w.What, w.Severity, w.Confidence), nil
|
||||
if _, matched := gosec.MatchCallByPackage(n, c, w.packagePath, funcName); matched {
|
||||
return gosec.NewIssue(c, n, w.ID(), w.What, w.Severity, w.Confidence), nil
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -41,14 +41,14 @@ func (w *weakRand) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
|||
}
|
||||
|
||||
// NewWeakRandCheck detects the use of random number generator that isn't cryptographically secure
|
||||
func NewWeakRandCheck(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
func NewWeakRandCheck(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
|
||||
return &weakRand{
|
||||
funcNames: []string{"Read", "Int"},
|
||||
packagePath: "math/rand",
|
||||
MetaData: gas.MetaData{
|
||||
MetaData: gosec.MetaData{
|
||||
ID: id,
|
||||
Severity: gas.High,
|
||||
Confidence: gas.Medium,
|
||||
Severity: gosec.High,
|
||||
Confidence: gosec.Medium,
|
||||
What: "Use of weak random number generator (math/rand instead of crypto/rand)",
|
||||
},
|
||||
}, []ast.Node{(*ast.CallExpr)(nil)}
|
||||
|
|
|
@ -18,12 +18,12 @@ import (
|
|||
"go/ast"
|
||||
"go/types"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
"github.com/securego/gosec"
|
||||
)
|
||||
|
||||
type readfile struct {
|
||||
gas.MetaData
|
||||
gas.CallList
|
||||
gosec.MetaData
|
||||
gosec.CallList
|
||||
}
|
||||
|
||||
// ID returns the identifier for this rule
|
||||
|
@ -32,13 +32,13 @@ func (r *readfile) ID() string {
|
|||
}
|
||||
|
||||
// Match inspects AST nodes to determine if the match the methods `os.Open` or `ioutil.ReadFile`
|
||||
func (r *readfile) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
||||
func (r *readfile) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) {
|
||||
if node := r.ContainsCallExpr(n, c); node != nil {
|
||||
for _, arg := range node.Args {
|
||||
if ident, ok := arg.(*ast.Ident); ok {
|
||||
obj := c.Info.ObjectOf(ident)
|
||||
if _, ok := obj.(*types.Var); ok && !gas.TryResolve(ident, c) {
|
||||
return gas.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil
|
||||
if _, ok := obj.(*types.Var); ok && !gosec.TryResolve(ident, c) {
|
||||
return gosec.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -47,14 +47,14 @@ func (r *readfile) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
|||
}
|
||||
|
||||
// NewReadFile detects cases where we read files
|
||||
func NewReadFile(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
func NewReadFile(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
|
||||
rule := &readfile{
|
||||
CallList: gas.NewCallList(),
|
||||
MetaData: gas.MetaData{
|
||||
CallList: gosec.NewCallList(),
|
||||
MetaData: gosec.MetaData{
|
||||
ID: id,
|
||||
What: "Potential file inclusion via variable",
|
||||
Severity: gas.Medium,
|
||||
Confidence: gas.High,
|
||||
Severity: gosec.Medium,
|
||||
Confidence: gosec.High,
|
||||
},
|
||||
}
|
||||
rule.Add("io/ioutil", "ReadFile")
|
||||
|
|
22
rules/rsa.go
22
rules/rsa.go
|
@ -18,12 +18,12 @@ import (
|
|||
"fmt"
|
||||
"go/ast"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
"github.com/securego/gosec"
|
||||
)
|
||||
|
||||
type weakKeyStrength struct {
|
||||
gas.MetaData
|
||||
calls gas.CallList
|
||||
gosec.MetaData
|
||||
calls gosec.CallList
|
||||
bits int
|
||||
}
|
||||
|
||||
|
@ -31,27 +31,27 @@ func (w *weakKeyStrength) ID() string {
|
|||
return w.MetaData.ID
|
||||
}
|
||||
|
||||
func (w *weakKeyStrength) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
||||
func (w *weakKeyStrength) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) {
|
||||
if callExpr := w.calls.ContainsCallExpr(n, c); callExpr != nil {
|
||||
if bits, err := gas.GetInt(callExpr.Args[1]); err == nil && bits < (int64)(w.bits) {
|
||||
return gas.NewIssue(c, n, w.ID(), w.What, w.Severity, w.Confidence), nil
|
||||
if bits, err := gosec.GetInt(callExpr.Args[1]); err == nil && bits < (int64)(w.bits) {
|
||||
return gosec.NewIssue(c, n, w.ID(), w.What, w.Severity, w.Confidence), nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// NewWeakKeyStrength builds a rule that detects RSA keys < 2048 bits
|
||||
func NewWeakKeyStrength(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
calls := gas.NewCallList()
|
||||
func NewWeakKeyStrength(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
|
||||
calls := gosec.NewCallList()
|
||||
calls.Add("crypto/rsa", "GenerateKey")
|
||||
bits := 2048
|
||||
return &weakKeyStrength{
|
||||
calls: calls,
|
||||
bits: bits,
|
||||
MetaData: gas.MetaData{
|
||||
MetaData: gosec.MetaData{
|
||||
ID: id,
|
||||
Severity: gas.Medium,
|
||||
Confidence: gas.High,
|
||||
Severity: gosec.Medium,
|
||||
Confidence: gosec.High,
|
||||
What: fmt.Sprintf("RSA keys should be at least %d bits", bits),
|
||||
},
|
||||
}, []ast.Node{(*ast.CallExpr)(nil)}
|
||||
|
|
|
@ -14,24 +14,22 @@
|
|||
|
||||
package rules
|
||||
|
||||
import (
|
||||
"github.com/GoASTScanner/gas"
|
||||
)
|
||||
import "github.com/securego/gosec"
|
||||
|
||||
// RuleDefinition contains the description of a rule and a mechanism to
|
||||
// create it.
|
||||
type RuleDefinition struct {
|
||||
ID string
|
||||
Description string
|
||||
Create gas.RuleBuilder
|
||||
Create gosec.RuleBuilder
|
||||
}
|
||||
|
||||
// RuleList is a mapping of rule ID's to rule definitions
|
||||
type RuleList map[string]RuleDefinition
|
||||
|
||||
// Builders returns all the create methods for a given rule list
|
||||
func (rl RuleList) Builders() map[string]gas.RuleBuilder {
|
||||
builders := make(map[string]gas.RuleBuilder)
|
||||
func (rl RuleList) Builders() map[string]gosec.RuleBuilder {
|
||||
builders := make(map[string]gosec.RuleBuilder)
|
||||
for _, def := range rl {
|
||||
builders[def.ID] = def.Create
|
||||
}
|
||||
|
@ -79,6 +77,7 @@ func Generate(filters ...RuleFilter) RuleList {
|
|||
{"G302", "Poor file permisions used when creation file or using chmod", NewFilePerms},
|
||||
{"G303", "Creating tempfile using a predictable path", NewBadTempFile},
|
||||
{"G304", "File path provided as taint input", NewReadFile},
|
||||
{"G305", "File path traversal when extracting zip archive", NewArchive},
|
||||
|
||||
// crypto
|
||||
{"G401", "Detect the usage of DES, RC4, or MD5", NewUsesWeakCryptography},
|
||||
|
|
|
@ -4,28 +4,27 @@ import (
|
|||
"fmt"
|
||||
"log"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
|
||||
"github.com/GoASTScanner/gas/rules"
|
||||
"github.com/GoASTScanner/gas/testutils"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/securego/gosec"
|
||||
"github.com/securego/gosec/rules"
|
||||
"github.com/securego/gosec/testutils"
|
||||
)
|
||||
|
||||
var _ = Describe("gas rules", func() {
|
||||
var _ = Describe("gosec rules", func() {
|
||||
|
||||
var (
|
||||
logger *log.Logger
|
||||
config gas.Config
|
||||
analyzer *gas.Analyzer
|
||||
config gosec.Config
|
||||
analyzer *gosec.Analyzer
|
||||
runner func(string, []testutils.CodeSample)
|
||||
buildTags []string
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
logger, _ = testutils.NewLogger()
|
||||
config = gas.NewConfig()
|
||||
analyzer = gas.NewAnalyzer(config, logger)
|
||||
config = gosec.NewConfig()
|
||||
analyzer = gosec.NewAnalyzer(config, logger)
|
||||
runner = func(rule string, samples []testutils.CodeSample) {
|
||||
analyzer.LoadRules(rules.Generate(rules.NewRuleFilter(false, rule)).Builders())
|
||||
for n, sample := range samples {
|
||||
|
@ -103,6 +102,10 @@ var _ = Describe("gas rules", func() {
|
|||
runner("G304", testutils.SampleCodeG304)
|
||||
})
|
||||
|
||||
It("should detect file path traversal when extracting zip archive", func() {
|
||||
runner("G305", testutils.SampleCodeG305)
|
||||
})
|
||||
|
||||
It("should detect weak crypto algorithms", func() {
|
||||
runner("G401", testutils.SampleCodeG401)
|
||||
})
|
||||
|
|
36
rules/sql.go
36
rules/sql.go
|
@ -18,11 +18,11 @@ import (
|
|||
"go/ast"
|
||||
"regexp"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
"github.com/securego/gosec"
|
||||
)
|
||||
|
||||
type sqlStatement struct {
|
||||
gas.MetaData
|
||||
gosec.MetaData
|
||||
|
||||
// Contains a list of patterns which must all match for the rule to match.
|
||||
patterns []*regexp.Regexp
|
||||
|
@ -59,10 +59,10 @@ func (s *sqlStrConcat) checkObject(n *ast.Ident) bool {
|
|||
}
|
||||
|
||||
// Look for "SELECT * FROM table WHERE " + " ' OR 1=1"
|
||||
func (s *sqlStrConcat) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
||||
func (s *sqlStrConcat) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) {
|
||||
if node, ok := n.(*ast.BinaryExpr); ok {
|
||||
if start, ok := node.X.(*ast.BasicLit); ok {
|
||||
if str, e := gas.GetString(start); e == nil {
|
||||
if str, e := gosec.GetString(start); e == nil {
|
||||
if !s.MatchPatterns(str) {
|
||||
return nil, nil
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ func (s *sqlStrConcat) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
|||
if second, ok := node.Y.(*ast.Ident); ok && s.checkObject(second) {
|
||||
return nil, nil
|
||||
}
|
||||
return gas.NewIssue(c, n, s.ID(), s.What, s.Severity, s.Confidence), nil
|
||||
return gosec.NewIssue(c, n, s.ID(), s.What, s.Severity, s.Confidence), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -80,16 +80,16 @@ func (s *sqlStrConcat) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
|||
}
|
||||
|
||||
// NewSQLStrConcat looks for cases where we are building SQL strings via concatenation
|
||||
func NewSQLStrConcat(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
func NewSQLStrConcat(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
|
||||
return &sqlStrConcat{
|
||||
sqlStatement: sqlStatement{
|
||||
patterns: []*regexp.Regexp{
|
||||
regexp.MustCompile(`(?)(SELECT|DELETE|INSERT|UPDATE|INTO|FROM|WHERE) `),
|
||||
},
|
||||
MetaData: gas.MetaData{
|
||||
MetaData: gosec.MetaData{
|
||||
ID: id,
|
||||
Severity: gas.Medium,
|
||||
Confidence: gas.High,
|
||||
Severity: gosec.Medium,
|
||||
Confidence: gosec.High,
|
||||
What: "SQL string concatenation",
|
||||
},
|
||||
},
|
||||
|
@ -98,34 +98,34 @@ func NewSQLStrConcat(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
|||
|
||||
type sqlStrFormat struct {
|
||||
sqlStatement
|
||||
calls gas.CallList
|
||||
calls gosec.CallList
|
||||
}
|
||||
|
||||
// Looks for "fmt.Sprintf("SELECT * FROM foo where '%s', userInput)"
|
||||
func (s *sqlStrFormat) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
||||
func (s *sqlStrFormat) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) {
|
||||
|
||||
// TODO(gm) improve confidence if database/sql is being used
|
||||
if node := s.calls.ContainsCallExpr(n, c); node != nil {
|
||||
if arg, e := gas.GetString(node.Args[0]); s.MatchPatterns(arg) && e == nil {
|
||||
return gas.NewIssue(c, n, s.ID(), s.What, s.Severity, s.Confidence), nil
|
||||
if arg, e := gosec.GetString(node.Args[0]); s.MatchPatterns(arg) && e == nil {
|
||||
return gosec.NewIssue(c, n, s.ID(), s.What, s.Severity, s.Confidence), nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// NewSQLStrFormat looks for cases where we're building SQL query strings using format strings
|
||||
func NewSQLStrFormat(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
func NewSQLStrFormat(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
|
||||
rule := &sqlStrFormat{
|
||||
calls: gas.NewCallList(),
|
||||
calls: gosec.NewCallList(),
|
||||
sqlStatement: sqlStatement{
|
||||
patterns: []*regexp.Regexp{
|
||||
regexp.MustCompile("(?)(SELECT|DELETE|INSERT|UPDATE|INTO|FROM|WHERE) "),
|
||||
regexp.MustCompile("%[^bdoxXfFp]"),
|
||||
},
|
||||
MetaData: gas.MetaData{
|
||||
MetaData: gosec.MetaData{
|
||||
ID: id,
|
||||
Severity: gas.Medium,
|
||||
Confidence: gas.High,
|
||||
Severity: gosec.Medium,
|
||||
Confidence: gosec.High,
|
||||
What: "SQL string formatting",
|
||||
},
|
||||
},
|
||||
|
|
18
rules/ssh.go
18
rules/ssh.go
|
@ -3,11 +3,11 @@ package rules
|
|||
import (
|
||||
"go/ast"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
"github.com/securego/gosec"
|
||||
)
|
||||
|
||||
type sshHostKey struct {
|
||||
gas.MetaData
|
||||
gosec.MetaData
|
||||
pkg string
|
||||
calls []string
|
||||
}
|
||||
|
@ -16,23 +16,23 @@ func (r *sshHostKey) ID() string {
|
|||
return r.MetaData.ID
|
||||
}
|
||||
|
||||
func (r *sshHostKey) Match(n ast.Node, c *gas.Context) (gi *gas.Issue, err error) {
|
||||
if _, matches := gas.MatchCallByPackage(n, c, r.pkg, r.calls...); matches {
|
||||
return gas.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil
|
||||
func (r *sshHostKey) Match(n ast.Node, c *gosec.Context) (gi *gosec.Issue, err error) {
|
||||
if _, matches := gosec.MatchCallByPackage(n, c, r.pkg, r.calls...); matches {
|
||||
return gosec.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// NewSSHHostKey rule detects the use of insecure ssh HostKeyCallback.
|
||||
func NewSSHHostKey(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
func NewSSHHostKey(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
|
||||
return &sshHostKey{
|
||||
pkg: "golang.org/x/crypto/ssh",
|
||||
calls: []string{"InsecureIgnoreHostKey"},
|
||||
MetaData: gas.MetaData{
|
||||
MetaData: gosec.MetaData{
|
||||
ID: id,
|
||||
What: "Use of ssh InsecureIgnoreHostKey should be audited",
|
||||
Severity: gas.Medium,
|
||||
Confidence: gas.High,
|
||||
Severity: gosec.Medium,
|
||||
Confidence: gosec.High,
|
||||
},
|
||||
}, []ast.Node{(*ast.CallExpr)(nil)}
|
||||
}
|
||||
|
|
|
@ -18,12 +18,12 @@ import (
|
|||
"go/ast"
|
||||
"go/types"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
"github.com/securego/gosec"
|
||||
)
|
||||
|
||||
type subprocess struct {
|
||||
gas.MetaData
|
||||
gas.CallList
|
||||
gosec.MetaData
|
||||
gosec.CallList
|
||||
}
|
||||
|
||||
func (r *subprocess) ID() string {
|
||||
|
@ -39,24 +39,24 @@ func (r *subprocess) ID() string {
|
|||
// is unsafe. For example:
|
||||
//
|
||||
// syscall.Exec("echo", "foobar" + tainted)
|
||||
func (r *subprocess) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
||||
func (r *subprocess) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) {
|
||||
if node := r.ContainsCallExpr(n, c); node != nil {
|
||||
for _, arg := range node.Args {
|
||||
if ident, ok := arg.(*ast.Ident); ok {
|
||||
obj := c.Info.ObjectOf(ident)
|
||||
if _, ok := obj.(*types.Var); ok && !gas.TryResolve(ident, c) {
|
||||
return gas.NewIssue(c, n, r.ID(), "Subprocess launched with variable", gas.Medium, gas.High), nil
|
||||
if _, ok := obj.(*types.Var); ok && !gosec.TryResolve(ident, c) {
|
||||
return gosec.NewIssue(c, n, r.ID(), "Subprocess launched with variable", gosec.Medium, gosec.High), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
return gas.NewIssue(c, n, r.ID(), "Subprocess launching should be audited", gas.Low, gas.High), nil
|
||||
return gosec.NewIssue(c, n, r.ID(), "Subprocess launching should be audited", gosec.Low, gosec.High), nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// NewSubproc detects cases where we are forking out to an external process
|
||||
func NewSubproc(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
rule := &subprocess{gas.MetaData{ID: id}, gas.NewCallList()}
|
||||
func NewSubproc(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
|
||||
rule := &subprocess{gosec.MetaData{ID: id}, gosec.NewCallList()}
|
||||
rule.Add("os/exec", "Command")
|
||||
rule.Add("os/exec", "CommandContext")
|
||||
rule.Add("syscall", "Exec")
|
||||
|
|
|
@ -18,12 +18,12 @@ import (
|
|||
"go/ast"
|
||||
"regexp"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
"github.com/securego/gosec"
|
||||
)
|
||||
|
||||
type badTempFile struct {
|
||||
gas.MetaData
|
||||
calls gas.CallList
|
||||
gosec.MetaData
|
||||
calls gosec.CallList
|
||||
args *regexp.Regexp
|
||||
}
|
||||
|
||||
|
@ -31,27 +31,27 @@ func (t *badTempFile) ID() string {
|
|||
return t.MetaData.ID
|
||||
}
|
||||
|
||||
func (t *badTempFile) Match(n ast.Node, c *gas.Context) (gi *gas.Issue, err error) {
|
||||
func (t *badTempFile) Match(n ast.Node, c *gosec.Context) (gi *gosec.Issue, err error) {
|
||||
if node := t.calls.ContainsCallExpr(n, c); node != nil {
|
||||
if arg, e := gas.GetString(node.Args[0]); t.args.MatchString(arg) && e == nil {
|
||||
return gas.NewIssue(c, n, t.ID(), t.What, t.Severity, t.Confidence), nil
|
||||
if arg, e := gosec.GetString(node.Args[0]); t.args.MatchString(arg) && e == nil {
|
||||
return gosec.NewIssue(c, n, t.ID(), t.What, t.Severity, t.Confidence), nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// NewBadTempFile detects direct writes to predictable path in temporary directory
|
||||
func NewBadTempFile(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
calls := gas.NewCallList()
|
||||
func NewBadTempFile(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
|
||||
calls := gosec.NewCallList()
|
||||
calls.Add("io/ioutil", "WriteFile")
|
||||
calls.Add("os", "Create")
|
||||
return &badTempFile{
|
||||
calls: calls,
|
||||
args: regexp.MustCompile(`^/tmp/.*$|^/var/tmp/.*$`),
|
||||
MetaData: gas.MetaData{
|
||||
MetaData: gosec.MetaData{
|
||||
ID: id,
|
||||
Severity: gas.Medium,
|
||||
Confidence: gas.High,
|
||||
Severity: gosec.Medium,
|
||||
Confidence: gosec.High,
|
||||
What: "File creation in shared tmp directory without using ioutil.Tempfile",
|
||||
},
|
||||
}, []ast.Node{(*ast.CallExpr)(nil)}
|
||||
|
|
|
@ -17,23 +17,23 @@ package rules
|
|||
import (
|
||||
"go/ast"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
"github.com/securego/gosec"
|
||||
)
|
||||
|
||||
type templateCheck struct {
|
||||
gas.MetaData
|
||||
calls gas.CallList
|
||||
gosec.MetaData
|
||||
calls gosec.CallList
|
||||
}
|
||||
|
||||
func (t *templateCheck) ID() string {
|
||||
return t.MetaData.ID
|
||||
}
|
||||
|
||||
func (t *templateCheck) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
||||
func (t *templateCheck) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) {
|
||||
if node := t.calls.ContainsCallExpr(n, c); node != nil {
|
||||
for _, arg := range node.Args {
|
||||
if _, ok := arg.(*ast.BasicLit); !ok { // basic lits are safe
|
||||
return gas.NewIssue(c, n, t.ID(), t.What, t.Severity, t.Confidence), nil
|
||||
return gosec.NewIssue(c, n, t.ID(), t.What, t.Severity, t.Confidence), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -42,19 +42,19 @@ func (t *templateCheck) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
|||
|
||||
// NewTemplateCheck constructs the template check rule. This rule is used to
|
||||
// find use of tempaltes where HTML/JS escaping is not being used
|
||||
func NewTemplateCheck(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
func NewTemplateCheck(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
|
||||
|
||||
calls := gas.NewCallList()
|
||||
calls := gosec.NewCallList()
|
||||
calls.Add("html/template", "HTML")
|
||||
calls.Add("html/template", "HTMLAttr")
|
||||
calls.Add("html/template", "JS")
|
||||
calls.Add("html/template", "URL")
|
||||
return &templateCheck{
|
||||
calls: calls,
|
||||
MetaData: gas.MetaData{
|
||||
MetaData: gosec.MetaData{
|
||||
ID: id,
|
||||
Severity: gas.Medium,
|
||||
Confidence: gas.Low,
|
||||
Severity: gosec.Medium,
|
||||
Confidence: gosec.Low,
|
||||
What: "this method will not auto-escape HTML. Verify data is well formed.",
|
||||
},
|
||||
}, []ast.Node{(*ast.CallExpr)(nil)}
|
||||
|
|
32
rules/tls.go
32
rules/tls.go
|
@ -20,11 +20,11 @@ import (
|
|||
"fmt"
|
||||
"go/ast"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
"github.com/securego/gosec"
|
||||
)
|
||||
|
||||
type insecureConfigTLS struct {
|
||||
gas.MetaData
|
||||
gosec.MetaData
|
||||
MinVersion int16
|
||||
MaxVersion int16
|
||||
requiredType string
|
||||
|
@ -44,14 +44,14 @@ func stringInSlice(a string, list []string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func (t *insecureConfigTLS) processTLSCipherSuites(n ast.Node, c *gas.Context) *gas.Issue {
|
||||
func (t *insecureConfigTLS) processTLSCipherSuites(n ast.Node, c *gosec.Context) *gosec.Issue {
|
||||
|
||||
if ciphers, ok := n.(*ast.CompositeLit); ok {
|
||||
for _, cipher := range ciphers.Elts {
|
||||
if ident, ok := cipher.(*ast.SelectorExpr); ok {
|
||||
if !stringInSlice(ident.Sel.Name, t.goodCiphers) {
|
||||
err := fmt.Sprintf("TLS Bad Cipher Suite: %s", ident.Sel.Name)
|
||||
return gas.NewIssue(c, ident, t.ID(), err, gas.High, gas.High)
|
||||
return gosec.NewIssue(c, ident, t.ID(), err, gosec.High, gosec.High)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -59,46 +59,46 @@ func (t *insecureConfigTLS) processTLSCipherSuites(n ast.Node, c *gas.Context) *
|
|||
return nil
|
||||
}
|
||||
|
||||
func (t *insecureConfigTLS) processTLSConfVal(n *ast.KeyValueExpr, c *gas.Context) *gas.Issue {
|
||||
func (t *insecureConfigTLS) processTLSConfVal(n *ast.KeyValueExpr, c *gosec.Context) *gosec.Issue {
|
||||
if ident, ok := n.Key.(*ast.Ident); ok {
|
||||
switch ident.Name {
|
||||
|
||||
case "InsecureSkipVerify":
|
||||
if node, ok := n.Value.(*ast.Ident); ok {
|
||||
if node.Name != "false" {
|
||||
return gas.NewIssue(c, n, t.ID(), "TLS InsecureSkipVerify set true.", gas.High, gas.High)
|
||||
return gosec.NewIssue(c, n, t.ID(), "TLS InsecureSkipVerify set true.", gosec.High, gosec.High)
|
||||
}
|
||||
} else {
|
||||
// TODO(tk): symbol tab look up to get the actual value
|
||||
return gas.NewIssue(c, n, t.ID(), "TLS InsecureSkipVerify may be true.", gas.High, gas.Low)
|
||||
return gosec.NewIssue(c, n, t.ID(), "TLS InsecureSkipVerify may be true.", gosec.High, gosec.Low)
|
||||
}
|
||||
|
||||
case "PreferServerCipherSuites":
|
||||
if node, ok := n.Value.(*ast.Ident); ok {
|
||||
if node.Name == "false" {
|
||||
return gas.NewIssue(c, n, t.ID(), "TLS PreferServerCipherSuites set false.", gas.Medium, gas.High)
|
||||
return gosec.NewIssue(c, n, t.ID(), "TLS PreferServerCipherSuites set false.", gosec.Medium, gosec.High)
|
||||
}
|
||||
} else {
|
||||
// TODO(tk): symbol tab look up to get the actual value
|
||||
return gas.NewIssue(c, n, t.ID(), "TLS PreferServerCipherSuites may be false.", gas.Medium, gas.Low)
|
||||
return gosec.NewIssue(c, n, t.ID(), "TLS PreferServerCipherSuites may be false.", gosec.Medium, gosec.Low)
|
||||
}
|
||||
|
||||
case "MinVersion":
|
||||
if ival, ierr := gas.GetInt(n.Value); ierr == nil {
|
||||
if ival, ierr := gosec.GetInt(n.Value); ierr == nil {
|
||||
if (int16)(ival) < t.MinVersion {
|
||||
return gas.NewIssue(c, n, t.ID(), "TLS MinVersion too low.", gas.High, gas.High)
|
||||
return gosec.NewIssue(c, n, t.ID(), "TLS MinVersion too low.", gosec.High, gosec.High)
|
||||
}
|
||||
// TODO(tk): symbol tab look up to get the actual value
|
||||
return gas.NewIssue(c, n, t.ID(), "TLS MinVersion may be too low.", gas.High, gas.Low)
|
||||
return gosec.NewIssue(c, n, t.ID(), "TLS MinVersion may be too low.", gosec.High, gosec.Low)
|
||||
}
|
||||
|
||||
case "MaxVersion":
|
||||
if ival, ierr := gas.GetInt(n.Value); ierr == nil {
|
||||
if ival, ierr := gosec.GetInt(n.Value); ierr == nil {
|
||||
if (int16)(ival) < t.MaxVersion {
|
||||
return gas.NewIssue(c, n, t.ID(), "TLS MaxVersion too low.", gas.High, gas.High)
|
||||
return gosec.NewIssue(c, n, t.ID(), "TLS MaxVersion too low.", gosec.High, gosec.High)
|
||||
}
|
||||
// TODO(tk): symbol tab look up to get the actual value
|
||||
return gas.NewIssue(c, n, t.ID(), "TLS MaxVersion may be too low.", gas.High, gas.Low)
|
||||
return gosec.NewIssue(c, n, t.ID(), "TLS MaxVersion may be too low.", gosec.High, gosec.Low)
|
||||
}
|
||||
|
||||
case "CipherSuites":
|
||||
|
@ -112,7 +112,7 @@ func (t *insecureConfigTLS) processTLSConfVal(n *ast.KeyValueExpr, c *gas.Contex
|
|||
return nil
|
||||
}
|
||||
|
||||
func (t *insecureConfigTLS) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
||||
func (t *insecureConfigTLS) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) {
|
||||
if complit, ok := n.(*ast.CompositeLit); ok && complit.Type != nil {
|
||||
actualType := c.Info.TypeOf(complit.Type)
|
||||
if actualType != nil && actualType.String() == t.requiredType {
|
||||
|
|
|
@ -3,14 +3,14 @@ package rules
|
|||
import (
|
||||
"go/ast"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
"github.com/securego/gosec"
|
||||
)
|
||||
|
||||
// NewModernTLSCheck creates a check for Modern TLS ciphers
|
||||
// DO NOT EDIT - generated by tlsconfig tool
|
||||
func NewModernTLSCheck(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
func NewModernTLSCheck(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
|
||||
return &insecureConfigTLS{
|
||||
MetaData: gas.MetaData{ID: id},
|
||||
MetaData: gosec.MetaData{ID: id},
|
||||
requiredType: "crypto/tls.Config",
|
||||
MinVersion: 0x0303,
|
||||
MaxVersion: 0x0303,
|
||||
|
@ -31,9 +31,9 @@ func NewModernTLSCheck(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
|||
|
||||
// NewIntermediateTLSCheck creates a check for Intermediate TLS ciphers
|
||||
// DO NOT EDIT - generated by tlsconfig tool
|
||||
func NewIntermediateTLSCheck(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
func NewIntermediateTLSCheck(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
|
||||
return &insecureConfigTLS{
|
||||
MetaData: gas.MetaData{ID: id},
|
||||
MetaData: gosec.MetaData{ID: id},
|
||||
requiredType: "crypto/tls.Config",
|
||||
MinVersion: 0x0301,
|
||||
MaxVersion: 0x0303,
|
||||
|
@ -74,9 +74,9 @@ func NewIntermediateTLSCheck(id string, conf gas.Config) (gas.Rule, []ast.Node)
|
|||
|
||||
// NewOldTLSCheck creates a check for Old TLS ciphers
|
||||
// DO NOT EDIT - generated by tlsconfig tool
|
||||
func NewOldTLSCheck(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
func NewOldTLSCheck(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
|
||||
return &insecureConfigTLS{
|
||||
MetaData: gas.MetaData{ID: id},
|
||||
MetaData: gosec.MetaData{ID: id},
|
||||
requiredType: "crypto/tls.Config",
|
||||
MinVersion: 0x0300,
|
||||
MaxVersion: 0x0303,
|
||||
|
|
|
@ -17,11 +17,11 @@ package rules
|
|||
import (
|
||||
"go/ast"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
"github.com/securego/gosec"
|
||||
)
|
||||
|
||||
type usingUnsafe struct {
|
||||
gas.MetaData
|
||||
gosec.MetaData
|
||||
pkg string
|
||||
calls []string
|
||||
}
|
||||
|
@ -30,24 +30,24 @@ func (r *usingUnsafe) ID() string {
|
|||
return r.MetaData.ID
|
||||
}
|
||||
|
||||
func (r *usingUnsafe) Match(n ast.Node, c *gas.Context) (gi *gas.Issue, err error) {
|
||||
if _, matches := gas.MatchCallByPackage(n, c, r.pkg, r.calls...); matches {
|
||||
return gas.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil
|
||||
func (r *usingUnsafe) Match(n ast.Node, c *gosec.Context) (gi *gosec.Issue, err error) {
|
||||
if _, matches := gosec.MatchCallByPackage(n, c, r.pkg, r.calls...); matches {
|
||||
return gosec.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// NewUsingUnsafe rule detects the use of the unsafe package. This is only
|
||||
// really useful for auditing purposes.
|
||||
func NewUsingUnsafe(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
func NewUsingUnsafe(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
|
||||
return &usingUnsafe{
|
||||
pkg: "unsafe",
|
||||
calls: []string{"Alignof", "Offsetof", "Sizeof", "Pointer"},
|
||||
MetaData: gas.MetaData{
|
||||
MetaData: gosec.MetaData{
|
||||
ID: id,
|
||||
What: "Use of unsafe calls should be audited",
|
||||
Severity: gas.Low,
|
||||
Confidence: gas.High,
|
||||
Severity: gosec.Low,
|
||||
Confidence: gosec.High,
|
||||
},
|
||||
}, []ast.Node{(*ast.CallExpr)(nil)}
|
||||
}
|
||||
|
|
|
@ -17,11 +17,11 @@ package rules
|
|||
import (
|
||||
"go/ast"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
"github.com/securego/gosec"
|
||||
)
|
||||
|
||||
type usesWeakCryptography struct {
|
||||
gas.MetaData
|
||||
gosec.MetaData
|
||||
blacklist map[string][]string
|
||||
}
|
||||
|
||||
|
@ -29,27 +29,27 @@ func (r *usesWeakCryptography) ID() string {
|
|||
return r.MetaData.ID
|
||||
}
|
||||
|
||||
func (r *usesWeakCryptography) Match(n ast.Node, c *gas.Context) (*gas.Issue, error) {
|
||||
func (r *usesWeakCryptography) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) {
|
||||
for pkg, funcs := range r.blacklist {
|
||||
if _, matched := gas.MatchCallByPackage(n, c, pkg, funcs...); matched {
|
||||
return gas.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil
|
||||
if _, matched := gosec.MatchCallByPackage(n, c, pkg, funcs...); matched {
|
||||
return gosec.NewIssue(c, n, r.ID(), r.What, r.Severity, r.Confidence), nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// NewUsesWeakCryptography detects uses of des.* md5.* or rc4.*
|
||||
func NewUsesWeakCryptography(id string, conf gas.Config) (gas.Rule, []ast.Node) {
|
||||
func NewUsesWeakCryptography(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
|
||||
calls := make(map[string][]string)
|
||||
calls["crypto/des"] = []string{"NewCipher", "NewTripleDESCipher"}
|
||||
calls["crypto/md5"] = []string{"New", "Sum"}
|
||||
calls["crypto/rc4"] = []string{"NewCipher"}
|
||||
rule := &usesWeakCryptography{
|
||||
blacklist: calls,
|
||||
MetaData: gas.MetaData{
|
||||
MetaData: gosec.MetaData{
|
||||
ID: id,
|
||||
Severity: gas.Medium,
|
||||
Confidence: gas.High,
|
||||
Severity: gosec.Medium,
|
||||
Confidence: gosec.High,
|
||||
What: "Use of weak cryptographic primitive",
|
||||
},
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ import (
|
|||
"path"
|
||||
"strings"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
"github.com/securego/gosec"
|
||||
"golang.org/x/tools/go/loader"
|
||||
)
|
||||
|
||||
|
@ -33,7 +33,7 @@ type TestPackage struct {
|
|||
func NewTestPackage() *TestPackage {
|
||||
// Files must exist in $GOPATH
|
||||
sourceDir := path.Join(os.Getenv("GOPATH"), "src")
|
||||
workingDir, err := ioutil.TempDir(sourceDir, "gas_test")
|
||||
workingDir, err := ioutil.TempDir(sourceDir, "gosecs_test")
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
@ -97,7 +97,7 @@ func (p *TestPackage) Build() error {
|
|||
}
|
||||
|
||||
// CreateContext builds a context out of supplied package context
|
||||
func (p *TestPackage) CreateContext(filename string) *gas.Context {
|
||||
func (p *TestPackage) CreateContext(filename string) *gosec.Context {
|
||||
if err := p.Build(); err != nil {
|
||||
log.Fatal(err)
|
||||
return nil
|
||||
|
@ -109,13 +109,13 @@ func (p *TestPackage) CreateContext(filename string) *gas.Context {
|
|||
strip := fmt.Sprintf("%s%c", p.Path, os.PathSeparator)
|
||||
pkgFile = strings.TrimPrefix(pkgFile, strip)
|
||||
if pkgFile == filename {
|
||||
ctx := &gas.Context{
|
||||
ctx := &gosec.Context{
|
||||
FileSet: p.build.program.Fset,
|
||||
Root: file,
|
||||
Config: gas.NewConfig(),
|
||||
Config: gosec.NewConfig(),
|
||||
Info: &pkg.Info,
|
||||
Pkg: pkg.Pkg,
|
||||
Imports: gas.NewImportTracker(),
|
||||
Imports: gosec.NewImportTracker(),
|
||||
}
|
||||
ctx.Imports.TrackPackages(ctx.Pkg.Imports()...)
|
||||
return ctx
|
||||
|
|
|
@ -515,6 +515,100 @@ func main() {
|
|||
log.Fatal(http.ListenAndServe(":3000", nil))
|
||||
}`, 1}}
|
||||
|
||||
// SampleCodeG305 - File path traversal when extracting zip archives
|
||||
SampleCodeG305 = []CodeSample{{`
|
||||
package unzip
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
func unzip(archive, target string) error {
|
||||
reader, err := zip.OpenReader(archive)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(target, 0750); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, file := range reader.File {
|
||||
path := filepath.Join(target, file.Name)
|
||||
if file.FileInfo().IsDir() {
|
||||
os.MkdirAll(path, file.Mode()) // #nosec
|
||||
continue
|
||||
}
|
||||
|
||||
fileReader, err := file.Open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fileReader.Close()
|
||||
|
||||
targetFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer targetFile.Close()
|
||||
|
||||
if _, err := io.Copy(targetFile, fileReader); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}`, 1}, {`
|
||||
package unzip
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
func unzip(archive, target string) error {
|
||||
reader, err := zip.OpenReader(archive)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(target, 0750); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, file := range reader.File {
|
||||
archiveFile := file.Name
|
||||
path := filepath.Join(target, archiveFile)
|
||||
if file.FileInfo().IsDir() {
|
||||
os.MkdirAll(path, file.Mode()) // #nosec
|
||||
continue
|
||||
}
|
||||
|
||||
fileReader, err := file.Open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer fileReader.Close()
|
||||
|
||||
targetFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer targetFile.Close()
|
||||
|
||||
if _, err := io.Copy(targetFile, fileReader); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}`, 1}}
|
||||
|
||||
// SampleCodeG401 - Use of weak crypto MD5
|
||||
SampleCodeG401 = []CodeSample{
|
||||
{`
|
||||
|
|
|
@ -3,14 +3,14 @@ package testutils
|
|||
import (
|
||||
"go/ast"
|
||||
|
||||
"github.com/GoASTScanner/gas"
|
||||
"github.com/securego/gosec"
|
||||
)
|
||||
|
||||
// MockVisitor is useful for stubbing out ast.Visitor with callback
|
||||
// and looking for specific conditions to exist.
|
||||
type MockVisitor struct {
|
||||
Context *gas.Context
|
||||
Callback func(n ast.Node, ctx *gas.Context) bool
|
||||
Context *gosec.Context
|
||||
Callback func(n ast.Node, ctx *gosec.Context) bool
|
||||
}
|
||||
|
||||
// NewMockVisitor creates a new empty struct, the Context and
|
||||
|
|
Loading…
Reference in a new issue