generated from ossf/project-template
-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Boostrap oscal generator and subcommand (#194)
* Boostrap oscal generator and subcommand This command initializes the first oscal output and generator for the baseline data. Signed-off-by: Adolfo García Veytia (Puerco) <[email protected]> * Use props instead of parts for maturity level Signed-off-by: Adolfo García Veytia (Puerco) <[email protected]> * Update OSCAL generator to new schema Signed-off-by: Adolfo García Veytia (Puerco) <[email protected]> * Fix validation of resulting OSCAL document Signed-off-by: Adolfo García Veytia (Puerco) <[email protected]> * Update cmd/pkg/baseline/generator_oscal.go Co-authored-by: Al <[email protected]> Signed-off-by: CRob <[email protected]> --------- Signed-off-by: Adolfo García Veytia (Puerco) <[email protected]> Signed-off-by: CRob <[email protected]> Signed-off-by: Ben Cotton <[email protected]> Co-authored-by: CRob <[email protected]> Co-authored-by: Al <[email protected]> Co-authored-by: Ben Cotton <[email protected]>
- Loading branch information
1 parent
6b0e12f
commit b75b30f
Showing
5 changed files
with
218 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
// SPDX-FileCopyrightText: Copyright 2025 The OSPS Authors | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package cmd | ||
|
||
import ( | ||
"errors" | ||
"fmt" | ||
"os" | ||
|
||
"github.com/ossf/security-baseline/pkg/baseline" | ||
"github.com/spf13/cobra" | ||
) | ||
|
||
var appname = "baseline" | ||
|
||
type oscalOptions struct { | ||
outPath string | ||
baselinePath string | ||
} | ||
|
||
// Validate the options in context with arguments | ||
func (o *oscalOptions) Validate() error { | ||
errs := []error{} | ||
|
||
if o.baselinePath == "" { | ||
errs = append(errs, errors.New("baseline path not specified")) | ||
} | ||
return errors.Join(errs...) | ||
} | ||
|
||
func (o *oscalOptions) AddFlags(cmd *cobra.Command) { | ||
cmd.PersistentFlags().StringVarP( | ||
&o.baselinePath, "baseline", "b", "", "path to directory containing the baseline YAML data", | ||
) | ||
|
||
cmd.PersistentFlags().StringVarP( | ||
&o.outPath, "out", "o", "", "path to output file (defaults to STDOUT)", | ||
) | ||
} | ||
|
||
func addOscal(parentCmd *cobra.Command) { | ||
opts := oscalOptions{} | ||
packCmd := &cobra.Command{ | ||
Short: "writes the baseline definition to an oscal json catalog", | ||
Long: fmt.Sprintf(` | ||
%s oscal: Write the OSPS Baseline to oscal definitions. | ||
This subcommand exports the OSPS Baseline data to OSCAL (Open Security Controls | ||
Assessment Language). This lets automated tools understand the criteria set as | ||
OSCAL controls. | ||
`, appname), | ||
Use: "oscal -o osps.oscal.json", | ||
SilenceUsage: false, | ||
SilenceErrors: true, | ||
PreRunE: func(_ *cobra.Command, args []string) error { | ||
if opts.outPath != "" && len(args) > 1 && opts.outPath != args[1] { | ||
return fmt.Errorf("out path specified twice") | ||
} | ||
|
||
if len(args) > 1 { | ||
opts.outPath = args[1] | ||
} | ||
return nil | ||
}, | ||
RunE: func(cmd *cobra.Command, args []string) error { | ||
if err := opts.Validate(); err != nil { | ||
return err | ||
} | ||
|
||
cmd.SilenceUsage = true | ||
|
||
loader := baseline.NewLoader() | ||
loader.DataPath = opts.baselinePath | ||
|
||
bline, err := loader.Load() | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// TODO: Open the output file | ||
|
||
gen := baseline.NewGenerator() | ||
if err := gen.ExportOSCAL(bline, os.Stdout); err != nil { | ||
return err | ||
} | ||
|
||
return nil | ||
}, | ||
} | ||
opts.AddFlags(packCmd) | ||
parentCmd.AddCommand(packCmd) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
// SPDX-FileCopyrightText: Copyright 2025 The OSPS Authors | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
package baseline | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"io" | ||
"strings" | ||
"time" | ||
|
||
oscal "github.com/defenseunicorns/go-oscal/src/types/oscal-1-1-3" | ||
"github.com/ossf/security-baseline/pkg/types" | ||
) | ||
|
||
const ( | ||
VersionOSPS = "devel" | ||
controlHREF = "https://baseline.openssf.org/versions/%s#%s" | ||
catalogUUID = "8c222a23-fc7e-4ad8-b6dd-289014f07a9f" | ||
|
||
// OpenSSFNS is the OSCAL namespace URI to define the baseline names. | ||
OpenSSFNS = "http://baseline.openssf.org/ns/oscal" | ||
) | ||
|
||
func (g *Generator) ExportOSCAL(b *types.Baseline, w io.Writer) error { | ||
n := time.Now() | ||
catalog := oscal.Catalog{ | ||
UUID: catalogUUID, | ||
Groups: nil, | ||
Metadata: oscal.Metadata{ | ||
LastModified: n, | ||
Links: &[]oscal.Link{ | ||
{ | ||
Href: fmt.Sprintf(controlHREF, VersionOSPS, ""), | ||
Rel: "canonical", | ||
}, | ||
}, | ||
OscalVersion: "1.1.3", | ||
Published: &n, | ||
Title: "Open Source Project Security Baseline", | ||
Version: VersionOSPS, | ||
}, | ||
} | ||
|
||
catalogGroups := []oscal.Group{} | ||
|
||
for code, cat := range b.Categories { | ||
group := oscal.Group{ | ||
Class: "OSPS", | ||
Controls: nil, | ||
ID: code, | ||
Title: cat.Description, | ||
} | ||
|
||
controls := []oscal.Control{} | ||
|
||
for _, control := range cat.Controls { | ||
|
||
parts := []oscal.Part{} | ||
for _, ar := range control.Requirements { | ||
parts = append(parts, oscal.Part{ | ||
Class: control.ID, | ||
ID: ar.ID, | ||
Name: ar.ID, | ||
Ns: "", | ||
Parts: &[]oscal.Part{ | ||
{ | ||
ID: ar.ID + ".R", | ||
Name: "recommendation", | ||
Ns: OpenSSFNS, | ||
Prose: ar.Recommendation, | ||
Links: &[]oscal.Link{ | ||
{ | ||
Href: fmt.Sprintf(controlHREF, VersionOSPS, ar.ID), | ||
Rel: "canonical", | ||
}, | ||
}, | ||
}, | ||
}, | ||
Prose: ar.Text, | ||
Title: "", | ||
}) | ||
} | ||
|
||
newCtl := oscal.Control{ | ||
Class: code, | ||
ID: control.ID, | ||
Links: &[]oscal.Link{ | ||
{ | ||
Href: fmt.Sprintf(controlHREF, VersionOSPS, strings.ToLower(control.ID)), | ||
Rel: "canonical", | ||
}, | ||
}, | ||
Parts: &parts, | ||
Title: strings.TrimSpace(control.Title), | ||
} | ||
controls = append(controls, newCtl) | ||
} | ||
|
||
group.Controls = &controls | ||
catalogGroups = append(catalogGroups, group) | ||
} | ||
catalog.Groups = &catalogGroups | ||
|
||
// Wrap the catalog to render the required "catalog" wrapper | ||
// in the JSON file: | ||
var wrapper = struct { | ||
Catalog oscal.Catalog `json:"catalog"` | ||
}{ | ||
Catalog: catalog, | ||
} | ||
|
||
enc := json.NewEncoder(w) | ||
enc.SetIndent("", " ") | ||
if err := enc.Encode(wrapper); err != nil { | ||
return fmt.Errorf("encoding oscal json data: %w", err) | ||
} | ||
return nil | ||
} |