mirror of
https://github.com/securego/gosec.git
synced 2026-01-15 09:53:40 +08:00
Compare commits
34 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1d909e2687 | ||
|
|
742aa848f9 | ||
|
|
7be6d4efb5 | ||
|
|
75cc7dcd51 | ||
|
|
58058af0c8 | ||
|
|
9d66b0d346 | ||
|
|
4c1afaa492 | ||
|
|
19bda8d15f | ||
|
|
827fca9a83 | ||
|
|
ad5d74d5a1 | ||
|
|
72f1145f8a | ||
|
|
63a8e789a1 | ||
|
|
3038a30e3c | ||
|
|
bf0dd2fdd3 | ||
|
|
2d1c1a6df7 | ||
|
|
e0f354aa0d | ||
|
|
d23ab2d997 | ||
|
|
35af340d07 | ||
|
|
6c0b34426c | ||
|
|
b45f95f6ad | ||
|
|
040327f7d7 | ||
|
|
6a41fb9e61 | ||
|
|
c95e9c21e7 | ||
|
|
e57efa8482 | ||
|
|
ff17c30a97 | ||
|
|
3eba7b8a3e | ||
|
|
55c6ceaaa6 | ||
|
|
40fa36d1de | ||
|
|
873ac243ea | ||
|
|
f1f0056a90 | ||
|
|
0680c75f99 | ||
|
|
79c8b79263 | ||
|
|
69213955da | ||
|
|
5a3a27afae |
1
.github/workflows/ci.yml
vendored
1
.github/workflows/ci.yml
vendored
@@ -21,7 +21,6 @@ jobs:
|
||||
strategy:
|
||||
matrix:
|
||||
go_version:
|
||||
- '1.15'
|
||||
- '1.16'
|
||||
- '1.17'
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
8
.github/workflows/release.yml
vendored
8
.github/workflows/release.yml
vendored
@@ -22,12 +22,10 @@ jobs:
|
||||
id: get_version
|
||||
run: echo ::set-env name=RELEASE_VERSION::$(echo ${GITHUB_REF:10})
|
||||
- name: Generate SBOM
|
||||
uses: CycloneDX/gh-gomod-generate-sbom@v1.0.0
|
||||
uses: CycloneDX/gh-gomod-generate-sbom@v1
|
||||
with:
|
||||
json: true
|
||||
output: bom.json
|
||||
resolve-licenses: true
|
||||
version: ^v0
|
||||
version: v1
|
||||
args: mod -licenses -json -output bom.json
|
||||
- name: Release Binaries
|
||||
uses: goreleaser/goreleaser-action@v2
|
||||
with:
|
||||
|
||||
@@ -8,7 +8,7 @@ RUN go mod download
|
||||
RUN make build-linux
|
||||
|
||||
FROM golang:${GO_VERSION}-alpine
|
||||
RUN apk add --update --no-cache ca-certificates bash git gcc libc-dev
|
||||
RUN apk add --update --no-cache ca-certificates bash git gcc libc-dev openssh
|
||||
ENV GO111MODULE on
|
||||
COPY --from=builder /build/gosec /bin/gosec
|
||||
COPY entrypoint.sh /bin/entrypoint.sh
|
||||
|
||||
8
Makefile
8
Makefile
@@ -12,23 +12,23 @@ GOBIN ?= $(GOPATH)/bin
|
||||
GOLINT ?= $(GOBIN)/golint
|
||||
GOSEC ?= $(GOBIN)/gosec
|
||||
GINKGO ?= $(GOBIN)/ginkgo
|
||||
GO_VERSION = 1.15
|
||||
GO_VERSION = 1.17
|
||||
|
||||
default:
|
||||
$(MAKE) build
|
||||
|
||||
install-test-deps:
|
||||
$(GO_NOMOD) get -u github.com/onsi/ginkgo/ginkgo
|
||||
go install github.com/onsi/ginkgo/v2/ginkgo@latest
|
||||
$(GO_NOMOD) get -u golang.org/x/crypto/ssh
|
||||
$(GO_NOMOD) get -u github.com/lib/pq
|
||||
|
||||
test: install-test-deps build fmt lint sec
|
||||
$(GINKGO) -r -v
|
||||
$(GINKGO) -v --fail-fast
|
||||
|
||||
fmt:
|
||||
@echo "FORMATTING"
|
||||
@FORMATTED=`$(GO) fmt ./...`
|
||||
@([[ ! -z "$(FORMATTED)" ]] && printf "Fixed unformatted files:\n$(FORMATTED)") || true
|
||||
@([ ! -z "$(FORMATTED)" ] && printf "Fixed unformatted files:\n$(FORMATTED)") || true
|
||||
|
||||
lint:
|
||||
@echo "LINTING"
|
||||
|
||||
40
README.md
40
README.md
@@ -269,14 +269,15 @@ gosec -exclude-generated ./...
|
||||
### Annotating code
|
||||
|
||||
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.
|
||||
it is possible to annotate the code with a comment that starts with `#nosec`.
|
||||
The `#nosec` comment should have the format `#nosec [RuleList] [-- Justification]`.
|
||||
|
||||
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
|
||||
|
||||
import "md5" // #nosec
|
||||
import "md5" //#nosec
|
||||
|
||||
|
||||
func main(){
|
||||
@@ -292,7 +293,11 @@ 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`
|
||||
the `#nosec` annotation, e.g: `/* #nosec G401 */` or `//#nosec G201 G202 G203`
|
||||
|
||||
You could put the description or justification text for the annotation. The
|
||||
justification should be after the rule(s) to suppress and start with two or
|
||||
more dashes, e.g: `//#nosec G101 G102 -- This is a false positive`
|
||||
|
||||
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
|
||||
@@ -302,6 +307,27 @@ can do the following:
|
||||
gosec -nosec=true ./...
|
||||
```
|
||||
|
||||
### Tracking suppressions
|
||||
|
||||
As described above, we could suppress violations externally (using `-include`/
|
||||
`-exclude`) or inline (using `#nosec` annotations) in gosec. This suppression
|
||||
inflammation can be used to generate corresponding signals for auditing
|
||||
purposes.
|
||||
|
||||
We could track suppressions by the `-track-suppressions` flag as follows:
|
||||
|
||||
```bash
|
||||
gosec -track-suppressions -exclude=G101 -fmt=sarif -out=results.sarif ./...
|
||||
```
|
||||
|
||||
- For external suppressions, gosec records suppression info where `kind` is
|
||||
`external` and `justification` is a certain sentence "Globally suppressed".
|
||||
- For inline suppressions, gosec records suppression info where `kind` is
|
||||
`inSource` and `justification` is the text after two or more dashes in the
|
||||
comment.
|
||||
|
||||
**Note:** Only SARIF and JSON formats support tracking suppressions.
|
||||
|
||||
### Build tags
|
||||
|
||||
gosec is able to pass your [Go build tags](https://golang.org/pkg/go/build/) to the analyzer.
|
||||
@@ -358,7 +384,7 @@ Then generate the types with :
|
||||
schema-generate -i sarif-schema-2.1.0.json -o mypath/types.go
|
||||
```
|
||||
|
||||
Most of the MarshallJSON/UnmarshalJSON are removed except the one for PropertyBag which is handy to inline the additionnal properties. The rest can be removed.
|
||||
Most of the MarshallJSON/UnmarshalJSON are removed except the one for PropertyBag which is handy to inline the additional properties. The rest can be removed.
|
||||
The URI,ID, UUID, GUID were renamed so it fits the Golang convention defined [here](https://github.com/golang/lint/blob/master/lint.go#L700)
|
||||
|
||||
### Tests
|
||||
@@ -419,3 +445,9 @@ This will generate the `rules/tls_config.go` file which will contain the current
|
||||
## Who is using gosec?
|
||||
|
||||
This is a [list](USERS.md) with some of the gosec's users.
|
||||
|
||||
## Sponsors
|
||||
|
||||
Support this project by becoming a sponsor. Your logo will show up here with a link to your website
|
||||
|
||||
<a href="https://github.com/Daimler" target="_blank"><img src="https://avatars.githubusercontent.com/u/34240465?s=80&v=4"></a>
|
||||
|
||||
153
analyzer.go
153
analyzer.go
@@ -43,6 +43,10 @@ const LoadMode = packages.NeedName |
|
||||
packages.NeedTypesInfo |
|
||||
packages.NeedSyntax
|
||||
|
||||
const externalSuppressionJustification = "Globally suppressed."
|
||||
|
||||
const aliasOfAllRules = "*"
|
||||
|
||||
var generatedCodePattern = regexp.MustCompile(`^// Code generated .* DO NOT EDIT\.$`)
|
||||
|
||||
// The Context is populated with data parsed from the source code as it is scanned.
|
||||
@@ -57,7 +61,7 @@ type Context struct {
|
||||
Root *ast.File
|
||||
Config Config
|
||||
Imports *ImportTracker
|
||||
Ignores []map[string]bool
|
||||
Ignores []map[string][]SuppressionInfo
|
||||
PassedValues map[string]interface{}
|
||||
}
|
||||
|
||||
@@ -72,21 +76,29 @@ type Metrics struct {
|
||||
// Analyzer object is the main object of gosec. It has methods traverse an AST
|
||||
// and invoke the correct checking rules as on each node as required.
|
||||
type Analyzer struct {
|
||||
ignoreNosec bool
|
||||
ruleset RuleSet
|
||||
context *Context
|
||||
config Config
|
||||
logger *log.Logger
|
||||
issues []*Issue
|
||||
stats *Metrics
|
||||
errors map[string][]Error // keys are file paths; values are the golang errors in those files
|
||||
tests bool
|
||||
excludeGenerated bool
|
||||
showIgnored bool
|
||||
ignoreNosec bool
|
||||
ruleset RuleSet
|
||||
context *Context
|
||||
config Config
|
||||
logger *log.Logger
|
||||
issues []*Issue
|
||||
stats *Metrics
|
||||
errors map[string][]Error // keys are file paths; values are the golang errors in those files
|
||||
tests bool
|
||||
excludeGenerated bool
|
||||
showIgnored bool
|
||||
trackSuppressions bool
|
||||
}
|
||||
|
||||
// SuppressionInfo object is to record the kind and the justification that used
|
||||
// to suppress violations.
|
||||
type SuppressionInfo struct {
|
||||
Kind string `json:"kind"`
|
||||
Justification string `json:"justification"`
|
||||
}
|
||||
|
||||
// NewAnalyzer builds a new analyzer.
|
||||
func NewAnalyzer(conf Config, tests bool, excludeGenerated bool, logger *log.Logger) *Analyzer {
|
||||
func NewAnalyzer(conf Config, tests bool, excludeGenerated bool, trackSuppressions bool, logger *log.Logger) *Analyzer {
|
||||
ignoreNoSec := false
|
||||
if enabled, err := conf.IsGlobalEnabled(Nosec); err == nil {
|
||||
ignoreNoSec = enabled
|
||||
@@ -99,21 +111,22 @@ func NewAnalyzer(conf Config, tests bool, excludeGenerated bool, logger *log.Log
|
||||
logger = log.New(os.Stderr, "[gosec]", log.LstdFlags)
|
||||
}
|
||||
return &Analyzer{
|
||||
ignoreNosec: ignoreNoSec,
|
||||
showIgnored: showIgnored,
|
||||
ruleset: make(RuleSet),
|
||||
context: &Context{},
|
||||
config: conf,
|
||||
logger: logger,
|
||||
issues: make([]*Issue, 0, 16),
|
||||
stats: &Metrics{},
|
||||
errors: make(map[string][]Error),
|
||||
tests: tests,
|
||||
excludeGenerated: excludeGenerated,
|
||||
ignoreNosec: ignoreNoSec,
|
||||
showIgnored: showIgnored,
|
||||
ruleset: NewRuleSet(),
|
||||
context: &Context{},
|
||||
config: conf,
|
||||
logger: logger,
|
||||
issues: make([]*Issue, 0, 16),
|
||||
stats: &Metrics{},
|
||||
errors: make(map[string][]Error),
|
||||
tests: tests,
|
||||
excludeGenerated: excludeGenerated,
|
||||
trackSuppressions: trackSuppressions,
|
||||
}
|
||||
}
|
||||
|
||||
// SetConfig upates the analyzer configuration
|
||||
// SetConfig updates the analyzer configuration
|
||||
func (gosec *Analyzer) SetConfig(conf Config) {
|
||||
gosec.config = conf
|
||||
}
|
||||
@@ -125,10 +138,10 @@ func (gosec *Analyzer) Config() Config {
|
||||
|
||||
// LoadRules instantiates all the rules to be used when analyzing source
|
||||
// packages
|
||||
func (gosec *Analyzer) LoadRules(ruleDefinitions map[string]RuleBuilder) {
|
||||
func (gosec *Analyzer) LoadRules(ruleDefinitions map[string]RuleBuilder, ruleSuppressed map[string]bool) {
|
||||
for id, def := range ruleDefinitions {
|
||||
r, nodes := def(id, gosec.config)
|
||||
gosec.ruleset.Register(r, nodes...)
|
||||
gosec.ruleset.Register(r, ruleSuppressed[id], nodes...)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,7 +219,12 @@ func (gosec *Analyzer) load(pkgPath string, conf *packages.Config) ([]*packages.
|
||||
func (gosec *Analyzer) Check(pkg *packages.Package) {
|
||||
gosec.logger.Println("Checking package:", pkg.Name)
|
||||
for _, file := range pkg.Syntax {
|
||||
checkedFile := pkg.Fset.File(file.Pos()).Name()
|
||||
fp := pkg.Fset.File(file.Pos())
|
||||
if fp == nil {
|
||||
// skip files which cannot be located
|
||||
continue
|
||||
}
|
||||
checkedFile := fp.Name()
|
||||
// Skip the no-Go file from analysis (e.g. a Cgo files is expanded in 3 different files
|
||||
// stored in the cache which do not need to by analyzed)
|
||||
if filepath.Ext(checkedFile) != ".go" {
|
||||
@@ -295,7 +313,7 @@ func (gosec *Analyzer) AppendError(file string, err error) {
|
||||
}
|
||||
|
||||
// ignore a node (and sub-tree) if it is tagged with a nosec tag comment
|
||||
func (gosec *Analyzer) ignore(n ast.Node) ([]string, bool) {
|
||||
func (gosec *Analyzer) ignore(n ast.Node) map[string]SuppressionInfo {
|
||||
if groups, ok := gosec.context.Comments[n]; ok && !gosec.ignoreNosec {
|
||||
|
||||
// Checks if an alternative for #nosec is set and, if not, uses the default.
|
||||
@@ -306,32 +324,52 @@ func (gosec *Analyzer) ignore(n ast.Node) ([]string, bool) {
|
||||
}
|
||||
|
||||
for _, group := range groups {
|
||||
|
||||
foundDefaultTag := strings.Contains(group.Text(), noSecDefaultTag)
|
||||
foundAlternativeTag := strings.Contains(group.Text(), noSecAlternativeTag)
|
||||
comment := strings.TrimSpace(group.Text())
|
||||
foundDefaultTag := strings.HasPrefix(comment, noSecDefaultTag) || regexp.MustCompile("\n *"+noSecDefaultTag).Match([]byte(comment))
|
||||
foundAlternativeTag := strings.HasPrefix(comment, noSecAlternativeTag) || regexp.MustCompile("\n *"+noSecAlternativeTag).Match([]byte(comment))
|
||||
|
||||
if foundDefaultTag || foundAlternativeTag {
|
||||
gosec.stats.NumNosec++
|
||||
|
||||
// Discard what's in front of the nosec tag.
|
||||
if foundDefaultTag {
|
||||
comment = strings.SplitN(comment, noSecDefaultTag, 2)[1]
|
||||
} else {
|
||||
comment = strings.SplitN(comment, noSecAlternativeTag, 2)[1]
|
||||
}
|
||||
|
||||
// Extract the directive and the justification.
|
||||
justification := ""
|
||||
commentParts := regexp.MustCompile(`-{2,}`).Split(comment, 2)
|
||||
directive := commentParts[0]
|
||||
if len(commentParts) > 1 {
|
||||
justification = strings.TrimSpace(strings.TrimRight(commentParts[1], "\n"))
|
||||
}
|
||||
|
||||
// Pull out the specific rules that are listed to be ignored.
|
||||
re := regexp.MustCompile(`(G\d{3})`)
|
||||
matches := re.FindAllStringSubmatch(group.Text(), -1)
|
||||
matches := re.FindAllStringSubmatch(directive, -1)
|
||||
|
||||
// If no specific rules were given, ignore everything.
|
||||
if len(matches) == 0 {
|
||||
return nil, true
|
||||
suppression := SuppressionInfo{
|
||||
Kind: "inSource",
|
||||
Justification: justification,
|
||||
}
|
||||
|
||||
// Find the rule IDs to ignore.
|
||||
var ignores []string
|
||||
ignores := make(map[string]SuppressionInfo)
|
||||
for _, v := range matches {
|
||||
ignores = append(ignores, v[1])
|
||||
ignores[v[1]] = suppression
|
||||
}
|
||||
return ignores, false
|
||||
|
||||
// If no specific rules were given, ignore everything.
|
||||
if len(matches) == 0 {
|
||||
ignores[aliasOfAllRules] = suppression
|
||||
}
|
||||
return ignores
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
return nil
|
||||
}
|
||||
|
||||
// Visit runs the gosec visitor logic over an AST created by parsing go code.
|
||||
@@ -346,31 +384,43 @@ func (gosec *Analyzer) Visit(n ast.Node) ast.Visitor {
|
||||
}
|
||||
|
||||
// Get any new rule exclusions.
|
||||
ignoredRules, ignoreAll := gosec.ignore(n)
|
||||
if ignoreAll {
|
||||
return nil
|
||||
}
|
||||
ignoredRules := gosec.ignore(n)
|
||||
|
||||
// Now create the union of exclusions.
|
||||
ignores := map[string]bool{}
|
||||
ignores := map[string][]SuppressionInfo{}
|
||||
if len(gosec.context.Ignores) > 0 {
|
||||
for k, v := range gosec.context.Ignores[0] {
|
||||
ignores[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
for _, v := range ignoredRules {
|
||||
ignores[v] = true
|
||||
for ruleID, suppression := range ignoredRules {
|
||||
ignores[ruleID] = append(ignores[ruleID], suppression)
|
||||
}
|
||||
|
||||
// Push the new set onto the stack.
|
||||
gosec.context.Ignores = append([]map[string]bool{ignores}, gosec.context.Ignores...)
|
||||
gosec.context.Ignores = append([]map[string][]SuppressionInfo{ignores}, gosec.context.Ignores...)
|
||||
|
||||
// Track aliased and initialization imports
|
||||
gosec.context.Imports.TrackImport(n)
|
||||
|
||||
for _, rule := range gosec.ruleset.RegisteredFor(n) {
|
||||
_, ignored := ignores[rule.ID()]
|
||||
// Check if all rules are ignored.
|
||||
generalSuppressions, generalIgnored := ignores[aliasOfAllRules]
|
||||
// Check if the specific rule is ignored
|
||||
ruleSuppressions, ruleIgnored := ignores[rule.ID()]
|
||||
|
||||
ignored := generalIgnored || ruleIgnored
|
||||
suppressions := append(generalSuppressions, ruleSuppressions...)
|
||||
|
||||
// Track external suppressions.
|
||||
if gosec.ruleset.IsRuleSuppressed(rule.ID()) {
|
||||
ignored = true
|
||||
suppressions = append(suppressions, SuppressionInfo{
|
||||
Kind: "external",
|
||||
Justification: externalSuppressionJustification,
|
||||
})
|
||||
}
|
||||
|
||||
issue, err := rule.Match(n, gosec.context)
|
||||
if err != nil {
|
||||
@@ -385,7 +435,10 @@ func (gosec *Analyzer) Visit(n ast.Node) ast.Visitor {
|
||||
if !ignored || !gosec.showIgnored {
|
||||
gosec.stats.NumFound++
|
||||
}
|
||||
if !ignored || gosec.showIgnored || gosec.ignoreNosec {
|
||||
if ignored && gosec.trackSuppressions {
|
||||
issue.WithSuppressions(suppressions)
|
||||
gosec.issues = append(gosec.issues, issue)
|
||||
} else if !ignored || gosec.showIgnored || gosec.ignoreNosec {
|
||||
gosec.issues = append(gosec.issues, issue)
|
||||
}
|
||||
}
|
||||
|
||||
280
analyzer_test.go
280
analyzer_test.go
@@ -7,7 +7,7 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/securego/gosec/v2"
|
||||
"github.com/securego/gosec/v2/rules"
|
||||
@@ -24,12 +24,12 @@ var _ = Describe("Analyzer", func() {
|
||||
)
|
||||
BeforeEach(func() {
|
||||
logger, _ = testutils.NewLogger()
|
||||
analyzer = gosec.NewAnalyzer(nil, tests, false, logger)
|
||||
analyzer = gosec.NewAnalyzer(nil, tests, false, false, logger)
|
||||
})
|
||||
|
||||
Context("when processing a package", func() {
|
||||
It("should not report an error if the package contains no Go files", func() {
|
||||
analyzer.LoadRules(rules.Generate().Builders())
|
||||
analyzer.LoadRules(rules.Generate(false).RulesInfo())
|
||||
dir, err := ioutil.TempDir("", "empty")
|
||||
defer os.RemoveAll(dir)
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
@@ -40,7 +40,7 @@ var _ = Describe("Analyzer", func() {
|
||||
})
|
||||
|
||||
It("should report an error if the package fails to build", func() {
|
||||
analyzer.LoadRules(rules.Generate().Builders())
|
||||
analyzer.LoadRules(rules.Generate(false).RulesInfo())
|
||||
pkg := testutils.NewTestPackage()
|
||||
defer pkg.Close()
|
||||
pkg.AddFile("wonky.go", `func main(){ println("forgot the package")}`)
|
||||
@@ -56,7 +56,7 @@ var _ = Describe("Analyzer", func() {
|
||||
})
|
||||
|
||||
It("should be able to analyze multiple Go files", func() {
|
||||
analyzer.LoadRules(rules.Generate().Builders())
|
||||
analyzer.LoadRules(rules.Generate(false).RulesInfo())
|
||||
pkg := testutils.NewTestPackage()
|
||||
defer pkg.Close()
|
||||
pkg.AddFile("foo.go", `
|
||||
@@ -78,7 +78,7 @@ var _ = Describe("Analyzer", func() {
|
||||
})
|
||||
|
||||
It("should be able to analyze multiple Go packages", func() {
|
||||
analyzer.LoadRules(rules.Generate().Builders())
|
||||
analyzer.LoadRules(rules.Generate(false).RulesInfo())
|
||||
pkg1 := testutils.NewTestPackage()
|
||||
pkg2 := testutils.NewTestPackage()
|
||||
defer pkg1.Close()
|
||||
@@ -104,7 +104,7 @@ var _ = Describe("Analyzer", func() {
|
||||
It("should find errors when nosec is not in use", func() {
|
||||
sample := testutils.SampleCodeG401[0]
|
||||
source := sample.Code[0]
|
||||
analyzer.LoadRules(rules.Generate(rules.NewRuleFilter(false, "G401")).Builders())
|
||||
analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G401")).RulesInfo())
|
||||
|
||||
controlPackage := testutils.NewTestPackage()
|
||||
defer controlPackage.Close()
|
||||
@@ -118,7 +118,7 @@ var _ = Describe("Analyzer", func() {
|
||||
})
|
||||
|
||||
It("should report Go build errors and invalid files", func() {
|
||||
analyzer.LoadRules(rules.Generate().Builders())
|
||||
analyzer.LoadRules(rules.Generate(false).RulesInfo())
|
||||
pkg := testutils.NewTestPackage()
|
||||
defer pkg.Close()
|
||||
pkg.AddFile("foo.go", `
|
||||
@@ -139,14 +139,31 @@ var _ = Describe("Analyzer", func() {
|
||||
}
|
||||
})
|
||||
|
||||
It("should not report errors when a nosec comment is present", func() {
|
||||
It("should not report errors when a nosec line comment is present", func() {
|
||||
sample := testutils.SampleCodeG401[0]
|
||||
source := sample.Code[0]
|
||||
analyzer.LoadRules(rules.Generate(rules.NewRuleFilter(false, "G401")).Builders())
|
||||
analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G401")).RulesInfo())
|
||||
|
||||
nosecPackage := testutils.NewTestPackage()
|
||||
defer nosecPackage.Close()
|
||||
nosecSource := strings.Replace(source, "h := md5.New()", "h := md5.New() // #nosec", 1)
|
||||
nosecSource := strings.Replace(source, "h := md5.New()", "h := md5.New() //#nosec", 1)
|
||||
nosecPackage.AddFile("md5.go", nosecSource)
|
||||
err := nosecPackage.Build()
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
err = analyzer.Process(buildTags, nosecPackage.Path)
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
nosecIssues, _, _ := analyzer.Report()
|
||||
Expect(nosecIssues).Should(BeEmpty())
|
||||
})
|
||||
|
||||
It("should not report errors when a nosec block comment is present", func() {
|
||||
sample := testutils.SampleCodeG401[0]
|
||||
source := sample.Code[0]
|
||||
analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G401")).RulesInfo())
|
||||
|
||||
nosecPackage := testutils.NewTestPackage()
|
||||
defer nosecPackage.Close()
|
||||
nosecSource := strings.Replace(source, "h := md5.New()", "h := md5.New() /* #nosec */", 1)
|
||||
nosecPackage.AddFile("md5.go", nosecSource)
|
||||
err := nosecPackage.Build()
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
@@ -160,11 +177,11 @@ var _ = Describe("Analyzer", func() {
|
||||
// Rule for MD5 weak crypto usage
|
||||
sample := testutils.SampleCodeG401[0]
|
||||
source := sample.Code[0]
|
||||
analyzer.LoadRules(rules.Generate(rules.NewRuleFilter(false, "G401")).Builders())
|
||||
analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G401")).RulesInfo())
|
||||
|
||||
nosecPackage := testutils.NewTestPackage()
|
||||
defer nosecPackage.Close()
|
||||
nosecSource := strings.Replace(source, "h := md5.New()", "h := md5.New() // #nosec G401", 1)
|
||||
nosecSource := strings.Replace(source, "h := md5.New()", "h := md5.New() //#nosec G401", 1)
|
||||
nosecPackage.AddFile("md5.go", nosecSource)
|
||||
err := nosecPackage.Build()
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
@@ -177,11 +194,11 @@ var _ = Describe("Analyzer", func() {
|
||||
It("should report errors when an exclude comment is present for a different rule", func() {
|
||||
sample := testutils.SampleCodeG401[0]
|
||||
source := sample.Code[0]
|
||||
analyzer.LoadRules(rules.Generate(rules.NewRuleFilter(false, "G401")).Builders())
|
||||
analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G401")).RulesInfo())
|
||||
|
||||
nosecPackage := testutils.NewTestPackage()
|
||||
defer nosecPackage.Close()
|
||||
nosecSource := strings.Replace(source, "h := md5.New()", "h := md5.New() // #nosec G301", 1)
|
||||
nosecSource := strings.Replace(source, "h := md5.New()", "h := md5.New() //#nosec G301", 1)
|
||||
nosecPackage.AddFile("md5.go", nosecSource)
|
||||
err := nosecPackage.Build()
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
@@ -194,11 +211,11 @@ var _ = Describe("Analyzer", func() {
|
||||
It("should not report errors when an exclude comment is present for multiple rules, including the correct rule", func() {
|
||||
sample := testutils.SampleCodeG401[0]
|
||||
source := sample.Code[0]
|
||||
analyzer.LoadRules(rules.Generate(rules.NewRuleFilter(false, "G401")).Builders())
|
||||
analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G401")).RulesInfo())
|
||||
|
||||
nosecPackage := testutils.NewTestPackage()
|
||||
defer nosecPackage.Close()
|
||||
nosecSource := strings.Replace(source, "h := md5.New()", "h := md5.New() // #nosec G301 G401", 1)
|
||||
nosecSource := strings.Replace(source, "h := md5.New()", "h := md5.New() //#nosec G301 G401", 1)
|
||||
nosecPackage.AddFile("md5.go", nosecSource)
|
||||
err := nosecPackage.Build()
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
@@ -213,7 +230,7 @@ var _ = Describe("Analyzer", func() {
|
||||
It("should pass the build tags", func() {
|
||||
sample := testutils.SampleCodeBuildTag[0]
|
||||
source := sample.Code[0]
|
||||
analyzer.LoadRules(rules.Generate().Builders())
|
||||
analyzer.LoadRules(rules.Generate(false).RulesInfo())
|
||||
pkg := testutils.NewTestPackage()
|
||||
defer pkg.Close()
|
||||
pkg.AddFile("tags.go", source)
|
||||
@@ -223,7 +240,7 @@ var _ = Describe("Analyzer", func() {
|
||||
})
|
||||
|
||||
It("should process an empty package with test file", func() {
|
||||
analyzer.LoadRules(rules.Generate().Builders())
|
||||
analyzer.LoadRules(rules.Generate(false).RulesInfo())
|
||||
pkg := testutils.NewTestPackage()
|
||||
defer pkg.Close()
|
||||
pkg.AddFile("foo_test.go", `
|
||||
@@ -245,12 +262,12 @@ var _ = Describe("Analyzer", func() {
|
||||
// overwrite nosec option
|
||||
nosecIgnoreConfig := gosec.NewConfig()
|
||||
nosecIgnoreConfig.SetGlobal(gosec.Nosec, "true")
|
||||
customAnalyzer := gosec.NewAnalyzer(nosecIgnoreConfig, tests, false, logger)
|
||||
customAnalyzer.LoadRules(rules.Generate(rules.NewRuleFilter(false, "G401")).Builders())
|
||||
customAnalyzer := gosec.NewAnalyzer(nosecIgnoreConfig, tests, false, false, logger)
|
||||
customAnalyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G401")).RulesInfo())
|
||||
|
||||
nosecPackage := testutils.NewTestPackage()
|
||||
defer nosecPackage.Close()
|
||||
nosecSource := strings.Replace(source, "h := md5.New()", "h := md5.New() // #nosec", 1)
|
||||
nosecSource := strings.Replace(source, "h := md5.New()", "h := md5.New() //#nosec", 1)
|
||||
nosecPackage.AddFile("md5.go", nosecSource)
|
||||
err := nosecPackage.Build()
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
@@ -269,12 +286,12 @@ var _ = Describe("Analyzer", func() {
|
||||
nosecIgnoreConfig := gosec.NewConfig()
|
||||
nosecIgnoreConfig.SetGlobal(gosec.Nosec, "true")
|
||||
nosecIgnoreConfig.SetGlobal(gosec.ShowIgnored, "true")
|
||||
customAnalyzer := gosec.NewAnalyzer(nosecIgnoreConfig, tests, false, logger)
|
||||
customAnalyzer.LoadRules(rules.Generate(rules.NewRuleFilter(false, "G401")).Builders())
|
||||
customAnalyzer := gosec.NewAnalyzer(nosecIgnoreConfig, tests, false, false, logger)
|
||||
customAnalyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G401")).RulesInfo())
|
||||
|
||||
nosecPackage := testutils.NewTestPackage()
|
||||
defer nosecPackage.Close()
|
||||
nosecSource := strings.Replace(source, "h := md5.New()", "h := md5.New() // #nosec", 1)
|
||||
nosecSource := strings.Replace(source, "h := md5.New()", "h := md5.New() //#nosec", 1)
|
||||
nosecPackage.AddFile("md5.go", nosecSource)
|
||||
err := nosecPackage.Build()
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
@@ -286,6 +303,74 @@ var _ = Describe("Analyzer", func() {
|
||||
Expect(metrics.NumNosec).Should(Equal(1))
|
||||
})
|
||||
|
||||
It("should not report errors when nosec tag is in front of a line", func() {
|
||||
sample := testutils.SampleCodeG401[0]
|
||||
source := sample.Code[0]
|
||||
analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G401")).RulesInfo())
|
||||
|
||||
nosecPackage := testutils.NewTestPackage()
|
||||
defer nosecPackage.Close()
|
||||
nosecSource := strings.Replace(source, "h := md5.New()", "//Some description\n//#nosec G401\nh := md5.New()", 1)
|
||||
nosecPackage.AddFile("md5.go", nosecSource)
|
||||
err := nosecPackage.Build()
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
err = analyzer.Process(buildTags, nosecPackage.Path)
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
nosecIssues, _, _ := analyzer.Report()
|
||||
Expect(nosecIssues).Should(BeEmpty())
|
||||
})
|
||||
|
||||
It("should report errors when nosec tag is not in front of a line", func() {
|
||||
sample := testutils.SampleCodeG401[0]
|
||||
source := sample.Code[0]
|
||||
analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G401")).RulesInfo())
|
||||
|
||||
nosecPackage := testutils.NewTestPackage()
|
||||
defer nosecPackage.Close()
|
||||
nosecSource := strings.Replace(source, "h := md5.New()", "//Some description\n//Another description #nosec G401\nh := md5.New()", 1)
|
||||
nosecPackage.AddFile("md5.go", nosecSource)
|
||||
err := nosecPackage.Build()
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
err = analyzer.Process(buildTags, nosecPackage.Path)
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
nosecIssues, _, _ := analyzer.Report()
|
||||
Expect(nosecIssues).Should(HaveLen(sample.Errors))
|
||||
})
|
||||
|
||||
It("should not report errors when rules are in front of nosec tag even rules are wrong", func() {
|
||||
sample := testutils.SampleCodeG401[0]
|
||||
source := sample.Code[0]
|
||||
analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G401")).RulesInfo())
|
||||
|
||||
nosecPackage := testutils.NewTestPackage()
|
||||
defer nosecPackage.Close()
|
||||
nosecSource := strings.Replace(source, "h := md5.New()", "//G301\n//#nosec\nh := md5.New()", 1)
|
||||
nosecPackage.AddFile("md5.go", nosecSource)
|
||||
err := nosecPackage.Build()
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
err = analyzer.Process(buildTags, nosecPackage.Path)
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
nosecIssues, _, _ := analyzer.Report()
|
||||
Expect(nosecIssues).Should(BeEmpty())
|
||||
})
|
||||
|
||||
It("should report errors when there are nosec tags after a #nosec WrongRuleList annotation", func() {
|
||||
sample := testutils.SampleCodeG401[0]
|
||||
source := sample.Code[0]
|
||||
analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G401")).RulesInfo())
|
||||
|
||||
nosecPackage := testutils.NewTestPackage()
|
||||
defer nosecPackage.Close()
|
||||
nosecSource := strings.Replace(source, "h := md5.New()", "//#nosec\n//G301\n//#nosec\nh := md5.New()", 1)
|
||||
nosecPackage.AddFile("md5.go", nosecSource)
|
||||
err := nosecPackage.Build()
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
err = analyzer.Process(buildTags, nosecPackage.Path)
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
nosecIssues, _, _ := analyzer.Report()
|
||||
Expect(nosecIssues).Should(HaveLen(sample.Errors))
|
||||
})
|
||||
|
||||
It("should be possible to use an alternative nosec tag", func() {
|
||||
// Rule for MD5 weak crypto usage
|
||||
sample := testutils.SampleCodeG401[0]
|
||||
@@ -294,8 +379,8 @@ var _ = Describe("Analyzer", func() {
|
||||
// overwrite nosec option
|
||||
nosecIgnoreConfig := gosec.NewConfig()
|
||||
nosecIgnoreConfig.SetGlobal(gosec.NoSecAlternative, "#falsePositive")
|
||||
customAnalyzer := gosec.NewAnalyzer(nosecIgnoreConfig, tests, false, logger)
|
||||
customAnalyzer.LoadRules(rules.Generate(rules.NewRuleFilter(false, "G401")).Builders())
|
||||
customAnalyzer := gosec.NewAnalyzer(nosecIgnoreConfig, tests, false, false, logger)
|
||||
customAnalyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G401")).RulesInfo())
|
||||
|
||||
nosecPackage := testutils.NewTestPackage()
|
||||
defer nosecPackage.Close()
|
||||
@@ -317,12 +402,12 @@ var _ = Describe("Analyzer", func() {
|
||||
// overwrite nosec option
|
||||
nosecIgnoreConfig := gosec.NewConfig()
|
||||
nosecIgnoreConfig.SetGlobal(gosec.NoSecAlternative, "#falsePositive")
|
||||
customAnalyzer := gosec.NewAnalyzer(nosecIgnoreConfig, tests, false, logger)
|
||||
customAnalyzer.LoadRules(rules.Generate(rules.NewRuleFilter(false, "G401")).Builders())
|
||||
customAnalyzer := gosec.NewAnalyzer(nosecIgnoreConfig, tests, false, false, logger)
|
||||
customAnalyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G401")).RulesInfo())
|
||||
|
||||
nosecPackage := testutils.NewTestPackage()
|
||||
defer nosecPackage.Close()
|
||||
nosecSource := strings.Replace(source, "h := md5.New()", "h := md5.New() // #nosec", 1)
|
||||
nosecSource := strings.Replace(source, "h := md5.New()", "h := md5.New() //#nosec", 1)
|
||||
nosecPackage.AddFile("md5.go", nosecSource)
|
||||
err := nosecPackage.Build()
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
@@ -333,8 +418,8 @@ var _ = Describe("Analyzer", func() {
|
||||
})
|
||||
|
||||
It("should be able to analyze Go test package", func() {
|
||||
customAnalyzer := gosec.NewAnalyzer(nil, true, false, logger)
|
||||
customAnalyzer.LoadRules(rules.Generate().Builders())
|
||||
customAnalyzer := gosec.NewAnalyzer(nil, true, false, false, logger)
|
||||
customAnalyzer.LoadRules(rules.Generate(false).RulesInfo())
|
||||
pkg := testutils.NewTestPackage()
|
||||
defer pkg.Close()
|
||||
pkg.AddFile("foo.go", `
|
||||
@@ -358,8 +443,8 @@ var _ = Describe("Analyzer", func() {
|
||||
Expect(issues).Should(HaveLen(1))
|
||||
})
|
||||
It("should be able to scan generated files if NOT excluded", func() {
|
||||
customAnalyzer := gosec.NewAnalyzer(nil, true, false, logger)
|
||||
customAnalyzer.LoadRules(rules.Generate().Builders())
|
||||
customAnalyzer := gosec.NewAnalyzer(nil, true, false, false, logger)
|
||||
customAnalyzer.LoadRules(rules.Generate(false).RulesInfo())
|
||||
pkg := testutils.NewTestPackage()
|
||||
defer pkg.Close()
|
||||
pkg.AddFile("foo.go", `
|
||||
@@ -379,8 +464,8 @@ var _ = Describe("Analyzer", func() {
|
||||
Expect(issues).Should(HaveLen(1))
|
||||
})
|
||||
It("should be able to skip generated files if excluded", func() {
|
||||
customAnalyzer := gosec.NewAnalyzer(nil, true, true, logger)
|
||||
customAnalyzer.LoadRules(rules.Generate().Builders())
|
||||
customAnalyzer := gosec.NewAnalyzer(nil, true, true, false, logger)
|
||||
customAnalyzer.LoadRules(rules.Generate(false).RulesInfo())
|
||||
pkg := testutils.NewTestPackage()
|
||||
defer pkg.Close()
|
||||
pkg.AddFile("foo.go", `
|
||||
@@ -401,7 +486,7 @@ var _ = Describe("Analyzer", func() {
|
||||
})
|
||||
})
|
||||
It("should be able to analyze Cgo files", func() {
|
||||
analyzer.LoadRules(rules.Generate().Builders())
|
||||
analyzer.LoadRules(rules.Generate(false).RulesInfo())
|
||||
sample := testutils.SampleCodeCgo[0]
|
||||
source := sample.Code[0]
|
||||
|
||||
@@ -583,4 +668,125 @@ var _ = Describe("Analyzer", func() {
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
Context("when tracking suppressions", func() {
|
||||
BeforeEach(func() {
|
||||
analyzer = gosec.NewAnalyzer(nil, tests, false, true, logger)
|
||||
})
|
||||
|
||||
It("should not report an error if the violation is suppressed", func() {
|
||||
sample := testutils.SampleCodeG401[0]
|
||||
source := sample.Code[0]
|
||||
analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G401")).RulesInfo())
|
||||
|
||||
nosecPackage := testutils.NewTestPackage()
|
||||
defer nosecPackage.Close()
|
||||
nosecSource := strings.Replace(source, "h := md5.New()", "h := md5.New() //#nosec G401 -- Justification", 1)
|
||||
nosecPackage.AddFile("md5.go", nosecSource)
|
||||
err := nosecPackage.Build()
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
err = analyzer.Process(buildTags, nosecPackage.Path)
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
issues, _, _ := analyzer.Report()
|
||||
Expect(issues).To(HaveLen(sample.Errors))
|
||||
Expect(issues[0].Suppressions).To(HaveLen(1))
|
||||
Expect(issues[0].Suppressions[0].Kind).To(Equal("inSource"))
|
||||
Expect(issues[0].Suppressions[0].Justification).To(Equal("Justification"))
|
||||
})
|
||||
|
||||
It("should not report an error if the violation is suppressed without certain rules", func() {
|
||||
sample := testutils.SampleCodeG401[0]
|
||||
source := sample.Code[0]
|
||||
analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G401")).RulesInfo())
|
||||
|
||||
nosecPackage := testutils.NewTestPackage()
|
||||
defer nosecPackage.Close()
|
||||
nosecSource := strings.Replace(source, "h := md5.New()", "h := md5.New() //#nosec", 1)
|
||||
nosecPackage.AddFile("md5.go", nosecSource)
|
||||
err := nosecPackage.Build()
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
err = analyzer.Process(buildTags, nosecPackage.Path)
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
issues, _, _ := analyzer.Report()
|
||||
Expect(issues).To(HaveLen(sample.Errors))
|
||||
Expect(issues[0].Suppressions).To(HaveLen(1))
|
||||
Expect(issues[0].Suppressions[0].Kind).To(Equal("inSource"))
|
||||
Expect(issues[0].Suppressions[0].Justification).To(Equal(""))
|
||||
})
|
||||
|
||||
It("should track multiple suppressions if the violation is suppressed by both #nosec and #nosec RuleList", func() {
|
||||
sample := testutils.SampleCodeG101[0]
|
||||
source := sample.Code[0]
|
||||
analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, "G101")).RulesInfo())
|
||||
|
||||
nosecPackage := testutils.NewTestPackage()
|
||||
defer nosecPackage.Close()
|
||||
nosecSource := strings.Replace(source, "}", "} //#nosec G101 -- Justification", 1)
|
||||
nosecSource = strings.Replace(nosecSource, "func", "//#nosec\nfunc", 1)
|
||||
nosecPackage.AddFile("pwd.go", nosecSource)
|
||||
err := nosecPackage.Build()
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
err = analyzer.Process(buildTags, nosecPackage.Path)
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
issues, _, _ := analyzer.Report()
|
||||
Expect(issues).To(HaveLen(sample.Errors))
|
||||
Expect(issues[0].Suppressions).To(HaveLen(2))
|
||||
})
|
||||
|
||||
It("should not report an error if the rule is not included", func() {
|
||||
sample := testutils.SampleCodeG101[0]
|
||||
source := sample.Code[0]
|
||||
analyzer.LoadRules(rules.Generate(true, rules.NewRuleFilter(false, "G401")).RulesInfo())
|
||||
|
||||
controlPackage := testutils.NewTestPackage()
|
||||
defer controlPackage.Close()
|
||||
controlPackage.AddFile("pwd.go", source)
|
||||
err := controlPackage.Build()
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
err = analyzer.Process(buildTags, controlPackage.Path)
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
controlIssues, _, _ := analyzer.Report()
|
||||
Expect(controlIssues).Should(HaveLen(sample.Errors))
|
||||
Expect(controlIssues[0].Suppressions).To(HaveLen(1))
|
||||
Expect(controlIssues[0].Suppressions[0].Kind).To(Equal("external"))
|
||||
Expect(controlIssues[0].Suppressions[0].Justification).To(Equal("Globally suppressed."))
|
||||
})
|
||||
|
||||
It("should not report an error if the rule is excluded", func() {
|
||||
sample := testutils.SampleCodeG101[0]
|
||||
source := sample.Code[0]
|
||||
analyzer.LoadRules(rules.Generate(true, rules.NewRuleFilter(true, "G101")).RulesInfo())
|
||||
|
||||
controlPackage := testutils.NewTestPackage()
|
||||
defer controlPackage.Close()
|
||||
controlPackage.AddFile("pwd.go", source)
|
||||
err := controlPackage.Build()
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
err = analyzer.Process(buildTags, controlPackage.Path)
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
issues, _, _ := analyzer.Report()
|
||||
Expect(issues).Should(HaveLen(sample.Errors))
|
||||
Expect(issues[0].Suppressions).To(HaveLen(1))
|
||||
Expect(issues[0].Suppressions[0].Kind).To(Equal("external"))
|
||||
Expect(issues[0].Suppressions[0].Justification).To(Equal("Globally suppressed."))
|
||||
})
|
||||
|
||||
It("should track multiple suppressions if the violation is multiply suppressed", func() {
|
||||
sample := testutils.SampleCodeG101[0]
|
||||
source := sample.Code[0]
|
||||
analyzer.LoadRules(rules.Generate(true, rules.NewRuleFilter(true, "G101")).RulesInfo())
|
||||
|
||||
nosecPackage := testutils.NewTestPackage()
|
||||
defer nosecPackage.Close()
|
||||
nosecSource := strings.Replace(source, "}", "} //#nosec G101 -- Justification", 1)
|
||||
nosecPackage.AddFile("pwd.go", nosecSource)
|
||||
err := nosecPackage.Build()
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
err = analyzer.Process(buildTags, nosecPackage.Path)
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
issues, _, _ := analyzer.Report()
|
||||
Expect(issues).Should(HaveLen(sample.Errors))
|
||||
Expect(issues[0].Suppressions).To(HaveLen(2))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -3,7 +3,7 @@ package gosec_test
|
||||
import (
|
||||
"go/ast"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/securego/gosec/v2"
|
||||
"github.com/securego/gosec/v2/testutils"
|
||||
|
||||
@@ -70,7 +70,7 @@ func (a *arrayFlags) Set(value string) error {
|
||||
}
|
||||
|
||||
var (
|
||||
// #nosec flag
|
||||
//#nosec flag
|
||||
flagIgnoreNoSec = flag.Bool("nosec", false, "Ignores #nosec comments when set")
|
||||
|
||||
// show ignored
|
||||
@@ -79,7 +79,7 @@ var (
|
||||
// format output
|
||||
flagFormat = flag.String("fmt", "text", "Set output format. Valid options are: json, yaml, csv, junit-xml, html, sonarqube, golint, sarif or text")
|
||||
|
||||
// #nosec alternative tag
|
||||
//#nosec alternative tag
|
||||
flagAlternativeNoSec = flag.String("nosec-tag", "", "Set an alternative string for #nosec. Some examples: #dontanalyze, #falsepositive")
|
||||
|
||||
// output file
|
||||
@@ -132,13 +132,16 @@ var (
|
||||
// overrides the output format when stdout the results while saving them in the output file
|
||||
flagVerbose = flag.String("verbose", "", "Overrides the output format when stdout the results while saving them in the output file.\nValid options are: json, yaml, csv, junit-xml, html, sonarqube, golint, sarif or text")
|
||||
|
||||
// output suppression information for auditing purposes
|
||||
flagTrackSuppressions = flag.Bool("track-suppressions", false, "Output suppression information, including its kind and justification")
|
||||
|
||||
// exlude the folders from scan
|
||||
flagDirsExclude arrayFlags
|
||||
|
||||
logger *log.Logger
|
||||
)
|
||||
|
||||
// #nosec
|
||||
//#nosec
|
||||
func usage() {
|
||||
usageText := fmt.Sprintf(usageText, Version, GitTag, BuildDate)
|
||||
fmt.Fprintln(os.Stderr, usageText)
|
||||
@@ -147,14 +150,14 @@ func usage() {
|
||||
fmt.Fprint(os.Stderr, "\n\nRULES:\n\n")
|
||||
|
||||
// sorted rule list for ease of reading
|
||||
rl := rules.Generate()
|
||||
keys := make([]string, 0, len(rl))
|
||||
for key := range rl {
|
||||
rl := rules.Generate(*flagTrackSuppressions)
|
||||
keys := make([]string, 0, len(rl.Rules))
|
||||
for key := range rl.Rules {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
for _, k := range keys {
|
||||
v := rl[k]
|
||||
v := rl.Rules[k]
|
||||
fmt.Fprintf(os.Stderr, "\t%s: %s\n", k, v.Description)
|
||||
}
|
||||
fmt.Fprint(os.Stderr, "\n")
|
||||
@@ -163,12 +166,12 @@ func usage() {
|
||||
func loadConfig(configFile string) (gosec.Config, error) {
|
||||
config := gosec.NewConfig()
|
||||
if configFile != "" {
|
||||
// #nosec
|
||||
//#nosec
|
||||
file, err := os.Open(configFile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close() // #nosec G307
|
||||
defer file.Close() //#nosec G307
|
||||
if _, err := config.ReadFrom(file); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -182,6 +185,14 @@ func loadConfig(configFile string) (gosec.Config, error) {
|
||||
if *flagAlternativeNoSec != "" {
|
||||
config.SetGlobal(gosec.NoSecAlternative, *flagAlternativeNoSec)
|
||||
}
|
||||
// set global option IncludeRules ,when flag set or global option IncludeRules is nil
|
||||
if v, _ := config.GetGlobal(gosec.IncludeRules); *flagRulesInclude != "" || v == "" {
|
||||
config.SetGlobal(gosec.IncludeRules, *flagRulesInclude)
|
||||
}
|
||||
// set global option ExcludeRules ,when flag set or global option IncludeRules is nil
|
||||
if v, _ := config.GetGlobal(gosec.ExcludeRules); flagRulesExclude.String() != "" || v == "" {
|
||||
config.SetGlobal(gosec.ExcludeRules, flagRulesExclude.String())
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
|
||||
@@ -202,7 +213,7 @@ func loadRules(include, exclude string) rules.RuleList {
|
||||
} else {
|
||||
logger.Println("Excluding rules: default")
|
||||
}
|
||||
return rules.Generate(filters...)
|
||||
return rules.Generate(*flagTrackSuppressions, filters...)
|
||||
}
|
||||
|
||||
func getRootPaths(paths []string) []string {
|
||||
@@ -235,11 +246,11 @@ func printReport(format string, color bool, rootPaths []string, reportInfo *gose
|
||||
}
|
||||
|
||||
func saveReport(filename, format string, rootPaths []string, reportInfo *gosec.ReportInfo) error {
|
||||
outfile, err := os.Create(filename)
|
||||
outfile, err := os.Create(filename) //#nosec G304
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer outfile.Close() // #nosec G307
|
||||
defer outfile.Close() //#nosec G307
|
||||
err = report.CreateReport(outfile, format, false, rootPaths, reportInfo)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -247,9 +258,9 @@ func saveReport(filename, format string, rootPaths []string, reportInfo *gosec.R
|
||||
return nil
|
||||
}
|
||||
|
||||
func convertToScore(severity string) (gosec.Score, error) {
|
||||
severity = strings.ToLower(severity)
|
||||
switch severity {
|
||||
func convertToScore(value string) (gosec.Score, error) {
|
||||
value = strings.ToLower(value)
|
||||
switch value {
|
||||
case "low":
|
||||
return gosec.Low, nil
|
||||
case "medium":
|
||||
@@ -257,7 +268,7 @@ func convertToScore(severity string) (gosec.Score, error) {
|
||||
case "high":
|
||||
return gosec.High, nil
|
||||
default:
|
||||
return gosec.Low, fmt.Errorf("provided severity '%s' not valid. Valid options: low, medium, high", severity)
|
||||
return gosec.Low, fmt.Errorf("provided value '%s' not valid. Valid options: low, medium, high", value)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -267,7 +278,7 @@ func filterIssues(issues []*gosec.Issue, severity gosec.Score, confidence gosec.
|
||||
for _, issue := range issues {
|
||||
if issue.Severity >= severity && issue.Confidence >= confidence {
|
||||
result = append(result, issue)
|
||||
if !issue.NoSec || !*flagShowIgnored {
|
||||
if (!issue.NoSec || !*flagShowIgnored) && len(issue.Suppressions) == 0 {
|
||||
trueIssues++
|
||||
}
|
||||
}
|
||||
@@ -306,7 +317,7 @@ func main() {
|
||||
|
||||
// Ensure at least one file was specified
|
||||
if flag.NArg() == 0 {
|
||||
fmt.Fprintf(os.Stderr, "\nError: FILE [FILE...] or './...' expected\n") // #nosec
|
||||
fmt.Fprintf(os.Stderr, "\nError: FILE [FILE...] or './...' expected\n") //#nosec
|
||||
flag.Usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
@@ -345,14 +356,23 @@ func main() {
|
||||
}
|
||||
|
||||
// Load enabled rule definitions
|
||||
ruleDefinitions := loadRules(*flagRulesInclude, flagRulesExclude.String())
|
||||
if len(ruleDefinitions) == 0 {
|
||||
excludeRules, err := config.GetGlobal(gosec.ExcludeRules)
|
||||
if err != nil {
|
||||
logger.Fatal(err)
|
||||
}
|
||||
includeRules, err := config.GetGlobal(gosec.IncludeRules)
|
||||
if err != nil {
|
||||
logger.Fatal(err)
|
||||
}
|
||||
|
||||
ruleList := loadRules(includeRules, excludeRules)
|
||||
if len(ruleList.Rules) == 0 {
|
||||
logger.Fatal("No rules are configured")
|
||||
}
|
||||
|
||||
// Create the analyzer
|
||||
analyzer := gosec.NewAnalyzer(config, *flagScanTests, *flagExcludeGenerated, logger)
|
||||
analyzer.LoadRules(ruleDefinitions.Builders())
|
||||
analyzer := gosec.NewAnalyzer(config, *flagScanTests, *flagExcludeGenerated, *flagTrackSuppressions, logger)
|
||||
analyzer.LoadRules(ruleList.RulesInfo())
|
||||
|
||||
excludedDirs := gosec.ExcludedDirsRegExp(flagDirsExclude)
|
||||
var packages []string
|
||||
@@ -414,7 +434,7 @@ func main() {
|
||||
}
|
||||
|
||||
// Finalize logging
|
||||
logWriter.Close() // #nosec
|
||||
logWriter.Close() //#nosec
|
||||
|
||||
// Do we have an issue? If so exit 1 unless NoFail is set
|
||||
if (len(issues) > 0 || len(errors) > 0) && !*flagNoFail {
|
||||
|
||||
@@ -3,7 +3,7 @@ package main
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/securego/gosec/v2"
|
||||
)
|
||||
@@ -38,7 +38,7 @@ func firstIsGreater(less, greater *gosec.Issue) {
|
||||
}
|
||||
|
||||
var _ = Describe("Sorting by Severity", func() {
|
||||
It("sortes by severity", func() {
|
||||
It("sorts by severity", func() {
|
||||
less := createIssue()
|
||||
less.Severity = gosec.Low
|
||||
greater := createIssue()
|
||||
@@ -46,8 +46,8 @@ var _ = Describe("Sorting by Severity", func() {
|
||||
firstIsGreater(&less, &greater)
|
||||
})
|
||||
|
||||
Context("Serverity is same", func() {
|
||||
It("sortes by What", func() {
|
||||
Context("Severity is same", func() {
|
||||
It("sorts by What", func() {
|
||||
less := createIssue()
|
||||
less.What = "test1"
|
||||
greater := createIssue()
|
||||
@@ -56,8 +56,8 @@ var _ = Describe("Sorting by Severity", func() {
|
||||
})
|
||||
})
|
||||
|
||||
Context("Serverity and What is same", func() {
|
||||
It("sortes by File", func() {
|
||||
Context("Severity and What is same", func() {
|
||||
It("sorts by File", func() {
|
||||
less := createIssue()
|
||||
less.File = "test1"
|
||||
greater := createIssue()
|
||||
@@ -67,8 +67,8 @@ var _ = Describe("Sorting by Severity", func() {
|
||||
})
|
||||
})
|
||||
|
||||
Context("Serverity, What and File is same", func() {
|
||||
It("sortes by line number", func() {
|
||||
Context("Severity, What and File is same", func() {
|
||||
It("sorts by line number", func() {
|
||||
less := createIssue()
|
||||
less.Line = "1"
|
||||
greater := createIssue()
|
||||
|
||||
@@ -76,12 +76,12 @@ func (u *utilities) run(args ...string) {
|
||||
func shouldSkip(path string) bool {
|
||||
st, e := os.Stat(path)
|
||||
if e != nil {
|
||||
// #nosec
|
||||
//#nosec
|
||||
fmt.Fprintf(os.Stderr, "Skipping: %s - %s\n", path, e)
|
||||
return true
|
||||
}
|
||||
if st.IsDir() {
|
||||
// #nosec
|
||||
//#nosec
|
||||
fmt.Fprintf(os.Stderr, "Skipping: %s - directory\n", path)
|
||||
return true
|
||||
}
|
||||
@@ -99,12 +99,12 @@ func dumpAst(files ...string) {
|
||||
fset := token.NewFileSet() // positions are relative to fset
|
||||
f, err := parser.ParseFile(fset, arg, nil, 0)
|
||||
if err != nil {
|
||||
// #nosec
|
||||
//#nosec
|
||||
fmt.Fprintf(os.Stderr, "Unable to parse file %s\n", err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Print the AST. #nosec
|
||||
//#nosec -- Print the AST.
|
||||
ast.Print(fset, f)
|
||||
}
|
||||
}
|
||||
@@ -122,7 +122,7 @@ func createContext(filename string) *context {
|
||||
fileset := token.NewFileSet()
|
||||
root, e := parser.ParseFile(fileset, filename, nil, parser.ParseComments)
|
||||
if e != nil {
|
||||
// #nosec
|
||||
//#nosec
|
||||
fmt.Fprintf(os.Stderr, "Unable to parse file: %s. Reason: %s\n", filename, e)
|
||||
return nil
|
||||
}
|
||||
@@ -138,7 +138,7 @@ func createContext(filename string) *context {
|
||||
config := types.Config{Importer: importer.Default()}
|
||||
pkg, e := config.Check("main.go", fileset, []*ast.File{root}, info)
|
||||
if e != nil {
|
||||
// #nosec
|
||||
//#nosec
|
||||
fmt.Fprintf(os.Stderr, "Type check failed for file: %s. Reason: %s\n", filename, e)
|
||||
return nil
|
||||
}
|
||||
@@ -163,7 +163,7 @@ func printObject(obj types.Object) {
|
||||
}
|
||||
|
||||
func checkContext(ctx *context, file string) bool {
|
||||
// #nosec
|
||||
//#nosec
|
||||
if ctx == nil {
|
||||
fmt.Fprintln(os.Stderr, "Failed to create context for file: ", file)
|
||||
return false
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//go:build go1.12 && !go1.14
|
||||
// +build go1.12,!go1.14
|
||||
|
||||
// This file can be removed once go1.13 is no longer supported
|
||||
|
||||
@@ -66,7 +66,7 @@ type goTLSConfiguration struct {
|
||||
|
||||
// getTLSConfFromURL retrieves the json containing the TLS configurations from the specified URL.
|
||||
func getTLSConfFromURL(url string) (*ServerSideTLSJson, error) {
|
||||
r, err := http.Get(url) // #nosec G107
|
||||
r, err := http.Get(url) //#nosec G107
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -190,5 +190,5 @@ func main() {
|
||||
outputPath := filepath.Join(dir, *outputFile)
|
||||
if err := ioutil.WriteFile(outputPath, src, 0o644); err != nil {
|
||||
log.Fatalf("Writing output: %s", err)
|
||||
} // #nosec G306
|
||||
} //#nosec G306
|
||||
}
|
||||
|
||||
@@ -26,6 +26,10 @@ const (
|
||||
Audit GlobalOption = "audit"
|
||||
// NoSecAlternative global option alternative for #nosec directive
|
||||
NoSecAlternative GlobalOption = "#nosec"
|
||||
// ExcludeRules global option for some rules should not be load
|
||||
ExcludeRules GlobalOption = "exclude"
|
||||
// IncludeRules global option for should be load
|
||||
IncludeRules GlobalOption = "include"
|
||||
)
|
||||
|
||||
// Config is used to provide configuration and customization to each of the rules.
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"bytes"
|
||||
"strings"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/securego/gosec/v2"
|
||||
)
|
||||
|
||||
@@ -3,7 +3,7 @@ package cwe_test
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package cwe_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/securego/gosec/v2/cwe"
|
||||
)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package cwe_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/securego/gosec/v2/cwe"
|
||||
)
|
||||
|
||||
@@ -20,7 +20,7 @@ func NewError(line, column int, err string) *Error {
|
||||
}
|
||||
}
|
||||
|
||||
// sortErros sorts the golang erros by line
|
||||
// sortErrors sorts the golang errors by line
|
||||
func sortErrors(allErrors map[string][]Error) {
|
||||
for _, errors := range allErrors {
|
||||
sort.Slice(errors, func(i, j int) bool {
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"flag"
|
||||
"os"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/securego/gosec/v2/cmd/vflag"
|
||||
)
|
||||
|
||||
12
go.mod
12
go.mod
@@ -2,16 +2,16 @@ module github.com/securego/gosec/v2
|
||||
|
||||
require (
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/gookit/color v1.4.2
|
||||
github.com/lib/pq v1.10.3
|
||||
github.com/gookit/color v1.5.0
|
||||
github.com/lib/pq v1.10.4
|
||||
github.com/mozilla/tls-observatory v0.0.0-20210609171429-7bc42856d2e5
|
||||
github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354
|
||||
github.com/onsi/ginkgo v1.16.4
|
||||
github.com/onsi/gomega v1.16.0
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519
|
||||
github.com/onsi/ginkgo/v2 v2.0.0
|
||||
github.com/onsi/gomega v1.17.0
|
||||
golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce
|
||||
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616
|
||||
golang.org/x/text v0.3.7
|
||||
golang.org/x/tools v0.1.7
|
||||
golang.org/x/tools v0.1.8
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
||||
|
||||
|
||||
43
go.sum
43
go.sum
@@ -157,6 +157,7 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf
|
||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200507031123-427632fa3b1c/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/trillian v1.3.11/go.mod h1:0tPraVHrSDkA3BO6vKX67zgLXs6SsOAbHEivX+9mPgw=
|
||||
github.com/google/uuid v0.0.0-20161128191214-064e2069ce9c/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
@@ -166,8 +167,8 @@ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/gookit/color v1.4.2 h1:tXy44JFSFkKnELV6WaMo/lLfu/meqITX3iAV52do7lk=
|
||||
github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ=
|
||||
github.com/gookit/color v1.5.0 h1:1Opow3+BWDwqor78DcJkJCIwnkviFi+rrOANki9BUFw=
|
||||
github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo=
|
||||
github.com/gordonklaus/ineffassign v0.0.0-20200309095847-7953dde2c7bf/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU=
|
||||
github.com/gorhill/cronexpr v0.0.0-20180427100037-88b0669f7d75/go.mod h1:g2644b03hfBX9Ov0ZBDgXXens4rxSxmqFBbhvKv2yVA=
|
||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
@@ -187,6 +188,7 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO
|
||||
github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo=
|
||||
github.com/huandu/xstrings v1.2.0/go.mod h1:DvyZB1rfVYsBIigL8HwpZgxHwXozlTgGqn63UyNX5k4=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/imdario/mergo v0.3.4/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
@@ -219,8 +221,8 @@ github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+
|
||||
github.com/letsencrypt/pkcs11key/v4 v4.0.0/go.mod h1:EFUvBDay26dErnNb70Nd0/VW3tJiIbETBPTl9ATXQag=
|
||||
github.com/lib/pq v1.8.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lib/pq v1.10.3 h1:v9QZf2Sn6AmjXtQeFpdoq/eaNtYP6IN+7lcrygsIAtg=
|
||||
github.com/lib/pq v1.10.3/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk=
|
||||
github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
|
||||
@@ -266,10 +268,12 @@ github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
|
||||
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
||||
github.com/onsi/ginkgo/v2 v2.0.0 h1:CcuG/HvWNkkaqCUpJifQY8z7qEMBJya6aLPx6ftGyjQ=
|
||||
github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/onsi/gomega v1.16.0 h1:6gjqkI8iiRHMvdccRJM8rVKjCWk6ZIm6FTm3ddIe4/c=
|
||||
github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
||||
github.com/onsi/gomega v1.17.0 h1:9Luw4uT5HTjHTN8+aNcSThgH1vdXnmdJ8xIfZ4wyTRE=
|
||||
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
|
||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
|
||||
@@ -322,8 +326,9 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20200427203606-3cfed13b9966/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
@@ -343,7 +348,7 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/bbolt v1.3.4/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
|
||||
go.etcd.io/etcd v0.0.0-20200513171258-e048e166ab9c/go.mod h1:xCI7ZzBfRuGgBXyXO6yfWfDmlWd35khcWpUa4L0xI/k=
|
||||
@@ -371,8 +376,8 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce h1:Roh6XWxHFKrPgC/EQhVubSAGQ6Ozk6IdxHSzt1mR0EI=
|
||||
golang.org/x/crypto v0.0.0-20220112180741-5e0467b6c7ce/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@@ -406,8 +411,8 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.5.1 h1:OJxoQ/rynoF0dcCdI7cLPktw/hR2cueqYfjm43oqK38=
|
||||
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -442,10 +447,10 @@ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d h1:20cMwl2fHAzkJMEA+8J4JgqBQcQGzbisXo31MIeenXI=
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2 h1:CIJ76btIcR3eFI5EgSo6k1qKw9KJexJuRLI9G7Hp5wE=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@@ -510,8 +515,8 @@ golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e h1:WUoyKPm6nCo1BnNUvPGnFG3T5DUVem42yDJZZ4CNxMA=
|
||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654 h1:id054HUawV2/6IGm2IV8KZQjqtwAOo2CYlOToYqa0d0=
|
||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1 h1:v+OssWQX+hTHEmOBgwxdZxK4zHq3yOs8F9J7mk0PY8E=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
@@ -575,8 +580,8 @@ golang.org/x/tools v0.0.0-20200626171337-aa94e735be7f/go.mod h1:EkVYQZoAsY45+roY
|
||||
golang.org/x/tools v0.0.0-20200630154851-b2d8b0336632/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200706234117-b22de6825cf7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.7 h1:6j8CgantCy3yc8JGBqkDLMKWqZ0RDU2g1HVgacojGWQ=
|
||||
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
|
||||
golang.org/x/tools v0.1.8 h1:P1HhGGuLW4aAclzjtmJdf0mJOjVUZUzOTqkAkWL+l6w=
|
||||
golang.org/x/tools v0.1.8/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
|
||||
@@ -3,7 +3,7 @@ package gosec_test
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/securego/gosec/v2"
|
||||
"github.com/securego/gosec/v2/testutils"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package gosec_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/securego/gosec/v2"
|
||||
"github.com/securego/gosec/v2/testutils"
|
||||
|
||||
29
issue.go
29
issue.go
@@ -88,16 +88,17 @@ var ruleToCWE = map[string]string{
|
||||
|
||||
// Issue is returned by a gosec rule if it discovers an issue with the scanned code.
|
||||
type Issue struct {
|
||||
Severity Score `json:"severity"` // issue severity (how problematic it is)
|
||||
Confidence Score `json:"confidence"` // issue confidence (how sure we are we found it)
|
||||
Cwe *cwe.Weakness `json:"cwe"` // Cwe associated with RuleID
|
||||
RuleID string `json:"rule_id"` // Human readable explanation
|
||||
What string `json:"details"` // Human readable explanation
|
||||
File string `json:"file"` // File name we found it in
|
||||
Code string `json:"code"` // Impacted code line
|
||||
Line string `json:"line"` // Line number in file
|
||||
Col string `json:"column"` // Column number in line
|
||||
NoSec bool `json:"nosec"` // true if the issue is nosec
|
||||
Severity Score `json:"severity"` // issue severity (how problematic it is)
|
||||
Confidence Score `json:"confidence"` // issue confidence (how sure we are we found it)
|
||||
Cwe *cwe.Weakness `json:"cwe"` // Cwe associated with RuleID
|
||||
RuleID string `json:"rule_id"` // Human readable explanation
|
||||
What string `json:"details"` // Human readable explanation
|
||||
File string `json:"file"` // File name we found it in
|
||||
Code string `json:"code"` // Impacted code line
|
||||
Line string `json:"line"` // Line number in file
|
||||
Col string `json:"column"` // Column number in line
|
||||
NoSec bool `json:"nosec"` // true if the issue is nosec
|
||||
Suppressions []SuppressionInfo `json:"suppressions"` // Suppression info of the issue
|
||||
}
|
||||
|
||||
// FileLocation point out the file path and line number in file
|
||||
@@ -179,7 +180,7 @@ func NewIssue(ctx *Context, node ast.Node, ruleID, desc string, severity Score,
|
||||
|
||||
var code string
|
||||
if file, err := os.Open(fobj.Name()); err == nil {
|
||||
defer file.Close() // #nosec
|
||||
defer file.Close() //#nosec
|
||||
s := codeSnippetStartLine(node, fobj)
|
||||
e := codeSnippetEndLine(node, fobj)
|
||||
code, err = codeSnippet(file, s, e, node)
|
||||
@@ -200,3 +201,9 @@ func NewIssue(ctx *Context, node ast.Node, ruleID, desc string, severity Score,
|
||||
Cwe: GetCweByRule(ruleID),
|
||||
}
|
||||
}
|
||||
|
||||
// WithSuppressions set the suppressions of the issue
|
||||
func (i *Issue) WithSuppressions(suppressions []SuppressionInfo) *Issue {
|
||||
i.Suppressions = suppressions
|
||||
return i
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package gosec_test
|
||||
import (
|
||||
"go/ast"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/securego/gosec/v2"
|
||||
"github.com/securego/gosec/v2/rules"
|
||||
|
||||
@@ -53,6 +53,9 @@ const (
|
||||
// the specified format. The formats currently accepted are: json, yaml, csv, junit-xml, html, sonarqube, golint and text.
|
||||
func CreateReport(w io.Writer, format string, enableColor bool, rootPaths []string, data *gosec.ReportInfo) error {
|
||||
var err error
|
||||
if format != "json" && format != "sarif" {
|
||||
data.Issues = filterOutSuppressedIssues(data.Issues)
|
||||
}
|
||||
switch format {
|
||||
case "json":
|
||||
err = json.WriteReport(w, data)
|
||||
@@ -77,3 +80,13 @@ func CreateReport(w io.Writer, format string, enableColor bool, rootPaths []stri
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func filterOutSuppressedIssues(issues []*gosec.Issue) []*gosec.Issue {
|
||||
nonSuppressedIssues := []*gosec.Issue{}
|
||||
for _, issue := range issues {
|
||||
if len(issue.Suppressions) == 0 {
|
||||
nonSuppressedIssues = append(nonSuppressedIssues, issue)
|
||||
}
|
||||
}
|
||||
return nonSuppressedIssues
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package report
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/securego/gosec/v2"
|
||||
"github.com/securego/gosec/v2/cwe"
|
||||
@@ -486,4 +486,53 @@ var _ = Describe("Formatter", func() {
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
Context("When converting suppressed issues", func() {
|
||||
ruleID := "G101"
|
||||
cwe := gosec.GetCweByRule(ruleID)
|
||||
suppressions := []gosec.SuppressionInfo{
|
||||
{
|
||||
Kind: "kind",
|
||||
Justification: "justification",
|
||||
},
|
||||
}
|
||||
suppressedIssue := createIssue(ruleID, cwe)
|
||||
suppressedIssue.WithSuppressions(suppressions)
|
||||
|
||||
It("text formatted report should contain the suppressed issues", func() {
|
||||
error := map[string][]gosec.Error{}
|
||||
reportInfo := gosec.NewReportInfo([]*gosec.Issue{&suppressedIssue}, &gosec.Metrics{}, error)
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
err := CreateReport(buf, "text", false, []string{}, reportInfo)
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
|
||||
result := stripString(buf.String())
|
||||
Expect(result).To(ContainSubstring("Results:Summary"))
|
||||
})
|
||||
|
||||
It("sarif formatted report should contain the suppressed issues", func() {
|
||||
error := map[string][]gosec.Error{}
|
||||
reportInfo := gosec.NewReportInfo([]*gosec.Issue{&suppressedIssue}, &gosec.Metrics{}, error)
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
err := CreateReport(buf, "sarif", false, []string{}, reportInfo)
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
|
||||
result := stripString(buf.String())
|
||||
Expect(result).To(ContainSubstring(`"results":[{`))
|
||||
})
|
||||
|
||||
It("json formatted report should contain the suppressed issues", func() {
|
||||
error := map[string][]gosec.Error{}
|
||||
reportInfo := gosec.NewReportInfo([]*gosec.Issue{&suppressedIssue}, &gosec.Metrics{}, error)
|
||||
|
||||
buf := new(bytes.Buffer)
|
||||
err := CreateReport(buf, "json", false, []string{}, reportInfo)
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
|
||||
result := stripString(buf.String())
|
||||
Expect(result).To(ContainSubstring(`"Issues":[{`))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,33 +1,16 @@
|
||||
// (c) Copyright 2016 Hewlett Packard Enterprise Development LP
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package html
|
||||
|
||||
const templateContent = `
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Golang Security Checker</title>
|
||||
<link rel="shortcut icon" type="image/png" href="https://securego.io/img/favicon.png">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.9.2/css/bulma.min.css" integrity="sha512-byErQdWdTqREz6DLAA9pCnLbdoGGhXfU6gm1c8bkf7F51JVmUBlayGe2A31VpXWQP+eiJ3ilTAZHCR3vmMyybA==" crossorigin="anonymous"/>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.7.2/styles/default.min.css" integrity="sha512-kZqGbhf9JTB4bVJ0G8HCkqmaPcRgo88F0dneK30yku5Y/dep7CZfCnNml2Je/sY4lBoqoksXz4PtVXS4GHSUzQ==" crossorigin="anonymous"/>
|
||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.7.2/highlight.min.js" integrity="sha512-s+tOYYcC3Jybgr9mVsdAxsRYlGNq4mlAurOrfNuGMQ/SCofNPu92tjE7YRZCsdEtWL1yGkqk15fU/ark206YTg==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.7.2/languages/go.min.js" integrity="sha512-+UYV2NyyynWEQcZ4sMTKmeppyV331gqvMOGZ61/dqc89Tn1H40lF05ACd03RSD9EWwGutNwKj256mIR8waEJBQ==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.2/react.min.js" integrity="sha256-cLWs9L+cjZg8CjGHMpJqUgKKouPlmoMP/0wIdPtaPGs=" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/react/15.3.2/react-dom.min.js" integrity="sha256-JIW8lNqN2EtqC6ggNZYnAdKMJXRQfkPMvdRt+b0/Jxc=" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.17.0/babel.min.js" integrity="sha256-1IWWLlCKFGFj/cjryvC7GDF5wRYnf9tSvNVVEj8Bm+o=" crossorigin="anonymous"></script>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.9.3/css/bulma.min.css" integrity="sha512-IgmDkwzs96t4SrChW29No3NXBIBv8baW490zk5aXvhCD8vuZM3yUSkbyTBcXohkySecyzIrUwiF/qV0cuPcL3Q==" crossorigin="anonymous"/>
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.4.0/styles/default.min.css" integrity="sha512-hasIneQUHlh06VNBe7f6ZcHmeRTLIaQWFd43YriJ0UND19bvYRauxthDg8E4eVNPm9bRUhr5JGeqH7FRFXQu5g==" crossorigin="anonymous"/>
|
||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.4.0/highlight.min.js" integrity="sha512-IaaKO80nPNs5j+VLxd42eK/7sYuXQmr+fyywCNA0e+C6gtQnuCXNtORe9xR4LqGPz5U9VpH+ff41wKs/ZmC3iA==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.4.0/languages/go.min.js" integrity="sha512-cSV8KK6UAf1DR6Fh7+AU8Vn9q/X1CX60ktQ4R1gfaWuRnGL30r7LPiCdI3AdyiIjcalKZnyAkw5xH1QZQkDT7A==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/react/15.7.0/react.min.js" integrity="sha512-+TFn1Gqbwx/qgwW3NU1/YtFYTfHGeD1e/8YfJZzkb6TFEZP4SUwp1Az9DMeWh3qC0F+YPKXbV3YclMUwBTvO3g==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/react/15.6.1/react-dom.min.js" integrity="sha512-8C49ZG/SaQnWaUgCHTU1o8uIQNYE6R8me38SwF26g2Q0byEXF4Jlvm+T/JAMHMeTBiEVPslSZRv9Xt4AV0pfmw==" crossorigin="anonymous"></script>
|
||||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/babel-standalone/6.26.0/babel.min.js" integrity="sha512-kp7YHLxuJDJcOzStgd6vtpxr4ZU9kjn77e6dBsivSz+pUuAuMlE2UTdKB7jjsWT84qbS8kdCWHPETnP/ctrFsA==" crossorigin="anonymous"></script>
|
||||
<style>
|
||||
.field-label {
|
||||
min-width: 80px;
|
||||
@@ -454,4 +437,4 @@ const templateContent = `
|
||||
);
|
||||
</script>
|
||||
</body>
|
||||
</html>`
|
||||
</html>
|
||||
@@ -1,12 +1,18 @@
|
||||
package html
|
||||
|
||||
import (
|
||||
|
||||
// use go embed to import template
|
||||
_ "embed"
|
||||
"html/template"
|
||||
"io"
|
||||
|
||||
"github.com/securego/gosec/v2"
|
||||
)
|
||||
|
||||
//go:embed template.html
|
||||
var templateContent string
|
||||
|
||||
// WriteReport write a report in html format to the output writer
|
||||
func WriteReport(w io.Writer, data *gosec.ReportInfo) error {
|
||||
t, e := template.New("gosec").Parse(templateContent)
|
||||
|
||||
@@ -79,12 +79,13 @@ func NewTool(driver *ToolComponent) *Tool {
|
||||
}
|
||||
|
||||
// NewResult instantiate a Result
|
||||
func NewResult(ruleID string, ruleIndex int, level Level, message string) *Result {
|
||||
func NewResult(ruleID string, ruleIndex int, level Level, message string, suppressions []*Suppression) *Result {
|
||||
return &Result{
|
||||
RuleID: ruleID,
|
||||
RuleIndex: ruleIndex,
|
||||
Level: level,
|
||||
Message: NewMessage(message),
|
||||
RuleID: ruleID,
|
||||
RuleIndex: ruleIndex,
|
||||
Level: level,
|
||||
Message: NewMessage(message),
|
||||
Suppressions: suppressions,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,3 +200,11 @@ func NewToolComponentReference(name string) *ToolComponentReference {
|
||||
GUID: uuid3(name),
|
||||
}
|
||||
}
|
||||
|
||||
// NewSuppression instantiate a Suppression
|
||||
func NewSuppression(kind string, justification string) *Suppression {
|
||||
return &Suppression{
|
||||
Kind: kind,
|
||||
Justification: justification,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ func GenerateReport(rootPaths []string, data *gosec.ReportInfo) (*Report, error)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result := NewResult(r.rule.ID, r.index, getSarifLevel(issue.Severity.String()), issue.What).
|
||||
result := NewResult(r.rule.ID, r.index, getSarifLevel(issue.Severity.String()), issue.What, buildSarifSuppressions(issue.Suppressions)).
|
||||
WithLocations(location)
|
||||
|
||||
results = append(results, result)
|
||||
@@ -199,3 +199,11 @@ func getSarifLevel(s string) Level {
|
||||
return Note
|
||||
}
|
||||
}
|
||||
|
||||
func buildSarifSuppressions(suppressions []gosec.SuppressionInfo) []*Suppression {
|
||||
var sarifSuppressionList []*Suppression
|
||||
for _, s := range suppressions {
|
||||
sarifSuppressionList = append(sarifSuppressionList, NewSuppression(s.Kind, s.Justification))
|
||||
}
|
||||
return sarifSuppressionList
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package sarif_test
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
|
||||
@@ -2,8 +2,9 @@ package sarif_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"regexp"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/securego/gosec/v2"
|
||||
"github.com/securego/gosec/v2/report/sarif"
|
||||
@@ -21,5 +22,39 @@ var _ = Describe("Sarif Formatter", func() {
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
Expect(result).To(ContainSubstring("\"results\": ["))
|
||||
})
|
||||
|
||||
It("sarif formatted report should contain the suppressed results", func() {
|
||||
ruleID := "G101"
|
||||
cwe := gosec.GetCweByRule(ruleID)
|
||||
suppressedIssue := gosec.Issue{
|
||||
File: "/home/src/project/test.go",
|
||||
Line: "1",
|
||||
Col: "1",
|
||||
RuleID: ruleID,
|
||||
What: "test",
|
||||
Confidence: gosec.High,
|
||||
Severity: gosec.High,
|
||||
Code: "1: testcode",
|
||||
Cwe: cwe,
|
||||
Suppressions: []gosec.SuppressionInfo{
|
||||
{
|
||||
Kind: "kind",
|
||||
Justification: "justification",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
reportInfo := gosec.NewReportInfo([]*gosec.Issue{&suppressedIssue}, &gosec.Metrics{}, map[string][]gosec.Error{}).WithVersion("v2.7.0")
|
||||
buf := new(bytes.Buffer)
|
||||
err := sarif.WriteReport(buf, reportInfo, []string{})
|
||||
result := buf.String()
|
||||
Expect(err).ShouldNot(HaveOccurred())
|
||||
|
||||
hasResults, _ := regexp.MatchString(`"results": \[(\s*){`, result)
|
||||
Expect(hasResults).To(BeTrue())
|
||||
|
||||
hasSuppressions, _ := regexp.MatchString(`"suppressions": \[(\s*){`, result)
|
||||
Expect(hasSuppressions).To(BeTrue())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -269,7 +269,7 @@ type ExternalProperties struct {
|
||||
// An array of graph objects that will be merged with a separate run.
|
||||
Graphs []*Graph `json:"graphs,omitempty"`
|
||||
|
||||
// A stable, unique identifer for this external properties object, in the form of a GUID.
|
||||
// A stable, unique identifier for this external properties object, in the form of a GUID.
|
||||
GUID string `json:"guid,omitempty"`
|
||||
|
||||
// Describes the invocation of the analysis tool that will be merged with a separate run.
|
||||
@@ -287,7 +287,7 @@ type ExternalProperties struct {
|
||||
// An array of result objects that will be merged with a separate run.
|
||||
Results []*Result `json:"results,omitempty"`
|
||||
|
||||
// A stable, unique identifer for the run associated with this external properties object, in the form of a GUID.
|
||||
// A stable, unique identifier for the run associated with this external properties object, in the form of a GUID.
|
||||
RunGUID string `json:"runGuid,omitempty"`
|
||||
|
||||
// The URI of the JSON schema corresponding to the version of the external property file format.
|
||||
@@ -315,7 +315,7 @@ type ExternalProperties struct {
|
||||
// ExternalPropertyFileReference Contains information that enables a SARIF consumer to locate the external property file that contains the value of an externalized property associated with the run.
|
||||
type ExternalPropertyFileReference struct {
|
||||
|
||||
// A stable, unique identifer for the external property file in the form of a GUID.
|
||||
// A stable, unique identifier for the external property file in the form of a GUID.
|
||||
GUID string `json:"guid,omitempty"`
|
||||
|
||||
// A non-negative integer specifying the number of items contained in the external property file.
|
||||
@@ -801,7 +801,7 @@ type ReportingDescriptor struct {
|
||||
// A description of the report. Should, as far as possible, provide details sufficient to enable resolution of any problem indicated by the result.
|
||||
FullDescription *MultiformatMessageString `json:"fullDescription,omitempty"`
|
||||
|
||||
// A unique identifer for the reporting descriptor in the form of a GUID.
|
||||
// A unique identifier for the reporting descriptor in the form of a GUID.
|
||||
GUID string `json:"guid,omitempty"`
|
||||
|
||||
// Provides the primary documentation for the report, useful when there is no online documentation.
|
||||
@@ -894,7 +894,7 @@ type Result struct {
|
||||
// An array of zero or more unique graph objects associated with the result.
|
||||
Graphs []*Graph `json:"graphs,omitempty"`
|
||||
|
||||
// A stable, unique identifer for the result in the form of a GUID.
|
||||
// A stable, unique identifier for the result in the form of a GUID.
|
||||
GUID string `json:"guid,omitempty"`
|
||||
|
||||
// An absolute URI at which the result can be viewed.
|
||||
@@ -1080,7 +1080,7 @@ type RunAutomationDetails struct {
|
||||
// A description of the identity and role played within the engineering system by this object's containing run object.
|
||||
Description *Message `json:"description,omitempty"`
|
||||
|
||||
// A stable, unique identifer for this object's containing run object in the form of a GUID.
|
||||
// A stable, unique identifier for this object's containing run object in the form of a GUID.
|
||||
GUID string `json:"guid,omitempty"`
|
||||
|
||||
// A hierarchical string that uniquely identifies this object's containing run object.
|
||||
@@ -1154,7 +1154,7 @@ type Report struct {
|
||||
// Suppression A suppression that is relevant to a result.
|
||||
type Suppression struct {
|
||||
|
||||
// A stable, unique identifer for the suprression in the form of a GUID.
|
||||
// A stable, unique identifier for the suprression in the form of a GUID.
|
||||
GUID string `json:"guid,omitempty"`
|
||||
|
||||
// A string representing the justification for the suppression.
|
||||
@@ -1278,7 +1278,7 @@ type ToolComponent struct {
|
||||
// A dictionary, each of whose keys is a resource identifier and each of whose values is a multiformatMessageString object, which holds message strings in plain text and (optionally) Markdown format. The strings can include placeholders, which can be used to construct a message in combination with an arbitrary number of additional string arguments.
|
||||
GlobalMessageStrings map[string]*MultiformatMessageString `json:"globalMessageStrings,omitempty"`
|
||||
|
||||
// A unique identifer for the tool component in the form of a GUID.
|
||||
// A unique identifier for the tool component in the form of a GUID.
|
||||
GUID string `json:"guid,omitempty"`
|
||||
|
||||
// The absolute URI at which information about this version of the tool component can be found.
|
||||
|
||||
@@ -3,7 +3,7 @@ package sonar_test
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package sonar_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/securego/gosec/v2"
|
||||
"github.com/securego/gosec/v2/report/sonar"
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
package text
|
||||
|
||||
const templateContent = `Results:
|
||||
Results:
|
||||
{{range $filePath,$fileErrors := .Errors}}
|
||||
Golang errors in file: [{{ $filePath }}]:
|
||||
{{range $index, $error := $fileErrors}}
|
||||
@@ -23,4 +21,3 @@ Golang errors in file: [{{ $filePath }}]:
|
||||
{{- danger .Stats.NumFound }}
|
||||
{{- end }}
|
||||
|
||||
`
|
||||
@@ -3,6 +3,9 @@ package text
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
|
||||
// use go embed to import template
|
||||
_ "embed"
|
||||
"fmt"
|
||||
"io"
|
||||
"strconv"
|
||||
@@ -17,6 +20,9 @@ var (
|
||||
errorTheme = color.New(color.FgLightWhite, color.BgRed)
|
||||
warningTheme = color.New(color.FgBlack, color.BgYellow)
|
||||
defaultTheme = color.New(color.FgWhite, color.BgBlack)
|
||||
|
||||
//go:embed template.txt
|
||||
templateContent string
|
||||
)
|
||||
|
||||
// WriteReport write a (colorized) report in text format
|
||||
|
||||
@@ -3,7 +3,7 @@ package gosec_test
|
||||
import (
|
||||
"go/ast"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/securego/gosec/v2"
|
||||
"github.com/securego/gosec/v2/testutils"
|
||||
|
||||
27
rule.go
27
rule.go
@@ -26,34 +26,45 @@ type Rule interface {
|
||||
// RuleBuilder is used to register a rule definition with the analyzer
|
||||
type RuleBuilder func(id string, c Config) (Rule, []ast.Node)
|
||||
|
||||
// A RuleSet maps lists of rules to the type of AST node they should be run on.
|
||||
// A RuleSet contains a mapping of lists of rules to the type of AST node they
|
||||
// should be run on and a mapping of rule ID's to whether the rule are
|
||||
// suppressed.
|
||||
// The analyzer will only invoke rules contained in the list associated with the
|
||||
// type of AST node it is currently visiting.
|
||||
type RuleSet map[reflect.Type][]Rule
|
||||
type RuleSet struct {
|
||||
Rules map[reflect.Type][]Rule
|
||||
RuleSuppressedMap map[string]bool
|
||||
}
|
||||
|
||||
// NewRuleSet constructs a new RuleSet
|
||||
func NewRuleSet() RuleSet {
|
||||
return make(RuleSet)
|
||||
return RuleSet{make(map[reflect.Type][]Rule), make(map[string]bool)}
|
||||
}
|
||||
|
||||
// Register adds a trigger for the supplied rule for the the
|
||||
// specified ast nodes.
|
||||
func (r RuleSet) Register(rule Rule, nodes ...ast.Node) {
|
||||
func (r RuleSet) Register(rule Rule, isSuppressed bool, nodes ...ast.Node) {
|
||||
for _, n := range nodes {
|
||||
t := reflect.TypeOf(n)
|
||||
if rules, ok := r[t]; ok {
|
||||
r[t] = append(rules, rule)
|
||||
if rules, ok := r.Rules[t]; ok {
|
||||
r.Rules[t] = append(rules, rule)
|
||||
} else {
|
||||
r[t] = []Rule{rule}
|
||||
r.Rules[t] = []Rule{rule}
|
||||
}
|
||||
}
|
||||
r.RuleSuppressedMap[rule.ID()] = isSuppressed
|
||||
}
|
||||
|
||||
// RegisteredFor will return all rules that are registered for a
|
||||
// specified ast node.
|
||||
func (r RuleSet) RegisteredFor(n ast.Node) []Rule {
|
||||
if rules, found := r[reflect.TypeOf(n)]; found {
|
||||
if rules, found := r.Rules[reflect.TypeOf(n)]; found {
|
||||
return rules
|
||||
}
|
||||
return []Rule{}
|
||||
}
|
||||
|
||||
// IsRuleSuppressed will return whether the rule is suppressed.
|
||||
func (r RuleSet) IsRuleSuppressed(ruleID string) bool {
|
||||
return r.RuleSuppressedMap[ruleID]
|
||||
}
|
||||
|
||||
22
rule_test.go
22
rule_test.go
@@ -4,7 +4,7 @@ import (
|
||||
"fmt"
|
||||
"go/ast"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/securego/gosec/v2"
|
||||
)
|
||||
@@ -59,26 +59,36 @@ var _ = Describe("Rule", func() {
|
||||
registeredNodeB := (*ast.AssignStmt)(nil)
|
||||
unregisteredNode := (*ast.BinaryExpr)(nil)
|
||||
|
||||
ruleset.Register(dummyIssueRule, registeredNodeA, registeredNodeB)
|
||||
ruleset.Register(dummyIssueRule, false, registeredNodeA, registeredNodeB)
|
||||
Expect(ruleset.RegisteredFor(unregisteredNode)).Should(BeEmpty())
|
||||
Expect(ruleset.RegisteredFor(registeredNodeA)).Should(ContainElement(dummyIssueRule))
|
||||
Expect(ruleset.RegisteredFor(registeredNodeB)).Should(ContainElement(dummyIssueRule))
|
||||
Expect(ruleset.IsRuleSuppressed(dummyIssueRule.ID())).Should(BeFalse())
|
||||
})
|
||||
|
||||
It("should not register a rule when no ast.Nodes are specified", func() {
|
||||
ruleset.Register(dummyErrorRule)
|
||||
Expect(ruleset).Should(BeEmpty())
|
||||
ruleset.Register(dummyErrorRule, false)
|
||||
Expect(ruleset.Rules).Should(BeEmpty())
|
||||
})
|
||||
|
||||
It("should be possible to retrieve a list of rules for a given node type", func() {
|
||||
registeredNode := (*ast.CallExpr)(nil)
|
||||
unregisteredNode := (*ast.AssignStmt)(nil)
|
||||
ruleset.Register(dummyErrorRule, registeredNode)
|
||||
ruleset.Register(dummyIssueRule, registeredNode)
|
||||
ruleset.Register(dummyErrorRule, false, registeredNode)
|
||||
ruleset.Register(dummyIssueRule, false, registeredNode)
|
||||
Expect(ruleset.RegisteredFor(unregisteredNode)).Should(BeEmpty())
|
||||
Expect(ruleset.RegisteredFor(registeredNode)).Should(HaveLen(2))
|
||||
Expect(ruleset.RegisteredFor(registeredNode)).Should(ContainElement(dummyErrorRule))
|
||||
Expect(ruleset.RegisteredFor(registeredNode)).Should(ContainElement(dummyIssueRule))
|
||||
})
|
||||
|
||||
It("should register a suppressed rule", func() {
|
||||
registeredNode := (*ast.CallExpr)(nil)
|
||||
unregisteredNode := (*ast.AssignStmt)(nil)
|
||||
ruleset.Register(dummyIssueRule, true, registeredNode)
|
||||
Expect(ruleset.RegisteredFor(registeredNode)).Should(ContainElement(dummyIssueRule))
|
||||
Expect(ruleset.RegisteredFor(unregisteredNode)).Should(BeEmpty())
|
||||
Expect(ruleset.IsRuleSuppressed(dummyIssueRule.ID())).Should(BeTrue())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -38,11 +38,10 @@ func contains(methods []string, method string) bool {
|
||||
func (r *badDefer) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) {
|
||||
if deferStmt, ok := n.(*ast.DeferStmt); ok {
|
||||
for _, deferTyp := range r.types {
|
||||
if issue := r.checkChild(n, c, deferStmt.Call, deferTyp); issue != nil {
|
||||
return issue, nil
|
||||
}
|
||||
if issue := r.checkFunction(n, c, deferStmt, deferTyp); issue != nil {
|
||||
return issue, nil
|
||||
if typ, method, err := gosec.GetCallInfo(deferStmt.Call, c); err == nil {
|
||||
if normalize(typ) == deferTyp.typ && contains(deferTyp.methods, method) {
|
||||
return gosec.NewIssue(c, n, r.ID(), fmt.Sprintf(r.What, method, typ), r.Severity, r.Confidence), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -50,42 +49,6 @@ func (r *badDefer) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (r *badDefer) checkChild(n ast.Node, c *gosec.Context, callExp *ast.CallExpr, deferTyp deferType) *gosec.Issue {
|
||||
if typ, method, err := gosec.GetCallInfo(callExp, c); err == nil {
|
||||
if normalize(typ) == deferTyp.typ && contains(deferTyp.methods, method) {
|
||||
return gosec.NewIssue(c, n, r.ID(), fmt.Sprintf(r.What, method, typ), r.Severity, r.Confidence)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *badDefer) checkFunction(n ast.Node, c *gosec.Context, deferStmt *ast.DeferStmt, deferTyp deferType) *gosec.Issue {
|
||||
if anonFunc, isAnonFunc := deferStmt.Call.Fun.(*ast.FuncLit); isAnonFunc {
|
||||
for _, subElem := range anonFunc.Body.List {
|
||||
if issue := r.checkStmt(n, c, subElem, deferTyp); issue != nil {
|
||||
return issue
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *badDefer) checkStmt(n ast.Node, c *gosec.Context, subElem ast.Stmt, deferTyp deferType) *gosec.Issue {
|
||||
switch stmt := subElem.(type) {
|
||||
case *ast.AssignStmt:
|
||||
for _, rh := range stmt.Rhs {
|
||||
if e, isCallExp := rh.(*ast.CallExpr); isCallExp {
|
||||
return r.checkChild(n, c, e, deferTyp)
|
||||
}
|
||||
}
|
||||
case *ast.IfStmt:
|
||||
if s, is := stmt.Init.(*ast.AssignStmt); is {
|
||||
return r.checkStmt(n, c, s, deferTyp)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// NewDeferredClosing detects unsafe defer of error returning methods
|
||||
func NewDeferredClosing(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
|
||||
return &badDefer{
|
||||
|
||||
@@ -125,5 +125,6 @@ func NewReadFile(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
|
||||
rule.Add("os", "ReadFile")
|
||||
rule.Add("os", "Open")
|
||||
rule.Add("os", "OpenFile")
|
||||
rule.Add("os", "Create")
|
||||
return rule, []ast.Node{(*ast.CallExpr)(nil)}
|
||||
}
|
||||
|
||||
@@ -24,16 +24,21 @@ type RuleDefinition struct {
|
||||
Create gosec.RuleBuilder
|
||||
}
|
||||
|
||||
// RuleList is a mapping of rule ID's to rule definitions
|
||||
type RuleList map[string]RuleDefinition
|
||||
// RuleList contains a mapping of rule ID's to rule definitions and a mapping
|
||||
// of rule ID's to whether rules are suppressed.
|
||||
type RuleList struct {
|
||||
Rules map[string]RuleDefinition
|
||||
RuleSuppressed map[string]bool
|
||||
}
|
||||
|
||||
// Builders returns all the create methods for a given rule list
|
||||
func (rl RuleList) Builders() map[string]gosec.RuleBuilder {
|
||||
// RulesInfo returns all the create methods and the rule suppressed map for a
|
||||
// given list
|
||||
func (rl RuleList) RulesInfo() (map[string]gosec.RuleBuilder, map[string]bool) {
|
||||
builders := make(map[string]gosec.RuleBuilder)
|
||||
for _, def := range rl {
|
||||
for _, def := range rl.Rules {
|
||||
builders[def.ID] = def.Create
|
||||
}
|
||||
return builders
|
||||
return builders, rl.RuleSuppressed
|
||||
}
|
||||
|
||||
// RuleFilter can be used to include or exclude a rule depending on the return
|
||||
@@ -56,7 +61,7 @@ func NewRuleFilter(action bool, ruleIDs ...string) RuleFilter {
|
||||
}
|
||||
|
||||
// Generate the list of rules to use
|
||||
func Generate(filters ...RuleFilter) RuleList {
|
||||
func Generate(trackSuppressions bool, filters ...RuleFilter) RuleList {
|
||||
rules := []RuleDefinition{
|
||||
// misc
|
||||
{"G101", "Look for hardcoded credentials", NewHardcodedCredentials},
|
||||
@@ -102,15 +107,20 @@ func Generate(filters ...RuleFilter) RuleList {
|
||||
}
|
||||
|
||||
ruleMap := make(map[string]RuleDefinition)
|
||||
ruleSuppressedMap := make(map[string]bool)
|
||||
|
||||
RULES:
|
||||
for _, rule := range rules {
|
||||
ruleSuppressedMap[rule.ID] = false
|
||||
for _, filter := range filters {
|
||||
if filter(rule.ID) {
|
||||
continue RULES
|
||||
ruleSuppressedMap[rule.ID] = true
|
||||
if !trackSuppressions {
|
||||
continue RULES
|
||||
}
|
||||
}
|
||||
}
|
||||
ruleMap[rule.ID] = rule
|
||||
}
|
||||
return ruleMap
|
||||
return RuleList{ruleMap, ruleSuppressedMap}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ package rules_test
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/securego/gosec/v2"
|
||||
"github.com/securego/gosec/v2/rules"
|
||||
@@ -24,12 +24,12 @@ var _ = Describe("gosec rules", func() {
|
||||
BeforeEach(func() {
|
||||
logger, _ = testutils.NewLogger()
|
||||
config = gosec.NewConfig()
|
||||
analyzer = gosec.NewAnalyzer(config, tests, false, logger)
|
||||
analyzer = gosec.NewAnalyzer(config, tests, false, false, logger)
|
||||
runner = func(rule string, samples []testutils.CodeSample) {
|
||||
for n, sample := range samples {
|
||||
analyzer.Reset()
|
||||
analyzer.SetConfig(sample.Config)
|
||||
analyzer.LoadRules(rules.Generate(rules.NewRuleFilter(false, rule)).Builders())
|
||||
analyzer.LoadRules(rules.Generate(false, rules.NewRuleFilter(false, rule)).RulesInfo())
|
||||
pkg := testutils.NewTestPackage()
|
||||
defer pkg.Close()
|
||||
for i, code := range sample.Code {
|
||||
|
||||
23
rules/sql.go
23
rules/sql.go
@@ -137,8 +137,8 @@ func NewSQLStrConcat(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
|
||||
},
|
||||
}
|
||||
|
||||
rule.AddAll("*database/sql.DB", "Query", "QueryContext", "QueryRow", "QueryRowContext")
|
||||
rule.AddAll("*database/sql.Tx", "Query", "QueryContext", "QueryRow", "QueryRowContext")
|
||||
rule.AddAll("*database/sql.DB", "Query", "QueryContext", "QueryRow", "QueryRowContext", "Exec", "ExecContext", "Prepare", "PrepareContext")
|
||||
rule.AddAll("*database/sql.Tx", "Query", "QueryContext", "QueryRow", "QueryRowContext", "Exec", "ExecContext", "Prepare", "PrepareContext")
|
||||
return rule, []ast.Node{(*ast.AssignStmt)(nil), (*ast.ExprStmt)(nil)}
|
||||
}
|
||||
|
||||
@@ -261,6 +261,19 @@ func (s *sqlStrFormat) Match(n ast.Node, ctx *gosec.Context) (*gosec.Issue, erro
|
||||
switch stmt := n.(type) {
|
||||
case *ast.AssignStmt:
|
||||
for _, expr := range stmt.Rhs {
|
||||
if call, ok := expr.(*ast.CallExpr); ok {
|
||||
selector, ok := call.Fun.(*ast.SelectorExpr)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
sqlQueryCall, ok := selector.X.(*ast.CallExpr)
|
||||
if ok && s.ContainsCallExpr(sqlQueryCall, ctx) != nil {
|
||||
issue, err := s.checkQuery(sqlQueryCall, ctx)
|
||||
if err == nil && issue != nil {
|
||||
return issue, err
|
||||
}
|
||||
}
|
||||
}
|
||||
if sqlQueryCall, ok := expr.(*ast.CallExpr); ok && s.ContainsCallExpr(expr, ctx) != nil {
|
||||
return s.checkQuery(sqlQueryCall, ctx)
|
||||
}
|
||||
@@ -282,7 +295,7 @@ func NewSQLStrFormat(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
|
||||
noIssueQuoted: gosec.NewCallList(),
|
||||
sqlStatement: sqlStatement{
|
||||
patterns: []*regexp.Regexp{
|
||||
regexp.MustCompile("(?i)(SELECT|DELETE|INSERT|UPDATE|INTO|FROM|WHERE) "),
|
||||
regexp.MustCompile("(?i)(SELECT|DELETE|INSERT|UPDATE|INTO|FROM|WHERE)( |\n|\r|\t)"),
|
||||
regexp.MustCompile("%[^bdoxXfFp]"),
|
||||
},
|
||||
MetaData: gosec.MetaData{
|
||||
@@ -293,8 +306,8 @@ func NewSQLStrFormat(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
|
||||
},
|
||||
},
|
||||
}
|
||||
rule.AddAll("*database/sql.DB", "Query", "QueryContext", "QueryRow", "QueryRowContext")
|
||||
rule.AddAll("*database/sql.Tx", "Query", "QueryContext", "QueryRow", "QueryRowContext")
|
||||
rule.AddAll("*database/sql.DB", "Query", "QueryContext", "QueryRow", "QueryRowContext", "Exec", "ExecContext", "Prepare", "PrepareContext")
|
||||
rule.AddAll("*database/sql.Tx", "Query", "QueryContext", "QueryRow", "QueryRowContext", "Exec", "ExecContext", "Prepare", "PrepareContext")
|
||||
rule.fmtCalls.AddAll("fmt", "Sprint", "Sprintf", "Sprintln", "Fprintf")
|
||||
rule.noIssue.AddAll("os", "Stdout", "Stderr")
|
||||
rule.noIssueQuoted.Add("github.com/lib/pq", "QuoteIdentifier")
|
||||
|
||||
@@ -55,6 +55,10 @@ func (r *subprocess) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) {
|
||||
// .. indeed it is a variable then processing is different than a normal
|
||||
// field assignment
|
||||
if variable {
|
||||
// skip the check when the declaration is not available
|
||||
if ident.Obj == nil {
|
||||
continue
|
||||
}
|
||||
switch ident.Obj.Decl.(type) {
|
||||
case *ast.AssignStmt:
|
||||
_, assignment := ident.Obj.Decl.(*ast.AssignStmt)
|
||||
|
||||
@@ -23,19 +23,41 @@ import (
|
||||
|
||||
type badTempFile struct {
|
||||
gosec.MetaData
|
||||
calls gosec.CallList
|
||||
args *regexp.Regexp
|
||||
calls gosec.CallList
|
||||
args *regexp.Regexp
|
||||
argCalls gosec.CallList
|
||||
nestedCalls gosec.CallList
|
||||
}
|
||||
|
||||
func (t *badTempFile) ID() string {
|
||||
return t.MetaData.ID
|
||||
}
|
||||
|
||||
func (t *badTempFile) findTempDirArgs(n ast.Node, c *gosec.Context, suspect ast.Node) *gosec.Issue {
|
||||
if s, e := gosec.GetString(suspect); e == nil {
|
||||
if t.args.MatchString(s) {
|
||||
return gosec.NewIssue(c, n, t.ID(), t.What, t.Severity, t.Confidence)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if ce := t.argCalls.ContainsPkgCallExpr(suspect, c, false); ce != nil {
|
||||
return gosec.NewIssue(c, n, t.ID(), t.What, t.Severity, t.Confidence)
|
||||
}
|
||||
if be, ok := suspect.(*ast.BinaryExpr); ok {
|
||||
if ops := gosec.GetBinaryExprOperands(be); len(ops) != 0 {
|
||||
return t.findTempDirArgs(n, c, ops[0])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if ce := t.nestedCalls.ContainsPkgCallExpr(suspect, c, false); ce != nil {
|
||||
return t.findTempDirArgs(n, c, ce.Args[0])
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *badTempFile) Match(n ast.Node, c *gosec.Context) (gi *gosec.Issue, err error) {
|
||||
if node := t.calls.ContainsPkgCallExpr(n, c, false); node != nil {
|
||||
if arg, e := gosec.GetString(node.Args[0]); t.args.MatchString(arg) && e == nil {
|
||||
return gosec.NewIssue(c, n, t.ID(), t.What, t.Severity, t.Confidence), nil
|
||||
}
|
||||
return t.findTempDirArgs(n, c, node.Args[0]), nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
@@ -44,10 +66,17 @@ func (t *badTempFile) Match(n ast.Node, c *gosec.Context) (gi *gosec.Issue, err
|
||||
func NewBadTempFile(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
|
||||
calls := gosec.NewCallList()
|
||||
calls.Add("io/ioutil", "WriteFile")
|
||||
calls.Add("os", "Create")
|
||||
calls.AddAll("os", "Create", "WriteFile")
|
||||
argCalls := gosec.NewCallList()
|
||||
argCalls.Add("os", "TempDir")
|
||||
nestedCalls := gosec.NewCallList()
|
||||
nestedCalls.Add("path", "Join")
|
||||
nestedCalls.Add("path/filepath", "Join")
|
||||
return &badTempFile{
|
||||
calls: calls,
|
||||
args: regexp.MustCompile(`^/tmp/.*$|^/var/tmp/.*$`),
|
||||
calls: calls,
|
||||
args: regexp.MustCompile(`^(/(usr|var))?/tmp(/.*)?$`),
|
||||
argCalls: argCalls,
|
||||
nestedCalls: nestedCalls,
|
||||
MetaData: gosec.MetaData{
|
||||
ID: id,
|
||||
Severity: gosec.Medium,
|
||||
|
||||
@@ -88,7 +88,7 @@ func (t *insecureConfigTLS) processTLSConfVal(n *ast.KeyValueExpr, c *gosec.Cont
|
||||
|
||||
case "MinVersion":
|
||||
if d, ok := n.Value.(*ast.Ident); ok {
|
||||
if vs, ok := d.Obj.Decl.(*ast.ValueSpec); ok {
|
||||
if vs, ok := d.Obj.Decl.(*ast.ValueSpec); ok && len(vs.Values) > 0 {
|
||||
if s, ok := vs.Values[0].(*ast.SelectorExpr); ok {
|
||||
x := s.X.(*ast.Ident).Name
|
||||
sel := s.Sel.Name
|
||||
|
||||
@@ -55,7 +55,7 @@ func (p *TestPackage) write() error {
|
||||
for filename, content := range p.Files {
|
||||
if e := ioutil.WriteFile(filename, []byte(content), 0o644); e != nil {
|
||||
return e
|
||||
} // #nosec G306
|
||||
} //#nosec G306
|
||||
}
|
||||
p.onDisk = true
|
||||
return nil
|
||||
|
||||
@@ -1168,7 +1168,190 @@ import (
|
||||
|
||||
func main(){
|
||||
fmt.Sprintln()
|
||||
}`}, 0, gosec.NewConfig()},
|
||||
}`}, 0, gosec.NewConfig()}, {[]string{`
|
||||
// Format string with \n\r
|
||||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main(){
|
||||
db, err := sql.Open("sqlite3", ":memory:")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
q := fmt.Sprintf("SELECT * FROM foo where\n name = '%s'", os.Args[1])
|
||||
rows, err := db.Query(q)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer rows.Close()
|
||||
}`}, 1, gosec.NewConfig()}, {[]string{`
|
||||
// Format string with \n\r
|
||||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main(){
|
||||
db, err := sql.Open("sqlite3", ":memory:")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
q := fmt.Sprintf("SELECT * FROM foo where\nname = '%s'", os.Args[1])
|
||||
rows, err := db.Query(q)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer rows.Close()
|
||||
}`}, 1, gosec.NewConfig()}, {[]string{`
|
||||
// SQLI by db.Query(some).Scan(&other)
|
||||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var name string
|
||||
db, err := sql.Open("sqlite3", ":memory:")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
q := fmt.Sprintf("SELECT name FROM users where id = '%s'", os.Args[1])
|
||||
row := db.QueryRow(q)
|
||||
err = row.Scan(&name)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer db.Close()
|
||||
}`}, 1, gosec.NewConfig()}, {[]string{`
|
||||
// SQLI by db.Query(some).Scan(&other)
|
||||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
var name string
|
||||
db, err := sql.Open("sqlite3", ":memory:")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
q := fmt.Sprintf("SELECT name FROM users where id = '%s'", os.Args[1])
|
||||
err = db.QueryRow(q).Scan(&name)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer db.Close()
|
||||
}`}, 1, gosec.NewConfig()}, {[]string{`
|
||||
// SQLI by db.Prepare(some)
|
||||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
const Table = "foo"
|
||||
|
||||
func main() {
|
||||
var album string
|
||||
db, err := sql.Open("sqlite3", ":memory:")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
q := fmt.Sprintf("SELECT name FROM users where '%s' = ?", os.Args[1])
|
||||
stmt, err := db.Prepare(q)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
stmt.QueryRow(fmt.Sprintf("%s", os.Args[2])).Scan(&album)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
defer stmt.Close()
|
||||
}
|
||||
`}, 1, gosec.NewConfig()}, {[]string{`
|
||||
// SQLI by db.PrepareContext(some)
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
const Table = "foo"
|
||||
|
||||
func main() {
|
||||
var album string
|
||||
db, err := sql.Open("sqlite3", ":memory:")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
q := fmt.Sprintf("SELECT name FROM users where '%s' = ?", os.Args[1])
|
||||
stmt, err := db.PrepareContext(context.Background(), q)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
stmt.QueryRow(fmt.Sprintf("%s", os.Args[2])).Scan(&album)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
defer stmt.Close()
|
||||
}
|
||||
`}, 1, gosec.NewConfig()}, {[]string{`
|
||||
// false positive
|
||||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
const Table = "foo"
|
||||
|
||||
func main() {
|
||||
var album string
|
||||
db, err := sql.Open("sqlite3", ":memory:")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
stmt, err := db.Prepare("SELECT * FROM album WHERE id = ?")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
stmt.QueryRow(fmt.Sprintf("%s", os.Args[1])).Scan(&album)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
defer stmt.Close()
|
||||
}
|
||||
`}, 0, gosec.NewConfig()},
|
||||
}
|
||||
|
||||
// SampleCodeG202 - SQL query string building via string concatenation
|
||||
@@ -1344,6 +1527,70 @@ func main(){
|
||||
}
|
||||
defer rows.Close()
|
||||
}
|
||||
`}, 0, gosec.NewConfig()}, {[]string{`
|
||||
// ExecContext match
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
db, err := sql.Open("sqlite3", ":memory:")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
result, err := db.ExecContext(context.Background(), "select * from foo where name = "+os.Args[1])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(result)
|
||||
}`}, 1, gosec.NewConfig()}, {[]string{`
|
||||
// Exec match
|
||||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
db, err := sql.Open("sqlite3", ":memory:")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
result, err := db.Exec("select * from foo where name = " + os.Args[1])
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(result)
|
||||
}`}, 1, gosec.NewConfig()}, {[]string{`
|
||||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
)
|
||||
const gender = "M"
|
||||
const age = "32"
|
||||
|
||||
var staticQuery = "SELECT * FROM foo WHERE age < "
|
||||
|
||||
func main() {
|
||||
db, err := sql.Open("sqlite3", ":memory:")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
result, err := db.Exec("SELECT * FROM foo WHERE gender = " + gender)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Println(result)
|
||||
}
|
||||
`}, 0, gosec.NewConfig()},
|
||||
}
|
||||
|
||||
@@ -1757,6 +2004,9 @@ package samples
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -1764,10 +2014,45 @@ func main() {
|
||||
if err != nil {
|
||||
fmt.Println("Error while writing!")
|
||||
}
|
||||
}`}, 1, gosec.NewConfig()}}
|
||||
f, err := os.Create("/tmp/demo2")
|
||||
if err != nil {
|
||||
fmt.Println("Error while writing!")
|
||||
} else if err = f.Close(); err != nil {
|
||||
fmt.Println("Error while closing!")
|
||||
}
|
||||
err = os.WriteFile("/tmp/demo2", []byte("This is some data"), 0644)
|
||||
if err != nil {
|
||||
fmt.Println("Error while writing!")
|
||||
}
|
||||
err = os.WriteFile("/usr/tmp/demo2", []byte("This is some data"), 0644)
|
||||
if err != nil {
|
||||
fmt.Println("Error while writing!")
|
||||
}
|
||||
err = os.WriteFile("/tmp/" + "demo2", []byte("This is some data"), 0644)
|
||||
if err != nil {
|
||||
fmt.Println("Error while writing!")
|
||||
}
|
||||
err = os.WriteFile(os.TempDir() + "/demo2", []byte("This is some data"), 0644)
|
||||
if err != nil {
|
||||
fmt.Println("Error while writing!")
|
||||
}
|
||||
err = os.WriteFile(path.Join("/var/tmp", "demo2"), []byte("This is some data"), 0644)
|
||||
if err != nil {
|
||||
fmt.Println("Error while writing!")
|
||||
}
|
||||
err = os.WriteFile(path.Join(os.TempDir(), "demo2"), []byte("This is some data"), 0644)
|
||||
if err != nil {
|
||||
fmt.Println("Error while writing!")
|
||||
}
|
||||
err = os.WriteFile(filepath.Join(os.TempDir(), "demo2"), []byte("This is some data"), 0644)
|
||||
if err != nil {
|
||||
fmt.Println("Error while writing!")
|
||||
}
|
||||
}`}, 9, gosec.NewConfig()}}
|
||||
|
||||
// SampleCodeG304 - potential file inclusion vulnerability
|
||||
SampleCodeG304 = []CodeSample{{[]string{`
|
||||
SampleCodeG304 = []CodeSample{
|
||||
{[]string{`
|
||||
package main
|
||||
|
||||
import (
|
||||
@@ -1962,7 +2247,38 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
`}, 0, gosec.NewConfig()}}
|
||||
`}, 0, gosec.NewConfig()}, {[]string{`
|
||||
package main
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
func createFile(file string) *os.File {
|
||||
f, err := os.Create(file)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
func main() {
|
||||
s, err := os.Open("src")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer s.Close()
|
||||
|
||||
d := createFile("dst")
|
||||
defer d.Close()
|
||||
|
||||
_, err = io.Copy(d, s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}`}, 1, gosec.NewConfig()},
|
||||
}
|
||||
|
||||
// SampleCodeG305 - File path traversal when extracting zip/tar archives
|
||||
SampleCodeG305 = []CodeSample{{[]string{`
|
||||
@@ -1988,7 +2304,7 @@ func unzip(archive, target string) error {
|
||||
for _, file := range reader.File {
|
||||
path := filepath.Join(target, file.Name)
|
||||
if file.FileInfo().IsDir() {
|
||||
os.MkdirAll(path, file.Mode()) // #nosec
|
||||
os.MkdirAll(path, file.Mode()) //#nosec
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -2034,7 +2350,7 @@ func unzip(archive, target string) error {
|
||||
archiveFile := file.Name
|
||||
path := filepath.Join(target, archiveFile)
|
||||
if file.FileInfo().IsDir() {
|
||||
os.MkdirAll(path, file.Mode()) // #nosec
|
||||
os.MkdirAll(path, file.Mode()) //#nosec
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -2181,120 +2497,37 @@ func main() {
|
||||
// SampleCodeG307 - Unsafe defer of os.Close
|
||||
SampleCodeG307 = []CodeSample{
|
||||
{[]string{`package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
)
|
||||
|
||||
func check(e error) {
|
||||
if e != nil {
|
||||
panic(e)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
d1 := []byte("hello\ngo\n")
|
||||
err := ioutil.WriteFile("/tmp/dat1", d1, 0744)
|
||||
check(err)
|
||||
|
||||
allowed := ioutil.WriteFile("/tmp/dat1", d1, 0600)
|
||||
check(allowed)
|
||||
|
||||
f, err := os.Create("/tmp/dat2")
|
||||
check(err)
|
||||
|
||||
defer f.Close()
|
||||
|
||||
d2 := []byte{115, 111, 109, 101, 10}
|
||||
n2, err := f.Write(d2)
|
||||
|
||||
defer check(err)
|
||||
fmt.Printf("wrote %d bytes\n", n2)
|
||||
|
||||
}`}, 1, gosec.NewConfig()},
|
||||
{[]string{`package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
func check(e error) {
|
||||
if e != nil {
|
||||
panic(e)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
d1 := []byte("hello\ngo\n")
|
||||
err := ioutil.WriteFile("/tmp/dat1", d1, 0744)
|
||||
check(err)
|
||||
|
||||
allowed := ioutil.WriteFile("/tmp/dat1", d1, 0600)
|
||||
check(allowed)
|
||||
|
||||
f, err := os.Create("/tmp/dat2")
|
||||
check(err)
|
||||
|
||||
defer func() {
|
||||
if err := f.Close(); err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}()
|
||||
|
||||
d2 := []byte{115, 111, 109, 101, 10}
|
||||
n2, err := f.Write(d2)
|
||||
|
||||
defer check(err)
|
||||
fmt.Printf("wrote %d bytes\n", n2)
|
||||
|
||||
}`}, 1, gosec.NewConfig()},
|
||||
{[]string{`package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
func check(e error) {
|
||||
if e != nil {
|
||||
panic(e)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
d1 := []byte("hello\ngo\n")
|
||||
err := ioutil.WriteFile("/tmp/dat1", d1, 0744)
|
||||
check(err)
|
||||
|
||||
allowed := ioutil.WriteFile("/tmp/dat1", d1, 0600)
|
||||
check(allowed)
|
||||
|
||||
f, err := os.Create("/tmp/dat2")
|
||||
check(err)
|
||||
|
||||
defer func() {
|
||||
err := f.Close()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}()
|
||||
|
||||
d2 := []byte{115, 111, 109, 101, 10}
|
||||
n2, err := f.Write(d2)
|
||||
|
||||
defer check(err)
|
||||
fmt.Printf("wrote %d bytes\n", n2)
|
||||
|
||||
n3, err := f.WriteString("writes\n")
|
||||
fmt.Printf("wrote %d bytes\n", n3)
|
||||
f.Sync()
|
||||
w := bufio.NewWriter(f)
|
||||
n4, err := w.WriteString("buffered\n")
|
||||
fmt.Printf("wrote %d bytes\n", n4)
|
||||
w.Flush()
|
||||
}`}, 1, gosec.NewConfig()},
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user