Skip to content

Commit

Permalink
🚧 interactivity and testing
Browse files Browse the repository at this point in the history
  • Loading branch information
acidjazz committed Mar 21, 2024
1 parent 33650e9 commit 67b1971
Show file tree
Hide file tree
Showing 14 changed files with 390 additions and 133 deletions.
6 changes: 6 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,16 @@ require (
)

require (
github.com/alecthomas/chroma/v2 v2.13.0 // indirect
github.com/atotto/clipboard v0.1.4 // indirect
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
github.com/catppuccin/go v0.2.0 // indirect
github.com/containerd/console v1.0.4-0.20230313162750-1ae8d489ac81 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/dlclark/regexp2 v1.11.0 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/gomarkdown/markdown v0.0.0-20210208175418-bda154fe17d8 // indirect
github.com/google/uuid v1.4.0 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
Expand All @@ -34,6 +39,7 @@ require (
github.com/muesli/cancelreader v0.2.2 // indirect
github.com/muesli/reflow v0.3.0 // indirect
github.com/muesli/termenv v0.15.2 // indirect
github.com/octoper/go-ray v0.1.5 // indirect
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/sagikazarmark/locafero v0.4.0 // indirect
Expand Down
16 changes: 16 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
github.com/MakeNowJust/heredoc/v2 v2.0.1 h1:rlCHh70XXXv7toz95ajQWOWQnN4WNLt0TdpZYIR/J6A=
github.com/MakeNowJust/heredoc/v2 v2.0.1/go.mod h1:6/2Abh5s+hc3g9nbWLe9ObDIOhaRrqsyY9MWy+4JdRM=
github.com/alecthomas/chroma/v2 v2.13.0 h1:VP72+99Fb2zEcYM0MeaWJmV+xQvz5v5cxRHd+ooU1lI=
github.com/alecthomas/chroma/v2 v2.13.0/go.mod h1:BUGjjsD+ndS6eX37YgTchSEG+Jg9Jv1GiZs9sqPqztk=
github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
github.com/bradleyjkemp/cupaloy/v2 v2.6.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1lps46Enkdqw6aRX0=
github.com/catppuccin/go v0.2.0 h1:ktBeIrIP42b/8FGiScP9sgrWOss3lw0Z5SktRoithGA=
github.com/catppuccin/go v0.2.0/go.mod h1:8IHJuMGaUUjQM82qBrGNBv7LFq6JI3NnQCF6MOlZjpc=
github.com/charmbracelet/bubbles v0.18.0 h1:PYv1A036luoBGroX6VWjQIE9Syf2Wby2oOl/39KLfy0=
Expand All @@ -23,12 +26,19 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/gomarkdown/markdown v0.0.0-20210208175418-bda154fe17d8 h1:nWU6p08f1VgIalT6iZyqXi4o5cZsz4X6qa87nusfcsc=
github.com/gomarkdown/markdown v0.0.0-20210208175418-bda154fe17d8/go.mod h1:aii0r/K0ZnHv7G0KF7xy1v0A7s2Ljrb5byB7MO5p6TU=
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
Expand Down Expand Up @@ -58,6 +68,8 @@ github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
github.com/octoper/go-ray v0.1.5 h1:tTN+4HptuzSnw60E0wM71wegKsy8wYhnQ2THjMvXBGQ=
github.com/octoper/go-ray v0.1.5/go.mod h1:Y1I9cUEZ4oD94H0/M+xwHhvGVbFu1o/dW3fDrknELk8=
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
Expand Down Expand Up @@ -87,9 +99,12 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ=
github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
Expand All @@ -101,6 +116,7 @@ go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
golang.org/dl v0.0.0-20190829154251-82a15e2f2ead/go.mod h1:IUMfjQLJQd4UTqG1Z90tenwKoCX93Gn3MAQJMOSBsDQ=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g=
golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k=
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
Expand Down
105 changes: 105 additions & 0 deletions pkg/cmd/auth/login/interact.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package login

import (
"fmt"
"github.com/charmbracelet/huh"
"github.com/charmbracelet/huh/spinner"
"github.com/spf13/cobra"
"github.com/vulncheck-oss/cli/pkg/config"
"github.com/vulncheck-oss/cli/pkg/session"
"github.com/vulncheck-oss/cli/pkg/ui"
"github.com/vulncheck-oss/sdk"
)

func chooseAuthMethod() (string, error) {

var choice string
form := huh.NewForm(
huh.NewGroup(
huh.NewSelect[string]().
Title("Select an authentication method").
Options(
huh.NewOption("Login with a web browser", "web"),
huh.NewOption("Paste an authentication token", "token"),
).Value(&choice),
),
)

err := form.Run()
if err != nil {
return "", ui.Error("Failed to select authentication method: %v", err)
}

return choice, nil
}

func existingToken() error {
logoutChoice := true
confirm := huh.NewForm(huh.NewGroup(huh.NewConfirm().
Title("You currently have a token saved. Do you want to invalidate it first?").
Affirmative("Yes").
Negative("No").
Value(&logoutChoice))).WithTheme(huh.ThemeDracula())
confirm.Run()

if logoutChoice {
if _, err := session.InvalidateToken(config.Token()); err != nil {
if err := config.RemoveToken(); err != nil {
return ui.Error("Failed to remove token from config")
}
ui.Info("Token was not valid, removing from config")
} else {
if err := config.RemoveToken(); err != nil {
return ui.Error("Failed to remove token from config")
}
ui.Success("Token invalidated successfully")
}
} else {
return nil
}

return nil
}

func cmdToken(cmd *cobra.Command, args []string) error {

var token string

input := huh.
NewInput().
Title("Enter your authentication token").
Password(true).
Placeholder("vulncheck_******************").
Value(&token)

if err := input.Run(); err != nil {
return ui.Error("Token verification failed: %v", err)
}

if !config.ValidToken(token) {
return ui.Error("Invalid token specified")
}

return SaveToken(token)
}

func SaveToken(token string) error {

var res *sdk.UserResponse
var err error

_ = spinner.New().
Style(ui.Pantone).
Title(" Verifying token...").Action(func() {
res, err = session.CheckToken(token)
}).Run()

if err != nil {
return ui.Error("Token verification failed: %v", err)
}
if err := config.SaveToken(token); err != nil {
return ui.Error("Failed to save token: %v", err)
}
ui.Success(fmt.Sprintf("Authenticated as %s (%s)", res.Data.Name, res.Data.Email))
return nil
}
99 changes: 13 additions & 86 deletions pkg/cmd/auth/login/login.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
package login

import (
"fmt"
"github.com/MakeNowJust/heredoc/v2"
"github.com/charmbracelet/huh"
"github.com/charmbracelet/huh/spinner"
"github.com/spf13/cobra"
"github.com/vulncheck-oss/cli/pkg/config"
"github.com/vulncheck-oss/cli/pkg/session"
"github.com/vulncheck-oss/cli/pkg/ui"
"github.com/vulncheck-oss/cli/pkg/util"
"github.com/vulncheck-oss/sdk"
)

type CmdCopy struct {
Expand Down Expand Up @@ -43,55 +38,29 @@ func Command() *cobra.Command {
`),
RunE: func(cmd *cobra.Command, args []string) error {

if config.IsCI() {
return ui.Error("This command is interactive and cannot be run in a CI environment, use the VC_TOKEN environment variable instead")
}

if config.HasConfig() && config.HasToken() {
logoutChoice := true
confirm := huh.NewForm(huh.NewGroup(huh.NewConfirm().
Title("You currently have a token saved. Do you want to invalidate it first?").
Affirmative("Yes").
Negative("No").
Value(&logoutChoice))).WithTheme(huh.ThemeDracula())
confirm.Run()

if logoutChoice {
if _, err := session.InvalidateToken(config.Token()); err != nil {
if err := config.RemoveToken(); err != nil {
return ui.Danger("Failed to remove token from config")
}
return ui.Info("Token was not valid, removing from config")
} else {
if err := config.RemoveToken(); err != nil {
return ui.Danger("Failed to remove token from config")
}
ui.Success("Token invalidated successfully")
}
} else {
return nil
if err := existingToken(); err != nil {
return err
}

}

var choice string
form := huh.NewForm(
huh.NewGroup(
huh.NewSelect[string]().
Title("Select an authentication method").
Options(
huh.NewOption("Login with a web browser", "web"),
huh.NewOption("Paste an authentication token", "token"),
).Value(&choice),
),
)

err := form.Run()
choice, err := chooseAuthMethod()

if err != nil {
return util.FlagErrorf("Failed to select authentication method: %v", err)
return err
}

switch choice {
case "token":
return cmdToken(cmd, args)
case "web":
return ui.Error("Command currently under construction")
default:
return util.FlagErrorf("Invalid choice")
return ui.Error("Invalid choice")
}
},
}
Expand All @@ -106,7 +75,7 @@ func Command() *cobra.Command {
Use: "web",
Short: "Log in with a VulnCheck account using a web browser",
RunE: func(cmd *cobra.Command, args []string) error {
return util.FlagErrorf("web login is not yet implemented")
return ui.Error("web login is not yet implemented")
},
}

Expand All @@ -115,45 +84,3 @@ func Command() *cobra.Command {
session.DisableAuthCheck(cmd)
return cmd
}

func cmdToken(cmd *cobra.Command, args []string) error {

var token string

input := huh.
NewInput().
Title("Enter your authentication token").
Password(true).
Placeholder("vulncheck_******************").
Value(&token)

if err := input.Run(); err != nil {
return ui.Danger(fmt.Sprintf("Token verification failed: %v", err))
}

if !config.ValidToken(token) {
return util.FlagErrorf("Invalid token specified")
}

return SaveToken(token)
}

func SaveToken(token string) error {

var res *sdk.UserResponse
var err error

_ = spinner.New().
Style(ui.Pantone).
Title(" Verifying token...").Action(func() {
res, err = session.CheckToken(token)
}).Run()

if err != nil {
return ui.Danger(fmt.Sprintf("Token verification failed: %v", err))
}
if err := config.SaveToken(token); err != nil {
return util.FlagErrorf("Failed to save token: %v", err)
}
return ui.Success(fmt.Sprintf("Authenticated as %s (%s)", res.Data.Name, res.Data.Email))
}
3 changes: 2 additions & 1 deletion pkg/cmd/auth/logout/logout.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ func Command() *cobra.Command {
if err := config.RemoveToken(); err != nil {
return ui.Danger("Failed to remove token")
}
return ui.Success("Token successfully invalidated")
ui.Success("Token successfully invalidated")
return nil
}
if errors.Is(err, sdk.ErrorUnauthorized) {
if err := config.RemoveToken(); err != nil {
Expand Down
48 changes: 48 additions & 0 deletions pkg/cmd/index/index.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package index

import (
"github.com/spf13/cobra"
"github.com/vulncheck-oss/cli/pkg/config"
"github.com/vulncheck-oss/cli/pkg/environment"
"github.com/vulncheck-oss/cli/pkg/ui"
"github.com/vulncheck-oss/sdk"
)

type indexOptions struct {
Json bool
Browse bool
}

func Command() *cobra.Command {

opts := &indexOptions{}

cmd := &cobra.Command{
Use: "index <index>",
Short: "",
RunE: func(cmd *cobra.Command, args []string) error {
if len(args) != 1 {
return ui.Error("index name is required")
}
response, err := sdk.Connect(environment.Env.API, config.Token()).GetIndex(args[0])
if err != nil {
return err
}

if opts.Json {
ui.Json(response.GetData())
return nil
}

if opts.Browse {
ui.Viewport(args[0], response.GetData())
}
return nil
},
}

cmd.Flags().BoolVar(&opts.Json, "json", false, "Output as JSON")
cmd.Flags().BoolVar(&opts.Browse, "browse", false, "Browse the index in a pager")

return cmd
}
Loading

0 comments on commit 67b1971

Please sign in to comment.