From 9f88dcab4d5254b7a09ebd9a42189f7b76bf4fb2 Mon Sep 17 00:00:00 2001 From: Evan Anderson Date: Tue, 14 Jan 2025 06:06:59 -0800 Subject: [PATCH 1/4] Re-implement replaced_by functionality from #130 Signed-off-by: Evan Anderson --- cmd/baseline.go | 60 ++++++++++++++++++++++++++++--------------------- cmd/template.md | 10 ++++++--- 2 files changed, 41 insertions(+), 29 deletions(-) diff --git a/cmd/baseline.go b/cmd/baseline.go index 7ed72049..bf55de73 100644 --- a/cmd/baseline.go +++ b/cmd/baseline.go @@ -1,10 +1,12 @@ package main import ( + "errors" "fmt" - "log" "os" "path/filepath" + "reflect" + "slices" "strings" "text/template" @@ -22,6 +24,8 @@ type Criterion struct { Details string `yaml:"details"` ControlMappings map[string]string `yaml:"control_mappings"` SecurityInsightsValue string `yaml:"security_insights_value"` + // If ReplacedBy is set, no other fields (beyond ID) should be set + ReplacedBy string `yaml:"replaced_by"` } // Struct for holding the entire YAML structure @@ -68,37 +72,43 @@ func newBaseline() (Baseline, error) { Categories: make(map[string]Category), Lexicon: lexicon, } - var failed bool + var errs []error for _, categoryName := range hardcodedCategories() { category, err := newCategory(categoryName) if err != nil { - failed = true - log.Printf("error reading category %s: %s", categoryName, err.Error()) + errs = append(errs, fmt.Errorf("error reading category %s: %w", categoryName, err)) } b.Categories[categoryName] = category } - if failed { - return b, fmt.Errorf("error setting up baseline") + categoryErr := errors.Join(errs...) + if categoryErr != nil { + return b, categoryErr } return b, b.validate() } func (b Baseline) validate() error { var entryIDs []string - var failed bool + retiredIDs := map[string]string{} + errs := []error{} for _, category := range b.Categories { for _, entry := range category.Criteria { - if contains(entryIDs, entry.ID) { - failed = true - log.Printf("duplicate ID for 'criterion' for %s", entry.ID) + if slices.Contains(entryIDs, entry.ID) { + errs = append(errs, fmt.Errorf("duplicate ID for 'criterion' for %s", entry.ID)) } + entryIDs = append(entryIDs, entry.ID) if entry.ID == "" { - failed = true - log.Printf("missing ID for 'criterion' %s", entry.ID) + errs = append(errs, fmt.Errorf("missing ID for 'criterion' %s", entry.ID)) + } + if entry.ReplacedBy != "" { + retiredIDs[entry.ID] = entry.ReplacedBy + if !reflect.DeepEqual(entry, Criterion{ID: entry.ID, ReplacedBy: entry.ReplacedBy}) { + errs = append(errs, fmt.Errorf("retired criterion %s has additional fields", entry.ID)) + } + continue } if entry.CriterionText == "" { - failed = true - log.Printf("missing 'criterion' text for %s", entry.ID) + errs = append(errs, fmt.Errorf("missing 'criterion' text for %s", entry.ID)) } // For after all fields are populated: // if entry.Rationale == "" { @@ -109,22 +119,17 @@ func (b Baseline) validate() error { // failed = true // log.Printf("missing 'details' for %s", entry.ID) // } - entryIDs = append(entryIDs, entry.ID) } } - if failed { - return fmt.Errorf("error validating baseline") - } - return nil -} - -func contains(list []string, term string) bool { - for _, item := range list { - if item == term { - return true + for retired, replacement := range retiredIDs { + if !slices.Contains(entryIDs, replacement) { + errs = append(errs, fmt.Errorf("retired criterion %s has invalid replacement %s", retired, replacement)) + } + if _, ok := retiredIDs[replacement]; ok { + errs = append(errs, fmt.Errorf("retired criterion %s references another retired criterion %s", retired, replacement)) } } - return false + return errors.Join(errs...) } func newCategory(categoryName string) (Category, error) { @@ -141,6 +146,9 @@ func newCategory(categoryName string) (Category, error) { if err := decoder.Decode(&category); err != nil { return category, fmt.Errorf("error decoding YAML: %v", err) } + slices.SortFunc(category.Criteria, func(a, b Criterion) int { + return strings.Compare(a.ID, b.ID) + }) return category, nil } diff --git a/cmd/template.md b/cmd/template.md index 41c2dc8c..e0969008 100644 --- a/cmd/template.md +++ b/cmd/template.md @@ -60,6 +60,10 @@ For more information on the project and to make contributions, visit the [GitHub **Criterion:** {{ .CriterionText | addLinks }} +{{ if .ReplacedBy -}} +**Replaced By:** {{ .ReplacedBy }} +{{- else -}} + **Maturity Level:** {{ .MaturityLevel }} **Rationale:** {{ .Rationale | addLinks}} @@ -76,11 +80,11 @@ For more information on the project and to make contributions, visit the [GitHub {{ if .SecurityInsightsValue }} **Security Insights Value:** {{ .SecurityInsightsValue }} {{- end }} - +{{- end }} --- -{{- end }} -{{- end }} +{{- end}} +{{- end}} ## Lexicon From a5d760b75aeba186962b56244dfc6c8cf88e87c7 Mon Sep 17 00:00:00 2001 From: Evan Anderson Date: Fri, 17 Jan 2025 11:03:47 -0800 Subject: [PATCH 2/4] Update cmd/template.md Co-authored-by: Eddie Knight Signed-off-by: Evan Anderson --- cmd/template.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/template.md b/cmd/template.md index e0969008..d2cbe1fe 100644 --- a/cmd/template.md +++ b/cmd/template.md @@ -83,8 +83,8 @@ For more information on the project and to make contributions, visit the [GitHub {{- end }} --- -{{- end}} -{{- end}} +{{- end }} +{{- end }} ## Lexicon From 324af2f909edf2915056df3196fdfb96bbee53a2 Mon Sep 17 00:00:00 2001 From: Evan Anderson Date: Fri, 17 Jan 2025 11:17:58 -0800 Subject: [PATCH 3/4] Fix templating a bit more Signed-off-by: Evan Anderson --- cmd/template.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cmd/template.md b/cmd/template.md index d2cbe1fe..ca99e4de 100644 --- a/cmd/template.md +++ b/cmd/template.md @@ -49,12 +49,15 @@ For more information on the project and to make contributions, visit the [GitHub {{ range .Categories }} +--- + ## {{ .CategoryName }} {{ .Description }} {{- range .Criteria }} +--- ### {{ .ID }} @@ -81,8 +84,6 @@ For more information on the project and to make contributions, visit the [GitHub **Security Insights Value:** {{ .SecurityInsightsValue }} {{- end }} {{- end }} ---- - {{- end }} {{- end }} From 08ac139d54fe43dc83990d0a06a31c1dc4c97759 Mon Sep 17 00:00:00 2001 From: Evan Anderson Date: Fri, 17 Jan 2025 11:27:27 -0800 Subject: [PATCH 4/4] Add links to retired criteria / fix formatting Signed-off-by: Evan Anderson --- cmd/baseline.go | 1 + cmd/template.md | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/cmd/baseline.go b/cmd/baseline.go index bf55de73..d0ad9144 100644 --- a/cmd/baseline.go +++ b/cmd/baseline.go @@ -200,6 +200,7 @@ func (b *Baseline) Generate() error { "asLink": func(s string) string { return asLinkTemplateFunction(s) }, + "toLower": strings.ToLower, }).Parse(string(templateContent)) if err != nil { return fmt.Errorf("error parsing template: %w", err) diff --git a/cmd/template.md b/cmd/template.md index ca99e4de..b85f1fa9 100644 --- a/cmd/template.md +++ b/cmd/template.md @@ -61,11 +61,11 @@ For more information on the project and to make contributions, visit the [GitHub ### {{ .ID }} -**Criterion:** {{ .CriterionText | addLinks }} - {{ if .ReplacedBy -}} -**Replaced By:** {{ .ReplacedBy }} -{{- else -}} +**Replaced By:** [{{ .ReplacedBy }}](#{{ .ReplacedBy | toLower }}) + +{{else -}} +**Criterion:** {{ .CriterionText | addLinks }} **Maturity Level:** {{ .MaturityLevel }}