Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Re-implement replaced_by functionality from #130 #136

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 35 additions & 26 deletions cmd/baseline.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package main

import (
"errors"
"fmt"
"log"
"os"
"path/filepath"
"reflect"
"slices"
"strings"
"text/template"

Expand All @@ -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
Expand Down Expand Up @@ -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 == "" {
Expand All @@ -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) {
Expand All @@ -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
}

Expand Down Expand Up @@ -192,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)
Expand Down
11 changes: 8 additions & 3 deletions cmd/template.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,22 @@ For more information on the project and to make contributions, visit the [GitHub

{{ range .Categories }}

---

## {{ .CategoryName }}

{{ .Description }}


{{- range .Criteria }}
---

### {{ .ID }}

{{ if .ReplacedBy -}}
**Replaced By:** [{{ .ReplacedBy }}](#{{ .ReplacedBy | toLower }})

{{else -}}
**Criterion:** {{ .CriterionText | addLinks }}

**Maturity Level:** {{ .MaturityLevel }}
Expand All @@ -76,9 +83,7 @@ For more information on the project and to make contributions, visit the [GitHub
{{ if .SecurityInsightsValue }}
**Security Insights Value:** {{ .SecurityInsightsValue }}
{{- end }}

---

{{- end }}
{{- end }}
{{- end }}

Expand Down
Loading