diff --git a/generator/generator_test.go b/generator/generator_test.go index 8c08b734..e563c41e 100644 --- a/generator/generator_test.go +++ b/generator/generator_test.go @@ -4,6 +4,7 @@ import ( "bytes" "fmt" "net/url" + "path/filepath" "testing" "github.com/docker/oscalkit/impl" @@ -438,6 +439,10 @@ func TestProcessSetParam(t *testing.T) { }, }, }, + profile.SetParam{ + Id: parameterID, + Constraints: []catalog.Constraint{}, + }, } controls := []catalog.Control{ catalog.Control{ @@ -520,3 +525,226 @@ func failTest(err error, t *testing.T) { t.Error(err) } } + +func TestMakeURL(t *testing.T) { + httpURI, _ := url.Parse("http://localhost:3000/v1/tests/profiles") + relpath, _ := url.Parse("../users") + + expectedOutput, _ := url.Parse("http://localhost:3000/v1/users") + + out, _ := makeURL(httpURI, relpath) + if expectedOutput.String() != out.String() { + t.Fail() + } +} + +func TestSetBasePathWithRelPath(t *testing.T) { + relativePath := "./something.xml" + absoulePath, _ := filepath.Abs(relativePath) + p := &profile.Profile{ + Imports: []profile.Import{ + profile.Import{ + Href: &catalog.Href{ + URL: func() *url.URL { + x, _ := url.Parse(relativePath) + return x + }(), + }, + }, + }, + } + p, err := SetBasePath(p, "") + if err != nil { + t.Error(err) + } + if p.Imports[0].Href.String() != absoulePath { + t.Fail() + } +} + +func TestSetBasePathWithHttpPath(t *testing.T) { + httpPath := "http://localhost:3000/v1/test/profiles/p1.xml" + relativePath := "./p2.xml" + outputPath := "http://localhost:3000/v1/test/profiles/p2.xml" + p := &profile.Profile{ + Imports: []profile.Import{ + profile.Import{ + Href: &catalog.Href{ + URL: func() *url.URL { + x, _ := url.Parse(relativePath) + return x + }(), + }, + }, + profile.Import{ + Href: &catalog.Href{ + URL: func() *url.URL { + x, _ := url.Parse(httpPath) + return x + }(), + }, + }, + }, + } + p, err := SetBasePath(p, httpPath) + if err != nil { + t.Error(err) + } + if p.Imports[0].Href.String() != outputPath { + t.Fail() + } + if p.Imports[1].Href.String() != httpPath { + t.Fail() + } +} + +func TestGetAltersWithAltersPresent(t *testing.T) { + + alterTitle := catalog.Title("test-title") + ctrlID := "ctrl-1" + p := &profile.Profile{ + Imports: []profile.Import{ + profile.Import{ + Href: &catalog.Href{ + URL: func() *url.URL { + u, _ := url.Parse("p1.xml") + return u + }(), + }, + Include: &profile.Include{ + IdSelectors: []profile.Call{ + profile.Call{ + ControlId: ctrlID, + }, + }, + }, + }, + }, + Modify: &profile.Modify{ + Alterations: []profile.Alter{ + profile.Alter{ + ControlId: ctrlID, + Additions: []profile.Add{ + profile.Add{ + Title: alterTitle, + }, + }, + }, + }, + }, + } + + alters, err := GetAlters(p) + if err != nil { + t.Error(err) + } + if len(alters) == 0 { + t.Error("no alters found") + } + if alters[0].ControlId != ctrlID { + t.Fail() + } +} + +func TestAddSubControlToControls(t *testing.T) { + controlDetails := catalog.Control{Id: "ac-2"} + subCtrlToAdd := catalog.Subcontrol{Id: "ac-2.1"} + g := catalog.Group{ + Controls: []catalog.Control{catalog.Control{Id: "ac-1"}}, + } + AddSubControlToControls(&g, controlDetails, subCtrlToAdd, &impl.NISTCatalog{}) + ctrlFound := false + for _, x := range g.Controls { + if x.Id == "ac-2" { + ctrlFound = true + subCtrlFound := false + for _, y := range x.Subcontrols { + if y.Id == "ac-2.1" { + subCtrlFound = true + } + } + if !subCtrlFound { + t.Fail() + } + break + } + } + if !ctrlFound { + t.Fail() + } +} + +func TestAddExistingControlToGroup(t *testing.T) { + ctrlToAdd := catalog.Control{Id: "ac-1"} + g := catalog.Group{ + Controls: []catalog.Control{ + catalog.Control{ + Id: "ac-1", + }, + }, + } + AddControlToGroup(&g, ctrlToAdd, &impl.NISTCatalog{}) + if len(g.Controls) > 1 { + t.Fail() + } +} + +func TestInvalidGetSubControl(t *testing.T) { + c := profile.Call{SubcontrolId: "ac-2.1"} + controls := []catalog.Control{catalog.Control{Id: "ac-1"}} + _, err := getSubControl(c, controls, &impl.NISTCatalog{}) + if err == nil { + t.Fail() + } +} + +func TestGetMappedCatalogControlsFromImport(t *testing.T) { + importedCatalog := catalog.Catalog{ + Groups: []catalog.Group{ + catalog.Group{ + Controls: []catalog.Control{ + catalog.Control{ + Id: "ac-2", + }, + }, + }, + }, + } + profileImport := profile.Import{ + Include: &profile.Include{ + IdSelectors: []profile.Call{ + profile.Call{ + SubcontrolId: "ac-2.1", + }, + }, + }, + } + _, err := GetMappedCatalogControlsFromImport(&importedCatalog, profileImport, &impl.NISTCatalog{}) + if err == nil { + t.Fail() + } +} + +func TestValidateHref(t *testing.T) { + err := ValidateHref(&catalog.Href{URL: &url.URL{RawPath: ":'//:://"}}) + if err != nil { + t.Fail() + } +} +func TestNilHref(t *testing.T) { + if err := ValidateHref(nil); err == nil { + t.Fail() + } +} + +func TestInvalidCreateCatalogsFromProfile(t *testing.T) { + p := profile.Profile{ + Imports: []profile.Import{ + profile.Import{Href: &catalog.Href{URL: &url.URL{RawPath: ":'//:://"}}}, + }, + } + _, err := CreateCatalogsFromProfile(&p) + if err == nil { + t.Fail() + } +} diff --git a/generator/mapper.go b/generator/mapper.go index ce8ee9bc..16e3580e 100644 --- a/generator/mapper.go +++ b/generator/mapper.go @@ -96,66 +96,74 @@ func getSubControl(call profile.Call, ctrls []catalog.Control, helper impl.Catal return catalog.Subcontrol{}, fmt.Errorf("could not find subcontrol %s in catalog", call.SubcontrolId) } +func doesCallContainSubcontrol(c profile.Call) bool { + return c.ControlId == "" +} + +// AddControlToGroup adds control to a group +func AddControlToGroup(g *catalog.Group, ctrl catalog.Control, catalogHelper impl.Catalog) { + ctrlExists := false + for _, x := range g.Controls { + if x.Id == ctrl.Id { + ctrlExists = true + continue + } + } + if !ctrlExists { + g.Controls = append(g.Controls, + catalog.Control{ + Id: ctrl.Id, + Class: ctrl.Class, + Title: ctrl.Title, + Subcontrols: []catalog.Subcontrol{}, + Params: ctrl.Params, + Parts: ctrl.Parts, + }, + ) + } +} + +// AddSubControlToControls adds subcontrols to a group. If parent ctrl of subctrl doesn't exists, it adds its parent ctrl as well +func AddSubControlToControls(g *catalog.Group, ctrl catalog.Control, sc catalog.Subcontrol, catalogHelper impl.Catalog) { + ctrlExistsInGroup := false + for i, mappedCtrl := range g.Controls { + if mappedCtrl.Id == strings.ToLower(catalogHelper.GetControl(sc.Id)) { + ctrlExistsInGroup = true + g.Controls[i].Subcontrols = append(g.Controls[i].Subcontrols, sc) + } + } + if !ctrlExistsInGroup { + g.Controls = append(g.Controls, + catalog.Control{ + Id: ctrl.Id, + Class: ctrl.Class, + Title: ctrl.Title, + Params: ctrl.Params, + Parts: ctrl.Parts, + Subcontrols: []catalog.Subcontrol{sc}, + }) + } +} + // GetMappedCatalogControlsFromImport gets mapped controls in catalog per profile import func GetMappedCatalogControlsFromImport(importedCatalog *catalog.Catalog, profileImport profile.Import, catalogHelper impl.Catalog) (catalog.Catalog, error) { - newCatalog := catalog.Catalog{ - Title: importedCatalog.Title, - Groups: []catalog.Group{}, - } + newCatalog := catalog.CreateCatalog(importedCatalog.Title, []catalog.Group{}) for _, group := range importedCatalog.Groups { - newGroup := catalog.Group{ - Title: group.Title, - Controls: []catalog.Control{}, - } + newGroup := catalog.CreateGroup(group.Title, []catalog.Control{}) for _, ctrl := range group.Controls { for _, call := range profileImport.Include.IdSelectors { - if call.ControlId == "" { + if doesCallContainSubcontrol(call) { if strings.ToLower(ctrl.Id) == strings.ToLower(catalogHelper.GetControl(call.SubcontrolId)) { - ctrlExistsInGroup := false sc, err := getSubControl(call, group.Controls, &impl.NISTCatalog{}) if err != nil { return catalog.Catalog{}, err } - for i, mappedCtrl := range newGroup.Controls { - if mappedCtrl.Id == strings.ToLower(catalogHelper.GetControl(call.SubcontrolId)) { - ctrlExistsInGroup = true - newGroup.Controls[i].Subcontrols = append(newGroup.Controls[i].Subcontrols, sc) - } - } - if !ctrlExistsInGroup { - newGroup.Controls = append(newGroup.Controls, - catalog.Control{ - Id: ctrl.Id, - Class: ctrl.Class, - Title: ctrl.Title, - Params: ctrl.Params, - Parts: ctrl.Parts, - Subcontrols: []catalog.Subcontrol{sc}, - }) - } + AddSubControlToControls(&newGroup, ctrl, sc, catalogHelper) } } if strings.ToLower(call.ControlId) == strings.ToLower(ctrl.Id) { - ctrlExists := false - for _, x := range newGroup.Controls { - if x.Id == ctrl.Id { - ctrlExists = true - continue - } - } - if !ctrlExists { - newGroup.Controls = append(newGroup.Controls, - catalog.Control{ - Id: ctrl.Id, - Class: ctrl.Class, - Title: ctrl.Title, - Subcontrols: []catalog.Subcontrol{}, - Params: ctrl.Params, - Parts: ctrl.Parts, - }, - ) - } + AddControlToGroup(&newGroup, ctrl, catalogHelper) } } } diff --git a/generator/profile.go b/generator/profile.go index 0c72a488..13076f48 100644 --- a/generator/profile.go +++ b/generator/profile.go @@ -102,6 +102,11 @@ func GetAlters(p *profile.Profile) ([]profile.Alter, error) { var alterations []profile.Alter for _, i := range p.Imports { + if i.Include == nil { + i.Include = &profile.Include{ + IdSelectors: []profile.Call{}, + } + } for _, call := range i.Include.IdSelectors { found := false if p.Modify == nil { diff --git a/types/oscal/catalog/helpers.go b/types/oscal/catalog/helpers.go index 1c5905a8..c372d2e9 100644 --- a/types/oscal/catalog/helpers.go +++ b/types/oscal/catalog/helpers.go @@ -31,3 +31,16 @@ func NewControl(id, title string, opts *ControlOpts) Control { } return ctrl } + +func CreateCatalog(title Title, groups []Group) Catalog { + return Catalog{ + Title: title, + Groups: groups, + } +} +func CreateGroup(title Title, ctrls []Control) Group { + return Group{ + Title: title, + Controls: ctrls, + } +}