From a131b0fb4bc97592d8ac4d80280706359b2a6811 Mon Sep 17 00:00:00 2001 From: koplas <54645365+koplas@users.noreply.github.com> Date: Thu, 25 Jul 2024 15:36:54 +0200 Subject: [PATCH 01/64] Improve SHA* marking --- cmd/csaf_checker/processor.go | 45 ++++++++++++++--- cmd/csaf_downloader/downloader.go | 34 ++++++++----- csaf/advisories.go | 83 +++++++++++++++---------------- 3 files changed, 99 insertions(+), 63 deletions(-) diff --git a/cmd/csaf_checker/processor.go b/cmd/csaf_checker/processor.go index 451a315c..de42e184 100644 --- a/cmd/csaf_checker/processor.go +++ b/cmd/csaf_checker/processor.go @@ -20,6 +20,7 @@ import ( "fmt" "io" "log" + "log/slog" "net/http" "net/url" "path/filepath" @@ -138,7 +139,7 @@ func (m *topicMessages) info(format string, args ...any) { m.add(InfoType, format, args...) } -// use signals that we going to use this topic. +// use signals that we're going to use this topic. func (m *topicMessages) use() { if *m == nil { *m = []Message{} @@ -164,7 +165,7 @@ func (m *topicMessages) hasErrors() bool { return false } -// newProcessor returns an initilaized processor. +// newProcessor returns an initialized processor. func newProcessor(cfg *config) (*processor, error) { var validator csaf.RemoteValidator @@ -594,10 +595,15 @@ func (p *processor) rolieFeedEntries(feed string) ([]csaf.AdvisoryFile, error) { var file csaf.AdvisoryFile - if sha256 != "" || sha512 != "" || sign != "" { - file = csaf.HashedAdvisoryFile{url, sha256, sha512, sign} - } else { - file = csaf.PlainAdvisoryFile(url) + switch { + case sha256 == "" && sha512 == "": + slog.Error("No hash listed on ROLIE feed", "file", url) + return + case sign == "": + slog.Error("No signature listed on ROLIE feed", "file", url) + return + default: + file = csaf.PlainAdvisoryFile{Path: url, SHA256: sha256, SHA512: sha512, Sign: sign} } files = append(files, file) @@ -888,7 +894,16 @@ func (p *processor) checkIndex(base string, mask whereType) error { p.badIntegrities.error("index.txt contains invalid URL %q in line %d", u, line) continue } - files = append(files, csaf.PlainAdvisoryFile(u)) + + SHA256 := p.checkURL(u + ".sha256") + SHA512 := p.checkURL(u + ".sha512") + sign := p.checkURL(u + ".asc") + files = append(files, csaf.PlainAdvisoryFile{ + Path: u, + SHA256: SHA256, + SHA512: SHA512, + Sign: sign, + }) } return files, scanner.Err() }() @@ -906,6 +921,15 @@ func (p *processor) checkIndex(base string, mask whereType) error { return p.integrity(files, base, mask, p.badIndices.add) } +// checkURL returns the URL if it is accessible. +func (p *processor) checkURL(url string) string { + _, err := p.client.Head(url) + if err != nil { + return url + } + return "" +} + // checkChanges fetches the "changes.csv" and calls the "checkTLS" method for HTTPs checks. // It extracts the file content, tests the column number and the validity of the time format // of the fields' values and if they are sorted properly. Then it passes the files to the @@ -970,9 +994,14 @@ func (p *processor) checkChanges(base string, mask whereType) error { continue } path := r[pathColumn] + + SHA256 := p.checkURL(path + ".sha256") + SHA512 := p.checkURL(path + ".sha512") + sign := p.checkURL(path + ".asc") + times, files = append(times, t), - append(files, csaf.PlainAdvisoryFile(path)) + append(files, csaf.PlainAdvisoryFile{Path: path, SHA256: SHA256, SHA512: SHA512, Sign: sign}) } return times, files, nil }() diff --git a/cmd/csaf_downloader/downloader.go b/cmd/csaf_downloader/downloader.go index badf0605..025ed65c 100644 --- a/cmd/csaf_downloader/downloader.go +++ b/cmd/csaf_downloader/downloader.go @@ -501,23 +501,31 @@ nextAdvisory: signData []byte ) - // Only hash when we have a remote counter part we can compare it with. - if remoteSHA256, s256Data, err = loadHash(client, file.SHA256URL()); err != nil { - slog.Warn("Cannot fetch SHA256", - "url", file.SHA256URL(), - "error", err) + if file.SHA256URL() == "" { + slog.Info("SHA256 not present", "file", file.URL()) } else { - s256 = sha256.New() - writers = append(writers, s256) + // Only hash when we have a remote counterpart we can compare it with. + if remoteSHA256, s256Data, err = loadHash(client, file.SHA256URL()); err != nil { + slog.Warn("Cannot fetch SHA256", + "url", file.SHA256URL(), + "error", err) + } else { + s256 = sha256.New() + writers = append(writers, s256) + } } - if remoteSHA512, s512Data, err = loadHash(client, file.SHA512URL()); err != nil { - slog.Warn("Cannot fetch SHA512", - "url", file.SHA512URL(), - "error", err) + if file.SHA512URL() == "" { + slog.Info("SHA512 not present", "file", file.URL()) } else { - s512 = sha512.New() - writers = append(writers, s512) + if remoteSHA512, s512Data, err = loadHash(client, file.SHA512URL()); err != nil { + slog.Warn("Cannot fetch SHA512", + "url", file.SHA512URL(), + "error", err) + } else { + s512 = sha512.New() + writers = append(writers, s512) + } } // Remember the data as we need to store it to file later. diff --git a/csaf/advisories.go b/csaf/advisories.go index 6f07648b..4aa7f52f 100644 --- a/csaf/advisories.go +++ b/csaf/advisories.go @@ -34,55 +34,30 @@ type AdvisoryFile interface { // PlainAdvisoryFile is a simple implementation of checkFile. // The hash and signature files are directly constructed by extending // the file name. -type PlainAdvisoryFile string +type PlainAdvisoryFile struct { + Path string + SHA256 string + SHA512 string + Sign string +} // URL returns the URL of this advisory. -func (paf PlainAdvisoryFile) URL() string { return string(paf) } +func (paf PlainAdvisoryFile) URL() string { return paf.Path } // SHA256URL returns the URL of SHA256 hash file of this advisory. -func (paf PlainAdvisoryFile) SHA256URL() string { return string(paf) + ".sha256" } +func (paf PlainAdvisoryFile) SHA256URL() string { return paf.SHA256 } // SHA512URL returns the URL of SHA512 hash file of this advisory. -func (paf PlainAdvisoryFile) SHA512URL() string { return string(paf) + ".sha512" } +func (paf PlainAdvisoryFile) SHA512URL() string { return paf.SHA512 } // SignURL returns the URL of signature file of this advisory. -func (paf PlainAdvisoryFile) SignURL() string { return string(paf) + ".asc" } +func (paf PlainAdvisoryFile) SignURL() string { return paf.Sign } // LogValue implements [slog.LogValuer] func (paf PlainAdvisoryFile) LogValue() slog.Value { return slog.GroupValue(slog.String("url", paf.URL())) } -// HashedAdvisoryFile is a more involed version of checkFile. -// Here each component can be given explicitly. -// If a component is not given it is constructed by -// extending the first component. -type HashedAdvisoryFile [4]string - -func (haf HashedAdvisoryFile) name(i int, ext string) string { - if haf[i] != "" { - return haf[i] - } - return haf[0] + ext -} - -// URL returns the URL of this advisory. -func (haf HashedAdvisoryFile) URL() string { return haf[0] } - -// SHA256URL returns the URL of SHA256 hash file of this advisory. -func (haf HashedAdvisoryFile) SHA256URL() string { return haf.name(1, ".sha256") } - -// SHA512URL returns the URL of SHA512 hash file of this advisory. -func (haf HashedAdvisoryFile) SHA512URL() string { return haf.name(2, ".sha512") } - -// SignURL returns the URL of signature file of this advisory. -func (haf HashedAdvisoryFile) SignURL() string { return haf.name(3, ".asc") } - -// LogValue implements [slog.LogValuer] -func (haf HashedAdvisoryFile) LogValue() slog.Value { - return slog.GroupValue(slog.String("url", haf.URL())) -} - // AdvisoryFileProcessor implements the extraction of // advisory file names from a given provider metadata. type AdvisoryFileProcessor struct { @@ -120,7 +95,7 @@ func empty(arr []string) bool { return true } -// Process extracts the adivisory filenames and passes them with +// Process extracts the advisory filenames and passes them with // the corresponding label to fn. func (afp *AdvisoryFileProcessor) Process( fn func(TLPLabel, []AdvisoryFile) error, @@ -201,6 +176,15 @@ func (afp *AdvisoryFileProcessor) Process( return nil } +// checkURL returns the URL if it is accessible. +func (afp *AdvisoryFileProcessor) checkURL(url string) string { + _, err := afp.client.Head(url) + if err != nil { + return url + } + return "" +} + // loadChanges loads baseURL/changes.csv and returns a list of files // prefixed by baseURL/. func (afp *AdvisoryFileProcessor) loadChanges( @@ -257,8 +241,19 @@ func (afp *AdvisoryFileProcessor) loadChanges( lg("%q contains an invalid URL %q in line %d", changesURL, path, line) continue } + + self := base.JoinPath(path).String() + sha256 := afp.checkURL(self + ".sha256") + sha512 := afp.checkURL(self + ".sha512") + sign := afp.checkURL(self + ".asc") + files = append(files, - PlainAdvisoryFile(base.JoinPath(path).String())) + PlainAdvisoryFile{ + Path: path, + SHA256: sha256, + SHA512: sha512, + Sign: sign, + }) } return files, nil } @@ -325,7 +320,6 @@ func (afp *AdvisoryFileProcessor) processROLIE( } rfeed.Entries(func(entry *Entry) { - // Filter if we have date checking. if afp.AgeAccept != nil { if t := time.Time(entry.Updated); !t.IsZero() && !afp.AgeAccept(t) { @@ -359,10 +353,15 @@ func (afp *AdvisoryFileProcessor) processROLIE( var file AdvisoryFile - if sha256 != "" || sha512 != "" || sign != "" { - file = HashedAdvisoryFile{self, sha256, sha512, sign} - } else { - file = PlainAdvisoryFile(self) + switch { + case sha256 == "" && sha512 == "": + slog.Error("No hash listed on ROLIE feed", "file", self) + return + case sign == "": + slog.Error("No signature listed on ROLIE feed", "file", self) + return + default: + file = PlainAdvisoryFile{self, sha256, sha512, sign} } files = append(files, file) From be2e4e74242774d9e8bfb97f13886d9c4fa6e241 Mon Sep 17 00:00:00 2001 From: koplas <54645365+koplas@users.noreply.github.com> Date: Wed, 31 Jul 2024 11:42:45 +0200 Subject: [PATCH 02/64] Improve hash path handling of directory feeds --- cmd/csaf_checker/processor.go | 25 ++----------- cmd/csaf_downloader/downloader.go | 29 +++++++++------- csaf/advisories.go | 58 ++++++++++++++++++------------- 3 files changed, 52 insertions(+), 60 deletions(-) diff --git a/cmd/csaf_checker/processor.go b/cmd/csaf_checker/processor.go index de42e184..38f3e34b 100644 --- a/cmd/csaf_checker/processor.go +++ b/cmd/csaf_checker/processor.go @@ -895,15 +895,7 @@ func (p *processor) checkIndex(base string, mask whereType) error { continue } - SHA256 := p.checkURL(u + ".sha256") - SHA512 := p.checkURL(u + ".sha512") - sign := p.checkURL(u + ".asc") - files = append(files, csaf.PlainAdvisoryFile{ - Path: u, - SHA256: SHA256, - SHA512: SHA512, - Sign: sign, - }) + files = append(files, csaf.DirectoryAdvisoryFile{Path: u}) } return files, scanner.Err() }() @@ -921,15 +913,6 @@ func (p *processor) checkIndex(base string, mask whereType) error { return p.integrity(files, base, mask, p.badIndices.add) } -// checkURL returns the URL if it is accessible. -func (p *processor) checkURL(url string) string { - _, err := p.client.Head(url) - if err != nil { - return url - } - return "" -} - // checkChanges fetches the "changes.csv" and calls the "checkTLS" method for HTTPs checks. // It extracts the file content, tests the column number and the validity of the time format // of the fields' values and if they are sorted properly. Then it passes the files to the @@ -995,13 +978,9 @@ func (p *processor) checkChanges(base string, mask whereType) error { } path := r[pathColumn] - SHA256 := p.checkURL(path + ".sha256") - SHA512 := p.checkURL(path + ".sha512") - sign := p.checkURL(path + ".asc") - times, files = append(times, t), - append(files, csaf.PlainAdvisoryFile{Path: path, SHA256: SHA256, SHA512: SHA512, Sign: sign}) + append(files, csaf.DirectoryAdvisoryFile{Path: path}) } return times, files, nil }() diff --git a/cmd/csaf_downloader/downloader.go b/cmd/csaf_downloader/downloader.go index 025ed65c..3bf36479 100644 --- a/cmd/csaf_downloader/downloader.go +++ b/cmd/csaf_downloader/downloader.go @@ -501,31 +501,31 @@ nextAdvisory: signData []byte ) - if file.SHA256URL() == "" { - slog.Info("SHA256 not present", "file", file.URL()) - } else { - // Only hash when we have a remote counterpart we can compare it with. - if remoteSHA256, s256Data, err = loadHash(client, file.SHA256URL()); err != nil { + // Only hash when we have a remote counterpart we can compare it with. + if remoteSHA256, s256Data, err = loadHash(client, file.SHA256URL()); err != nil { + if !file.IsDirectory() { slog.Warn("Cannot fetch SHA256", "url", file.SHA256URL(), "error", err) } else { - s256 = sha256.New() - writers = append(writers, s256) + slog.Info("SHA256 not present", "file", file.URL()) } + } else { + s256 = sha256.New() + writers = append(writers, s256) } - if file.SHA512URL() == "" { - slog.Info("SHA512 not present", "file", file.URL()) - } else { - if remoteSHA512, s512Data, err = loadHash(client, file.SHA512URL()); err != nil { + if remoteSHA512, s512Data, err = loadHash(client, file.SHA512URL()); err != nil { + if !file.IsDirectory() { slog.Warn("Cannot fetch SHA512", "url", file.SHA512URL(), "error", err) } else { - s512 = sha512.New() - writers = append(writers, s512) + slog.Info("SHA512 not present", "file", file.URL()) } + } else { + s512 = sha512.New() + writers = append(writers, s512) } // Remember the data as we need to store it to file later. @@ -757,6 +757,9 @@ func loadSignature(client util.Client, p string) (*crypto.PGPSignature, []byte, } func loadHash(client util.Client, p string) ([]byte, []byte, error) { + if p == "" { + return nil, nil, fmt.Errorf("no hash path provided") + } resp, err := client.Get(p) if err != nil { return nil, nil, err diff --git a/csaf/advisories.go b/csaf/advisories.go index 4aa7f52f..d05331c2 100644 --- a/csaf/advisories.go +++ b/csaf/advisories.go @@ -29,11 +29,10 @@ type AdvisoryFile interface { SHA256URL() string SHA512URL() string SignURL() string + IsDirectory() bool } -// PlainAdvisoryFile is a simple implementation of checkFile. -// The hash and signature files are directly constructed by extending -// the file name. +// PlainAdvisoryFile contains all relevant urls of a remote file. type PlainAdvisoryFile struct { Path string SHA256 string @@ -53,11 +52,41 @@ func (paf PlainAdvisoryFile) SHA512URL() string { return paf.SHA512 } // SignURL returns the URL of signature file of this advisory. func (paf PlainAdvisoryFile) SignURL() string { return paf.Sign } +// IsDirectory returns true, if was fetched via directory feeds. +func (paf PlainAdvisoryFile) IsDirectory() bool { return false } + // LogValue implements [slog.LogValuer] func (paf PlainAdvisoryFile) LogValue() slog.Value { return slog.GroupValue(slog.String("url", paf.URL())) } +// DirectoryAdvisoryFile only contains the base file path. +// The hash and signature files are directly constructed by extending +// the file name. +type DirectoryAdvisoryFile struct { + Path string +} + +// URL returns the URL of this advisory. +func (daf DirectoryAdvisoryFile) URL() string { return daf.Path } + +// SHA256URL returns the URL of SHA256 hash file of this advisory. +func (daf DirectoryAdvisoryFile) SHA256URL() string { return daf.Path + ".sha256" } + +// SHA512URL returns the URL of SHA512 hash file of this advisory. +func (daf DirectoryAdvisoryFile) SHA512URL() string { return daf.Path + ".sha512" } + +// SignURL returns the URL of signature file of this advisory. +func (daf DirectoryAdvisoryFile) SignURL() string { return daf.Path + ".asc" } + +// IsDirectory returns true, if was fetched via directory feeds. +func (daf DirectoryAdvisoryFile) IsDirectory() bool { return true } + +// LogValue implements [slog.LogValuer] +func (daf DirectoryAdvisoryFile) LogValue() slog.Value { + return slog.GroupValue(slog.String("url", daf.URL())) +} + // AdvisoryFileProcessor implements the extraction of // advisory file names from a given provider metadata. type AdvisoryFileProcessor struct { @@ -69,7 +98,7 @@ type AdvisoryFileProcessor struct { base *url.URL } -// NewAdvisoryFileProcessor constructs an filename extractor +// NewAdvisoryFileProcessor constructs a filename extractor // for a given metadata document. func NewAdvisoryFileProcessor( client util.Client, @@ -176,15 +205,6 @@ func (afp *AdvisoryFileProcessor) Process( return nil } -// checkURL returns the URL if it is accessible. -func (afp *AdvisoryFileProcessor) checkURL(url string) string { - _, err := afp.client.Head(url) - if err != nil { - return url - } - return "" -} - // loadChanges loads baseURL/changes.csv and returns a list of files // prefixed by baseURL/. func (afp *AdvisoryFileProcessor) loadChanges( @@ -242,18 +262,8 @@ func (afp *AdvisoryFileProcessor) loadChanges( continue } - self := base.JoinPath(path).String() - sha256 := afp.checkURL(self + ".sha256") - sha512 := afp.checkURL(self + ".sha512") - sign := afp.checkURL(self + ".asc") - files = append(files, - PlainAdvisoryFile{ - Path: path, - SHA256: sha256, - SHA512: sha512, - Sign: sign, - }) + DirectoryAdvisoryFile{Path: base.JoinPath(path).String()}) } return files, nil } From 37c9eaf3467acd8e7ad08dfb3a076cf9849c67cc Mon Sep 17 00:00:00 2001 From: koplas <54645365+koplas@users.noreply.github.com> Date: Mon, 9 Sep 2024 10:25:13 +0200 Subject: [PATCH 03/64] Add CLI flags to specify what hash is preferred --- cmd/csaf_downloader/config.go | 12 +++++++- cmd/csaf_downloader/downloader.go | 50 ++++++++++++++----------------- 2 files changed, 34 insertions(+), 28 deletions(-) diff --git a/cmd/csaf_downloader/config.go b/cmd/csaf_downloader/config.go index dcfc0900..71c50556 100644 --- a/cmd/csaf_downloader/config.go +++ b/cmd/csaf_downloader/config.go @@ -41,6 +41,13 @@ const ( validationUnsafe = validationMode("unsafe") ) +type hashAlgorithm string + +const ( + algSha256 = hashAlgorithm("SHA256") + algSha2512 = hashAlgorithm("SHA512") +) + type config struct { Directory string `short:"d" long:"directory" description:"DIRectory to store the downloaded files in" value-name:"DIR" toml:"directory"` Insecure bool `long:"insecure" description:"Do not check TLS certificates from provider" toml:"insecure"` @@ -79,6 +86,9 @@ type config struct { clientCerts []tls.Certificate ignorePattern filter.PatternMatcher + + //lint:ignore SA5008 We are using choice or than once: sha256, sha512 + PreferredHash hashAlgorithm `long:"preferred_hash" short:"h" choice:"sha256" choice:"sha512" value-name:"HASH" description:"HASH to prefer" toml:"preferred_hash"` } // configPaths are the potential file locations of the config file. @@ -220,7 +230,7 @@ func (cfg *config) prepareLogging() error { w = f } ho := slog.HandlerOptions{ - //AddSource: true, + // AddSource: true, Level: cfg.LogLevel.Level, ReplaceAttr: dropSubSeconds, } diff --git a/cmd/csaf_downloader/downloader.go b/cmd/csaf_downloader/downloader.go index 3bf36479..3cb73325 100644 --- a/cmd/csaf_downloader/downloader.go +++ b/cmd/csaf_downloader/downloader.go @@ -53,7 +53,6 @@ type downloader struct { const failedValidationDir = "failed_validation" func newDownloader(cfg *config) (*downloader, error) { - var validator csaf.RemoteValidator if cfg.RemoteValidator != "" { @@ -103,7 +102,6 @@ func logRedirect(req *http.Request, via []*http.Request) error { } func (d *downloader) httpClient() util.Client { - hClient := http.Client{} if d.cfg.verbose() { @@ -253,7 +251,6 @@ func (d *downloader) downloadFiles( label csaf.TLPLabel, files []csaf.AdvisoryFile, ) error { - var ( advisoryCh = make(chan csaf.AdvisoryFile) errorCh = make(chan error) @@ -303,7 +300,6 @@ func (d *downloader) loadOpenPGPKeys( base *url.URL, expr *util.PathEval, ) error { - src, err := expr.Eval("$.public_openpgp_keys", doc) if err != nil { // no keys. @@ -357,7 +353,6 @@ func (d *downloader) loadOpenPGPKeys( defer res.Body.Close() return crypto.NewKeyFromArmoredReader(res.Body) }() - if err != nil { slog.Warn( "Reading public OpenPGP key failed", @@ -501,31 +496,35 @@ nextAdvisory: signData []byte ) - // Only hash when we have a remote counterpart we can compare it with. - if remoteSHA256, s256Data, err = loadHash(client, file.SHA256URL()); err != nil { - if !file.IsDirectory() { - slog.Warn("Cannot fetch SHA256", - "url", file.SHA256URL(), - "error", err) + if (d.cfg.PreferredHash != "sha512" || file.SHA512URL() == "") && file.SHA256URL() != "" { + // Only hash when we have a remote counterpart we can compare it with. + if remoteSHA256, s256Data, err = loadHash(client, file.SHA256URL()); err != nil { + if !file.IsDirectory() { + slog.Warn("Cannot fetch SHA256", + "url", file.SHA256URL(), + "error", err) + } else { + slog.Info("SHA256 not present", "file", file.URL()) + } } else { - slog.Info("SHA256 not present", "file", file.URL()) + s256 = sha256.New() + writers = append(writers, s256) } - } else { - s256 = sha256.New() - writers = append(writers, s256) } - if remoteSHA512, s512Data, err = loadHash(client, file.SHA512URL()); err != nil { - if !file.IsDirectory() { - slog.Warn("Cannot fetch SHA512", - "url", file.SHA512URL(), - "error", err) + if (d.cfg.PreferredHash != "sha256" || file.SHA256URL() == "") && file.SHA512URL() != "" { + if remoteSHA512, s512Data, err = loadHash(client, file.SHA512URL()); err != nil { + if !file.IsDirectory() { + slog.Warn("Cannot fetch SHA512", + "url", file.SHA512URL(), + "error", err) + } else { + slog.Info("SHA512 not present", "file", file.URL()) + } } else { - slog.Info("SHA512 not present", "file", file.URL()) + s512 = sha512.New() + writers = append(writers, s512) } - } else { - s512 = sha512.New() - writers = append(writers, s512) } // Remember the data as we need to store it to file later. @@ -757,9 +756,6 @@ func loadSignature(client util.Client, p string) (*crypto.PGPSignature, []byte, } func loadHash(client util.Client, p string) ([]byte, []byte, error) { - if p == "" { - return nil, nil, fmt.Errorf("no hash path provided") - } resp, err := client.Get(p) if err != nil { return nil, nil, err From c0de0c2b6de4d0b739badfcbe6d259739af9cffa Mon Sep 17 00:00:00 2001 From: koplas Date: Fri, 27 Sep 2024 15:20:36 +0200 Subject: [PATCH 04/64] Check if hash present, before sending a request --- cmd/csaf_checker/processor.go | 44 +++++++++++------------------------ 1 file changed, 14 insertions(+), 30 deletions(-) diff --git a/cmd/csaf_checker/processor.go b/cmd/csaf_checker/processor.go index 38f3e34b..ede8fd6b 100644 --- a/cmd/csaf_checker/processor.go +++ b/cmd/csaf_checker/processor.go @@ -84,10 +84,8 @@ type reporter interface { report(*processor, *Domain) } -var ( - // errContinue indicates that the current check should continue. - errContinue = errors.New("continue") -) +// errContinue indicates that the current check should continue. +var errContinue = errors.New("continue") type whereType byte @@ -167,7 +165,6 @@ func (m *topicMessages) hasErrors() bool { // newProcessor returns an initialized processor. func newProcessor(cfg *config) (*processor, error) { - var validator csaf.RemoteValidator if cfg.RemoteValidator != "" { @@ -240,7 +237,6 @@ func (p *processor) reset() { // Then it calls the report method on each report from the given "reporters" parameter for each domain. // It returns a pointer to the report and nil, otherwise an error. func (p *processor) run(domains []string) (*Report, error) { - report := Report{ Date: ReportTime{Time: time.Now().UTC()}, Version: util.SemVersion, @@ -297,7 +293,6 @@ func (p *processor) run(domains []string) (*Report, error) { // fillMeta fills the report with extra informations from provider metadata. func (p *processor) fillMeta(domain *Domain) error { - if p.pmd == nil { return nil } @@ -323,7 +318,6 @@ func (p *processor) fillMeta(domain *Domain) error { // domainChecks compiles a list of checks which should be performed // for a given domain. func (p *processor) domainChecks(domain string) []func(*processor, string) error { - // If we have a direct domain url we dont need to // perform certain checks. direct := strings.HasPrefix(domain, "https://") @@ -393,7 +387,6 @@ func (p *processor) markChecked(s string, mask whereType) bool { } func (p *processor) checkRedirect(r *http.Request, via []*http.Request) error { - url := r.URL.String() p.checkTLS(url) if p.redirects == nil { @@ -495,7 +488,6 @@ func (p *processor) usedAuthorizedClient() bool { // rolieFeedEntries loads the references to the advisory files for a given feed. func (p *processor) rolieFeedEntries(feed string) ([]csaf.AdvisoryFile, error) { - client := p.httpClient() res, err := client.Get(feed) p.badDirListings.use() @@ -546,7 +538,6 @@ func (p *processor) rolieFeedEntries(feed string) ([]csaf.AdvisoryFile, error) { var files []csaf.AdvisoryFile rfeed.Entries(func(entry *csaf.Entry) { - // Filter if we have date checking. if accept := p.cfg.Range; accept != nil { if t := time.Time(entry.Updated); !t.IsZero() && !accept.Contains(t) { @@ -759,14 +750,20 @@ func (p *processor) integrity( // Check hashes p.badIntegrities.use() - for _, x := range []struct { + type hash struct { ext string url func() string hash []byte - }{ - {"SHA256", f.SHA256URL, s256.Sum(nil)}, - {"SHA512", f.SHA512URL, s512.Sum(nil)}, - } { + } + hashes := []hash{} + if f.SHA256URL() != "" { + hashes = append(hashes, hash{"SHA256", f.SHA256URL, s256.Sum(nil)}) + } + if f.SHA512URL() != "" { + hashes = append(hashes, hash{"SHA512", f.SHA512URL, s512.Sum(nil)}) + } + + for _, x := range hashes { hu, err := url.Parse(x.url()) if err != nil { lg(ErrorType, "Bad URL %s: %v", x.url(), err) @@ -918,7 +915,6 @@ func (p *processor) checkIndex(base string, mask whereType) error { // of the fields' values and if they are sorted properly. Then it passes the files to the // "integrity" functions. It returns error if some test fails, otherwise nil. func (p *processor) checkChanges(base string, mask whereType) error { - bu, err := url.Parse(base) if err != nil { return err @@ -978,8 +974,7 @@ func (p *processor) checkChanges(base string, mask whereType) error { } path := r[pathColumn] - times, files = - append(times, t), + times, files = append(times, t), append(files, csaf.DirectoryAdvisoryFile{Path: path}) } return times, files, nil @@ -1152,7 +1147,6 @@ func (p *processor) checkMissing(string) error { // checkInvalid goes over all found adivisories URLs and checks // if file name conforms to standard. func (p *processor) checkInvalid(string) error { - p.badDirListings.use() var invalids []string @@ -1174,7 +1168,6 @@ func (p *processor) checkInvalid(string) error { // checkListing goes over all found adivisories URLs and checks // if their parent directory is listable. func (p *processor) checkListing(string) error { - p.badDirListings.use() pgs := pages{} @@ -1209,7 +1202,6 @@ func (p *processor) checkListing(string) error { // checkWhitePermissions checks if the TLP:WHITE advisories are // available with unprotected access. func (p *processor) checkWhitePermissions(string) error { - var ids []string for id, open := range p.labelChecker.whiteAdvisories { if !open { @@ -1235,7 +1227,6 @@ func (p *processor) checkWhitePermissions(string) error { // According to the result, the respective error messages added to // badProviderMetadata. func (p *processor) checkProviderMetadata(domain string) bool { - p.badProviderMetadata.use() client := p.httpClient() @@ -1282,7 +1273,6 @@ func (p *processor) checkSecurity(domain string, legacy bool) (int, string) { // checkSecurityFolder checks the security.txt in a given folder. func (p *processor) checkSecurityFolder(folder string) string { - client := p.httpClient() path := folder + "security.txt" res, err := client.Get(path) @@ -1349,7 +1339,6 @@ func (p *processor) checkSecurityFolder(folder string) string { // and serves the "provider-metadata.json". // It returns an empty string if all checks are passed, otherwise the errormessage. func (p *processor) checkDNS(domain string) string { - client := p.httpClient() path := "https://csaf.data.security." + domain res, err := client.Get(path) @@ -1359,7 +1348,6 @@ func (p *processor) checkDNS(domain string) string { if res.StatusCode != http.StatusOK { return fmt.Sprintf("Fetching %s failed. Status code %d (%s)", path, res.StatusCode, res.Status) - } hash := sha256.New() defer res.Body.Close() @@ -1378,7 +1366,6 @@ func (p *processor) checkDNS(domain string) string { // available under the /.well-known/csaf/ directory. Returns the errormessage if // an error was encountered, or an empty string otherwise func (p *processor) checkWellknown(domain string) string { - client := p.httpClient() path := "https://" + domain + "/.well-known/csaf/provider-metadata.json" @@ -1408,7 +1395,6 @@ func (p *processor) checkWellknown(domain string) string { // The function returns nil, unless errors outside the checks were found. // In that case, errors are returned. func (p *processor) checkWellknownSecurityDNS(domain string) error { - warningsW := p.checkWellknown(domain) // Security check for well known (default) and legacy location warningsS, sDMessage := p.checkSecurity(domain, false) @@ -1461,7 +1447,6 @@ func (p *processor) checkWellknownSecurityDNS(domain string) error { // As a result of these a respective error messages are passed to badPGP method // in case of errors. It returns nil if all checks are passed. func (p *processor) checkPGPKeys(_ string) error { - p.badPGPs.use() src, err := p.expr.Eval("$.public_openpgp_keys", p.pmd) @@ -1520,7 +1505,6 @@ func (p *processor) checkPGPKeys(_ string) error { defer res.Body.Close() return crypto.NewKeyFromArmoredReader(res.Body) }() - if err != nil { p.badPGPs.error("Reading public OpenPGP key %s failed: %v", u, err) continue From f7dc3f5ec74ea8ccada62f64a15cd9d6f9fd8b72 Mon Sep 17 00:00:00 2001 From: Paul Schwabauer Date: Sun, 29 Sep 2024 09:08:01 +0200 Subject: [PATCH 05/64] Use .test TLD for integration setup (#577) .local is reserved for local-area networks, and .localhost is reserved for loopback devices. Using .test allows easier usage for different test setups. * https://www.rfc-editor.org/rfc/rfc2606#section-2 defines the "test." top level domain and "localhost.". * https://www.rfc-editor.org/rfc/rfc6761.html#section-6.2 explains how different implementations can use "test.". --- docs/development-ca.md | 2 +- docs/scripts/setupProviderForITest.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/development-ca.md b/docs/development-ca.md index 483732c3..21f4ef4f 100644 --- a/docs/development-ca.md +++ b/docs/development-ca.md @@ -55,7 +55,7 @@ signing_key encryption_key non_repudiation -dns_name = "*.local" +dns_name = "*.test" dns_name = "localhost" serial = 010 diff --git a/docs/scripts/setupProviderForITest.sh b/docs/scripts/setupProviderForITest.sh index 1a57f1e1..f9d7d184 100755 --- a/docs/scripts/setupProviderForITest.sh +++ b/docs/scripts/setupProviderForITest.sh @@ -17,7 +17,7 @@ sudo chgrp -R www-data /var/www sudo chmod -R g+ws /var/www export NGINX_CONFIG_PATH=/etc/nginx/sites-available/default -export DNS_NAME=csaf.data.security.localhost +export DNS_NAME=csaf.data.security.test sudo cp /usr/share/doc/fcgiwrap/examples/nginx.conf /etc/nginx/fcgiwrap.conf From 18e2e35e7cf0d92d463eaad736074c5c9d43165b Mon Sep 17 00:00:00 2001 From: "Bernhard E. Reiter" Date: Tue, 29 Oct 2024 09:49:27 +0100 Subject: [PATCH 06/64] Update README.md with link update alert --- README.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/README.md b/README.md index 14ac64f6..19538543 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,13 @@ Software-Engineering: 2024 Intevation GmbH --> + +> [!IMPORTANT] +> Adjust your links and go module paths if you still have `csaf-poc` in them, to avoid future breakage. +> (This repository was moved here on 2024-10-28. The old one is decrecated +> and redirection will be switched off in a few months.) + + # csaf_distribution Implements a [CSAF](https://csaf.io/) @@ -16,6 +23,7 @@ and its [errata](https://docs.oasis-open.org/csaf/csaf/v2.0/csaf-v2.0.html)) trusted provider, checker, aggregator and downloader. Includes an uploader command line tool for the trusted provider. + ## Tools for users ### [csaf_downloader](docs/csaf_downloader.md) is a tool for downloading advisories from a provider. From bdd8aa0a9415da3641cf2624ac0f57381e16b9b2 Mon Sep 17 00:00:00 2001 From: "Bernhard E. Reiter" Date: Tue, 29 Oct 2024 09:50:26 +0100 Subject: [PATCH 07/64] Update README.md --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 19538543..e6ea77ff 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,6 @@ and its [errata](https://docs.oasis-open.org/csaf/csaf/v2.0/csaf-v2.0.html)) trusted provider, checker, aggregator and downloader. Includes an uploader command line tool for the trusted provider. - ## Tools for users ### [csaf_downloader](docs/csaf_downloader.md) is a tool for downloading advisories from a provider. From bf057e2fa8f25e155bb616ebe98523c0f76e5148 Mon Sep 17 00:00:00 2001 From: "Bernhard E. Reiter" Date: Wed, 30 Oct 2024 10:51:38 +0100 Subject: [PATCH 08/64] Update repo move alert in README.md HTML links can be adjusted right now, go module paths will have to wait a bit. --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e6ea77ff..53920d81 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,9 @@ > [!IMPORTANT] -> Adjust your links and go module paths if you still have `csaf-poc` in them, to avoid future breakage. +> To avoid future breakage, if you still use `csaf-poc`: +> 1. Adjust your HTML links. +> 2. Prepare to adjust your go module paths once https://github.com/gocsaf/csaf/issues/579 is solved. > (This repository was moved here on 2024-10-28. The old one is decrecated > and redirection will be switched off in a few months.) From 6ebe7f5f5d3845cc1c9fa26d209de12b4870150a Mon Sep 17 00:00:00 2001 From: "Bernhard E. Reiter" Date: Wed, 30 Oct 2024 10:53:15 +0100 Subject: [PATCH 09/64] Update repo move alert in README.md use a better phrasing --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 53920d81..f28567e1 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ > 1. Adjust your HTML links. > 2. Prepare to adjust your go module paths once https://github.com/gocsaf/csaf/issues/579 is solved. > (This repository was moved here on 2024-10-28. The old one is decrecated -> and redirection will be switched off in a few months.) +> and redirection will be switched off a few months later.) # csaf_distribution From 7aa95c03ca1f5a19914cce0158fb3212cab80d19 Mon Sep 17 00:00:00 2001 From: "Sascha L. Teichmann" Date: Wed, 30 Oct 2024 11:03:18 +0100 Subject: [PATCH 10/64] fix: bring aggregator schema to errata01 (#583) --- csaf/schema/aggregator_json_schema.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/csaf/schema/aggregator_json_schema.json b/csaf/schema/aggregator_json_schema.json index 7929f1ff..cdad1094 100644 --- a/csaf/schema/aggregator_json_schema.json +++ b/csaf/schema/aggregator_json_schema.json @@ -175,7 +175,7 @@ "type": "object", "required": [ "metadata", - "mirror", + "mirrors", "update_interval" ], "properties": { From 1aad5331d2d8d992467e8b5694c43f53dae2d22b Mon Sep 17 00:00:00 2001 From: "Bernhard E. Reiter" Date: Wed, 30 Oct 2024 11:15:31 +0100 Subject: [PATCH 11/64] Update README.md reformat a bit --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index f28567e1..8bdfd889 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ > To avoid future breakage, if you still use `csaf-poc`: > 1. Adjust your HTML links. > 2. Prepare to adjust your go module paths once https://github.com/gocsaf/csaf/issues/579 is solved. +> > (This repository was moved here on 2024-10-28. The old one is decrecated > and redirection will be switched off a few months later.) From 1c860a1ab21692f176ecc033fc484dcebc9f5728 Mon Sep 17 00:00:00 2001 From: JanHoefelmeyer <107021473+JanHoefelmeyer@users.noreply.github.com> Date: Wed, 30 Oct 2024 11:22:24 +0100 Subject: [PATCH 12/64] Update README.md: Fix: typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 8bdfd889..568bf03b 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ > 1. Adjust your HTML links. > 2. Prepare to adjust your go module paths once https://github.com/gocsaf/csaf/issues/579 is solved. > -> (This repository was moved here on 2024-10-28. The old one is decrecated +> (This repository was moved here on 2024-10-28. The old one is deprecated > and redirection will be switched off a few months later.) From ffadad38c6cc9aa9b29af2489ea4487d676e0f34 Mon Sep 17 00:00:00 2001 From: Bernhard Reiter Date: Wed, 30 Oct 2024 15:53:22 +0100 Subject: [PATCH 13/64] improve test setupscript by adding missing zip Add zip as packages to be installed in preparation as the `make dist` target uses it. --- docs/scripts/prepareUbuntuInstanceForITests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/scripts/prepareUbuntuInstanceForITests.sh b/docs/scripts/prepareUbuntuInstanceForITests.sh index ea88fc42..f1240449 100755 --- a/docs/scripts/prepareUbuntuInstanceForITests.sh +++ b/docs/scripts/prepareUbuntuInstanceForITests.sh @@ -6,7 +6,7 @@ set -e # by installing the required packages. apt update -apt install -y make bash curl gnupg sed tar git nginx fcgiwrap gnutls-bin +apt install -y make bash curl gnupg sed tar git nginx fcgiwrap gnutls-bin zip # Install Go from binary distribution latest_go="$(curl https://go.dev/VERSION\?m=text| head -1).linux-amd64.tar.gz" From e8706e5eb99d40f464587c6d3aba2e2484a3fd6a Mon Sep 17 00:00:00 2001 From: Bernhard Reiter Date: Wed, 30 Oct 2024 14:46:50 +0100 Subject: [PATCH 14/64] feat: perform go path repo move * Change the go module path from github.com/csaf-poc/csaf_distribution to github.com/gocsaf/csaf. * Rename archive for release tarballs. * Adjust testing scripts and documentation. --- .github/workflows/itest.yml | 2 +- .github/workflows/release.yml | 4 ++-- Makefile | 6 +++--- README.md | 10 +++++----- cmd/csaf_aggregator/client.go | 2 +- cmd/csaf_aggregator/config.go | 12 ++++++------ cmd/csaf_aggregator/full.go | 4 ++-- cmd/csaf_aggregator/indices.go | 4 ++-- cmd/csaf_aggregator/interim.go | 4 ++-- cmd/csaf_aggregator/lazytransaction.go | 2 +- cmd/csaf_aggregator/lister.go | 4 ++-- cmd/csaf_aggregator/main.go | 2 +- cmd/csaf_aggregator/mirror.go | 4 ++-- cmd/csaf_aggregator/processor.go | 4 ++-- cmd/csaf_checker/config.go | 8 ++++---- cmd/csaf_checker/links.go | 2 +- cmd/csaf_checker/main.go | 2 +- cmd/csaf_checker/processor.go | 4 ++-- cmd/csaf_checker/report.go | 4 ++-- cmd/csaf_checker/reporters.go | 2 +- cmd/csaf_checker/roliecheck.go | 4 ++-- cmd/csaf_checker/rules.go | 2 +- cmd/csaf_downloader/config.go | 8 ++++---- cmd/csaf_downloader/downloader.go | 4 ++-- cmd/csaf_downloader/forwarder.go | 4 ++-- cmd/csaf_downloader/forwarder_test.go | 4 ++-- cmd/csaf_downloader/main.go | 2 +- cmd/csaf_provider/actions.go | 4 ++-- cmd/csaf_provider/config.go | 2 +- cmd/csaf_provider/create.go | 4 ++-- cmd/csaf_provider/files.go | 2 +- cmd/csaf_provider/indices.go | 2 +- cmd/csaf_provider/main.go | 2 +- cmd/csaf_provider/rolie.go | 4 ++-- cmd/csaf_provider/transaction.go | 4 ++-- cmd/csaf_uploader/config.go | 4 ++-- cmd/csaf_uploader/main.go | 2 +- cmd/csaf_uploader/processor.go | 6 +++--- cmd/csaf_validator/main.go | 4 ++-- csaf/advisories.go | 2 +- csaf/models.go | 2 +- csaf/providermetaloader.go | 2 +- csaf/rolie.go | 2 +- csaf/summary.go | 2 +- docs/csaf_checker.md | 2 +- docs/csaf_provider.md | 4 ++-- docs/provider-setup.md | 2 +- docs/scripts/Readme.md | 8 ++++---- docs/scripts/TLSClientConfigsForITest.sh | 2 +- docs/scripts/TLSConfigsForITest.sh | 2 +- docs/scripts/prepareUbuntuInstanceForITests.sh | 2 +- docs/scripts/testAggregator.sh | 2 +- docs/scripts/testChecker.sh | 2 +- docs/scripts/testDownloader.sh | 2 +- examples/README.md | 2 +- examples/purls_searcher/main.go | 6 +++--- go.mod | 2 +- internal/options/options.go | 2 +- 58 files changed, 102 insertions(+), 102 deletions(-) diff --git a/.github/workflows/itest.yml b/.github/workflows/itest.yml index 364c330c..9cc4c6b6 100644 --- a/.github/workflows/itest.yml +++ b/.github/workflows/itest.yml @@ -25,7 +25,7 @@ jobs: sudo apt install -y make nginx fcgiwrap gnutls-bin cp -r $GITHUB_WORKSPACE ~ cd ~ - cd csaf_distribution/docs/scripts/ + cd csaf/docs/scripts/ # keep in sync with docs/scripts/Readme.md export FOLDERNAME=devca1 ORGANAME="CSAF Tools Development (internal)" source ./TLSConfigsForITest.sh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 739f45c1..4bcd6bab 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -24,5 +24,5 @@ jobs: uses: softprops/action-gh-release@v1 with: files: | - dist/csaf_distribution-*.zip - dist/csaf_distribution-*.tar.gz + dist/csaf-*.zip + dist/csaf-*.tar.gz diff --git a/Makefile b/Makefile index b4b3964b..083d3b66 100644 --- a/Makefile +++ b/Makefile @@ -6,7 +6,7 @@ # SPDX-FileCopyrightText: 2021 German Federal Office for Information Security (BSI) # Software-Engineering: 2021 Intevation GmbH # -# Makefile to build csaf_distribution components +# Makefile to build csaf components SHELL = /bin/bash BUILD = go build @@ -59,7 +59,7 @@ testsemver: # Set -ldflags parameter to pass the semversion. -LDFLAGS = -ldflags "-X github.com/csaf-poc/csaf_distribution/v3/util.SemVersion=$(SEMVER)" +LDFLAGS = -ldflags "-X github.com/gocsaf/csaf/v3/util.SemVersion=$(SEMVER)" # Build binaries and place them under bin-$(GOOS)-$(GOARCH) # Using 'Target-specific Variable Values' to specify the build target system @@ -78,7 +78,7 @@ build_linux build_win build_mac_amd64 build_mac_arm64: env GOARCH=$(GOARCH) GOOS=$(GOOS) $(BUILD) -o $(BINDIR) $(LDFLAGS) -v ./cmd/... -DISTDIR := csaf_distribution-$(SEMVER) +DISTDIR := csaf-$(SEMVER) dist: build_linux build_win build_mac_amd64 build_mac_arm64 mkdir -p dist mkdir -p dist/$(DISTDIR)-windows-amd64/bin-windows-amd64 diff --git a/README.md b/README.md index 568bf03b..cec92483 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ > and redirection will be switched off a few months later.) -# csaf_distribution +# csaf Implements a [CSAF](https://csaf.io/) ([specification v2.0](https://docs.oasis-open.org/csaf/csaf/v2.0/os/csaf-v2.0-os.html) @@ -52,10 +52,10 @@ is a CSAF Aggregator, to list or mirror providers. ## Other stuff ### [examples](./examples/README.md) -are small examples of how to use `github.com/csaf-poc/csaf_distribution` +are small examples of how to use `github.com/gocsaf/csaf` as an API. Currently this is a work in progress, as usage of this repository as a library to access is _not officially supported_, e.g. -see https://github.com/csaf-poc/csaf_distribution/issues/367 . +see https://github.com/gocsaf/csaf/issues/367 . ## Setup Binaries for the server side are only available and tested @@ -81,7 +81,7 @@ Download the binaries from the most recent release assets on Github. - A recent version of **Go** (1.22+) should be installed. [Go installation](https://go.dev/doc/install) -- Clone the repository `git clone https://github.com/csaf-poc/csaf_distribution.git ` +- Clone the repository `git clone https://github.com/gocsaf/csaf.git ` - Build Go components Makefile supplies the following targets: - Build for GNU/Linux system: `make build_linux` @@ -110,7 +110,7 @@ For further details of the development process consult our [development page](./ ## License -- `csaf_distribution` is licensed as Free Software under the terms of the [Apache License, Version 2.0](./LICENSES/Apache-2.0.txt). +- `csaf` is licensed as Free Software under the terms of the [Apache License, Version 2.0](./LICENSES/Apache-2.0.txt). - See the specific source files for details, the license itself can be found in the directory `LICENSES/`. diff --git a/cmd/csaf_aggregator/client.go b/cmd/csaf_aggregator/client.go index 8200d34e..916baa5b 100644 --- a/cmd/csaf_aggregator/client.go +++ b/cmd/csaf_aggregator/client.go @@ -13,7 +13,7 @@ import ( "io" "net/http" - "github.com/csaf-poc/csaf_distribution/v3/util" + "github.com/gocsaf/csaf/v3/util" ) var errNotFound = errors.New("not found") diff --git a/cmd/csaf_aggregator/config.go b/cmd/csaf_aggregator/config.go index b73286c3..81db0b7c 100644 --- a/cmd/csaf_aggregator/config.go +++ b/cmd/csaf_aggregator/config.go @@ -20,12 +20,12 @@ import ( "time" "github.com/ProtonMail/gopenpgp/v2/crypto" - "github.com/csaf-poc/csaf_distribution/v3/csaf" - "github.com/csaf-poc/csaf_distribution/v3/internal/certs" - "github.com/csaf-poc/csaf_distribution/v3/internal/filter" - "github.com/csaf-poc/csaf_distribution/v3/internal/models" - "github.com/csaf-poc/csaf_distribution/v3/internal/options" - "github.com/csaf-poc/csaf_distribution/v3/util" + "github.com/gocsaf/csaf/v3/csaf" + "github.com/gocsaf/csaf/v3/internal/certs" + "github.com/gocsaf/csaf/v3/internal/filter" + "github.com/gocsaf/csaf/v3/internal/models" + "github.com/gocsaf/csaf/v3/internal/options" + "github.com/gocsaf/csaf/v3/util" "golang.org/x/time/rate" ) diff --git a/cmd/csaf_aggregator/full.go b/cmd/csaf_aggregator/full.go index 9ec9812a..e71d7b65 100644 --- a/cmd/csaf_aggregator/full.go +++ b/cmd/csaf_aggregator/full.go @@ -18,8 +18,8 @@ import ( "sync" "time" - "github.com/csaf-poc/csaf_distribution/v3/csaf" - "github.com/csaf-poc/csaf_distribution/v3/util" + "github.com/gocsaf/csaf/v3/csaf" + "github.com/gocsaf/csaf/v3/util" ) type fullJob struct { diff --git a/cmd/csaf_aggregator/indices.go b/cmd/csaf_aggregator/indices.go index 272d25b4..17c8d3a1 100644 --- a/cmd/csaf_aggregator/indices.go +++ b/cmd/csaf_aggregator/indices.go @@ -19,8 +19,8 @@ import ( "strings" "time" - "github.com/csaf-poc/csaf_distribution/v3/csaf" - "github.com/csaf-poc/csaf_distribution/v3/util" + "github.com/gocsaf/csaf/v3/csaf" + "github.com/gocsaf/csaf/v3/util" ) const ( diff --git a/cmd/csaf_aggregator/interim.go b/cmd/csaf_aggregator/interim.go index 023c9c42..94147bcb 100644 --- a/cmd/csaf_aggregator/interim.go +++ b/cmd/csaf_aggregator/interim.go @@ -24,8 +24,8 @@ import ( "sync" "time" - "github.com/csaf-poc/csaf_distribution/v3/csaf" - "github.com/csaf-poc/csaf_distribution/v3/util" + "github.com/gocsaf/csaf/v3/csaf" + "github.com/gocsaf/csaf/v3/util" ) type interimJob struct { diff --git a/cmd/csaf_aggregator/lazytransaction.go b/cmd/csaf_aggregator/lazytransaction.go index 606d892f..af36ee29 100644 --- a/cmd/csaf_aggregator/lazytransaction.go +++ b/cmd/csaf_aggregator/lazytransaction.go @@ -13,7 +13,7 @@ import ( "os" "path/filepath" - "github.com/csaf-poc/csaf_distribution/v3/util" + "github.com/gocsaf/csaf/v3/util" ) type lazyTransaction struct { diff --git a/cmd/csaf_aggregator/lister.go b/cmd/csaf_aggregator/lister.go index 4d758e49..7e1fb582 100644 --- a/cmd/csaf_aggregator/lister.go +++ b/cmd/csaf_aggregator/lister.go @@ -11,8 +11,8 @@ package main import ( "fmt" - "github.com/csaf-poc/csaf_distribution/v3/csaf" - "github.com/csaf-poc/csaf_distribution/v3/util" + "github.com/gocsaf/csaf/v3/csaf" + "github.com/gocsaf/csaf/v3/util" ) // mirrorAllowed checks if mirroring is allowed. diff --git a/cmd/csaf_aggregator/main.go b/cmd/csaf_aggregator/main.go index 39c10518..2056e84d 100644 --- a/cmd/csaf_aggregator/main.go +++ b/cmd/csaf_aggregator/main.go @@ -15,7 +15,7 @@ import ( "os" "path/filepath" - "github.com/csaf-poc/csaf_distribution/v3/internal/options" + "github.com/gocsaf/csaf/v3/internal/options" "github.com/gofrs/flock" ) diff --git a/cmd/csaf_aggregator/mirror.go b/cmd/csaf_aggregator/mirror.go index 6bf72a33..c90ef683 100644 --- a/cmd/csaf_aggregator/mirror.go +++ b/cmd/csaf_aggregator/mirror.go @@ -30,8 +30,8 @@ import ( "github.com/ProtonMail/gopenpgp/v2/constants" "github.com/ProtonMail/gopenpgp/v2/crypto" - "github.com/csaf-poc/csaf_distribution/v3/csaf" - "github.com/csaf-poc/csaf_distribution/v3/util" + "github.com/gocsaf/csaf/v3/csaf" + "github.com/gocsaf/csaf/v3/util" ) // mirrorAllowed checks if mirroring is allowed. diff --git a/cmd/csaf_aggregator/processor.go b/cmd/csaf_aggregator/processor.go index 5cb36283..b22e8396 100644 --- a/cmd/csaf_aggregator/processor.go +++ b/cmd/csaf_aggregator/processor.go @@ -14,8 +14,8 @@ import ( "os" "path/filepath" - "github.com/csaf-poc/csaf_distribution/v3/csaf" - "github.com/csaf-poc/csaf_distribution/v3/util" + "github.com/gocsaf/csaf/v3/csaf" + "github.com/gocsaf/csaf/v3/util" "github.com/ProtonMail/gopenpgp/v2/crypto" ) diff --git a/cmd/csaf_checker/config.go b/cmd/csaf_checker/config.go index ac9ce62b..3ea18403 100644 --- a/cmd/csaf_checker/config.go +++ b/cmd/csaf_checker/config.go @@ -13,10 +13,10 @@ import ( "fmt" "net/http" - "github.com/csaf-poc/csaf_distribution/v3/internal/certs" - "github.com/csaf-poc/csaf_distribution/v3/internal/filter" - "github.com/csaf-poc/csaf_distribution/v3/internal/models" - "github.com/csaf-poc/csaf_distribution/v3/internal/options" + "github.com/gocsaf/csaf/v3/internal/certs" + "github.com/gocsaf/csaf/v3/internal/filter" + "github.com/gocsaf/csaf/v3/internal/models" + "github.com/gocsaf/csaf/v3/internal/options" ) type outputFormat string diff --git a/cmd/csaf_checker/links.go b/cmd/csaf_checker/links.go index 0456acea..a3236614 100644 --- a/cmd/csaf_checker/links.go +++ b/cmd/csaf_checker/links.go @@ -15,7 +15,7 @@ import ( "github.com/PuerkitoBio/goquery" - "github.com/csaf-poc/csaf_distribution/v3/util" + "github.com/gocsaf/csaf/v3/util" ) type ( diff --git a/cmd/csaf_checker/main.go b/cmd/csaf_checker/main.go index 752fdf8e..4efb3516 100644 --- a/cmd/csaf_checker/main.go +++ b/cmd/csaf_checker/main.go @@ -12,7 +12,7 @@ package main import ( "log" - "github.com/csaf-poc/csaf_distribution/v3/internal/options" + "github.com/gocsaf/csaf/v3/internal/options" ) // run uses a processor to check all the given domains or direct urls diff --git a/cmd/csaf_checker/processor.go b/cmd/csaf_checker/processor.go index da4214b9..5fd3fbdd 100644 --- a/cmd/csaf_checker/processor.go +++ b/cmd/csaf_checker/processor.go @@ -32,8 +32,8 @@ import ( "github.com/ProtonMail/gopenpgp/v2/crypto" "golang.org/x/time/rate" - "github.com/csaf-poc/csaf_distribution/v3/csaf" - "github.com/csaf-poc/csaf_distribution/v3/util" + "github.com/gocsaf/csaf/v3/csaf" + "github.com/gocsaf/csaf/v3/util" ) // topicMessages stores the collected topicMessages for a specific topic. diff --git a/cmd/csaf_checker/report.go b/cmd/csaf_checker/report.go index 9b5251b0..58ed25a5 100644 --- a/cmd/csaf_checker/report.go +++ b/cmd/csaf_checker/report.go @@ -18,8 +18,8 @@ import ( "os" "time" - "github.com/csaf-poc/csaf_distribution/v3/csaf" - "github.com/csaf-poc/csaf_distribution/v3/internal/models" + "github.com/gocsaf/csaf/v3/csaf" + "github.com/gocsaf/csaf/v3/internal/models" ) // MessageType is the kind of the message. diff --git a/cmd/csaf_checker/reporters.go b/cmd/csaf_checker/reporters.go index 016d3713..157eabee 100644 --- a/cmd/csaf_checker/reporters.go +++ b/cmd/csaf_checker/reporters.go @@ -13,7 +13,7 @@ import ( "sort" "strings" - "github.com/csaf-poc/csaf_distribution/v3/util" + "github.com/gocsaf/csaf/v3/util" ) type ( diff --git a/cmd/csaf_checker/roliecheck.go b/cmd/csaf_checker/roliecheck.go index 53d11500..28bd4379 100644 --- a/cmd/csaf_checker/roliecheck.go +++ b/cmd/csaf_checker/roliecheck.go @@ -15,8 +15,8 @@ import ( "sort" "strings" - "github.com/csaf-poc/csaf_distribution/v3/csaf" - "github.com/csaf-poc/csaf_distribution/v3/util" + "github.com/gocsaf/csaf/v3/csaf" + "github.com/gocsaf/csaf/v3/util" ) // identifier consist of document/tracking/id and document/publisher/namespace, diff --git a/cmd/csaf_checker/rules.go b/cmd/csaf_checker/rules.go index eadbbb24..e04388dc 100644 --- a/cmd/csaf_checker/rules.go +++ b/cmd/csaf_checker/rules.go @@ -12,7 +12,7 @@ import ( "fmt" "sort" - "github.com/csaf-poc/csaf_distribution/v3/csaf" + "github.com/gocsaf/csaf/v3/csaf" ) type ruleCondition int diff --git a/cmd/csaf_downloader/config.go b/cmd/csaf_downloader/config.go index dcfc0900..33f8dc2d 100644 --- a/cmd/csaf_downloader/config.go +++ b/cmd/csaf_downloader/config.go @@ -19,10 +19,10 @@ import ( "path/filepath" "time" - "github.com/csaf-poc/csaf_distribution/v3/internal/certs" - "github.com/csaf-poc/csaf_distribution/v3/internal/filter" - "github.com/csaf-poc/csaf_distribution/v3/internal/models" - "github.com/csaf-poc/csaf_distribution/v3/internal/options" + "github.com/gocsaf/csaf/v3/internal/certs" + "github.com/gocsaf/csaf/v3/internal/filter" + "github.com/gocsaf/csaf/v3/internal/models" + "github.com/gocsaf/csaf/v3/internal/options" ) const ( diff --git a/cmd/csaf_downloader/downloader.go b/cmd/csaf_downloader/downloader.go index fde4cd3a..f21fcc0d 100644 --- a/cmd/csaf_downloader/downloader.go +++ b/cmd/csaf_downloader/downloader.go @@ -33,8 +33,8 @@ import ( "github.com/ProtonMail/gopenpgp/v2/crypto" "golang.org/x/time/rate" - "github.com/csaf-poc/csaf_distribution/v3/csaf" - "github.com/csaf-poc/csaf_distribution/v3/util" + "github.com/gocsaf/csaf/v3/csaf" + "github.com/gocsaf/csaf/v3/util" ) type downloader struct { diff --git a/cmd/csaf_downloader/forwarder.go b/cmd/csaf_downloader/forwarder.go index c3681eba..12d9fe4a 100644 --- a/cmd/csaf_downloader/forwarder.go +++ b/cmd/csaf_downloader/forwarder.go @@ -19,8 +19,8 @@ import ( "path/filepath" "strings" - "github.com/csaf-poc/csaf_distribution/v3/internal/misc" - "github.com/csaf-poc/csaf_distribution/v3/util" + "github.com/gocsaf/csaf/v3/internal/misc" + "github.com/gocsaf/csaf/v3/util" ) // failedForwardDir is the name of the special sub folder diff --git a/cmd/csaf_downloader/forwarder_test.go b/cmd/csaf_downloader/forwarder_test.go index 907bbcea..25f0f1ff 100644 --- a/cmd/csaf_downloader/forwarder_test.go +++ b/cmd/csaf_downloader/forwarder_test.go @@ -23,8 +23,8 @@ import ( "strings" "testing" - "github.com/csaf-poc/csaf_distribution/v3/internal/options" - "github.com/csaf-poc/csaf_distribution/v3/util" + "github.com/gocsaf/csaf/v3/internal/options" + "github.com/gocsaf/csaf/v3/util" ) func TestValidationStatusUpdate(t *testing.T) { diff --git a/cmd/csaf_downloader/main.go b/cmd/csaf_downloader/main.go index cc284bbc..fe6efd17 100644 --- a/cmd/csaf_downloader/main.go +++ b/cmd/csaf_downloader/main.go @@ -15,7 +15,7 @@ import ( "os" "os/signal" - "github.com/csaf-poc/csaf_distribution/v3/internal/options" + "github.com/gocsaf/csaf/v3/internal/options" ) func run(cfg *config, domains []string) error { diff --git a/cmd/csaf_provider/actions.go b/cmd/csaf_provider/actions.go index 8f385e6e..18629832 100644 --- a/cmd/csaf_provider/actions.go +++ b/cmd/csaf_provider/actions.go @@ -26,8 +26,8 @@ import ( "github.com/ProtonMail/gopenpgp/v2/constants" "github.com/ProtonMail/gopenpgp/v2/crypto" - "github.com/csaf-poc/csaf_distribution/v3/csaf" - "github.com/csaf-poc/csaf_distribution/v3/util" + "github.com/gocsaf/csaf/v3/csaf" + "github.com/gocsaf/csaf/v3/util" ) const dateFormat = time.RFC3339 diff --git a/cmd/csaf_provider/config.go b/cmd/csaf_provider/config.go index 49a72047..826b7bf2 100644 --- a/cmd/csaf_provider/config.go +++ b/cmd/csaf_provider/config.go @@ -18,7 +18,7 @@ import ( "github.com/ProtonMail/gopenpgp/v2/crypto" "golang.org/x/crypto/bcrypt" - "github.com/csaf-poc/csaf_distribution/v3/csaf" + "github.com/gocsaf/csaf/v3/csaf" ) const ( diff --git a/cmd/csaf_provider/create.go b/cmd/csaf_provider/create.go index 56893c69..11e0b7c5 100644 --- a/cmd/csaf_provider/create.go +++ b/cmd/csaf_provider/create.go @@ -22,8 +22,8 @@ import ( "github.com/ProtonMail/gopenpgp/v2/crypto" - "github.com/csaf-poc/csaf_distribution/v3/csaf" - "github.com/csaf-poc/csaf_distribution/v3/util" + "github.com/gocsaf/csaf/v3/csaf" + "github.com/gocsaf/csaf/v3/util" ) // ensureFolders initializes the paths and call functions to create diff --git a/cmd/csaf_provider/files.go b/cmd/csaf_provider/files.go index 39a97e36..3b99ff5b 100644 --- a/cmd/csaf_provider/files.go +++ b/cmd/csaf_provider/files.go @@ -13,7 +13,7 @@ import ( "crypto/sha512" "os" - "github.com/csaf-poc/csaf_distribution/v3/util" + "github.com/gocsaf/csaf/v3/util" ) func writeHashedFile(fname, name string, data []byte, armored string) error { diff --git a/cmd/csaf_provider/indices.go b/cmd/csaf_provider/indices.go index 805371bb..a4eb97a6 100644 --- a/cmd/csaf_provider/indices.go +++ b/cmd/csaf_provider/indices.go @@ -18,7 +18,7 @@ import ( "sort" "time" - "github.com/csaf-poc/csaf_distribution/v3/util" + "github.com/gocsaf/csaf/v3/util" ) func updateIndex(dir, fname string) error { diff --git a/cmd/csaf_provider/main.go b/cmd/csaf_provider/main.go index 8740e814..6c858c9f 100644 --- a/cmd/csaf_provider/main.go +++ b/cmd/csaf_provider/main.go @@ -18,7 +18,7 @@ import ( "github.com/jessevdk/go-flags" - "github.com/csaf-poc/csaf_distribution/v3/util" + "github.com/gocsaf/csaf/v3/util" ) type options struct { diff --git a/cmd/csaf_provider/rolie.go b/cmd/csaf_provider/rolie.go index 98448bd3..d9717b14 100644 --- a/cmd/csaf_provider/rolie.go +++ b/cmd/csaf_provider/rolie.go @@ -15,8 +15,8 @@ import ( "strings" "time" - "github.com/csaf-poc/csaf_distribution/v3/csaf" - "github.com/csaf-poc/csaf_distribution/v3/util" + "github.com/gocsaf/csaf/v3/csaf" + "github.com/gocsaf/csaf/v3/util" ) // mergeCategories merges the given categories into the old ones. diff --git a/cmd/csaf_provider/transaction.go b/cmd/csaf_provider/transaction.go index 1b66ae0a..c4c93a80 100644 --- a/cmd/csaf_provider/transaction.go +++ b/cmd/csaf_provider/transaction.go @@ -12,8 +12,8 @@ import ( "os" "path/filepath" - "github.com/csaf-poc/csaf_distribution/v3/csaf" - "github.com/csaf-poc/csaf_distribution/v3/util" + "github.com/gocsaf/csaf/v3/csaf" + "github.com/gocsaf/csaf/v3/util" ) func doTransaction( diff --git a/cmd/csaf_uploader/config.go b/cmd/csaf_uploader/config.go index a83361c7..ceecff78 100644 --- a/cmd/csaf_uploader/config.go +++ b/cmd/csaf_uploader/config.go @@ -18,8 +18,8 @@ import ( "golang.org/x/crypto/bcrypt" "golang.org/x/term" - "github.com/csaf-poc/csaf_distribution/v3/internal/certs" - "github.com/csaf-poc/csaf_distribution/v3/internal/options" + "github.com/gocsaf/csaf/v3/internal/certs" + "github.com/gocsaf/csaf/v3/internal/options" ) const ( diff --git a/cmd/csaf_uploader/main.go b/cmd/csaf_uploader/main.go index 20f89fdf..db1cef40 100644 --- a/cmd/csaf_uploader/main.go +++ b/cmd/csaf_uploader/main.go @@ -9,7 +9,7 @@ // Implements a command line tool that uploads csaf documents to csaf_provider. package main -import "github.com/csaf-poc/csaf_distribution/v3/internal/options" +import "github.com/gocsaf/csaf/v3/internal/options" func main() { args, cfg, err := parseArgsConfig() diff --git a/cmd/csaf_uploader/processor.go b/cmd/csaf_uploader/processor.go index 45988652..b57cafb9 100644 --- a/cmd/csaf_uploader/processor.go +++ b/cmd/csaf_uploader/processor.go @@ -26,9 +26,9 @@ import ( "github.com/ProtonMail/gopenpgp/v2/constants" "github.com/ProtonMail/gopenpgp/v2/crypto" - "github.com/csaf-poc/csaf_distribution/v3/csaf" - "github.com/csaf-poc/csaf_distribution/v3/internal/misc" - "github.com/csaf-poc/csaf_distribution/v3/util" + "github.com/gocsaf/csaf/v3/csaf" + "github.com/gocsaf/csaf/v3/internal/misc" + "github.com/gocsaf/csaf/v3/util" ) type processor struct { diff --git a/cmd/csaf_validator/main.go b/cmd/csaf_validator/main.go index f6aecc4b..b07c2f49 100644 --- a/cmd/csaf_validator/main.go +++ b/cmd/csaf_validator/main.go @@ -18,8 +18,8 @@ import ( "github.com/jessevdk/go-flags" - "github.com/csaf-poc/csaf_distribution/v3/csaf" - "github.com/csaf-poc/csaf_distribution/v3/util" + "github.com/gocsaf/csaf/v3/csaf" + "github.com/gocsaf/csaf/v3/util" ) type options struct { diff --git a/csaf/advisories.go b/csaf/advisories.go index 6f07648b..c51c84c7 100644 --- a/csaf/advisories.go +++ b/csaf/advisories.go @@ -19,7 +19,7 @@ import ( "strings" "time" - "github.com/csaf-poc/csaf_distribution/v3/util" + "github.com/gocsaf/csaf/v3/util" ) // AdvisoryFile constructs the urls of a remote file. diff --git a/csaf/models.go b/csaf/models.go index c7e507dd..c4b132d9 100644 --- a/csaf/models.go +++ b/csaf/models.go @@ -17,7 +17,7 @@ import ( "strings" "time" - "github.com/csaf-poc/csaf_distribution/v3/util" + "github.com/gocsaf/csaf/v3/util" ) // TLPLabel is the traffic light policy of the CSAF. diff --git a/csaf/providermetaloader.go b/csaf/providermetaloader.go index b21ddc61..b28b606a 100644 --- a/csaf/providermetaloader.go +++ b/csaf/providermetaloader.go @@ -18,7 +18,7 @@ import ( "net/http" "strings" - "github.com/csaf-poc/csaf_distribution/v3/util" + "github.com/gocsaf/csaf/v3/util" ) // ProviderMetadataLoader helps load provider-metadata.json from diff --git a/csaf/rolie.go b/csaf/rolie.go index c2b5b085..b94cfa30 100644 --- a/csaf/rolie.go +++ b/csaf/rolie.go @@ -14,7 +14,7 @@ import ( "sort" "time" - "github.com/csaf-poc/csaf_distribution/v3/util" + "github.com/gocsaf/csaf/v3/util" ) // ROLIEServiceWorkspaceCollectionCategoriesCategory is a category in a ROLIE service collection. diff --git a/csaf/summary.go b/csaf/summary.go index 72d2faf9..b10dd657 100644 --- a/csaf/summary.go +++ b/csaf/summary.go @@ -11,7 +11,7 @@ package csaf import ( "time" - "github.com/csaf-poc/csaf_distribution/v3/util" + "github.com/gocsaf/csaf/v3/util" ) const ( diff --git a/docs/csaf_checker.md b/docs/csaf_checker.md index 0b223b68..51525017 100644 --- a/docs/csaf_checker.md +++ b/docs/csaf_checker.md @@ -93,7 +93,7 @@ ignorepattern = [".*white.*", ".*red.*"] The `role` given in the `provider-metadata.json` is not yet considered to change the overall result, -see . +see . If a provider hosts one or more advisories with a TLP level of AMBER or RED, then these advisories must be access protected. To check these advisories, authorization can be given via custom headers or certificates. diff --git a/docs/csaf_provider.md b/docs/csaf_provider.md index 81a45fa2..b88924dc 100644 --- a/docs/csaf_provider.md +++ b/docs/csaf_provider.md @@ -141,5 +141,5 @@ contact_details = "Example Company can be reached at contact_us@example.com, or There is an experimental upload interface which works with a web browser. It is disabled by default, as there are known issues, notably: - * https://github.com/csaf-poc/csaf_distribution/issues/43 - * https://github.com/csaf-poc/csaf_distribution/issues/256 + * https://github.com/gocsaf/csaf/issues/43 + * https://github.com/gocsaf/csaf/issues/256 diff --git a/docs/provider-setup.md b/docs/provider-setup.md index 3f07fd0a..48c29d03 100644 --- a/docs/provider-setup.md +++ b/docs/provider-setup.md @@ -115,7 +115,7 @@ sudo chmod g+r,o-rwx /etc/csaf/config.toml Here is a minimal example configuration, which you need to customize for a production setup, -see the [options of `csaf_provider`](https://github.com/csaf-poc/csaf_distribution/blob/main/docs/csaf_provider.md). +see the [options of `csaf_provider`](https://github.com/gocsaf/csaf/blob/main/docs/csaf_provider.md). diff --git a/docs/scripts/Readme.md b/docs/scripts/Readme.md index 95f39b2a..77e8daed 100644 --- a/docs/scripts/Readme.md +++ b/docs/scripts/Readme.md @@ -1,7 +1,7 @@ Scripts for assisting the Integration tests. They were written on Ubuntu 20.04 TLS amd64 and also tested with 24.04 TLS. -- `prepareUbuntuInstanceForITests.sh` installs the required packages for the csaf_distribution integration tests on a naked ubuntu LTS amd64. +- `prepareUbuntuInstanceForITests.sh` installs the required packages for the csaf integration tests on a naked Ubuntu LTS amd64. - `TLSConfigsForITest.sh` generates a root CA and webserver cert by running `createRootCAForITest.sh` and `createWebserverCertForITest.sh` and configures nginx for serving TLS connections. @@ -14,11 +14,11 @@ As creating the folders needs to authenticate with the csaf_provider, the config Calling example (as user with sudo privileges): ``` bash - curl --fail -O https://raw.githubusercontent.com/csaf-poc/csaf_distribution/main/docs/scripts/prepareUbuntuInstanceForITests.sh + curl --fail -O https://raw.githubusercontent.com/gocsaf/csaf/main/docs/scripts/prepareUbuntuInstanceForITests.sh sudo bash prepareUbuntuInstanceForITests.sh - git clone https://github.com/csaf-poc/csaf_distribution.git # --branch - pushd csaf_distribution/docs/scripts/ + git clone https://github.com/gocsaf/csaf.git # --branch + pushd csaf/docs/scripts/ export FOLDERNAME=devca1 ORGANAME="CSAF Tools Development (internal)" source ./TLSConfigsForITest.sh diff --git a/docs/scripts/TLSClientConfigsForITest.sh b/docs/scripts/TLSClientConfigsForITest.sh index 1f941175..830666fa 100755 --- a/docs/scripts/TLSClientConfigsForITest.sh +++ b/docs/scripts/TLSClientConfigsForITest.sh @@ -18,7 +18,7 @@ set -e NGINX_CONFIG_PATH=/etc/nginx/sites-available/default -cd ~/csaf_distribution/docs/scripts/ +cd ~/csaf/docs/scripts/ source ./createCCForITest.sh echo ' diff --git a/docs/scripts/TLSConfigsForITest.sh b/docs/scripts/TLSConfigsForITest.sh index c1a5420a..d7c06f9f 100644 --- a/docs/scripts/TLSConfigsForITest.sh +++ b/docs/scripts/TLSConfigsForITest.sh @@ -17,7 +17,7 @@ set -e NGINX_CONFIG_PATH=/etc/nginx/sites-available/default -cd ~/csaf_distribution/docs/scripts/ +cd ~/csaf/docs/scripts/ ## Create Root CA ./createRootCAForITest.sh diff --git a/docs/scripts/prepareUbuntuInstanceForITests.sh b/docs/scripts/prepareUbuntuInstanceForITests.sh index ea88fc42..75ce44b9 100755 --- a/docs/scripts/prepareUbuntuInstanceForITests.sh +++ b/docs/scripts/prepareUbuntuInstanceForITests.sh @@ -2,7 +2,7 @@ set -e # This script prepares a naked Ubuntu LTS amd64 -# for the csaf_distribution integration tests +# for the csaf integration tests # by installing the required packages. apt update diff --git a/docs/scripts/testAggregator.sh b/docs/scripts/testAggregator.sh index 366ac075..f6322f62 100755 --- a/docs/scripts/testAggregator.sh +++ b/docs/scripts/testAggregator.sh @@ -29,6 +29,6 @@ popd echo echo '=== run aggregator' -cd ~/csaf_distribution/ +cd ~/csaf/ sudo cp docs/examples/aggregator.toml /etc/csaf sudo ./bin-linux-amd64/csaf_aggregator -c /etc/csaf/aggregator.toml diff --git a/docs/scripts/testChecker.sh b/docs/scripts/testChecker.sh index cb45aad6..28474d01 100755 --- a/docs/scripts/testChecker.sh +++ b/docs/scripts/testChecker.sh @@ -11,7 +11,7 @@ set -e # to exit if a command in the script fails echo '==== run checker (twice)' -cd ~/csaf_distribution +cd ~/csaf ./bin-linux-amd64/csaf_checker -f html -o ../checker-results.html --insecure \ --client_cert ~/devca1/testclient1.crt \ diff --git a/docs/scripts/testDownloader.sh b/docs/scripts/testDownloader.sh index c4b9bced..6326536a 100755 --- a/docs/scripts/testDownloader.sh +++ b/docs/scripts/testDownloader.sh @@ -10,7 +10,7 @@ set -e # to exit if a command in the script fails -cd ~/csaf_distribution +cd ~/csaf echo echo '==== run downloader (1)' diff --git a/examples/README.md b/examples/README.md index a70ea09a..c525e96e 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,6 +1,6 @@ # API examples -An experimental example of how to use `github.com/csaf-poc/csaf_distribution` +An experimental example of how to use `github.com/gocsaf/csaf` as a library. As usage of the repository as an API is currently a _work in progress_, these examples are likely to be changed. diff --git a/examples/purls_searcher/main.go b/examples/purls_searcher/main.go index c1ec3e19..72fb976d 100644 --- a/examples/purls_searcher/main.go +++ b/examples/purls_searcher/main.go @@ -1,5 +1,5 @@ // Package main implements a simple demo program to -// work with the csaf_distribution library. +// work with the csaf library. package main import ( @@ -9,8 +9,8 @@ import ( "os" "strings" - "github.com/csaf-poc/csaf_distribution/v3/csaf" - "github.com/csaf-poc/csaf_distribution/v3/util" + "github.com/gocsaf/csaf/v3/csaf" + "github.com/gocsaf/csaf/v3/util" ) func main() { diff --git a/go.mod b/go.mod index 52f1f024..c8101f04 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/csaf-poc/csaf_distribution/v3 +module github.com/gocsaf/csaf/v3 go 1.22 diff --git a/internal/options/options.go b/internal/options/options.go index c0ad2bc1..3a4867fd 100644 --- a/internal/options/options.go +++ b/internal/options/options.go @@ -15,7 +15,7 @@ import ( "log/slog" "os" - "github.com/csaf-poc/csaf_distribution/v3/util" + "github.com/gocsaf/csaf/v3/util" "github.com/BurntSushi/toml" "github.com/jessevdk/go-flags" From ace8aeaf985517cca2d3ba4b4a17db4e0f048021 Mon Sep 17 00:00:00 2001 From: Bernhard Reiter Date: Fri, 15 Nov 2024 11:46:26 +0100 Subject: [PATCH 15/64] fix: build-in version for release tags * Change Makefile to remove the leading `v` from the git tag in the case of release tags. Previously this was only done for pre-release git tags. --- Makefile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 083d3b66..04ec8664 100644 --- a/Makefile +++ b/Makefile @@ -47,13 +47,13 @@ tag_checked_out: # In this case we might in some situations see an error like # `/bin/bash: line 1: 2b55bbb: value too great for base (error token is "2b55bbb")` # which can be ignored. -GITDESC := $(shell git describe --tags --always) -GITDESCPATCH := $(shell echo '$(GITDESC)' | sed -E 's/v?[0-9]+\.[0-9]+\.([0-9]+)[-+]?.*/\1/') +GITDESC := $(shell git describe --tags --always | sed -E 's/^v//') +GITDESCPATCH := $(shell echo '$(GITDESC)' | sed -E 's/[0-9]+\.[0-9]+\.([0-9]+)[-+]?.*/\1/') SEMVERPATCH := $(shell echo $$(( $(GITDESCPATCH) + 1 ))) # Hint: The regexp in the next line only matches if there is a hyphen (`-`) # followed by a number, by which we assume that git describe # has added a string after the tag -SEMVER := $(shell echo '$(GITDESC)' | sed -E 's/v?([0-9]+\.[0-9]+\.)([0-9]+)(-[1-9].*)/\1$(SEMVERPATCH)\3/' ) +SEMVER := $(shell echo '$(GITDESC)' | sed -E 's/([0-9]+\.[0-9]+\.)([0-9]+)(-[1-9].*)/\1$(SEMVERPATCH)\3/' ) testsemver: @echo from \'$(GITDESC)\' transformed to \'$(SEMVER)\' From 1e3504c7539fd6dac3e7ffdb2c35cb1111153299 Mon Sep 17 00:00:00 2001 From: Bernhard Reiter Date: Fri, 15 Nov 2024 12:12:24 +0100 Subject: [PATCH 16/64] improve Makefile improvement --- Makefile | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Makefile b/Makefile index 04ec8664..163ace5b 100644 --- a/Makefile +++ b/Makefile @@ -41,19 +41,19 @@ tag_checked_out: # into a semver version. For this we increase the PATCH number, so that # any commit after a tag is considered newer than the semver from the tag # without an optional 'v' -# Note we need `--tags` because github release only creates lightweight tags +# Note we need `--tags` because github releases only create lightweight tags # (see feature request https://github.com/github/feedback/discussions/4924). # We use `--always` in case of being run as github action with shallow clone. # In this case we might in some situations see an error like # `/bin/bash: line 1: 2b55bbb: value too great for base (error token is "2b55bbb")` # which can be ignored. -GITDESC := $(shell git describe --tags --always | sed -E 's/^v//') -GITDESCPATCH := $(shell echo '$(GITDESC)' | sed -E 's/[0-9]+\.[0-9]+\.([0-9]+)[-+]?.*/\1/') +GITDESC := $(shell git describe --tags --always) +GITDESCPATCH := $(shell echo '$(GITDESC)' | sed -E 's/v?[0-9]+\.[0-9]+\.([0-9]+)[-+]?.*/\1/') SEMVERPATCH := $(shell echo $$(( $(GITDESCPATCH) + 1 ))) -# Hint: The regexp in the next line only matches if there is a hyphen (`-`) -# followed by a number, by which we assume that git describe -# has added a string after the tag -SEMVER := $(shell echo '$(GITDESC)' | sed -E 's/([0-9]+\.[0-9]+\.)([0-9]+)(-[1-9].*)/\1$(SEMVERPATCH)\3/' ) +# Hint: The second regexp in the next line only matches +# if there is a hyphen (`-`) followed by a number, +# by which we assume that git describe has added a string after the tag +SEMVER := $(shell echo '$(GITDESC)' | sed -E -e 's/^v//' -e 's/([0-9]+\.[0-9]+\.)([0-9]+)(-[1-9].*)/\1$(SEMVERPATCH)\3/' ) testsemver: @echo from \'$(GITDESC)\' transformed to \'$(SEMVER)\' From c00dc36547e433f52d6dbcbf5345d6cc534c2d8a Mon Sep 17 00:00:00 2001 From: koplas <54645365+koplas@users.noreply.github.com> Date: Thu, 21 Nov 2024 12:31:58 +0100 Subject: [PATCH 17/64] Remove `-h` for preferred hash configuration This option was in conflict with the help display. --- cmd/csaf_downloader/config.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/csaf_downloader/config.go b/cmd/csaf_downloader/config.go index 71c50556..619cce11 100644 --- a/cmd/csaf_downloader/config.go +++ b/cmd/csaf_downloader/config.go @@ -88,7 +88,7 @@ type config struct { ignorePattern filter.PatternMatcher //lint:ignore SA5008 We are using choice or than once: sha256, sha512 - PreferredHash hashAlgorithm `long:"preferred_hash" short:"h" choice:"sha256" choice:"sha512" value-name:"HASH" description:"HASH to prefer" toml:"preferred_hash"` + PreferredHash hashAlgorithm `long:"preferred_hash" choice:"sha256" choice:"sha512" value-name:"HASH" description:"HASH to prefer" toml:"preferred_hash"` } // configPaths are the potential file locations of the config file. From de047b76829f898ba9e22be99ca384dc0ddc7563 Mon Sep 17 00:00:00 2001 From: JanHoefelmeyer Date: Thu, 21 Nov 2024 12:53:07 +0100 Subject: [PATCH 18/64] Feat: Add prefered hash to downloader docs --- docs/csaf_downloader.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/csaf_downloader.md b/docs/csaf_downloader.md index fcf6634d..6335366a 100644 --- a/docs/csaf_downloader.md +++ b/docs/csaf_downloader.md @@ -32,6 +32,7 @@ Application Options: --logfile=FILE FILE to log downloading to (default: downloader.log) --loglevel=LEVEL[debug|info|warn|error] LEVEL of logging details (default: info) -c, --config=TOML-FILE Path to config TOML file + --preferred_hash=HASH[sha256|sha512] HASH to prefer Help Options: -h, --help Show this help message From 01645f55598e01e891c1a146eda6b9817b2e9c9c Mon Sep 17 00:00:00 2001 From: JanHoefelmeyer Date: Thu, 21 Nov 2024 12:49:02 +0100 Subject: [PATCH 19/64] Fix: Update downloader docs --- docs/csaf_downloader.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/csaf_downloader.md b/docs/csaf_downloader.md index 07c6e63c..04f93b28 100644 --- a/docs/csaf_downloader.md +++ b/docs/csaf_downloader.md @@ -22,6 +22,7 @@ Application Options: -f, --folder=FOLDER Download into a given subFOLDER -i, --ignore_pattern=PATTERN Do not download files if their URLs match any of the given PATTERNs -H, --header= One or more extra HTTP header fields + --enumerate_pmd_only If this flag is set to true, the downloader will only enumerate valid provider metadata files, but not download documents --validator=URL URL to validate documents remotely --validator_cache=FILE FILE to cache remote validations --validator_preset=PRESETS One or more PRESETS to validate remotely (default: [mandatory]) @@ -30,8 +31,8 @@ Application Options: --forward_header= One or more extra HTTP header fields used by forwarding --forward_queue=LENGTH Maximal queue LENGTH before forwarder (default: 5) --forward_insecure Do not check TLS certificates from forward endpoint - --logfile=FILE FILE to log downloading to (default: downloader.log) - --loglevel=LEVEL[debug|info|warn|error] LEVEL of logging details (default: info) + --log_file=FILE FILE to log downloading to (default: downloader.log) + --log_level=LEVEL[debug|info|warn|error] LEVEL of logging details (default: info) -c, --config=TOML-FILE Path to config TOML file Help Options: From fe4f01d06255e67db2c5ee3f6f3e9a1453b2dea0 Mon Sep 17 00:00:00 2001 From: JanHoefelmeyer <107021473+JanHoefelmeyer@users.noreply.github.com> Date: Fri, 22 Nov 2024 14:52:56 +0100 Subject: [PATCH 20/64] fix: Link to file was not working (#592) --- docs/csaf_provider.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/csaf_provider.md b/docs/csaf_provider.md index b88924dc..cb27f9fd 100644 --- a/docs/csaf_provider.md +++ b/docs/csaf_provider.md @@ -4,7 +4,7 @@ The [setup docs](../README.md#setup-trusted-provider) explain how to wire this up with nginx and where the config file lives. When installed, two endpoints are offered, -and you should use the [csaf_uploader](../docs/csaf_uploader) +and you should use the [csaf_uploader](../docs/csaf_uploader.md) to access them: ### /api/create From f6d7589fde4b7208572d6a0781dd0624ecbbe582 Mon Sep 17 00:00:00 2001 From: koplas <54645365+koplas@users.noreply.github.com> Date: Fri, 22 Nov 2024 15:58:41 +0100 Subject: [PATCH 21/64] Add required upload permissions --- .github/workflows/release.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 4bcd6bab..d1e370fa 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,6 +8,8 @@ jobs: releases-matrix: name: Release Go binaries runs-on: ubuntu-20.04 + permissions: + contents: write steps: - name: Checkout uses: actions/checkout@v3 From 9495d8b1c38ac814f10fd29762e509ed849203db Mon Sep 17 00:00:00 2001 From: "Sascha L. Teichmann" Date: Fri, 22 Nov 2024 16:10:54 +0100 Subject: [PATCH 22/64] Update Go 3rd party libs --- go.mod | 30 +++++++++++----------- go.sum | 80 +++++++++++++++++++++++----------------------------------- 2 files changed, 47 insertions(+), 63 deletions(-) diff --git a/go.mod b/go.mod index c8101f04..1ef2216a 100644 --- a/go.mod +++ b/go.mod @@ -1,31 +1,31 @@ module github.com/gocsaf/csaf/v3 -go 1.22 +go 1.22.9 require ( - github.com/BurntSushi/toml v1.3.2 + github.com/BurntSushi/toml v1.4.0 github.com/Intevation/gval v1.3.0 github.com/Intevation/jsonpath v0.2.1 - github.com/ProtonMail/gopenpgp/v2 v2.7.4 + github.com/ProtonMail/gopenpgp/v2 v2.8.0 github.com/PuerkitoBio/goquery v1.8.1 - github.com/gofrs/flock v0.8.1 - github.com/jessevdk/go-flags v1.5.0 + github.com/gofrs/flock v0.12.1 + github.com/jessevdk/go-flags v1.6.1 github.com/mitchellh/go-homedir v1.1.0 github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 - go.etcd.io/bbolt v1.3.8 - golang.org/x/crypto v0.14.0 - golang.org/x/term v0.13.0 - golang.org/x/time v0.3.0 + go.etcd.io/bbolt v1.3.11 + golang.org/x/crypto v0.29.0 + golang.org/x/term v0.26.0 + golang.org/x/time v0.8.0 ) require ( - github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c // indirect + github.com/ProtonMail/go-crypto v1.1.2 // indirect github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f // indirect github.com/andybalholm/cascadia v1.3.2 // indirect - github.com/cloudflare/circl v1.3.6 // indirect + github.com/cloudflare/circl v1.5.0 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/shopspring/decimal v1.3.1 // indirect - golang.org/x/net v0.17.0 // indirect - golang.org/x/sys v0.14.0 // indirect - golang.org/x/text v0.13.0 // indirect + github.com/shopspring/decimal v1.4.0 // indirect + golang.org/x/net v0.31.0 // indirect + golang.org/x/sys v0.27.0 // indirect + golang.org/x/text v0.20.0 // indirect ) diff --git a/go.sum b/go.sum index f81653dc..47637e90 100644 --- a/go.sum +++ b/go.sum @@ -1,34 +1,30 @@ -github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8= -github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= +github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/Intevation/gval v1.3.0 h1:+Ze5sft5MmGbZrHj06NVUbcxCb67l9RaPTLMNr37mjw= github.com/Intevation/gval v1.3.0/go.mod h1:xmGyGpP5be12EL0P12h+dqiYG8qn2j3PJxIgkoOHO5o= github.com/Intevation/jsonpath v0.2.1 h1:rINNQJ0Pts5XTFEG+zamtdL7l9uuE1z0FBA+r55Sw+A= github.com/Intevation/jsonpath v0.2.1/go.mod h1:WnZ8weMmwAx/fAO3SutjYFU+v7DFreNYnibV7CiaYIw= -github.com/ProtonMail/go-crypto v0.0.0-20230717121422-5aa5874ade95/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= -github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c h1:kMFnB0vCcX7IL/m9Y5LO+KQYv+t1CQOiFe6+SV2J7bE= -github.com/ProtonMail/go-crypto v0.0.0-20230923063757-afb1ddc0824c/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= +github.com/ProtonMail/go-crypto v1.1.2 h1:A7JbD57ThNqh7XjmHE+PXpQ3Dqt3BrSAC0AL0Go3KS0= +github.com/ProtonMail/go-crypto v1.1.2/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f h1:tCbYj7/299ekTTXpdwKYF8eBlsYsDVoggDAuAjoK66k= github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f/go.mod h1:gcr0kNtGBqin9zDW9GOHcVntrwnjrK+qdJ06mWYBybw= -github.com/ProtonMail/gopenpgp/v2 v2.7.4 h1:Vz/8+HViFFnf2A6XX8JOvZMrA6F5puwNvvF21O1mRlo= -github.com/ProtonMail/gopenpgp/v2 v2.7.4/go.mod h1:IhkNEDaxec6NyzSI0PlxapinnwPVIESk8/76da3Ct3g= +github.com/ProtonMail/gopenpgp/v2 v2.8.0 h1:WvMv3CMcFsqKSM4/Qf8sf3tgyQkzDqQmoSE49bnBuP4= +github.com/ProtonMail/gopenpgp/v2 v2.8.0/go.mod h1:qb2GUSnmA9ipBW5GVtCtEhkummSlqs2A8Ar3S0HBgSY= github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM= github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ= github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA= github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss= github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU= -github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= -github.com/cloudflare/circl v1.3.3/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= -github.com/cloudflare/circl v1.3.6 h1:/xbKIqSHbZXHwkhbrhrt2YOHIwYJlXH94E3tI/gDlUg= -github.com/cloudflare/circl v1.3.6/go.mod h1:5XYMA4rFBvNIrhs50XuiBJ15vF2pZn4nnUKZrLbUZFA= -github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/cloudflare/circl v1.5.0 h1:hxIWksrX6XN5a1L2TI/h53AGPhNHoUBo+TD1ms9+pys= +github.com/cloudflare/circl v1.5.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw= -github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU= +github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E= +github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0= github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/jessevdk/go-flags v1.5.0 h1:1jKYvbxEjfUl0fmqTCOfonvskHHXMjBySTLW4y9LFvc= -github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= +github.com/jessevdk/go-flags v1.6.1 h1:Cvu5U8UGrLay1rZfv/zP7iLpSHGUZ/Ou68T0iX1bBK4= +github.com/jessevdk/go-flags v1.6.1/go.mod h1:Mk8T1hIAWpOiJiHa9rJASDK2UGWji0EuPGBnNLMooyc= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -37,78 +33,66 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4= github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY= -github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8= github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= -github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= +github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= +github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA= -go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= +go.etcd.io/bbolt v1.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0= +go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= -golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc= -golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4= +golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= +golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= -golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM= -golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE= +golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo= +golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= +golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= -golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= +golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= -golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek= -golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U= +golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU= +golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= +golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= +golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg= +golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 7824f3b48da9c868940936b3839483d15feaf8f3 Mon Sep 17 00:00:00 2001 From: koplas Date: Fri, 22 Nov 2024 16:31:56 +0100 Subject: [PATCH 23/64] Improve hash fetching and logging --- cmd/csaf_downloader/config.go | 4 +- cmd/csaf_downloader/downloader.go | 108 +++++++++++++++++++++++------- 2 files changed, 84 insertions(+), 28 deletions(-) diff --git a/cmd/csaf_downloader/config.go b/cmd/csaf_downloader/config.go index 619cce11..a262ef7b 100644 --- a/cmd/csaf_downloader/config.go +++ b/cmd/csaf_downloader/config.go @@ -44,8 +44,8 @@ const ( type hashAlgorithm string const ( - algSha256 = hashAlgorithm("SHA256") - algSha2512 = hashAlgorithm("SHA512") + algSha256 = hashAlgorithm("SHA256") + algSha512 = hashAlgorithm("SHA512") ) type config struct { diff --git a/cmd/csaf_downloader/downloader.go b/cmd/csaf_downloader/downloader.go index 3cb73325..18fc1e8f 100644 --- a/cmd/csaf_downloader/downloader.go +++ b/cmd/csaf_downloader/downloader.go @@ -25,6 +25,7 @@ import ( "os" "path" "path/filepath" + "slices" "strconv" "strings" "sync" @@ -37,6 +38,13 @@ import ( "github.com/csaf-poc/csaf_distribution/v3/util" ) +type hashFetchInfo struct { + url string + preferred bool + warn bool + hashType hashAlgorithm +} + type downloader struct { cfg *config keys *crypto.KeyRing @@ -496,35 +504,39 @@ nextAdvisory: signData []byte ) - if (d.cfg.PreferredHash != "sha512" || file.SHA512URL() == "") && file.SHA256URL() != "" { - // Only hash when we have a remote counterpart we can compare it with. - if remoteSHA256, s256Data, err = loadHash(client, file.SHA256URL()); err != nil { - if !file.IsDirectory() { - slog.Warn("Cannot fetch SHA256", - "url", file.SHA256URL(), - "error", err) - } else { - slog.Info("SHA256 not present", "file", file.URL()) - } - } else { - s256 = sha256.New() - writers = append(writers, s256) + hashToFetch := []hashFetchInfo{} + if file.SHA512URL() != "" { + hashToFetch = append(hashToFetch, hashFetchInfo{ + url: file.SHA512URL(), + warn: true, + hashType: algSha512, + preferred: strings.EqualFold(string(d.cfg.PreferredHash), string(algSha512))}) + } else { + slog.Info("SHA512 not present") + } + if file.SHA256URL() != "" { + hashToFetch = append(hashToFetch, hashFetchInfo{ + url: file.SHA256URL(), + warn: true, + hashType: algSha256, + preferred: strings.EqualFold(string(d.cfg.PreferredHash), string(algSha256))}) + } else { + slog.Info("SHA256 not present") + } + if file.IsDirectory() { + for i := range hashToFetch { + hashToFetch[i].warn = false } } - if (d.cfg.PreferredHash != "sha256" || file.SHA256URL() == "") && file.SHA512URL() != "" { - if remoteSHA512, s512Data, err = loadHash(client, file.SHA512URL()); err != nil { - if !file.IsDirectory() { - slog.Warn("Cannot fetch SHA512", - "url", file.SHA512URL(), - "error", err) - } else { - slog.Info("SHA512 not present", "file", file.URL()) - } - } else { - s512 = sha512.New() - writers = append(writers, s512) - } + remoteSHA256, s256Data, remoteSHA512, s512Data = loadHashes(client, hashToFetch) + if remoteSHA512 != nil { + s512 = sha512.New() + writers = append(writers, s512) + } + if remoteSHA256 != nil { + s256 = sha256.New() + writers = append(writers, s256) } // Remember the data as we need to store it to file later. @@ -755,6 +767,50 @@ func loadSignature(client util.Client, p string) (*crypto.PGPSignature, []byte, return sign, data, nil } +func loadHashes(client util.Client, hashes []hashFetchInfo) ([]byte, []byte, []byte, []byte) { + var remoteSha256, remoteSha512, sha256Data, sha512Data []byte + + // Load preferred hashes first + slices.SortStableFunc(hashes, func(a, b hashFetchInfo) int { + if a.preferred == b.preferred { + return 0 + } + if a.preferred && !b.preferred { + return -1 + } + return 1 + }) + for _, h := range hashes { + if remote, data, err := loadHash(client, h.url); err != nil { + if h.warn { + slog.Warn("Cannot fetch hash", + "hash", h.hashType, + "url", h.url, + "error", err) + } else { + slog.Info("Hash not present", "hash", h.hashType, "file", h.url) + } + } else { + switch h.hashType { + case algSha512: + { + remoteSha512 = remote + sha512Data = data + } + case algSha256: + { + remoteSha256 = remote + sha256Data = data + } + } + if h.preferred { + break + } + } + } + return remoteSha256, sha256Data, remoteSha512, sha512Data +} + func loadHash(client util.Client, p string) ([]byte, []byte, error) { resp, err := client.Get(p) if err != nil { From b2180849e99f2b1df9dbc97a6b2d3c6d93fcc679 Mon Sep 17 00:00:00 2001 From: Bernhard Reiter Date: Mon, 25 Nov 2024 09:38:13 +0100 Subject: [PATCH 24/64] Update README.md that go paths can be adjusted --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index cec92483..463b1d92 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,8 @@ > [!IMPORTANT] > To avoid future breakage, if you still use `csaf-poc`: > 1. Adjust your HTML links. -> 2. Prepare to adjust your go module paths once https://github.com/gocsaf/csaf/issues/579 is solved. -> +> 2. Adjust your go module paths, see [#579](https://github.com/gocsaf/csaf/issues/579#issuecomment-2497244379). +> > (This repository was moved here on 2024-10-28. The old one is deprecated > and redirection will be switched off a few months later.) From a167bf65ad14acb142dba288529ee760799f338d Mon Sep 17 00:00:00 2001 From: koplas Date: Mon, 25 Nov 2024 14:27:56 +0100 Subject: [PATCH 25/64] Add Apache 2.0 license to root folder This allows other programs like google/licensecheck to correctly detect the license. This is required to display the documentation in `pkg.go.dev`. --- LICENSE-Apache-2.0.txt | 73 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 LICENSE-Apache-2.0.txt diff --git a/LICENSE-Apache-2.0.txt b/LICENSE-Apache-2.0.txt new file mode 100644 index 00000000..137069b8 --- /dev/null +++ b/LICENSE-Apache-2.0.txt @@ -0,0 +1,73 @@ +Apache License +Version 2.0, January 2004 +http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + +"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. + +"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. + +"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. + +"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. + +"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. + +"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. + +"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). + +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. + +"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." + +"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: + + (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. + + You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. From ffb4eff933fef6c222dd131e90675152589c8003 Mon Sep 17 00:00:00 2001 From: koplas Date: Wed, 27 Nov 2024 12:15:21 +0100 Subject: [PATCH 26/64] Merge unittest into sha-handling commit 990c74a1a64cf8688a7fd14ebb524ce96a320eef Merge: 86d7ce1 7824f3b Author: koplas Date: Fri Nov 22 16:58:46 2024 +0100 Merge branch 'sha-handling' into unittest commit 86d7ce13dcf1ff2250f27b5e9b811da38937fff5 Merge: a6807d2 79b8900 Author: koplas Date: Fri Nov 22 16:54:45 2024 +0100 Merge branch 'sha-handling' into unittest commit 79b89009dd7f5dd748ccedc0ea87ea26e75b65d2 Author: koplas Date: Fri Nov 22 16:31:56 2024 +0100 Improve hash fetching and logging commit a6807d24d604cafa4e5d30d6ba9c948490d9f883 Merge: ddb5518 d18d2c3 Author: koplas Date: Fri Nov 22 16:51:55 2024 +0100 Merge branch 'sha-handling' into unittest commit d18d2c3bf17950dad276457136c2262988cca129 Author: koplas Date: Fri Nov 22 16:31:56 2024 +0100 Improve hash fetching and logging commit ddb5518c6d57adce14fb5f7665d219778e642c53 Author: koplas <54645365+koplas@users.noreply.github.com> Date: Tue Sep 17 10:45:25 2024 +0200 Extend SHA marking tests commit 13c94f4fa06c0ba3ca52f76b93934f6855d80e81 Author: koplas Date: Mon Sep 16 20:46:31 2024 +0200 Use temp directory for downloads commit 1819b4896babaf9bd1136e5846e07224fb201b18 Author: koplas Date: Mon Sep 16 20:37:55 2024 +0200 Fix rolie feed commit 989e3667bad4c10cb1a779d3a7efd526929dc002 Author: koplas Date: Mon Sep 16 20:23:22 2024 +0200 Fix provider-metadata.json commit 714735d74a159e1fd8f7e756673742708dc758d4 Author: koplas Date: Mon Sep 16 20:08:21 2024 +0200 Implement provider handler commit d488e3994749c3e7daf2c00f2a7952974a8dce49 Author: koplas Date: Mon Sep 16 16:26:37 2024 +0200 Add info about gpg key commit a9bf9da130a04fffbf00481930575d1b292d138f Author: koplas Date: Mon Sep 16 16:12:49 2024 +0200 Rename directory testdata commit 6ca6dfee25c947758fac0abfb28e10049809d3ec Author: koplas Date: Mon Sep 16 16:01:41 2024 +0200 Add initial downloader tests commit 20bee797c61a457c58b37c208f0540a5ed7d7468 Author: koplas Date: Mon Sep 16 15:58:31 2024 +0200 Fix: Remove unecessary error print commit 8e4e508073e6a8d34922295de35da42b4ea8a93a Author: koplas Date: Mon Sep 16 14:50:48 2024 +0200 Extend links test commit 3ba29f94de3eebc379adc021f40fd5cd0587b57d Author: koplas Date: Mon Sep 16 14:11:14 2024 +0200 Add initial directory feed testdata commit dee55aafd9052adcda28a231b04271d866d06dd7 Author: koplas <54645365+koplas@users.noreply.github.com> Date: Mon Sep 16 10:47:32 2024 +0200 Add initial testdata commit cd9338ae7279791db62e28e8f4b5cfe9cf370881 Author: koplas <54645365+koplas@users.noreply.github.com> Date: Thu Sep 12 15:54:42 2024 +0200 Add initial download unittests --- cmd/csaf_aggregator/client_test.go | 67 ++++++ cmd/csaf_checker/links_test.go | 80 ++++++- cmd/csaf_downloader/config.go | 4 +- cmd/csaf_downloader/downloader.go | 6 + cmd/csaf_downloader/downloader_test.go | 218 ++++++++++++++++++ csaf/providermetaloader.go | 2 +- .../openpgp/info.txt | 2 + .../openpgp/privkey.asc | 15 ++ .../openpgp/pubkey.asc | 13 ++ .../provider-metadata.json | 25 ++ .../simple-directory-provider/security.txt | 2 + .../avendor-advisory-0004-not-listed.json | 170 ++++++++++++++ .../white/avendor-advisory-0004.json | 170 ++++++++++++++ .../white/avendor-advisory-0004.json.asc | 7 + .../white/avendor-advisory-0004.json.sha256 | 1 + .../white/avendor-advisory-0004.json.sha512 | 1 + .../white/changes.csv | 1 + .../white/index.html | 6 + .../simple-directory-provider/white/index.txt | 1 + .../simple-rolie-provider/openpgp/info.txt | 2 + .../simple-rolie-provider/openpgp/privkey.asc | 15 ++ .../simple-rolie-provider/openpgp/pubkey.asc | 13 ++ .../provider-metadata.json | 33 +++ testdata/simple-rolie-provider/security.txt | 2 + testdata/simple-rolie-provider/service.json | 23 ++ .../white/avendor-advisory-0004.json | 170 ++++++++++++++ .../white/avendor-advisory-0004.json.asc | 7 + .../white/avendor-advisory-0004.json.sha256 | 1 + .../white/avendor-advisory-0004.json.sha512 | 1 + .../white/white-feed.json | 61 +++++ 30 files changed, 1115 insertions(+), 4 deletions(-) create mode 100644 cmd/csaf_aggregator/client_test.go create mode 100644 cmd/csaf_downloader/downloader_test.go create mode 100644 testdata/simple-directory-provider/openpgp/info.txt create mode 100644 testdata/simple-directory-provider/openpgp/privkey.asc create mode 100644 testdata/simple-directory-provider/openpgp/pubkey.asc create mode 100644 testdata/simple-directory-provider/provider-metadata.json create mode 100644 testdata/simple-directory-provider/security.txt create mode 100644 testdata/simple-directory-provider/white/avendor-advisory-0004-not-listed.json create mode 100644 testdata/simple-directory-provider/white/avendor-advisory-0004.json create mode 100644 testdata/simple-directory-provider/white/avendor-advisory-0004.json.asc create mode 100644 testdata/simple-directory-provider/white/avendor-advisory-0004.json.sha256 create mode 100644 testdata/simple-directory-provider/white/avendor-advisory-0004.json.sha512 create mode 100644 testdata/simple-directory-provider/white/changes.csv create mode 100644 testdata/simple-directory-provider/white/index.html create mode 100644 testdata/simple-directory-provider/white/index.txt create mode 100644 testdata/simple-rolie-provider/openpgp/info.txt create mode 100644 testdata/simple-rolie-provider/openpgp/privkey.asc create mode 100644 testdata/simple-rolie-provider/openpgp/pubkey.asc create mode 100644 testdata/simple-rolie-provider/provider-metadata.json create mode 100644 testdata/simple-rolie-provider/security.txt create mode 100644 testdata/simple-rolie-provider/service.json create mode 100644 testdata/simple-rolie-provider/white/avendor-advisory-0004.json create mode 100644 testdata/simple-rolie-provider/white/avendor-advisory-0004.json.asc create mode 100644 testdata/simple-rolie-provider/white/avendor-advisory-0004.json.sha256 create mode 100644 testdata/simple-rolie-provider/white/avendor-advisory-0004.json.sha512 create mode 100644 testdata/simple-rolie-provider/white/white-feed.json diff --git a/cmd/csaf_aggregator/client_test.go b/cmd/csaf_aggregator/client_test.go new file mode 100644 index 00000000..c08b29ab --- /dev/null +++ b/cmd/csaf_aggregator/client_test.go @@ -0,0 +1,67 @@ +// This file is Free Software under the MIT License +// without warranty, see README.md and LICENSES/MIT.txt for details. +// +// SPDX-License-Identifier: MIT +// +// SPDX-FileCopyrightText: 2022 German Federal Office for Information Security (BSI) +// Software-Engineering: 2022 Intevation GmbH + +package main + +import ( + "io" + "net/http" + "net/http/httptest" + "testing" + + "github.com/csaf-poc/csaf_distribution/v3/util" +) + +func Test_downloadJSON(t *testing.T) { + tests := []struct { + name string + statusCode int + contentType string + wantErr error + }{ + { + name: "status ok, application/json", + statusCode: http.StatusOK, + contentType: "application/json", + wantErr: nil, + }, + { + name: "status found, application/json", + statusCode: http.StatusFound, + contentType: "application/json", + wantErr: errNotFound, + }, + { + name: "status ok, application/xml", + statusCode: http.StatusOK, + contentType: "application/xml", + wantErr: errNotFound, + }, + } + + t.Parallel() + for _, testToRun := range tests { + test := testToRun + t.Run(test.name, func(tt *testing.T) { + tt.Parallel() + found := func(r io.Reader) error { + return nil + } + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.Header().Add("Content-Type", test.contentType) + w.WriteHeader(test.statusCode) + })) + defer server.Close() + hClient := http.Client{} + client := util.Client(&hClient) + if gotErr := downloadJSON(client, server.URL, found); gotErr != test.wantErr { + t.Errorf("downloadJSON: Expected %q but got %q.", test.wantErr, gotErr) + } + }) + } +} diff --git a/cmd/csaf_checker/links_test.go b/cmd/csaf_checker/links_test.go index 8abf4e6a..aa04222d 100644 --- a/cmd/csaf_checker/links_test.go +++ b/cmd/csaf_checker/links_test.go @@ -10,8 +10,12 @@ package main import ( "fmt" + "net/http" + "net/http/httptest" "strings" "testing" + + "github.com/csaf-poc/csaf_distribution/v3/util" ) const page0 = ` @@ -31,7 +35,6 @@ const page0 = ` ` func TestLinksOnPage(t *testing.T) { - var links []string err := linksOnPage( @@ -58,3 +61,78 @@ func TestLinksOnPage(t *testing.T) { } } } + +func Test_listed(t *testing.T) { + tests := []struct { + name string + badDirs util.Set[string] + path string + want bool + }{ + { + name: "listed path", + badDirs: util.Set[string]{}, + path: "/white/avendor-advisory-0004.json", + want: true, + }, + { + name: "badDirs contains path", + badDirs: util.Set[string]{"/white/": {}}, + path: "/white/avendor-advisory-0004.json", + want: false, + }, + { + name: "not found", + badDirs: util.Set[string]{}, + path: "/not-found/resource.json", + want: false, + }, + { + name: "badDirs does not contain path", + badDirs: util.Set[string]{"/bad-dir/": {}}, + path: "/white/avendor-advisory-0004.json", + want: true, + }, + { + name: "unlisted path", + badDirs: util.Set[string]{}, + path: "/white/avendor-advisory-0004-not-listed.json", + want: false, + }, + } + + t.Parallel() + for _, testToRun := range tests { + test := testToRun + t.Run(test.name, func(tt *testing.T) { + tt.Parallel() + serverURL := "" + fs := http.FileServer(http.Dir("../../testdata/simple-directory-provider")) + server := httptest.NewTLSServer(fs) + defer server.Close() + + serverURL = server.URL + + hClient := server.Client() + client := util.Client(hClient) + + pgs := pages{} + cfg := config{RemoteValidator: "", RemoteValidatorCache: ""} + p, err := newProcessor(&cfg) + if err != nil { + t.Error(err) + } + p.client = client + + badDirs := util.Set[string]{} + for dir := range test.badDirs { + badDirs.Add(serverURL + dir) + } + + got, _ := pgs.listed(serverURL+test.path, p, badDirs) + if got != test.want { + t.Errorf("%q: Expected %t but got %t.", test.name, test.want, got) + } + }) + } +} diff --git a/cmd/csaf_downloader/config.go b/cmd/csaf_downloader/config.go index a262ef7b..a44fa811 100644 --- a/cmd/csaf_downloader/config.go +++ b/cmd/csaf_downloader/config.go @@ -44,8 +44,8 @@ const ( type hashAlgorithm string const ( - algSha256 = hashAlgorithm("SHA256") - algSha512 = hashAlgorithm("SHA512") + algSha256 = hashAlgorithm("sha256") + algSha512 = hashAlgorithm("sha512") ) type config struct { diff --git a/cmd/csaf_downloader/downloader.go b/cmd/csaf_downloader/downloader.go index 18fc1e8f..ca5cccc7 100644 --- a/cmd/csaf_downloader/downloader.go +++ b/cmd/csaf_downloader/downloader.go @@ -47,6 +47,7 @@ type hashFetchInfo struct { type downloader struct { cfg *config + client *util.Client // Used for testing keys *crypto.KeyRing validator csaf.RemoteValidator forwarder *forwarder @@ -131,6 +132,11 @@ func (d *downloader) httpClient() util.Client { client := util.Client(&hClient) + // Overwrite for testing purposes + if client != nil { + client = *d.client + } + // Add extra headers. if len(d.cfg.ExtraHeader) > 0 { client = &util.HeaderClient{ diff --git a/cmd/csaf_downloader/downloader_test.go b/cmd/csaf_downloader/downloader_test.go new file mode 100644 index 00000000..cf020355 --- /dev/null +++ b/cmd/csaf_downloader/downloader_test.go @@ -0,0 +1,218 @@ +// This file is Free Software under the Apache-2.0 License +// without warranty, see README.md and LICENSES/Apache-2.0.txt for details. +// +// SPDX-License-Identifier: Apache-2.0 +// +// SPDX-FileCopyrightText: 2023 German Federal Office for Information Security (BSI) +// Software-Engineering: 2023 Intevation GmbH + +package main + +import ( + "context" + "errors" + "html/template" + "log/slog" + "net/http" + "net/http/httptest" + "os" + "strings" + "testing" + + "github.com/csaf-poc/csaf_distribution/v3/internal/options" + "github.com/csaf-poc/csaf_distribution/v3/util" +) + +type ProviderParams struct { + URL string + EnableSha256 bool + EnableSha512 bool +} + +func ProviderHandler(params *ProviderParams, directoryProvider bool) http.HandlerFunc { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + path := "../../testdata/" + if directoryProvider { + path += "simple-directory-provider" + } else { + path += "simple-rolie-provider" + } + + path += r.URL.Path + + if strings.HasSuffix(r.URL.Path, "/") { + path += "index.html" + } + + content, err := os.ReadFile(path) + if err != nil { + w.WriteHeader(http.StatusNotFound) + return + } + switch { + case strings.HasSuffix(path, ".html"): + w.Header().Add("Content-Type", "text/html") + case strings.HasSuffix(path, ".json"): + w.Header().Add("Content-Type", "application/json") + case strings.HasSuffix(path, ".sha256") && directoryProvider && !params.EnableSha256: + w.WriteHeader(http.StatusNotFound) + return + case strings.HasSuffix(path, ".sha512") && directoryProvider && !params.EnableSha512: + w.WriteHeader(http.StatusNotFound) + return + default: + w.Header().Add("Content-Type", "text/plain") + } + + tmplt, err := template.New("base").Parse(string(content)) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } + err = tmplt.Execute(w, params) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } + }) +} + +func checkIfFileExists(path string, t *testing.T) bool { + if _, err := os.Stat(path); err == nil { + return true + } else if errors.Is(err, os.ErrNotExist) { + return false + } else { + t.Fatalf("Failed to check if file exists: %v", err) + return false + } +} + +func TestShaMarking(t *testing.T) { + tests := []struct { + name string + directoryProvider bool + wantSha256 bool + wantSha512 bool + enableSha256 bool + enableSha512 bool + preferredHash hashAlgorithm + }{ + { + name: "want sha256 and sha512", + directoryProvider: false, + wantSha256: true, + wantSha512: true, + enableSha256: true, + enableSha512: true, + }, + { + name: "only want sha256", + directoryProvider: false, + wantSha256: true, + wantSha512: false, + enableSha256: true, + enableSha512: true, + preferredHash: algSha256, + }, + { + name: "only want sha512", + directoryProvider: false, + wantSha256: false, + wantSha512: true, + enableSha256: true, + enableSha512: true, + preferredHash: algSha512, + }, + { + name: "only want sha512", + directoryProvider: false, + wantSha256: false, + wantSha512: true, + enableSha256: true, + enableSha512: true, + preferredHash: algSha512, + }, + + { + name: "only deliver sha256", + directoryProvider: false, + wantSha256: true, + wantSha512: false, + enableSha256: true, + enableSha512: false, + preferredHash: algSha512, + }, + { + name: "only want sha256, directory provider", + directoryProvider: true, + wantSha256: true, + wantSha512: false, + enableSha256: true, + enableSha512: true, + preferredHash: algSha256, + }, + { + name: "only want sha512, directory provider", + directoryProvider: true, + wantSha256: false, + wantSha512: true, + enableSha256: true, + enableSha512: true, + preferredHash: algSha512, + }, + } + + t.Parallel() + for _, testToRun := range tests { + test := testToRun + t.Run(test.name, func(tt *testing.T) { + tt.Parallel() + serverURL := "" + params := ProviderParams{ + URL: "", + EnableSha256: test.enableSha256, + EnableSha512: test.enableSha512, + } + server := httptest.NewTLSServer(ProviderHandler(¶ms, test.directoryProvider)) + defer server.Close() + + serverURL = server.URL + params.URL = server.URL + + hClient := server.Client() + client := util.Client(hClient) + + tempDir := t.TempDir() + cfg := config{LogLevel: &options.LogLevel{Level: slog.LevelDebug}, Directory: tempDir, PreferredHash: test.preferredHash} + err := cfg.prepare() + if err != nil { + t.Fatalf("SHA marking config failed: %v", err) + } + d, err := newDownloader(&cfg) + if err != nil { + t.Fatalf("could not init downloader: %v", err) + } + d.client = &client + + ctx := context.Background() + err = d.run(ctx, []string{serverURL + "/provider-metadata.json"}) + if err != nil { + t.Errorf("SHA marking %v: Expected no error, got: %v", test.name, err) + } + d.close() + + // Check for downloaded hashes + sha256Exists := checkIfFileExists(tempDir+"/white/2020/avendor-advisory-0004.json.sha256", t) + sha512Exists := checkIfFileExists(tempDir+"/white/2020/avendor-advisory-0004.json.sha512", t) + + if sha256Exists != test.wantSha256 { + t.Errorf("%v: expected sha256 hash present to be %v, got: %v", test.name, test.wantSha256, sha256Exists) + } + + if sha512Exists != test.wantSha512 { + t.Errorf("%v: expected sha512 hash present to be %v, got: %v", test.name, test.wantSha512, sha512Exists) + } + }) + } +} diff --git a/csaf/providermetaloader.go b/csaf/providermetaloader.go index b21ddc61..aa3c38af 100644 --- a/csaf/providermetaloader.go +++ b/csaf/providermetaloader.go @@ -352,7 +352,7 @@ func (pmdl *ProviderMetadataLoader) loadFromURL(path string) *LoadedProviderMeta case len(errors) > 0: result.Messages = []ProviderMetadataLoadMessage{{ Type: SchemaValidationFailed, - Message: fmt.Sprintf("%s: Validating against JSON schema failed: %v", path, err), + Message: fmt.Sprintf("%s: Validating against JSON schema failed", path), }} for _, msg := range errors { result.Messages.Add( diff --git a/testdata/simple-directory-provider/openpgp/info.txt b/testdata/simple-directory-provider/openpgp/info.txt new file mode 100644 index 00000000..3a159f68 --- /dev/null +++ b/testdata/simple-directory-provider/openpgp/info.txt @@ -0,0 +1,2 @@ +The GPG key was generated with no passphrase and this command: +`gpg --default-new-key-algo "ed25519/cert,sign+cv25519/encr" --quick-generate-key security@example.com"` diff --git a/testdata/simple-directory-provider/openpgp/privkey.asc b/testdata/simple-directory-provider/openpgp/privkey.asc new file mode 100644 index 00000000..816f3097 --- /dev/null +++ b/testdata/simple-directory-provider/openpgp/privkey.asc @@ -0,0 +1,15 @@ +-----BEGIN PGP PRIVATE KEY BLOCK----- + +lFgEZtsQNxYJKwYBBAHaRw8BAQdASr3y4zW+4XGqUlvRJ7stRCUHv8HB4ZoMoTtU +KLgnHr4AAQD5G5xy/yTN5b+lvV5Ahrbz1qOZ/wmKTieGOH9GZb6JwhHwtBRzZWN1 +cml0eUBleGFtcGxlLmNvbYiZBBMWCgBBFiEEqJFMovEROcammgAY+zzZsV3mFZYF +AmbbEDcCGwMFCQWjmoAFCwkIBwICIgIGFQoJCAsCBBYCAwECHgcCF4AACgkQ+zzZ +sV3mFZZskQEAg5Dttqm6TA7MtLxz7VSlklx95LQr9d5jm4jcOaqlGT0A/1mAAlUq +SDySFGI6DFQLcaZaUd9Yl+1b0Icr0tUiOaQHnF0EZtsQNxIKKwYBBAGXVQEFAQEH +QOTHP4FkopIGJMWXTYsaeQ1Dugd+yNYWB357vRYq6QsiAwEIBwAA/0RIazq1s8Oe +23jvNaZGb/adDYnRrkCMXXTBKsuA6WOAEhKIeAQYFgoAIBYhBKiRTKLxETnGppoA +GPs82bFd5hWWBQJm2xA3AhsMAAoJEPs82bFd5hWWDKABAOl+NoM6FBhKAvckUXDR +MLZ4k778N4Vy9VHbectjRKj1AQCO3JOmON+U6/mjohXrc2bwzKzt2yGiLP2HMxDx +uzMXBQ== +=4XHC +-----END PGP PRIVATE KEY BLOCK----- diff --git a/testdata/simple-directory-provider/openpgp/pubkey.asc b/testdata/simple-directory-provider/openpgp/pubkey.asc new file mode 100644 index 00000000..88cb720d --- /dev/null +++ b/testdata/simple-directory-provider/openpgp/pubkey.asc @@ -0,0 +1,13 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mDMEZtsQNxYJKwYBBAHaRw8BAQdASr3y4zW+4XGqUlvRJ7stRCUHv8HB4ZoMoTtU +KLgnHr60FHNlY3VyaXR5QGV4YW1wbGUuY29tiJkEExYKAEEWIQSokUyi8RE5xqaa +ABj7PNmxXeYVlgUCZtsQNwIbAwUJBaOagAULCQgHAgIiAgYVCgkICwIEFgIDAQIe +BwIXgAAKCRD7PNmxXeYVlmyRAQCDkO22qbpMDsy0vHPtVKWSXH3ktCv13mObiNw5 +qqUZPQD/WYACVSpIPJIUYjoMVAtxplpR31iX7VvQhyvS1SI5pAe4OARm2xA3Egor +BgEEAZdVAQUBAQdA5Mc/gWSikgYkxZdNixp5DUO6B37I1hYHfnu9FirpCyIDAQgH +iHgEGBYKACAWIQSokUyi8RE5xqaaABj7PNmxXeYVlgUCZtsQNwIbDAAKCRD7PNmx +XeYVlgygAQDpfjaDOhQYSgL3JFFw0TC2eJO+/DeFcvVR23nLY0So9QEAjtyTpjjf +lOv5o6IV63Nm8Mys7dshoiz9hzMQ8bszFwU= +=rhGT +-----END PGP PUBLIC KEY BLOCK----- diff --git a/testdata/simple-directory-provider/provider-metadata.json b/testdata/simple-directory-provider/provider-metadata.json new file mode 100644 index 00000000..792afd32 --- /dev/null +++ b/testdata/simple-directory-provider/provider-metadata.json @@ -0,0 +1,25 @@ +{ + "canonical_url": "{{.URL}}/provider-metadata.json", + "distributions": [ + { + "directory_url": "{{.URL}}/white/" + } + ], + "last_updated": "2020-01-01T00:00:00Z", + "list_on_CSAF_aggregators": true, + "metadata_version": "2.0", + "mirror_on_CSAF_aggregators": true, + "public_openpgp_keys": [ + { + "fingerprint": "A8914CA2F11139C6A69A0018FB3CD9B15DE61596", + "url": "{{.URL}}/openpgp/pubkey.asc" + } + ], + "publisher": { + "category": "vendor", + "name": "ACME Inc", + "namespace": "https://example.com", + "contact_details": "mailto:security@example.com" + }, + "role": "csaf_trusted_provider" +} diff --git a/testdata/simple-directory-provider/security.txt b/testdata/simple-directory-provider/security.txt new file mode 100644 index 00000000..0ae943dc --- /dev/null +++ b/testdata/simple-directory-provider/security.txt @@ -0,0 +1,2 @@ +CSAF: /provider-metadata.json + diff --git a/testdata/simple-directory-provider/white/avendor-advisory-0004-not-listed.json b/testdata/simple-directory-provider/white/avendor-advisory-0004-not-listed.json new file mode 100644 index 00000000..0e194e9f --- /dev/null +++ b/testdata/simple-directory-provider/white/avendor-advisory-0004-not-listed.json @@ -0,0 +1,170 @@ +{ + "document": { + "category": "csaf_vex", + "csaf_version": "2.0", + "distribution": { + "tlp": { + "label": "WHITE", + "url": "https://www.first.org/tlp/v1/" + } + }, + "notes": [ + { + "category": "summary", + "title": "Test document summary", + "text": "Auto generated test CSAF document" + } + ], + "publisher": { + "category": "vendor", + "name": "ACME Inc.", + "namespace": "https://www.example.com" + }, + "title": "Test CSAF document", + "tracking": { + "current_release_date": "2020-01-01T00:00:00Z", + "generator": { + "date": "2020-01-01T00:00:00Z", + "engine": { + "name": "csaf-tool", + "version": "0.3.2" + } + }, + "id": "Avendor-advisory-0004", + "initial_release_date": "2020-01-01T00:00:00Z", + "revision_history": [ + { + "date": "2020-01-01T00:00:00Z", + "number": "1", + "summary": "Initial version" + } + ], + "status": "final", + "version": "1" + } + }, + "product_tree": { + "branches": [ + { + "category": "vendor", + "name": "AVendor", + "branches": [ + { + "category": "product_name", + "name": "product_1", + "branches": [ + { + "category": "product_version", + "name": "1.1", + "product": { + "name": "AVendor product_1 1.1", + "product_id": "CSAFPID_0001" + } + }, + { + "category": "product_version", + "name": "1.2", + "product": { + "name": "AVendor product_1 1.2", + "product_id": "CSAFPID_0002" + } + }, + { + "category": "product_version", + "name": "2.0", + "product": { + "name": "AVendor product_1 2.0", + "product_id": "CSAFPID_0003" + } + } + ] + } + ] + }, + { + "category": "vendor", + "name": "AVendor1", + "branches": [ + { + "category": "product_name", + "name": "product_2", + "branches": [ + { + "category": "product_version", + "name": "1", + "product": { + "name": "AVendor1 product_2 1", + "product_id": "CSAFPID_0004" + } + } + ] + } + ] + }, + { + "category": "vendor", + "name": "AVendor", + "branches": [ + { + "category": "product_name", + "name": "product_3", + "branches": [ + { + "category": "product_version", + "name": "2022H2", + "product": { + "name": "AVendor product_3 2022H2", + "product_id": "CSAFPID_0005" + } + } + ] + } + ] + } + ] + }, + "vulnerabilities": [ + { + "cve": "CVE-2020-1234", + "notes": [ + { + "category": "description", + "title": "CVE description", + "text": "https://nvd.nist.gov/vuln/detail/CVE-2020-1234" + } + ], + "product_status": { + "under_investigation": ["CSAFPID_0001"] + }, + "threats": [ + { + "category": "impact", + "details": "Customers should upgrade to the latest version of the product", + "date": "2020-01-01T00:00:00Z", + "product_ids": ["CSAFPID_0001"] + } + ] + }, + { + "cve": "CVE-2020-9876", + "notes": [ + { + "category": "description", + "title": "CVE description", + "text": "https://nvd.nist.gov/vuln/detail/CVE-2020-9876" + } + ], + "product_status": { + "under_investigation": ["CSAFPID_0001"] + }, + "threats": [ + { + "category": "impact", + "details": "Still under investigation", + "date": "2020-01-01T00:00:00Z", + "product_ids": ["CSAFPID_0001"] + } + ] + } + ] +} diff --git a/testdata/simple-directory-provider/white/avendor-advisory-0004.json b/testdata/simple-directory-provider/white/avendor-advisory-0004.json new file mode 100644 index 00000000..0e194e9f --- /dev/null +++ b/testdata/simple-directory-provider/white/avendor-advisory-0004.json @@ -0,0 +1,170 @@ +{ + "document": { + "category": "csaf_vex", + "csaf_version": "2.0", + "distribution": { + "tlp": { + "label": "WHITE", + "url": "https://www.first.org/tlp/v1/" + } + }, + "notes": [ + { + "category": "summary", + "title": "Test document summary", + "text": "Auto generated test CSAF document" + } + ], + "publisher": { + "category": "vendor", + "name": "ACME Inc.", + "namespace": "https://www.example.com" + }, + "title": "Test CSAF document", + "tracking": { + "current_release_date": "2020-01-01T00:00:00Z", + "generator": { + "date": "2020-01-01T00:00:00Z", + "engine": { + "name": "csaf-tool", + "version": "0.3.2" + } + }, + "id": "Avendor-advisory-0004", + "initial_release_date": "2020-01-01T00:00:00Z", + "revision_history": [ + { + "date": "2020-01-01T00:00:00Z", + "number": "1", + "summary": "Initial version" + } + ], + "status": "final", + "version": "1" + } + }, + "product_tree": { + "branches": [ + { + "category": "vendor", + "name": "AVendor", + "branches": [ + { + "category": "product_name", + "name": "product_1", + "branches": [ + { + "category": "product_version", + "name": "1.1", + "product": { + "name": "AVendor product_1 1.1", + "product_id": "CSAFPID_0001" + } + }, + { + "category": "product_version", + "name": "1.2", + "product": { + "name": "AVendor product_1 1.2", + "product_id": "CSAFPID_0002" + } + }, + { + "category": "product_version", + "name": "2.0", + "product": { + "name": "AVendor product_1 2.0", + "product_id": "CSAFPID_0003" + } + } + ] + } + ] + }, + { + "category": "vendor", + "name": "AVendor1", + "branches": [ + { + "category": "product_name", + "name": "product_2", + "branches": [ + { + "category": "product_version", + "name": "1", + "product": { + "name": "AVendor1 product_2 1", + "product_id": "CSAFPID_0004" + } + } + ] + } + ] + }, + { + "category": "vendor", + "name": "AVendor", + "branches": [ + { + "category": "product_name", + "name": "product_3", + "branches": [ + { + "category": "product_version", + "name": "2022H2", + "product": { + "name": "AVendor product_3 2022H2", + "product_id": "CSAFPID_0005" + } + } + ] + } + ] + } + ] + }, + "vulnerabilities": [ + { + "cve": "CVE-2020-1234", + "notes": [ + { + "category": "description", + "title": "CVE description", + "text": "https://nvd.nist.gov/vuln/detail/CVE-2020-1234" + } + ], + "product_status": { + "under_investigation": ["CSAFPID_0001"] + }, + "threats": [ + { + "category": "impact", + "details": "Customers should upgrade to the latest version of the product", + "date": "2020-01-01T00:00:00Z", + "product_ids": ["CSAFPID_0001"] + } + ] + }, + { + "cve": "CVE-2020-9876", + "notes": [ + { + "category": "description", + "title": "CVE description", + "text": "https://nvd.nist.gov/vuln/detail/CVE-2020-9876" + } + ], + "product_status": { + "under_investigation": ["CSAFPID_0001"] + }, + "threats": [ + { + "category": "impact", + "details": "Still under investigation", + "date": "2020-01-01T00:00:00Z", + "product_ids": ["CSAFPID_0001"] + } + ] + } + ] +} diff --git a/testdata/simple-directory-provider/white/avendor-advisory-0004.json.asc b/testdata/simple-directory-provider/white/avendor-advisory-0004.json.asc new file mode 100644 index 00000000..9dff47ba --- /dev/null +++ b/testdata/simple-directory-provider/white/avendor-advisory-0004.json.asc @@ -0,0 +1,7 @@ +-----BEGIN PGP SIGNATURE----- + +iHUEABYKAB0WIQSokUyi8RE5xqaaABj7PNmxXeYVlgUCZukv9QAKCRD7PNmxXeYV +ljq0AP9n/rTgoNCJzSTZzNrrMy28ZR+Ppp1MSPWGFUzsx6qLJgD/d8cu0lokMsXf +y0uc9k7hrla/ajFUzNt3AVvT+CPFtAo= +=7E66 +-----END PGP SIGNATURE----- diff --git a/testdata/simple-directory-provider/white/avendor-advisory-0004.json.sha256 b/testdata/simple-directory-provider/white/avendor-advisory-0004.json.sha256 new file mode 100644 index 00000000..851b27c6 --- /dev/null +++ b/testdata/simple-directory-provider/white/avendor-advisory-0004.json.sha256 @@ -0,0 +1 @@ +cb263bf1beab18b893de63f2966d0d8c5f38d60101c24d3fd7a5feebaad02c3b avendor-advisory-0004.json diff --git a/testdata/simple-directory-provider/white/avendor-advisory-0004.json.sha512 b/testdata/simple-directory-provider/white/avendor-advisory-0004.json.sha512 new file mode 100644 index 00000000..6703550d --- /dev/null +++ b/testdata/simple-directory-provider/white/avendor-advisory-0004.json.sha512 @@ -0,0 +1 @@ +39476e1d08a0871d166091c90de259544382a3599eebda118a93468499a30fd034286086c461a97d3d5298e093b0be3868e8d89d8a6a255c4aa6adb81ebbfcad avendor-advisory-0004.json diff --git a/testdata/simple-directory-provider/white/changes.csv b/testdata/simple-directory-provider/white/changes.csv new file mode 100644 index 00000000..4acdb290 --- /dev/null +++ b/testdata/simple-directory-provider/white/changes.csv @@ -0,0 +1 @@ +"avendor-advisory-0004.json","2020-01-01T00:00:00+00:00" diff --git a/testdata/simple-directory-provider/white/index.html b/testdata/simple-directory-provider/white/index.html new file mode 100644 index 00000000..bcfabd93 --- /dev/null +++ b/testdata/simple-directory-provider/white/index.html @@ -0,0 +1,6 @@ + + + + avendor-advisory-0004 + + diff --git a/testdata/simple-directory-provider/white/index.txt b/testdata/simple-directory-provider/white/index.txt new file mode 100644 index 00000000..d19d30f7 --- /dev/null +++ b/testdata/simple-directory-provider/white/index.txt @@ -0,0 +1 @@ +avendor-advisory-0004.json diff --git a/testdata/simple-rolie-provider/openpgp/info.txt b/testdata/simple-rolie-provider/openpgp/info.txt new file mode 100644 index 00000000..3a159f68 --- /dev/null +++ b/testdata/simple-rolie-provider/openpgp/info.txt @@ -0,0 +1,2 @@ +The GPG key was generated with no passphrase and this command: +`gpg --default-new-key-algo "ed25519/cert,sign+cv25519/encr" --quick-generate-key security@example.com"` diff --git a/testdata/simple-rolie-provider/openpgp/privkey.asc b/testdata/simple-rolie-provider/openpgp/privkey.asc new file mode 100644 index 00000000..816f3097 --- /dev/null +++ b/testdata/simple-rolie-provider/openpgp/privkey.asc @@ -0,0 +1,15 @@ +-----BEGIN PGP PRIVATE KEY BLOCK----- + +lFgEZtsQNxYJKwYBBAHaRw8BAQdASr3y4zW+4XGqUlvRJ7stRCUHv8HB4ZoMoTtU +KLgnHr4AAQD5G5xy/yTN5b+lvV5Ahrbz1qOZ/wmKTieGOH9GZb6JwhHwtBRzZWN1 +cml0eUBleGFtcGxlLmNvbYiZBBMWCgBBFiEEqJFMovEROcammgAY+zzZsV3mFZYF +AmbbEDcCGwMFCQWjmoAFCwkIBwICIgIGFQoJCAsCBBYCAwECHgcCF4AACgkQ+zzZ +sV3mFZZskQEAg5Dttqm6TA7MtLxz7VSlklx95LQr9d5jm4jcOaqlGT0A/1mAAlUq +SDySFGI6DFQLcaZaUd9Yl+1b0Icr0tUiOaQHnF0EZtsQNxIKKwYBBAGXVQEFAQEH +QOTHP4FkopIGJMWXTYsaeQ1Dugd+yNYWB357vRYq6QsiAwEIBwAA/0RIazq1s8Oe +23jvNaZGb/adDYnRrkCMXXTBKsuA6WOAEhKIeAQYFgoAIBYhBKiRTKLxETnGppoA +GPs82bFd5hWWBQJm2xA3AhsMAAoJEPs82bFd5hWWDKABAOl+NoM6FBhKAvckUXDR +MLZ4k778N4Vy9VHbectjRKj1AQCO3JOmON+U6/mjohXrc2bwzKzt2yGiLP2HMxDx +uzMXBQ== +=4XHC +-----END PGP PRIVATE KEY BLOCK----- diff --git a/testdata/simple-rolie-provider/openpgp/pubkey.asc b/testdata/simple-rolie-provider/openpgp/pubkey.asc new file mode 100644 index 00000000..88cb720d --- /dev/null +++ b/testdata/simple-rolie-provider/openpgp/pubkey.asc @@ -0,0 +1,13 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- + +mDMEZtsQNxYJKwYBBAHaRw8BAQdASr3y4zW+4XGqUlvRJ7stRCUHv8HB4ZoMoTtU +KLgnHr60FHNlY3VyaXR5QGV4YW1wbGUuY29tiJkEExYKAEEWIQSokUyi8RE5xqaa +ABj7PNmxXeYVlgUCZtsQNwIbAwUJBaOagAULCQgHAgIiAgYVCgkICwIEFgIDAQIe +BwIXgAAKCRD7PNmxXeYVlmyRAQCDkO22qbpMDsy0vHPtVKWSXH3ktCv13mObiNw5 +qqUZPQD/WYACVSpIPJIUYjoMVAtxplpR31iX7VvQhyvS1SI5pAe4OARm2xA3Egor +BgEEAZdVAQUBAQdA5Mc/gWSikgYkxZdNixp5DUO6B37I1hYHfnu9FirpCyIDAQgH +iHgEGBYKACAWIQSokUyi8RE5xqaaABj7PNmxXeYVlgUCZtsQNwIbDAAKCRD7PNmx +XeYVlgygAQDpfjaDOhQYSgL3JFFw0TC2eJO+/DeFcvVR23nLY0So9QEAjtyTpjjf +lOv5o6IV63Nm8Mys7dshoiz9hzMQ8bszFwU= +=rhGT +-----END PGP PUBLIC KEY BLOCK----- diff --git a/testdata/simple-rolie-provider/provider-metadata.json b/testdata/simple-rolie-provider/provider-metadata.json new file mode 100644 index 00000000..7abb3163 --- /dev/null +++ b/testdata/simple-rolie-provider/provider-metadata.json @@ -0,0 +1,33 @@ +{ + "canonical_url": "{{.URL}}/provider-metadata.json", + "distributions": [ + { + "rolie": { + "feeds": [ + { + "summary": "TLP:WHITE advisories", + "tlp_label": "WHITE", + "url": "{{.URL}}/white/white-feed.json" + } + ] + } + } + ], + "last_updated": "2020-01-01T00:00:00Z", + "list_on_CSAF_aggregators": true, + "metadata_version": "2.0", + "mirror_on_CSAF_aggregators": true, + "public_openpgp_keys": [ + { + "fingerprint": "A8914CA2F11139C6A69A0018FB3CD9B15DE61596", + "url": "{{.URL}}/openpgp/pubkey.asc" + } + ], + "publisher": { + "category": "vendor", + "name": "ACME Inc", + "namespace": "https://example.com", + "contact_details": "mailto:security@example.com" + }, + "role": "csaf_trusted_provider" +} diff --git a/testdata/simple-rolie-provider/security.txt b/testdata/simple-rolie-provider/security.txt new file mode 100644 index 00000000..0ae943dc --- /dev/null +++ b/testdata/simple-rolie-provider/security.txt @@ -0,0 +1,2 @@ +CSAF: /provider-metadata.json + diff --git a/testdata/simple-rolie-provider/service.json b/testdata/simple-rolie-provider/service.json new file mode 100644 index 00000000..500d882a --- /dev/null +++ b/testdata/simple-rolie-provider/service.json @@ -0,0 +1,23 @@ +{ + "service": { + "workspace": [ + { + "title": "CSAF feeds", + "collection": [ + { + "title": "CSAF feed (TLP:WHITE)", + "href": "/white/white-feed.json", + "categories": { + "category": [ + { + "scheme": "urn:ietf:params:rolie:category:information-type", + "term": "csaf" + } + ] + } + } + ] + } + ] + } +} diff --git a/testdata/simple-rolie-provider/white/avendor-advisory-0004.json b/testdata/simple-rolie-provider/white/avendor-advisory-0004.json new file mode 100644 index 00000000..0e194e9f --- /dev/null +++ b/testdata/simple-rolie-provider/white/avendor-advisory-0004.json @@ -0,0 +1,170 @@ +{ + "document": { + "category": "csaf_vex", + "csaf_version": "2.0", + "distribution": { + "tlp": { + "label": "WHITE", + "url": "https://www.first.org/tlp/v1/" + } + }, + "notes": [ + { + "category": "summary", + "title": "Test document summary", + "text": "Auto generated test CSAF document" + } + ], + "publisher": { + "category": "vendor", + "name": "ACME Inc.", + "namespace": "https://www.example.com" + }, + "title": "Test CSAF document", + "tracking": { + "current_release_date": "2020-01-01T00:00:00Z", + "generator": { + "date": "2020-01-01T00:00:00Z", + "engine": { + "name": "csaf-tool", + "version": "0.3.2" + } + }, + "id": "Avendor-advisory-0004", + "initial_release_date": "2020-01-01T00:00:00Z", + "revision_history": [ + { + "date": "2020-01-01T00:00:00Z", + "number": "1", + "summary": "Initial version" + } + ], + "status": "final", + "version": "1" + } + }, + "product_tree": { + "branches": [ + { + "category": "vendor", + "name": "AVendor", + "branches": [ + { + "category": "product_name", + "name": "product_1", + "branches": [ + { + "category": "product_version", + "name": "1.1", + "product": { + "name": "AVendor product_1 1.1", + "product_id": "CSAFPID_0001" + } + }, + { + "category": "product_version", + "name": "1.2", + "product": { + "name": "AVendor product_1 1.2", + "product_id": "CSAFPID_0002" + } + }, + { + "category": "product_version", + "name": "2.0", + "product": { + "name": "AVendor product_1 2.0", + "product_id": "CSAFPID_0003" + } + } + ] + } + ] + }, + { + "category": "vendor", + "name": "AVendor1", + "branches": [ + { + "category": "product_name", + "name": "product_2", + "branches": [ + { + "category": "product_version", + "name": "1", + "product": { + "name": "AVendor1 product_2 1", + "product_id": "CSAFPID_0004" + } + } + ] + } + ] + }, + { + "category": "vendor", + "name": "AVendor", + "branches": [ + { + "category": "product_name", + "name": "product_3", + "branches": [ + { + "category": "product_version", + "name": "2022H2", + "product": { + "name": "AVendor product_3 2022H2", + "product_id": "CSAFPID_0005" + } + } + ] + } + ] + } + ] + }, + "vulnerabilities": [ + { + "cve": "CVE-2020-1234", + "notes": [ + { + "category": "description", + "title": "CVE description", + "text": "https://nvd.nist.gov/vuln/detail/CVE-2020-1234" + } + ], + "product_status": { + "under_investigation": ["CSAFPID_0001"] + }, + "threats": [ + { + "category": "impact", + "details": "Customers should upgrade to the latest version of the product", + "date": "2020-01-01T00:00:00Z", + "product_ids": ["CSAFPID_0001"] + } + ] + }, + { + "cve": "CVE-2020-9876", + "notes": [ + { + "category": "description", + "title": "CVE description", + "text": "https://nvd.nist.gov/vuln/detail/CVE-2020-9876" + } + ], + "product_status": { + "under_investigation": ["CSAFPID_0001"] + }, + "threats": [ + { + "category": "impact", + "details": "Still under investigation", + "date": "2020-01-01T00:00:00Z", + "product_ids": ["CSAFPID_0001"] + } + ] + } + ] +} diff --git a/testdata/simple-rolie-provider/white/avendor-advisory-0004.json.asc b/testdata/simple-rolie-provider/white/avendor-advisory-0004.json.asc new file mode 100644 index 00000000..9dff47ba --- /dev/null +++ b/testdata/simple-rolie-provider/white/avendor-advisory-0004.json.asc @@ -0,0 +1,7 @@ +-----BEGIN PGP SIGNATURE----- + +iHUEABYKAB0WIQSokUyi8RE5xqaaABj7PNmxXeYVlgUCZukv9QAKCRD7PNmxXeYV +ljq0AP9n/rTgoNCJzSTZzNrrMy28ZR+Ppp1MSPWGFUzsx6qLJgD/d8cu0lokMsXf +y0uc9k7hrla/ajFUzNt3AVvT+CPFtAo= +=7E66 +-----END PGP SIGNATURE----- diff --git a/testdata/simple-rolie-provider/white/avendor-advisory-0004.json.sha256 b/testdata/simple-rolie-provider/white/avendor-advisory-0004.json.sha256 new file mode 100644 index 00000000..851b27c6 --- /dev/null +++ b/testdata/simple-rolie-provider/white/avendor-advisory-0004.json.sha256 @@ -0,0 +1 @@ +cb263bf1beab18b893de63f2966d0d8c5f38d60101c24d3fd7a5feebaad02c3b avendor-advisory-0004.json diff --git a/testdata/simple-rolie-provider/white/avendor-advisory-0004.json.sha512 b/testdata/simple-rolie-provider/white/avendor-advisory-0004.json.sha512 new file mode 100644 index 00000000..6703550d --- /dev/null +++ b/testdata/simple-rolie-provider/white/avendor-advisory-0004.json.sha512 @@ -0,0 +1 @@ +39476e1d08a0871d166091c90de259544382a3599eebda118a93468499a30fd034286086c461a97d3d5298e093b0be3868e8d89d8a6a255c4aa6adb81ebbfcad avendor-advisory-0004.json diff --git a/testdata/simple-rolie-provider/white/white-feed.json b/testdata/simple-rolie-provider/white/white-feed.json new file mode 100644 index 00000000..1bc17bcf --- /dev/null +++ b/testdata/simple-rolie-provider/white/white-feed.json @@ -0,0 +1,61 @@ +{ + "feed": { + "id": "csaf-feed-tlp-white", + "title": "CSAF feed (TLP:WHITE)", + "link": [ + { + "rel": "self", + "href": "/white/csaf-feed-tlp-white.json" + }, + { + "rel": "service", + "href": "/service.json" + } + ], + "category": [ + { + "scheme": "urn:ietf:params:rolie:category:information-type", + "term": "csaf" + } + ], + "updated": "2020-01-01T00:00:00Z", + "entry": [ + { + "id": "Avendor-advisory-0004", + "title": "Test CSAF document", + "link": [ + { + "rel": "self", + "href": "/white/avendor-advisory-0004.json" + }, + {{if .EnableSha256}} + { + "rel": "hash", + "href": "/white/avendor-advisory-0004.json.sha256" + }, + {{end}} + {{if .EnableSha512}} + { + "rel": "hash", + "href": "/white/avendor-advisory-0004.json.sha512" + }, + {{end}} + { + "rel": "signature", + "href": "/white/avendor-advisory-0004.json.asc" + } + ], + "published": "2020-01-01T00:00:00Z", + "updated": "2020-01-01T00:00:00Z", + "content": { + "type": "application/json", + "src": "/avendor-advisory-0004.json" + }, + "format": { + "schema": "https://docs.oasis-open.org/csaf/csaf/v2.0/csaf_json_schema.json", + "version": "2.0" + } + } + ] + } +} From 56509bbb4d868454d01e3b7ce9dffd8bdb658e58 Mon Sep 17 00:00:00 2001 From: koplas Date: Wed, 27 Nov 2024 12:51:38 +0100 Subject: [PATCH 27/64] Use new path in tests --- cmd/csaf_aggregator/client_test.go | 2 +- cmd/csaf_checker/links_test.go | 2 +- cmd/csaf_downloader/downloader_test.go | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cmd/csaf_aggregator/client_test.go b/cmd/csaf_aggregator/client_test.go index c08b29ab..fc5b0952 100644 --- a/cmd/csaf_aggregator/client_test.go +++ b/cmd/csaf_aggregator/client_test.go @@ -14,7 +14,7 @@ import ( "net/http/httptest" "testing" - "github.com/csaf-poc/csaf_distribution/v3/util" + "github.com/gocsaf/csaf/v3/util" ) func Test_downloadJSON(t *testing.T) { diff --git a/cmd/csaf_checker/links_test.go b/cmd/csaf_checker/links_test.go index aa04222d..6baccf89 100644 --- a/cmd/csaf_checker/links_test.go +++ b/cmd/csaf_checker/links_test.go @@ -15,7 +15,7 @@ import ( "strings" "testing" - "github.com/csaf-poc/csaf_distribution/v3/util" + "github.com/gocsaf/csaf/v3/util" ) const page0 = ` diff --git a/cmd/csaf_downloader/downloader_test.go b/cmd/csaf_downloader/downloader_test.go index cf020355..1ae15242 100644 --- a/cmd/csaf_downloader/downloader_test.go +++ b/cmd/csaf_downloader/downloader_test.go @@ -19,8 +19,8 @@ import ( "strings" "testing" - "github.com/csaf-poc/csaf_distribution/v3/internal/options" - "github.com/csaf-poc/csaf_distribution/v3/util" + "github.com/gocsaf/csaf/v3/internal/options" + "github.com/gocsaf/csaf/v3/util" ) type ProviderParams struct { From b8a98033bf3721bdec6a055dfb07873e2306e512 Mon Sep 17 00:00:00 2001 From: Bernhard Reiter Date: Thu, 28 Nov 2024 15:58:20 +0100 Subject: [PATCH 28/64] fix docs link to standard --- docs/proxy-provider-for-aggregator.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/proxy-provider-for-aggregator.md b/docs/proxy-provider-for-aggregator.md index f34d7147..4148f528 100644 --- a/docs/proxy-provider-for-aggregator.md +++ b/docs/proxy-provider-for-aggregator.md @@ -5,7 +5,9 @@ calls it a *CSAF publisher*. After manually downloading the advisories from such a publisher, the tools here can be used to offer the CSAF files for automated downloading -as *CSAF aggregator*. (The construct is called *CSAF proxy provider*. See [Section 7.2.5](https://docs.oasis-open.org/csaf/csaf/v2.0/csaf-v2.0.html#725-role-csaf-aggregator) for more details.) +as *CSAF aggregator*. (The construct is called *CSAF proxy provider*. +See [Section 7.2.5](https://docs.oasis-open.org/csaf/csaf/v2.0/os/csaf-v2.0-os.html#725-role-csaf-aggregator) +for more details.) There are three necessary steps, easiest is to use one single virtual maschine (or container) per internal provider. From 1daaed2c516d3fd674eb99c39dfc5f87ba43f78a Mon Sep 17 00:00:00 2001 From: ncsc-ie-devs <112564016+ncsc-ie-devs@users.noreply.github.com> Date: Mon, 2 Dec 2024 10:42:54 +0000 Subject: [PATCH 29/64] ensure HTTP requests use proxy env vars (#597) * fix: ensure HTTP requests use proxy env vars Updated all instances of `http.Transport` to include the `Proxy` field set to `http.ProxyFromEnvironment`. This ensures that the application respects proxy configuration defined by the `HTTP_PROXY`, `HTTPS_PROXY`, and `NO_PROXY` environment variables. ### Changes: - Modified `http.Transport` initialization across the codebase to use: ```go Proxy: http.ProxyFromEnvironment ``` - Ensured TLS configurations remain intact by preserving `TLSClientConfig`. ### Why: - Previously, HTTP requests bypassed proxy settings due to missing configuration in the transport layer. - This fix enables compatibility with proxied environments, aligning with standard Go behavior. ### Impact: - All HTTP and HTTPS traffic now adheres to proxy settings. - Domains listed in `NO_PROXY` bypass the proxy as expected. ### Verification: - Tested with proxy environment variables set (`HTTP_PROXY`, `HTTPS_PROXY`). - Verified requests route through the proxy and `NO_PROXY` works as intended. * reformat with fmt --------- Co-authored-by: Cormac Doherty --- cmd/csaf_aggregator/config.go | 1 + cmd/csaf_checker/processor.go | 2 ++ cmd/csaf_downloader/downloader.go | 1 + cmd/csaf_downloader/forwarder.go | 1 + cmd/csaf_uploader/processor.go | 1 + 5 files changed, 6 insertions(+) diff --git a/cmd/csaf_aggregator/config.go b/cmd/csaf_aggregator/config.go index 81db0b7c..3c2c46b5 100644 --- a/cmd/csaf_aggregator/config.go +++ b/cmd/csaf_aggregator/config.go @@ -284,6 +284,7 @@ func (c *config) httpClient(p *provider) util.Client { hClient.Transport = &http.Transport{ TLSClientConfig: &tlsConfig, + Proxy: http.ProxyFromEnvironment, } client := util.Client(&hClient) diff --git a/cmd/csaf_checker/processor.go b/cmd/csaf_checker/processor.go index 5fd3fbdd..5d1b69b5 100644 --- a/cmd/csaf_checker/processor.go +++ b/cmd/csaf_checker/processor.go @@ -430,6 +430,7 @@ func (p *processor) fullClient() util.Client { hClient.Transport = &http.Transport{ TLSClientConfig: &tlsConfig, + Proxy: http.ProxyFromEnvironment, } client := util.Client(&hClient) @@ -460,6 +461,7 @@ func (p *processor) basicClient() *http.Client { if p.cfg.Insecure { tr := &http.Transport{ TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + Proxy: http.ProxyFromEnvironment, } return &http.Client{Transport: tr} } diff --git a/cmd/csaf_downloader/downloader.go b/cmd/csaf_downloader/downloader.go index f21fcc0d..b7e73422 100644 --- a/cmd/csaf_downloader/downloader.go +++ b/cmd/csaf_downloader/downloader.go @@ -121,6 +121,7 @@ func (d *downloader) httpClient() util.Client { hClient.Transport = &http.Transport{ TLSClientConfig: &tlsConfig, + Proxy: http.ProxyFromEnvironment, } client := util.Client(&hClient) diff --git a/cmd/csaf_downloader/forwarder.go b/cmd/csaf_downloader/forwarder.go index 12d9fe4a..15982839 100644 --- a/cmd/csaf_downloader/forwarder.go +++ b/cmd/csaf_downloader/forwarder.go @@ -106,6 +106,7 @@ func (f *forwarder) httpClient() util.Client { hClient.Transport = &http.Transport{ TLSClientConfig: &tlsConfig, + Proxy: http.ProxyFromEnvironment, } client := util.Client(&hClient) diff --git a/cmd/csaf_uploader/processor.go b/cmd/csaf_uploader/processor.go index b57cafb9..f655e02a 100644 --- a/cmd/csaf_uploader/processor.go +++ b/cmd/csaf_uploader/processor.go @@ -51,6 +51,7 @@ func (p *processor) httpClient() *http.Client { client.Transport = &http.Transport{ TLSClientConfig: &tlsConfig, + Proxy: http.ProxyFromEnvironment, } return &client From 57953e495f10c26312a05eec3d1e7acb2a40e363 Mon Sep 17 00:00:00 2001 From: koplas Date: Wed, 4 Dec 2024 13:23:57 +0100 Subject: [PATCH 30/64] Warn if no remote validator was specified --- cmd/csaf_validator/main.go | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cmd/csaf_validator/main.go b/cmd/csaf_validator/main.go index b07c2f49..69855099 100644 --- a/cmd/csaf_validator/main.go +++ b/cmd/csaf_validator/main.go @@ -69,6 +69,8 @@ func run(opts *options, files []string) error { "preparing remote validator failed: %w", err) } defer validator.Close() + } else { + log.Printf("warn: no remote validator specified") } // Select amount level of output for remote validation. From 938ceb872ac4b5460379c86b89b6ca0db6ed72f2 Mon Sep 17 00:00:00 2001 From: koplas Date: Wed, 4 Dec 2024 13:53:56 +0100 Subject: [PATCH 31/64] Return exit code based on validation result --- cmd/csaf_validator/main.go | 13 +++++++++++++ docs/csaf_validator.md | 7 +++++++ 2 files changed, 20 insertions(+) diff --git a/cmd/csaf_validator/main.go b/cmd/csaf_validator/main.go index 69855099..4a9e8273 100644 --- a/cmd/csaf_validator/main.go +++ b/cmd/csaf_validator/main.go @@ -22,6 +22,13 @@ import ( "github.com/gocsaf/csaf/v3/util" ) +const ( + exitCodeAllValid = 0 + exitCodeSchemaInvalid = 1 << 0 + exitCodeNoRemoteValidator = 1 << 1 + exitCodeFailedRemoteValidation = 1 << 2 +) + type options struct { Version bool `long:"version" description:"Display version of the binary"` RemoteValidator string `long:"validator" description:"URL to validate documents remotely" value-name:"URL"` @@ -53,6 +60,7 @@ func main() { // run validates the given files. func run(opts *options, files []string) error { + exitCode := exitCodeAllValid var validator csaf.RemoteValidator eval := util.NewPathEval() @@ -70,6 +78,7 @@ func run(opts *options, files []string) error { } defer validator.Close() } else { + exitCode |= exitCodeNoRemoteValidator log.Printf("warn: no remote validator specified") } @@ -106,6 +115,7 @@ func run(opts *options, files []string) error { } if len(validationErrs) > 0 { + exitCode |= exitCodeSchemaInvalid fmt.Printf("schema validation errors of %q\n", file) for _, vErr := range validationErrs { fmt.Printf(" * %s\n", vErr) @@ -132,12 +142,15 @@ func run(opts *options, files []string) error { if rvr.Valid { passes = "passes" } else { + exitCode |= exitCodeFailedRemoteValidation passes = "does not pass" } fmt.Printf("%q %s remote validation.\n", file, passes) } } + // Exit code is based on validation results + os.Exit(exitCodeAllValid) return nil } diff --git a/docs/csaf_validator.md b/docs/csaf_validator.md index dfa0c9a3..74dbaaf7 100644 --- a/docs/csaf_validator.md +++ b/docs/csaf_validator.md @@ -2,6 +2,13 @@ is a tool to validate local advisories files against the JSON Schema and an optional remote validator. +### Exit codes +If no fatal error occurs the program will exit with the following codes: +- `0`: all valid +- `2⁰`: schema invalid +- `2¹`: no remote validator configured +- `2²`: failure in remote validation + ### Usage ``` From 16e86051c5d1b0912a179eb2b30ba568da4e81ce Mon Sep 17 00:00:00 2001 From: "Sascha L. Teichmann" Date: Wed, 4 Dec 2024 14:27:24 +0100 Subject: [PATCH 32/64] Be more precise about exit codes. --- cmd/csaf_validator/main.go | 8 ++++---- docs/csaf_validator.md | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cmd/csaf_validator/main.go b/cmd/csaf_validator/main.go index 4a9e8273..9e844b70 100644 --- a/cmd/csaf_validator/main.go +++ b/cmd/csaf_validator/main.go @@ -23,10 +23,10 @@ import ( ) const ( - exitCodeAllValid = 0 - exitCodeSchemaInvalid = 1 << 0 - exitCodeNoRemoteValidator = 1 << 1 - exitCodeFailedRemoteValidation = 1 << 2 + exitCodeSchemaInvalid = 1 << iota + exitCodeNoRemoteValidator + exitCodeFailedRemoteValidation + exitCodeAllValid = 0 ) type options struct { diff --git a/docs/csaf_validator.md b/docs/csaf_validator.md index 74dbaaf7..64ded6dc 100644 --- a/docs/csaf_validator.md +++ b/docs/csaf_validator.md @@ -3,11 +3,11 @@ is a tool to validate local advisories files against the JSON Schema and an optional remote validator. ### Exit codes -If no fatal error occurs the program will exit with the following codes: -- `0`: all valid -- `2⁰`: schema invalid -- `2¹`: no remote validator configured -- `2²`: failure in remote validation +If no fatal error occurs the program will exit with an exit code `n` with the following conditions: +- `n == 0`: all valid +- `(n / 2) % 1 == 1`: schema validation failed +- `(n / 4) % 1 == 1`: no remote validator configured +- `(n / 8) % 1 == 1`: failure in remote validation ### Usage From a51964be3f6a9360ed0c4e05ccc5bcc8418d0f7e Mon Sep 17 00:00:00 2001 From: koplas Date: Wed, 4 Dec 2024 16:02:03 +0100 Subject: [PATCH 33/64] Add initial csaf_checker provider test --- cmd/csaf_checker/processor_test.go | 103 +++++++++++++++++++++++++ cmd/csaf_downloader/downloader_test.go | 62 +-------------- internal/testutil/testutil.go | 73 ++++++++++++++++++ 3 files changed, 179 insertions(+), 59 deletions(-) create mode 100644 cmd/csaf_checker/processor_test.go create mode 100644 internal/testutil/testutil.go diff --git a/cmd/csaf_checker/processor_test.go b/cmd/csaf_checker/processor_test.go new file mode 100644 index 00000000..b8b1b1f8 --- /dev/null +++ b/cmd/csaf_checker/processor_test.go @@ -0,0 +1,103 @@ +// This file is Free Software under the Apache-2.0 License +// without warranty, see README.md and LICENSES/Apache-2.0.txt for details. +// +// SPDX-License-Identifier: Apache-2.0 +// +// SPDX-FileCopyrightText: 2023 German Federal Office for Information Security (BSI) +// Software-Engineering: 2023 Intevation GmbH + +package main + +import ( + "net/http/httptest" + "testing" + + "github.com/gocsaf/csaf/v3/internal/testutil" + "github.com/gocsaf/csaf/v3/util" +) + +func TestShaMarking(t *testing.T) { + tests := []struct { + name string + directoryProvider bool + enableSha256 bool + enableSha512 bool + }{ + { + name: "deliver sha256 and sha512", + directoryProvider: false, + enableSha256: true, + enableSha512: true, + }, + { + name: "only deliver sha256", + directoryProvider: false, + enableSha256: true, + enableSha512: false, + }, + { + name: "only deliver sha512", + directoryProvider: false, + enableSha256: false, + enableSha512: true, + }, + { + name: "only deliver sha256 and sha512, directory provider", + directoryProvider: true, + enableSha256: true, + enableSha512: true, + }, + { + name: "only deliver sha256, directory provider", + directoryProvider: true, + enableSha256: true, + enableSha512: false, + }, + { + name: "only deliver sha512, directory provider", + directoryProvider: true, + enableSha256: false, + enableSha512: true, + }, + } + + t.Parallel() + for _, testToRun := range tests { + test := testToRun + t.Run(test.name, func(tt *testing.T) { + tt.Parallel() + serverURL := "" + params := testutil.ProviderParams{ + URL: "", + EnableSha256: test.enableSha256, + EnableSha512: test.enableSha512, + } + server := httptest.NewTLSServer(testutil.ProviderHandler(¶ms, test.directoryProvider)) + defer server.Close() + + serverURL = server.URL + params.URL = server.URL + + hClient := server.Client() + client := util.Client(hClient) + + cfg := config{} + err := cfg.prepare() + if err != nil { + t.Fatalf("SHA marking config failed: %v", err) + } + p, err := newProcessor(&cfg) + if err != nil { + t.Fatalf("could not init downloader: %v", err) + } + p.client = client + + // TODO check result of processor + _, err = p.run([]string{serverURL + "/provider-metadata.json"}) + if err != nil { + t.Errorf("SHA marking %v: Expected no error, got: %v", test.name, err) + } + p.close() + }) + } +} diff --git a/cmd/csaf_downloader/downloader_test.go b/cmd/csaf_downloader/downloader_test.go index 1ae15242..d7eaae34 100644 --- a/cmd/csaf_downloader/downloader_test.go +++ b/cmd/csaf_downloader/downloader_test.go @@ -11,72 +11,16 @@ package main import ( "context" "errors" - "html/template" "log/slog" - "net/http" "net/http/httptest" "os" - "strings" "testing" "github.com/gocsaf/csaf/v3/internal/options" + "github.com/gocsaf/csaf/v3/internal/testutil" "github.com/gocsaf/csaf/v3/util" ) -type ProviderParams struct { - URL string - EnableSha256 bool - EnableSha512 bool -} - -func ProviderHandler(params *ProviderParams, directoryProvider bool) http.HandlerFunc { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - path := "../../testdata/" - if directoryProvider { - path += "simple-directory-provider" - } else { - path += "simple-rolie-provider" - } - - path += r.URL.Path - - if strings.HasSuffix(r.URL.Path, "/") { - path += "index.html" - } - - content, err := os.ReadFile(path) - if err != nil { - w.WriteHeader(http.StatusNotFound) - return - } - switch { - case strings.HasSuffix(path, ".html"): - w.Header().Add("Content-Type", "text/html") - case strings.HasSuffix(path, ".json"): - w.Header().Add("Content-Type", "application/json") - case strings.HasSuffix(path, ".sha256") && directoryProvider && !params.EnableSha256: - w.WriteHeader(http.StatusNotFound) - return - case strings.HasSuffix(path, ".sha512") && directoryProvider && !params.EnableSha512: - w.WriteHeader(http.StatusNotFound) - return - default: - w.Header().Add("Content-Type", "text/plain") - } - - tmplt, err := template.New("base").Parse(string(content)) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - return - } - err = tmplt.Execute(w, params) - if err != nil { - w.WriteHeader(http.StatusInternalServerError) - return - } - }) -} - func checkIfFileExists(path string, t *testing.T) bool { if _, err := os.Stat(path); err == nil { return true @@ -169,12 +113,12 @@ func TestShaMarking(t *testing.T) { t.Run(test.name, func(tt *testing.T) { tt.Parallel() serverURL := "" - params := ProviderParams{ + params := testutil.ProviderParams{ URL: "", EnableSha256: test.enableSha256, EnableSha512: test.enableSha512, } - server := httptest.NewTLSServer(ProviderHandler(¶ms, test.directoryProvider)) + server := httptest.NewTLSServer(testutil.ProviderHandler(¶ms, test.directoryProvider)) defer server.Close() serverURL = server.URL diff --git a/internal/testutil/testutil.go b/internal/testutil/testutil.go new file mode 100644 index 00000000..455d217c --- /dev/null +++ b/internal/testutil/testutil.go @@ -0,0 +1,73 @@ +// This file is Free Software under the Apache-2.0 License +// without warranty, see README.md and LICENSES/Apache-2.0.txt for details. +// +// SPDX-License-Identifier: Apache-2.0 +// +// SPDX-FileCopyrightText: 2023 German Federal Office for Information Security (BSI) +// Software-Engineering: 2023 Intevation GmbH + +// Package testutil contains shared helper functions for testing the application. +package testutil + +import ( + "html/template" + "net/http" + "os" + "strings" +) + +// ProviderParams configures the test provider. +type ProviderParams struct { + URL string + EnableSha256 bool + EnableSha512 bool +} + +// ProviderHandler returns a test provider handler with the specified configuration. +func ProviderHandler(params *ProviderParams, directoryProvider bool) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + path := "../../testdata/" + if directoryProvider { + path += "simple-directory-provider" + } else { + path += "simple-rolie-provider" + } + + path += r.URL.Path + + if strings.HasSuffix(r.URL.Path, "/") { + path += "index.html" + } + + content, err := os.ReadFile(path) + if err != nil { + w.WriteHeader(http.StatusNotFound) + return + } + switch { + case strings.HasSuffix(path, ".html"): + w.Header().Add("Content-Type", "text/html") + case strings.HasSuffix(path, ".json"): + w.Header().Add("Content-Type", "application/json") + case strings.HasSuffix(path, ".sha256") && directoryProvider && !params.EnableSha256: + w.WriteHeader(http.StatusNotFound) + return + case strings.HasSuffix(path, ".sha512") && directoryProvider && !params.EnableSha512: + w.WriteHeader(http.StatusNotFound) + return + default: + w.Header().Add("Content-Type", "text/plain") + } + + tmplt, err := template.New("base").Parse(string(content)) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } + err = tmplt.Execute(w, params) + if err != nil { + w.WriteHeader(http.StatusInternalServerError) + return + } + } +} From 5b6af7a4ad26bb53795e94fe3576a636b0b81df1 Mon Sep 17 00:00:00 2001 From: koplas Date: Wed, 4 Dec 2024 17:52:00 +0100 Subject: [PATCH 34/64] WIP: Add requirement tests --- cmd/csaf_checker/processor_test.go | 106 +++++++++++++++++- testdata/simple-rolie-provider/service.json | 2 +- .../white/white-feed.json | 14 +-- 3 files changed, 112 insertions(+), 10 deletions(-) diff --git a/cmd/csaf_checker/processor_test.go b/cmd/csaf_checker/processor_test.go index b8b1b1f8..73574bde 100644 --- a/cmd/csaf_checker/processor_test.go +++ b/cmd/csaf_checker/processor_test.go @@ -9,55 +9,150 @@ package main import ( + "fmt" "net/http/httptest" + "reflect" "testing" "github.com/gocsaf/csaf/v3/internal/testutil" "github.com/gocsaf/csaf/v3/util" ) +func getBaseRequirements(url string) []Requirement { + return []Requirement{ + { + Num: 1, + Description: "Valid CSAF documents", + Messages: []Message{{Type: 1, Text: "No remote validator configured"}, {Type: 0, Text: "All advisories validated fine against the schema."}}, + }, { + Num: 2, + Description: "Filename", + Messages: []Message{{Type: 0, Text: "All found filenames are conforming."}}}, + { + Num: 3, + Description: "TLS", + Messages: []Message{{Type: 0, Text: "All tested URLs were HTTPS."}}}, + { + Num: 4, + Description: "TLP:WHITE", + Messages: []Message{{Type: 0, Text: "All advisories labeled TLP:WHITE were freely accessible."}}}, + { + Num: 5, + Description: "TLP:AMBER and TLP:RED", + Messages: []Message{ + {Type: 0, Text: "No advisories labeled TLP:AMBER or TLP:RED tested for accessibility."}}}, + { + Num: 6, + Description: "Redirects", + Messages: []Message{{Type: 0, Text: "No redirections found."}}}, + { + Num: 7, + Description: "provider-metadata.json", + Messages: []Message{{Type: 0, Text: "Found good provider metadata."}}}, + { + Num: 8, + Description: "security.txt", + Messages: []Message{{Type: 0, Text: "Performed no test of security.txt since the direct url of the provider-metadata.json was used."}}}, + { + Num: 9, + Description: "/.well-known/csaf/provider-metadata.json", + Messages: []Message{{Type: 0, Text: "Performed no test on whether the provider-metadata.json is available under the .well-known path since the direct url of the provider-metadata.json was used."}}}, + { + Num: 10, + Description: "DNS path", + Messages: []Message{{Type: 0, Text: "Performed no test on the contents of https://csaf.data.security.DOMAIN since the direct url of the provider-metadata.json was used."}}}, + { + Num: 11, + Description: "One folder per year", + Messages: []Message{{Type: 2, Text: fmt.Sprintf("No year folder found in %s/white/avendor-advisory-0004.json", url)}}}, + { + Num: 12, + Description: "index.txt", + Messages: []Message{{Type: 0, Text: fmt.Sprintf("Found %s/white/index.txt", url)}}}, + { + Num: 13, + Description: "changes.csv", + Messages: []Message{{Type: 0, Text: fmt.Sprintf("Found %s/white/changes.csv", url)}}}, + { + Num: 14, + Description: "Directory listings", + Messages: []Message{{Type: 0, Text: "All directory listings are valid."}}}, + { + Num: 15, + Description: "ROLIE feed", + Messages: []Message{{Type: 2, Text: "ROLIE feed based distribution was not used."}}}, + { + Num: 16, + Description: "ROLIE service document", + Messages: []Message{{Type: 1, Text: "No ROLIE service document found."}}}, + { + Num: 17, + Description: "ROLIE category document", + Messages: []Message{{Type: 1, Text: "No ROLIE category document found."}}}, + { + Num: 18, + Description: "Integrity", + Messages: []Message{{Type: 0, Text: "All checksums match."}}}, + { + Num: 19, + Description: "Signatures", + Messages: []Message{{Type: 0, Text: "All signatures verified."}}}, + { + Num: 20, + Description: "Public OpenPGP Key", + Messages: []Message{{Type: 0, Text: "1 public OpenPGP key(s) loaded."}}}, + } +} + func TestShaMarking(t *testing.T) { tests := []struct { name string directoryProvider bool enableSha256 bool enableSha512 bool + expected func(string) []Requirement }{ { name: "deliver sha256 and sha512", directoryProvider: false, enableSha256: true, enableSha512: true, + expected: getBaseRequirements, }, { name: "only deliver sha256", directoryProvider: false, enableSha256: true, enableSha512: false, + expected: getBaseRequirements, }, { name: "only deliver sha512", directoryProvider: false, enableSha256: false, enableSha512: true, + expected: getBaseRequirements, }, { name: "only deliver sha256 and sha512, directory provider", directoryProvider: true, enableSha256: true, enableSha512: true, + expected: getBaseRequirements, }, { name: "only deliver sha256, directory provider", directoryProvider: true, enableSha256: true, enableSha512: false, + expected: getBaseRequirements, }, { name: "only deliver sha512, directory provider", directoryProvider: true, enableSha256: false, enableSha512: true, + expected: getBaseRequirements, }, } @@ -92,11 +187,18 @@ func TestShaMarking(t *testing.T) { } p.client = client - // TODO check result of processor - _, err = p.run([]string{serverURL + "/provider-metadata.json"}) + report, err := p.run([]string{serverURL + "/provider-metadata.json"}) if err != nil { t.Errorf("SHA marking %v: Expected no error, got: %v", test.name, err) } + expected := test.expected(serverURL) + for i, got := range report.Domains[0].Requirements { + want := expected[i] + if !reflect.DeepEqual(*got, want) { + t.Errorf("SHA marking %v: Expected %v, got %v", test.name, want, *got) + } + } + p.close() }) } diff --git a/testdata/simple-rolie-provider/service.json b/testdata/simple-rolie-provider/service.json index 500d882a..a398a400 100644 --- a/testdata/simple-rolie-provider/service.json +++ b/testdata/simple-rolie-provider/service.json @@ -6,7 +6,7 @@ "collection": [ { "title": "CSAF feed (TLP:WHITE)", - "href": "/white/white-feed.json", + "href": "{{.URL}}/white/white-feed.json", "categories": { "category": [ { diff --git a/testdata/simple-rolie-provider/white/white-feed.json b/testdata/simple-rolie-provider/white/white-feed.json index 1bc17bcf..923a492d 100644 --- a/testdata/simple-rolie-provider/white/white-feed.json +++ b/testdata/simple-rolie-provider/white/white-feed.json @@ -5,11 +5,11 @@ "link": [ { "rel": "self", - "href": "/white/csaf-feed-tlp-white.json" + "href": "{{.URL}}/white/csaf-feed-tlp-white.json" }, { "rel": "service", - "href": "/service.json" + "href": "{{.URL}}/service.json" } ], "category": [ @@ -26,30 +26,30 @@ "link": [ { "rel": "self", - "href": "/white/avendor-advisory-0004.json" + "href": "{{.URL}}/white/avendor-advisory-0004.json" }, {{if .EnableSha256}} { "rel": "hash", - "href": "/white/avendor-advisory-0004.json.sha256" + "href": "{{.URL}}/white/avendor-advisory-0004.json.sha256" }, {{end}} {{if .EnableSha512}} { "rel": "hash", - "href": "/white/avendor-advisory-0004.json.sha512" + "href": "{{.URL}}/white/avendor-advisory-0004.json.sha512" }, {{end}} { "rel": "signature", - "href": "/white/avendor-advisory-0004.json.asc" + "href": "{{.URL}}/white/avendor-advisory-0004.json.asc" } ], "published": "2020-01-01T00:00:00Z", "updated": "2020-01-01T00:00:00Z", "content": { "type": "application/json", - "src": "/avendor-advisory-0004.json" + "src": "{{.URL}}/avendor-advisory-0004.json" }, "format": { "schema": "https://docs.oasis-open.org/csaf/csaf/v2.0/csaf_json_schema.json", From 68bd04676cc425dca87751bca989457baf5f56a1 Mon Sep 17 00:00:00 2001 From: koplas Date: Fri, 6 Dec 2024 13:11:07 +0100 Subject: [PATCH 35/64] Add requirement checker test data --- cmd/csaf_checker/processor_test.go | 147 +++++------- .../sha256-directory.json | 206 +++++++++++++++++ .../processor-requirements/sha256-rolie.json | 210 ++++++++++++++++++ .../sha256-sha512-directory.json | 206 +++++++++++++++++ .../sha256-sha512-rolie.json | 210 ++++++++++++++++++ .../sha512-directory.json | 207 +++++++++++++++++ .../processor-requirements/sha512-rolie.json | 210 ++++++++++++++++++ 7 files changed, 1299 insertions(+), 97 deletions(-) create mode 100644 testdata/processor-requirements/sha256-directory.json create mode 100644 testdata/processor-requirements/sha256-rolie.json create mode 100644 testdata/processor-requirements/sha256-sha512-directory.json create mode 100644 testdata/processor-requirements/sha256-sha512-rolie.json create mode 100644 testdata/processor-requirements/sha512-directory.json create mode 100644 testdata/processor-requirements/sha512-rolie.json diff --git a/cmd/csaf_checker/processor_test.go b/cmd/csaf_checker/processor_test.go index 73574bde..c4fb532d 100644 --- a/cmd/csaf_checker/processor_test.go +++ b/cmd/csaf_checker/processor_test.go @@ -9,99 +9,54 @@ package main import ( - "fmt" + "bytes" + "encoding/json" "net/http/httptest" + "os" "reflect" "testing" + "text/template" "github.com/gocsaf/csaf/v3/internal/testutil" "github.com/gocsaf/csaf/v3/util" ) -func getBaseRequirements(url string) []Requirement { - return []Requirement{ - { - Num: 1, - Description: "Valid CSAF documents", - Messages: []Message{{Type: 1, Text: "No remote validator configured"}, {Type: 0, Text: "All advisories validated fine against the schema."}}, - }, { - Num: 2, - Description: "Filename", - Messages: []Message{{Type: 0, Text: "All found filenames are conforming."}}}, - { - Num: 3, - Description: "TLS", - Messages: []Message{{Type: 0, Text: "All tested URLs were HTTPS."}}}, - { - Num: 4, - Description: "TLP:WHITE", - Messages: []Message{{Type: 0, Text: "All advisories labeled TLP:WHITE were freely accessible."}}}, - { - Num: 5, - Description: "TLP:AMBER and TLP:RED", - Messages: []Message{ - {Type: 0, Text: "No advisories labeled TLP:AMBER or TLP:RED tested for accessibility."}}}, - { - Num: 6, - Description: "Redirects", - Messages: []Message{{Type: 0, Text: "No redirections found."}}}, - { - Num: 7, - Description: "provider-metadata.json", - Messages: []Message{{Type: 0, Text: "Found good provider metadata."}}}, - { - Num: 8, - Description: "security.txt", - Messages: []Message{{Type: 0, Text: "Performed no test of security.txt since the direct url of the provider-metadata.json was used."}}}, - { - Num: 9, - Description: "/.well-known/csaf/provider-metadata.json", - Messages: []Message{{Type: 0, Text: "Performed no test on whether the provider-metadata.json is available under the .well-known path since the direct url of the provider-metadata.json was used."}}}, - { - Num: 10, - Description: "DNS path", - Messages: []Message{{Type: 0, Text: "Performed no test on the contents of https://csaf.data.security.DOMAIN since the direct url of the provider-metadata.json was used."}}}, - { - Num: 11, - Description: "One folder per year", - Messages: []Message{{Type: 2, Text: fmt.Sprintf("No year folder found in %s/white/avendor-advisory-0004.json", url)}}}, - { - Num: 12, - Description: "index.txt", - Messages: []Message{{Type: 0, Text: fmt.Sprintf("Found %s/white/index.txt", url)}}}, - { - Num: 13, - Description: "changes.csv", - Messages: []Message{{Type: 0, Text: fmt.Sprintf("Found %s/white/changes.csv", url)}}}, - { - Num: 14, - Description: "Directory listings", - Messages: []Message{{Type: 0, Text: "All directory listings are valid."}}}, - { - Num: 15, - Description: "ROLIE feed", - Messages: []Message{{Type: 2, Text: "ROLIE feed based distribution was not used."}}}, - { - Num: 16, - Description: "ROLIE service document", - Messages: []Message{{Type: 1, Text: "No ROLIE service document found."}}}, - { - Num: 17, - Description: "ROLIE category document", - Messages: []Message{{Type: 1, Text: "No ROLIE category document found."}}}, - { - Num: 18, - Description: "Integrity", - Messages: []Message{{Type: 0, Text: "All checksums match."}}}, - { - Num: 19, - Description: "Signatures", - Messages: []Message{{Type: 0, Text: "All signatures verified."}}}, - { - Num: 20, - Description: "Public OpenPGP Key", - Messages: []Message{{Type: 0, Text: "1 public OpenPGP key(s) loaded."}}}, +func getRequirementTestData(t *testing.T, params testutil.ProviderParams, directoryProvider bool) []Requirement { + path := "../../testdata/processor-requirements/" + if params.EnableSha256 { + path += "sha256-" + } + if params.EnableSha512 { + path += "sha512-" + } + if directoryProvider { + path += "directory" + } else { + path += "rolie" + } + path += ".json" + + content, err := os.ReadFile(path) + if err != nil { + t.Fatal(err) + } + + tmplt, err := template.New("base").Parse(string(content)) + if err != nil { + t.Fatal(err) + } + + var output bytes.Buffer + err = tmplt.Execute(&output, params) + if err != nil { + t.Fatal(err) + } + var requirement []Requirement + err = json.Unmarshal(output.Bytes(), &requirement) + if err != nil { + t.Fatal(err) } + return requirement } func TestShaMarking(t *testing.T) { @@ -110,49 +65,42 @@ func TestShaMarking(t *testing.T) { directoryProvider bool enableSha256 bool enableSha512 bool - expected func(string) []Requirement }{ { name: "deliver sha256 and sha512", directoryProvider: false, enableSha256: true, enableSha512: true, - expected: getBaseRequirements, }, { name: "only deliver sha256", directoryProvider: false, enableSha256: true, enableSha512: false, - expected: getBaseRequirements, }, { name: "only deliver sha512", directoryProvider: false, enableSha256: false, enableSha512: true, - expected: getBaseRequirements, }, { - name: "only deliver sha256 and sha512, directory provider", + name: "deliver sha256 and sha512, directory provider", directoryProvider: true, enableSha256: true, enableSha512: true, - expected: getBaseRequirements, }, { name: "only deliver sha256, directory provider", directoryProvider: true, enableSha256: true, enableSha512: false, - expected: getBaseRequirements, }, { name: "only deliver sha512, directory provider", directoryProvider: true, enableSha256: false, enableSha512: true, - expected: getBaseRequirements, }, } @@ -191,11 +139,16 @@ func TestShaMarking(t *testing.T) { if err != nil { t.Errorf("SHA marking %v: Expected no error, got: %v", test.name, err) } - expected := test.expected(serverURL) - for i, got := range report.Domains[0].Requirements { - want := expected[i] - if !reflect.DeepEqual(*got, want) { - t.Errorf("SHA marking %v: Expected %v, got %v", test.name, want, *got) + expected := getRequirementTestData(t, + testutil.ProviderParams{ + URL: serverURL, + EnableSha256: test.enableSha256, + EnableSha512: test.enableSha512, + }, + test.directoryProvider) + for i, want := range report.Domains[0].Requirements { + if !reflect.DeepEqual(expected[i], *want) { + t.Errorf("SHA marking %v: Expected %v, got %v", test.name, *want, expected[i]) } } diff --git a/testdata/processor-requirements/sha256-directory.json b/testdata/processor-requirements/sha256-directory.json new file mode 100644 index 00000000..a1069772 --- /dev/null +++ b/testdata/processor-requirements/sha256-directory.json @@ -0,0 +1,206 @@ +[ + { + "num": 1, + "description": "Valid CSAF documents", + "messages": [ + { + "type": 1, + "text": "No remote validator configured" + }, + { + "type": 0, + "text": "All advisories validated fine against the schema." + } + ] + }, + { + "num": 2, + "description": "Filename", + "messages": [ + { + "type": 0, + "text": "All found filenames are conforming." + } + ] + }, + { + "num": 3, + "description": "TLS", + "messages": [ + { + "type": 0, + "text": "All tested URLs were HTTPS." + } + ] + }, + { + "num": 4, + "description": "TLP:WHITE", + "messages": [ + { + "type": 0, + "text": "All advisories labeled TLP:WHITE were freely accessible." + } + ] + }, + { + "num": 5, + "description": "TLP:AMBER and TLP:RED", + "messages": [ + { + "type": 0, + "text": "No advisories labeled TLP:AMBER or TLP:RED tested for accessibility." + } + ] + }, + { + "num": 6, + "description": "Redirects", + "messages": [ + { + "type": 0, + "text": "No redirections found." + } + ] + }, + { + "num": 7, + "description": "provider-metadata.json", + "messages": [ + { + "type": 0, + "text": "Found good provider metadata." + } + ] + }, + { + "num": 8, + "description": "security.txt", + "messages": [ + { + "type": 0, + "text": "Performed no test of security.txt since the direct url of the provider-metadata.json was used." + } + ] + }, + { + "num": 9, + "description": "/.well-known/csaf/provider-metadata.json", + "messages": [ + { + "type": 0, + "text": "Performed no test on whether the provider-metadata.json is available under the .well-known path since the direct url of the provider-metadata.json was used." + } + ] + }, + { + "num": 10, + "description": "DNS path", + "messages": [ + { + "type": 0, + "text": "Performed no test on the contents of https://csaf.data.security.DOMAIN since the direct url of the provider-metadata.json was used." + } + ] + }, + { + "num": 11, + "description": "One folder per year", + "messages": [ + { + "type": 2, + "text": "No year folder found in {{.URL}}/white/avendor-advisory-0004.json" + } + ] + }, + { + "num": 12, + "description": "index.txt", + "messages": [ + { + "type": 0, + "text": "Found {{.URL}}/white/index.txt" + } + ] + }, + { + "num": 13, + "description": "changes.csv", + "messages": [ + { + "type": 0, + "text": "Found {{.URL}}/white/changes.csv" + } + ] + }, + { + "num": 14, + "description": "Directory listings", + "messages": [ + { + "type": 0, + "text": "All directory listings are valid." + } + ] + }, + { + "num": 15, + "description": "ROLIE feed", + "messages": [ + { + "type": 2, + "text": "ROLIE feed based distribution was not used." + } + ] + }, + { + "num": 16, + "description": "ROLIE service document", + "messages": [ + { + "type": 1, + "text": "No ROLIE service document found." + } + ] + }, + { + "num": 17, + "description": "ROLIE category document", + "messages": [ + { + "type": 1, + "text": "No ROLIE category document found." + } + ] + }, + { + "num": 18, + "description": "Integrity", + "messages": [ + { + "type": 2, + "text": "Fetching {{.URL}}/white/avendor-advisory-0004.json.sha512 failed: Status code 404 (404 Not Found)" + } + ] + }, + { + "num": 19, + "description": "Signatures", + "messages": [ + { + "type": 0, + "text": "All signatures verified." + } + ] + }, + { + "num": 20, + "description": "Public OpenPGP Key", + "messages": [ + { + "type": 0, + "text": "1 public OpenPGP key(s) loaded." + } + ] + } +] \ No newline at end of file diff --git a/testdata/processor-requirements/sha256-rolie.json b/testdata/processor-requirements/sha256-rolie.json new file mode 100644 index 00000000..5875174b --- /dev/null +++ b/testdata/processor-requirements/sha256-rolie.json @@ -0,0 +1,210 @@ +[ + { + "num": 1, + "description": "Valid CSAF documents", + "messages": [ + { + "type": 1, + "text": "No remote validator configured" + }, + { + "type": 0, + "text": "All advisories validated fine against the schema." + } + ] + }, + { + "num": 2, + "description": "Filename", + "messages": [ + { + "type": 0, + "text": "All found filenames are conforming." + } + ] + }, + { + "num": 3, + "description": "TLS", + "messages": [ + { + "type": 0, + "text": "All tested URLs were HTTPS." + } + ] + }, + { + "num": 4, + "description": "TLP:WHITE", + "messages": [ + { + "type": 0, + "text": "All advisories labeled TLP:WHITE were freely accessible." + } + ] + }, + { + "num": 5, + "description": "TLP:AMBER and TLP:RED", + "messages": [ + { + "type": 0, + "text": "No advisories labeled TLP:AMBER or TLP:RED tested for accessibility." + } + ] + }, + { + "num": 6, + "description": "Redirects", + "messages": [ + { + "type": 0, + "text": "No redirections found." + } + ] + }, + { + "num": 7, + "description": "provider-metadata.json", + "messages": [ + { + "type": 0, + "text": "Found good provider metadata." + } + ] + }, + { + "num": 8, + "description": "security.txt", + "messages": [ + { + "type": 0, + "text": "Performed no test of security.txt since the direct url of the provider-metadata.json was used." + } + ] + }, + { + "num": 9, + "description": "/.well-known/csaf/provider-metadata.json", + "messages": [ + { + "type": 0, + "text": "Performed no test on whether the provider-metadata.json is available under the .well-known path since the direct url of the provider-metadata.json was used." + } + ] + }, + { + "num": 10, + "description": "DNS path", + "messages": [ + { + "type": 0, + "text": "Performed no test on the contents of https://csaf.data.security.DOMAIN since the direct url of the provider-metadata.json was used." + } + ] + }, + { + "num": 11, + "description": "One folder per year", + "messages": [ + { + "type": 2, + "text": "No year folder found in {{.URL}}/white/avendor-advisory-0004.json" + } + ] + }, + { + "num": 12, + "description": "index.txt", + "messages": [ + { + "type": 2, + "text": "Fetching index.txt failed: {{.URL}}/index.txt not found." + } + ] + }, + { + "num": 13, + "description": "changes.csv", + "messages": [ + { + "type": 2, + "text": "Fetching changes.csv failed: {{.URL}}/changes.csv not found." + } + ] + }, + { + "num": 14, + "description": "Directory listings", + "messages": [ + { + "type": 2, + "text": "Fetching {{.URL}}/white/ failed. Status code 404 (404 Not Found)" + }, + { + "type": 2, + "text": "Not listed advisories: {{.URL}}/white/avendor-advisory-0004.json" + } + ] + }, + { + "num": 15, + "description": "ROLIE feed", + "messages": [ + { + "type": 0, + "text": "All checked ROLIE feeds validated fine." + } + ] + }, + { + "num": 16, + "description": "ROLIE service document", + "messages": [ + { + "type": 0, + "text": "ROLIE service document validated fine." + } + ] + }, + { + "num": 17, + "description": "ROLIE category document", + "messages": [ + { + "type": 1, + "text": "Fetching {{.URL}}/white/category-white.json failed. Status code 404 (404 Not Found)" + } + ] + }, + { + "num": 18, + "description": "Integrity", + "messages": [ + { + "type": 0, + "text": "All checksums match." + } + ] + }, + { + "num": 19, + "description": "Signatures", + "messages": [ + { + "type": 0, + "text": "All signatures verified." + } + ] + }, + { + "num": 20, + "description": "Public OpenPGP Key", + "messages": [ + { + "type": 0, + "text": "1 public OpenPGP key(s) loaded." + } + ] + } +] diff --git a/testdata/processor-requirements/sha256-sha512-directory.json b/testdata/processor-requirements/sha256-sha512-directory.json new file mode 100644 index 00000000..3e30b9a5 --- /dev/null +++ b/testdata/processor-requirements/sha256-sha512-directory.json @@ -0,0 +1,206 @@ +[ + { + "num": 1, + "description": "Valid CSAF documents", + "messages": [ + { + "type": 1, + "text": "No remote validator configured" + }, + { + "type": 0, + "text": "All advisories validated fine against the schema." + } + ] + }, + { + "num": 2, + "description": "Filename", + "messages": [ + { + "type": 0, + "text": "All found filenames are conforming." + } + ] + }, + { + "num": 3, + "description": "TLS", + "messages": [ + { + "type": 0, + "text": "All tested URLs were HTTPS." + } + ] + }, + { + "num": 4, + "description": "TLP:WHITE", + "messages": [ + { + "type": 0, + "text": "All advisories labeled TLP:WHITE were freely accessible." + } + ] + }, + { + "num": 5, + "description": "TLP:AMBER and TLP:RED", + "messages": [ + { + "type": 0, + "text": "No advisories labeled TLP:AMBER or TLP:RED tested for accessibility." + } + ] + }, + { + "num": 6, + "description": "Redirects", + "messages": [ + { + "type": 0, + "text": "No redirections found." + } + ] + }, + { + "num": 7, + "description": "provider-metadata.json", + "messages": [ + { + "type": 0, + "text": "Found good provider metadata." + } + ] + }, + { + "num": 8, + "description": "security.txt", + "messages": [ + { + "type": 0, + "text": "Performed no test of security.txt since the direct url of the provider-metadata.json was used." + } + ] + }, + { + "num": 9, + "description": "/.well-known/csaf/provider-metadata.json", + "messages": [ + { + "type": 0, + "text": "Performed no test on whether the provider-metadata.json is available under the .well-known path since the direct url of the provider-metadata.json was used." + } + ] + }, + { + "num": 10, + "description": "DNS path", + "messages": [ + { + "type": 0, + "text": "Performed no test on the contents of https://csaf.data.security.DOMAIN since the direct url of the provider-metadata.json was used." + } + ] + }, + { + "num": 11, + "description": "One folder per year", + "messages": [ + { + "type": 2, + "text": "No year folder found in {{.URL}}/white/avendor-advisory-0004.json" + } + ] + }, + { + "num": 12, + "description": "index.txt", + "messages": [ + { + "type": 0, + "text": "Found {{.URL}}/white/index.txt" + } + ] + }, + { + "num": 13, + "description": "changes.csv", + "messages": [ + { + "type": 0, + "text": "Found {{.URL}}/white/changes.csv" + } + ] + }, + { + "num": 14, + "description": "Directory listings", + "messages": [ + { + "type": 0, + "text": "All directory listings are valid." + } + ] + }, + { + "num": 15, + "description": "ROLIE feed", + "messages": [ + { + "type": 2, + "text": "ROLIE feed based distribution was not used." + } + ] + }, + { + "num": 16, + "description": "ROLIE service document", + "messages": [ + { + "type": 1, + "text": "No ROLIE service document found." + } + ] + }, + { + "num": 17, + "description": "ROLIE category document", + "messages": [ + { + "type": 1, + "text": "No ROLIE category document found." + } + ] + }, + { + "num": 18, + "description": "Integrity", + "messages": [ + { + "type": 0, + "text": "All checksums match." + } + ] + }, + { + "num": 19, + "description": "Signatures", + "messages": [ + { + "type": 0, + "text": "All signatures verified." + } + ] + }, + { + "num": 20, + "description": "Public OpenPGP Key", + "messages": [ + { + "type": 0, + "text": "1 public OpenPGP key(s) loaded." + } + ] + } +] diff --git a/testdata/processor-requirements/sha256-sha512-rolie.json b/testdata/processor-requirements/sha256-sha512-rolie.json new file mode 100644 index 00000000..5875174b --- /dev/null +++ b/testdata/processor-requirements/sha256-sha512-rolie.json @@ -0,0 +1,210 @@ +[ + { + "num": 1, + "description": "Valid CSAF documents", + "messages": [ + { + "type": 1, + "text": "No remote validator configured" + }, + { + "type": 0, + "text": "All advisories validated fine against the schema." + } + ] + }, + { + "num": 2, + "description": "Filename", + "messages": [ + { + "type": 0, + "text": "All found filenames are conforming." + } + ] + }, + { + "num": 3, + "description": "TLS", + "messages": [ + { + "type": 0, + "text": "All tested URLs were HTTPS." + } + ] + }, + { + "num": 4, + "description": "TLP:WHITE", + "messages": [ + { + "type": 0, + "text": "All advisories labeled TLP:WHITE were freely accessible." + } + ] + }, + { + "num": 5, + "description": "TLP:AMBER and TLP:RED", + "messages": [ + { + "type": 0, + "text": "No advisories labeled TLP:AMBER or TLP:RED tested for accessibility." + } + ] + }, + { + "num": 6, + "description": "Redirects", + "messages": [ + { + "type": 0, + "text": "No redirections found." + } + ] + }, + { + "num": 7, + "description": "provider-metadata.json", + "messages": [ + { + "type": 0, + "text": "Found good provider metadata." + } + ] + }, + { + "num": 8, + "description": "security.txt", + "messages": [ + { + "type": 0, + "text": "Performed no test of security.txt since the direct url of the provider-metadata.json was used." + } + ] + }, + { + "num": 9, + "description": "/.well-known/csaf/provider-metadata.json", + "messages": [ + { + "type": 0, + "text": "Performed no test on whether the provider-metadata.json is available under the .well-known path since the direct url of the provider-metadata.json was used." + } + ] + }, + { + "num": 10, + "description": "DNS path", + "messages": [ + { + "type": 0, + "text": "Performed no test on the contents of https://csaf.data.security.DOMAIN since the direct url of the provider-metadata.json was used." + } + ] + }, + { + "num": 11, + "description": "One folder per year", + "messages": [ + { + "type": 2, + "text": "No year folder found in {{.URL}}/white/avendor-advisory-0004.json" + } + ] + }, + { + "num": 12, + "description": "index.txt", + "messages": [ + { + "type": 2, + "text": "Fetching index.txt failed: {{.URL}}/index.txt not found." + } + ] + }, + { + "num": 13, + "description": "changes.csv", + "messages": [ + { + "type": 2, + "text": "Fetching changes.csv failed: {{.URL}}/changes.csv not found." + } + ] + }, + { + "num": 14, + "description": "Directory listings", + "messages": [ + { + "type": 2, + "text": "Fetching {{.URL}}/white/ failed. Status code 404 (404 Not Found)" + }, + { + "type": 2, + "text": "Not listed advisories: {{.URL}}/white/avendor-advisory-0004.json" + } + ] + }, + { + "num": 15, + "description": "ROLIE feed", + "messages": [ + { + "type": 0, + "text": "All checked ROLIE feeds validated fine." + } + ] + }, + { + "num": 16, + "description": "ROLIE service document", + "messages": [ + { + "type": 0, + "text": "ROLIE service document validated fine." + } + ] + }, + { + "num": 17, + "description": "ROLIE category document", + "messages": [ + { + "type": 1, + "text": "Fetching {{.URL}}/white/category-white.json failed. Status code 404 (404 Not Found)" + } + ] + }, + { + "num": 18, + "description": "Integrity", + "messages": [ + { + "type": 0, + "text": "All checksums match." + } + ] + }, + { + "num": 19, + "description": "Signatures", + "messages": [ + { + "type": 0, + "text": "All signatures verified." + } + ] + }, + { + "num": 20, + "description": "Public OpenPGP Key", + "messages": [ + { + "type": 0, + "text": "1 public OpenPGP key(s) loaded." + } + ] + } +] diff --git a/testdata/processor-requirements/sha512-directory.json b/testdata/processor-requirements/sha512-directory.json new file mode 100644 index 00000000..e47e1f97 --- /dev/null +++ b/testdata/processor-requirements/sha512-directory.json @@ -0,0 +1,207 @@ +[ + { + "num": 1, + "description": "Valid CSAF documents", + "messages": [ + { + "type": 1, + "text": "No remote validator configured" + }, + { + "type": 0, + "text": "All advisories validated fine against the schema." + } + ] + }, + { + "num": 2, + "description": "Filename", + "messages": [ + { + "type": 0, + "text": "All found filenames are conforming." + } + ] + }, + { + "num": 3, + "description": "TLS", + "messages": [ + { + "type": 0, + "text": "All tested URLs were HTTPS." + } + ] + }, + { + "num": 4, + "description": "TLP:WHITE", + "messages": [ + { + "type": 0, + "text": "All advisories labeled TLP:WHITE were freely accessible." + } + ] + }, + { + "num": 5, + "description": "TLP:AMBER and TLP:RED", + "messages": [ + { + "type": 0, + "text": "No advisories labeled TLP:AMBER or TLP:RED tested for accessibility." + } + ] + }, + { + "num": 6, + "description": "Redirects", + "messages": [ + { + "type": 0, + "text": "No redirections found." + } + ] + }, + { + "num": 7, + "description": "provider-metadata.json", + "messages": [ + { + "type": 0, + "text": "Found good provider metadata." + } + ] + }, + { + "num": 8, + "description": "security.txt", + "messages": [ + { + "type": 0, + "text": "Performed no test of security.txt since the direct url of the provider-metadata.json was used." + } + ] + }, + { + "num": 9, + "description": "/.well-known/csaf/provider-metadata.json", + "messages": [ + { + "type": 0, + "text": "Performed no test on whether the provider-metadata.json is available under the .well-known path since the direct url of the provider-metadata.json was used." + } + ] + }, + { + "num": 10, + "description": "DNS path", + "messages": [ + { + "type": 0, + "text": "Performed no test on the contents of https://csaf.data.security.DOMAIN since the direct url of the provider-metadata.json was used." + } + ] + }, + { + "num": 11, + "description": "One folder per year", + "messages": [ + { + "type": 2, + "text": "No year folder found in {{.URL}}/white/avendor-advisory-0004.json" + } + ] + }, + { + "num": 12, + "description": "index.txt", + "messages": [ + { + "type": 0, + "text": "Found {{.URL}}/white/index.txt" + } + ] + }, + { + "num": 13, + "description": "changes.csv", + "messages": [ + { + "type": 0, + "text": "Found {{.URL}}/white/changes.csv" + } + ] + }, + { + "num": 14, + "description": "Directory listings", + "messages": [ + { + "type": 0, + "text": "All directory listings are valid." + } + ] + }, + { + "num": 15, + "description": "ROLIE feed", + "messages": [ + { + "type": 2, + "text": "ROLIE feed based distribution was not used." + } + ] + }, + { + "num": 16, + "description": "ROLIE service document", + "messages": [ + { + "type": 1, + "text": "No ROLIE service document found." + } + ] + }, + { + "num": 17, + "description": "ROLIE category document", + "messages": [ + { + "type": 1, + "text": "No ROLIE category document found." + } + ] + }, + { + "num": 18, + "description": "Integrity", + "messages": [ + { + "type": 2, + "text": "Fetching {{.URL}}/white/avendor-advisory-0004.json.sha256 failed: Status code 404 (404 Not Found)" + } + ] + }, + { + "num": 19, + "description": "Signatures", + "messages": [ + { + "type": 0, + "text": "All signatures verified." + } + ] + }, + { + "num": 20, + "description": "Public OpenPGP Key", + "messages": [ + { + "type": 0, + "text": "1 public OpenPGP key(s) loaded." + } + ] + } +] + diff --git a/testdata/processor-requirements/sha512-rolie.json b/testdata/processor-requirements/sha512-rolie.json new file mode 100644 index 00000000..5875174b --- /dev/null +++ b/testdata/processor-requirements/sha512-rolie.json @@ -0,0 +1,210 @@ +[ + { + "num": 1, + "description": "Valid CSAF documents", + "messages": [ + { + "type": 1, + "text": "No remote validator configured" + }, + { + "type": 0, + "text": "All advisories validated fine against the schema." + } + ] + }, + { + "num": 2, + "description": "Filename", + "messages": [ + { + "type": 0, + "text": "All found filenames are conforming." + } + ] + }, + { + "num": 3, + "description": "TLS", + "messages": [ + { + "type": 0, + "text": "All tested URLs were HTTPS." + } + ] + }, + { + "num": 4, + "description": "TLP:WHITE", + "messages": [ + { + "type": 0, + "text": "All advisories labeled TLP:WHITE were freely accessible." + } + ] + }, + { + "num": 5, + "description": "TLP:AMBER and TLP:RED", + "messages": [ + { + "type": 0, + "text": "No advisories labeled TLP:AMBER or TLP:RED tested for accessibility." + } + ] + }, + { + "num": 6, + "description": "Redirects", + "messages": [ + { + "type": 0, + "text": "No redirections found." + } + ] + }, + { + "num": 7, + "description": "provider-metadata.json", + "messages": [ + { + "type": 0, + "text": "Found good provider metadata." + } + ] + }, + { + "num": 8, + "description": "security.txt", + "messages": [ + { + "type": 0, + "text": "Performed no test of security.txt since the direct url of the provider-metadata.json was used." + } + ] + }, + { + "num": 9, + "description": "/.well-known/csaf/provider-metadata.json", + "messages": [ + { + "type": 0, + "text": "Performed no test on whether the provider-metadata.json is available under the .well-known path since the direct url of the provider-metadata.json was used." + } + ] + }, + { + "num": 10, + "description": "DNS path", + "messages": [ + { + "type": 0, + "text": "Performed no test on the contents of https://csaf.data.security.DOMAIN since the direct url of the provider-metadata.json was used." + } + ] + }, + { + "num": 11, + "description": "One folder per year", + "messages": [ + { + "type": 2, + "text": "No year folder found in {{.URL}}/white/avendor-advisory-0004.json" + } + ] + }, + { + "num": 12, + "description": "index.txt", + "messages": [ + { + "type": 2, + "text": "Fetching index.txt failed: {{.URL}}/index.txt not found." + } + ] + }, + { + "num": 13, + "description": "changes.csv", + "messages": [ + { + "type": 2, + "text": "Fetching changes.csv failed: {{.URL}}/changes.csv not found." + } + ] + }, + { + "num": 14, + "description": "Directory listings", + "messages": [ + { + "type": 2, + "text": "Fetching {{.URL}}/white/ failed. Status code 404 (404 Not Found)" + }, + { + "type": 2, + "text": "Not listed advisories: {{.URL}}/white/avendor-advisory-0004.json" + } + ] + }, + { + "num": 15, + "description": "ROLIE feed", + "messages": [ + { + "type": 0, + "text": "All checked ROLIE feeds validated fine." + } + ] + }, + { + "num": 16, + "description": "ROLIE service document", + "messages": [ + { + "type": 0, + "text": "ROLIE service document validated fine." + } + ] + }, + { + "num": 17, + "description": "ROLIE category document", + "messages": [ + { + "type": 1, + "text": "Fetching {{.URL}}/white/category-white.json failed. Status code 404 (404 Not Found)" + } + ] + }, + { + "num": 18, + "description": "Integrity", + "messages": [ + { + "type": 0, + "text": "All checksums match." + } + ] + }, + { + "num": 19, + "description": "Signatures", + "messages": [ + { + "type": 0, + "text": "All signatures verified." + } + ] + }, + { + "num": 20, + "description": "Public OpenPGP Key", + "messages": [ + { + "type": 0, + "text": "1 public OpenPGP key(s) loaded." + } + ] + } +] From df65ad13cbd222d2a2b1784287bd9e2e8b22ba7b Mon Sep 17 00:00:00 2001 From: koplas Date: Tue, 10 Dec 2024 10:13:42 +0100 Subject: [PATCH 36/64] Fix: return correct exit code --- cmd/csaf_validator/main.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/csaf_validator/main.go b/cmd/csaf_validator/main.go index 9e844b70..1a34be0d 100644 --- a/cmd/csaf_validator/main.go +++ b/cmd/csaf_validator/main.go @@ -150,7 +150,7 @@ func run(opts *options, files []string) error { } // Exit code is based on validation results - os.Exit(exitCodeAllValid) + os.Exit(exitCode) return nil } From fc404e499c90ead7643bbbbba4b75855bdbfe938 Mon Sep 17 00:00:00 2001 From: JanHoefelmeyer Date: Fri, 13 Dec 2024 13:33:22 +0100 Subject: [PATCH 37/64] Unfix: Add should-states --- testdata/processor-requirements/sha256-directory.json | 2 +- testdata/processor-requirements/sha256-rolie.json | 2 +- testdata/processor-requirements/sha512-directory.json | 2 +- testdata/processor-requirements/sha512-rolie.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/testdata/processor-requirements/sha256-directory.json b/testdata/processor-requirements/sha256-directory.json index a1069772..46b40491 100644 --- a/testdata/processor-requirements/sha256-directory.json +++ b/testdata/processor-requirements/sha256-directory.json @@ -178,7 +178,7 @@ "description": "Integrity", "messages": [ { - "type": 2, + "type": 0, "text": "Fetching {{.URL}}/white/avendor-advisory-0004.json.sha512 failed: Status code 404 (404 Not Found)" } ] diff --git a/testdata/processor-requirements/sha256-rolie.json b/testdata/processor-requirements/sha256-rolie.json index 5875174b..61c5ccf9 100644 --- a/testdata/processor-requirements/sha256-rolie.json +++ b/testdata/processor-requirements/sha256-rolie.json @@ -172,7 +172,7 @@ "description": "ROLIE category document", "messages": [ { - "type": 1, + "type": 0, "text": "Fetching {{.URL}}/white/category-white.json failed. Status code 404 (404 Not Found)" } ] diff --git a/testdata/processor-requirements/sha512-directory.json b/testdata/processor-requirements/sha512-directory.json index e47e1f97..5102fab1 100644 --- a/testdata/processor-requirements/sha512-directory.json +++ b/testdata/processor-requirements/sha512-directory.json @@ -178,7 +178,7 @@ "description": "Integrity", "messages": [ { - "type": 2, + "type": 0, "text": "Fetching {{.URL}}/white/avendor-advisory-0004.json.sha256 failed: Status code 404 (404 Not Found)" } ] diff --git a/testdata/processor-requirements/sha512-rolie.json b/testdata/processor-requirements/sha512-rolie.json index 5875174b..61c5ccf9 100644 --- a/testdata/processor-requirements/sha512-rolie.json +++ b/testdata/processor-requirements/sha512-rolie.json @@ -172,7 +172,7 @@ "description": "ROLIE category document", "messages": [ { - "type": 1, + "type": 0, "text": "Fetching {{.URL}}/white/category-white.json failed. Status code 404 (404 Not Found)" } ] From a3d6d6acfb3fed53967ae8c024ddc2b565bd284b Mon Sep 17 00:00:00 2001 From: koplas Date: Fri, 13 Dec 2024 14:26:00 +0100 Subject: [PATCH 38/64] Downgrade error to info in directory hash fetching --- cmd/csaf_checker/processor.go | 9 +++++++-- cmd/csaf_checker/processor_test.go | 6 +++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/cmd/csaf_checker/processor.go b/cmd/csaf_checker/processor.go index 7972e2ba..eed561a1 100644 --- a/cmd/csaf_checker/processor.go +++ b/cmd/csaf_checker/processor.go @@ -776,8 +776,13 @@ func (p *processor) integrity( continue } if res.StatusCode != http.StatusOK { - p.badIntegrities.error("Fetching %s failed: Status code %d (%s)", - hashFile, res.StatusCode, res.Status) + if f.IsDirectory() { + p.badIntegrities.info("Fetching %s failed: Status code %d (%s)", + hashFile, res.StatusCode, res.Status) + } else { + p.badIntegrities.error("Fetching %s failed: Status code %d (%s)", + hashFile, res.StatusCode, res.Status) + } continue } h, err := func() ([]byte, error) { diff --git a/cmd/csaf_checker/processor_test.go b/cmd/csaf_checker/processor_test.go index c4fb532d..ea5aed50 100644 --- a/cmd/csaf_checker/processor_test.go +++ b/cmd/csaf_checker/processor_test.go @@ -146,9 +146,9 @@ func TestShaMarking(t *testing.T) { EnableSha512: test.enableSha512, }, test.directoryProvider) - for i, want := range report.Domains[0].Requirements { - if !reflect.DeepEqual(expected[i], *want) { - t.Errorf("SHA marking %v: Expected %v, got %v", test.name, *want, expected[i]) + for i, got := range report.Domains[0].Requirements { + if !reflect.DeepEqual(expected[i], *got) { + t.Errorf("SHA marking %v: Expected %v, got %v", test.name, expected[i], *got) } } From ebd96011fcfd38a6a6c8c82ab2a9e99d8aee3f8c Mon Sep 17 00:00:00 2001 From: koplas Date: Fri, 13 Dec 2024 14:38:49 +0100 Subject: [PATCH 39/64] Revert new requirement 17 test Changing the ROLIE category fetching warning to info can be addressed later. --- testdata/processor-requirements/sha256-rolie.json | 2 +- testdata/processor-requirements/sha512-rolie.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/testdata/processor-requirements/sha256-rolie.json b/testdata/processor-requirements/sha256-rolie.json index 61c5ccf9..5875174b 100644 --- a/testdata/processor-requirements/sha256-rolie.json +++ b/testdata/processor-requirements/sha256-rolie.json @@ -172,7 +172,7 @@ "description": "ROLIE category document", "messages": [ { - "type": 0, + "type": 1, "text": "Fetching {{.URL}}/white/category-white.json failed. Status code 404 (404 Not Found)" } ] diff --git a/testdata/processor-requirements/sha512-rolie.json b/testdata/processor-requirements/sha512-rolie.json index 61c5ccf9..5875174b 100644 --- a/testdata/processor-requirements/sha512-rolie.json +++ b/testdata/processor-requirements/sha512-rolie.json @@ -172,7 +172,7 @@ "description": "ROLIE category document", "messages": [ { - "type": 0, + "type": 1, "text": "Fetching {{.URL}}/white/category-white.json failed. Status code 404 (404 Not Found)" } ] From 9dd4b7fc8dca06e7eb87e54da60680fd4f8a6b41 Mon Sep 17 00:00:00 2001 From: koplas Date: Fri, 13 Dec 2024 15:54:39 +0100 Subject: [PATCH 40/64] Add tests for no hash given or available --- cmd/csaf_checker/processor.go | 10 +- cmd/csaf_checker/processor_test.go | 101 ++++++--- internal/testutil/testutil.go | 10 +- .../processor-requirements/directory.json | 210 +++++++++++++++++ testdata/processor-requirements/rolie.json | 210 +++++++++++++++++ ...256-sha512-forbid-hash-fetching-rolie.json | 214 ++++++++++++++++++ 6 files changed, 711 insertions(+), 44 deletions(-) create mode 100644 testdata/processor-requirements/directory.json create mode 100644 testdata/processor-requirements/rolie.json create mode 100644 testdata/processor-requirements/sha256-sha512-forbid-hash-fetching-rolie.json diff --git a/cmd/csaf_checker/processor.go b/cmd/csaf_checker/processor.go index eed561a1..b9138646 100644 --- a/cmd/csaf_checker/processor.go +++ b/cmd/csaf_checker/processor.go @@ -20,7 +20,6 @@ import ( "fmt" "io" "log" - "log/slog" "net/http" "net/url" "path/filepath" @@ -586,14 +585,11 @@ func (p *processor) rolieFeedEntries(feed string) ([]csaf.AdvisoryFile, error) { switch { case sha256 == "" && sha512 == "": - slog.Error("No hash listed on ROLIE feed", "file", url) - return + p.badROLIEFeed.error("No hash listed on ROLIE feed %s", url) case sign == "": - slog.Error("No signature listed on ROLIE feed", "file", url) - return - default: - file = csaf.PlainAdvisoryFile{Path: url, SHA256: sha256, SHA512: sha512, Sign: sign} + p.badROLIEFeed.error("No signature listed on ROLIE feed %s", url) } + file = csaf.PlainAdvisoryFile{Path: url, SHA256: sha256, SHA512: sha512, Sign: sign} files = append(files, file) }) diff --git a/cmd/csaf_checker/processor_test.go b/cmd/csaf_checker/processor_test.go index ea5aed50..5b0241ef 100644 --- a/cmd/csaf_checker/processor_test.go +++ b/cmd/csaf_checker/processor_test.go @@ -29,6 +29,9 @@ func getRequirementTestData(t *testing.T, params testutil.ProviderParams, direct if params.EnableSha512 { path += "sha512-" } + if params.ForbidHashFetching { + path += "forbid-hash-fetching-" + } if directoryProvider { path += "directory" } else { @@ -61,46 +64,74 @@ func getRequirementTestData(t *testing.T, params testutil.ProviderParams, direct func TestShaMarking(t *testing.T) { tests := []struct { - name string - directoryProvider bool - enableSha256 bool - enableSha512 bool + name string + directoryProvider bool + enableSha256 bool + enableSha512 bool + forbidHashFetching bool }{ { - name: "deliver sha256 and sha512", - directoryProvider: false, - enableSha256: true, - enableSha512: true, + name: "deliver sha256 and sha512", + directoryProvider: false, + enableSha256: true, + enableSha512: true, + forbidHashFetching: false, + }, + { + name: "enable sha256 and sha512, forbid fetching", + directoryProvider: false, + enableSha256: true, + enableSha512: true, + forbidHashFetching: true, + }, + { + name: "only deliver sha256", + directoryProvider: false, + enableSha256: true, + enableSha512: false, + forbidHashFetching: false, + }, + { + name: "only deliver sha512", + directoryProvider: false, + enableSha256: false, + enableSha512: true, + forbidHashFetching: false, }, { - name: "only deliver sha256", - directoryProvider: false, - enableSha256: true, - enableSha512: false, + name: "deliver sha256 and sha512, directory provider", + directoryProvider: true, + enableSha256: true, + enableSha512: true, + forbidHashFetching: false, }, { - name: "only deliver sha512", - directoryProvider: false, - enableSha256: false, - enableSha512: true, + name: "only deliver sha256, directory provider", + directoryProvider: true, + enableSha256: true, + enableSha512: false, + forbidHashFetching: false, }, { - name: "deliver sha256 and sha512, directory provider", - directoryProvider: true, - enableSha256: true, - enableSha512: true, + name: "only deliver sha512, directory provider", + directoryProvider: true, + enableSha256: false, + enableSha512: true, + forbidHashFetching: false, }, { - name: "only deliver sha256, directory provider", - directoryProvider: true, - enableSha256: true, - enableSha512: false, + name: "no hash", + directoryProvider: false, + enableSha256: false, + enableSha512: false, + forbidHashFetching: false, }, { - name: "only deliver sha512, directory provider", - directoryProvider: true, - enableSha256: false, - enableSha512: true, + name: "no hash, directory provider", + directoryProvider: true, + enableSha256: false, + enableSha512: false, + forbidHashFetching: false, }, } @@ -111,9 +142,10 @@ func TestShaMarking(t *testing.T) { tt.Parallel() serverURL := "" params := testutil.ProviderParams{ - URL: "", - EnableSha256: test.enableSha256, - EnableSha512: test.enableSha512, + URL: "", + EnableSha256: test.enableSha256, + EnableSha512: test.enableSha512, + ForbidHashFetching: test.forbidHashFetching, } server := httptest.NewTLSServer(testutil.ProviderHandler(¶ms, test.directoryProvider)) defer server.Close() @@ -141,9 +173,10 @@ func TestShaMarking(t *testing.T) { } expected := getRequirementTestData(t, testutil.ProviderParams{ - URL: serverURL, - EnableSha256: test.enableSha256, - EnableSha512: test.enableSha512, + URL: serverURL, + EnableSha256: test.enableSha256, + EnableSha512: test.enableSha512, + ForbidHashFetching: test.forbidHashFetching, }, test.directoryProvider) for i, got := range report.Domains[0].Requirements { diff --git a/internal/testutil/testutil.go b/internal/testutil/testutil.go index 455d217c..e933742a 100644 --- a/internal/testutil/testutil.go +++ b/internal/testutil/testutil.go @@ -18,9 +18,10 @@ import ( // ProviderParams configures the test provider. type ProviderParams struct { - URL string - EnableSha256 bool - EnableSha512 bool + URL string + EnableSha256 bool + EnableSha512 bool + ForbidHashFetching bool } // ProviderHandler returns a test provider handler with the specified configuration. @@ -49,6 +50,9 @@ func ProviderHandler(params *ProviderParams, directoryProvider bool) http.Handle w.Header().Add("Content-Type", "text/html") case strings.HasSuffix(path, ".json"): w.Header().Add("Content-Type", "application/json") + case (strings.HasSuffix(path, ".sha256") || strings.HasSuffix(path, ".sha512")) && params.ForbidHashFetching: + w.WriteHeader(http.StatusForbidden) + return case strings.HasSuffix(path, ".sha256") && directoryProvider && !params.EnableSha256: w.WriteHeader(http.StatusNotFound) return diff --git a/testdata/processor-requirements/directory.json b/testdata/processor-requirements/directory.json new file mode 100644 index 00000000..ed61fcc9 --- /dev/null +++ b/testdata/processor-requirements/directory.json @@ -0,0 +1,210 @@ +[ + { + "num": 1, + "description": "Valid CSAF documents", + "messages": [ + { + "type": 1, + "text": "No remote validator configured" + }, + { + "type": 0, + "text": "All advisories validated fine against the schema." + } + ] + }, + { + "num": 2, + "description": "Filename", + "messages": [ + { + "type": 0, + "text": "All found filenames are conforming." + } + ] + }, + { + "num": 3, + "description": "TLS", + "messages": [ + { + "type": 0, + "text": "All tested URLs were HTTPS." + } + ] + }, + { + "num": 4, + "description": "TLP:WHITE", + "messages": [ + { + "type": 0, + "text": "All advisories labeled TLP:WHITE were freely accessible." + } + ] + }, + { + "num": 5, + "description": "TLP:AMBER and TLP:RED", + "messages": [ + { + "type": 0, + "text": "No advisories labeled TLP:AMBER or TLP:RED tested for accessibility." + } + ] + }, + { + "num": 6, + "description": "Redirects", + "messages": [ + { + "type": 0, + "text": "No redirections found." + } + ] + }, + { + "num": 7, + "description": "provider-metadata.json", + "messages": [ + { + "type": 0, + "text": "Found good provider metadata." + } + ] + }, + { + "num": 8, + "description": "security.txt", + "messages": [ + { + "type": 0, + "text": "Performed no test of security.txt since the direct url of the provider-metadata.json was used." + } + ] + }, + { + "num": 9, + "description": "/.well-known/csaf/provider-metadata.json", + "messages": [ + { + "type": 0, + "text": "Performed no test on whether the provider-metadata.json is available under the .well-known path since the direct url of the provider-metadata.json was used." + } + ] + }, + { + "num": 10, + "description": "DNS path", + "messages": [ + { + "type": 0, + "text": "Performed no test on the contents of https://csaf.data.security.DOMAIN since the direct url of the provider-metadata.json was used." + } + ] + }, + { + "num": 11, + "description": "One folder per year", + "messages": [ + { + "type": 2, + "text": "No year folder found in {{.URL}}/white/avendor-advisory-0004.json" + } + ] + }, + { + "num": 12, + "description": "index.txt", + "messages": [ + { + "type": 0, + "text": "Found {{.URL}}/white/index.txt" + } + ] + }, + { + "num": 13, + "description": "changes.csv", + "messages": [ + { + "type": 0, + "text": "Found {{.URL}}/white/changes.csv" + } + ] + }, + { + "num": 14, + "description": "Directory listings", + "messages": [ + { + "type": 0, + "text": "All directory listings are valid." + } + ] + }, + { + "num": 15, + "description": "ROLIE feed", + "messages": [ + { + "type": 2, + "text": "ROLIE feed based distribution was not used." + } + ] + }, + { + "num": 16, + "description": "ROLIE service document", + "messages": [ + { + "type": 1, + "text": "No ROLIE service document found." + } + ] + }, + { + "num": 17, + "description": "ROLIE category document", + "messages": [ + { + "type": 1, + "text": "No ROLIE category document found." + } + ] + }, + { + "num": 18, + "description": "Integrity", + "messages": [ + { + "type": 0, + "text": "Fetching {{.URL}}/white/avendor-advisory-0004.json.sha256 failed: Status code 404 (404 Not Found)" + }, + { + "type": 0, + "text": "Fetching {{.URL}}/white/avendor-advisory-0004.json.sha512 failed: Status code 404 (404 Not Found)" + } + ] + }, + { + "num": 19, + "description": "Signatures", + "messages": [ + { + "type": 0, + "text": "All signatures verified." + } + ] + }, + { + "num": 20, + "description": "Public OpenPGP Key", + "messages": [ + { + "type": 0, + "text": "1 public OpenPGP key(s) loaded." + } + ] + } +] \ No newline at end of file diff --git a/testdata/processor-requirements/rolie.json b/testdata/processor-requirements/rolie.json new file mode 100644 index 00000000..cd65a7ed --- /dev/null +++ b/testdata/processor-requirements/rolie.json @@ -0,0 +1,210 @@ +[ + { + "num": 1, + "description": "Valid CSAF documents", + "messages": [ + { + "type": 1, + "text": "No remote validator configured" + }, + { + "type": 0, + "text": "All advisories validated fine against the schema." + } + ] + }, + { + "num": 2, + "description": "Filename", + "messages": [ + { + "type": 0, + "text": "All found filenames are conforming." + } + ] + }, + { + "num": 3, + "description": "TLS", + "messages": [ + { + "type": 0, + "text": "All tested URLs were HTTPS." + } + ] + }, + { + "num": 4, + "description": "TLP:WHITE", + "messages": [ + { + "type": 0, + "text": "All advisories labeled TLP:WHITE were freely accessible." + } + ] + }, + { + "num": 5, + "description": "TLP:AMBER and TLP:RED", + "messages": [ + { + "type": 0, + "text": "No advisories labeled TLP:AMBER or TLP:RED tested for accessibility." + } + ] + }, + { + "num": 6, + "description": "Redirects", + "messages": [ + { + "type": 0, + "text": "No redirections found." + } + ] + }, + { + "num": 7, + "description": "provider-metadata.json", + "messages": [ + { + "type": 0, + "text": "Found good provider metadata." + } + ] + }, + { + "num": 8, + "description": "security.txt", + "messages": [ + { + "type": 0, + "text": "Performed no test of security.txt since the direct url of the provider-metadata.json was used." + } + ] + }, + { + "num": 9, + "description": "/.well-known/csaf/provider-metadata.json", + "messages": [ + { + "type": 0, + "text": "Performed no test on whether the provider-metadata.json is available under the .well-known path since the direct url of the provider-metadata.json was used." + } + ] + }, + { + "num": 10, + "description": "DNS path", + "messages": [ + { + "type": 0, + "text": "Performed no test on the contents of https://csaf.data.security.DOMAIN since the direct url of the provider-metadata.json was used." + } + ] + }, + { + "num": 11, + "description": "One folder per year", + "messages": [ + { + "type": 2, + "text": "No year folder found in {{.URL}}/white/avendor-advisory-0004.json" + } + ] + }, + { + "num": 12, + "description": "index.txt", + "messages": [ + { + "type": 2, + "text": "Fetching index.txt failed: {{.URL}}/index.txt not found." + } + ] + }, + { + "num": 13, + "description": "changes.csv", + "messages": [ + { + "type": 2, + "text": "Fetching changes.csv failed: {{.URL}}/changes.csv not found." + } + ] + }, + { + "num": 14, + "description": "Directory listings", + "messages": [ + { + "type": 2, + "text": "Fetching {{.URL}}/white/ failed. Status code 404 (404 Not Found)" + }, + { + "type": 2, + "text": "Not listed advisories: {{.URL}}/white/avendor-advisory-0004.json" + } + ] + }, + { + "num": 15, + "description": "ROLIE feed", + "messages": [ + { + "type": 2, + "text": "No hash listed on ROLIE feed {{.URL}}/white/avendor-advisory-0004.json" + } + ] + }, + { + "num": 16, + "description": "ROLIE service document", + "messages": [ + { + "type": 0, + "text": "ROLIE service document validated fine." + } + ] + }, + { + "num": 17, + "description": "ROLIE category document", + "messages": [ + { + "type": 1, + "text": "Fetching {{.URL}}/white/category-white.json failed. Status code 404 (404 Not Found)" + } + ] + }, + { + "num": 18, + "description": "Integrity", + "messages": [ + { + "type": 0, + "text": "All checksums match." + } + ] + }, + { + "num": 19, + "description": "Signatures", + "messages": [ + { + "type": 0, + "text": "All signatures verified." + } + ] + }, + { + "num": 20, + "description": "Public OpenPGP Key", + "messages": [ + { + "type": 0, + "text": "1 public OpenPGP key(s) loaded." + } + ] + } +] diff --git a/testdata/processor-requirements/sha256-sha512-forbid-hash-fetching-rolie.json b/testdata/processor-requirements/sha256-sha512-forbid-hash-fetching-rolie.json new file mode 100644 index 00000000..03359f0d --- /dev/null +++ b/testdata/processor-requirements/sha256-sha512-forbid-hash-fetching-rolie.json @@ -0,0 +1,214 @@ +[ + { + "num": 1, + "description": "Valid CSAF documents", + "messages": [ + { + "type": 1, + "text": "No remote validator configured" + }, + { + "type": 0, + "text": "All advisories validated fine against the schema." + } + ] + }, + { + "num": 2, + "description": "Filename", + "messages": [ + { + "type": 0, + "text": "All found filenames are conforming." + } + ] + }, + { + "num": 3, + "description": "TLS", + "messages": [ + { + "type": 0, + "text": "All tested URLs were HTTPS." + } + ] + }, + { + "num": 4, + "description": "TLP:WHITE", + "messages": [ + { + "type": 0, + "text": "All advisories labeled TLP:WHITE were freely accessible." + } + ] + }, + { + "num": 5, + "description": "TLP:AMBER and TLP:RED", + "messages": [ + { + "type": 0, + "text": "No advisories labeled TLP:AMBER or TLP:RED tested for accessibility." + } + ] + }, + { + "num": 6, + "description": "Redirects", + "messages": [ + { + "type": 0, + "text": "No redirections found." + } + ] + }, + { + "num": 7, + "description": "provider-metadata.json", + "messages": [ + { + "type": 0, + "text": "Found good provider metadata." + } + ] + }, + { + "num": 8, + "description": "security.txt", + "messages": [ + { + "type": 0, + "text": "Performed no test of security.txt since the direct url of the provider-metadata.json was used." + } + ] + }, + { + "num": 9, + "description": "/.well-known/csaf/provider-metadata.json", + "messages": [ + { + "type": 0, + "text": "Performed no test on whether the provider-metadata.json is available under the .well-known path since the direct url of the provider-metadata.json was used." + } + ] + }, + { + "num": 10, + "description": "DNS path", + "messages": [ + { + "type": 0, + "text": "Performed no test on the contents of https://csaf.data.security.DOMAIN since the direct url of the provider-metadata.json was used." + } + ] + }, + { + "num": 11, + "description": "One folder per year", + "messages": [ + { + "type": 2, + "text": "No year folder found in {{.URL}}/white/avendor-advisory-0004.json" + } + ] + }, + { + "num": 12, + "description": "index.txt", + "messages": [ + { + "type": 2, + "text": "Fetching index.txt failed: {{.URL}}/index.txt not found." + } + ] + }, + { + "num": 13, + "description": "changes.csv", + "messages": [ + { + "type": 2, + "text": "Fetching changes.csv failed: {{.URL}}/changes.csv not found." + } + ] + }, + { + "num": 14, + "description": "Directory listings", + "messages": [ + { + "type": 2, + "text": "Fetching {{.URL}}/white/ failed. Status code 404 (404 Not Found)" + }, + { + "type": 2, + "text": "Not listed advisories: {{.URL}}/white/avendor-advisory-0004.json" + } + ] + }, + { + "num": 15, + "description": "ROLIE feed", + "messages": [ + { + "type": 0, + "text": "All checked ROLIE feeds validated fine." + } + ] + }, + { + "num": 16, + "description": "ROLIE service document", + "messages": [ + { + "type": 0, + "text": "ROLIE service document validated fine." + } + ] + }, + { + "num": 17, + "description": "ROLIE category document", + "messages": [ + { + "type": 1, + "text": "Fetching {{.URL}}/white/category-white.json failed. Status code 404 (404 Not Found)" + } + ] + }, + { + "num": 18, + "description": "Integrity", + "messages": [ + { + "type": 2, + "text": "Fetching {{.URL}}/white/avendor-advisory-0004.json.sha256 failed: Status code 403 (403 Forbidden)" + }, + { + "type": 2, + "text": "Fetching {{.URL}}/white/avendor-advisory-0004.json.sha512 failed: Status code 403 (403 Forbidden)" + } + ] + }, + { + "num": 19, + "description": "Signatures", + "messages": [ + { + "type": 0, + "text": "All signatures verified." + } + ] + }, + { + "num": 20, + "description": "Public OpenPGP Key", + "messages": [ + { + "type": 0, + "text": "1 public OpenPGP key(s) loaded." + } + ] + } +] From b1a76207636a7c312c94344b44546116f31c5641 Mon Sep 17 00:00:00 2001 From: koplas Date: Mon, 16 Dec 2024 12:23:10 +0100 Subject: [PATCH 41/64] Extend processor SHA fetching tests Allow to forbid individual hashes from downloading. This allows to for testing the behavior, if one of the hashes could not be downloaded. --- cmd/csaf_checker/processor_test.go | 119 +++++++++--------- internal/testutil/testutil.go | 14 ++- ...12-forbid-sha256-forbid-sha512-rolie.json} | 0 3 files changed, 68 insertions(+), 65 deletions(-) rename testdata/processor-requirements/{sha256-sha512-forbid-hash-fetching-rolie.json => sha256-sha512-forbid-sha256-forbid-sha512-rolie.json} (100%) diff --git a/cmd/csaf_checker/processor_test.go b/cmd/csaf_checker/processor_test.go index 5b0241ef..9e3f112b 100644 --- a/cmd/csaf_checker/processor_test.go +++ b/cmd/csaf_checker/processor_test.go @@ -29,8 +29,11 @@ func getRequirementTestData(t *testing.T, params testutil.ProviderParams, direct if params.EnableSha512 { path += "sha512-" } - if params.ForbidHashFetching { - path += "forbid-hash-fetching-" + if params.ForbidSha256 { + path += "forbid-sha256-" + } + if params.ForbidSha512 { + path += "forbid-sha512-" } if directoryProvider { path += "directory" @@ -64,74 +67,68 @@ func getRequirementTestData(t *testing.T, params testutil.ProviderParams, direct func TestShaMarking(t *testing.T) { tests := []struct { - name string - directoryProvider bool - enableSha256 bool - enableSha512 bool - forbidHashFetching bool + name string + directoryProvider bool + enableSha256 bool + enableSha512 bool + forbidSha256 bool + forbidSha512 bool }{ { - name: "deliver sha256 and sha512", - directoryProvider: false, - enableSha256: true, - enableSha512: true, - forbidHashFetching: false, + name: "deliver sha256 and sha512", + directoryProvider: false, + enableSha256: true, + enableSha512: true, }, { - name: "enable sha256 and sha512, forbid fetching", - directoryProvider: false, - enableSha256: true, - enableSha512: true, - forbidHashFetching: true, + name: "enable sha256 and sha512, forbid fetching", + directoryProvider: false, + enableSha256: true, + enableSha512: true, + forbidSha256: true, + forbidSha512: true, }, { - name: "only deliver sha256", - directoryProvider: false, - enableSha256: true, - enableSha512: false, - forbidHashFetching: false, + name: "only deliver sha256", + directoryProvider: false, + enableSha256: true, + enableSha512: false, }, { - name: "only deliver sha512", - directoryProvider: false, - enableSha256: false, - enableSha512: true, - forbidHashFetching: false, + name: "only deliver sha512", + directoryProvider: false, + enableSha256: false, + enableSha512: true, }, { - name: "deliver sha256 and sha512, directory provider", - directoryProvider: true, - enableSha256: true, - enableSha512: true, - forbidHashFetching: false, + name: "deliver sha256 and sha512, directory provider", + directoryProvider: true, + enableSha256: true, + enableSha512: true, }, { - name: "only deliver sha256, directory provider", - directoryProvider: true, - enableSha256: true, - enableSha512: false, - forbidHashFetching: false, + name: "only deliver sha256, directory provider", + directoryProvider: true, + enableSha256: true, + enableSha512: false, }, { - name: "only deliver sha512, directory provider", - directoryProvider: true, - enableSha256: false, - enableSha512: true, - forbidHashFetching: false, + name: "only deliver sha512, directory provider", + directoryProvider: true, + enableSha256: false, + enableSha512: true, }, { - name: "no hash", - directoryProvider: false, - enableSha256: false, - enableSha512: false, - forbidHashFetching: false, + name: "no hash", + directoryProvider: false, + enableSha256: false, + enableSha512: false, }, { - name: "no hash, directory provider", - directoryProvider: true, - enableSha256: false, - enableSha512: false, - forbidHashFetching: false, + name: "no hash, directory provider", + directoryProvider: true, + enableSha256: false, + enableSha512: false, }, } @@ -142,10 +139,11 @@ func TestShaMarking(t *testing.T) { tt.Parallel() serverURL := "" params := testutil.ProviderParams{ - URL: "", - EnableSha256: test.enableSha256, - EnableSha512: test.enableSha512, - ForbidHashFetching: test.forbidHashFetching, + URL: "", + EnableSha256: test.enableSha256, + EnableSha512: test.enableSha512, + ForbidSha256: test.forbidSha256, + ForbidSha512: test.forbidSha512, } server := httptest.NewTLSServer(testutil.ProviderHandler(¶ms, test.directoryProvider)) defer server.Close() @@ -173,10 +171,11 @@ func TestShaMarking(t *testing.T) { } expected := getRequirementTestData(t, testutil.ProviderParams{ - URL: serverURL, - EnableSha256: test.enableSha256, - EnableSha512: test.enableSha512, - ForbidHashFetching: test.forbidHashFetching, + URL: serverURL, + EnableSha256: test.enableSha256, + EnableSha512: test.enableSha512, + ForbidSha256: test.forbidSha256, + ForbidSha512: test.forbidSha512, }, test.directoryProvider) for i, got := range report.Domains[0].Requirements { diff --git a/internal/testutil/testutil.go b/internal/testutil/testutil.go index e933742a..c7bad68b 100644 --- a/internal/testutil/testutil.go +++ b/internal/testutil/testutil.go @@ -18,10 +18,11 @@ import ( // ProviderParams configures the test provider. type ProviderParams struct { - URL string - EnableSha256 bool - EnableSha512 bool - ForbidHashFetching bool + URL string + EnableSha256 bool + EnableSha512 bool + ForbidSha256 bool + ForbidSha512 bool } // ProviderHandler returns a test provider handler with the specified configuration. @@ -50,7 +51,10 @@ func ProviderHandler(params *ProviderParams, directoryProvider bool) http.Handle w.Header().Add("Content-Type", "text/html") case strings.HasSuffix(path, ".json"): w.Header().Add("Content-Type", "application/json") - case (strings.HasSuffix(path, ".sha256") || strings.HasSuffix(path, ".sha512")) && params.ForbidHashFetching: + case (strings.HasSuffix(path, ".sha256")) && params.ForbidSha256: + w.WriteHeader(http.StatusForbidden) + return + case strings.HasSuffix(path, ".sha512") && params.ForbidSha512: w.WriteHeader(http.StatusForbidden) return case strings.HasSuffix(path, ".sha256") && directoryProvider && !params.EnableSha256: diff --git a/testdata/processor-requirements/sha256-sha512-forbid-hash-fetching-rolie.json b/testdata/processor-requirements/sha256-sha512-forbid-sha256-forbid-sha512-rolie.json similarity index 100% rename from testdata/processor-requirements/sha256-sha512-forbid-hash-fetching-rolie.json rename to testdata/processor-requirements/sha256-sha512-forbid-sha256-forbid-sha512-rolie.json From d38150c6a0d334300dfb3391964ea051c66aa4ce Mon Sep 17 00:00:00 2001 From: koplas Date: Mon, 16 Dec 2024 12:57:28 +0100 Subject: [PATCH 42/64] Add testdata for individual hash forbidden tests --- cmd/csaf_checker/processor_test.go | 16 ++ .../sha256-sha512-forbid-sha256-rolie.json | 210 ++++++++++++++++++ .../sha256-sha512-forbid-sha512-rolie.json | 210 ++++++++++++++++++ 3 files changed, 436 insertions(+) create mode 100644 testdata/processor-requirements/sha256-sha512-forbid-sha256-rolie.json create mode 100644 testdata/processor-requirements/sha256-sha512-forbid-sha512-rolie.json diff --git a/cmd/csaf_checker/processor_test.go b/cmd/csaf_checker/processor_test.go index 9e3f112b..0710f329 100644 --- a/cmd/csaf_checker/processor_test.go +++ b/cmd/csaf_checker/processor_test.go @@ -88,6 +88,22 @@ func TestShaMarking(t *testing.T) { forbidSha256: true, forbidSha512: true, }, + { + name: "enable sha256 and sha512, forbid sha256", + directoryProvider: false, + enableSha256: true, + enableSha512: true, + forbidSha256: true, + forbidSha512: false, + }, + { + name: "enable sha256 and sha512, forbid sha512", + directoryProvider: false, + enableSha256: true, + enableSha512: true, + forbidSha256: false, + forbidSha512: true, + }, { name: "only deliver sha256", directoryProvider: false, diff --git a/testdata/processor-requirements/sha256-sha512-forbid-sha256-rolie.json b/testdata/processor-requirements/sha256-sha512-forbid-sha256-rolie.json new file mode 100644 index 00000000..2a1f2a82 --- /dev/null +++ b/testdata/processor-requirements/sha256-sha512-forbid-sha256-rolie.json @@ -0,0 +1,210 @@ +[ + { + "num": 1, + "description": "Valid CSAF documents", + "messages": [ + { + "type": 1, + "text": "No remote validator configured" + }, + { + "type": 0, + "text": "All advisories validated fine against the schema." + } + ] + }, + { + "num": 2, + "description": "Filename", + "messages": [ + { + "type": 0, + "text": "All found filenames are conforming." + } + ] + }, + { + "num": 3, + "description": "TLS", + "messages": [ + { + "type": 0, + "text": "All tested URLs were HTTPS." + } + ] + }, + { + "num": 4, + "description": "TLP:WHITE", + "messages": [ + { + "type": 0, + "text": "All advisories labeled TLP:WHITE were freely accessible." + } + ] + }, + { + "num": 5, + "description": "TLP:AMBER and TLP:RED", + "messages": [ + { + "type": 0, + "text": "No advisories labeled TLP:AMBER or TLP:RED tested for accessibility." + } + ] + }, + { + "num": 6, + "description": "Redirects", + "messages": [ + { + "type": 0, + "text": "No redirections found." + } + ] + }, + { + "num": 7, + "description": "provider-metadata.json", + "messages": [ + { + "type": 0, + "text": "Found good provider metadata." + } + ] + }, + { + "num": 8, + "description": "security.txt", + "messages": [ + { + "type": 0, + "text": "Performed no test of security.txt since the direct url of the provider-metadata.json was used." + } + ] + }, + { + "num": 9, + "description": "/.well-known/csaf/provider-metadata.json", + "messages": [ + { + "type": 0, + "text": "Performed no test on whether the provider-metadata.json is available under the .well-known path since the direct url of the provider-metadata.json was used." + } + ] + }, + { + "num": 10, + "description": "DNS path", + "messages": [ + { + "type": 0, + "text": "Performed no test on the contents of https://csaf.data.security.DOMAIN since the direct url of the provider-metadata.json was used." + } + ] + }, + { + "num": 11, + "description": "One folder per year", + "messages": [ + { + "type": 2, + "text": "No year folder found in {{.URL}}/white/avendor-advisory-0004.json" + } + ] + }, + { + "num": 12, + "description": "index.txt", + "messages": [ + { + "type": 2, + "text": "Fetching index.txt failed: {{.URL}}/index.txt not found." + } + ] + }, + { + "num": 13, + "description": "changes.csv", + "messages": [ + { + "type": 2, + "text": "Fetching changes.csv failed: {{.URL}}/changes.csv not found." + } + ] + }, + { + "num": 14, + "description": "Directory listings", + "messages": [ + { + "type": 2, + "text": "Fetching {{.URL}}/white/ failed. Status code 404 (404 Not Found)" + }, + { + "type": 2, + "text": "Not listed advisories: {{.URL}}/white/avendor-advisory-0004.json" + } + ] + }, + { + "num": 15, + "description": "ROLIE feed", + "messages": [ + { + "type": 0, + "text": "All checked ROLIE feeds validated fine." + } + ] + }, + { + "num": 16, + "description": "ROLIE service document", + "messages": [ + { + "type": 0, + "text": "ROLIE service document validated fine." + } + ] + }, + { + "num": 17, + "description": "ROLIE category document", + "messages": [ + { + "type": 1, + "text": "Fetching {{.URL}}/white/category-white.json failed. Status code 404 (404 Not Found)" + } + ] + }, + { + "num": 18, + "description": "Integrity", + "messages": [ + { + "type": 2, + "text": "Fetching {{.URL}}/white/avendor-advisory-0004.json.sha256 failed: Status code 403 (403 Forbidden)" + } + ] + }, + { + "num": 19, + "description": "Signatures", + "messages": [ + { + "type": 0, + "text": "All signatures verified." + } + ] + }, + { + "num": 20, + "description": "Public OpenPGP Key", + "messages": [ + { + "type": 0, + "text": "1 public OpenPGP key(s) loaded." + } + ] + } +] diff --git a/testdata/processor-requirements/sha256-sha512-forbid-sha512-rolie.json b/testdata/processor-requirements/sha256-sha512-forbid-sha512-rolie.json new file mode 100644 index 00000000..2a4c98fc --- /dev/null +++ b/testdata/processor-requirements/sha256-sha512-forbid-sha512-rolie.json @@ -0,0 +1,210 @@ +[ + { + "num": 1, + "description": "Valid CSAF documents", + "messages": [ + { + "type": 1, + "text": "No remote validator configured" + }, + { + "type": 0, + "text": "All advisories validated fine against the schema." + } + ] + }, + { + "num": 2, + "description": "Filename", + "messages": [ + { + "type": 0, + "text": "All found filenames are conforming." + } + ] + }, + { + "num": 3, + "description": "TLS", + "messages": [ + { + "type": 0, + "text": "All tested URLs were HTTPS." + } + ] + }, + { + "num": 4, + "description": "TLP:WHITE", + "messages": [ + { + "type": 0, + "text": "All advisories labeled TLP:WHITE were freely accessible." + } + ] + }, + { + "num": 5, + "description": "TLP:AMBER and TLP:RED", + "messages": [ + { + "type": 0, + "text": "No advisories labeled TLP:AMBER or TLP:RED tested for accessibility." + } + ] + }, + { + "num": 6, + "description": "Redirects", + "messages": [ + { + "type": 0, + "text": "No redirections found." + } + ] + }, + { + "num": 7, + "description": "provider-metadata.json", + "messages": [ + { + "type": 0, + "text": "Found good provider metadata." + } + ] + }, + { + "num": 8, + "description": "security.txt", + "messages": [ + { + "type": 0, + "text": "Performed no test of security.txt since the direct url of the provider-metadata.json was used." + } + ] + }, + { + "num": 9, + "description": "/.well-known/csaf/provider-metadata.json", + "messages": [ + { + "type": 0, + "text": "Performed no test on whether the provider-metadata.json is available under the .well-known path since the direct url of the provider-metadata.json was used." + } + ] + }, + { + "num": 10, + "description": "DNS path", + "messages": [ + { + "type": 0, + "text": "Performed no test on the contents of https://csaf.data.security.DOMAIN since the direct url of the provider-metadata.json was used." + } + ] + }, + { + "num": 11, + "description": "One folder per year", + "messages": [ + { + "type": 2, + "text": "No year folder found in {{.URL}}/white/avendor-advisory-0004.json" + } + ] + }, + { + "num": 12, + "description": "index.txt", + "messages": [ + { + "type": 2, + "text": "Fetching index.txt failed: {{.URL}}/index.txt not found." + } + ] + }, + { + "num": 13, + "description": "changes.csv", + "messages": [ + { + "type": 2, + "text": "Fetching changes.csv failed: {{.URL}}/changes.csv not found." + } + ] + }, + { + "num": 14, + "description": "Directory listings", + "messages": [ + { + "type": 2, + "text": "Fetching {{.URL}}/white/ failed. Status code 404 (404 Not Found)" + }, + { + "type": 2, + "text": "Not listed advisories: {{.URL}}/white/avendor-advisory-0004.json" + } + ] + }, + { + "num": 15, + "description": "ROLIE feed", + "messages": [ + { + "type": 0, + "text": "All checked ROLIE feeds validated fine." + } + ] + }, + { + "num": 16, + "description": "ROLIE service document", + "messages": [ + { + "type": 0, + "text": "ROLIE service document validated fine." + } + ] + }, + { + "num": 17, + "description": "ROLIE category document", + "messages": [ + { + "type": 1, + "text": "Fetching {{.URL}}/white/category-white.json failed. Status code 404 (404 Not Found)" + } + ] + }, + { + "num": 18, + "description": "Integrity", + "messages": [ + { + "type": 2, + "text": "Fetching {{.URL}}/white/avendor-advisory-0004.json.sha512 failed: Status code 403 (403 Forbidden)" + } + ] + }, + { + "num": 19, + "description": "Signatures", + "messages": [ + { + "type": 0, + "text": "All signatures verified." + } + ] + }, + { + "num": 20, + "description": "Public OpenPGP Key", + "messages": [ + { + "type": 0, + "text": "1 public OpenPGP key(s) loaded." + } + ] + } +] From bc5d149f74d2ce5e7ed03316141a31eafbd80ea1 Mon Sep 17 00:00:00 2001 From: koplas Date: Mon, 16 Dec 2024 19:28:24 +0100 Subject: [PATCH 43/64] Use exit code 1 for general errors, fix documentation --- cmd/csaf_validator/main.go | 2 +- docs/csaf_validator.md | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/cmd/csaf_validator/main.go b/cmd/csaf_validator/main.go index 1a34be0d..346180bc 100644 --- a/cmd/csaf_validator/main.go +++ b/cmd/csaf_validator/main.go @@ -23,7 +23,7 @@ import ( ) const ( - exitCodeSchemaInvalid = 1 << iota + exitCodeSchemaInvalid = 2 << iota exitCodeNoRemoteValidator exitCodeFailedRemoteValidation exitCodeAllValid = 0 diff --git a/docs/csaf_validator.md b/docs/csaf_validator.md index 64ded6dc..a0e00bbe 100644 --- a/docs/csaf_validator.md +++ b/docs/csaf_validator.md @@ -5,9 +5,10 @@ is a tool to validate local advisories files against the JSON Schema and an opti ### Exit codes If no fatal error occurs the program will exit with an exit code `n` with the following conditions: - `n == 0`: all valid -- `(n / 2) % 1 == 1`: schema validation failed -- `(n / 4) % 1 == 1`: no remote validator configured -- `(n / 8) % 1 == 1`: failure in remote validation +- `(n & 1) > 0`: general error, see logs +- `(n & 2) > 0`: schema validation failed +- `(n & 4) > 0`: no remote validator configured +- `(n & 8) > 0`: failure in remote validation ### Usage From 95ff418a270d618ffc2b6fb661e702cf7639d75f Mon Sep 17 00:00:00 2001 From: Bernhard Reiter Date: Wed, 18 Dec 2024 08:55:48 +0100 Subject: [PATCH 44/64] fix: Content-Type header for JSON responses * Remove `charset=utf-8` parameter, which is not allowed for JSON, according to rfc8259. --- cmd/csaf_provider/controller.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/csaf_provider/controller.go b/cmd/csaf_provider/controller.go index 7f64fe29..f04b7bd1 100644 --- a/cmd/csaf_provider/controller.go +++ b/cmd/csaf_provider/controller.go @@ -174,7 +174,7 @@ func (c *controller) web( // writeJSON sets the header for the response and writes the JSON encoding of the given "content". // It logs out an error message in case of an error. func writeJSON(rw http.ResponseWriter, content any, code int) { - rw.Header().Set("Content-type", "application/json; charset=utf-8") + rw.Header().Set("Content-type", "application/json") rw.Header().Set("X-Content-Type-Options", "nosniff") rw.WriteHeader(code) if err := json.NewEncoder(rw).Encode(content); err != nil { From d8e903587a8744b51227da17867505da75a44c41 Mon Sep 17 00:00:00 2001 From: koplas Date: Wed, 18 Dec 2024 15:37:58 +0100 Subject: [PATCH 45/64] Warn only if the other hash could be fetched --- cmd/csaf_checker/processor.go | 28 +++++++++++++------ .../sha256-sha512-forbid-sha256-rolie.json | 2 +- .../sha256-sha512-forbid-sha512-rolie.json | 2 +- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/cmd/csaf_checker/processor.go b/cmd/csaf_checker/processor.go index b9138646..224e2259 100644 --- a/cmd/csaf_checker/processor.go +++ b/cmd/csaf_checker/processor.go @@ -757,6 +757,9 @@ func (p *processor) integrity( hashes = append(hashes, hash{"SHA512", f.SHA512URL, s512.Sum(nil)}) } + couldFetchHash := false + hashFetchErrors := []string{} + for _, x := range hashes { hu, err := url.Parse(x.url()) if err != nil { @@ -768,19 +771,15 @@ func (p *processor) integrity( p.checkTLS(hashFile) if res, err = client.Get(hashFile); err != nil { - p.badIntegrities.error("Fetching %s failed: %v.", hashFile, err) + hashFetchErrors = append(hashFetchErrors, fmt.Sprintf("Fetching %s failed: %v.", hashFile, err)) continue } if res.StatusCode != http.StatusOK { - if f.IsDirectory() { - p.badIntegrities.info("Fetching %s failed: Status code %d (%s)", - hashFile, res.StatusCode, res.Status) - } else { - p.badIntegrities.error("Fetching %s failed: Status code %d (%s)", - hashFile, res.StatusCode, res.Status) - } + hashFetchErrors = append(hashFetchErrors, fmt.Sprintf("Fetching %s failed: Status code %d (%s)", + hashFile, res.StatusCode, res.Status)) continue } + couldFetchHash = true h, err := func() ([]byte, error) { defer res.Body.Close() return util.HashFromReader(res.Body) @@ -798,6 +797,19 @@ func (p *processor) integrity( x.ext, u, hashFile) } } + + msgType := ErrorType + // Log only as warning, if the other hash could be fetched + if couldFetchHash { + msgType = WarnType + } + if f.IsDirectory() { + msgType = InfoType + } + for _, fetchError := range hashFetchErrors { + p.badIntegrities.add(msgType, fetchError) + } + // Check signature su, err := url.Parse(f.SignURL()) if err != nil { diff --git a/testdata/processor-requirements/sha256-sha512-forbid-sha256-rolie.json b/testdata/processor-requirements/sha256-sha512-forbid-sha256-rolie.json index 2a1f2a82..72a173a6 100644 --- a/testdata/processor-requirements/sha256-sha512-forbid-sha256-rolie.json +++ b/testdata/processor-requirements/sha256-sha512-forbid-sha256-rolie.json @@ -182,7 +182,7 @@ "description": "Integrity", "messages": [ { - "type": 2, + "type": 1, "text": "Fetching {{.URL}}/white/avendor-advisory-0004.json.sha256 failed: Status code 403 (403 Forbidden)" } ] diff --git a/testdata/processor-requirements/sha256-sha512-forbid-sha512-rolie.json b/testdata/processor-requirements/sha256-sha512-forbid-sha512-rolie.json index 2a4c98fc..1ab8f1ee 100644 --- a/testdata/processor-requirements/sha256-sha512-forbid-sha512-rolie.json +++ b/testdata/processor-requirements/sha256-sha512-forbid-sha512-rolie.json @@ -182,7 +182,7 @@ "description": "Integrity", "messages": [ { - "type": 2, + "type": 1, "text": "Fetching {{.URL}}/white/avendor-advisory-0004.json.sha512 failed: Status code 403 (403 Forbidden)" } ] From 8fc7f5bfad0c6022cbcc07cec36b875cb4ad292e Mon Sep 17 00:00:00 2001 From: koplas Date: Tue, 7 Jan 2025 12:23:40 +0100 Subject: [PATCH 46/64] Make documentation more explicit --- docs/csaf_validator.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/csaf_validator.md b/docs/csaf_validator.md index a0e00bbe..87ec831e 100644 --- a/docs/csaf_validator.md +++ b/docs/csaf_validator.md @@ -3,9 +3,11 @@ is a tool to validate local advisories files against the JSON Schema and an optional remote validator. ### Exit codes + If no fatal error occurs the program will exit with an exit code `n` with the following conditions: + - `n == 0`: all valid -- `(n & 1) > 0`: general error, see logs +- `(n & 1) > 0`: a general error occurred, all other flags are unset (see logs for more information) - `(n & 2) > 0`: schema validation failed - `(n & 4) > 0`: no remote validator configured - `(n & 8) > 0`: failure in remote validation From b8a5fa72d5d164b5996ec068de9c5e5e9bac15c5 Mon Sep 17 00:00:00 2001 From: koplas Date: Wed, 8 Jan 2025 08:49:42 +0100 Subject: [PATCH 47/64] Fix nil check in downloader --- cmd/csaf_downloader/downloader.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/csaf_downloader/downloader.go b/cmd/csaf_downloader/downloader.go index c8d92c12..ba6ccff5 100644 --- a/cmd/csaf_downloader/downloader.go +++ b/cmd/csaf_downloader/downloader.go @@ -133,7 +133,7 @@ func (d *downloader) httpClient() util.Client { client := util.Client(&hClient) // Overwrite for testing purposes - if client != nil { + if d.client != nil { client = *d.client } From 9275a37a9faa07943b326ebded09559ef36a1084 Mon Sep 17 00:00:00 2001 From: koplas Date: Wed, 8 Jan 2025 08:50:30 +0100 Subject: [PATCH 48/64] Format --- cmd/csaf_downloader/downloader.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/cmd/csaf_downloader/downloader.go b/cmd/csaf_downloader/downloader.go index ba6ccff5..88a63c26 100644 --- a/cmd/csaf_downloader/downloader.go +++ b/cmd/csaf_downloader/downloader.go @@ -514,7 +514,8 @@ nextAdvisory: url: file.SHA512URL(), warn: true, hashType: algSha512, - preferred: strings.EqualFold(string(d.cfg.PreferredHash), string(algSha512))}) + preferred: strings.EqualFold(string(d.cfg.PreferredHash), string(algSha512)), + }) } else { slog.Info("SHA512 not present") } @@ -523,7 +524,8 @@ nextAdvisory: url: file.SHA256URL(), warn: true, hashType: algSha256, - preferred: strings.EqualFold(string(d.cfg.PreferredHash), string(algSha256))}) + preferred: strings.EqualFold(string(d.cfg.PreferredHash), string(algSha256)), + }) } else { slog.Info("SHA256 not present") } From b6721e1d5ad3b2f4f4d6d37501a4b74cd665a2bd Mon Sep 17 00:00:00 2001 From: JanHoefelmeyer Date: Fri, 10 Jan 2025 11:42:54 +0100 Subject: [PATCH 49/64] Add check for missing either sha256 or sha512 hashes only --- cmd/csaf_checker/processor.go | 4 ++++ testdata/processor-requirements/sha256-rolie.json | 2 +- testdata/processor-requirements/sha512-rolie.json | 2 +- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/cmd/csaf_checker/processor.go b/cmd/csaf_checker/processor.go index 224e2259..5c4f66ee 100644 --- a/cmd/csaf_checker/processor.go +++ b/cmd/csaf_checker/processor.go @@ -584,6 +584,10 @@ func (p *processor) rolieFeedEntries(feed string) ([]csaf.AdvisoryFile, error) { var file csaf.AdvisoryFile switch { + case sha256 == "" && sha512 != "": + p.badROLIEFeed.info("%s has no sha256 hash file listed", url) + case sha256 != "" && sha512 == "": + p.badROLIEFeed.info("%s has no sha512 hash file listed", url) case sha256 == "" && sha512 == "": p.badROLIEFeed.error("No hash listed on ROLIE feed %s", url) case sign == "": diff --git a/testdata/processor-requirements/sha256-rolie.json b/testdata/processor-requirements/sha256-rolie.json index 5875174b..4ed47f16 100644 --- a/testdata/processor-requirements/sha256-rolie.json +++ b/testdata/processor-requirements/sha256-rolie.json @@ -153,7 +153,7 @@ "messages": [ { "type": 0, - "text": "All checked ROLIE feeds validated fine." + "text": "{{.URL}}/white/avendor-advisory-0004.json has no sha512 hash file listed" } ] }, diff --git a/testdata/processor-requirements/sha512-rolie.json b/testdata/processor-requirements/sha512-rolie.json index 5875174b..a2a195d8 100644 --- a/testdata/processor-requirements/sha512-rolie.json +++ b/testdata/processor-requirements/sha512-rolie.json @@ -153,7 +153,7 @@ "messages": [ { "type": 0, - "text": "All checked ROLIE feeds validated fine." + "text": "{{.URL}}/white/avendor-advisory-0004.json has no sha256 hash file listed" } ] }, From 028f468d6f25f2e47d96fb1a5d924d3e22ab5949 Mon Sep 17 00:00:00 2001 From: koplas Date: Thu, 23 Jan 2025 10:32:13 +0100 Subject: [PATCH 50/64] Fix typo in error message Closes #608 --- cmd/csaf_checker/reporters.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/csaf_checker/reporters.go b/cmd/csaf_checker/reporters.go index 157eabee..9cd3fc88 100644 --- a/cmd/csaf_checker/reporters.go +++ b/cmd/csaf_checker/reporters.go @@ -178,7 +178,7 @@ func (r *tlpAmberRedReporter) report(p *processor, domain *Domain) { return } if len(p.badAmberRedPermissions) == 0 { - req.message(InfoType, "All tested advisories labeled TLP:WHITE or TLP:RED were access-protected.") + req.message(InfoType, "All tested advisories labeled TLP:AMBER or TLP:RED were access-protected.") return } req.Messages = p.badAmberRedPermissions From 59d2cef0826080f9bf7bd60332c15ec614153834 Mon Sep 17 00:00:00 2001 From: koplas Date: Thu, 23 Jan 2025 11:53:57 +0100 Subject: [PATCH 51/64] Fix typos --- cmd/csaf_validator/main.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmd/csaf_validator/main.go b/cmd/csaf_validator/main.go index 346180bc..3250388e 100644 --- a/cmd/csaf_validator/main.go +++ b/cmd/csaf_validator/main.go @@ -107,7 +107,7 @@ func run(opts *options, files []string) error { log.Printf("error: loading %q as JSON failed: %v\n", file, err) continue } - // Validate agsinst Schema. + // Validate against Schema. validationErrs, err := csaf.ValidateCSAF(doc) if err != nil { log.Printf("error: validating %q against schema failed: %v\n", @@ -124,7 +124,7 @@ func run(opts *options, files []string) error { fmt.Printf("%q passes the schema validation.\n", file) } - // Check filename agains ID + // Check filename against ID if err := util.IDMatchesFilename(eval, doc, filepath.Base(file)); err != nil { log.Printf("%s: %s.\n", file, err) continue From 6e8c2ecc059090865dd6bc48bc4ff0371757c8ee Mon Sep 17 00:00:00 2001 From: koplas Date: Thu, 23 Jan 2025 12:22:11 +0100 Subject: [PATCH 52/64] Check remote validator even if file validation fails This makes it consistent with the handling of schema validation. --- cmd/csaf_validator/main.go | 1 - 1 file changed, 1 deletion(-) diff --git a/cmd/csaf_validator/main.go b/cmd/csaf_validator/main.go index 3250388e..b3a08557 100644 --- a/cmd/csaf_validator/main.go +++ b/cmd/csaf_validator/main.go @@ -127,7 +127,6 @@ func run(opts *options, files []string) error { // Check filename against ID if err := util.IDMatchesFilename(eval, doc, filepath.Base(file)); err != nil { log.Printf("%s: %s.\n", file, err) - continue } // Validate against remote validator. From 84026b682d80e1edcc3ca8a8346c69a7e8e56059 Mon Sep 17 00:00:00 2001 From: "Bernhard E. Reiter" Date: Tue, 28 Jan 2025 17:41:54 +0100 Subject: [PATCH 53/64] Update README.md to exchange csaf.io until it is fixed --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 463b1d92..8f0c5f31 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ # csaf -Implements a [CSAF](https://csaf.io/) +Implements a [CSAF](https://oasis-open.github.io/csaf-documentation/) ([specification v2.0](https://docs.oasis-open.org/csaf/csaf/v2.0/os/csaf-v2.0-os.html) and its [errata](https://docs.oasis-open.org/csaf/csaf/v2.0/csaf-v2.0.html)) trusted provider, checker, aggregator and downloader. From 7d74543bbbf7cc3f5051f6fef3a84c97347d5eba Mon Sep 17 00:00:00 2001 From: JanHoefelmeyer Date: Wed, 29 Jan 2025 09:02:18 +0100 Subject: [PATCH 54/64] Fix: Now give errors if lookup methods fail, refactor accordingly --- cmd/csaf_checker/processor.go | 72 ++++++++++++++++------------------- 1 file changed, 33 insertions(+), 39 deletions(-) diff --git a/cmd/csaf_checker/processor.go b/cmd/csaf_checker/processor.go index 5d1b69b5..e07f5ad7 100644 --- a/cmd/csaf_checker/processor.go +++ b/cmd/csaf_checker/processor.go @@ -1340,49 +1340,57 @@ func (p *processor) checkSecurityFolder(folder string) string { // checkDNS checks if the "csaf.data.security.domain.tld" DNS record is available // and serves the "provider-metadata.json". // It returns an empty string if all checks are passed, otherwise the errormessage. -func (p *processor) checkDNS(domain string) string { +func (p *processor) checkDNS(domain string) { + + p.badDNSPath.use() client := p.httpClient() path := "https://csaf.data.security." + domain res, err := client.Get(path) if err != nil { - return fmt.Sprintf("Fetching %s failed: %v", path, err) + p.badDNSPath.add(ErrorType, + fmt.Sprintf("Fetching %s failed: %v", path, err)) + return } if res.StatusCode != http.StatusOK { - return fmt.Sprintf("Fetching %s failed. Status code %d (%s)", - path, res.StatusCode, res.Status) - + p.badDNSPath.add(ErrorType, + fmt.Sprintf("Fetching %s failed. Status code %d (%s)", + path, res.StatusCode, res.Status)) } hash := sha256.New() defer res.Body.Close() content, err := io.ReadAll(res.Body) if err != nil { - return fmt.Sprintf("Error while reading the response from %s", path) + p.badDNSPath.add(ErrorType, + fmt.Sprintf("Error while reading the response from %s", path)) } hash.Write(content) if !bytes.Equal(hash.Sum(nil), p.pmd256) { - return fmt.Sprintf("%s does not serve the same provider-metadata.json as previously found", path) + p.badDNSPath.add(ErrorType, + fmt.Sprintf("%s does not serve the same provider-metadata.json as previously found", + path)) } - return "" } -// checkWellknownMetadataReporter checks if the provider-metadata.json file is +// checkWellknown checks if the provider-metadata.json file is // available under the /.well-known/csaf/ directory. Returns the errormessage if // an error was encountered, or an empty string otherwise -func (p *processor) checkWellknown(domain string) string { +func (p *processor) checkWellknown(domain string) { + + p.badWellknownMetadata.use() client := p.httpClient() path := "https://" + domain + "/.well-known/csaf/provider-metadata.json" res, err := client.Get(path) if err != nil { - return fmt.Sprintf("Fetching %s failed: %v", path, err) + p.badWellknownMetadata.add(ErrorType, + fmt.Sprintf("Fetching %s failed: %v", path, err)) } if res.StatusCode != http.StatusOK { - return fmt.Sprintf("Fetching %s failed. Status code %d (%s)", - path, res.StatusCode, res.Status) + p.badWellknownMetadata.add(ErrorType, fmt.Sprintf("Fetching %s failed. Status code %d (%s)", + path, res.StatusCode, res.Status)) } - return "" } // checkWellknownSecurityDNS @@ -1401,50 +1409,36 @@ func (p *processor) checkWellknown(domain string) string { // In that case, errors are returned. func (p *processor) checkWellknownSecurityDNS(domain string) error { - warningsW := p.checkWellknown(domain) + p.checkWellknown(domain) + p.checkDNS(domain) + // Security check for well known (default) and legacy location - warningsS, sDMessage := p.checkSecurity(domain, false) + warnings, sDMessage := p.checkSecurity(domain, false) // if the security.txt under .well-known was not okay // check for a security.txt within its legacy location sLMessage := "" - if warningsS == 1 { - warningsS, sLMessage = p.checkSecurity(domain, true) + if warnings == 1 { + warnings, sLMessage = p.checkSecurity(domain, true) } - warningsD := p.checkDNS(domain) - p.badWellknownMetadata.use() p.badSecurity.use() - p.badDNSPath.use() - - var kind MessageType - if warningsS != 1 || warningsD == "" || warningsW == "" { - kind = WarnType - } else { - kind = ErrorType - } // Info, Warning or Error depending on kind and warningS - kindSD := kind - if warningsS == 0 { + kindSD := WarnType + if warnings == 0 { kindSD = InfoType } - kindSL := kind - if warningsS == 2 { + kindSL := ErrorType + if warnings == 2 { kindSL = InfoType } - if warningsW != "" { - p.badWellknownMetadata.add(kind, warningsW) - } p.badSecurity.add(kindSD, sDMessage) // only if the well-known security.txt was not successful: // report about the legacy location - if warningsS != 0 { + if warnings != 0 { p.badSecurity.add(kindSL, sLMessage) } - if warningsD != "" { - p.badDNSPath.add(kind, warningsD) - } return nil } From 02787b24b799113b769b9ce3bfaeeb66b435340e Mon Sep 17 00:00:00 2001 From: JanHoefelmeyer Date: Wed, 29 Jan 2025 09:26:59 +0100 Subject: [PATCH 55/64] Update comments, clean up security check --- cmd/csaf_checker/processor.go | 44 +++++++++++++++++------------------ 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/cmd/csaf_checker/processor.go b/cmd/csaf_checker/processor.go index e07f5ad7..cb38bda3 100644 --- a/cmd/csaf_checker/processor.go +++ b/cmd/csaf_checker/processor.go @@ -1339,7 +1339,6 @@ func (p *processor) checkSecurityFolder(folder string) string { // checkDNS checks if the "csaf.data.security.domain.tld" DNS record is available // and serves the "provider-metadata.json". -// It returns an empty string if all checks are passed, otherwise the errormessage. func (p *processor) checkDNS(domain string) { p.badDNSPath.use() @@ -1373,8 +1372,7 @@ func (p *processor) checkDNS(domain string) { } // checkWellknown checks if the provider-metadata.json file is -// available under the /.well-known/csaf/ directory. Returns the errormessage if -// an error was encountered, or an empty string otherwise +// available under the /.well-known/csaf/ directory. func (p *processor) checkWellknown(domain string) { p.badWellknownMetadata.use() @@ -1402,15 +1400,13 @@ func (p *processor) checkWellknown(domain string) { // 4. Finally it checks if the "csaf.data.security.domain.tld" DNS record // is available and serves the "provider-metadata.json". // -// / -// If all three checks fail, errors are given, -// otherwise warnings for all failed checks. -// The function returns nil, unless errors outside the checks were found. -// In that case, errors are returned. +// For the security.txt checks, it first checks the default location. +// Should this lookup fail, a warning is will be given and a lookup +// for the legacy location will be made. If this fails as well, then an +// error is given. func (p *processor) checkWellknownSecurityDNS(domain string) error { p.checkWellknown(domain) - p.checkDNS(domain) // Security check for well known (default) and legacy location warnings, sDMessage := p.checkSecurity(domain, false) @@ -1423,22 +1419,24 @@ func (p *processor) checkWellknownSecurityDNS(domain string) error { p.badSecurity.use() - // Info, Warning or Error depending on kind and warningS - kindSD := WarnType - if warnings == 0 { - kindSD = InfoType - } - kindSL := ErrorType - if warnings == 2 { - kindSL = InfoType + // Report about Securitytxt: + // Only report about Legacy if default was succesful (0). + // Report default and legacy as errors if neither was succesful (1). + // Warn about missing security in the default position if not found + // but found in the legacy location, and inform about finding it there (2). + switch warnings { + case 0: + p.badSecurity.add(InfoType, sDMessage) + case 1: + p.badSecurity.add(ErrorType, sDMessage) + p.badSecurity.add(ErrorType, sLMessage) + case 2: + p.badSecurity.add(WarnType, sDMessage) + p.badSecurity.add(InfoType, sLMessage) } - p.badSecurity.add(kindSD, sDMessage) - // only if the well-known security.txt was not successful: - // report about the legacy location - if warnings != 0 { - p.badSecurity.add(kindSL, sLMessage) - } + p.checkDNS(domain) + return nil } From 82a6929e4dd9aea3743cb905e415665825f0dc89 Mon Sep 17 00:00:00 2001 From: JanHoefelmeyer Date: Wed, 29 Jan 2025 09:41:16 +0100 Subject: [PATCH 56/64] Fix: Poor phrasing corrected --- cmd/csaf_checker/processor.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmd/csaf_checker/processor.go b/cmd/csaf_checker/processor.go index cb38bda3..d6f0f6b4 100644 --- a/cmd/csaf_checker/processor.go +++ b/cmd/csaf_checker/processor.go @@ -1420,7 +1420,7 @@ func (p *processor) checkWellknownSecurityDNS(domain string) error { p.badSecurity.use() // Report about Securitytxt: - // Only report about Legacy if default was succesful (0). + // Only report about default location if it was succesful (0). // Report default and legacy as errors if neither was succesful (1). // Warn about missing security in the default position if not found // but found in the legacy location, and inform about finding it there (2). From 6e02de974e537ace9cd08179225a715674f8f096 Mon Sep 17 00:00:00 2001 From: Bernhard Reiter Date: Tue, 25 Feb 2025 15:03:38 +0100 Subject: [PATCH 57/64] update release workflow dependencies and so glibc * Update runner to ubuntu-22.04 which is the eldest to be supported by github from 2025-04-01. * Update github actions and go version needed. --- .github/workflows/release.yml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index d1e370fa..f77c9e3f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,23 +7,23 @@ on: jobs: releases-matrix: name: Release Go binaries - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 permissions: contents: write steps: - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Set up Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v5 with: - go-version: '^1.21.0' + go-version: '^1.23.6' - name: Build run: make dist - name: Upload release assets - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: files: | dist/csaf-*.zip From a4a90f4f9274b295c27bfb6df255e6b2a5134f45 Mon Sep 17 00:00:00 2001 From: Bernhard Reiter Date: Tue, 25 Feb 2025 15:07:34 +0100 Subject: [PATCH 58/64] update go version to 1.23 --- .github/workflows/itest.yml | 6 +++--- README.md | 2 +- docs/Development.md | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/itest.yml b/.github/workflows/itest.yml index 9cc4c6b6..b537b39a 100644 --- a/.github/workflows/itest.yml +++ b/.github/workflows/itest.yml @@ -7,9 +7,9 @@ jobs: steps: - name: Set up Go - uses: actions/setup-go@v3 + uses: actions/setup-go@v5 with: - go-version: 1.22.0 + go-version: '^1.23.6' - name: Set up Node.js uses: actions/setup-node@v3 @@ -17,7 +17,7 @@ jobs: node-version: 16 - name: Checkout - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Execute the scripts run: | diff --git a/README.md b/README.md index 8f0c5f31..b76bf952 100644 --- a/README.md +++ b/README.md @@ -79,7 +79,7 @@ Download the binaries from the most recent release assets on Github. ### Build from sources -- A recent version of **Go** (1.22+) should be installed. [Go installation](https://go.dev/doc/install) +- A recent version of **Go** (1.23+) should be installed. [Go installation](https://go.dev/doc/install) - Clone the repository `git clone https://github.com/gocsaf/csaf.git ` diff --git a/docs/Development.md b/docs/Development.md index 5c4df224..bc71c2c1 100644 --- a/docs/Development.md +++ b/docs/Development.md @@ -3,7 +3,7 @@ ## Supported Go versions We support the latest version and the one before -the latest version of Go (currently 1.22 and 1.23). +the latest version of Go (currently 1.23 and 1.24). ## Generated files From 3afa8d8b2e908cba70bddde5442240cab5ec9bb9 Mon Sep 17 00:00:00 2001 From: koplas Date: Tue, 25 Feb 2025 15:24:24 +0100 Subject: [PATCH 59/64] Upgrade to artifact action v4 --- .github/workflows/itest.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/itest.yml b/.github/workflows/itest.yml index b537b39a..8bc87d52 100644 --- a/.github/workflows/itest.yml +++ b/.github/workflows/itest.yml @@ -36,7 +36,7 @@ jobs: shell: bash - name: Upload test results - uses: actions/upload-artifact@v3 + uses: actions/upload-artifact@v4 with: name: checker-results path: | From e91bdec201822e1e334582a5dde0388e92d74994 Mon Sep 17 00:00:00 2001 From: Paul Schwabauer Date: Mon, 3 Mar 2025 17:31:21 +0100 Subject: [PATCH 60/64] Add example for iterating product id and product helper (#617) * Add example for iterating product id and product helper * simplify code a bit * Remove newline --------- Co-authored-by: Sascha L. Teichmann --- examples/product_lister/main.go | 141 ++++++++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 examples/product_lister/main.go diff --git a/examples/product_lister/main.go b/examples/product_lister/main.go new file mode 100644 index 00000000..5ad26a93 --- /dev/null +++ b/examples/product_lister/main.go @@ -0,0 +1,141 @@ +// Package main implements a simple demo program to +// work with the csaf library. +package main + +import ( + "encoding/json" + "flag" + "fmt" + "log" + "os" + + "github.com/gocsaf/csaf/v3/csaf" +) + +func main() { + flag.Usage = func() { + if _, err := fmt.Fprintf(flag.CommandLine.Output(), + "Usage:\n %s [OPTIONS] files...\n\nOptions:\n", os.Args[0]); err != nil { + log.Fatalf("error: %v\n", err) + } + flag.PrintDefaults() + } + printProductIdentHelper := flag.Bool("print_ident_helper", false, "print product helper mapping") + flag.Parse() + + files := flag.Args() + if len(files) == 0 { + log.Println("No files given.") + return + } + + var printer func(*csaf.Advisory) error + if *printProductIdentHelper { + printer = printProductIdentHelperMapping + } else { + printer = printProductIDMapping + } + + if err := run(files, printer); err != nil { + log.Fatalf("error: %v\n", err) + } +} + +// visitFullProductNames iterates all full product names in the advisory. +func visitFullProductNames( + adv *csaf.Advisory, + visit func(*csaf.FullProductName), +) { + // Iterate over all full product names + if fpns := adv.ProductTree.FullProductNames; fpns != nil { + for _, fpn := range *fpns { + if fpn != nil && fpn.ProductID != nil { + visit(fpn) + } + } + } + + // Iterate over branches recursively + var recBranch func(b *csaf.Branch) + recBranch = func(b *csaf.Branch) { + if b == nil { + return + } + if fpn := b.Product; fpn != nil && fpn.ProductID != nil { + visit(fpn) + + } + for _, c := range b.Branches { + recBranch(c) + } + } + for _, b := range adv.ProductTree.Branches { + recBranch(b) + } + + // Iterate over relationships + if rels := adv.ProductTree.RelationShips; rels != nil { + for _, rel := range *rels { + if rel != nil { + if fpn := rel.FullProductName; fpn != nil && fpn.ProductID != nil { + visit(fpn) + } + } + } + } +} + +// run applies fn to all loaded advisories. +func run(files []string, fn func(*csaf.Advisory) error) error { + for _, file := range files { + adv, err := csaf.LoadAdvisory(file) + if err != nil { + return fmt.Errorf("loading %q failed: %w", file, err) + } + if err := fn(adv); err != nil { + return err + } + } + return nil +} + +// printJSON serializes v as indented JSON to stdout. +func printJSON(v any) error { + enc := json.NewEncoder(os.Stdout) + enc.SetIndent("", " ") + return enc.Encode(v) +} + +// printProductIDMapping prints all product ids with their name and identification helper. +func printProductIDMapping(adv *csaf.Advisory) error { + type productNameHelperMapping struct { + FullProductName *csaf.FullProductName `json:"product"` + ProductIdentificationHelper *csaf.ProductIdentificationHelper `json:"product_identification_helper"` + } + + productIDMap := map[csaf.ProductID][]productNameHelperMapping{} + visitFullProductNames(adv, func(fpn *csaf.FullProductName) { + productIDMap[*fpn.ProductID] = append(productIDMap[*fpn.ProductID], productNameHelperMapping{ + FullProductName: fpn, + ProductIdentificationHelper: fpn.ProductIdentificationHelper, + }) + }) + return printJSON(productIDMap) +} + +// printProductIdentHelperMapping prints all product identifier helper with their product id. +func printProductIdentHelperMapping(adv *csaf.Advisory) error { + type productIdentIDMapping struct { + ProductNameHelperMapping csaf.ProductIdentificationHelper `json:"product_identification_helper"` + ProductID *csaf.ProductID `json:"product_id"` + } + + productIdentMap := []productIdentIDMapping{} + visitFullProductNames(adv, func(fpn *csaf.FullProductName) { + productIdentMap = append(productIdentMap, productIdentIDMapping{ + ProductNameHelperMapping: *fpn.ProductIdentificationHelper, + ProductID: fpn.ProductID, + }) + }) + return printJSON(productIdentMap) +} From 24f9af7f26bf558ec92dedc86317a1267b169896 Mon Sep 17 00:00:00 2001 From: Paul Schwabauer Date: Wed, 5 Mar 2025 09:55:11 +0100 Subject: [PATCH 61/64] Add documentation for externally signed documents Closes #607 --- docs/csaf_uploader.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/csaf_uploader.md b/docs/csaf_uploader.md index 0e68aa96..76af99f8 100644 --- a/docs/csaf_uploader.md +++ b/docs/csaf_uploader.md @@ -43,6 +43,12 @@ E.g. uploading a csaf-document which asks to enter a password interactively. +To upload an already signed document, use the `-x` option +```bash +# Note: The file CSAF-document-1.json.asc must exist +./csaf_uploader -x -a upload -I -t white -u https://localhost/cgi-bin/csaf_provider.go CSAF-document-1.json +``` + By default csaf_uploader will try to load a config file from the following places: From ec0c3f9c2ca9a9080f876944ddac5f0a583b5b11 Mon Sep 17 00:00:00 2001 From: Marcus Perlick <38723273+marcusperlick@users.noreply.github.com> Date: Mon, 10 Mar 2025 09:24:49 +0100 Subject: [PATCH 62/64] Fix potential leak of HTTP response body in downloadJSON of csaf_aggregator (#618) --- cmd/csaf_aggregator/client.go | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/cmd/csaf_aggregator/client.go b/cmd/csaf_aggregator/client.go index 916baa5b..abd475cf 100644 --- a/cmd/csaf_aggregator/client.go +++ b/cmd/csaf_aggregator/client.go @@ -10,6 +10,7 @@ package main import ( "errors" + "fmt" "io" "net/http" @@ -20,13 +21,14 @@ var errNotFound = errors.New("not found") func downloadJSON(c util.Client, url string, found func(io.Reader) error) error { res, err := c.Get(url) - if err != nil || res.StatusCode != http.StatusOK || + if err != nil { + return fmt.Errorf("not found: %w", err) + } + defer res.Body.Close() + if res.StatusCode != http.StatusOK || res.Header.Get("Content-Type") != "application/json" { // ignore this as it is expected. return errNotFound } - return func() error { - defer res.Body.Close() - return found(res.Body) - }() + return found(res.Body) } From 1042e764643bb84299077704fce3353c34ef952a Mon Sep 17 00:00:00 2001 From: Marius Goetze Date: Tue, 11 Mar 2025 13:26:55 +0100 Subject: [PATCH 63/64] change back paths to non-internal --- cmd/csaf_aggregator/config.go | 4 +- cmd/csaf_aggregator/main.go | 2 +- cmd/csaf_checker/config.go | 4 +- cmd/csaf_checker/main.go | 2 +- cmd/csaf_checker/report.go | 2 +- cmd/csaf_downloader/config.go | 4 +- cmd/csaf_downloader/downloader_test.go | 2 +- cmd/csaf_downloader/forwarder_test.go | 2 +- cmd/csaf_downloader/main/main.go | 2 +- cmd/csaf_uploader/config.go | 2 +- cmd/csaf_uploader/main.go | 2 +- go.mod | 18 --------- go.sum | 51 -------------------------- 13 files changed, 14 insertions(+), 83 deletions(-) diff --git a/cmd/csaf_aggregator/config.go b/cmd/csaf_aggregator/config.go index 3c2c46b5..0a5c34f2 100644 --- a/cmd/csaf_aggregator/config.go +++ b/cmd/csaf_aggregator/config.go @@ -23,8 +23,8 @@ import ( "github.com/gocsaf/csaf/v3/csaf" "github.com/gocsaf/csaf/v3/internal/certs" "github.com/gocsaf/csaf/v3/internal/filter" - "github.com/gocsaf/csaf/v3/internal/models" - "github.com/gocsaf/csaf/v3/internal/options" + "github.com/gocsaf/csaf/v3/pkg/models" + "github.com/gocsaf/csaf/v3/pkg/options" "github.com/gocsaf/csaf/v3/util" "golang.org/x/time/rate" ) diff --git a/cmd/csaf_aggregator/main.go b/cmd/csaf_aggregator/main.go index 2056e84d..d83eca35 100644 --- a/cmd/csaf_aggregator/main.go +++ b/cmd/csaf_aggregator/main.go @@ -15,7 +15,7 @@ import ( "os" "path/filepath" - "github.com/gocsaf/csaf/v3/internal/options" + "github.com/gocsaf/csaf/v3/pkg/options" "github.com/gofrs/flock" ) diff --git a/cmd/csaf_checker/config.go b/cmd/csaf_checker/config.go index 3ea18403..3bc3e668 100644 --- a/cmd/csaf_checker/config.go +++ b/cmd/csaf_checker/config.go @@ -15,8 +15,8 @@ import ( "github.com/gocsaf/csaf/v3/internal/certs" "github.com/gocsaf/csaf/v3/internal/filter" - "github.com/gocsaf/csaf/v3/internal/models" - "github.com/gocsaf/csaf/v3/internal/options" + "github.com/gocsaf/csaf/v3/pkg/models" + "github.com/gocsaf/csaf/v3/pkg/options" ) type outputFormat string diff --git a/cmd/csaf_checker/main.go b/cmd/csaf_checker/main.go index 4efb3516..fd509b28 100644 --- a/cmd/csaf_checker/main.go +++ b/cmd/csaf_checker/main.go @@ -12,7 +12,7 @@ package main import ( "log" - "github.com/gocsaf/csaf/v3/internal/options" + "github.com/gocsaf/csaf/v3/pkg/options" ) // run uses a processor to check all the given domains or direct urls diff --git a/cmd/csaf_checker/report.go b/cmd/csaf_checker/report.go index 58ed25a5..cee01deb 100644 --- a/cmd/csaf_checker/report.go +++ b/cmd/csaf_checker/report.go @@ -19,7 +19,7 @@ import ( "time" "github.com/gocsaf/csaf/v3/csaf" - "github.com/gocsaf/csaf/v3/internal/models" + "github.com/gocsaf/csaf/v3/pkg/models" ) // MessageType is the kind of the message. diff --git a/cmd/csaf_downloader/config.go b/cmd/csaf_downloader/config.go index fdddea61..d61c5de5 100644 --- a/cmd/csaf_downloader/config.go +++ b/cmd/csaf_downloader/config.go @@ -21,8 +21,8 @@ import ( "github.com/gocsaf/csaf/v3/internal/certs" "github.com/gocsaf/csaf/v3/internal/filter" - "github.com/gocsaf/csaf/v3/internal/models" - "github.com/gocsaf/csaf/v3/internal/options" + "github.com/gocsaf/csaf/v3/pkg/models" + "github.com/gocsaf/csaf/v3/pkg/options" ) const ( diff --git a/cmd/csaf_downloader/downloader_test.go b/cmd/csaf_downloader/downloader_test.go index d7eaae34..052dd21c 100644 --- a/cmd/csaf_downloader/downloader_test.go +++ b/cmd/csaf_downloader/downloader_test.go @@ -16,8 +16,8 @@ import ( "os" "testing" - "github.com/gocsaf/csaf/v3/internal/options" "github.com/gocsaf/csaf/v3/internal/testutil" + "github.com/gocsaf/csaf/v3/pkg/options" "github.com/gocsaf/csaf/v3/util" ) diff --git a/cmd/csaf_downloader/forwarder_test.go b/cmd/csaf_downloader/forwarder_test.go index b9020c63..175eccaf 100644 --- a/cmd/csaf_downloader/forwarder_test.go +++ b/cmd/csaf_downloader/forwarder_test.go @@ -23,7 +23,7 @@ import ( "strings" "testing" - "github.com/gocsaf/csaf/v3/internal/options" + "github.com/gocsaf/csaf/v3/pkg/options" "github.com/gocsaf/csaf/v3/util" ) diff --git a/cmd/csaf_downloader/main/main.go b/cmd/csaf_downloader/main/main.go index 9d00838a..d6c0d4d6 100644 --- a/cmd/csaf_downloader/main/main.go +++ b/cmd/csaf_downloader/main/main.go @@ -16,7 +16,7 @@ import ( "os/signal" "github.com/gocsaf/csaf/v3/cmd/csaf_downloader" - "github.com/gocsaf/csaf/v3/internal/options" + "github.com/gocsaf/csaf/v3/pkg/options" ) func run(cfg *csaf_downloader.Config, domains []string) error { diff --git a/cmd/csaf_uploader/config.go b/cmd/csaf_uploader/config.go index ceecff78..85d47495 100644 --- a/cmd/csaf_uploader/config.go +++ b/cmd/csaf_uploader/config.go @@ -19,7 +19,7 @@ import ( "golang.org/x/term" "github.com/gocsaf/csaf/v3/internal/certs" - "github.com/gocsaf/csaf/v3/internal/options" + "github.com/gocsaf/csaf/v3/pkg/options" ) const ( diff --git a/cmd/csaf_uploader/main.go b/cmd/csaf_uploader/main.go index db1cef40..f05ebc49 100644 --- a/cmd/csaf_uploader/main.go +++ b/cmd/csaf_uploader/main.go @@ -9,7 +9,7 @@ // Implements a command line tool that uploads csaf documents to csaf_provider. package main -import "github.com/gocsaf/csaf/v3/internal/options" +import "github.com/gocsaf/csaf/v3/pkg/options" func main() { args, cfg, err := parseArgsConfig() diff --git a/go.mod b/go.mod index f7d6cb01..137fa846 100644 --- a/go.mod +++ b/go.mod @@ -8,8 +8,6 @@ require ( github.com/Intevation/jsonpath v0.2.1 github.com/ProtonMail/gopenpgp/v2 v2.8.3 github.com/PuerkitoBio/goquery v1.10.2 - github.com/ProtonMail/gopenpgp/v2 v2.8.0 - github.com/PuerkitoBio/goquery v1.8.1 github.com/gofrs/flock v0.12.1 github.com/jessevdk/go-flags v1.6.1 github.com/mitchellh/go-homedir v1.1.0 @@ -36,20 +34,4 @@ require ( golang.org/x/sys v0.31.0 // indirect golang.org/x/text v0.23.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - go.etcd.io/bbolt v1.3.11 - golang.org/x/crypto v0.29.0 - golang.org/x/term v0.26.0 - golang.org/x/time v0.8.0 -) - -require ( - github.com/ProtonMail/go-crypto v1.1.2 // indirect - github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f // indirect - github.com/andybalholm/cascadia v1.3.2 // indirect - github.com/cloudflare/circl v1.5.0 // indirect - github.com/pkg/errors v0.9.1 // indirect - github.com/shopspring/decimal v1.4.0 // indirect - golang.org/x/net v0.31.0 // indirect - golang.org/x/sys v0.27.0 // indirect - golang.org/x/text v0.20.0 // indirect ) diff --git a/go.sum b/go.sum index d2cca579..0098cb1f 100644 --- a/go.sum +++ b/go.sum @@ -17,19 +17,6 @@ github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmg github.com/cloudflare/circl v1.6.0 h1:cr5JKic4HI+LkINy2lg3W2jF8sHCVTBncJr5gIIq7qk= github.com/cloudflare/circl v1.6.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/ProtonMail/go-crypto v1.1.2 h1:A7JbD57ThNqh7XjmHE+PXpQ3Dqt3BrSAC0AL0Go3KS0= -github.com/ProtonMail/go-crypto v1.1.2/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE= -github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f h1:tCbYj7/299ekTTXpdwKYF8eBlsYsDVoggDAuAjoK66k= -github.com/ProtonMail/go-mime v0.0.0-20230322103455-7d82a3887f2f/go.mod h1:gcr0kNtGBqin9zDW9GOHcVntrwnjrK+qdJ06mWYBybw= -github.com/ProtonMail/gopenpgp/v2 v2.8.0 h1:WvMv3CMcFsqKSM4/Qf8sf3tgyQkzDqQmoSE49bnBuP4= -github.com/ProtonMail/gopenpgp/v2 v2.8.0/go.mod h1:qb2GUSnmA9ipBW5GVtCtEhkummSlqs2A8Ar3S0HBgSY= -github.com/PuerkitoBio/goquery v1.8.1 h1:uQxhNlArOIdbrH1tr0UXwdVFgDcZDrZVdcpygAcwmWM= -github.com/PuerkitoBio/goquery v1.8.1/go.mod h1:Q8ICL1kNUJ2sXGoAhPGUdYDJvgQgHzJsnnd3H7Ho5jQ= -github.com/andybalholm/cascadia v1.3.1/go.mod h1:R4bJ1UQfqADjvDa4P6HZHLh/3OxWWEqc0Sk8XGwHqvA= -github.com/andybalholm/cascadia v1.3.2 h1:3Xi6Dw5lHF15JtdcmAHD3i1+T8plmv7BQ/nsViSLyss= -github.com/andybalholm/cascadia v1.3.2/go.mod h1:7gtRlve5FxPPgIgX36uWBX58OdBsSS6lUvCFb+h7KvU= -github.com/cloudflare/circl v1.5.0 h1:hxIWksrX6XN5a1L2TI/h53AGPhNHoUBo+TD1ms9+pys= -github.com/cloudflare/circl v1.5.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E= @@ -43,10 +30,6 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/jessevdk/go-flags v1.6.1 h1:Cvu5U8UGrLay1rZfv/zP7iLpSHGUZ/Ou68T0iX1bBK4= -github.com/jessevdk/go-flags v1.6.1/go.mod h1:Mk8T1hIAWpOiJiHa9rJASDK2UGWji0EuPGBnNLMooyc= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -73,15 +56,6 @@ golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -go.etcd.io/bbolt v1.3.11 h1:yGEzV1wPz2yVCLsD8ZAiGHhHVlczyC9d1rP43/VCRJ0= -go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ= -golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= @@ -109,18 +83,6 @@ golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= -golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo= -golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ= -golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -143,15 +105,6 @@ golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= -golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s= -golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= -golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU= -golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= @@ -166,10 +119,6 @@ golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= -golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= -golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= -golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg= -golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= From 180ee4a6fd5594b7529150a5269ac2138a6c6c87 Mon Sep 17 00:00:00 2001 From: Marius Goetze Date: Tue, 11 Mar 2025 13:38:07 +0100 Subject: [PATCH 64/64] fix wrongly resolved merge conflicts --- cmd/csaf_downloader/config.go | 2 +- cmd/csaf_downloader/downloader.go | 10 ++-------- cmd/csaf_downloader/downloader_test.go | 12 ++++++------ 3 files changed, 9 insertions(+), 15 deletions(-) diff --git a/cmd/csaf_downloader/config.go b/cmd/csaf_downloader/config.go index d61c5de5..ce371a2d 100644 --- a/cmd/csaf_downloader/config.go +++ b/cmd/csaf_downloader/config.go @@ -48,7 +48,7 @@ const ( algSha512 = hashAlgorithm("sha512") ) -type config struct { +type Config struct { Directory string `short:"d" long:"directory" description:"DIRectory to store the downloaded files in" value-name:"DIR" toml:"directory"` Insecure bool `long:"insecure" description:"Do not check TLS certificates from provider" toml:"insecure"` IgnoreSignatureCheck bool `long:"ignore_sigcheck" description:"Ignore signature check results, just warn on mismatch" toml:"ignore_sigcheck"` diff --git a/cmd/csaf_downloader/downloader.go b/cmd/csaf_downloader/downloader.go index fb5f983e..8cee5cec 100644 --- a/cmd/csaf_downloader/downloader.go +++ b/cmd/csaf_downloader/downloader.go @@ -39,12 +39,6 @@ import ( "github.com/gocsaf/csaf/v3/util" ) -type Downloader struct { - cfg *Config - "github.com/gocsaf/csaf/v3/csaf" - "github.com/gocsaf/csaf/v3/util" -) - type hashFetchInfo struct { url string preferred bool @@ -52,8 +46,8 @@ type hashFetchInfo struct { hashType hashAlgorithm } -type downloader struct { - cfg *config +type Downloader struct { + cfg *Config client *util.Client // Used for testing keys *crypto.KeyRing validator csaf.RemoteValidator diff --git a/cmd/csaf_downloader/downloader_test.go b/cmd/csaf_downloader/downloader_test.go index 052dd21c..c343257f 100644 --- a/cmd/csaf_downloader/downloader_test.go +++ b/cmd/csaf_downloader/downloader_test.go @@ -6,7 +6,7 @@ // SPDX-FileCopyrightText: 2023 German Federal Office for Information Security (BSI) // Software-Engineering: 2023 Intevation GmbH -package main +package csaf_downloader import ( "context" @@ -128,23 +128,23 @@ func TestShaMarking(t *testing.T) { client := util.Client(hClient) tempDir := t.TempDir() - cfg := config{LogLevel: &options.LogLevel{Level: slog.LevelDebug}, Directory: tempDir, PreferredHash: test.preferredHash} - err := cfg.prepare() + cfg := Config{LogLevel: &options.LogLevel{Level: slog.LevelDebug}, Directory: tempDir, PreferredHash: test.preferredHash} + err := cfg.Prepare() if err != nil { t.Fatalf("SHA marking config failed: %v", err) } - d, err := newDownloader(&cfg) + d, err := NewDownloader(&cfg) if err != nil { t.Fatalf("could not init downloader: %v", err) } d.client = &client ctx := context.Background() - err = d.run(ctx, []string{serverURL + "/provider-metadata.json"}) + err = d.Run(ctx, []string{serverURL + "/provider-metadata.json"}) if err != nil { t.Errorf("SHA marking %v: Expected no error, got: %v", test.name, err) } - d.close() + d.Close() // Check for downloaded hashes sha256Exists := checkIfFileExists(tempDir+"/white/2020/avendor-advisory-0004.json.sha256", t)