Compare commits

...

34 Commits

Author SHA1 Message Date
kaiili
1d909e2687 Add db.Exec and db.Prepare to the sql rule (#763)
* Add db.Exec and db.Prepare to the sql rule

* add test cases for G201,G202
2022-01-17 13:50:37 +01:00
renovate[bot]
742aa848f9 chore(deps): update golang.org/x/crypto commit hash to 5e0467b (#764)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2022-01-17 12:42:54 +01:00
Cosmin Cojocar
7be6d4efb5 Add os.Create to the readfile rule (#761) 2022-01-12 19:33:17 +01:00
kaiili
75cc7dcd51 Fix false negative for SQL injection when using DB.QueryRow.Scan() (#759) 2022-01-12 16:33:39 +01:00
renovate[bot]
58058af0c8 chore(deps): update dependency highlight.js to v11.4.0 (#758)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2022-01-10 10:56:36 +01:00
kaiili
9d66b0d346 Fix false negatives for SQL injection in multi-line queries 2022-01-05 12:05:53 +01:00
Ville Skyttä
4c1afaa492 Find G303 with filepath.Join'd temp dirs (#754) 2022-01-04 14:48:02 +01:00
Ville Skyttä
19bda8d15f Find more tempdirs
* Find G303 in string concatenations, with os.TempDir, and in path.Join args

* Find G303 with /usr/tmp, too

/usr/tmp is commonly found e.g. on Solaris.
2022-01-03 21:58:25 +01:00
Ville Skyttä
827fca9a83 build(fmt): use [ instead of [[ (#751)
When `/bin/sh` is not a shell having `[[`, `make fmt` fails:

```
FORMATTING
/bin/sh: 1: [[: not found
```
2022-01-03 20:26:14 +01:00
Cosmin Cojocar
ad5d74d5a1 Update to ginkgo v2 (#753) 2022-01-03 18:11:35 +01:00
Yiwei Ding
72f1145f8a Fix #743 (#748)
* Check if nosec tag is in front of a line

* Use \n instead of a whitespace in a test case
2022-01-03 16:48:42 +01:00
Cosmin Cojocar
63a8e789a1 Handle nil when looking up a file by position into a package (#747) 2021-12-22 17:50:46 +01:00
kaiili
3038a30e3c Add in the config file settings for exclude and include options
Co-authored-by: kaiili <kaii@openingsource.org>
2021-12-20 23:43:50 +01:00
renovate[bot]
bf0dd2fdd3 chore(deps): update golang.org/x/crypto commit hash to e495a2d (#745)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-12-20 23:36:02 +01:00
Yiwei Ding
2d1c1a6df7 Track both #nosec and #nosec rulelist for one violation (#741) 2021-12-20 23:33:01 +01:00
Cosmin Cojocar
e0f354aa0d Add the sponsors section in the README file (#740) 2021-12-15 20:10:40 +01:00
Ville Skyttä
d23ab2d997 Remove space between // and #nosec in examples and internal use
Comments intended for machines to read do not have the space by
convention.
2021-12-15 19:31:14 +01:00
Yiwei Ding
35af340d07 Fix #736 (#738) 2021-12-13 17:45:47 +01:00
renovate[bot]
6c0b34426c chore(deps): update golang.org/x/crypto commit hash to 4570a08 (#737)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-12-13 17:44:29 +01:00
Yiwei Ding
b45f95f6ad Add support for suppressing the findings 2021-12-09 11:53:36 +01:00
renovate[bot]
040327f7d7 chore(deps): update all dependencies (#734)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-12-07 15:49:48 +01:00
Lars
6a41fb9e61 Fix https://github.com/securego/gosec/issues/714 (#733) 2021-11-24 16:34:42 +01:00
renovate[bot]
c95e9c21e7 chore(deps): update all dependencies (#731)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-11-22 14:04:48 +01:00
Cosmin Cojocar
e57efa8482 Fix a panic in suproc rule when the declaration of the variable is not available in the AST (#728) 2021-11-16 21:41:26 +01:00
Marc Brugger
ff17c30a97 Use go embed for templates (#725) 2021-11-15 16:17:22 +01:00
Matthew Jaffee
3eba7b8a3e add openssh to docker image (#719)
I'm trying to scan a project which has dependencies which are private projects. When Go tries to fetch the dependencies it normally uses HTTPS, but that doesn't work if they're private (terminal prompts disabled, can't enter username/password). So you do this little trick with git configuration to get Go to fetch dependencies over ssh: `GIT_CONFIG_PARAMETERS=url.ssh://git@github.com/.insteadOf=https://github.com/`
unfortunately the docker image doesn't have ssh installed so this doesn't work :)
2021-11-10 21:13:20 +01:00
Cosmin Cojocar
55c6ceaaa6 Fix crash when parsing the TLS min version value (#724) 2021-11-09 21:59:53 +01:00
Ville Skyttä
40fa36d1de G303: catch with os.WriteFile, add os.Create test case (#718)
* Add G303 os.Create test case

* Catch G303 with os.WriteFile too
2021-11-09 21:13:45 +01:00
renovate[bot]
873ac243ea chore(deps): update all dependencies (#722)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-11-09 21:05:07 +01:00
Ville Skyttä
f1f0056a90 Spelling fixes (#717) 2021-11-09 21:02:24 +01:00
renovate[bot]
0680c75f99 chore(deps): update all dependencies (#716)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-11-01 20:56:57 +01:00
Ryan Leung
79c8b79263 use a better naming for the variable (#715)
Signed-off-by: Ryan Leung <rleungx@gmail.com>
2021-10-19 11:54:51 +02:00
Cosmin Cojocar
69213955da Fix the SBOM generation step in the release action (#712) 2021-10-15 10:57:09 +02:00
Cosmin Cojocar
5a3a27afae Phase out support for go version 1.15 because current ginko is not backward compatible (#710)
We are going to support only the stable versions provided by the Go team.
2021-10-15 10:46:13 +02:00
55 changed files with 1124 additions and 419 deletions

View File

@@ -21,7 +21,6 @@ jobs:
strategy:
matrix:
go_version:
- '1.15'
- '1.16'
- '1.17'
runs-on: ubuntu-latest

View File

@@ -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:

View File

@@ -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

View File

@@ -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"

View File

@@ -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>

View File

@@ -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)
}
}

View File

@@ -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))
})
})
})

View File

@@ -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"

View File

@@ -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 {

View File

@@ -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()

View File

@@ -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

View File

@@ -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

View File

@@ -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
}

View File

@@ -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.

View File

@@ -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"
)

View File

@@ -3,7 +3,7 @@ package cwe_test
import (
"testing"
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

View File

@@ -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"
)

View File

@@ -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"
)

View File

@@ -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 {

View File

@@ -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
View File

@@ -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
View File

@@ -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=

View File

@@ -3,7 +3,7 @@ package gosec_test
import (
"testing"
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

View File

@@ -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"

View File

@@ -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"

View File

@@ -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
}

View File

@@ -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"

View File

@@ -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
}

View File

@@ -3,7 +3,7 @@ package report
import (
"testing"
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

View File

@@ -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":[{`))
})
})
})

View File

@@ -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>

View File

@@ -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)

View File

@@ -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,
}
}

View File

@@ -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
}

View File

@@ -3,7 +3,7 @@ package sarif_test
import (
"testing"
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

View File

@@ -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())
})
})
})

View File

@@ -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.

View File

@@ -3,7 +3,7 @@ package sonar_test
import (
"testing"
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

View File

@@ -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"

View File

@@ -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 }}
`

View File

@@ -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

View File

@@ -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
View File

@@ -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]
}

View File

@@ -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())
})
})
})

View File

@@ -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{

View File

@@ -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)}
}

View File

@@ -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}
}

View File

@@ -3,7 +3,7 @@ package rules_test
import (
"testing"
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

View File

@@ -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 {

View File

@@ -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")

View File

@@ -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)

View File

@@ -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,

View File

@@ -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

View File

@@ -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

View File

@@ -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()},
}