From 8c8ccf6a2e7dbd746b73c0e564eb3fafc432e58c Mon Sep 17 00:00:00 2001 From: "Sascha L. Teichmann" Date: Sun, 22 Jan 2023 20:55:26 +0100 Subject: [PATCH] Extract more than one string from expr: category fields. --- cmd/csaf_aggregator/mirror.go | 9 ++++--- cmd/csaf_provider/actions.go | 5 ++-- docs/csaf_provider.md | 6 +++++ util/json.go | 50 +++++++++++++++++++++++++++++++++++ 4 files changed, 64 insertions(+), 6 deletions(-) diff --git a/cmd/csaf_aggregator/mirror.go b/cmd/csaf_aggregator/mirror.go index 7fe71d7..3ea4837 100644 --- a/cmd/csaf_aggregator/mirror.go +++ b/cmd/csaf_aggregator/mirror.go @@ -451,18 +451,19 @@ func (w *worker) extractCategories(label string, advisory any) error { w.categories[label] = cats } - var result string - matcher := util.StringMatcher(&result) - const exprPrefix = "expr:" for _, cat := range categories { if strings.HasPrefix(cat, exprPrefix) { expr := cat[len(exprPrefix):] + var results []string + matcher := util.StringTreeMatcher(&results) if err := w.expr.Extract(expr, matcher, true, advisory); err != nil { return err } - cats[result] = true + for _, result := range results { + cats[result] = true + } } else { // Normal cats[cat] = true } diff --git a/cmd/csaf_provider/actions.go b/cmd/csaf_provider/actions.go index ea099e3..b1ad0a9 100644 --- a/cmd/csaf_provider/actions.go +++ b/cmd/csaf_provider/actions.go @@ -195,9 +195,10 @@ func (c *controller) upload(r *http.Request) (any, error) { var dynamicCategories []string if catExprs := c.cfg.DynamicCategories(); len(catExprs) > 0 { var err error - if dynamicCategories, err = pe.Strings(catExprs, true, content); err != nil { + if dynamicCategories, err = pe.StringsFromTree( + catExprs, true, content); err != nil { // XXX: Should we die here? - log.Printf("eval of dynamic catecory expressions failed: %v\n", err) + log.Printf("eval of dynamic catergory expressions failed: %v\n", err) } } diff --git a/docs/csaf_provider.md b/docs/csaf_provider.md index 81a45fa..464f0db 100644 --- a/docs/csaf_provider.md +++ b/docs/csaf_provider.md @@ -106,6 +106,12 @@ The following example file documents all available configuration options: # If a list item starts with `expr:` # the rest of the string is used as a JsonPath expression # to extract a string from the incoming advisories. +# If the result of the expression is a string this string +# is used. If the result is an array each element of +# this array is tested if it is a string or an array. +# If this test fails the expression fails. If the +# test succeeds the rules are applied recursively to +# collect all strings in the result. # Strings not starting with `expr:` are taken verbatim. # By default no category documents are created. # This example provides an overview over the syntax, diff --git a/util/json.go b/util/json.go index ed0fd70..48d0e09 100644 --- a/util/json.go +++ b/util/json.go @@ -101,6 +101,37 @@ func StringMatcher(dst *string) func(any) error { } } +// StringTreeMatcher returns a matcher which add strings from +// stringss and recursively from arrays from strings. +func StringTreeMatcher(strings *[]string) func(any) error { + // Only add unique strings. + unique := func(s string) { + for _, t := range *strings { + if s == t { + return + } + } + *strings = append(*strings, s) + } + var recurse func(any) error + recurse = func(x any) error { + switch y := x.(type) { + case string: + unique(y) + case []any: + for _, z := range y { + if err := recurse(z); err != nil { + return err + } + } + default: + return fmt.Errorf("unsupported type: %T", x) + } + return nil + } + return recurse +} + // TimeMatcher stores a time with a given format. func TimeMatcher(dst *time.Time, format string) func(any) error { return func(x any) error { @@ -147,6 +178,25 @@ func (pe *PathEval) Match(matcher []PathEvalMatcher, doc any) error { return nil } +// StringsFromTree returns strings from the given exprs. +// 1. If a expression results a string this string is used. +// 2. if a expression results in an array the elements +// of this array are recursively treated with 1. and 2. +func (pe *PathEval) StringsFromTree( + exprs []string, + optional bool, + doc any, +) ([]string, error) { + results := make([]string, 0, len(exprs)) + matcher := StringTreeMatcher(&results) + for _, expr := range exprs { + if err := pe.Extract(expr, matcher, optional, doc); err != nil { + return nil, err + } + } + return results, 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.