Skip to content
This repository has been archived by the owner on Aug 20, 2021. It is now read-only.

Commit

Permalink
Add implementation code generation (#32)
Browse files Browse the repository at this point in the history
* Add implementation code generation

* Add attributes for assessment data in implementation struct
  • Loading branch information
minhaj10p authored and anweiss committed Jan 10, 2019
1 parent dc65f31 commit a0afab2
Show file tree
Hide file tree
Showing 22 changed files with 2,053 additions and 14 deletions.
9 changes: 9 additions & 0 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions cli/cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ func Execute() error {
Validate,
Sign,
Generate,
Implementation,
}

return app.Run(os.Args)
Expand Down
126 changes: 126 additions & 0 deletions cli/cmd/implementation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package cmd

import (
"bytes"
"encoding/csv"
"fmt"
"go/format"
"io"
"io/ioutil"
"log"
"os"

"github.com/Sirupsen/logrus"

"github.com/opencontrol/oscalkit/templates"

"github.com/opencontrol/oscalkit/impl"

"github.com/opencontrol/oscalkit/generator"
"github.com/urfave/cli"
)

var profile string
var excelSheet string

//Implementation generates implemntation
var Implementation = cli.Command{
Name: "implementation",
Usage: "generates go code for implementation against provided profile and excel sheet",
Flags: []cli.Flag{
cli.StringFlag{
Name: "profile, p",
Usage: "profile to intersect against",
Destination: &profile,
},
cli.StringFlag{
Name: "excel, e",
Usage: "excel sheet to get component configs",
Destination: &excelSheet,
},
cli.StringFlag{
Name: "output, o",
Usage: "output filename",
Destination: &outputFileName,
Value: "implementation.go",
},
},
Before: func(c *cli.Context) error {
if profile == "" {
return cli.NewExitError("oscalkit generate is missing the --profile flag", 1)
}
if excelSheet == "" {
return cli.NewExitError("oscalkit implementation is missing --excel flag", 1)
}
return nil
},
Action: func(c *cli.Context) error {

profileF, err := generator.GetFilePath(profile)
if err != nil {
return cli.NewExitError(err.Error(), 1)
}
f, err := os.Open(profileF)
if err != nil {
return cli.NewExitError(fmt.Sprintf("cannot open profile %v", err), 1)
}
defer f.Close()
profile, err := generator.ReadProfile(f)
if err != nil {
return err
}
excelF, err := generator.GetFilePath(excelSheet)
if err != nil {
return err
}
b, err := ioutil.ReadFile(excelF)
if err != nil {
return err
}

outputFile, err := os.Create(outputFileName)
if err != nil {
return fmt.Errorf("cannot create file for implementation: err: %v", err)
}
defer outputFile.Close()

var records [][]string
reader := bytes.NewReader(b)
r := csv.NewReader(reader)
for {
record, err := r.Read()
if err == io.EOF {
break
}
if err != nil {
log.Fatal(err)
}
records = append(records, record)
}

catalog := impl.NISTCatalog{ID: "NIST_SP-800-53"}
implementation := impl.GenerateImplementation(records, profile, &catalog)
t, err := templates.GetImplementationTemplate()
if err != nil {
return fmt.Errorf("cannot get implementation template err %v", err)
}
err = t.Execute(outputFile, implementation)
if err != nil {
return err
}
b, err = ioutil.ReadFile(outputFileName)
if err != nil {
return cli.NewExitError(fmt.Sprintf("cannot open %s file", outputFileName), 1)
}
b, err = format.Source(b)
if err != nil {
logrus.Warn(fmt.Sprintf("cannot format %s file", outputFileName))
return cli.NewExitError(err, 1)
}
err = ioutil.WriteFile(outputFileName, b, 0)
if err != nil {
return cli.NewExitError(fmt.Sprintf("cannot write formmated "), 1)
}
return nil
},
}
2 changes: 1 addition & 1 deletion generator/generator_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ func TestSubControlsMapping(t *testing.T) {
func TestGetCatalogInvalidFilePath(t *testing.T) {

url := "http://[::1]a"
_, err := GetCatalogFilePath(url)
_, err := GetFilePath(url)
if err == nil {
t.Error("should fail")
}
Expand Down
2 changes: 1 addition & 1 deletion generator/mapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ func CreateCatalogsFromProfile(profileArg *profile.Profile) ([]*catalog.Catalog,
for _, profileImport := range profileArg.Imports {
go func(profileImport profile.Import) {
//ForEach Import's Href, Fetch the Catalog JSON file
catalogReference, err := GetCatalogFilePath(profileImport.Href.String())
catalogReference, err := GetFilePath(profileImport.Href.String())
if err != nil {
logrus.Errorf("invalid file path: %v", err)
catalogChan <- nil
Expand Down
11 changes: 7 additions & 4 deletions generator/reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,18 +40,21 @@ func ReadProfile(r io.Reader) (*profile.Profile, error) {
if err != nil {
return nil, fmt.Errorf("cannot read oscal profile from file. err: %v,", err)
}
if o.Profile == nil {
return nil, fmt.Errorf("unable to marshall profile")
}
return o.Profile, nil
}

//GetCatalogFilePath GetCatalogFilePath
func GetCatalogFilePath(catalogURL string) (string, error) {
uri, err := url.Parse(catalogURL)
//GetFilePath GetFilePath
func GetFilePath(URL string) (string, error) {
uri, err := url.Parse(URL)
if err != nil {
return "", fmt.Errorf("invalid URL pattern %v", err)
}

if !isHTTPResource(uri) {
return GetAbsolutePath(catalogURL)
return GetAbsolutePath(URL)
}
body, err := fetchFromHTTPResource(uri)
if err != nil {
Expand Down
90 changes: 90 additions & 0 deletions impl/impl_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package impl

import (
"fmt"
"net/url"
"testing"

"github.com/opencontrol/oscalkit/types/oscal/catalog"
"github.com/opencontrol/oscalkit/types/oscal/profile"
)

const (
temporaryProfileID = "oscaltestprofile"
catalogRef = "https://raw.githubusercontent.com/usnistgov/OSCAL/master/content/nist.gov/SP800-53/rev4/NIST_SP-800-53_rev4_catalog.xml"
)

func TestGenerateImplementation(t *testing.T) {

components := []string{"CompA", "CompB", "CompC"}
controls := []string{"ac-2", "ac-2.2", "ac-4"}
ComponentDetails := [][]string{
[]string{controls[0], fmt.Sprintf("%s|%s", components[0], components[1]), "2-Narrative"},
[]string{controls[1], fmt.Sprintf("%s|%s", components[0], components[1]), "2.2-Narrative"},
[]string{controls[2], "CompC", "4-Narrative"},
}
csvs := make([][]string, TotalControlsInExcel)
for i := range csvs {
csvs[i] = make([]string, 20)
}
for i, x := range ComponentDetails {
csvs[i+RowIndex][ControlIndex] = x[0]
csvs[i+RowIndex][ComponentNameIndex] = x[1]
csvs[i+RowIndex][NarrativeIndex] = x[2]
}

p := profile.Profile{
ID: temporaryProfileID,
Imports: []profile.Import{
profile.Import{
Href: &catalog.Href{
URL: func() *url.URL {
uri, _ := url.Parse(catalogRef)
return uri
}(),
},
Include: &profile.Include{
IdSelectors: []profile.Call{
profile.Call{
ControlId: "ac-2",
},
profile.Call{
ControlId: "ac-4",
},
profile.Call{
SubcontrolId: "ac-4.2",
},
},
},
},
},
Modify: &profile.Modify{
ParamSettings: []profile.SetParam{
profile.SetParam{
Id: "ac-2_prm",
Constraints: []catalog.Constraint{catalog.Constraint{Value: "some constraint"}},
},
profile.SetParam{
Id: "ac-2_prm_obj",
Constraints: []catalog.Constraint{catalog.Constraint{Value: "some constraint"}},
},
profile.SetParam{
Id: "",
Constraints: []catalog.Constraint{},
},
profile.SetParam{
Id: "ac-4_prm",
Constraints: []catalog.Constraint{},
},
},
},
}
i := GenerateImplementation(csvs, &p, &NISTCatalog{"NISTSP80053"})
if len(i.ComponentDefinitions[0].ComponentConfigurations) != len(components) {
t.Error("mismatch number of components")
}
if len(i.ComponentDefinitions[0].ControlImplementations[0].ControlIds) != len(controls) {
t.Error("mismatch number of controls")
}

}
Loading

0 comments on commit a0afab2

Please sign in to comment.