diff --git a/README.md b/README.md index 1dd02337..3a732838 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,9 @@ Can be used for automated forwarding of CSAF documents. ### [csaf_validator](docs/csaf_validator.md) is a tool to validate local advisories files against the JSON Schema and an optional remote validator. +### [csaf_searcher](docs/csaf_searcher.md) +is a tool to search through local advisories. It finds PURLs based on the product ID of an advisory. + ## Tools for advisory providers ### [csaf_provider](docs/csaf_provider.md) diff --git a/cmd/csaf_searcher/main.go b/cmd/csaf_searcher/main.go new file mode 100644 index 00000000..871433dc --- /dev/null +++ b/cmd/csaf_searcher/main.go @@ -0,0 +1,126 @@ +// Package main implements a simple demo program to +// work with the csaf_distribution library. +package main + +import ( + "flag" + "fmt" + "log" + "os" + "slices" + "strings" + + "github.com/csaf-poc/csaf_distribution/v2/csaf" +) + +func main() { + flag.Usage = func() { + fmt.Fprintf(flag.CommandLine.Output(), + "Usage:\n %s [OPTIONS] files...\n\nOptions:\n", os.Args[0]) + flag.PrintDefaults() + } + idsString := flag.String("p", "", "ID1,ID2,...") + flag.Parse() + + files := flag.Args() + if len(files) == 0 { + log.Println("No files given.") + return + } + if err := run(files, *idsString); err != nil { + log.Fatalf("error: %v\n", err) + } +} + +// run prints PURLs belonging to the given Product IDs. +func run(files []string, ids string) error { + + uf := newURLFinder(strings.Split(ids, ",")) + + for _, file := range files { + adv, err := csaf.LoadAdvisory(file) + if err != nil { + return fmt.Errorf("loading %q failed: %w", file, err) + } + uf.findURLs(adv) + uf.dumpURLs() + uf.clear() + } + + return nil +} + +// urlFinder helps to find the URLs of a set of product ids in advisories. +type urlFinder struct { + ids []csaf.ProductID + urls [][]csaf.PURL +} + +// newURLFinder creates a new urlFinder for given ids. +func newURLFinder(ids []string) *urlFinder { + uf := &urlFinder{ + ids: make([]csaf.ProductID, len(ids)), + urls: make([][]csaf.PURL, len(ids)), + } + for i := range uf.ids { + uf.ids[i] = csaf.ProductID(ids[i]) + } + return uf +} + +// clear resets the url finder after a run on an advisory. +func (uf *urlFinder) clear() { + clear(uf.urls) +} + +// dumpURLs dumps the found URLs to stdout. +func (uf *urlFinder) dumpURLs() { + for i, urls := range uf.urls { + if len(urls) == 0 { + continue + } + fmt.Printf("Found URLs for %s:\n", uf.ids[i]) + for j, url := range urls { + fmt.Printf("%d. %s\n", j+1, url) + } + } +} + +// findURLs find the URLs in an advisory. +func (uf *urlFinder) findURLs(adv *csaf.Advisory) { + tree := adv.ProductTree + if tree == nil { + return + } + + // If we have found it and we have a valid URL add unique. + add := func(idx int, h *csaf.ProductIdentificationHelper) { + if idx != -1 && h != nil && h.PURL != nil && + !slices.Contains(uf.urls[idx], *h.PURL) { + uf.urls[idx] = append(uf.urls[idx], *h.PURL) + } + } + + // First iterate over full product names. + if names := tree.FullProductNames; names != nil { + for _, name := range *names { + if name != nil && name.ProductID != nil { + add(slices.Index(uf.ids, *name.ProductID), name.ProductIdentificationHelper) + } + } + } + + // Second traverse the branches recursively. + var recBranch func(*csaf.Branch) + recBranch = func(b *csaf.Branch) { + if p := b.Product; p != nil && p.ProductID != nil { + add(slices.Index(uf.ids, *p.ProductID), p.ProductIdentificationHelper) + } + for _, c := range b.Branches { + recBranch(c) + } + } + for _, b := range tree.Branches { + recBranch(b) + } +} diff --git a/docs/csaf_searcher.md b/docs/csaf_searcher.md new file mode 100644 index 00000000..e821025c --- /dev/null +++ b/docs/csaf_searcher.md @@ -0,0 +1,16 @@ +# csaf_advisory_example + +This is a small searcher using the advisory model to search for PURLs belonging to a product ID in an advisory of the CSAF 2.0 standard. + +Usage: +``` + +csaf_advisory_example OPTIONS [files...] + +Application Options: +-p The Product ID + +Help Options: +-h, --help Show a help message + +```