Skip to content

Commit

Permalink
Feat/snyk timeout secs (#4942)
Browse files Browse the repository at this point in the history
* feat: ci mode differentiating permanent errors

Temporary failures might be treated differently from permanent failures
in a CI context.

A temporary network partition might be retried with an exponential
backoff delay in a CI script.

Such a temporary failure might even be skipped in the CI workflow, with
assurance that security testing will be covered by Snyk monitors and
reporting.

This option gives more flexibility and self-determination in choosing
security posture. In some cases landing a critical fix is more important
than waiting for a network partition or temporary outage to resolve.

This CI mode may be opted into by setting an environment variable SNYK_CI=1
when running the CLI. CI mode causes the following errors to terminate
with a different exit code:

- Authorization errors, including entitlement errors (HTTP 401 and 403),
  will exit code 77 (EX_NOPERM)
- Other non-recoverable errors will exit code 69 (EX_UNAVAILABLE). This
  includes all other HTTP 4xx errors from Snyk APIs.

Recoverable errors (network connection errors, HTTP 5xx) will exit code 2.

The difference between CI and non-CI mode, is that the above errors will
normally exit code 2.

* feat: snyk_timeout_secs option

Terminate CLI with exit code EX_UNAVAILABLE after a timeout, when set
using SNYK_TIMEOUT_SECS=N environment variable, where N is the number of
seconds the user is willing to wait for the command to complete.
  • Loading branch information
cmars authored Nov 27, 2023
1 parent 91ba735 commit a0308f1
Show file tree
Hide file tree
Showing 7 changed files with 57 additions and 7 deletions.
29 changes: 23 additions & 6 deletions cliv2/cmd/cliv2/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,26 @@ import (
"github.com/snyk/cli-extension-dep-graph/pkg/depgraph"
"github.com/snyk/cli-extension-iac-rules/iacrules"
"github.com/snyk/cli-extension-sbom/pkg/sbom"
"github.com/snyk/cli/cliv2/internal/cliv2"
"github.com/snyk/cli/cliv2/internal/constants"
"github.com/snyk/cli/cliv2/pkg/basic_workflows"
"github.com/snyk/container-cli/pkg/container"
"github.com/snyk/go-application-framework/pkg/analytics"
"github.com/snyk/go-application-framework/pkg/app"
"github.com/snyk/go-application-framework/pkg/auth"
"github.com/snyk/go-application-framework/pkg/configuration"

localworkflows "github.com/snyk/go-application-framework/pkg/local_workflows"
"github.com/snyk/go-application-framework/pkg/networking"
"github.com/snyk/go-application-framework/pkg/runtimeinfo"
"github.com/snyk/go-application-framework/pkg/utils"
"github.com/snyk/go-application-framework/pkg/workflow"
"github.com/snyk/go-httpauth/pkg/httpauth"
"github.com/snyk/snyk-iac-capture/pkg/capture"

snykls "github.com/snyk/snyk-ls/ls_extension"
"github.com/spf13/cobra"
"github.com/spf13/pflag"

"github.com/snyk/go-application-framework/pkg/runtimeinfo"

"github.com/snyk/cli/cliv2/internal/cliv2"
"github.com/snyk/cli/cliv2/internal/constants"
"github.com/snyk/cli/cliv2/pkg/basic_workflows"
)

var internalOS string
Expand Down Expand Up @@ -444,6 +444,10 @@ func MainWithErrorCode() int {
defer sendAnalytics(cliAnalytics, debugLogger)
}

setTimeout(globalConfiguration, func() {
os.Exit(constants.SNYK_EXIT_CODE_EX_UNAVAILABLE)
})

// run the extensible cli
err = rootCommand.Execute()

Expand All @@ -467,3 +471,16 @@ func MainWithErrorCode() int {

return exitCode
}

func setTimeout(config configuration.Configuration, onTimeout func()) {
timeout := config.GetInt(configuration.TIMEOUT)
if timeout == 0 {
return
}
debugLogger.Printf("Command timeout set for %d seconds", timeout)
go func() {
<-time.After(time.Duration(timeout) * time.Second)
fmt.Fprintf(os.Stderr, "command timed out\n")
onTimeout()
}()
}
17 changes: 17 additions & 0 deletions cliv2/cmd/cliv2/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
"os"
"testing"
"time"

"github.com/snyk/go-application-framework/pkg/configuration"
localworkflows "github.com/snyk/go-application-framework/pkg/local_workflows"
Expand Down Expand Up @@ -213,3 +214,19 @@ func Test_runMainWorkflow_unknownargs(t *testing.T) {
})
}
}

func Test_setTimeout(t *testing.T) {
exitedCh := make(chan struct{})
fakeExit := func() {
close(exitedCh)
}
config := configuration.New()
config.Set(configuration.TIMEOUT, 1)
setTimeout(config, fakeExit)
select {
case <-exitedCh:
break
case <-time.After(5 * time.Second):
t.Fatal("timeout func never executed")
}
}
2 changes: 1 addition & 1 deletion cliv2/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ require (
github.com/snyk/cli-extension-iac-rules v0.0.0-20230601153200-c572cfce46ce
github.com/snyk/cli-extension-sbom v0.0.0-20231123083311-52b1cecc1a7a
github.com/snyk/container-cli v0.0.0-20230920093251-fe865879a91f
github.com/snyk/go-application-framework v0.0.0-20231121110922-9719383f0706
github.com/snyk/go-application-framework v0.0.0-20231122083330-bbb0d2002b01
github.com/snyk/go-httpauth v0.0.0-20231117135515-eb445fea7530
github.com/snyk/snyk-iac-capture v0.6.5
github.com/snyk/snyk-ls v0.0.0-20231124091213-5a223c21e0aa
Expand Down
4 changes: 4 additions & 0 deletions cliv2/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,8 @@ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWR
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cmars/go-application-framework v0.0.0-20231121235901-2a517c3dca80 h1:/ih3AkS+EPO51JoSgJCbS5D+5ErEEYQ5Kv3UDtBOhKU=
github.com/cmars/go-application-framework v0.0.0-20231121235901-2a517c3dca80/go.mod h1:Yz/qxFyfhf0xbA+z8Vzr5IM9IDG+BS+2PiGaP1yAsEw=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
Expand Down Expand Up @@ -667,6 +669,8 @@ github.com/snyk/container-cli v0.0.0-20230920093251-fe865879a91f h1:ghajT5PEiLP8
github.com/snyk/container-cli v0.0.0-20230920093251-fe865879a91f/go.mod h1:38w+dcAQp9eG3P5t2eNS9eG0reut10AeJjLv5lJ5lpM=
github.com/snyk/go-application-framework v0.0.0-20231121110922-9719383f0706 h1:z/g5P0kS7bedN07rNChlPEifKvAe9+hufGEEifPNcJg=
github.com/snyk/go-application-framework v0.0.0-20231121110922-9719383f0706/go.mod h1:Yz/qxFyfhf0xbA+z8Vzr5IM9IDG+BS+2PiGaP1yAsEw=
github.com/snyk/go-application-framework v0.0.0-20231122083330-bbb0d2002b01 h1:2WL20Lgh2YSifXNJ4zw3tZqX2Qa4CqM2C2m0+oWtKGw=
github.com/snyk/go-application-framework v0.0.0-20231122083330-bbb0d2002b01/go.mod h1:Yz/qxFyfhf0xbA+z8Vzr5IM9IDG+BS+2PiGaP1yAsEw=
github.com/snyk/go-httpauth v0.0.0-20231117135515-eb445fea7530 h1:s9PHNkL6ueYRiAKNfd8OVxlUOqU3qY0VDbgCD1f6WQY=
github.com/snyk/go-httpauth v0.0.0-20231117135515-eb445fea7530/go.mod h1:88KbbvGYlmLgee4OcQ19yr0bNpXpOr2kciOthaSzCAg=
github.com/snyk/policy-engine v0.22.0 h1:od9pduGrXyfWO791X+8M1qmnvWUxaIXh0gBzGKqeseA=
Expand Down
1 change: 1 addition & 0 deletions cliv2/internal/constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package constants

const SNYK_EXIT_CODE_OK = 0
const SNYK_EXIT_CODE_ERROR = 2
const SNYK_EXIT_CODE_EX_UNAVAILABLE = 69
const SNYK_INTEGRATION_NAME = "CLI_V1_PLUGIN"
const SNYK_INTEGRATION_NAME_ENV = "SNYK_INTEGRATION_NAME"
const SNYK_INTEGRATION_VERSION_ENV = "SNYK_INTEGRATION_VERSION"
Expand Down
2 changes: 2 additions & 0 deletions src/cli/exit-codes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@ export const EXIT_CODES = {
VULNS_FOUND: 1,
ERROR: 2,
NO_SUPPORTED_PROJECTS_DETECTED: 3,
EX_UNAVAILABLE: 69,
EX_NOPERM: 77,
};
9 changes: 9 additions & 0 deletions src/cli/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,15 @@ async function handleError(args, error) {
let command = 'bad-command';
let exitCode = EXIT_CODES.ERROR;

// If Snyk CLI is running in CI mode (SNYK_CI=1), differentiate authorization
if (process.env.SNYK_CI === '1') {
if (error.code === 401 || error.code === 403) {
exitCode = EXIT_CODES.EX_NOPERM;
} else if (error.code >= 400 && error.code < 500) {
exitCode = EXIT_CODES.EX_UNAVAILABLE;
}
}

const vulnsFound = error.code === 'VULNS';

if (args.command === 'test' && args.options?.unmanaged) {
Expand Down

0 comments on commit a0308f1

Please sign in to comment.