From 198e5b8897ff40d95dd7d251b93e0e90742c10b2 Mon Sep 17 00:00:00 2001 From: "Sascha L. Teichmann" Date: Thu, 30 Jun 2022 11:58:36 +0200 Subject: [PATCH] write dynamic categories into feed categories document. --- cmd/csaf_provider/actions.go | 24 ++++++++++++++++++- cmd/csaf_provider/rolie.go | 46 ++++++++++++++++++++++++++++++++++++ csaf/rolie.go | 40 +++++++++++++++++++++++++------ util/json.go | 20 ++++++++++++++++ 4 files changed, 122 insertions(+), 8 deletions(-) diff --git a/cmd/csaf_provider/actions.go b/cmd/csaf_provider/actions.go index ec621c0..486cfd7 100644 --- a/cmd/csaf_provider/actions.go +++ b/cmd/csaf_provider/actions.go @@ -14,6 +14,7 @@ import ( "errors" "fmt" "io" + "log" "net/http" "os" "path/filepath" @@ -182,11 +183,24 @@ func (c *controller) upload(r *http.Request) (interface{}, error) { } } - ex, err := csaf.NewAdvisorySummary(util.NewPathEval(), content) + // Extract informations from the document. + pe := util.NewPathEval() + + ex, err := csaf.NewAdvisorySummary(pe, content) if err != nil { return nil, err } + // Check if we have to search for dynamic categories. + var dynamicCategories []string + if catExprs := c.cfg.DynamicCategories(); len(catExprs) > 0 { + var err error + if dynamicCategories, err = pe.Strings(catExprs, true, content); err != nil { + // XXX: Should we die here? + log.Printf("eval of dynamic catecory expressions failed: %v\n", err) + } + } + t, err := c.tlpParam(r) if err != nil { return nil, err @@ -217,6 +231,14 @@ func (c *controller) upload(r *http.Request) (interface{}, error) { return err } + // if we have found dynamic categories merge them into + // the existing once. + if len(dynamicCategories) > 0 { + if err := c.mergeCategories(folder, t, dynamicCategories); err != nil { + return err + } + } + // Create yearly subfolder year := strconv.Itoa(ex.InitialReleaseDate.Year()) diff --git a/cmd/csaf_provider/rolie.go b/cmd/csaf_provider/rolie.go index e260de9..2334699 100644 --- a/cmd/csaf_provider/rolie.go +++ b/cmd/csaf_provider/rolie.go @@ -19,6 +19,52 @@ import ( "github.com/csaf-poc/csaf_distribution/util" ) +// mergeCategories merges the given categories into the old ones. +func (c *controller) mergeCategories( + folder string, + t tlp, + categories []string, +) error { + ts := string(t) + catName := "category-" + ts + ".json" + catPath := filepath.Join(folder, catName) + + catDoc, err := loadCategoryDocument(catPath) + if err != nil { + return err + } + + var changed bool + + if catDoc == nil { + catDoc = csaf.NewROLIECategoryDocument(categories...) + changed = true + } else { + changed = catDoc.Merge(categories...) + } + + if changed { + if err := util.WriteToFile(catPath, catDoc); err != nil { + return err + } + } + return nil +} + +// loadROLIEFeed loads a ROLIE feed from file if its exists. +// Returns nil if the file does not exists. +func loadCategoryDocument(path string) (*csaf.ROLIECategoryDocument, error) { + f, err := os.Open(path) + if err != nil { + if os.IsNotExist(err) { + return nil, nil + } + return nil, err + } + defer f.Close() + return csaf.LoadROLIECategoryDocument(f) +} + // extendROLIE adds a new entry to the ROLIE feed for a given advisory. func (c *controller) extendROLIE( folder string, diff --git a/csaf/rolie.go b/csaf/rolie.go index 84dc3de..6b9d4ef 100644 --- a/csaf/rolie.go +++ b/csaf/rolie.go @@ -82,15 +82,41 @@ type ROLIECategoryDocument struct { // NewROLIECategoryDocument creates a new ROLIE category document from a list // of categories. func NewROLIECategoryDocument(categories ...string) *ROLIECategoryDocument { - cats := make([]ROLIECategory, len(categories)) - for i, cat := range categories { - cats[i] = ROLIECategory{Term: cat} + rcd := &ROLIECategoryDocument{} + rcd.Merge(categories...) + return rcd +} + +// Merge merges the given categories into the existing ones. +// The results indicates if there were changes. +func (rcd *ROLIECategoryDocument) Merge(categories ...string) bool { + index := make(map[string]bool) + for i := range rcd.Categories.Category { + index[rcd.Categories.Category[i].Term] = true } - return &ROLIECategoryDocument{ - Categories: ROLIECategories{ - Category: cats, - }, + + oldLen := len(index) + + for _, cat := range categories { + if index[cat] { + continue + } + index[cat] = true + rcd.Categories.Category = append( + rcd.Categories.Category, ROLIECategory{Term: cat}) } + + if len(index) == oldLen { + // No new categories + return false + } + + // Re-establish order. + sort.Slice(rcd.Categories.Category, func(i, j int) bool { + return rcd.Categories.Category[i].Term < rcd.Categories.Category[j].Term + }) + + return true } // LoadROLIECategoryDocument loads a ROLIE category document from a reader. diff --git a/util/json.go b/util/json.go index 45878c0..bd53f64 100644 --- a/util/json.go +++ b/util/json.go @@ -146,3 +146,23 @@ func (pe *PathEval) Match(matcher []PathEvalMatcher, doc interface{}) error { } return nil } + +// Strings searches the given document for the given set of expressions +// and returns the corresponding strings. The optional flag indicates +// if the expression evaluation have to succseed or not. +func (pe *PathEval) Strings( + exprs []string, + optional bool, + doc interface{}, +) ([]string, error) { + results := make([]string, 0, len(exprs)) + var result string + matcher := StringMatcher(&result) + for _, expr := range exprs { + if err := pe.Extract(expr, matcher, optional, doc); err != nil { + return nil, err + } + results = append(results, result) + } + return results, nil +}