Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(cmd): #281 Create upgrade command implementing go-oscal revise. #295

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ compliance_report-*
out/
assessment-results-*.yaml
.idea
.vscode/settings.json
.vscode/settings.json
.vscode/launch.json
4 changes: 3 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/defenseunicorns/lula
go 1.22.1

require (
github.com/defenseunicorns/go-oscal v0.2.0
github.com/defenseunicorns/go-oscal v0.2.5
github.com/mitchellh/mapstructure v1.5.0
github.com/open-policy-agent/opa v0.62.1
github.com/pterm/pterm v0.12.79
Expand Down Expand Up @@ -94,6 +94,8 @@ require (
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/swaggest/jsonschema-go v0.3.69 // indirect
github.com/swaggest/refl v1.3.0 // indirect
github.com/tchap/go-patricia/v2 v2.3.1 // indirect
github.com/vladimirvivien/gexe v0.2.0 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
Expand Down
12 changes: 12 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ github.com/daviddengcn/go-colortext v1.0.0 h1:ANqDyC0ys6qCSvuEK7l3g5RaehL/Xck9EX
github.com/daviddengcn/go-colortext v1.0.0/go.mod h1:zDqEI5NVUop5QPpVJUxE9UO10hRnmkD5G4Pmri9+m4c=
github.com/defenseunicorns/go-oscal v0.2.0 h1:hyRMUoQT2RFk/VIxz19yZKngobjdIuI+si6+k7+OX/M=
github.com/defenseunicorns/go-oscal v0.2.0/go.mod h1:4JXNIFmWK1VBHpmXicW/g65MizUEHKUexy3Lb2lH2/I=
github.com/defenseunicorns/go-oscal v0.2.2 h1:JEXhNNRoVseDcH+Tlagf+MM8Y9nv5r7mPEHrAQIW7I0=
github.com/defenseunicorns/go-oscal v0.2.2/go.mod h1:4JXNIFmWK1VBHpmXicW/g65MizUEHKUexy3Lb2lH2/I=
github.com/defenseunicorns/go-oscal v0.2.3 h1:3ZaP956g4Eh+SlthopUBAMglFmWKSMPdbkIhHqeoz+s=
github.com/defenseunicorns/go-oscal v0.2.3/go.mod h1:4JXNIFmWK1VBHpmXicW/g65MizUEHKUexy3Lb2lH2/I=
github.com/defenseunicorns/go-oscal v0.2.5 h1:r8A2sXz/VTjVIf4Er/WyDSTyqUteek5AHeKEAT2XzTU=
github.com/defenseunicorns/go-oscal v0.2.5/go.mod h1:4JXNIFmWK1VBHpmXicW/g65MizUEHKUexy3Lb2lH2/I=
github.com/dgraph-io/badger/v3 v3.2103.5 h1:ylPa6qzbjYRQMU6jokoj4wzcaweHylt//CH0AKt0akg=
github.com/dgraph-io/badger/v3 v3.2103.5/go.mod h1:4MPiseMeDQ3FNCYwRbbcBOGJLf5jsE0PPFzRiKjtcdw=
github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8=
Expand Down Expand Up @@ -305,6 +311,12 @@ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/swaggest/jsonschema-go v0.3.67 h1:09tIfjZdeJW5ijK6ahkSpfVZv8Rj7tG3SLi/C/knj4c=
github.com/swaggest/jsonschema-go v0.3.67/go.mod h1:7N43/CwdaWgPUDfYV70K7Qm79tRqe/al7gLSt9YeGIE=
github.com/swaggest/jsonschema-go v0.3.69 h1:BNEajhoQjnEQzxZqPmjD1Pcs1pxnjx/X9L5KWllLHxo=
github.com/swaggest/jsonschema-go v0.3.69/go.mod h1:7N43/CwdaWgPUDfYV70K7Qm79tRqe/al7gLSt9YeGIE=
github.com/swaggest/refl v1.3.0 h1:PEUWIku+ZznYfsoyheF97ypSduvMApYyGkYF3nabS0I=
github.com/swaggest/refl v1.3.0/go.mod h1:3Ujvbmh1pfSbDYjC6JGG7nMgPvpG0ehQL4iNonnLNbg=
github.com/tchap/go-patricia/v2 v2.3.1 h1:6rQp39lgIYZ+MHmdEq4xzuk1t7OdC35z/xm0BGhTkes=
github.com/tchap/go-patricia/v2 v2.3.1/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k=
github.com/vladimirvivien/gexe v0.2.0 h1:nbdAQ6vbZ+ZNsolCgSVb9Fno60kzSuvtzVh6Ytqi/xY=
Expand Down
22 changes: 15 additions & 7 deletions src/cmd/tools/lint.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
package tools

import (
"fmt"

"github.com/defenseunicorns/go-oscal/src/cmd/validate"
"github.com/defenseunicorns/go-oscal/src/pkg/validation"
"github.com/defenseunicorns/lula/src/config"
"github.com/defenseunicorns/lula/src/pkg/message"
"github.com/spf13/cobra"
)

type flags struct {
InputFile string // -f --input-file
InputFile string // -f --input-file
ResultFile string // -r --result-file
}

var opts = &flags{}
Expand All @@ -33,17 +32,26 @@ func init() {
spinner := message.NewProgressSpinner("Linting %s", opts.InputFile)
defer spinner.Stop()

validator, err := validate.ValidateCommand(opts.InputFile)
validationResp, err := validation.ValidationCommand(opts.InputFile)

for _, warning := range validationResp.Warnings {
message.Warn(warning)
}

if opts.ResultFile != "" {
validation.WriteValidationResult(validationResp.Result, opts.ResultFile)
}

if err != nil {
fmt.Println(err)
message.Fatalf(err, "Failed to lint %s", opts.InputFile)
}
message.Infof("Successfully validated %s is valid OSCAL version %s %s\n", opts.InputFile, validator.GetSchemaVersion(), validator.GetModelType())
message.Infof("Successfully validated %s is valid OSCAL version %s %s\n", opts.InputFile, validationResp.Validator.GetSchemaVersion(), validationResp.Validator.GetModelType())
spinner.Success()
},
}

toolsCmd.AddCommand(lintCmd)

lintCmd.Flags().StringVarP(&opts.InputFile, "input-file", "f", "", "the path to a oscal json schema file")
lintCmd.Flags().StringVarP(&opts.ResultFile, "result-file", "r", "", "the path to write the validation result")
}
72 changes: 72 additions & 0 deletions src/cmd/tools/upgrade.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package tools

import (
"github.com/defenseunicorns/go-oscal/src/pkg/revision"
goOscalUtils "github.com/defenseunicorns/go-oscal/src/pkg/utils"
"github.com/defenseunicorns/go-oscal/src/pkg/validation"
"github.com/defenseunicorns/lula/src/config"
"github.com/defenseunicorns/lula/src/pkg/message"
"github.com/spf13/cobra"
)

var upgradeHelp = `
To Upgrade an existing OSCAL file:
lula tools upgrade -f <path to oscal> -v <version>
`

type upgradeOptions struct {
revision.RevisionOptions
}

var upgradeOpts upgradeOptions = upgradeOptions{}

func init() {
upgradeCmd := &cobra.Command{
Use: "upgrade",
Short: "Upgrade OSCAL document to a new version if possible.",
PersistentPreRun: func(cmd *cobra.Command, args []string) {
config.SkipLogFile = true
},
Long: "Validate an OSCAL document against the OSCAL schema version provided. If the document is valid, upgrade it to the provided OSCAL version. Otherwise, return or write as ValidationError. Yaml formatting handled by gopkg/yaml.v3 and while objects will maintain deep equality, visual representation may be different than the input file.",
Example: upgradeHelp,
Run: func(cmd *cobra.Command, args []string) {
spinner := message.NewProgressSpinner("Upgrading %s to version %s", upgradeOpts.InputFile, upgradeOpts.Version)
defer spinner.Stop()

// If no output file is specified, write to the input file
if upgradeOpts.OutputFile == "" {
upgradeOpts.OutputFile = upgradeOpts.InputFile
}

revisionResponse, err := revision.RevisionCommand(&upgradeOpts.RevisionOptions)

if upgradeOpts.ValidationResult != "" {
validation.WriteValidationResult(revisionResponse.Result, upgradeOpts.ValidationResult)
}

if len(revisionResponse.Warnings) > 0 {
for _, warning := range revisionResponse.Warnings {
message.Warn(warning)
}
}

err = goOscalUtils.WriteOutput(revisionResponse.RevisedBytes, upgradeOpts.OutputFile)
if err != nil {
message.Fatalf(err, "Failed to write upgraded %s with: %s", upgradeOpts.OutputFile, err)
}

if err != nil {
message.Fatalf(err, "Failed to upgrade %s to OSCAL version %s %s", upgradeOpts.InputFile, revisionResponse.Reviser.GetSchemaVersion(), revisionResponse.Reviser.GetModelType())
}
message.Infof("Successfully upgraded %s to OSCAL version %s %s\n", upgradeOpts.InputFile, revisionResponse.Reviser.GetSchemaVersion(), revisionResponse.Reviser.GetModelType())
spinner.Success()
},
}

toolsCmd.AddCommand(upgradeCmd)

upgradeCmd.Flags().StringVarP(&upgradeOpts.InputFile, "input-file", "f", "", "the path to a oscal json schema file")
upgradeCmd.Flags().StringVarP(&upgradeOpts.OutputFile, "output-file", "o", "", "the path to write the linted oscal json schema file (default is the input file)")
upgradeCmd.Flags().StringVarP(&upgradeOpts.Version, "version", "v", goOscalUtils.GetLatestSupportedVersion(), "the version of the oscal schema to validate against (default is the latest supported version)")
upgradeCmd.Flags().StringVarP(&upgradeOpts.ValidationResult, "validation-result", "r", "", "the path to write the validation result file")
}
30 changes: 26 additions & 4 deletions src/test/e2e/pod_validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import (
"testing"
"time"

validator "github.com/defenseunicorns/go-oscal/src/cmd/validate"
"github.com/defenseunicorns/go-oscal/src/pkg/revision"
"github.com/defenseunicorns/go-oscal/src/pkg/validation"
"github.com/defenseunicorns/lula/src/cmd/validate"
"github.com/defenseunicorns/lula/src/pkg/common/oscal"
"github.com/defenseunicorns/lula/src/pkg/message"
Expand All @@ -16,6 +17,8 @@ import (
"sigs.k8s.io/e2e-framework/klient/wait/conditions"
"sigs.k8s.io/e2e-framework/pkg/envconf"
"sigs.k8s.io/e2e-framework/pkg/features"

gooscalUtils "github.com/defenseunicorns/go-oscal/src/pkg/utils"
)

func TestPodLabelValidation(t *testing.T) {
Expand All @@ -38,7 +41,26 @@ func TestPodLabelValidation(t *testing.T) {
oscalPath := "./scenarios/pod-label/oscal-component.yaml"
message.NoProgress = true

findingMap, observations, err := validate.ValidateOnPath(oscalPath)
tempDir := t.TempDir()

// Upgrade the component definition to latest osscal version
revisionOptions := revision.RevisionOptions{
InputFile: oscalPath,
OutputFile: tempDir + "/oscal-component-upgraded.yaml",
Version: gooscalUtils.GetLatestSupportedVersion(),
}
revisionResponse, err := revision.RevisionCommand(&revisionOptions)
if err != nil {
t.Fatal("Failed to upgrade component definition with: ", err)
}
// Write the upgraded component definition to a temp file
err = gooscalUtils.WriteOutput(revisionResponse.RevisedBytes, revisionOptions.OutputFile)
if err != nil {
t.Fatal("Failed to write upgraded component definition with: ", err)
}
message.Infof("Successfully upgraded %s to %s with OSCAL version %s %s\n", oscalPath, revisionOptions.OutputFile, revisionResponse.Reviser.GetSchemaVersion(), revisionResponse.Reviser.GetModelType())

findingMap, observations, err := validate.ValidateOnPath(revisionOptions.OutputFile)
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -91,11 +113,11 @@ func TestPodLabelValidation(t *testing.T) {
t.Fatal("Failed to append results to existing report")
}

validator, err := validator.ValidateCommand("sar-test.yaml")
validatorResponse, err := validation.ValidationCommand("sar-test.yaml")
if err != nil {
t.Fatal("File failed linting")
}
message.Infof("Successfully validated %s is valid OSCAL version %s %s\n", "sar-test.yaml", validator.GetSchemaVersion(), validator.GetModelType())
message.Infof("Successfully validated %s is valid OSCAL version %s %s\n", "sar-test.yaml", validatorResponse.Validator.GetSchemaVersion(), validatorResponse.Validator.GetModelType())

return ctx
}).
Expand Down
Loading