Compare commits

...

51 Commits

Author SHA1 Message Date
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
Cam
17105ab93e spelling fix (#708) 2021-10-14 09:54:32 +02:00
xq840622
1297bedbc7 Update README.md (#707)
"io/ioutil" package name is "ioutil"
2021-10-14 09:54:09 +02:00
Eng Zer Jun
7fd4aef9dc feat: add os.ReadFile to G304 (#706)
In Go 1.16 or higher, the `io/ioutil` has been deprecated and the
`ioutil.ReadFile` function now calls `os.ReadFile`.

Signed-off-by: Eng Zer Jun <engzerjun@gmail.com>
2021-10-14 09:53:26 +02:00
nobishii
991dd94f3a Update local installation instruction (#703)
Update local installation instruction for Go1.16+.
2021-10-05 19:33:55 +02:00
Yuval Kashtan
1933cba5b5 Add os.Unsetenv to NoErrorCheck whitelist (#702)
it always return nil err
2021-10-05 19:30:34 +02:00
renovate[bot]
e73248cc12 chore(deps): update all dependencies (#701)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-10-05 19:29:19 +02:00
Matthieu MOREL
c59cd6bb95 Update renovate.json (#699) 2021-09-19 23:24:39 +02:00
Matthieu MOREL
bfb0f422fe chore(lint): enable errorlint and gci (#698) 2021-09-13 09:40:10 +02:00
renovate[bot]
cb89567f99 chore(deps): update module github.com/lib/pq to v1.10.3 (#695)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-09-13 09:39:36 +02:00
Cosmin Cojocar
1b2eecc8c4 Enable go 1.17 in the build and release workflows (#694)
Signed-off-by: Cosmin Cojocar <cosmin.cojocar@gmx.ch>
2021-09-02 14:59:49 +02:00
Nanik
efbefc6930 fix: create a separate type for flag that has validation (#692) 2021-09-02 14:44:20 +02:00
renovate[bot]
1978a52ff4 Update all dependencies (#690)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-08-23 08:04:46 +02:00
Marc Brugger
fd5472caaf remove tabs (#689) 2021-08-20 10:27:45 +02:00
Cosmin Cojocar
d3309fb4f5 Add a more generic message for rule g204 (#688) 2021-08-19 15:18:20 +02:00
Marc Brugger
b695b66e4d add key to Highlight component (#686) 2021-08-19 14:54:12 +02:00
Marc Brugger
aee782bfe8 add summary to html report (#687) 2021-08-19 14:53:39 +02:00
Cosmin Cojocar
f285d612b5 Fix formatting issues with gofumpt (#685)
Signed-off-by: Cosmin Cojocar <ccojocar@cloudbees.com>
2021-08-18 13:16:21 +02:00
Marc Brugger
ba23b5e49a Add possibility to list waived (nosec) marked issues but not count them as such 2021-08-18 13:00:38 +02:00
Nanik
5a131be2ec fix: add more rules for G204 (#677)
* fix: add more rules for G204

* fix: add extra test and comment
2021-08-16 11:31:51 +02:00
Rodrigo Broggi
9f30bb6602 Typo correction (#681)
Correcting the command flag from 'tag' to 'tags'
2021-08-16 11:29:35 +02:00
renovate[bot]
83355dc837 Update all dependencies (#683)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-08-16 11:28:27 +02:00
renovate[bot]
2d4133d7a1 Update module github.com/onsi/gomega to v1.15.0 (#679)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-08-09 17:29:21 +02:00
Phil Calçado
91447a45f5 Add SeatGeek to USERS.md (#678)
We just added gosec to our standard Golang tooling and pipelines.
2021-08-09 17:28:31 +02:00
Marc Brugger
c0c122cdc7 Add rule ID and CWE reference to the html report 2021-08-04 17:39:03 +02:00
Marc Brugger
62db81342e Allow excluding generated files 2021-08-04 17:33:20 +02:00
Marc Brugger
521e69ef66 Allows the exclude-dir option to exclude sub directories 2021-08-04 17:31:16 +02:00
Lars
d4dc2d2df5 Improve the G307 rule
* Add G307 sample code.
The sample should reflect a defered close that leads to data loss.
Due to IDE auto-complete people tend at least log errors, but not
really care about handling.

* Add more G307 sample code. Propose a way to implement

* Remove unused code. Add example that should not return an error but does

* Remove test for synced closed file for now.
Will add this later

Co-authored-by: Cosmin Cojocar <cosmin.cojocar@gmx.ch>
2021-07-31 23:03:09 +02:00
Nanik
8b90c95c07 fix: BUILDFLAGS updated with correct versions information (#672) 2021-07-30 16:58:13 +02:00
renovate[bot]
5b3d23117c Update codecov/codecov-action action to v2 (#670)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-07-27 22:04:29 +02:00
Nanik
9535c9e3e1 fix: add variable assignment checking as part of MinVersion (#669)
* fix: add variable assignment checking as part of MinVersion

* fix: add more code to allow assignment with const

* fix: rework the code and add more test cases for MinVersion

* fix: format linting issue using gofumpt
2021-07-27 22:03:59 +02:00
renovate[bot]
b869720342 Update module golang.org/x/tools to v0.1.5 (#668)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-07-19 11:26:21 +02:00
Niklas
0ee8ad3d5b Use version ^v0 instead of latest for cyclonedx-gomod (#667)
To avoid breaking the build when cyclonedx-gomod introduces breaking changes in a new major version. See https://github.com/CycloneDX/gh-gomod-generate-sbom/releases/tag/v0.3.0

Signed-off-by: nscuro <nscuro@protonmail.com>
2021-07-19 11:25:04 +02:00
Nanik
2a4064d45d feat: adding new keyword for hardcoded credentials (#666) 2021-07-19 11:23:39 +02:00
renovate[bot]
a484c77736 Update all dependencies (#663)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-07-13 18:43:09 +02:00
Josh Kaufman
514f65f3c3 Add G204 rule for sys/execabs (#660)
* Add G204 rule for sys/execabs

* syntax error in testutils/sources.go
2021-07-01 17:43:25 +02:00
renovate[bot]
e936c84a90 Update all dependencies (#658)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-06-28 16:09:51 +02:00
renovate[bot]
8c43b96d54 Update golang.org/x/crypto commit hash to 5ff15b2 (#656)
Co-authored-by: Renovate Bot <bot@renovateapp.com>
2021-06-21 10:51:01 +02:00
Vinod Anandan
5032f998a0 Generate SBOM (#655)
* Generate SBOM

* Update release.yml

* Update .github/workflows/release.yml

Co-authored-by: Matthieu MOREL <mmorel-35@users.noreply.github.com>

* Publish bom.json

* Ignore SBOMs generated during CI

Co-authored-by: Matthieu MOREL <mmorel-35@users.noreply.github.com>
2021-06-21 10:50:44 +02:00
Matthieu MOREL
03e876754d Add security scan (#654)
* Add security scan

* Update scan.yml
2021-06-21 10:49:57 +02:00
Matthieu MOREL
01b12b43d4 Golangci : add linters (#653)
* Update .golangci.yml

* Update .golangci.yml
2021-06-18 13:47:05 +02:00
45 changed files with 1187 additions and 234 deletions

View File

@@ -21,9 +21,8 @@ jobs:
strategy:
matrix:
go_version:
- '1.14'
- '1.15'
- '1.16'
- '1.17'
runs-on: ubuntu-latest
env:
GO111MODULE: on
@@ -51,7 +50,7 @@ jobs:
- name: Setup go
uses: actions/setup-go@v2
with:
go-version: '1.16'
go-version: '1.17'
- name: Checkout Source
uses: actions/checkout@v2
- uses: actions/cache@v2
@@ -63,7 +62,7 @@ jobs:
- name: Create Test Coverage
run: make test-coverage
- name: Upload Test Coverage
uses: codecov/codecov-action@v1
uses: codecov/codecov-action@v2
with:
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: true

View File

@@ -17,10 +17,15 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v2
with:
go-version: 1.16.x
go-version: 1.17
- name : Get release version
id: get_version
run: echo ::set-env name=RELEASE_VERSION::$(echo ${GITHUB_REF:10})
- name: Generate SBOM
uses: CycloneDX/gh-gomod-generate-sbom@v1
with:
version: v1
args: mod -licenses -json -output bom.json
- name: Release Binaries
uses: goreleaser/goreleaser-action@v2
with:
@@ -34,6 +39,6 @@ jobs:
name: securego/gosec
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
buildargs: GO_VERSION=1.16
buildargs: GO_VERSION=1.17
tags: "latest,${{ env.RELEASE_VERSION }}"
tag_names: true

26
.github/workflows/scan.yml vendored Normal file
View File

@@ -0,0 +1,26 @@
name: "Security Scan"
# Run workflow each time code is pushed to your repository and on a schedule.
# The scheduled workflow runs every at 00:00 on Sunday UTC time.
on:
push:
pull_request:
schedule:
- cron: '0 0 * * 0'
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Security Scan
uses: securego/gosec@master
with:
# we let the report trigger content trigger a failure using the GitHub Security features.
args: '-no-fail -fmt sarif -out results.sarif ./...'
- name: Upload SARIF file
uses: github/codeql-action/upload-sarif@v1
with:
# Path to SARIF file relative to the root of the repository
sarif_file: results.sarif

4
.gitignore vendored
View File

@@ -33,3 +33,7 @@ _testmain.go
.DS_Store
.vscode
.idea
# SBOMs generated during CI
/bom.json

View File

@@ -2,22 +2,32 @@ linters:
enable:
- asciicheck
- bodyclose
- deadcode
- depguard
- dogsled
- durationcheck
- errcheck
- errorlint
- exportloopref
- gci
- gofmt
- gofumpt
- goimports
- gosec
- gosimple
- govet
- importas
- ineffassign
- megacheck
- misspell
- nakedret
- nolintlint
- revive
- staticcheck
- structcheck
- typecheck
- unconvert
- unparam
- wastedassign
- unused
- varcheck
- wastedassign

View File

@@ -2,6 +2,8 @@
project_name: gosec
release:
extra_files:
- glob: ./bom.json
github:
owner: securego
name: gosec

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

@@ -2,7 +2,8 @@ GIT_TAG?= $(shell git describe --always --tags)
BIN = gosec
FMT_CMD = $(gofmt -s -l -w $(find . -type f -name '*.go' -not -path './vendor/*') | tee /dev/stderr)
IMAGE_REPO = securego
BUILDFLAGS := '-w -s'
BUILD_DATE ?= $(shell date +%Y-%m-%d)
BUILDFLAGS := "-w -s -X 'main.Version=$(GIT_TAG)' -X 'main.GitTag=$(GIT_TAG)' -X 'main.BuildDate=$(BUILD_DATE)'"
CGO_ENABLED = 0
GO := GO111MODULE=on go
GO_NOMOD :=GO111MODULE=off go
@@ -55,7 +56,7 @@ release:
goreleaser release
build-linux:
CGO_ENABLED=$(CGO_ENABLED) GOOS=linux GOARCH=amd64 go build -ldflags $(BUILDFLAGS) -o $(BIN) ./cmd/gosec/
CGO_ENABLED=$(CGO_ENABLED) GOOS=linux GOARCH=amd64 go build -ldflags=$(BUILDFLAGS) -o $(BIN) ./cmd/gosec/
image:
@echo "Building the Docker image..."

View File

@@ -113,6 +113,14 @@ jobs:
### Local Installation
#### Go 1.16+
```bash
go install github.com/securego/gosec/v2/cmd/gosec@latest
```
#### Go version < 1.16
```bash
go get -u github.com/securego/gosec/v2/cmd/gosec
```
@@ -205,7 +213,7 @@ of functions which will be skipped when auditing the not checked errors:
```JSON
{
"G104": {
"io/ioutil": ["WriteFile"]
"ioutil": ["WriteFile"]
}
}
```
@@ -236,7 +244,6 @@ gosec will ignore test files across all packages and any dependencies in your ve
The scanning of test files can be enabled with the following flag:
```bash
gosec -tests ./...
```
@@ -246,6 +253,19 @@ Also additional folders can be excluded as follows:
gosec -exclude-dir=rules -exclude-dir=cmd ./...
```
### Excluding generated files
gosec can ignore generated go files with default generated code comment.
```
// Code generated by some generator DO NOT EDIT.
```
```bash
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,
@@ -288,7 +308,7 @@ gosec is able to pass your [Go build tags](https://golang.org/pkg/go/build/) to
They can be provided as a comma separated list as follows:
```bash
gosec -tag debug,ignore ./...
gosec -tags debug,ignore ./...
```
### Output formats
@@ -338,7 +358,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

View File

@@ -14,6 +14,7 @@ This is a list of gosec's users. Please send a pull request with your organisati
8. [1Password](https://github.com/1Password/srp)
9. [PingCAP/tidb](https://github.com/pingcap/tidb)
10. [Checkmarx](https://www.checkmarx.com/)
11. [SeatGeek](https://www.seatgeek.com/)
## Projects

View File

@@ -43,6 +43,8 @@ const LoadMode = packages.NeedName |
packages.NeedTypesInfo |
packages.NeedSyntax
var generatedCodePattern = regexp.MustCompile(`^// Code generated .* DO NOT EDIT\.$`)
// The Context is populated with data parsed from the source code as it is scanned.
// It is passed through to all rule functions as they are called. Rules may use
// this data in conjunction withe the encountered AST node.
@@ -70,40 +72,48 @@ 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
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
}
// NewAnalyzer builds a new analyzer.
func NewAnalyzer(conf Config, tests bool, logger *log.Logger) *Analyzer {
func NewAnalyzer(conf Config, tests bool, excludeGenerated bool, logger *log.Logger) *Analyzer {
ignoreNoSec := false
if enabled, err := conf.IsGlobalEnabled(Nosec); err == nil {
ignoreNoSec = enabled
}
showIgnored := false
if enabled, err := conf.IsGlobalEnabled(ShowIgnored); err == nil {
showIgnored = enabled
}
if logger == nil {
logger = log.New(os.Stderr, "[gosec]", log.LstdFlags)
}
return &Analyzer{
ignoreNosec: ignoreNoSec,
ruleset: make(RuleSet),
context: &Context{},
config: conf,
logger: logger,
issues: make([]*Issue, 0, 16),
stats: &Metrics{},
errors: make(map[string][]Error),
tests: tests,
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,
}
}
// SetConfig upates the analyzer configuration
// SetConfig updates the analyzer configuration
func (gosec *Analyzer) SetConfig(conf Config) {
gosec.config = conf
}
@@ -139,7 +149,7 @@ func (gosec *Analyzer) Process(buildTags []string, packagePaths ...string) error
if pkg.Name != "" {
err := gosec.ParseErrors(pkg)
if err != nil {
return fmt.Errorf("parsing errors in pkg %q: %v", pkg.Name, err)
return fmt.Errorf("parsing errors in pkg %q: %w", pkg.Name, err)
}
gosec.Check(pkg)
}
@@ -163,7 +173,7 @@ func (gosec *Analyzer) load(pkgPath string, conf *packages.Config) ([]*packages.
buildD.BuildTags = conf.BuildFlags
basePackage, err := buildD.ImportDir(pkgPath, build.ImportComment)
if err != nil {
return []*packages.Package{}, fmt.Errorf("importing dir %q: %v", pkgPath, err)
return []*packages.Package{}, fmt.Errorf("importing dir %q: %w", pkgPath, err)
}
var packageFiles []string
@@ -175,7 +185,7 @@ func (gosec *Analyzer) load(pkgPath string, conf *packages.Config) ([]*packages.
}
if gosec.tests {
testsFiles := []string{}
testsFiles := make([]string, 0)
testsFiles = append(testsFiles, basePackage.TestGoFiles...)
testsFiles = append(testsFiles, basePackage.XTestGoFiles...)
for _, filename := range testsFiles {
@@ -187,7 +197,7 @@ func (gosec *Analyzer) load(pkgPath string, conf *packages.Config) ([]*packages.
conf.BuildFlags = nil
pkgs, err := packages.Load(conf, packageFiles...)
if err != nil {
return []*packages.Package{}, fmt.Errorf("loading files from package %q: %v", pkgPath, err)
return []*packages.Package{}, fmt.Errorf("loading files from package %q: %w", pkgPath, err)
}
return pkgs, nil
}
@@ -202,6 +212,11 @@ func (gosec *Analyzer) Check(pkg *packages.Package) {
if filepath.Ext(checkedFile) != ".go" {
continue
}
if gosec.excludeGenerated && isGeneratedFile(file) {
gosec.logger.Println("Ignoring generated file:", checkedFile)
continue
}
gosec.logger.Println("Checking file:", checkedFile)
gosec.context.FileSet = pkg.Fset
gosec.context.Config = gosec.config
@@ -219,6 +234,17 @@ func (gosec *Analyzer) Check(pkg *packages.Package) {
}
}
func isGeneratedFile(file *ast.File) bool {
for _, comment := range file.Comments {
for _, row := range comment.List {
if generatedCodePattern.MatchString(row.Text) {
return true
}
}
}
return false
}
// ParseErrors parses the errors from given package
func (gosec *Analyzer) ParseErrors(pkg *packages.Package) error {
if len(pkg.Errors) == 0 {
@@ -231,13 +257,13 @@ func (gosec *Analyzer) ParseErrors(pkg *packages.Package) error {
var line int
if len(parts) > 1 {
if line, err = strconv.Atoi(parts[1]); err != nil {
return fmt.Errorf("parsing line: %v", err)
return fmt.Errorf("parsing line: %w", err)
}
}
var column int
if len(parts) > 2 {
if column, err = strconv.Atoi(parts[2]); err != nil {
return fmt.Errorf("parsing column: %v", err)
return fmt.Errorf("parsing column: %w", err)
}
}
msg := strings.TrimSpace(pkgErr.Msg)
@@ -259,7 +285,7 @@ func (gosec *Analyzer) AppendError(file string, err error) {
if r.MatchString(err.Error()) {
return
}
errors := []Error{}
errors := make([]Error, 0)
if ferrs, ok := gosec.errors[file]; ok {
errors = ferrs
}
@@ -344,9 +370,8 @@ func (gosec *Analyzer) Visit(n ast.Node) ast.Visitor {
gosec.context.Imports.TrackImport(n)
for _, rule := range gosec.ruleset.RegisteredFor(n) {
if _, ok := ignores[rule.ID()]; ok {
continue
}
_, ignored := ignores[rule.ID()]
issue, err := rule.Match(n, gosec.context)
if err != nil {
file, line := GetLocation(n, gosec.context)
@@ -354,8 +379,15 @@ func (gosec *Analyzer) Visit(n ast.Node) ast.Visitor {
gosec.logger.Printf("Rule error: %v => %s (%s:%d)\n", reflect.TypeOf(rule), err, file, line)
}
if issue != nil {
gosec.issues = append(gosec.issues, issue)
gosec.stats.NumFound++
if gosec.showIgnored {
issue.NoSec = ignored
}
if !ignored || !gosec.showIgnored {
gosec.stats.NumFound++
}
if !ignored || gosec.showIgnored || gosec.ignoreNosec {
gosec.issues = append(gosec.issues, issue)
}
}
}
return gosec

View File

@@ -7,13 +7,12 @@ import (
"os"
"strings"
"github.com/securego/gosec/v2"
"github.com/securego/gosec/v2/rules"
"golang.org/x/tools/go/packages"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/securego/gosec/v2"
"github.com/securego/gosec/v2/rules"
"github.com/securego/gosec/v2/testutils"
"golang.org/x/tools/go/packages"
)
var _ = Describe("Analyzer", func() {
@@ -25,7 +24,7 @@ var _ = Describe("Analyzer", func() {
)
BeforeEach(func() {
logger, _ = testutils.NewLogger()
analyzer = gosec.NewAnalyzer(nil, tests, logger)
analyzer = gosec.NewAnalyzer(nil, tests, false, logger)
})
Context("when processing a package", func() {
@@ -246,7 +245,7 @@ var _ = Describe("Analyzer", func() {
// overwrite nosec option
nosecIgnoreConfig := gosec.NewConfig()
nosecIgnoreConfig.SetGlobal(gosec.Nosec, "true")
customAnalyzer := gosec.NewAnalyzer(nosecIgnoreConfig, tests, logger)
customAnalyzer := gosec.NewAnalyzer(nosecIgnoreConfig, tests, false, logger)
customAnalyzer.LoadRules(rules.Generate(rules.NewRuleFilter(false, "G401")).Builders())
nosecPackage := testutils.NewTestPackage()
@@ -261,6 +260,32 @@ var _ = Describe("Analyzer", func() {
Expect(nosecIssues).Should(HaveLen(sample.Errors))
})
XIt("should be possible to overwrite nosec comments, and report issues but the should not be counted", func() {
// Rule for MD5 weak crypto usage
sample := testutils.SampleCodeG401[0]
source := sample.Code[0]
// overwrite nosec option
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())
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 = customAnalyzer.Process(buildTags, nosecPackage.Path)
Expect(err).ShouldNot(HaveOccurred())
nosecIssues, metrics, _ := customAnalyzer.Report()
Expect(nosecIssues).Should(HaveLen(sample.Errors))
Expect(metrics.NumFound).Should(Equal(0))
Expect(metrics.NumNosec).Should(Equal(1))
})
It("should be possible to use an alternative nosec tag", func() {
// Rule for MD5 weak crypto usage
sample := testutils.SampleCodeG401[0]
@@ -269,7 +294,7 @@ var _ = Describe("Analyzer", func() {
// overwrite nosec option
nosecIgnoreConfig := gosec.NewConfig()
nosecIgnoreConfig.SetGlobal(gosec.NoSecAlternative, "#falsePositive")
customAnalyzer := gosec.NewAnalyzer(nosecIgnoreConfig, tests, logger)
customAnalyzer := gosec.NewAnalyzer(nosecIgnoreConfig, tests, false, logger)
customAnalyzer.LoadRules(rules.Generate(rules.NewRuleFilter(false, "G401")).Builders())
nosecPackage := testutils.NewTestPackage()
@@ -292,7 +317,7 @@ var _ = Describe("Analyzer", func() {
// overwrite nosec option
nosecIgnoreConfig := gosec.NewConfig()
nosecIgnoreConfig.SetGlobal(gosec.NoSecAlternative, "#falsePositive")
customAnalyzer := gosec.NewAnalyzer(nosecIgnoreConfig, tests, logger)
customAnalyzer := gosec.NewAnalyzer(nosecIgnoreConfig, tests, false, logger)
customAnalyzer.LoadRules(rules.Generate(rules.NewRuleFilter(false, "G401")).Builders())
nosecPackage := testutils.NewTestPackage()
@@ -308,7 +333,7 @@ var _ = Describe("Analyzer", func() {
})
It("should be able to analyze Go test package", func() {
customAnalyzer := gosec.NewAnalyzer(nil, true, logger)
customAnalyzer := gosec.NewAnalyzer(nil, true, false, logger)
customAnalyzer.LoadRules(rules.Generate().Builders())
pkg := testutils.NewTestPackage()
defer pkg.Close()
@@ -332,6 +357,48 @@ var _ = Describe("Analyzer", func() {
issues, _, _ := customAnalyzer.Report()
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())
pkg := testutils.NewTestPackage()
defer pkg.Close()
pkg.AddFile("foo.go", `
package foo
// Code generated some-generator DO NOT EDIT.
func test() error {
return nil
}
func TestFoo(t *testing.T){
test()
}`)
err := pkg.Build()
Expect(err).ShouldNot(HaveOccurred())
err = customAnalyzer.Process(buildTags, pkg.Path)
Expect(err).ShouldNot(HaveOccurred())
issues, _, _ := customAnalyzer.Report()
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())
pkg := testutils.NewTestPackage()
defer pkg.Close()
pkg.AddFile("foo.go", `
package foo
// Code generated some-generator DO NOT EDIT.
func test() error {
return nil
}
func TestFoo(t *testing.T){
test()
}`)
err := pkg.Build()
Expect(err).ShouldNot(HaveOccurred())
err = customAnalyzer.Process(buildTags, pkg.Path)
Expect(err).ShouldNot(HaveOccurred())
issues, _, _ := customAnalyzer.Report()
Expect(issues).Should(HaveLen(0))
})
})
It("should be able to analyze Cgo files", func() {
analyzer.LoadRules(rules.Generate().Builders())

View File

@@ -24,6 +24,7 @@ import (
"strings"
"github.com/securego/gosec/v2"
"github.com/securego/gosec/v2/cmd/vflag"
"github.com/securego/gosec/v2/report"
"github.com/securego/gosec/v2/rules"
)
@@ -72,6 +73,9 @@ var (
// #nosec flag
flagIgnoreNoSec = flag.Bool("nosec", false, "Ignores #nosec comments when set")
// show ignored
flagShowIgnored = flag.Bool("show-ignored", false, "If enabled, ignored issues are printed")
// format output
flagFormat = flag.String("fmt", "text", "Set output format. Valid options are: json, yaml, csv, junit-xml, html, sonarqube, golint, sarif or text")
@@ -91,11 +95,13 @@ var (
flagRulesInclude = flag.String("include", "", "Comma separated list of rules IDs to include. (see rule list)")
// rules to explicitly exclude
flagRulesExclude = flag.String("exclude", "", "Comma separated list of rules IDs to exclude. (see rule list)")
flagRulesExclude = vflag.ValidatedFlag{}
// rules to explicitly exclude
flagExcludeGenerated = flag.Bool("exclude-generated", false, "Exclude generated files")
// log to file or stderr
flagLogfile = flag.String("log", "", "Log messages to file rather than stderr")
// sort the issues by severity
flagSortIssues = flag.Bool("sort", true, "Sort issues by severity")
@@ -170,6 +176,9 @@ func loadConfig(configFile string) (gosec.Config, error) {
if *flagIgnoreNoSec {
config.SetGlobal(gosec.Nosec, "true")
}
if *flagShowIgnored {
config.SetGlobal(gosec.ShowIgnored, "true")
}
if *flagAlternativeNoSec != "" {
config.SetGlobal(gosec.NoSecAlternative, *flagAlternativeNoSec)
}
@@ -197,11 +206,11 @@ func loadRules(include, exclude string) rules.RuleList {
}
func getRootPaths(paths []string) []string {
rootPaths := []string{}
rootPaths := make([]string, 0)
for _, path := range paths {
rootPath, err := gosec.RootPath(path)
if err != nil {
logger.Fatal(fmt.Errorf("failed to get the root path of the projects: %s", err))
logger.Fatal(fmt.Errorf("failed to get the root path of the projects: %w", err))
}
rootPaths = append(rootPaths, rootPath)
}
@@ -238,9 +247,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":
@@ -248,18 +257,22 @@ 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)
}
}
func filterIssues(issues []*gosec.Issue, severity gosec.Score, confidence gosec.Score) []*gosec.Issue {
result := []*gosec.Issue{}
func filterIssues(issues []*gosec.Issue, severity gosec.Score, confidence gosec.Score) ([]*gosec.Issue, int) {
result := make([]*gosec.Issue, 0)
trueIssues := 0
for _, issue := range issues {
if issue.Severity >= severity && issue.Confidence >= confidence {
result = append(result, issue)
if !issue.NoSec || !*flagShowIgnored {
trueIssues++
}
}
}
return result
return result, trueIssues
}
func main() {
@@ -280,6 +293,9 @@ func main() {
fmt.Fprintf(os.Stderr, "\nError: failed to exclude the %q directory from scan", ".git")
}
// set for exclude
flag.Var(&flagRulesExclude, "exclude", "Comma separated list of rules IDs to exclude. (see rule list)")
// Parse command line arguments
flag.Parse()
@@ -329,13 +345,13 @@ func main() {
}
// Load enabled rule definitions
ruleDefinitions := loadRules(*flagRulesInclude, *flagRulesExclude)
ruleDefinitions := loadRules(*flagRulesInclude, flagRulesExclude.String())
if len(ruleDefinitions) == 0 {
logger.Fatal("No rules are configured")
}
// Create the analyzer
analyzer := gosec.NewAnalyzer(config, *flagScanTests, logger)
analyzer := gosec.NewAnalyzer(config, *flagScanTests, *flagExcludeGenerated, logger)
analyzer.LoadRules(ruleDefinitions.Builders())
excludedDirs := gosec.ExcludedDirsRegExp(flagDirsExclude)
@@ -369,9 +385,10 @@ func main() {
}
// Filter the issues by severity and confidence
issues = filterIssues(issues, failSeverity, failConfidence)
if metrics.NumFound != len(issues) {
metrics.NumFound = len(issues)
var trueIssues int
issues, trueIssues = filterIssues(issues, failSeverity, failConfidence)
if metrics.NumFound != trueIssues {
metrics.NumFound = trueIssues
}
// Exit quietly if nothing was found
@@ -387,7 +404,7 @@ func main() {
if *flagOutput == "" || *flagStdOut {
fileFormat := getPrintedFormat(*flagFormat, *flagVerbose)
if err := printReport(fileFormat, *flagColor, rootPaths, reportInfo); err != nil {
logger.Fatal((err))
logger.Fatal(err)
}
}
if *flagOutput != "" {

View File

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

@@ -1,3 +1,4 @@
//go:build go1.14 || !go1.11
// +build go1.14 !go1.11
// main

View File

@@ -1,3 +1,4 @@
//go:build go1.12
// +build go1.12
package main
@@ -187,7 +188,7 @@ func main() {
}
outputPath := filepath.Join(dir, *outputFile)
if err := ioutil.WriteFile(outputPath, src, 0644); err != nil {
if err := ioutil.WriteFile(outputPath, src, 0o644); err != nil {
log.Fatalf("Writing output: %s", err)
} // #nosec G306
}

25
cmd/vflag/flag.go Normal file
View File

@@ -0,0 +1,25 @@
package vflag
import (
"errors"
"strings"
)
// ValidatedFlag cli string type
type ValidatedFlag struct {
Value string
}
func (f *ValidatedFlag) String() string {
return f.Value
}
// Set will be called for flag that is of validateFlag type
func (f *ValidatedFlag) Set(value string) error {
if strings.Contains(value, "-") {
return errors.New("flag value cannot start with -")
}
f.Value = value
return nil
}

View File

@@ -20,6 +20,8 @@ type GlobalOption string
const (
// Nosec global option for #nosec directive
Nosec GlobalOption = "nosec"
// ShowIgnored defines whether nosec issues are counted as finding or not
ShowIgnored GlobalOption = "show-ignored"
// Audit global option which indicates that gosec runs in audit mode
Audit GlobalOption = "audit"
// NoSecAlternative global option alternative for #nosec directive

View File

@@ -1,6 +1,6 @@
#!/usr/bin/env bash
# Expand the arguments into an array of strings. This is requires because the GitHub action
# Expand the arguments into an array of strings. This is required because the GitHub action
# provides all arguments concatenated as a single string.
ARGS=("$@")

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 {

42
flag_test.go Normal file
View File

@@ -0,0 +1,42 @@
package gosec_test
import (
"flag"
"os"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/securego/gosec/v2/cmd/vflag"
)
var _ = Describe("Cli", func() {
Context("vflag test", func() {
It("value must be empty as parameter value contains invalid character", func() {
os.Args = []string{"gosec", "-test1=-incorrect"}
f := vflag.ValidatedFlag{}
flag.Var(&f, "test1", "")
flag.CommandLine.Init("test1", flag.ContinueOnError)
flag.Parse()
Expect(flag.Parsed()).Should(Equal(true))
Expect(f.Value).Should(Equal(``))
})
It("value must be empty as parameter value contains invalid character without equal sign", func() {
os.Args = []string{"gosec", "-test2= -incorrect"}
f := vflag.ValidatedFlag{}
flag.Var(&f, "test2", "")
flag.CommandLine.Init("test2", flag.ContinueOnError)
flag.Parse()
Expect(flag.Parsed()).Should(Equal(true))
Expect(f.Value).Should(Equal(``))
})
It("value must not be empty as parameter value contains valid character", func() {
os.Args = []string{"gosec", "-test3=correct"}
f := vflag.ValidatedFlag{}
flag.Var(&f, "test3", "")
flag.CommandLine.Init("test3", flag.ContinueOnError)
flag.Parse()
Expect(flag.Parsed()).Should(Equal(true))
Expect(f.Value).Should(Equal(`correct`))
})
})
})

16
go.mod
View File

@@ -1,17 +1,17 @@
module github.com/securego/gosec/v2
require (
github.com/google/uuid v1.2.0
github.com/gookit/color v1.4.2
github.com/lib/pq v1.10.2
github.com/google/uuid v1.3.0
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.13.0
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a
github.com/onsi/ginkgo v1.16.5
github.com/onsi/gomega v1.17.0
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616
golang.org/x/text v0.3.6
golang.org/x/tools v0.1.3
golang.org/x/text v0.3.7
golang.org/x/tools v0.1.7
gopkg.in/yaml.v2 v2.4.0
)

45
go.sum
View File

@@ -162,12 +162,12 @@ github.com/google/trillian v1.3.11/go.mod h1:0tPraVHrSDkA3BO6vKX67zgLXs6SsOAbHEi
github.com/google/uuid v0.0.0-20161128191214-064e2069ce9c/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.2.0 h1:qJYtXnJRWmpe7m/3XlyhrsLrEURqHRM2kxzoxXqyUDs=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
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=
@@ -219,8 +219,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.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8=
github.com/lib/pq v1.10.2/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=
@@ -264,13 +264,13 @@ github.com/olekukonko/tablewriter v0.0.2/go.mod h1:rSAaSIOAGT9odnlyGlUfAJaoc5w2f
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E=
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 v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
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.13.0 h1:7lLHu94wT9Ij0o6EWWclhu0aOh32VxhkwEJvzuWPeak=
github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY=
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=
@@ -323,8 +323,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=
@@ -344,7 +345,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.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
github.com/yuin/goldmark v1.4.0/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=
@@ -372,8 +373,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-20210513164829-c07d793c2f9a h1:kr2P4QFmQr29mSLA43kwrOcgcReGTfbE9N577tCTuBc=
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
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/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=
@@ -444,9 +445,9 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R
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-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 h1:DzZ89McO9/gWPsQXS/FVKAlG02ZjaQ6AlZRBimEYOd0=
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/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 +511,9 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
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-20210510120138-977fb7262007 h1:gG67DSER+11cZvqIMb8S8bt0vZtiN6xWYARwirrOSfE=
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
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/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=
@@ -520,8 +522,9 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -574,8 +577,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.3 h1:L69ShwSZEyCsLKoAxDKeMvLDZkumEe8gXUZAjab0tX8=
golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
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/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

@@ -402,7 +402,7 @@ func PackagePaths(root string, excludes []*regexp.Regexp) ([]string, error) {
err := filepath.Walk(root, func(path string, f os.FileInfo, err error) error {
if filepath.Ext(path) == ".go" {
path = filepath.Dir(path)
if isExcluded(path, excludes) {
if isExcluded(filepath.ToSlash(path), excludes) {
return nil
}
paths[path] = true
@@ -437,7 +437,7 @@ func isExcluded(str string, excludes []*regexp.Regexp) bool {
func ExcludedDirsRegExp(excludedDirs []string) []*regexp.Regexp {
var exps []*regexp.Regexp
for _, excludedDir := range excludedDirs {
str := fmt.Sprintf(`([\\/])?%s([\\/])?`, excludedDir)
str := fmt.Sprintf(`([\\/])?%s([\\/])?`, strings.ReplaceAll(filepath.ToSlash(excludedDir), "/", `\/`))
r := regexp.MustCompile(str)
exps = append(exps, r)
}

View File

@@ -39,7 +39,7 @@ var _ = Describe("Helpers", func() {
})
It("should exclude folder", func() {
nested := dir + "/vendor"
err := os.Mkdir(nested, 0755)
err := os.Mkdir(nested, 0o755)
Expect(err).ShouldNot(HaveOccurred())
_, err = os.Create(nested + "/test.go")
Expect(err).ShouldNot(HaveOccurred())
@@ -49,6 +49,18 @@ var _ = Describe("Helpers", func() {
Expect(err).ShouldNot(HaveOccurred())
Expect(paths).Should(Equal([]string{dir}))
})
It("should exclude folder with subpath", func() {
nested := dir + "/pkg/generated"
err := os.MkdirAll(nested, 0o755)
Expect(err).ShouldNot(HaveOccurred())
_, err = os.Create(nested + "/test.go")
Expect(err).ShouldNot(HaveOccurred())
exclude, err := regexp.Compile(`([\\/])?/pkg\/generated([\\/])?`)
Expect(err).ShouldNot(HaveOccurred())
paths, err := gosec.PackagePaths(dir+"/...", []*regexp.Regexp{exclude})
Expect(err).ShouldNot(HaveOccurred())
Expect(paths).Should(Equal([]string{dir}))
})
It("should be empty when folder does not exist", func() {
nested := dir + "/test"
paths, err := gosec.PackagePaths(nested+"/...", nil)
@@ -66,7 +78,7 @@ var _ = Describe("Helpers", func() {
Expect(err).ShouldNot(HaveOccurred())
Expect(root).Should(Equal(filepath.Join(cwd, base)))
})
It("should retrun the absolute path from ellipsis path", func() {
It("should return the absolute path from ellipsis path", func() {
base := "test"
cwd, err := os.Getwd()
Expect(err).ShouldNot(HaveOccurred())
@@ -86,6 +98,17 @@ var _ = Describe("Helpers", func() {
Expect(match).Should(BeFalse())
})
It("should create a proper regexp for dir with subdir", func() {
r := gosec.ExcludedDirsRegExp([]string{`test/generated`})
Expect(len(r)).Should(Equal(1))
match := r[0].MatchString("/home/go/src/project/test/generated")
Expect(match).Should(BeTrue())
match = r[0].MatchString("/home/go/src/project/test/pkg")
Expect(match).Should(BeFalse())
match = r[0].MatchString("/home/go/src/project/vendor/pkg")
Expect(match).Should(BeFalse())
})
It("should create no regexp when dir list is empty", func() {
r := gosec.ExcludedDirsRegExp(nil)
Expect(len(r)).Should(Equal(0))

View File

@@ -1,11 +1,10 @@
package gosec_test
import (
"github.com/securego/gosec/v2"
"github.com/securego/gosec/v2/testutils"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/securego/gosec/v2"
"github.com/securego/gosec/v2/testutils"
)
var _ = Describe("Import Tracker", func() {

View File

@@ -97,6 +97,7 @@ type Issue struct {
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
}
// FileLocation point out the file path and line number in file

View File

@@ -1,5 +1,6 @@
{
"dependencyDashboard": true,
"dependencyDashboardTitle" : "Renovate(bot) : dependency dashboard",
"vulnerabilityAlerts": {
"enabled": true
},

View File

@@ -1,20 +1,3 @@
// (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>
@@ -41,6 +24,15 @@ const templateContent = `
.tag {
width: 80px;
}
.summary-first {
padding: .75rem .75rem .1rem .75rem;
}
.summary-last {
padding: .1rem .75rem .75rem .75rem;
}
.summary {
padding: .1rem .75rem ;
}
</style>
</head>
<body>
@@ -62,6 +54,8 @@ const templateContent = `
level += " is-warning";
} else if (this.props.level === "LOW") {
level += " is-info";
} else if (this.props.level === "WAIVED") {
level += " is-success";
}
level +=" is-rounded";
return (
@@ -92,17 +86,18 @@ const templateContent = `
<div className="columns">
<div className="column is-three-quarters">
<strong className="break-word">{ this.props.data.file } (line { this.props.data.line })</strong>
<p>{ this.props.data.details }</p>
<p>{this.props.data.rule_id} (CWE-{this.props.data.cwe.id}): { this.props.data.details }</p>
</div>
<div className="column is-one-quarter">
<div className="field is-grouped is-grouped-multiline">
{this.props.data.nosec && <IssueTag label="NoSec" level="WAIVED"/>}
<IssueTag label="Severity" level={ this.props.data.severity }/>
<IssueTag label="Confidence" level={ this.props.data.confidence }/>
</div>
</div>
</div>
<div className="highlight">
<Highlight code={ this.props.data.code }/>
<Highlight key={ this.props.data.file + this.props.data.line } code={ this.props.data.code }/>
</div>
</div>
);
@@ -248,50 +243,89 @@ const templateContent = `
);
}.bind(this));
return (
<nav className="panel">
<div className="panel-heading">Filters</div>
<div className="panel-block">
<div className="field is-horizontal">
<div className="field-label is-normal">
<label className="label is-pulled-left">Severity</label>
<div>
<nav className="panel">
<div className="panel-heading">Filters</div>
<div className="panel-block">
<div className="field is-horizontal">
<div className="field-label is-normal">
<label className="label is-pulled-left">Severity</label>
</div>
<div className="field-body">
<LevelSelector selected={ this.props.severity } available={ this.props.allSeverities } onChange={ this.updateSeverity } />
</div>
</div>
</div>
<div className="field-body">
<LevelSelector selected={ this.props.severity } available={ this.props.allSeverities } onChange={ this.updateSeverity } />
<div className="panel-block">
<div className="field is-horizontal">
<div className="field-label is-normal">
<label className="label is-pulled-left">Confidence</label>
</div>
<div className="field-body">
<LevelSelector selected={ this.props.confidence } available={ this.props.allConfidences } onChange={ this.updateConfidence } />
</div>
</div>
</div>
</div>
</div>
<div className="panel-block">
<div className="field is-horizontal">
<div className="field-label is-normal">
<label className="label is-pulled-left">Confidence</label>
</div>
<div className="field-body">
<LevelSelector selected={ this.props.confidence } available={ this.props.allConfidences } onChange={ this.updateConfidence } />
</div>
</div>
</div>
<div className="panel-block">
<div className="field is-horizontal">
<div className="field-label is-normal">
<label className="label is-pulled-left">Issue type</label>
</div>
<div className="field-body">
<div className="field">
<div className="control">
<div className="select is-fullwidth">
<select onChange={ this.updateIssueType }>
<option value="all" selected={ !this.props.issueType }>
(all)
</option>
{ issueTypes }
</select>
<div className="panel-block">
<div className="field is-horizontal">
<div className="field-label is-normal">
<label className="label is-pulled-left">Issue type</label>
</div>
<div className="field-body">
<div className="field">
<div className="control">
<div className="select is-fullwidth">
<select onChange={ this.updateIssueType }>
<option value="all" selected={ !this.props.issueType }>
(all)
</option>
{ issueTypes }
</select>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</nav>
</nav>
<nav className="panel">
<div className="panel-heading">Summary</div>
<div className="panel-block">
<div className="columns is-multiline">
<div className="column is-half summary-first">
<label className="label is-pulled-left">Gosec: </label>
</div>
<div className="column is-half summary-first">
{this.props.data.GosecVersion}
</div>
<div className="column is-half summary">
<label className="label is-pulled-left">Files: </label>
</div>
<div className="column is-half summary">
{this.props.data.Stats.files.toLocaleString()}
</div>
<div className="column is-half summary">
<label className="label is-pulled-left">Lines: </label>
</div>
<div className="column is-half summary">
{this.props.data.Stats.lines.toLocaleString()}
</div>
<div className="column is-half summary">
<label className="label is-pulled-left">Nosec: </label>
</div>
<div className="column is-half summary">
{this.props.data.Stats.nosec.toLocaleString()}
</div>
<div className="column is-half summary-last">
<label className="label is-pulled-left">Issues: </label>
</div>
<div className="column is-half summary-last">
{this.props.data.Stats.found.toLocaleString()}
</div>
</div>
</div>
</nav>
</div>
);
}
});
@@ -372,6 +406,7 @@ const templateContent = `
<div className="columns">
<div className="column is-one-quarter">
<Navigation
data={ this.props.data }
severity={ this.state.severity }
confidence={ this.state.confidence }
issueType={ this.state.issueType }
@@ -402,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

@@ -7,7 +7,6 @@ import (
"strings"
"github.com/google/uuid"
"github.com/securego/gosec/v2"
"github.com/securego/gosec/v2/cwe"
)

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

@@ -1,6 +1,4 @@
package text
const templateContent = `Results:
Results:
{{range $filePath,$fileErrors := .Errors}}
Golang errors in file: [{{ $filePath }}]:
{{range $index, $error := $fileErrors}}
@@ -8,7 +6,7 @@ Golang errors in file: [{{ $filePath }}]:
{{end}}
{{end}}
{{ range $index, $issue := .Issues }}
[{{ highlight $issue.FileLocation $issue.Severity }}] - {{ $issue.RuleID }} ({{ $issue.Cwe.SprintID }}): {{ $issue.What }} (Confidence: {{ $issue.Confidence}}, Severity: {{ $issue.Severity }})
[{{ highlight $issue.FileLocation $issue.Severity $issue.NoSec }}] - {{ $issue.RuleID }}{{ if $issue.NoSec }} ({{- success "NoSec" -}}){{ end }} ({{ $issue.Cwe.SprintID }}): {{ $issue.What }} (Confidence: {{ $issue.Confidence}}, Severity: {{ $issue.Severity }})
{{ printCode $issue }}
{{ end }}
@@ -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
@@ -45,7 +51,7 @@ func plainTextFuncMap(enableColor bool) template.FuncMap {
// by default those functions return the given content untouched
return template.FuncMap{
"highlight": func(t string, s gosec.Score) string {
"highlight": func(t string, s gosec.Score, ignored bool) string {
return t
},
"danger": fmt.Sprint,
@@ -56,7 +62,10 @@ func plainTextFuncMap(enableColor bool) template.FuncMap {
}
// highlight returns content t colored based on Score
func highlight(t string, s gosec.Score) string {
func highlight(t string, s gosec.Score, ignored bool) string {
if ignored {
return defaultTheme.Sprint(t)
}
switch s {
case gosec.High:
return errorTheme.Sprint(t)

View File

@@ -38,10 +38,11 @@ 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 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
}
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
}
}
}
@@ -49,6 +50,42 @@ 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

@@ -87,6 +87,7 @@ func NewNoErrorCheck(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
whitelist.AddAll("strings.Builder", "Write", "WriteByte", "WriteRune", "WriteString")
whitelist.Add("io.PipeWriter", "CloseWithError")
whitelist.Add("hash.Hash", "Write")
whitelist.Add("os", "Unsetenv")
if configured, ok := conf["G104"]; ok {
if whitelisted, ok := configured.(map[string]interface{}); ok {

View File

@@ -64,7 +64,7 @@ func (r *filePermissions) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, err
// NewWritePerms creates a rule to detect file Writes with bad permissions.
func NewWritePerms(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
mode := getConfiguredMode(conf, "G306", 0600)
mode := getConfiguredMode(conf, "G306", 0o600)
return &filePermissions{
mode: mode,
pkgs: []string{"io/ioutil", "os"},
@@ -81,7 +81,7 @@ func NewWritePerms(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
// NewFilePerms creates a rule to detect file creation with a more permissive than configured
// permission mask.
func NewFilePerms(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
mode := getConfiguredMode(conf, "G302", 0600)
mode := getConfiguredMode(conf, "G302", 0o600)
return &filePermissions{
mode: mode,
pkgs: []string{"os"},
@@ -98,7 +98,7 @@ func NewFilePerms(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
// NewMkdirPerms creates a rule to detect directory creation with more permissive than
// configured permission mask.
func NewMkdirPerms(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
mode := getConfiguredMode(conf, "G301", 0750)
mode := getConfiguredMode(conf, "G301", 0o750)
return &filePermissions{
mode: mode,
pkgs: []string{"os"},

View File

@@ -117,7 +117,7 @@ func (r *credentials) matchEqualityCheck(binaryExpr *ast.BinaryExpr, ctx *gosec.
// NewHardcodedCredentials attempts to find high entropy string constants being
// assigned to variables that appear to be related to credentials.
func NewHardcodedCredentials(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
pattern := `(?i)passwd|pass|password|pwd|secret|token`
pattern := `(?i)passwd|pass|password|pwd|secret|token|pw|apiKey|bearer|cred`
entropyThreshold := 80.0
perCharThreshold := 3.0
ignoreEntropy := false

View File

@@ -122,6 +122,7 @@ func NewReadFile(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
rule.clean.Add("path/filepath", "Clean")
rule.clean.Add("path/filepath", "Rel")
rule.Add("io/ioutil", "ReadFile")
rule.Add("os", "ReadFile")
rule.Add("os", "Open")
rule.Add("os", "OpenFile")
return rule, []ast.Node{(*ast.CallExpr)(nil)}

View File

@@ -6,7 +6,6 @@ import (
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
"github.com/securego/gosec/v2"
"github.com/securego/gosec/v2/rules"
"github.com/securego/gosec/v2/testutils"
@@ -25,7 +24,7 @@ var _ = Describe("gosec rules", func() {
BeforeEach(func() {
logger, _ = testutils.NewLogger()
config = gosec.NewConfig()
analyzer = gosec.NewAnalyzer(config, tests, logger)
analyzer = gosec.NewAnalyzer(config, tests, false, logger)
runner = func(rule string, samples []testutils.CodeSample) {
for n, sample := range samples {
analyzer.Reset()

View File

@@ -48,12 +48,40 @@ func (r *subprocess) Match(n ast.Node, c *gosec.Context) (*gosec.Issue, error) {
for _, arg := range args {
if ident, ok := arg.(*ast.Ident); ok {
obj := c.Info.ObjectOf(ident)
if _, ok := obj.(*types.Var); ok && !gosec.TryResolve(ident, c) {
return gosec.NewIssue(c, n, r.ID(), "Subprocess launched with variable", gosec.Medium, gosec.High), nil
// need to cast and check whether it is for a variable ?
_, variable := obj.(*types.Var)
// .. 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)
if variable && assignment {
if !gosec.TryResolve(ident, c) {
return gosec.NewIssue(c, n, r.ID(), "Subprocess launched with variable", gosec.Medium, gosec.High), nil
}
}
case *ast.Field:
_, field := ident.Obj.Decl.(*ast.Field)
if variable && field {
// check if the variable exist in the scope
vv, vvok := obj.(*types.Var)
if vvok && vv.Parent().Lookup(ident.Name) == nil {
return gosec.NewIssue(c, n, r.ID(), "Subprocess launched with variable", gosec.Medium, gosec.High), nil
}
}
}
}
} else if !gosec.TryResolve(arg, c) {
// the arg is not a constant or a variable but instead a function call or os.Args[i]
return gosec.NewIssue(c, n, r.ID(), "Subprocess launched with function call as argument or cmd arguments", gosec.Medium, gosec.High), nil
return gosec.NewIssue(c, n, r.ID(), "Subprocess launched with a potential tainted input or cmd arguments", gosec.Medium, gosec.High), nil
}
}
}
@@ -81,5 +109,7 @@ func NewSubproc(id string, conf gosec.Config) (gosec.Rule, []ast.Node) {
rule.Add("syscall", "Exec")
rule.Add("syscall", "ForkExec")
rule.Add("syscall", "StartProcess")
rule.Add("golang.org/x/sys/execabs", "Command")
rule.Add("golang.org/x/sys/execabs", "CommandContext")
return rule, []ast.Node{(*ast.CallExpr)(nil)}
}

View File

@@ -44,7 +44,7 @@ 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")
return &badTempFile{
calls: calls,
args: regexp.MustCompile(`^/tmp/.*$|^/var/tmp/.*$`),

View File

@@ -20,6 +20,8 @@ import (
"crypto/tls"
"fmt"
"go/ast"
"go/types"
"strconv"
"github.com/securego/gosec/v2"
)
@@ -85,7 +87,29 @@ func (t *insecureConfigTLS) processTLSConfVal(n *ast.KeyValueExpr, c *gosec.Cont
}
case "MinVersion":
if ival, ierr := gosec.GetInt(n.Value); ierr == nil {
if d, ok := n.Value.(*ast.Ident); 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
for _, imp := range c.Pkg.Imports() {
if imp.Name() == x {
tObj := imp.Scope().Lookup(sel)
if cst, ok := tObj.(*types.Const); ok {
// ..got the value check if this can be translated
if minVersion, err := strconv.ParseInt(cst.Val().String(), 10, 64); err == nil {
t.actualMinVersion = minVersion
}
}
}
}
}
if ival, ierr := gosec.GetInt(vs.Values[0]); ierr == nil {
t.actualMinVersion = ival
}
}
} else if ival, ierr := gosec.GetInt(n.Value); ierr == nil {
t.actualMinVersion = ival
} else {
if se, ok := n.Value.(*ast.SelectorExpr); ok {

View File

@@ -53,7 +53,7 @@ func (p *TestPackage) write() error {
return nil
}
for filename, content := range p.Files {
if e := ioutil.WriteFile(filename, []byte(content), 0644); e != nil {
if e := ioutil.WriteFile(filename, []byte(content), 0o644); e != nil {
return e
} // #nosec G306
}

File diff suppressed because it is too large Load Diff