Compare commits

...

23 Commits
1.0.0 ... 1.1.0

Author SHA1 Message Date
Cosmin Cojocar
e4ba96adc3 Update README 2018-08-21 11:15:14 +02:00
Cosmin Cojocar
ec0f8ec9d6 Set the GOROOT and GOPATH env variables in Dockerfile 2018-08-21 11:15:14 +02:00
Cosmin Cojocar
247828cfa5 Update docker base image to 1.10.3-alpine3.8 2018-08-21 11:15:14 +02:00
cschoenduve-splunk
b6891998ce Add Fprintf to Rule G201 2018-08-21 09:31:38 +02:00
cschoenduve-splunk
a7cff91312 Small update to G201 and added ConcatString Function (#228) 2018-08-19 19:57:36 +02:00
Grant Murphy
1c438e36af Tweak makefile to match up with docker repo (#231) 2018-08-19 10:28:17 +10:00
Cosmin Cojocar
9577fd0b44 Update README 2018-08-15 09:58:26 +02:00
Cosmin Cojocar
e543f4662c Use the Linux build for Docker image 2018-08-15 09:53:33 +02:00
Cosmin Cojocar
dbd0f8f511 Use the make build goal when creeating the docker image 2018-08-15 09:45:37 +02:00
Cosmin Cojocar
f06a84ebaa Merge pull request #227 from ccojocar/sha1
Add sha1 to weak crypto primitives
2018-08-09 09:34:49 +02:00
Cosmin Cojocar
8dfa8dc015 Update README 2018-08-08 16:41:34 +02:00
Cosmin Cojocar
fb0dc73a96 Add sha1 to weak crypto primitives 2018-08-08 16:38:57 +02:00
Cosmin Cojocar
90a1c1d625 Merge pull request #225 from jvmatl/jvmatl-patch-1
Document #nosec use with a list of rules
2018-08-03 10:02:42 +02:00
John Martinez
0d2e16dfa3 Document #nosec use with a list of rules
Extend the readme to document the ability to prevent some, but not all, rules from being enforced within an AST node.
2018-07-31 16:22:19 -04:00
Cosmin Cojocar
639987a295 Merge pull request #223 from ccojocar/fail_by_severity
Add a flag to specify the severity for which the scanning will be failed
2018-07-30 13:46:25 +02:00
Cosmin Cojocar
de10a7456f Fix the help message 2018-07-30 09:45:29 +02:00
Cosmin Cojocar
4702cc5da7 Add a flag to specify the severity for which the scanning will be failed 2018-07-30 09:43:41 +02:00
Cosmin Cojocar
c0db486820 Merge pull request #222 from ccojocar/vendor_folder_flag
Add a flag to turn on scanning on vendor folder
2018-07-30 09:23:52 +02:00
Cosmin Cojocar
6919d97188 Add a flag to turn on scanning on vendor folder 2018-07-30 09:11:23 +02:00
Cosmin Cojocar
f5b44b0740 Merge pull request #221 from Quasilyte/quasilyte/dupSubExpr
fix duplicated index issue in Less method
2018-07-30 08:44:30 +02:00
Cosmin Cojocar
7d767b4b66 Merge pull request #220 from Quasilyte/quasilyte/sloppyLen
replace len(x)<=0 with len(x)==0
2018-07-30 08:43:44 +02:00
Iskander Sharipov
3c8707c6c4 fix duplicated index issue in Less method
Found using https://go-critic.github.io/overview#dupSubExpr-ref
2018-07-28 23:18:12 +03:00
Iskander Sharipov
2f61fad317 replace len(x)<=0 with len(x)==0
length can't be negative.

Found using https://go-critic.github.io/overview#sloppyLen-ref
2018-07-28 23:16:16 +03:00
12 changed files with 186 additions and 29 deletions

View File

@@ -1,8 +1,10 @@
FROM golang:1.9.4-alpine3.7
FROM golang:1.10.3-alpine3.8
ENV BIN=gosec
ENV GOROOT=/usr/local/go
ENV GOPATH=/go
COPY dist/linux_amd64/$BIN /go/bin/$BIN
COPY $BIN /go/bin/$BIN
COPY docker-entrypoint.sh /usr/local/bin
ENTRYPOINT ["docker-entrypoint.sh"]

View File

@@ -1,7 +1,9 @@
GIT_TAG?= $(shell git describe --always --tags)
BIN = gosec
FMT_CMD = $(gofmt -s -l -w $(find . -type f -name '*.go' -not -path './vendor/*') | tee /dev/stderr)
IMAGE_REPO = docker.io
IMAGE_REPO = securego
BUILDFLAGS := ''
CGO_ENABLED = 0
default:
$(MAKE) bootstrap
@@ -27,8 +29,11 @@ clean:
release: bootstrap
@echo "Releasing the gosec binary..."
goreleaser release
build-linux:
CGO_ENABLED=$(CGO_ENABLED) GOOS=linux GOARCH=amd64 go build -ldflags $(BUILDFLAGS) -o $(BIN) ./cmd/gosec/
image: release
image: build-linux
@echo "Building the Docker image..."
docker build -t $(IMAGE_REPO)/$(BIN):$(GIT_TAG) .
docker tag $(IMAGE_REPO)/$(BIN):$(GIT_TAG) $(IMAGE_REPO)/$(BIN):latest
@@ -36,8 +41,7 @@ image: release
image-push: image
@echo "Pushing the Docker image..."
docker push $(IMAGE_REPO)/$(BIN):$(GIT_TAG)
docker push $(IMAGE_REPO)/$(BIN):$(GIT_TAG)
docker push $(IMAGE_REPO)/$(BIN):latest
.PHONY: test build clean release image image-push

View File

@@ -50,7 +50,7 @@ or to specify a set of rules to explicitly exclude using the '-exclude=' flag.
- G303: Creating tempfile using a predictable path
- G304: File path provided as taint input
- G305: File traversal when extracting zip archive
- G401: Detect the usage of DES, RC4, or MD5
- G401: Detect the usage of DES, RC4, MD5 or SHA1
- G402: Look for bad TLS connection settings
- G403: Ensure minimum RSA key length of 2048 bits
- G404: Insecure random number source (rand)
@@ -58,6 +58,7 @@ or to specify a set of rules to explicitly exclude using the '-exclude=' flag.
- G502: Import blacklist: crypto/des
- G503: Import blacklist: crypto/rc4
- G504: Import blacklist: net/http/cgi
- G505: Import blacklist: crypto/sha1
```
@@ -77,8 +78,8 @@ that are not considered build artifacts by the compiler (so test files).
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 gosec to stop processing any further nodes within the
AST so can apply to a whole block or more granularly to a single expression.
The annotation causes gosec to stop processing any further nodes within the
AST so can apply to a whole block or more granularly to a single expression.
```go
@@ -96,6 +97,8 @@ func main(){
```
When a specific false positive has been identified and verified as safe, you may wish to suppress only that single rule (or a specific set of rules) within a section of code, while continuing to scan for other problems. To do this, you can list the rule(s) to be suppressed within the `#nosec` annotation, e.g: `/* #nosec G401 */` or `// #nosec G201 G202 G203 `
In some cases you may also want to revisit places where #nosec annotations
have been used. To run the scanner and ignore any #nosec annotations you
can do the following:
@@ -144,9 +147,12 @@ make test
#### Release Build
Make sure you have installed the [goreleaser](https://github.com/goreleaser/goreleaser) tool and then you can release gosec as follows:
```
git tag 1.0.0
export GITHUB_TOKEN=<YOUR GITHUB TOKEN>
make release
```
The released version of the tool is available in the `dist` folder. The build information should be displayed in the usage text.
@@ -166,18 +172,17 @@ Note that all released archives are also uploaded to GitHub.
#### Docker image
You can execute a release and build the docker image as follows:
You can build the docker image as follows:
```
git tag <VERSION>
export GITHUB_TOKEN=<Your GitHub token>
make image
```
Now you can run the gosec tool in a container against your local workspace:
You can run the `gosec` tool in a container against your local Go project. You just have to mount the project in the
`GOPATH` of the container:
```
docker run -it -v <YOUR LOCAL WORKSPACE>:/workspace gosec /workspace
docker run -it -v $GOPATH/src/<YOUR PROJECT PATH>:/go/src/<YOUR PORJECT PATH> securego/gosec /go/src/<YOUR PROJECT PATH>
```
#### Generate TLS rule

View File

@@ -91,6 +91,12 @@ var (
// go build tags
flagBuildTags = flag.String("tags", "", "Comma separated list of build tags")
// scan the vendor folder
flagScanVendor = flag.Bool("vendor", false, "Scan the vendor folder")
// fail by severity
flagSeverity = flag.String("severity", "low", "Fail the scanning for issues with the given or higher severity. Valid options are: low, medium, high")
logger *log.Logger
)
@@ -222,6 +228,20 @@ func resolvePackage(pkg string, searchPaths []string) string {
return pkg
}
func convertToScore(severity string) (gosec.Score, error) {
severity = strings.ToLower(severity)
switch severity {
case "low":
return gosec.Low, nil
case "medium":
return gosec.Medium, nil
case "high":
return gosec.High, nil
default:
return gosec.Low, fmt.Errorf("provided severity '%s' not valid. Valid options: low, medium, high", severity)
}
}
func main() {
// Setup usage description
@@ -254,6 +274,11 @@ func main() {
logger = log.New(logWriter, "[gosec] ", log.LstdFlags)
}
failSeverity, err := convertToScore(*flagSeverity)
if err != nil {
logger.Fatal(err)
}
// Load config
config, err := loadConfig(*flagConfig)
if err != nil {
@@ -262,7 +287,7 @@ func main() {
// Load enabled rule definitions
ruleDefinitions := loadRules(*flagRulesInclude, *flagRulesExclude)
if len(ruleDefinitions) <= 0 {
if len(ruleDefinitions) == 0 {
logger.Fatal("cannot continue: no rules are configured.")
}
@@ -278,8 +303,10 @@ func main() {
for _, pkg := range gotool.ImportPaths(cleanPaths(flag.Args())) {
// Skip vendor directory
if vendor.MatchString(pkg) {
continue
if !*flagScanVendor {
if vendor.MatchString(pkg) {
continue
}
}
packages = append(packages, resolvePackage(pkg, gopaths))
}
@@ -295,17 +322,24 @@ func main() {
// Collect the results
issues, metrics := analyzer.Report()
issuesFound := len(issues) > 0
// Exit quietly if nothing was found
if !issuesFound && *flagQuiet {
os.Exit(0)
}
// Sort the issue by severity
if *flagSortIssues {
sortIssues(issues)
}
issuesFound := false
for _, issue := range issues {
if issue.Severity >= failSeverity {
issuesFound = true
break
}
}
// Exit quietly if nothing was found
if !issuesFound && *flagQuiet {
os.Exit(0)
}
// Create output report
if err := saveOutput(*flagOutput, *flagFormat, issues, metrics); err != nil {
logger.Fatal(err)

View File

@@ -10,7 +10,7 @@ type sortBySeverity []*gosec.Issue
func (s sortBySeverity) Len() int { return len(s) }
func (s sortBySeverity) Less(i, j int) bool { return s[i].Severity > s[i].Severity }
func (s sortBySeverity) Less(i, j int) bool { return s[i].Severity > s[j].Severity }
func (s sortBySeverity) Swap(i, j int) { s[i], s[j] = s[j], s[i] }

View File

@@ -256,3 +256,28 @@ func GetPkgAbsPath(pkgPath string) (string, error) {
}
return absPath, nil
}
// ConcatString recusively concatenates strings from a binary expression
func ConcatString(n *ast.BinaryExpr) (string, bool) {
var s string
// sub expressions are found in X object, Y object is always last BasicLit
if rightOperand, ok := n.Y.(*ast.BasicLit); ok {
if str, err := GetString(rightOperand); err == nil {
s = str + s
}
} else {
return "", false
}
if leftOperand, ok := n.X.(*ast.BinaryExpr); ok {
if recursion, ok := ConcatString(leftOperand); ok {
s = recursion + s
}
} else if leftOperand, ok := n.X.(*ast.BasicLit); ok {
if str, err := GetString(leftOperand); err == nil {
s = str + s
}
} else {
return "", false
}
return s, true
}

View File

@@ -85,3 +85,10 @@ func NewBlacklistedImportCGI(id string, conf gosec.Config) (gosec.Rule, []ast.No
"net/http/cgi": "Blacklisted import net/http/cgi: Go versions < 1.6.3 are vulnerable to Httpoxy attack: (CVE-2016-5386)",
})
}
// NewBlacklistedImportSHA1 fails if SHA1 is imported
func NewBlacklistedImportSHA1(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
return NewBlacklistedImports(id, conf, map[string]string{
"crypto/sha1": "Blacklisted import crypto/sha1: weak cryptographic primitive",
})
}

View File

@@ -80,7 +80,7 @@ func Generate(filters ...RuleFilter) RuleList {
{"G305", "File path traversal when extracting zip archive", NewArchive},
// crypto
{"G401", "Detect the usage of DES, RC4, or MD5", NewUsesWeakCryptography},
{"G401", "Detect the usage of DES, RC4, MD5 or SHA1", NewUsesWeakCryptography},
{"G402", "Look for bad TLS connection settings", NewIntermediateTLSCheck},
{"G403", "Ensure minimum RSA key length of 2048 bits", NewWeakKeyStrength},
{"G404", "Insecure random number source (rand)", NewWeakRandCheck},
@@ -90,6 +90,7 @@ func Generate(filters ...RuleFilter) RuleList {
{"G502", "Import blacklist: crypto/des", NewBlacklistedImportDES},
{"G503", "Import blacklist: crypto/rc4", NewBlacklistedImportRC4},
{"G504", "Import blacklist: net/http/cgi", NewBlacklistedImportCGI},
{"G505", "Import blacklist: crypto/sha1", NewBlacklistedImportSHA1},
}
ruleMap := make(map[string]RuleDefinition)

View File

@@ -6,6 +6,7 @@ import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/securego/gosec"
"github.com/securego/gosec/rules"
"github.com/securego/gosec/testutils"
@@ -110,6 +111,10 @@ var _ = Describe("gosec rules", func() {
runner("G401", testutils.SampleCodeG401)
})
It("should detect weak crypto algorithms", func() {
runner("G401", testutils.SampleCodeG401b)
})
It("should find insecure tls settings", func() {
runner("G402", testutils.SampleCodeG402)
})
@@ -137,6 +142,9 @@ var _ = Describe("gosec rules", func() {
It("should detect blacklisted imports - CGI (httpoxy)", func() {
runner("G504", testutils.SampleCodeG504)
})
It("should detect blacklisted imports - SHA1", func() {
runner("G505", testutils.SampleCodeG505)
})
})

View File

@@ -98,15 +98,44 @@ func NewSQLStrConcat(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
type sqlStrFormat struct {
sqlStatement
calls gosec.CallList
calls gosec.CallList
noIssue gosec.CallList
}
// Looks for "fmt.Sprintf("SELECT * FROM foo where '%s', userInput)"
func (s *sqlStrFormat) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) {
// argIndex changes the function argument which gets matched to the regex
argIndex := 0
// TODO(gm) improve confidence if database/sql is being used
if node := s.calls.ContainsCallExpr(n, c); node != nil {
if arg, e := gosec.GetString(node.Args[0]); s.MatchPatterns(arg) && e == nil {
// if the function is fmt.Fprintf, search for SQL statement in Args[1] instead
if sel, ok := node.Fun.(*ast.SelectorExpr); ok {
if sel.Sel.Name == "Fprintf" {
// if os.Stderr or os.Stdout is in Arg[0], mark as no issue
if arg, ok := node.Args[0].(*ast.SelectorExpr); ok {
if ident, ok := arg.X.(*ast.Ident); ok {
if s.noIssue.Contains(ident.Name, arg.Sel.Name) {
return nil, nil
}
}
}
// the function is Fprintf so set argIndex = 1
argIndex = 1
}
}
// concats callexpr arg strings together if needed before regex evaluation
if argExpr, ok := node.Args[argIndex].(*ast.BinaryExpr); ok {
if fullStr, ok := gosec.ConcatString(argExpr); ok {
if s.MatchPatterns(fullStr) {
return gosec.NewIssue(c, n, s.ID(), s.What, s.Severity, s.Confidence),
nil
}
}
}
if arg, e := gosec.GetString(node.Args[argIndex]); s.MatchPatterns(arg) && e == nil {
return gosec.NewIssue(c, n, s.ID(), s.What, s.Severity, s.Confidence), nil
}
}
@@ -116,7 +145,8 @@ func (s *sqlStrFormat) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error)
// NewSQLStrFormat looks for cases where we're building SQL query strings using format strings
func NewSQLStrFormat(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
rule := &sqlStrFormat{
calls: gosec.NewCallList(),
calls: gosec.NewCallList(),
noIssue: gosec.NewCallList(),
sqlStatement: sqlStatement{
patterns: []*regexp.Regexp{
regexp.MustCompile("(?)(SELECT|DELETE|INSERT|UPDATE|INTO|FROM|WHERE) "),
@@ -130,6 +160,7 @@ func NewSQLStrFormat(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
},
},
}
rule.calls.AddAll("fmt", "Sprint", "Sprintf", "Sprintln")
rule.calls.AddAll("fmt", "Sprint", "Sprintf", "Sprintln", "Fprintf")
rule.noIssue.AddAll("os", "Stdout", "Stderr")
return rule, []ast.Node{(*ast.CallExpr)(nil)}
}

View File

@@ -43,6 +43,7 @@ func NewUsesWeakCryptography(id string, conf gosec.Config) (gosec.Rule, []ast.No
calls := make(map[string][]string)
calls["crypto/des"] = []string{"NewCipher", "NewTripleDESCipher"}
calls["crypto/md5"] = []string{"New", "Sum"}
calls["crypto/sha1"] = []string{"New", "Sum"}
calls["crypto/rc4"] = []string{"NewCipher"}
rule := &usesWeakCryptography{
blacklist: calls,

View File

@@ -633,6 +633,31 @@ func main() {
fmt.Printf("%x", h.Sum(nil))
}`, 1}}
// SampleCodeG401b - Use of weak crypto SHA1
SampleCodeG401b = []CodeSample{
{`
package main
import (
"crypto/sha1"
"fmt"
"io"
"log"
"os"
)
func main() {
f, err := os.Open("file.txt")
if err != nil {
log.Fatal(err)
}
defer f.Close()
h := sha1.New()
if _, err := io.Copy(h, f); err != nil {
log.Fatal(err)
}
fmt.Printf("%x", h.Sum(nil))
}`, 1}}
// SampleCodeG402 - TLS settings
SampleCodeG402 = []CodeSample{{`
// InsecureSkipVerify
@@ -827,6 +852,20 @@ import (
)
func main() {
cgi.Serve(http.FileServer(http.Dir("/usr/share/doc")))
}`, 1}}
// SampleCodeG505 - Blacklisted import SHA1
SampleCodeG505 = []CodeSample{
{`
package main
import (
"crypto/sha1"
"fmt"
"os"
)
func main() {
for _, arg := range os.Args {
fmt.Printf("%x - %s\n", sha1.Sum([]byte(arg)), arg)
}
}`, 1}}
// SampleCode601 - Go build tags
SampleCode601 = []CodeSample{{`