diff --git a/internal/osbuild/oscap_remediation_stage.go b/internal/osbuild/oscap_remediation_stage.go new file mode 100644 index 000000000..04d5a57a6 --- /dev/null +++ b/internal/osbuild/oscap_remediation_stage.go @@ -0,0 +1,87 @@ +package osbuild + +import "fmt" + +type OscapVerbosityLevel string + +const ( + OscapVerbosityLevelDevel = "DEVEL" + OscapVerbosityLevelInfo = "INFO" + OscapVerbosityLevelError = "ERROR" + OscapVerbosityLevelWarning = "WARNING" +) + +type OscapRemediationStageOptions struct { + DataDir string `json:"data_dir,omitempty"` + Config OscapConfig `json:"config"` +} +type OscapConfig struct { + Datastream string `json:"datastream" toml:"datastream"` + ProfileID string `json:"profile_id" toml:"profile_id"` + DatastreamID string `json:"datastream_id,omitempty" toml:"datastream_id,omitempty"` + XCCDFID string `json:"xccdf_id,omitempty" toml:"xccdf_id,omitempty"` + BenchmarkID string `json:"benchmark_id,omitempty" toml:"benchmark_id,omitempty"` + Tailoring string `json:"tailoring,omitempty" toml:"tailoring,omitempty"` + TailoringID string `json:"tailoring_id,omitempty" toml:"tailoring_id,omitempty"` + ArfResult string `json:"arf_result,omitempty" toml:"arf_result,omitempty"` + HtmlReport string `json:"html_report,omitempty" toml:"html_report,omitempty"` + VerboseLog string `json:"verbose_log,omitempty" toml:"verbose_log,omitempty"` + VerboseLevel OscapVerbosityLevel `json:"verbose_level,omitempty" toml:"verbose_level,omitempty"` +} + +func (OscapRemediationStageOptions) isStageOptions() {} + +func (c OscapConfig) validate() error { + if c.Datastream == "" { + return fmt.Errorf("'datastream' must be specified") + } + if c.ProfileID == "" { + return fmt.Errorf("'profile_id' must be specified") + } + if c.VerboseLevel != "" { + allowedVerboseLevelValues := []OscapVerbosityLevel{ + OscapVerbosityLevelDevel, + OscapVerbosityLevelError, + OscapVerbosityLevelInfo, + OscapVerbosityLevelWarning, + } + valid := false + for _, value := range allowedVerboseLevelValues { + if c.VerboseLevel == value { + valid = true + break + } + } + if !valid { + return fmt.Errorf("'verbose_level' option does not allow %q as a value", c.VerboseLevel) + } + } + return nil +} + +func NewOscapRemediationStage(options *OscapRemediationStageOptions) *Stage { + if err := options.Config.validate(); err != nil { + panic(err) + } + + return &Stage{ + Type: "org.osbuild.oscap.remediation", + Options: options, + } +} + +func NewOscapRemediationStageOptions(options OscapConfig) *OscapRemediationStageOptions { + return &OscapRemediationStageOptions{ + Config: OscapConfig{ + ProfileID: options.ProfileID, + Datastream: options.Datastream, + DatastreamID: options.DatastreamID, + XCCDFID: options.XCCDFID, + BenchmarkID: options.BenchmarkID, + ArfResult: options.ArfResult, + HtmlReport: options.HtmlReport, + VerboseLog: options.VerboseLog, + VerboseLevel: options.VerboseLevel, + }, + } +} diff --git a/internal/osbuild/oscap_remediation_stage_test.go b/internal/osbuild/oscap_remediation_stage_test.go new file mode 100644 index 000000000..f8657b22e --- /dev/null +++ b/internal/osbuild/oscap_remediation_stage_test.go @@ -0,0 +1,85 @@ +package osbuild + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestNewOscapRemediationStage(t *testing.T) { + stageOptions := &OscapRemediationStageOptions{DataDir: "/var/tmp", Config: OscapConfig{ + Datastream: "test_stream", + ProfileID: "test_profile", + }} + expectedStage := &Stage{ + Type: "org.osbuild.oscap.remediation", + Options: stageOptions, + } + actualStage := NewOscapRemediationStage(stageOptions) + assert.Equal(t, expectedStage, actualStage) +} + +func TestOscapRemediationStageOptionsValidate(t *testing.T) { + tests := []struct { + name string + options OscapRemediationStageOptions + err bool + }{ + { + name: "empty-options", + options: OscapRemediationStageOptions{}, + err: true, + }, + { + name: "empty-datastream", + options: OscapRemediationStageOptions{ + Config: OscapConfig{ + ProfileID: "test-profile", + }, + }, + err: true, + }, + { + name: "empty-profile-id", + options: OscapRemediationStageOptions{ + Config: OscapConfig{ + Datastream: "test-datastream", + }, + }, + err: true, + }, + { + name: "invalid-verbosity-level", + options: OscapRemediationStageOptions{ + Config: OscapConfig{ + Datastream: "test-datastream", + ProfileID: "test-profile", + VerboseLevel: "FAKE", + }, + }, + err: true, + }, + { + name: "valid-data", + options: OscapRemediationStageOptions{ + Config: OscapConfig{ + Datastream: "test-datastream", + ProfileID: "test-profile", + VerboseLevel: "INFO", + }, + }, + err: false, + }, + } + for idx, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.err { + assert.Errorf(t, tt.options.Config.validate(), "%q didn't return an error [idx: %d]", tt.name, idx) + assert.Panics(t, func() { NewOscapRemediationStage(&tt.options) }) + } else { + assert.NoErrorf(t, tt.options.Config.validate(), "%q returned an error [idx: %d]", tt.name, idx) + assert.NotPanics(t, func() { NewOscapRemediationStage(&tt.options) }) + } + }) + } +}