mirror of
https://github.com/aquasecurity/trivy.git
synced 2025-12-12 15:50:15 -08:00
refactor(cli): Update the cloud config command (#9676)
This commit is contained in:
@@ -24,6 +24,5 @@ Control Trivy Cloud platform integration settings
|
||||
### SEE ALSO
|
||||
|
||||
* [trivy](trivy.md) - Unified security scanner
|
||||
* [trivy cloud edit-config](trivy_cloud_edit-config.md) - Edit Trivy Cloud configuration
|
||||
* [trivy cloud show-config](trivy_cloud_show-config.md) - Show Trivy Cloud configuration
|
||||
* [trivy cloud config](trivy_cloud_config.md) - Control Trivy Cloud configuration
|
||||
|
||||
|
||||
32
docs/docs/references/configuration/cli/trivy_cloud_config.md
Normal file
32
docs/docs/references/configuration/cli/trivy_cloud_config.md
Normal file
@@ -0,0 +1,32 @@
|
||||
## trivy cloud config
|
||||
|
||||
Control Trivy Cloud configuration
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for config
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--cache-dir string cache directory (default "/path/to/cache")
|
||||
-c, --config string config path (default "trivy.yaml")
|
||||
-d, --debug debug mode
|
||||
--generate-default-config write the default config to trivy-default.yaml
|
||||
--insecure allow insecure server connections
|
||||
-q, --quiet suppress progress bar and log output
|
||||
--timeout duration timeout (default 5m0s)
|
||||
-v, --version show version
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [trivy cloud](trivy_cloud.md) - Control Trivy Cloud platform integration settings
|
||||
* [trivy cloud config edit](trivy_cloud_config_edit.md) - Edit Trivy Cloud configuration
|
||||
* [trivy cloud config get](trivy_cloud_config_get.md) - Get Trivy Cloud configuration
|
||||
* [trivy cloud config list](trivy_cloud_config_list.md) - List Trivy Cloud configuration
|
||||
* [trivy cloud config set](trivy_cloud_config_set.md) - Set Trivy Cloud configuration
|
||||
* [trivy cloud config unset](trivy_cloud_config_unset.md) - Unset Trivy Cloud configuration
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
## trivy cloud edit-config
|
||||
## trivy cloud config edit
|
||||
|
||||
Edit Trivy Cloud configuration
|
||||
|
||||
### Synopsis
|
||||
|
||||
Edit the Trivy Cloud platform configuration in the default editor specified in the EDITOR environment variable
|
||||
Edit Trivy Cloud platform configuration in the default editor specified in the EDITOR environment variable
|
||||
|
||||
```
|
||||
trivy cloud edit-config [flags]
|
||||
trivy cloud config edit [flags]
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for edit-config
|
||||
-h, --help help for edit
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
@@ -31,5 +31,5 @@ trivy cloud edit-config [flags]
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [trivy cloud](trivy_cloud.md) - Control Trivy Cloud platform integration settings
|
||||
* [trivy cloud config](trivy_cloud_config.md) - Control Trivy Cloud configuration
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
## trivy cloud config get
|
||||
|
||||
Get Trivy Cloud configuration
|
||||
|
||||
### Synopsis
|
||||
|
||||
Get a Trivy Cloud platform configuration
|
||||
|
||||
Available config settings can be viewed by using the `trivy cloud config list` command
|
||||
|
||||
```
|
||||
trivy cloud config get [setting] [flags]
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
```
|
||||
$ trivy cloud config get server.scanning.enabled
|
||||
$ trivy cloud config get server.scanning.upload-results
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for get
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--cache-dir string cache directory (default "/path/to/cache")
|
||||
-c, --config string config path (default "trivy.yaml")
|
||||
-d, --debug debug mode
|
||||
--generate-default-config write the default config to trivy-default.yaml
|
||||
--insecure allow insecure server connections
|
||||
-q, --quiet suppress progress bar and log output
|
||||
--timeout duration timeout (default 5m0s)
|
||||
-v, --version show version
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [trivy cloud config](trivy_cloud_config.md) - Control Trivy Cloud configuration
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
## trivy cloud show-config
|
||||
## trivy cloud config list
|
||||
|
||||
Show Trivy Cloud configuration
|
||||
List Trivy Cloud configuration
|
||||
|
||||
### Synopsis
|
||||
|
||||
Show Trivy Cloud platform configuration in human readable format
|
||||
List Trivy Cloud platform configuration in human readable format
|
||||
|
||||
```
|
||||
trivy cloud show-config [flags]
|
||||
trivy cloud config list [flags]
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for show-config
|
||||
-h, --help help for list
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
@@ -31,5 +31,5 @@ trivy cloud show-config [flags]
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [trivy cloud](trivy_cloud.md) - Control Trivy Cloud platform integration settings
|
||||
* [trivy cloud config](trivy_cloud_config.md) - Control Trivy Cloud configuration
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
## trivy cloud config set
|
||||
|
||||
Set Trivy Cloud configuration
|
||||
|
||||
### Synopsis
|
||||
|
||||
Set a Trivy Cloud platform setting
|
||||
|
||||
Available config settings can be viewed by using the `trivy cloud config list` command
|
||||
|
||||
```
|
||||
trivy cloud config set [setting] [value] [flags]
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
```
|
||||
$ trivy cloud config set server.scanning.enabled true
|
||||
$ trivy cloud config set server.scanning.upload-results false
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for set
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--cache-dir string cache directory (default "/path/to/cache")
|
||||
-c, --config string config path (default "trivy.yaml")
|
||||
-d, --debug debug mode
|
||||
--generate-default-config write the default config to trivy-default.yaml
|
||||
--insecure allow insecure server connections
|
||||
-q, --quiet suppress progress bar and log output
|
||||
--timeout duration timeout (default 5m0s)
|
||||
-v, --version show version
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [trivy cloud config](trivy_cloud_config.md) - Control Trivy Cloud configuration
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
## trivy cloud config unset
|
||||
|
||||
Unset Trivy Cloud configuration
|
||||
|
||||
### Synopsis
|
||||
|
||||
Unset a Trivy Cloud platform configuration and return it to the default setting
|
||||
|
||||
Available config settings can be viewed by using the `trivy cloud config list` command
|
||||
|
||||
```
|
||||
trivy cloud config unset [setting] [flags]
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
```
|
||||
$ trivy cloud config unset server.scanning.enabled
|
||||
$ trivy cloud config unset server.scanning.upload-results
|
||||
```
|
||||
|
||||
### Options
|
||||
|
||||
```
|
||||
-h, --help help for unset
|
||||
```
|
||||
|
||||
### Options inherited from parent commands
|
||||
|
||||
```
|
||||
--cache-dir string cache directory (default "/path/to/cache")
|
||||
-c, --config string config path (default "trivy.yaml")
|
||||
-d, --debug debug mode
|
||||
--generate-default-config write the default config to trivy-default.yaml
|
||||
--insecure allow insecure server connections
|
||||
-q, --quiet suppress progress bar and log output
|
||||
--timeout duration timeout (default 5m0s)
|
||||
-v, --version show version
|
||||
```
|
||||
|
||||
### SEE ALSO
|
||||
|
||||
* [trivy cloud config](trivy_cloud_config.md) - Control Trivy Cloud configuration
|
||||
|
||||
10
mkdocs.yml
10
mkdocs.yml
@@ -170,9 +170,13 @@ nav:
|
||||
- Overview: docs/references/configuration/cli/trivy.md
|
||||
- Clean: docs/references/configuration/cli/trivy_clean.md
|
||||
- Cloud:
|
||||
- Cloud: docs/references/configuration/cli/trivy_cloud.md
|
||||
- Cloud Edit Config: docs/references/configuration/cli/trivy_cloud_edit-config.md
|
||||
- Cloud Show Config: docs/references/configuration/cli/trivy_cloud_show-config.md
|
||||
- Cloud: docs/references/configuration/cli/trivy_cloud.md
|
||||
- Cloud Config: docs/references/configuration/cli/trivy_cloud_config.md
|
||||
- Cloud Config Edit: docs/references/configuration/cli/trivy_cloud_config_edit.md
|
||||
- Cloud Config List: docs/references/configuration/cli/trivy_cloud_config_list.md
|
||||
- Cloud Config Set: docs/references/configuration/cli/trivy_cloud_config_set.md
|
||||
- Cloud Config Unset: docs/references/configuration/cli/trivy_cloud_config_unset.md
|
||||
- Cloud Config Get: docs/references/configuration/cli/trivy_cloud_config_get.md
|
||||
- Config: docs/references/configuration/cli/trivy_config.md
|
||||
- Convert: docs/references/configuration/cli/trivy_convert.md
|
||||
- Filesystem: docs/references/configuration/cli/trivy_filesystem.md
|
||||
|
||||
@@ -7,9 +7,10 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/samber/lo"
|
||||
"github.com/zalando/go-keyring"
|
||||
@@ -28,21 +29,37 @@ const (
|
||||
DefaultTrivyServerUrl = "https://scan.trivy.dev"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
ServerURL string `yaml:"server-url"`
|
||||
ApiURL string `yaml:"api-url"`
|
||||
ServerScanning bool `yaml:"server-scanning"`
|
||||
UploadResults bool `yaml:"results-upload"`
|
||||
type Api struct {
|
||||
URL string `yaml:"url"`
|
||||
}
|
||||
|
||||
type Scanning struct {
|
||||
Enabled bool `yaml:"enabled"`
|
||||
UploadResults bool `yaml:"upload-results"`
|
||||
SecretConfig bool `yaml:"secret-config"`
|
||||
MisconfigConfig bool `yaml:"misconfig-config"`
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
URL string `yaml:"url"`
|
||||
Scanning Scanning `yaml:"scanning"`
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
Api Api `yaml:"api"`
|
||||
Server Server `yaml:"server"`
|
||||
IsLoggedIn bool `yaml:"-"`
|
||||
Token string `yaml:"-"`
|
||||
}
|
||||
|
||||
var defaultConfig = &Config{
|
||||
ServerScanning: true,
|
||||
UploadResults: true,
|
||||
ServerURL: DefaultTrivyServerUrl,
|
||||
ApiURL: DefaultApiUrl,
|
||||
Api: Api{
|
||||
URL: DefaultApiUrl,
|
||||
},
|
||||
Server: Server{
|
||||
URL: DefaultTrivyServerUrl,
|
||||
Scanning: Scanning{},
|
||||
},
|
||||
}
|
||||
|
||||
func getConfigPath() string {
|
||||
@@ -51,10 +68,14 @@ func getConfigPath() string {
|
||||
}
|
||||
|
||||
func (c *Config) Save() error {
|
||||
if c.Token == "" && c.ServerURL == "" && c.ApiURL == "" {
|
||||
if c.Token == "" && c.Server.URL == "" && c.Api.URL == "" {
|
||||
return xerrors.New("no config to save, required fields are token, server url, and api url")
|
||||
}
|
||||
|
||||
if err := c.initFirstLogin(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := keyring.Set(ServiceName, TokenKey, c.Token); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -90,6 +111,33 @@ func Clear() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// initFirstLogin initializes the default scanning settings to turn them on
|
||||
// after this, the user can configure in the config using the config set/unset commands
|
||||
func (c *Config) initFirstLogin() error {
|
||||
if c.Token == "" {
|
||||
// this isn't a login save, without a token it can't login
|
||||
return nil
|
||||
}
|
||||
|
||||
var firstLogin bool
|
||||
_, err := keyring.Get(ServiceName, TokenKey)
|
||||
if err != nil {
|
||||
if !errors.Is(err, keyring.ErrNotFound) {
|
||||
return err
|
||||
}
|
||||
firstLogin = true
|
||||
}
|
||||
|
||||
if firstLogin {
|
||||
// if first login, turn on all scanning options
|
||||
c.Server.Scanning.Enabled = true
|
||||
c.Server.Scanning.UploadResults = true
|
||||
c.Server.Scanning.MisconfigConfig = true
|
||||
c.Server.Scanning.SecretConfig = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Load loads the Trivy Cloud config from the config file and the keychain
|
||||
// If the config file does not exist the default config is returned
|
||||
func Load() (*Config, error) {
|
||||
@@ -102,7 +150,8 @@ func Load() (*Config, error) {
|
||||
return nil, err
|
||||
}
|
||||
logger.Debug("No cloud config file found")
|
||||
return defaultConfig, nil
|
||||
defaultCopy := *defaultConfig
|
||||
return &defaultCopy, nil
|
||||
}
|
||||
if err := yaml.Unmarshal(yamlData, &config); err != nil {
|
||||
return nil, err
|
||||
@@ -114,7 +163,8 @@ func Load() (*Config, error) {
|
||||
return nil, err
|
||||
}
|
||||
logger.Debug("No token found in keychain")
|
||||
return defaultConfig, nil
|
||||
config.Token = ""
|
||||
return &config, nil
|
||||
}
|
||||
|
||||
config.Token = token
|
||||
@@ -128,18 +178,19 @@ func (c *Config) Verify(ctx context.Context) error {
|
||||
return xerrors.New("no token provided for verification")
|
||||
}
|
||||
|
||||
if c.ServerURL == "" {
|
||||
if c.Server.URL == "" {
|
||||
return xerrors.New("no server URL provided for verification")
|
||||
}
|
||||
|
||||
logger := log.WithPrefix(log.PrefixCloud)
|
||||
logger.Debug("Verifying Trivy Cloud token")
|
||||
|
||||
client := xhttp.Client()
|
||||
url, err := url.JoinPath(c.ServerURL, "verify")
|
||||
url, err := url.JoinPath(c.Server.URL, "verify")
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to join server URL and verify path: %w", err)
|
||||
}
|
||||
|
||||
logger.Debug("Verifying Trivy Cloud token against server", log.String("verification_url", url))
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, url, http.NoBody)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to create verification request: %w", err)
|
||||
@@ -159,29 +210,8 @@ func (c *Config) Verify(ctx context.Context) error {
|
||||
|
||||
}
|
||||
|
||||
// OpenConfigForEditing opens the Trivy Cloud config file for editing in the default editor specified in the EDITOR environment variable
|
||||
func OpenConfigForEditing() error {
|
||||
configPath := getConfigPath()
|
||||
|
||||
logger := log.WithPrefix(log.PrefixCloud)
|
||||
if !fsutils.FileExists(configPath) {
|
||||
logger.Debug("Trivy Cloud config file does not exist", log.String("config_path", configPath))
|
||||
defaultConfig.Save()
|
||||
configPath = getConfigPath()
|
||||
}
|
||||
|
||||
editor := getEditCommand()
|
||||
|
||||
cmd := exec.Command(editor, configPath)
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
// ShowConfig shows the Trivy Cloud config in human readable format
|
||||
func ShowConfig() error {
|
||||
// ListConfig shows the Trivy Cloud config in human readable format
|
||||
func ListConfig() error {
|
||||
cloudConfig, err := Load()
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to load Trivy Cloud config file: %w", err)
|
||||
@@ -197,25 +227,72 @@ func ShowConfig() error {
|
||||
fmt.Println()
|
||||
fmt.Println("Trivy Cloud Configuration")
|
||||
fmt.Println("-------------------------")
|
||||
fmt.Printf("Logged In: %s\n", lo.Ternary(loggedIn, "Yes", "No"))
|
||||
fmt.Printf("Trivy Server URL: %s\n", cloudConfig.ServerURL)
|
||||
fmt.Printf("API URL: %s\n", cloudConfig.ApiURL)
|
||||
fmt.Printf("Server Scanning: %s\n", lo.Ternary(cloudConfig.ServerScanning, "Enabled", "Disabled"))
|
||||
fmt.Printf("Results Upload: %s\n", lo.Ternary(cloudConfig.UploadResults, "Enabled", "Disabled"))
|
||||
fmt.Printf("Filepath: %s\n", getConfigPath())
|
||||
fmt.Printf("Filepath: %s\n", getConfigPath())
|
||||
fmt.Printf("Logged In: %s\n", lo.Ternary(loggedIn, "Yes", "No"))
|
||||
fmt.Println()
|
||||
|
||||
fields := collectConfigFields(reflect.ValueOf(cloudConfig).Elem(), "")
|
||||
maxKeyLen := 0
|
||||
for _, field := range fields {
|
||||
maxKeyLen = max(maxKeyLen, len(field.path))
|
||||
}
|
||||
|
||||
for _, field := range fields {
|
||||
fmt.Printf("%-*s %s\n", maxKeyLen, field.path, formatValue(field.value))
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func getEditCommand() string {
|
||||
editor := os.Getenv("EDITOR")
|
||||
if editor != "" {
|
||||
return editor
|
||||
}
|
||||
|
||||
// fallback to notepad for windows or vi for macos/linux
|
||||
if runtime.GOOS == "windows" {
|
||||
return "notepad"
|
||||
}
|
||||
return "vi"
|
||||
|
||||
type configField struct {
|
||||
path string
|
||||
value reflect.Value
|
||||
}
|
||||
|
||||
func collectConfigFields(v reflect.Value, prefix string) []configField {
|
||||
var fields []configField
|
||||
t := v.Type()
|
||||
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
field := t.Field(i)
|
||||
fieldValue := v.Field(i)
|
||||
|
||||
yamlTag := field.Tag.Get("yaml")
|
||||
if yamlTag == "-" || yamlTag == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
tagName := strings.Split(yamlTag, ",")[0]
|
||||
fullPath := tagName
|
||||
if prefix != "" {
|
||||
fullPath = prefix + "." + tagName
|
||||
}
|
||||
if fieldValue.Kind() == reflect.Struct {
|
||||
fields = append(fields, collectConfigFields(fieldValue, fullPath)...)
|
||||
} else {
|
||||
fields = append(fields, configField{
|
||||
path: fullPath,
|
||||
value: fieldValue,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return fields
|
||||
}
|
||||
|
||||
func formatValue(v reflect.Value) string {
|
||||
switch v.Kind() {
|
||||
case reflect.Bool:
|
||||
return lo.Ternary(v.Bool(), "Enabled", "Disabled")
|
||||
case reflect.String:
|
||||
return v.String()
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||
return strconv.FormatInt(v.Int(), 10)
|
||||
case reflect.Float32, reflect.Float64:
|
||||
return fmt.Sprintf("%f", v.Float())
|
||||
default:
|
||||
return fmt.Sprintf("%v", v.Interface())
|
||||
}
|
||||
}
|
||||
|
||||
44
pkg/cloud/config_edit.go
Normal file
44
pkg/cloud/config_edit.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package cloud
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"runtime"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
"github.com/aquasecurity/trivy/pkg/utils/fsutils"
|
||||
)
|
||||
|
||||
// OpenConfigForEditing opens the Trivy Cloud config file for editing in the default editor specified in the EDITOR environment variable
|
||||
func OpenConfigForEditing() error {
|
||||
configPath := getConfigPath()
|
||||
|
||||
logger := log.WithPrefix(log.PrefixCloud)
|
||||
if !fsutils.FileExists(configPath) {
|
||||
logger.Debug("Trivy Cloud config file does not exist", log.String("config_path", configPath))
|
||||
defaultConfig.Save()
|
||||
configPath = getConfigPath()
|
||||
}
|
||||
|
||||
editor := getEditCommand()
|
||||
|
||||
cmd := exec.Command(editor, configPath)
|
||||
cmd.Stdin = os.Stdin
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
func getEditCommand() string {
|
||||
editor := os.Getenv("EDITOR")
|
||||
if editor != "" {
|
||||
return editor
|
||||
}
|
||||
|
||||
// fallback to notepad for windows or vi for macos/linux
|
||||
if runtime.GOOS == "windows" {
|
||||
return "notepad"
|
||||
}
|
||||
return "vi"
|
||||
}
|
||||
147
pkg/cloud/config_modify.go
Normal file
147
pkg/cloud/config_modify.go
Normal file
@@ -0,0 +1,147 @@
|
||||
package cloud
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/xerrors"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// Set sets a nested field in the Trivy Cloud config
|
||||
func Set(attribute string, value any) error {
|
||||
config, err := Load()
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to load Trivy Cloud config file: %w", err)
|
||||
}
|
||||
|
||||
if err := setNestedField(reflect.ValueOf(config).Elem(), attribute, value); err != nil {
|
||||
return xerrors.Errorf("failed to set attribute %q: %w", attribute, err)
|
||||
}
|
||||
|
||||
return config.Save()
|
||||
}
|
||||
|
||||
// Unset sets a nested field in the Trivy Cloud config to its default value
|
||||
func Unset(attribute string) error {
|
||||
config, err := Load()
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to load Trivy Cloud config file: %w", err)
|
||||
}
|
||||
|
||||
if err := unsetNestedField(reflect.ValueOf(config).Elem(), attribute); err != nil {
|
||||
return xerrors.Errorf("failed to unset attribute %q: %w", attribute, err)
|
||||
}
|
||||
|
||||
return config.Save()
|
||||
}
|
||||
|
||||
func unsetNestedField(value reflect.Value, attribute string) error {
|
||||
field, err := navigateToField(value, attribute)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
defaultField, err := navigateToField(reflect.ValueOf(defaultConfig).Elem(), attribute)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
field.Set(defaultField)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get gets a nested field from the Trivy Cloud config
|
||||
func Get(attribute string) (any, error) {
|
||||
return GetWithDefault[any](attribute, nil)
|
||||
}
|
||||
|
||||
// GetWithDefault gets a nested field from the Trivy Cloud config with a default value
|
||||
func GetWithDefault[T any](attribute string, defaultValue T) (T, error) {
|
||||
config, err := Load()
|
||||
if err != nil {
|
||||
return defaultValue, xerrors.Errorf("failed to load Trivy Cloud config file: %w", err)
|
||||
}
|
||||
|
||||
field, err := navigateToField(reflect.ValueOf(config).Elem(), attribute)
|
||||
if err != nil {
|
||||
return defaultValue, xerrors.Errorf("failed to get attribute %q: %w", attribute, err)
|
||||
}
|
||||
|
||||
return field.Interface().(T), nil
|
||||
}
|
||||
|
||||
func setNestedField(v reflect.Value, path string, value any) error {
|
||||
field, err := navigateToField(v, path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
convertedValue, err := convertToType(value, field.Type())
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to convert value: %w", err)
|
||||
}
|
||||
|
||||
field.Set(convertedValue)
|
||||
return nil
|
||||
}
|
||||
|
||||
func convertToType(value any, targetType reflect.Type) (reflect.Value, error) {
|
||||
val := reflect.ValueOf(value)
|
||||
if val.Type().AssignableTo(targetType) {
|
||||
return val, nil
|
||||
}
|
||||
targetPtr := reflect.New(targetType) // *T
|
||||
targetInterface := targetPtr.Interface()
|
||||
data, err := yaml.Marshal(value)
|
||||
if err != nil {
|
||||
return reflect.Value{}, xerrors.Errorf("failed to marshal value: %w", err)
|
||||
}
|
||||
if err := yaml.Unmarshal(data, targetInterface); err != nil {
|
||||
return reflect.Value{}, xerrors.Errorf("failed to decode into %v: %w", targetType, err)
|
||||
}
|
||||
return targetPtr.Elem(), nil
|
||||
}
|
||||
|
||||
func navigateToField(v reflect.Value, path string) (reflect.Value, error) {
|
||||
parts := strings.Split(path, ".")
|
||||
if len(parts) == 0 {
|
||||
return reflect.Value{}, xerrors.New("empty attribute path")
|
||||
}
|
||||
|
||||
for i, part := range parts {
|
||||
fieldName := yamlTagToFieldName(v, part)
|
||||
if fieldName == "" {
|
||||
return reflect.Value{}, xerrors.Errorf("field %q not found in config", part)
|
||||
}
|
||||
|
||||
field := v.FieldByName(fieldName)
|
||||
if !field.IsValid() {
|
||||
return reflect.Value{}, xerrors.Errorf("field %q not found", fieldName)
|
||||
}
|
||||
if !field.CanSet() {
|
||||
return reflect.Value{}, xerrors.Errorf("field %q cannot be set", fieldName)
|
||||
}
|
||||
|
||||
if i == len(parts)-1 {
|
||||
return field, nil
|
||||
}
|
||||
|
||||
v = field
|
||||
}
|
||||
|
||||
return reflect.Value{}, xerrors.New("unexpected end of path")
|
||||
}
|
||||
|
||||
func yamlTagToFieldName(v reflect.Value, yamlTag string) string {
|
||||
t := v.Type()
|
||||
for i := 0; i < t.NumField(); i++ {
|
||||
field := t.Field(i)
|
||||
tag := field.Tag.Get("yaml")
|
||||
tagName := strings.Split(tag, ",")[0]
|
||||
if tagName == yamlTag {
|
||||
return field.Name
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
244
pkg/cloud/config_modify_test.go
Normal file
244
pkg/cloud/config_modify_test.go
Normal file
@@ -0,0 +1,244 @@
|
||||
package cloud
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/zalando/go-keyring"
|
||||
)
|
||||
|
||||
func TestSet(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
configToSet map[string]any
|
||||
expected *Config
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
name: "success with valid config",
|
||||
configToSet: map[string]any{"server.scanning.enabled": true},
|
||||
expected: &Config{Api: Api{URL: "https://api.trivy.dev"}, Server: Server{URL: "https://scan.trivy.dev", Scanning: Scanning{Enabled: true, UploadResults: false, SecretConfig: false, MisconfigConfig: false}}, IsLoggedIn: false, Token: ""},
|
||||
expectedError: "",
|
||||
},
|
||||
{
|
||||
name: "success with valid config using off for a boolean",
|
||||
configToSet: map[string]any{"server.scanning.enabled": "on"},
|
||||
expected: &Config{Api: Api{URL: "https://api.trivy.dev"}, Server: Server{URL: "https://scan.trivy.dev", Scanning: Scanning{Enabled: true, UploadResults: false, SecretConfig: false, MisconfigConfig: false}}, IsLoggedIn: false, Token: ""},
|
||||
expectedError: "",
|
||||
},
|
||||
{
|
||||
name: "error with invalid config",
|
||||
configToSet: map[string]any{"server.scanning.foo": false},
|
||||
expected: &Config{Api: Api{URL: "https://api.trivy.dev"}, Server: Server{URL: "https://scan.trivy.dev", Scanning: Scanning{Enabled: false, UploadResults: false, SecretConfig: false, MisconfigConfig: false}}, IsLoggedIn: false, Token: ""},
|
||||
expectedError: "field \"foo\" not found in config",
|
||||
},
|
||||
{
|
||||
name: "error when setting boolean with yessir",
|
||||
configToSet: map[string]any{"server.scanning.enabled": "yessir"},
|
||||
expected: &Config{Api: Api{URL: "https://api.trivy.dev"}, Server: Server{URL: "https://scan.trivy.dev", Scanning: Scanning{Enabled: false, UploadResults: false, SecretConfig: false, MisconfigConfig: false}}, IsLoggedIn: false, Token: ""},
|
||||
expectedError: "cannot unmarshal !!str `yessir` into bool",
|
||||
},
|
||||
{
|
||||
name: "error when setting boolean with invalid value",
|
||||
configToSet: map[string]any{"server.scanning.enabled": "invalid"},
|
||||
expected: &Config{Api: Api{URL: "https://api.trivy.dev"}, Server: Server{URL: "https://scan.trivy.dev", Scanning: Scanning{Enabled: false, UploadResults: false, SecretConfig: false, MisconfigConfig: false}}, IsLoggedIn: false, Token: ""},
|
||||
expectedError: "cannot unmarshal !!str `invalid` into bool",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
t.Setenv("XDG_DATA_HOME", tempDir)
|
||||
|
||||
keyring.MockInit()
|
||||
defer keyring.DeleteAll(ServiceName)
|
||||
defer Clear()
|
||||
|
||||
for key, value := range tt.configToSet {
|
||||
err := Set(key, value)
|
||||
if tt.expectedError != "" {
|
||||
require.ErrorContains(t, err, tt.expectedError)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
config, err := Load()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tt.expected, config)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
primeToken bool
|
||||
setupConfig *Config
|
||||
attribute string
|
||||
defaultValue any
|
||||
expected any
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
name: "success with default config",
|
||||
setupConfig: nil,
|
||||
attribute: "server.scanning.enabled",
|
||||
defaultValue: false,
|
||||
expected: false,
|
||||
expectedError: "",
|
||||
},
|
||||
{
|
||||
name: "success with custom config",
|
||||
primeToken: true,
|
||||
setupConfig: &Config{
|
||||
Token: "test",
|
||||
Server: Server{
|
||||
URL: "https://example.com",
|
||||
Scanning: Scanning{
|
||||
Enabled: false,
|
||||
UploadResults: true,
|
||||
SecretConfig: false,
|
||||
MisconfigConfig: true,
|
||||
},
|
||||
},
|
||||
Api: Api{URL: "https://api.example.com"},
|
||||
},
|
||||
attribute: "server.scanning.enabled",
|
||||
defaultValue: false,
|
||||
expected: false,
|
||||
expectedError: "",
|
||||
},
|
||||
{
|
||||
name: "error with invalid attribute",
|
||||
setupConfig: nil,
|
||||
attribute: "server.scanning.foo",
|
||||
defaultValue: true,
|
||||
expected: true,
|
||||
expectedError: "field \"foo\" not found in config",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
t.Setenv("XDG_DATA_HOME", tempDir)
|
||||
|
||||
keyring.MockInit()
|
||||
defer keyring.DeleteAll(ServiceName)
|
||||
defer Clear()
|
||||
|
||||
if tt.primeToken {
|
||||
// add the key so the custom config isn't overwritten
|
||||
require.NoError(t, keyring.Set(ServiceName, TokenKey, tt.setupConfig.Token))
|
||||
}
|
||||
|
||||
if tt.setupConfig != nil {
|
||||
err := tt.setupConfig.Save()
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
value, err := GetWithDefault(tt.attribute, tt.defaultValue)
|
||||
if tt.expectedError != "" {
|
||||
require.ErrorContains(t, err, tt.expectedError)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tt.expected, value)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnset(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
primeToken bool
|
||||
setupConfig *Config
|
||||
attribute string
|
||||
expectedValue any
|
||||
expectedError string
|
||||
}{
|
||||
{
|
||||
name: "success with default config",
|
||||
setupConfig: defaultConfig,
|
||||
attribute: "server.scanning.enabled",
|
||||
expectedValue: false,
|
||||
expectedError: "",
|
||||
},
|
||||
{
|
||||
name: "success with custom config",
|
||||
setupConfig: &Config{
|
||||
Token: "test",
|
||||
Server: Server{
|
||||
URL: "https://example.com",
|
||||
Scanning: Scanning{
|
||||
Enabled: false,
|
||||
UploadResults: true,
|
||||
SecretConfig: false,
|
||||
MisconfigConfig: true,
|
||||
},
|
||||
},
|
||||
Api: Api{URL: "https://api.example.com"},
|
||||
},
|
||||
attribute: "server.scanning.enabled",
|
||||
expectedValue: false,
|
||||
expectedError: "",
|
||||
},
|
||||
{
|
||||
name: "success with custom url reset",
|
||||
setupConfig: &Config{
|
||||
Token: "test",
|
||||
Server: Server{
|
||||
URL: "https://example.com",
|
||||
Scanning: Scanning{
|
||||
Enabled: false,
|
||||
UploadResults: true,
|
||||
SecretConfig: false,
|
||||
MisconfigConfig: true,
|
||||
},
|
||||
},
|
||||
Api: Api{URL: "https://api.custom.com"},
|
||||
},
|
||||
attribute: "api.url",
|
||||
expectedValue: "https://api.trivy.dev",
|
||||
expectedError: "",
|
||||
},
|
||||
{
|
||||
name: "error with invalid attribute",
|
||||
setupConfig: defaultConfig,
|
||||
attribute: "server.scanning.foo",
|
||||
expectedValue: true,
|
||||
expectedError: "field \"foo\" not found in config",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
tempDir := t.TempDir()
|
||||
t.Setenv("XDG_DATA_HOME", tempDir)
|
||||
|
||||
keyring.MockInit()
|
||||
defer keyring.DeleteAll(ServiceName)
|
||||
defer Clear()
|
||||
|
||||
if tt.primeToken {
|
||||
// prime the token so it doesn't get overwritten
|
||||
require.NoError(t, keyring.Set(ServiceName, TokenKey, tt.setupConfig.Token))
|
||||
}
|
||||
|
||||
require.NoError(t, tt.setupConfig.Save())
|
||||
err := Unset(tt.attribute)
|
||||
if tt.expectedError != "" {
|
||||
require.ErrorContains(t, err, tt.expectedError)
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
value, err := Get(tt.attribute)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tt.expectedValue, value)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -27,17 +27,25 @@ func TestSave(t *testing.T) {
|
||||
{
|
||||
name: "config with all fields",
|
||||
config: &Config{
|
||||
Token: "test-token-123",
|
||||
ServerURL: "https://example.com",
|
||||
ApiURL: "https://api.example.com",
|
||||
Token: "test-token-123",
|
||||
Server: Server{
|
||||
URL: "https://example.com",
|
||||
},
|
||||
Api: Api{
|
||||
URL: "https://api.example.com",
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "config without token",
|
||||
config: &Config{
|
||||
ServerURL: "https://example.com",
|
||||
ApiURL: "https://api.example.com",
|
||||
Server: Server{
|
||||
URL: "https://example.com",
|
||||
},
|
||||
Api: Api{
|
||||
URL: "https://api.example.com",
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
@@ -67,7 +75,7 @@ func TestSave(t *testing.T) {
|
||||
assert.Equal(t, tt.config, config)
|
||||
|
||||
configPath := getConfigPath()
|
||||
if tt.config.ServerURL != "" || tt.config.ApiURL != "" {
|
||||
if tt.config.Server.URL != "" || tt.config.Api.URL != "" {
|
||||
assert.FileExists(t, configPath)
|
||||
}
|
||||
})
|
||||
@@ -102,8 +110,10 @@ func TestClear(t *testing.T) {
|
||||
|
||||
if tt.createConfig {
|
||||
config := &Config{
|
||||
Token: "testtoken",
|
||||
ServerURL: "https://example.com",
|
||||
Token: "testtoken",
|
||||
Server: Server{
|
||||
URL: "https://example.com",
|
||||
},
|
||||
}
|
||||
err := config.Save()
|
||||
require.NoError(t, err)
|
||||
@@ -153,9 +163,13 @@ func TestLoad(t *testing.T) {
|
||||
token := "testtoken"
|
||||
if tt.createConfig {
|
||||
config := &Config{
|
||||
Token: token,
|
||||
ServerURL: "https://example.com",
|
||||
ApiURL: "https://api.example.com",
|
||||
Token: token,
|
||||
Server: Server{
|
||||
URL: "https://example.com",
|
||||
},
|
||||
Api: Api{
|
||||
URL: "https://api.example.com",
|
||||
},
|
||||
}
|
||||
err := config.Save()
|
||||
require.NoError(t, err)
|
||||
@@ -169,8 +183,8 @@ func TestLoad(t *testing.T) {
|
||||
require.NotNil(t, config)
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, token, config.Token)
|
||||
assert.Equal(t, "https://example.com", config.ServerURL)
|
||||
assert.Equal(t, "https://api.example.com", config.ApiURL)
|
||||
assert.Equal(t, "https://example.com", config.Server.URL)
|
||||
assert.Equal(t, "https://api.example.com", config.Api.URL)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -184,7 +198,7 @@ func TestVerify(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
name: "success with valid config",
|
||||
config: &Config{Token: "testtoken", ServerURL: "https://example.com", ApiURL: "https://api.example.com"},
|
||||
config: &Config{Token: "testtoken", Server: Server{URL: "https://example.com"}, Api: Api{URL: "https://api.example.com"}},
|
||||
status: http.StatusOK,
|
||||
wantErr: false,
|
||||
},
|
||||
@@ -211,7 +225,7 @@ func TestVerify(t *testing.T) {
|
||||
}))
|
||||
defer server.Close()
|
||||
|
||||
tt.config.ServerURL = server.URL
|
||||
tt.config.Server.URL = server.URL
|
||||
|
||||
err := tt.config.Verify(context.Background())
|
||||
if tt.wantErr {
|
||||
@@ -223,21 +237,60 @@ func TestVerify(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestShowConfig(t *testing.T) {
|
||||
func TestListConfig(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
config *Config
|
||||
primeToken bool
|
||||
setupConfig *Config
|
||||
wantErr string
|
||||
wantContains []string
|
||||
}{
|
||||
{
|
||||
name: "success with valid config",
|
||||
config: &Config{Token: "testtoken", ServerURL: "https://example.com", ApiURL: "https://api.example.com"},
|
||||
name: "success with valid config",
|
||||
primeToken: true,
|
||||
setupConfig: &Config{
|
||||
Token: "testtoken",
|
||||
Server: Server{
|
||||
URL: "https://example.com",
|
||||
Scanning: Scanning{
|
||||
Enabled: true,
|
||||
UploadResults: false,
|
||||
SecretConfig: true,
|
||||
MisconfigConfig: false,
|
||||
},
|
||||
},
|
||||
Api: Api{URL: "https://api.example.com"},
|
||||
},
|
||||
wantContains: []string{
|
||||
"Trivy Cloud Configuration",
|
||||
"Trivy Server URL: https://example.com",
|
||||
"API URL: https://api.example.com",
|
||||
"Logged In: No",
|
||||
"Logged In: No",
|
||||
"Filepath:",
|
||||
"api.url",
|
||||
"https://api.example.com",
|
||||
"server.url",
|
||||
"https://example.com",
|
||||
"server.scanning.enabled",
|
||||
"Enabled",
|
||||
"server.scanning.upload-results",
|
||||
"Disabled",
|
||||
"server.scanning.secret-config",
|
||||
"server.scanning.misconfig-config",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "success with default config",
|
||||
setupConfig: nil,
|
||||
wantContains: []string{
|
||||
"Trivy Cloud Configuration",
|
||||
"Logged In: No",
|
||||
"api.url",
|
||||
DefaultApiUrl,
|
||||
"server.url",
|
||||
DefaultTrivyServerUrl,
|
||||
"server.scanning.enabled",
|
||||
"server.scanning.upload-results",
|
||||
"server.scanning.secret-config",
|
||||
"server.scanning.misconfig-config",
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -251,8 +304,13 @@ func TestShowConfig(t *testing.T) {
|
||||
defer keyring.DeleteAll(ServiceName)
|
||||
defer Clear()
|
||||
|
||||
if tt.config != nil {
|
||||
err := tt.config.Save()
|
||||
if tt.primeToken {
|
||||
// prime the token in the keyring so the custom config doesn't get overwritten
|
||||
require.NoError(t, keyring.Set(ServiceName, TokenKey, tt.setupConfig.Token))
|
||||
}
|
||||
|
||||
if tt.setupConfig != nil {
|
||||
err := tt.setupConfig.Save()
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
@@ -264,7 +322,7 @@ func TestShowConfig(t *testing.T) {
|
||||
|
||||
errChan := make(chan error, 1)
|
||||
go func() {
|
||||
errChan <- ShowConfig()
|
||||
errChan <- ListConfig()
|
||||
w.Close()
|
||||
}()
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ func (h *CloudPlatformResultsHook) uploadResults(ctx context.Context, jsonReport
|
||||
}
|
||||
|
||||
func (h *CloudPlatformResultsHook) getPresignedUploadUrl(ctx context.Context) (string, error) {
|
||||
uploadUrl, err := url.JoinPath(h.cloudConfig.ApiURL, presignedUploadUrl)
|
||||
uploadUrl, err := url.JoinPath(h.cloudConfig.Api.URL, presignedUploadUrl)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to join API URL and presigned upload URL: %w", err)
|
||||
}
|
||||
|
||||
@@ -1476,7 +1476,7 @@ func NewLogoutCommand() *cobra.Command {
|
||||
|
||||
func NewCloudCommand() *cobra.Command {
|
||||
cloudCmd := &cobra.Command{
|
||||
Use: "cloud [flags]",
|
||||
Use: "cloud subcommand",
|
||||
Short: "Control Trivy Cloud platform integration settings",
|
||||
GroupID: cloud.GroupCloud,
|
||||
}
|
||||
@@ -1487,26 +1487,81 @@ func NewCloudCommand() *cobra.Command {
|
||||
Title: "Trivy Cloud Commands",
|
||||
})
|
||||
|
||||
cloudCmd.AddCommand(
|
||||
configCmd := &cobra.Command{
|
||||
Use: "config subcommand",
|
||||
Short: "Control Trivy Cloud configuration",
|
||||
GroupID: cloud.GroupCloud,
|
||||
}
|
||||
|
||||
configCmd.AddGroup(&cobra.Group{
|
||||
ID: cloud.GroupCloud,
|
||||
Title: "Trivy Cloud Configuration Commands",
|
||||
})
|
||||
|
||||
configCmd.AddCommand(
|
||||
&cobra.Command{
|
||||
Use: "edit-config",
|
||||
Use: "edit",
|
||||
Short: "Edit Trivy Cloud configuration",
|
||||
Long: "Edit the Trivy Cloud platform configuration in the default editor specified in the EDITOR environment variable",
|
||||
Long: "Edit Trivy Cloud platform configuration in the default editor specified in the EDITOR environment variable",
|
||||
GroupID: cloud.GroupCloud,
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
return cloud.EditConfig()
|
||||
},
|
||||
},
|
||||
&cobra.Command{
|
||||
Use: "show-config",
|
||||
Short: "Show Trivy Cloud configuration",
|
||||
Long: "Show Trivy Cloud platform configuration in human readable format",
|
||||
Use: "list",
|
||||
Short: "List Trivy Cloud configuration",
|
||||
Long: "List Trivy Cloud platform configuration in human readable format",
|
||||
GroupID: cloud.GroupCloud,
|
||||
RunE: func(_ *cobra.Command, _ []string) error {
|
||||
return cloud.ShowConfig()
|
||||
return cloud.ListConfig()
|
||||
},
|
||||
},
|
||||
&cobra.Command{
|
||||
Use: "set [setting] [value]",
|
||||
Short: "Set Trivy Cloud configuration",
|
||||
Long: `Set a Trivy Cloud platform setting
|
||||
|
||||
Available config settings can be viewed by using the ` + "`trivy cloud config list`" + ` command`,
|
||||
Example: ` $ trivy cloud config set server.scanning.enabled true
|
||||
$ trivy cloud config set server.scanning.upload-results false`,
|
||||
Args: cobra.ExactArgs(2),
|
||||
GroupID: cloud.GroupCloud,
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
return cloud.SetConfig(args[0], args[1])
|
||||
},
|
||||
},
|
||||
&cobra.Command{
|
||||
Use: "unset [setting]",
|
||||
Short: "Unset Trivy Cloud configuration",
|
||||
Long: `Unset a Trivy Cloud platform configuration and return it to the default setting
|
||||
|
||||
Available config settings can be viewed by using the ` + "`trivy cloud config list`" + ` command`,
|
||||
Example: ` $ trivy cloud config unset server.scanning.enabled
|
||||
$ trivy cloud config unset server.scanning.upload-results`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
GroupID: cloud.GroupCloud,
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
return cloud.UnsetConfig(args[0])
|
||||
},
|
||||
},
|
||||
&cobra.Command{
|
||||
Use: "get [setting]",
|
||||
Short: "Get Trivy Cloud configuration",
|
||||
Long: `Get a Trivy Cloud platform configuration
|
||||
|
||||
Available config settings can be viewed by using the ` + "`trivy cloud config list`" + ` command`,
|
||||
Example: ` $ trivy cloud config get server.scanning.enabled
|
||||
$ trivy cloud config get server.scanning.upload-results`,
|
||||
Args: cobra.ExactArgs(1),
|
||||
GroupID: cloud.GroupCloud,
|
||||
RunE: func(_ *cobra.Command, args []string) error {
|
||||
return cloud.GetConfig(args[0])
|
||||
},
|
||||
},
|
||||
)
|
||||
cloudCmd.AddCommand(configCmd)
|
||||
|
||||
return cloudCmd
|
||||
}
|
||||
|
||||
|
||||
@@ -36,8 +36,8 @@ func Login(ctx context.Context, opts flag.Options) error {
|
||||
return xerrors.Errorf("failed to load Trivy Cloud config: %w", err)
|
||||
}
|
||||
cloudConfig.Token = creds.Token
|
||||
cloudConfig.ServerURL = opts.CloudOptions.TrivyServerUrl
|
||||
cloudConfig.ApiURL = opts.CloudOptions.ApiUrl
|
||||
cloudConfig.Server.URL = opts.CloudOptions.TrivyServerUrl
|
||||
cloudConfig.Api.URL = opts.CloudOptions.ApiUrl
|
||||
|
||||
if err := cloudConfig.Verify(ctx); err != nil {
|
||||
return xerrors.Errorf("failed to verify Trivy Cloud config: %w", err)
|
||||
@@ -77,14 +77,14 @@ func CheckTrivyCloudStatus(cmd *cobra.Command) error {
|
||||
|
||||
if cloudConfig != nil && cloudConfig.Verify(cmd.Context()) == nil {
|
||||
logger.Info("Trivy cloud is logged in")
|
||||
if cloudConfig.ServerScanning {
|
||||
if cloudConfig.Server.Scanning.Enabled {
|
||||
logger.Info("Trivy Cloud server scanning is enabled")
|
||||
os.Setenv("TRIVY_SERVER", cloudConfig.ServerURL)
|
||||
os.Setenv("TRIVY_SERVER", cloudConfig.Server.URL)
|
||||
os.Setenv("TRIVY_TOKEN_HEADER", "Authorization")
|
||||
os.Setenv("TRIVY_TOKEN", fmt.Sprintf("Bearer %s", cloudConfig.Token))
|
||||
}
|
||||
|
||||
if cloudConfig.UploadResults {
|
||||
if cloudConfig.Server.Scanning.UploadResults {
|
||||
logger.Info("Trivy Cloud results upload is enabled")
|
||||
// add hook to upload the results to Trivy Cloud
|
||||
resultHook := hooks.NewResultsHook(cloudConfig)
|
||||
@@ -95,10 +95,26 @@ func CheckTrivyCloudStatus(cmd *cobra.Command) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func ShowConfig() error {
|
||||
return cloud.ShowConfig()
|
||||
func ListConfig() error {
|
||||
return cloud.ListConfig()
|
||||
}
|
||||
|
||||
func EditConfig() error {
|
||||
return cloud.OpenConfigForEditing()
|
||||
}
|
||||
|
||||
func SetConfig(attribute string, value any) error {
|
||||
return cloud.Set(attribute, value)
|
||||
}
|
||||
|
||||
func UnsetConfig(attribute string) error {
|
||||
return cloud.Unset(attribute)
|
||||
}
|
||||
func GetConfig(attribute string) error {
|
||||
value, err := cloud.Get(attribute)
|
||||
if err != nil {
|
||||
return xerrors.Errorf("failed to get Trivy Cloud config: %w", err)
|
||||
}
|
||||
fmt.Println(value)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -42,8 +42,12 @@ func TestLogout(t *testing.T) {
|
||||
|
||||
if tt.createConfigFile {
|
||||
config := &cloud.Config{
|
||||
ServerURL: "https://example.com",
|
||||
ApiURL: "https://api.example.com",
|
||||
Server: cloud.Server{
|
||||
URL: "https://example.com",
|
||||
},
|
||||
Api: cloud.Api{
|
||||
URL: "https://api.example.com",
|
||||
},
|
||||
}
|
||||
err := config.Save()
|
||||
require.NoError(t, err)
|
||||
@@ -134,8 +138,8 @@ func TestLogin(t *testing.T) {
|
||||
config, err := cloud.Load()
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tt.token, config.Token)
|
||||
require.Equal(t, server.URL, config.ServerURL)
|
||||
require.Equal(t, server.URL+"/api", config.ApiURL)
|
||||
require.Equal(t, server.URL, config.Server.URL)
|
||||
require.Equal(t, server.URL+"/api", config.Api.URL)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user