Skip to content

Commit

Permalink
feat: OS CLI output - merge from branch 'master'
Browse files Browse the repository at this point in the history
Merge from branch 'master' into 'feat/os-cli-output+test'
  • Loading branch information
florindumitrascu committed Jul 27, 2022
2 parents 305de41 + 204d5f5 commit e7a7b04
Show file tree
Hide file tree
Showing 145 changed files with 6,465 additions and 2,630 deletions.
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ src/cli/commands/log4shell-hashes.ts @snyk/tundra
src/cli/commands/log4shell.ts @snyk/tundra
test/fixtures/unmanaged-log4j-fixture @snyk/tundra
test/jest/acceptance/snyk-log4shell/log4shell-detection.spec.ts @snyk/tundra
test/jest/acceptance/snyk-test/app-vuln-container-project.spec.ts @snyk/mycelium
/.github @snyk/hammer

# tap tests ownership
Expand Down
24 changes: 18 additions & 6 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ If you are mostly testing functions calling other functions, consider writing an

### Acceptance tests

Acceptance tests enforce the correctness of our distribution and are written from the perspective of a customer.
Acceptance tests enforce the correctness of our distribution and are written from the perspective of an user.

Snyk CLI's acceptance tests execute a specific command line as a standalone process, then assert on `stdout`, `stdin` and the exit code. As an example, see: [`oauth-token.spec.ts`](test/jest/acceptance/oauth-token.spec.ts).

Expand All @@ -117,6 +117,14 @@ Use [fake-server](./test/acceptance/fake-server.ts) to mock any Snyk API calls.

Place fixtures in `./test/fixtures`. Keep them minimal to reduce maintenance. Use [`createProject`](./test/jest/util/createProject.ts) to use your fixtures in isolated working directories for your tests.

### Smoke Tests

Smoke tests typically don't run on branches unless the branch is specifically prefixed with `smoke/`. They usually run on an hourly basis against the latest published version of the CLI.

If you merge a PR that changes smoke tests, remember that the tests will fail until your changes are deployed.

See [the smoke tests readme](./test/smoke/README.md) for more info

## Code ownership

For current ownership assignments, see: [CODEOWNERS](./.github/CODEOWNERS).
Expand Down Expand Up @@ -154,7 +162,7 @@ You will need to fix any remaining issues manually.

When making changes, ensure documentation is updated accordingly.

Customer-facing documentation is [available on GitBook](https://docs.snyk.io/features/snyk-cli).
User-facing documentation is [available on GitBook](https://docs.snyk.io/features/snyk-cli).

`snyk help` output must also be [edited on GitBook](https://docs.snyk.io/features/snyk-cli/commands). Changes will automatically be pulled into Snyk CLI as pull requests.

Expand Down Expand Up @@ -191,9 +199,7 @@ Each commit must provide some benefit on its own without breaking the release pi

For larger changes, break down each step into multiple commits so that it's easy to review in pull requests and git history.

Your changes must be backwards compatible and cannot break existing customer pipelines.

Commits must follow this structure:
Commits must follow [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) structure:

```
type: summary of your changes
Expand All @@ -209,13 +215,19 @@ docs: update contributing guide
We often get questions on how to contribute to this repo. What versions to use, what the workflow is, and so on. This change updates our CONTRIBUTING guide to answer those types of questions.
```

### No breaking changes

Your changes must be backwards compatible and cannot break existing user pipelines.

Don't use `BREAKING CHANGE` or exclamation mark `!` from the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/).

### Commit types

The commit type is used to summarize intent and to automate various steps.

| Type | Description |
| ---------- | ----------------------------------------------- |
| `feat` | A new customer-facing feature. |
| `feat` | A new user-facing feature. |
| `fix` | A bug fix for an existing feature. |
| `refactor` | Changes which do not affect existing features. |
| `test` | Changes to tests for existing features. |
Expand Down
9 changes: 7 additions & 2 deletions cliv2/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ bindir = $(exec_prefix)/bin
WORKING_DIR = $(CURDIR)
BUILD_DIR = $(WORKING_DIR)/_bin
CACHE_DIR = $(WORKING_DIR)/_cache
SRCS = $(shell find $(WORKING_DIR) -type f -name '*.go')

# load cached variables if available
-include $(CACHE_DIR)/variables.mk
Expand Down Expand Up @@ -137,7 +138,7 @@ dependencies: $(V1_DIRECTORY)/$(V1_EXECUTABLE_NAME) $(V1_DIRECTORY)/$(V1_EXECUTA
.PHONY: configure
configure: $(V2_DIRECTORY)/cliv2.version $(CACHE_DIR) $(CACHE_DIR)/version.mk $(CACHE_DIR)/variables.mk $(V1_DIRECTORY)/$(V1_EMBEDDED_FILE_OUTPUT) dependencies

$(BUILD_DIR)/$(V2_EXECUTABLE_NAME): $(BUILD_DIR)
$(BUILD_DIR)/$(V2_EXECUTABLE_NAME): $(BUILD_DIR) $(SRCS)
@echo "$(LOG_PREFIX) Building ( $(BUILD_DIR)/$(V2_EXECUTABLE_NAME) )"
@GOOS=$(GOOS) GOARCH=$(GOARCH) $(GOCMD) build -o $(BUILD_DIR)/$(V2_EXECUTABLE_NAME) $(WORKING_DIR)/cmd/cliv2/main.go

Expand Down Expand Up @@ -168,8 +169,12 @@ whiteboxtest:
@echo "$(LOG_PREFIX) Running $@"
@$(GOCMD) test -cover ./...

.PHONY: acceptancetest
acceptancetest: build
TEST_SNYK_COMMAND="$(BUILD_DIR)/$(V2_EXECUTABLE_NAME)" npx jest

.PHONY: test
test: whiteboxtest blackboxtest
test: whiteboxtest blackboxtest acceptancetest

.PHONY: lint
lint:
Expand Down
6 changes: 5 additions & 1 deletion cliv2/cmd/cliv2/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ func getDebugLogger(args []string) *log.Logger {
debugLogger := log.New(os.Stderr, "", log.Ldate|log.Ltime|log.Lmicroseconds|log.Lshortfile)
debug := utils.Contains(args, "--debug")

if !debug {
debug = utils.Contains(args, "-d")
}

if !debug {
debugLogger.SetOutput(ioutil.Discard)
}
Expand Down Expand Up @@ -95,7 +99,7 @@ func MainWithErrorCode(envVariables EnvironmentVariables, args []string) int {
return cliv2.SNYK_EXIT_CODE_ERROR
}

wrapperProxy.SetUpstreamProxy(envVariables.ProxyAddr)
wrapperProxy.SetUpstreamProxyFromUrl(envVariables.ProxyAddr)
wrapperProxy.SetUpstreamProxyAuthentication(envVariables.ProxyAuthenticationMechanism)

port, err := wrapperProxy.Start()
Expand Down
8 changes: 4 additions & 4 deletions cliv2/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,23 @@ module github.com/snyk/cli/cliv2
go 1.18

require (
github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74
github.com/elazarl/goproxy v0.0.0-20220328115640-894aeddb713e
github.com/jcmturner/gokrb5/v8 v8.4.2
github.com/stretchr/testify v1.7.0
golang.org/x/net v0.0.0-20220630215102-69896b714898
)

require (
github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 // indirect
github.com/davecgh/go-spew v1.1.0 // indirect
github.com/dpotapov/go-spnego v0.0.0-20220426193508-b7f82e4507db // indirect
github.com/hashicorp/go-uuid v1.0.2 // indirect
github.com/jcmturner/aescts/v2 v2.0.0 // indirect
github.com/jcmturner/dnsutils/v2 v2.0.0 // indirect
github.com/jcmturner/gofork v1.0.0 // indirect
github.com/jcmturner/goidentity/v6 v6.0.1 // indirect
github.com/jcmturner/gokrb5/v8 v8.4.2 // indirect
github.com/jcmturner/rpc/v2 v2.0.3 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9 // indirect
golang.org/x/net v0.0.0-20201021035429-f5854403a974 // indirect
golang.org/x/text v0.3.7 // indirect
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
)
14 changes: 6 additions & 8 deletions cliv2/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 h1:Kk6a4nehpJ3Uu
github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dpotapov/go-spnego v0.0.0-20220426193508-b7f82e4507db h1:3EIvol92cLWG5m13Me3/LfEtQqr23xzwZCxiWoT5gGE=
github.com/dpotapov/go-spnego v0.0.0-20220426193508-b7f82e4507db/go.mod h1:AVSs/gZKt1bOd2AhkhbS7Qh56Hv7klde22yXVbwYJhc=
github.com/elazarl/goproxy v0.0.0-20220328115640-894aeddb713e h1:99KFda6F/mw8xSfceY2JEVCrYWX7l+Ms6BcO5wEct+Q=
github.com/elazarl/goproxy v0.0.0-20220328115640-894aeddb713e/go.mod h1:Ro8st/ElPeALwNFlcTpWmkr6IoMFfkjXAvTHpevnDsM=
github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2 h1:dWB6v3RcOy03t/bUadywsbyrQwCqZeNIEX6M1OtSZOM=
github.com/elazarl/goproxy/ext v0.0.0-20190711103511-473e67f1d7d2/go.mod h1:gNh8nYJoAm43RfaxurUnxr+N1PwuFV3ZMl/efxlIlY8=
github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ=
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI=
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE=
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
Expand All @@ -33,19 +33,17 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9 h1:umElSU9WZirRdgu2yFHY0ayQkEnKiOC1TtM3fWXFnoU=
golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20220630215102-69896b714898 h1:K7wO6V1IrczY9QOQ2WkVpw4JQSwCd52UsxVEirZUfiw=
golang.org/x/net v0.0.0-20220630215102-69896b714898/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
Expand Down
140 changes: 106 additions & 34 deletions cliv2/internal/httpauth/httpauth.go
Original file line number Diff line number Diff line change
@@ -1,68 +1,140 @@
package httpauth

import (
"net/http"
"fmt"
"io"
"log"
"net/url"

"github.com/dpotapov/go-spnego"
)

type AuthenticationMechanism int
type AuthenticationMechanism string
type AuthenticationState int

const maxCycleCount int = 10

const (
NoAuth AuthenticationMechanism = "NoAuth"
Mock AuthenticationMechanism = "Mock"
Negotiate AuthenticationMechanism = "Negotiate"
UnknownMechanism AuthenticationMechanism = "UnknownMechanism"
)

const (
NoAuth AuthenticationMechanism = iota
Mock AuthenticationMechanism = iota
Negotiate AuthenticationMechanism = iota
Initial AuthenticationState = iota
Negotiating AuthenticationState = iota
Done AuthenticationState = iota
Error AuthenticationState = iota
Cancel AuthenticationState = iota
Close AuthenticationState = iota
)

const (
AuthorizationKey string = "Authorization"
ProxyAuthorizationKey string = "Proxy-Authorization"
ProxyAuthenticateKey string = "Proxy-Authenticate"
)

type AuthenticationHandlerInterface interface {
Close()
Cancel()
Succesful()
IsStopped() bool
GetAuthorizationValue(url *url.URL, responseToken string) (string, error)
SetLogger(logger *log.Logger)
}

type AuthenticationHandler struct {
Mechanism AuthenticationMechanism
spnegoProvider SpnegoProvider
Mechanism AuthenticationMechanism
state AuthenticationState
cycleCount int
logger *log.Logger
}

func (a *AuthenticationHandler) GetAuthorizationValue(url *url.URL) (string, error) {
func NewHandler(mechanism AuthenticationMechanism) AuthenticationHandlerInterface {
a := &AuthenticationHandler{
spnegoProvider: SpnegoProviderInstance(),
Mechanism: mechanism,
state: Initial,
logger: log.New(io.Discard, "", 0),
}
return a
}

var authorizeValue string
func (a *AuthenticationHandler) Close() {
a.spnegoProvider.Close()
a.state = Close
}

tmpRequest := http.Request{
URL: url,
Header: map[string][]string{},
}
func (a *AuthenticationHandler) GetAuthorizationValue(url *url.URL, responseToken string) (string, error) {
authorizeValue := ""
mechanism := string(a.Mechanism)
var err error

if a.Mechanism == Negotiate { // supporting mechanism: Negotiate (SPNEGO)
var provider spnego.Provider = spnego.New()
cannonicalize := false
var token string
var done bool

if err := provider.SetSPNEGOHeader(&tmpRequest, cannonicalize); err != nil {
if len(responseToken) == 0 && Negotiating == a.state {
a.state = Error
return "", fmt.Errorf("Authentication failed! Unexpected empty token during negotiation!")
}

a.state = Negotiating

token, done, err = a.spnegoProvider.GetToken(url, responseToken)
if err != nil {
a.state = Error
return "", err
}

if done {
a.logger.Println("Security context done!")
}

authorizeValue = mechanism + " " + token
} else if a.Mechanism == Mock { // supporting mechanism: Mock for testing
tmpRequest.Header.Set(AuthorizationKey, "Mock")
authorizeValue = mechanism + " " + responseToken
a.Succesful()
}

// ugly work around the fact that go-spnego only adds an "Authorize" Header and not "Proxy-Authorize"
if a.Mechanism != NoAuth {
authorizeValue = tmpRequest.Header.Get(AuthorizationKey)
a.cycleCount++
if a.cycleCount >= maxCycleCount {
err = fmt.Errorf("Failed to authenticate with %d cycles, stopping now!", maxCycleCount)
}

return authorizeValue, nil
return authorizeValue, err
}

func (a *AuthenticationHandler) IsStopped() bool {
return (a.state == Done || a.state == Error || a.state == Cancel || a.state == Close)
}

func (a *AuthenticationHandler) Reset() {
a.state = Initial
a.cycleCount = 0
a.logger.Println("AuthenticationHandler.Reset()")
}

func (a *AuthenticationHandler) Cancel() {
a.state = Cancel
a.logger.Println("AuthenticationHandler.Cancel()")
}

func (a *AuthenticationHandler) Succesful() {
a.state = Done
a.logger.Println("AuthenticationHandler.Succesful()")
}

func (a *AuthenticationHandler) SetLogger(logger *log.Logger) {
a.logger = logger
a.spnegoProvider.SetLogger(logger)
}

func StringFromAuthenticationMechanism(mechanism AuthenticationMechanism) string {
var result string
switch mechanism {
case NoAuth:
result = "NoAuth"
case Negotiate:
result = "Negotiate"
case Mock:
result = "Mock"
default:
result = "Unknonwn AuthenticationMechanism"
}
return result
return string(mechanism)
}

func AuthenticationMechanismFromString(mechanism string) AuthenticationMechanism {
return AuthenticationMechanism(mechanism)
}
Loading

0 comments on commit e7a7b04

Please sign in to comment.