From a630247b9032eac41abe09d472f8a98509b5fe56 Mon Sep 17 00:00:00 2001 From: mitchell Date: Tue, 17 Sep 2024 14:22:34 -0400 Subject: [PATCH 1/2] Allow config options to be an enum. Use it for 'security.prompt.level'. --- internal/mediators/config/registry.go | 19 +++++++++++++++++- internal/runbits/cves/cves.go | 8 +++++++- internal/runners/config/set.go | 11 ++++++++++ test/integration/config_int_test.go | 29 +++++++++++++++++++++++++++ 4 files changed, 65 insertions(+), 2 deletions(-) diff --git a/internal/mediators/config/registry.go b/internal/mediators/config/registry.go index 767d87caa6..d2af1f32d3 100644 --- a/internal/mediators/config/registry.go +++ b/internal/mediators/config/registry.go @@ -6,12 +6,20 @@ const ( String Type = iota Int Bool + Enum ) // Event is run when a user tries to set or get a config value via `state config` type Event func(value interface{}) (interface{}, error) -var EmptyEvent = func(value interface{}) (interface{}, error) { return value, nil } +var EmptyEvent = func(value interface{}) (interface{}, error) { + if enum, ok := value.(*Enums); ok { + // In case this config option is not set, return its default value instead + // of the Enums struct itself. + return enum.Default, nil + } + return value, nil +} // Option defines what a config value's name and type should be, along with any get/set events type Option struct { @@ -27,6 +35,15 @@ type Registry map[string]Option var registry = make(Registry) +type Enums struct { + Options []string + Default string +} + +func NewEnum(options []string, default_ string) *Enums { + return &Enums{options, default_} +} + // GetOption returns a config option, regardless of whether or not it has been registered. // Use KnownOption to determine if the returned option has been previously registered. func GetOption(key string) Option { diff --git a/internal/runbits/cves/cves.go b/internal/runbits/cves/cves.go index e3cdf029e2..96c88135f1 100644 --- a/internal/runbits/cves/cves.go +++ b/internal/runbits/cves/cves.go @@ -21,7 +21,13 @@ import ( func init() { configMediator.RegisterOption(constants.SecurityPromptConfig, configMediator.Bool, true) - configMediator.RegisterOption(constants.SecurityPromptLevelConfig, configMediator.String, vulnModel.SeverityCritical) + severities := configMediator.NewEnum([]string{ + vulnModel.SeverityCritical, + vulnModel.SeverityHigh, + vulnModel.SeverityMedium, + vulnModel.SeverityLow, + }, vulnModel.SeverityCritical) + configMediator.RegisterOption(constants.SecurityPromptLevelConfig, configMediator.Enum, severities) } type primeable interface { diff --git a/internal/runners/config/set.go b/internal/runners/config/set.go index eeb1719804..5c2b0d4d44 100644 --- a/internal/runners/config/set.go +++ b/internal/runners/config/set.go @@ -4,6 +4,9 @@ import ( "context" "fmt" "strconv" + "strings" + + "github.com/thoas/go-funk" "github.com/ActiveState/cli/internal/analytics" "github.com/ActiveState/cli/internal/analytics/constants" @@ -52,6 +55,14 @@ func (s *Set) Run(params SetParams) error { if err != nil { return locale.WrapInputError(err, "Invalid integer value") } + case configMediator.Enum: + enums := option.Default.(*configMediator.Enums) + if !funk.Contains(enums.Options, params.Value) { + return locale.NewInputError( + "err_config_set_enum_invalid_value", "Invalid value '{{.V0}}': expected one of: {{.V1}}", + params.Value, strings.Join(enums.Options, ", ")) + } + value = params.Value default: value = params.Value } diff --git a/test/integration/config_int_test.go b/test/integration/config_int_test.go index 4a2bdd8b00..db1bb3dfe9 100644 --- a/test/integration/config_int_test.go +++ b/test/integration/config_int_test.go @@ -1,12 +1,14 @@ package integration import ( + "strings" "testing" "github.com/ActiveState/cli/internal/constants" "github.com/ActiveState/cli/internal/testhelpers/e2e" "github.com/ActiveState/cli/internal/testhelpers/suite" "github.com/ActiveState/cli/internal/testhelpers/tagsuite" + vulnModel "github.com/ActiveState/cli/pkg/platform/api/vulnerabilities/model" ) type ConfigIntegrationTestSuite struct { @@ -41,6 +43,33 @@ func (suite *ConfigIntegrationTestSuite) TestConfig() { cp.Expect("Invalid boolean value") } +func (suite *ConfigIntegrationTestSuite) TestEnum() { + suite.OnlyRunForTags(tagsuite.Config) + ts := e2e.New(suite.T(), false) + defer ts.Close() + + cp := ts.Spawn("config", "get", constants.SecurityPromptLevelConfig) + cp.Expect(vulnModel.SeverityCritical) + + severities := []string{ + vulnModel.SeverityCritical, + vulnModel.SeverityHigh, + vulnModel.SeverityMedium, + vulnModel.SeverityLow, + } + + cp = ts.Spawn("config", "set", constants.SecurityPromptLevelConfig, "invalid") + cp.Expect("Invalid value 'invalid': expected one of: " + strings.Join(severities, ", ")) + cp.ExpectNotExitCode(0) + + cp = ts.Spawn("config", "set", constants.SecurityPromptLevelConfig, vulnModel.SeverityLow) + cp.ExpectExitCode(0) + + cp = ts.Spawn("config", "get", constants.SecurityPromptLevelConfig) + cp.Expect(vulnModel.SeverityLow) + cp.ExpectExitCode(0) +} + func (suite *ConfigIntegrationTestSuite) TestJSON() { suite.OnlyRunForTags(tagsuite.Config, tagsuite.JSON) ts := e2e.New(suite.T(), false) From 8eabdcf39ab452fcbb5fc50406eb742a9df41dc3 Mon Sep 17 00:00:00 2001 From: mitchell Date: Mon, 23 Sep 2024 11:22:28 -0400 Subject: [PATCH 2/2] Catch programming error. --- internal/runners/config/set.go | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/internal/runners/config/set.go b/internal/runners/config/set.go index 5c2b0d4d44..9cf451d26a 100644 --- a/internal/runners/config/set.go +++ b/internal/runners/config/set.go @@ -56,7 +56,10 @@ func (s *Set) Run(params SetParams) error { return locale.WrapInputError(err, "Invalid integer value") } case configMediator.Enum: - enums := option.Default.(*configMediator.Enums) + enums, ok := option.Default.(*configMediator.Enums) + if !ok { + return errs.New("Programming error: config key '%s' was registered as an enum, but the default was not an enum", params.Key.String()) + } if !funk.Contains(enums.Options, params.Value) { return locale.NewInputError( "err_config_set_enum_invalid_value", "Invalid value '{{.V0}}': expected one of: {{.V1}}",