diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml index 328a874c8..573583085 100644 --- a/.github/workflows/security.yml +++ b/.github/workflows/security.yml @@ -30,6 +30,7 @@ jobs: fetch-depth: 0 - name: security env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} HORUSEC_CLI_REPOSITORY_AUTHORIZATION: ${{ secrets.HORUSEC_CLI_REPOSITORY_AUTHORIZATION }} HORUSEC_CLI_HORUSEC_API_URI: ${{ secrets.HORUSEC_CLI_HORUSEC_API_URI }} HORUSEC_CLI_REPOSITORY_NAME: ${{ secrets.HORUSEC_CLI_REPOSITORY_NAME }} diff --git a/internal/enums/images/images.go b/internal/enums/images/images.go index a22831f82..928424417 100644 --- a/internal/enums/images/images.go +++ b/internal/enums/images/images.go @@ -22,7 +22,7 @@ const ( Csharp = "horuszup/horusec-csharp:v1.1.0" Elixir = "horuszup/horusec-elixir:v1.1.0" Generic = "horuszup/horusec-generic:v1.1.0" - Go = "horuszup/horusec-go:v1.2.0" + Go = "horuszup/horusec-go:v1.2.1" HCL = "horuszup/horusec-hcl:v1.1.0" Javascript = "horuszup/horusec-js:v1.2.0" Leaks = "horuszup/horusec-leaks:v1.1.0" diff --git a/internal/helpers/messages/error.go b/internal/helpers/messages/error.go index e9d4e69a4..40dbac7b6 100644 --- a/internal/helpers/messages/error.go +++ b/internal/helpers/messages/error.go @@ -39,6 +39,13 @@ const ( MsgErrorGemLockNotFound = "{HORUSEC_CLI} Error It looks like your project doesn't have a gemfile.lock file, " + "it would be a good idea to commit it so horusec can check for vulnerabilities" MsgErrorGetFilenameByExt = "Could not get filename by extension: " + MsgErrorNancyRateLimit = `{HORUSEC_CLI} Nancy tool failed to query the GitHub API for updates. +This is most likely due to GitHub rate-limiting on unauthenticated requests. +To make authenticated requests please: + 1. Generate a token at https://github.com/settings/tokens + 2. Set the token by setting the GITHUB_TOKEN environment variable. +Instructions for generating a token can be found at: +https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line. ` ) // Block of messages usage into log of the level error diff --git a/internal/services/formatters/go/deployments/Dockerfile b/internal/services/formatters/go/deployments/Dockerfile index f5a7c5b90..e6540357d 100644 --- a/internal/services/formatters/go/deployments/Dockerfile +++ b/internal/services/formatters/go/deployments/Dockerfile @@ -16,7 +16,7 @@ FROM golang:1.17.5-alpine RUN apk add curl -RUN curl -fsSL https://github.com/sonatype-nexus-community/nancy/releases/download/v1.0.28/nancy-v1.0.28-linux-amd64 -o /bin/nancy +RUN curl -fsSL https://github.com/sonatype-nexus-community/nancy/releases/download/v1.0.29/nancy-v1.0.29-linux-amd64 -o /bin/nancy RUN chmod +x /bin/nancy RUN wget -O - -q https://raw.githubusercontent.com/securego/gosec/master/install.sh | sh -s v2.8.1 diff --git a/internal/services/formatters/go/nancy/formatter.go b/internal/services/formatters/go/nancy/formatter.go index b06c042a7..452e694e3 100644 --- a/internal/services/formatters/go/nancy/formatter.go +++ b/internal/services/formatters/go/nancy/formatter.go @@ -16,6 +16,8 @@ package nancy import ( "encoding/json" + "errors" + "os" "path/filepath" "strings" @@ -34,8 +36,10 @@ import ( ) const ( - goModulesExt = ".mod" - goSumExt = ".sum" + goModulesExt = ".mod" + goSumExt = ".sum" + rateLimitValidation = "this is most likely due to github rate-limiting on unauthenticated requests" + rateLimitPrefix = "error: failed to query the github api for updates" ) type Formatter struct { @@ -54,8 +58,13 @@ func (f *Formatter) StartAnalysis(projectSubPath string) { return } - output, err := f.startNancy(projectSubPath) - f.SetAnalysisError(err, tools.Nancy, output, projectSubPath) + if os.Getenv("GITHUB_TOKEN") == "" { + logger.LogWarnWithLevel(messages.MsgErrorNancyRateLimit) + } else { + output, err := f.startNancy(projectSubPath) + f.SetAnalysisError(err, tools.Nancy, output, projectSubPath) + } + f.LogDebugWithReplace(messages.MsgDebugToolFinishAnalysis, tools.Nancy, languages.Go) } @@ -66,10 +75,13 @@ func (f *Formatter) startNancy(projectSubPath string) (string, error) { if err != nil { return output, err } - if output == "" { return output, nil } + if strings.Contains(strings.ToLower(output), rateLimitPrefix) && + strings.Contains(strings.ToLower(output), rateLimitValidation) { + return "", errors.New(messages.MsgErrorNancyRateLimit) + } return output, f.processOutput(output, projectSubPath) } diff --git a/internal/services/formatters/go/nancy/formatter_test.go b/internal/services/formatters/go/nancy/formatter_test.go index 0c87b51c6..e3fe4dbbd 100644 --- a/internal/services/formatters/go/nancy/formatter_test.go +++ b/internal/services/formatters/go/nancy/formatter_test.go @@ -16,6 +16,7 @@ package nancy import ( "errors" + "os" "path/filepath" "testing" @@ -27,12 +28,14 @@ import ( "github.com/ZupIT/horusec/config" "github.com/ZupIT/horusec/internal/entities/toolsconfig" + "github.com/ZupIT/horusec/internal/helpers/messages" "github.com/ZupIT/horusec/internal/services/formatters" "github.com/ZupIT/horusec/internal/utils/testutil" ) func TestParseOutput(t *testing.T) { t.Run("should success parse output to analysis", func(t *testing.T) { + _ = os.Setenv("GITHUB_TOKEN", "1243567890") analysis := new(analysis.Analysis) cfg := config.New() @@ -75,6 +78,7 @@ func TestParseOutput(t *testing.T) { }) t.Run("should success parse output empty to analysis", func(t *testing.T) { + _ = os.Setenv("GITHUB_TOKEN", "1243567890") analysis := new(analysis.Analysis) cfg := config.New() @@ -92,6 +96,7 @@ func TestParseOutput(t *testing.T) { }) t.Run("should add error on analysis when parsing invalid output", func(t *testing.T) { + _ = os.Setenv("GITHUB_TOKEN", "1243567890") analysis := new(analysis.Analysis) cfg := config.New() @@ -108,7 +113,45 @@ func TestParseOutput(t *testing.T) { assert.True(t, analysis.HasErrors(), "Expected errors on analysis") }) + t.Run("should not run nancy tool because not exists environment", func(t *testing.T) { + _ = os.Setenv("GITHUB_TOKEN", "") + analysis := new(analysis.Analysis) + + cfg := config.New() + + dockerAPIControllerMock := testutil.NewDockerMock() + dockerAPIControllerMock.On("SetAnalysisID") + dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return(outputRateLimit, nil) + + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, cfg) + + formatter := NewFormatter(service) + formatter.StartAnalysis("") + + assert.False(t, analysis.HasErrors(), "Expected errors on analysis") + }) + + t.Run("should add error on analysis when output return rate limit requests", func(t *testing.T) { + _ = os.Setenv("GITHUB_TOKEN", "1243567890") + analysis := new(analysis.Analysis) + + cfg := config.New() + + dockerAPIControllerMock := testutil.NewDockerMock() + dockerAPIControllerMock.On("SetAnalysisID") + dockerAPIControllerMock.On("CreateLanguageAnalysisContainer").Return(outputRateLimit, nil) + + service := formatters.NewFormatterService(analysis, dockerAPIControllerMock, cfg) + + formatter := NewFormatter(service) + formatter.StartAnalysis("") + + assert.True(t, analysis.HasErrors(), "Expected errors on analysis") + assert.Equal(t, messages.MsgErrorNancyRateLimit, analysis.Errors) + }) + t.Run("should add error on analysis when something went wrong executing container", func(t *testing.T) { + _ = os.Setenv("GITHUB_TOKEN", "1243567890") analysis := new(analysis.Analysis) dockerAPIControllerMock := testutil.NewDockerMock() @@ -126,6 +169,7 @@ func TestParseOutput(t *testing.T) { }) t.Run("should not execute tool because it's ignored", func(t *testing.T) { + _ = os.Setenv("GITHUB_TOKEN", "1243567890") analysis := new(analysis.Analysis) dockerAPIControllerMock := testutil.NewDockerMock() @@ -221,3 +265,51 @@ const output = ` ] } ` + +const outputRateLimit = `Error: Failed to query the GitHub API for updates. + +This is most likely due to GitHub rate-limiting on unauthenticated requests. + +To make authenticated requests please: + + 1. Generate a token at https://github.com/settings/tokens + 2. Set the token by either adding it to your ~/.gitconfig or + setting the GITHUB_TOKEN environment variable. + +Instructions for generating a token can be found at: +https://help.github.com/articles/creating-a-personal-access-token-for-the-command-line/ + +We call the GitHub releases API to look for new releases. +More information about that API can be found here: https://developer.github.com/v3/repos/releases/ + +: Get \"https://api.github.com/repos/sonatype-nexus-community/nancy/releases\": net/http: TLS handshake timeout + +For more information, check the log file at /root/.ossindex/nancy.combined.log +nancy version: 1.0.28 + +Usage: + nancy sleuth [flags] + +Examples: + go list -json -deps | nancy sleuth --username your_user --token your_token + nancy sleuth -p Gopkg.lock --username your_user --token your_token + +Flags: + -e, --exclude-vulnerability CveListFlag Comma separated list of CVEs or OSS Index IDs to exclude (default []) + -x, --exclude-vulnerability-file string Path to a file containing newline separated CVEs or OSS Index IDs to be excluded (default \"./.nancy-ignore\") + -h, --help help for sleuth + -n, --no-color indicate output should not be colorized + -o, --output string Styling for output format. json, json-pretty, text, csv (default \"text\") + +Global Flags: + -v, -- count Set log level, multiple v's is more verbose + -d, --db-cache-path string Specify an alternate path for caching responses from OSS Inde, example: /tmp + --loud indicate output should include non-vulnerable packages + -p, --path string Specify a path to a dep Gopkg.lock file for scanning + -q, --quiet indicate output should contain only packages with vulnerabilities (default true) + --skip-update-check Skip the check for updates. + -t, --token string Specify OSS Index API token for request + -u, --username string Specify OSS Index username for request + -V, --version Get the version + +go list -m: dmitri.shuralyov.com/gpu/mtl@v0.0.0-20190408044501-666a987793e9: Get \"https://proxy.golang.org/dmitri.shuralyov.com/gpu/mtl/@v/v0.0.0-20190408044501-666a987793e9.mod\": net/http: TLS handshake timeout`