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