From ec6032ac1a95c671126bb6a9736ca1f2a745ec89 Mon Sep 17 00:00:00 2001 From: minhaj10p <32382853+minhaj10p@users.noreply.github.com> Date: Thu, 7 Feb 2019 21:26:17 +0500 Subject: [PATCH] Remove profile dependency from implementation code generation (#80) --- cli/cmd/generate/implementation.go | 23 +-- impl/impl_test.go | 303 ++++++++++++++++++++++++----- impl/implementation.go | 202 +++++++------------ impl/implementsprofile.go | 157 +++++++++++++++ templates/implementation.go | 14 +- 5 files changed, 491 insertions(+), 208 deletions(-) create mode 100644 impl/implementsprofile.go diff --git a/cli/cmd/generate/implementation.go b/cli/cmd/generate/implementation.go index 4b84f1ad..00b60259 100644 --- a/cli/cmd/generate/implementation.go +++ b/cli/cmd/generate/implementation.go @@ -26,11 +26,6 @@ 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", @@ -50,9 +45,6 @@ var Implementation = cli.Command{ }, }, 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) } @@ -64,19 +56,6 @@ var Implementation = cli.Command{ return cli.NewExitError(err, 1) } - 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 @@ -107,7 +86,7 @@ var Implementation = cli.Command{ } catalog := impl.NISTCatalog{ID: "NIST_SP-800-53"} - implementationData := impl.GenerateImplementation(records, profile, &catalog) + implementationData := impl.GenerateImplementation(records, &catalog) t, err := templates.GetImplementationTemplate() if err != nil { return fmt.Errorf("cannot get implementation template err %v", err) diff --git a/impl/impl_test.go b/impl/impl_test.go index a38d8ffd..15189a75 100644 --- a/impl/impl_test.go +++ b/impl/impl_test.go @@ -2,11 +2,9 @@ package impl import ( "fmt" - "net/url" "testing" - "github.com/docker/oscalkit/types/oscal/catalog" - "github.com/docker/oscalkit/types/oscal/profile" + "github.com/docker/oscalkit/types/oscal/implementation" ) const ( @@ -71,53 +69,7 @@ func TestGenerateImplementation(t *testing.T) { } } - 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"}) + i := GenerateImplementation(csvs, &NISTCatalog{"NISTSP80053"}) if len(i.ComponentDefinitions) != len(comps) { t.Error("mismatch number of component definitions") @@ -130,3 +82,254 @@ func TestGenerateImplementation(t *testing.T) { } } + +func TestFindOrCreateImplementsProfile(t *testing.T) { + cd := implementation.ComponentDefinition{} + profileID := "123" + implementsProfile := findOrCreateImplementsProfile(&cd, profileID) + if implementsProfile == nil { + t.Error("nothing created") + return + } + for _, x := range cd.ImplementsProfiles { + if x.ProfileID == profileID { + return + } + } + t.Error("profile did not get appended") +} + +func TestFindOrCreateControlConfig(t *testing.T) { + cd := implementation.ComponentDefinition{ + ImplementsProfiles: []*implementation.ImplementsProfile{ + &implementation.ImplementsProfile{ + ProfileID: "123", + }, + }, + } + configIDRef := "some-guid" + controlConfig := findOrCreateControlConfig(cd.ImplementsProfiles[0], configIDRef) + if controlConfig == nil { + t.Error("empty ctrl conf") + } + for _, x := range cd.ImplementsProfiles[0].ControlConfigurations { + if x.ConfigurationIDRef == configIDRef { + return + } + } + t.Error("coudnt find conf id") +} + +func TestMapImplementsProfile(t *testing.T) { + + parameterID := "ac-10_prm_3" + profileID := "uuid-fedramp-high-20180806-195540" + configurationIDRef := "random-guid-id" + Value := "<=2" + checkAndValue := fmt.Sprintf("MakeItRight(%s)", Value) + cd := implementation.ComponentDefinition{ + ComponentConfigurations: []*implementation.ComponentConfiguration{ + &implementation.ComponentConfiguration{ + ID: configurationIDRef, + Name: "MakeItRight", + }, + }, + } + mapImplementsProfile(&cd, parameterID, profileID, checkAndValue) + if len(cd.ImplementsProfiles) < 1 { + t.Error("implements profile array should have one element") + } + if cd.ImplementsProfiles[0].ProfileID != profileID { + t.Errorf("profile id should be %s", profileID) + } + if len(cd.ImplementsProfiles[0].ControlConfigurations) < 1 { + t.Error("control configurations array should have one element") + } + if cd.ImplementsProfiles[0].ControlConfigurations[0].ConfigurationIDRef != configurationIDRef { + t.Errorf("config ref id should be %s", configurationIDRef) + } + if len(cd.ImplementsProfiles[0].ControlConfigurations[0].Parameters) < 1 { + t.Error("parameters array should have one element") + } + if cd.ImplementsProfiles[0].ControlConfigurations[0].Parameters[0].ParameterID != parameterID { + t.Errorf("parameter id should be %s", parameterID) + } + if len(cd.ImplementsProfiles[0].ControlConfigurations[0].Parameters[0].PossibleValues) < 1 { + t.Error("possible value array should have one element") + } + if cd.ImplementsProfiles[0].ControlConfigurations[0].Parameters[0].PossibleValues[0] != Value { + t.Errorf("possible value should be %s", Value) + } +} + +func TestMapImplementsProfileWithMultiplePossibleValues(t *testing.T) { + + parameterID := "ac-10_prm_3" + profileID := "uuid-fedramp-high-20180806-195540" + configurationIDRef := "random-guid-id" + Value := "<=2" + Value2 := "<=3" + checkAndValue := fmt.Sprintf("MakeItRight(%s)", Value) + checkAndValue2 := fmt.Sprintf("MakeItRight(%s)", Value2) + + cd := implementation.ComponentDefinition{ + ComponentConfigurations: []*implementation.ComponentConfiguration{ + &implementation.ComponentConfiguration{ + ID: configurationIDRef, + Name: "MakeItRight", + }, + }, + } + mapImplementsProfile(&cd, parameterID, profileID, checkAndValue) + mapImplementsProfile(&cd, parameterID, profileID, checkAndValue2) + if len(cd.ImplementsProfiles) < 1 { + t.Error("implements profile array should have one element") + } + if cd.ImplementsProfiles[0].ProfileID != profileID { + t.Errorf("profile id should be %s", profileID) + } + if len(cd.ImplementsProfiles[0].ControlConfigurations) < 1 { + t.Error("control configurations array should have one element") + } + if cd.ImplementsProfiles[0].ControlConfigurations[0].ConfigurationIDRef != configurationIDRef { + t.Errorf("config ref id should be %s", configurationIDRef) + } + if len(cd.ImplementsProfiles[0].ControlConfigurations[0].Parameters) < 1 { + t.Error("parameters array should have one element") + } + if cd.ImplementsProfiles[0].ControlConfigurations[0].Parameters[0].ParameterID != parameterID { + t.Errorf("parameter id should be %s", parameterID) + } + if len(cd.ImplementsProfiles[0].ControlConfigurations[0].Parameters[0].PossibleValues) < 2 { + t.Error("possible value array should have one element") + } + if cd.ImplementsProfiles[0].ControlConfigurations[0].Parameters[0].PossibleValues[0] != Value { + t.Errorf("possible value should be %s", Value) + } + if cd.ImplementsProfiles[0].ControlConfigurations[0].Parameters[0].PossibleValues[1] != Value2 { + t.Errorf("possible value should be %s", Value) + } +} + +func TestMapImplementsProfileWithMultipleProfiles(t *testing.T) { + + parameterID := "ac-10_prm_3" + profileID := "uuid-fedramp-high-20180806-195540" + profileID2 := "uuid-fedramp-moderate-20180806-195540" + configurationIDRef := "random-guid-id" + Value := "<=2" + checkAndValue := fmt.Sprintf("MakeItRight(%s)", Value) + + cd := implementation.ComponentDefinition{ + ComponentConfigurations: []*implementation.ComponentConfiguration{ + &implementation.ComponentConfiguration{ + ID: configurationIDRef, + Name: "MakeItRight", + }, + }, + } + mapImplementsProfile(&cd, parameterID, profileID, checkAndValue) + mapImplementsProfile(&cd, parameterID, profileID2, checkAndValue) + if len(cd.ImplementsProfiles) < 2 { + t.Error("implements profile array should have one element") + } + if cd.ImplementsProfiles[0].ProfileID != profileID { + t.Errorf("profile id should be %s", profileID) + } + if cd.ImplementsProfiles[1].ProfileID != profileID2 { + t.Errorf("profile id should be %s", profileID2) + } + if len(cd.ImplementsProfiles[0].ControlConfigurations) < 1 { + t.Error("control configurations array should have one element") + } + if cd.ImplementsProfiles[0].ControlConfigurations[0].ConfigurationIDRef != configurationIDRef { + t.Errorf("config ref id should be %s", configurationIDRef) + } + if len(cd.ImplementsProfiles[0].ControlConfigurations[0].Parameters) < 1 { + t.Error("parameters array should have one element") + } + if cd.ImplementsProfiles[0].ControlConfigurations[0].Parameters[0].ParameterID != parameterID { + t.Errorf("parameter id should be %s", parameterID) + } + if len(cd.ImplementsProfiles[0].ControlConfigurations[0].Parameters[0].PossibleValues) < 1 { + t.Error("possible value array should have one element") + } + if cd.ImplementsProfiles[0].ControlConfigurations[0].Parameters[0].PossibleValues[0] != Value { + t.Errorf("possible value should be %s", Value) + } +} + +func TestMapImplementsProfileWithMultipleParameters(t *testing.T) { + + parameterID := "ac-10_prm_3" + parameterID2 := "ac-10_prm_2" + profileID := "uuid-fedramp-high-20180806-195540" + configurationIDRef := "random-guid-id" + Value := "<=2" + checkAndValue := fmt.Sprintf("MakeItRight(%s)", Value) + + cd := implementation.ComponentDefinition{ + ComponentConfigurations: []*implementation.ComponentConfiguration{ + &implementation.ComponentConfiguration{ + ID: configurationIDRef, + Name: "MakeItRight", + }, + }, + } + mapImplementsProfile(&cd, parameterID, profileID, checkAndValue) + mapImplementsProfile(&cd, parameterID2, profileID, checkAndValue) + if len(cd.ImplementsProfiles) < 1 { + t.Error("implements profile array should have one element") + } + if cd.ImplementsProfiles[0].ProfileID != profileID { + t.Errorf("profile id should be %s", profileID) + } + + if len(cd.ImplementsProfiles[0].ControlConfigurations) < 1 { + t.Error("control configurations array should have one element") + } + if cd.ImplementsProfiles[0].ControlConfigurations[0].ConfigurationIDRef != configurationIDRef { + t.Errorf("config ref id should be %s", configurationIDRef) + } + if len(cd.ImplementsProfiles[0].ControlConfigurations[0].Parameters) < 2 { + t.Error("parameters array should have one element") + } + if cd.ImplementsProfiles[0].ControlConfigurations[0].Parameters[0].ParameterID != parameterID { + t.Errorf("parameter id should be %s", parameterID) + } + if cd.ImplementsProfiles[0].ControlConfigurations[0].Parameters[1].ParameterID != parameterID2 { + t.Errorf("parameter id should be %s", parameterID2) + } + if len(cd.ImplementsProfiles[0].ControlConfigurations[0].Parameters[0].PossibleValues) < 1 { + t.Error("possible value array should have one element") + } + if cd.ImplementsProfiles[0].ControlConfigurations[0].Parameters[0].PossibleValues[0] != Value { + t.Errorf("possible value should be %s", Value) + } +} + +func TestGetProfileIDWithValidProfile(t *testing.T) { + x := "FedRAMP_High" + o := getProfileID(x) + if o != profileMap[x] { + t.Error("failed to map profile id") + } +} +func TestGetProfileIDWithInvalidProfile(t *testing.T) { + x := "123" + o := getProfileID(x) + if o == profileMap[x] { + t.Error("mapped invalid profile id") + } +} + +func TestDetokenizeParameterString(t *testing.T) { + + x := "FedRAMP_High->SetParam(5)" + p := "FedRAMP_High" + c := "SetParam(5)" + profileID, checkAndValue := detokenizeParameterString(x) + if profileMap[p] != profileID || c != checkAndValue { + t.Errorf("failed to tokenize parameter string %s| output %s:%s", x, profileMap[p], checkAndValue) + } +} diff --git a/impl/implementation.go b/impl/implementation.go index a03c3bbd..3abf91b6 100644 --- a/impl/implementation.go +++ b/impl/implementation.go @@ -18,6 +18,7 @@ const ( // rowIndex Starting point for valid rows (neglects titles) rowIndex = 3 delimiter = "|" + profileDelimiter = "->" componentIDRegex = `cpe:[0-9].[0-9]:[a-z]:docker:[a-z-]*:(\d+\.)?(\d+\.)?(\*|\d+)` // componentNameRow is the index for getting component name componentNameRow = 1 @@ -27,111 +28,106 @@ const ( type guidMap map[string]uuid.UUID type cdMap map[string]implementation.ComponentDefinition - type component struct { - id string - compNameIndex int - name string - uuidIndex int - narrativeIndex int - definition cdMap + id string + compNameIndex int + name string + parameterIDIndex int + parameterStringIndex int + uuidIndex int + narrativeIndex int + definition cdMap + hasParameterMapping bool } -// GenerateImplementation generates implementation from component excel sheet -func GenerateImplementation(CSVS [][]string, p *profile.Profile, c Catalog) implementation.Implementation { +var profileMap = map[string]string{ + "FedRAMP_High": "uuid-fedramp-high-20180806-195540", + "FedRAMP_HIGH": "uuid-fedramp-high-20180806-195540", + "FedRAMP_Moderate": "uuid-fedramp-moderate-20180806-195542", + "FedRAMP_moderate": "uuid-fedramp-moderate-20180806-195542", +} +// GenerateImplementation generates implementation from component excel sheet +func GenerateImplementation(CSVS [][]string, c Catalog) implementation.Implementation { var cdMapList = make([]cdMap, 0) - components := []component{ { - id: getComponentID(CSVS[componentNameRow][17]), - name: "UCP", - compNameIndex: 17, - uuidIndex: 18, - narrativeIndex: 19, - definition: make(cdMap), + id: getComponentID(CSVS[componentNameRow][17]), + name: "UCP", + compNameIndex: 17, + parameterIDIndex: 18, + parameterStringIndex: 19, + uuidIndex: 20, + narrativeIndex: 21, + definition: make(cdMap), + hasParameterMapping: true, }, { - id: getComponentID(CSVS[componentNameRow][20]), - name: "DTR", - compNameIndex: 20, - uuidIndex: 21, - narrativeIndex: 22, - definition: make(cdMap), + id: getComponentID(CSVS[componentNameRow][22]), + name: "DTR", + compNameIndex: 22, + uuidIndex: 23, + narrativeIndex: 24, + definition: make(cdMap), + hasParameterMapping: false, }, { - id: getComponentID(CSVS[componentNameRow][14]), - name: "Engine", - compNameIndex: 14, - uuidIndex: 15, - narrativeIndex: 16, - definition: make(cdMap), + id: getComponentID(CSVS[componentNameRow][14]), + name: "Engine", + compNameIndex: 14, + uuidIndex: 15, + narrativeIndex: 16, + definition: make(cdMap), + hasParameterMapping: false, }, } - for _, comp := range components { - cdMapList = append(cdMapList, fillCDMap(CSVS, comp.compNameIndex, comp.definition, comp.uuidIndex, comp.narrativeIndex, comp.id, p, c)) + //comp.compNameIndex, comp.definition, comp.uuidIndex, comp.narrativeIndex, comp.parameterIDIndex, comp.parameterStringIndex, comp.id + cdMapList = append(cdMapList, fillCDMap(CSVS, comp, c)) } - - return CompileImplementation(cdMapList, CSVS, c, p, components) - + return CompileImplementation(cdMapList, CSVS, c, components) } -func fillCDMap(CSVS [][]string, compNameIndex int, compDef cdMap, uuidIndex int, narrativeIndex int, compID string, p *profile.Profile, c Catalog) cdMap { +func fillCDMap(CSVS [][]string, comp component, c Catalog) cdMap { checkAgainstGUID := make(map[string]uuid.UUID) for i := rowIndex; i < totalControlsInExcel; i++ { applicableControl := CSVS[i][controlIndex] if applicableControl == "" { continue } - applicableNarrative := CSVS[i][narrativeIndex] - ListOfComponentConfigName := strings.Split(CSVS[i][compNameIndex], delimiter) + applicableNarrative := CSVS[i][comp.narrativeIndex] + parameterID := CSVS[i][comp.parameterIDIndex] + parameterString := CSVS[i][comp.parameterStringIndex] + ListOfComponentConfigName := strings.Split(CSVS[i][comp.compNameIndex], delimiter) for compIndex, componentConfigName := range ListOfComponentConfigName { componentConfigName = strings.TrimSpace(componentConfigName) if componentConfigName == "" { continue } - if _, ok := compDef[componentConfigName]; !ok { + if _, ok := comp.definition[componentConfigName]; !ok { - guid := strings.Split(CSVS[i][uuidIndex], delimiter)[compIndex] + guid := strings.Split(CSVS[i][comp.uuidIndex], delimiter)[compIndex] guid = strings.TrimSpace(guid) - CreateComponentDefinition(checkAgainstGUID, compDef, componentConfigName, p, c, applicableControl, applicableNarrative, guid, compID) + CreateComponentDefinition(checkAgainstGUID, comp.definition, componentConfigName, c, applicableControl, applicableNarrative, guid, comp.id, parameterID, parameterString) } else { - securityCheck := compDef[componentConfigName] + securityCheck := comp.definition[componentConfigName] guid := checkAgainstGUID[componentConfigName] - temp := AppendParameterInImplementation(securityCheck, guid, p, c, applicableControl) - temp = AppendControlInImplementation(securityCheck, guid, c, applicableControl) - compDef[componentConfigName] = temp + temp := AppendControlInImplementation(securityCheck, guid, c, applicableControl) + comp.definition[componentConfigName] = temp } } } - return compDef + return comp.definition } // CreateComponentDefinition creates a component definition -func CreateComponentDefinition(gm guidMap, cdm cdMap, componentConfName string, p *profile.Profile, c Catalog, control, narrative, guid string, cdID string) { +func CreateComponentDefinition(gm guidMap, cdm cdMap, componentConfName string, c Catalog, control, narrative, guid string, cdID string, parameterID, parameterString string) { componentConfGUID, _ := uuid.FromString(guid) gm[componentConfName] = componentConfGUID controlConfiguration := implementation.ControlConfiguration{ ConfigurationIDRef: componentConfGUID.String(), } - var parameters []implementation.Parameter - if p.Modify != nil { - for _, param := range p.Modify.ParamSettings { - if param.Id == "" { - continue - } - if c.GetControl(param.Id) == c.GetControl(control) { - if existsInParams(param.Id, parameters) { - continue - } - guidance := getGuidance(p.Modify.Alterations, param.Id) - x := GenerateImplementationParameter(param, guidance) - parameters = append(parameters, x) - } - } - } controlConfiguration.ProvisioningMechanisms = []implementation.ProvisioningMechanism{ implementation.ProvisioningMechanism{ ProvisionedControls: []implementation.ControlId{ @@ -143,20 +139,12 @@ func CreateComponentDefinition(gm guidMap, cdm cdMap, componentConfName string, }, }, } - controlConfiguration.Parameters = parameters cdm[componentConfName] = implementation.ComponentDefinition{ ID: cdID, ComponentConfigurations: []*implementation.ComponentConfiguration{ CreateComponentConfiguration(componentConfGUID, componentConfName, narrative), }, - ImplementsProfiles: []*implementation.ImplementsProfile{ - &implementation.ImplementsProfile{ - ProfileID: p.ID, - ControlConfigurations: []implementation.ControlConfiguration{ - controlConfiguration, - }, - }, - }, + ImplementsProfiles: []*implementation.ImplementsProfile{}, ControlImplementations: []*implementation.ControlImplementation{ &implementation.ControlImplementation{ ControlConfigurations: []implementation.ControlConfiguration{ @@ -184,31 +172,6 @@ func CreateComponentConfiguration(guid uuid.UUID, componentConfName, narrative s } } -// AppendParameterInImplementation Appends parameter in the relative guid -func AppendParameterInImplementation(cd implementation.ComponentDefinition, guid uuid.UUID, p *profile.Profile, c Catalog, control string) implementation.ComponentDefinition { - for i := range cd.ImplementsProfiles { - for j := range cd.ImplementsProfiles[i].ControlConfigurations { - if guid.String() == cd.ImplementsProfiles[i].ControlConfigurations[j].ConfigurationIDRef { - for _, param := range p.Modify.ParamSettings { - if param.Id == "" { - continue - } - if existsInParams(param.Id, cd.ImplementsProfiles[i].ControlConfigurations[j].Parameters) { - continue - } - if c.GetControl(param.Id) == c.GetControl(control) { - guidance := getGuidance(p.Modify.Alterations, param.Id) - x := GenerateImplementationParameter(param, guidance) - cd.ImplementsProfiles[i].ControlConfigurations[j].Parameters = append(cd.ImplementsProfiles[i].ControlConfigurations[j].Parameters, x) - } - } - } - } - } - return cd - -} - // AppendControlInImplementation appends a control in the implementation func AppendControlInImplementation(cd implementation.ComponentDefinition, guid uuid.UUID, c Catalog, control string) implementation.ComponentDefinition { for i := range cd.ControlImplementations { @@ -231,11 +194,10 @@ func AppendControlInImplementation(cd implementation.ComponentDefinition, guid u } // CompileImplementation compiles all checks from maps to implementation json -func CompileImplementation(cdList []cdMap, CSVS [][]string, cat Catalog, p *profile.Profile, components []component) implementation.Implementation { +func CompileImplementation(cdList []cdMap, CSVS [][]string, cat Catalog, components []component) implementation.Implementation { - return implementation.Implementation{ + x := implementation.Implementation{ ComponentDefinitions: func() []implementation.ComponentDefinition { - var cds []implementation.ComponentDefinition for _, cd := range cdList { compD := implementation.ComponentDefinition{ @@ -263,7 +225,8 @@ func CompileImplementation(cdList []cdMap, CSVS [][]string, cat Catalog, p *prof ControlImplementations: func() []*implementation.ControlImplementation { arr := []*implementation.ControlImplementation{ &implementation.ControlImplementation{ - ControlIds: []implementation.ControlId{}, + ControlIds: []implementation.ControlId{}, + ControlConfigurations: []implementation.ControlConfiguration{}, }, } for i := 3; i < totalControlsInExcel; i++ { @@ -285,43 +248,17 @@ func CompileImplementation(cdList []cdMap, CSVS [][]string, cat Catalog, p *prof CatalogIDRef: cat.GetID(), }) } - for _, def := range cd { - for _, ci := range def.ImplementsProfiles { - for _, cc := range ci.ControlConfigurations { - arr[0].ControlConfigurations = append(arr[0].ControlConfigurations, cc) - } - } - } - for j, x := range arr[0].ControlConfigurations { - for _, def := range cd { - for _, ci := range def.ControlImplementations { - for _, cc := range ci.ControlConfigurations { - if cc.ConfigurationIDRef == x.ConfigurationIDRef { - arr[0].ControlConfigurations[j].ProvisioningMechanisms = cc.ProvisioningMechanisms - } - } - } + for _, v := range cd { + for _, x := range v.ControlImplementations { + arr[0].ControlConfigurations = append( + arr[0].ControlConfigurations, + x.ControlConfigurations..., + ) } } return arr }(), - ImplementsProfiles: []*implementation.ImplementsProfile{ - &implementation.ImplementsProfile{ - ProfileID: p.ID, - ControlConfigurations: func() []implementation.ControlConfiguration { - var arr []implementation.ControlConfiguration - for _, v := range cd { - for _, x := range v.ImplementsProfiles { - for _, y := range x.ControlConfigurations { - arr = append(arr, y) - } - } - } - return arr - }(), - }, - }, } cds = append(cds, compD) @@ -329,6 +266,8 @@ func CompileImplementation(cdList []cdMap, CSVS [][]string, cat Catalog, p *prof return cds }(), } + i := fillImplementsProfile(&x, components, CSVS) + return *i } //Catalog catalog interface to determine control id pattern @@ -402,7 +341,6 @@ func existsInParams(pID string, p []implementation.Parameter) bool { } func existsInControls(cID string, controls []implementation.ControlId) bool { - for _, x := range controls { if x.ControlID == cID { return true diff --git a/impl/implementsprofile.go b/impl/implementsprofile.go new file mode 100644 index 00000000..2b81fc2f --- /dev/null +++ b/impl/implementsprofile.go @@ -0,0 +1,157 @@ +package impl + +import ( + "regexp" + "strings" + + "github.com/docker/oscalkit/types/oscal/implementation" +) + +func detokenizeParameterString(paramStr string) (string, string) { + tokens := strings.Split(paramStr, profileDelimiter) + if len(tokens) < 2 { + return "", "" + } + profileID := getProfileID(strings.TrimSpace(tokens[0])) + return strings.TrimSpace(profileID), strings.TrimSpace(tokens[1]) + +} + +func fillImplementsProfile(imp *implementation.Implementation, cmps []component, CSVS [][]string) *implementation.Implementation { + for _, c := range cmps { + if !c.hasParameterMapping { + continue + } + for i := rowIndex; i < totalControlsInExcel; i++ { + parameterID := strings.TrimSpace(CSVS[i][c.parameterIDIndex]) + parameterType := strings.TrimSpace(CSVS[i][c.parameterStringIndex]) + mappings := strings.Split(parameterType, delimiter) + for _, mapping := range mappings { + profileID, checkAndValue := detokenizeParameterString(mapping) + if profileID == "" || checkAndValue == "" { + continue + } + for i := range imp.ComponentDefinitions { + mapImplementsProfile(&imp.ComponentDefinitions[i], + parameterID, + profileID, + checkAndValue) + } + } + } + } + return imp + +} + +func addParemeters(ctrlconf *implementation.ControlConfiguration, parameterID, parameterValue string) []implementation.Parameter { + paramFound := false + for k, param := range ctrlconf.Parameters { + if param.ParameterID == parameterID { + paramFound = true + valueFound := false + for _, possibleValue := range param.PossibleValues { + if possibleValue == parameterValue { + valueFound = true + break + } + } + if !valueFound { + ctrlconf.Parameters[k].PossibleValues = append( + ctrlconf.Parameters[k].PossibleValues, + parameterValue, + ) + } + } + } + if !paramFound { + ctrlconf.Parameters = append(ctrlconf.Parameters, implementation.Parameter{ + ParameterID: parameterID, + PossibleValues: []string{parameterValue}, + }) + } + return ctrlconf.Parameters +} + +func mapImplementsProfile(cd *implementation.ComponentDefinition, parameterID, profileID, checkAndValue string) { + ip := findOrCreateImplementsProfile(cd, profileID) + checkName, parameterValue := parseCheckAndValue(checkAndValue) + componentConfigID, found := findConfigIDByName(cd.ComponentConfigurations, checkName) + if !found { + return + } + ctrlConf := findOrCreateControlConfig(ip, componentConfigID) + parameters := addParemeters(ctrlConf, parameterID, parameterValue) + for j, cc := range ip.ControlConfigurations { + if cc.ConfigurationIDRef == ctrlConf.ConfigurationIDRef { + ip.ControlConfigurations[j].Parameters = parameters + } + } +} + +func findOrCreateControlConfig(ip *implementation.ImplementsProfile, configIDRef string) *implementation.ControlConfiguration { + for _, cc := range ip.ControlConfigurations { + if cc.ConfigurationIDRef == configIDRef { + return &cc + } + } + newControlConfig := implementation.ControlConfiguration{ + ConfigurationIDRef: configIDRef, + Parameters: []implementation.Parameter{}, + } + ip.ControlConfigurations = append(ip.ControlConfigurations, newControlConfig) + return &newControlConfig +} + +func findOrCreateImplementsProfile(cd *implementation.ComponentDefinition, profileID string) *implementation.ImplementsProfile { + for _, ip := range cd.ImplementsProfiles { + if ip.ProfileID == profileID { + return ip + } + } + newProfile := &implementation.ImplementsProfile{ + ProfileID: profileID, + ControlConfigurations: func() []implementation.ControlConfiguration { + arr := []implementation.ControlConfiguration{} + for _, cc := range cd.ComponentConfigurations { + arr = append(arr, implementation.ControlConfiguration{ + ConfigurationIDRef: cc.ID, + }) + } + return arr + }(), + } + cd.ImplementsProfiles = append(cd.ImplementsProfiles, newProfile) + return newProfile +} + +func getProfileID(s string) string { + s = strings.TrimSpace(s) + if v, ok := profileMap[s]; ok { + return v + } + return s +} + +//parseCheckAndValue Something(<=2) will change to [Something, <=2] +func parseCheckAndValue(s string) (string, string) { + reg := regexp.MustCompile(`(\w{1,})\((.{1,})\)`) + matches := reg.FindAllStringSubmatch(s, -1) + if len(matches) < 1 { + return "", "" + } + if len(matches[0]) < 2 { + return "", "" + } + return matches[0][1], matches[0][2] + +} + +func findConfigIDByName(ccfs []*implementation.ComponentConfiguration, name string) (string, bool) { + for _, ccf := range ccfs { + if ccf.Name == name { + return ccf.ID, true + } + } + return "", false +} diff --git a/templates/implementation.go b/templates/implementation.go index 8e4a3a87..eebfc693 100644 --- a/templates/implementation.go +++ b/templates/implementation.go @@ -1,10 +1,16 @@ package templates -import "html/template" +import ( + "html/template" +) -//GetImplementationTemplate gets implementation template for implementation go struct file +// GetImplementationTemplate gets implementation template for implementation go struct file func GetImplementationTemplate() (*template.Template, error) { - return template.New("").Parse(implementationTemplate) + return template.New("").Funcs(map[string]interface{}{"norm": Normalize}).Parse(implementationTemplate) +} + +func Normalize(s string) template.HTML { + return template.HTML(s) } const implementationTemplate = ` @@ -60,7 +66,7 @@ var ImplementationGenerated = implementation.Implementation{ ValueID: "{{.ValueID}}", PossibleValues: []string{ {{range .PossibleValues}} - "{{.}}", + "{{norm .}}", {{end}} }, },