From 20a08750465383e090338dd4df2644cb3a9f2889 Mon Sep 17 00:00:00 2001 From: Jeff Ortel Date: Wed, 11 Oct 2023 09:22:05 -0500 Subject: [PATCH] :bug: Fix HTML analysis report. (#513) The HTML report (tarball) is composed/streamed by the hub in steps: 1. Add the static report directory (template). 2. Add the composed `output.js` file which contains the analysis data. Using this method, the ReportWriter was adding the `output.js` (entry) twice in the tar. First from the static report (template) and the second generated by the hub. The tar command line seems to deal with this by overwriting the file. However the _extract_ functionality used by browsers ignores the 2nd occurrence. The fix is to filter out the 1st output.js when using tar.Writer.AddDir(). The `Filter` in the `tar` package only supported _include_ filtering. This PR updates the `tar.Filter` to support both _included_ and _excluded_ patterns. Refitted and tested the /bucket. --------- Signed-off-by: Jeff Ortel --- api/analysis.go | 4 ++ api/bucket.go | 6 +-- tar/filter.go | 97 ++++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 93 insertions(+), 14 deletions(-) diff --git a/api/analysis.go b/api/analysis.go index d04d7c6e0..459581996 100644 --- a/api/analysis.go +++ b/api/analysis.go @@ -2270,6 +2270,7 @@ func (r *ReportWriter) db() (db *gorm.DB) { // // Write builds and streams the analysis report. func (r *ReportWriter) Write(id uint) { + reportDir := Settings.Analysis.ReportPath path, err := r.buildOutput(id) if err != nil { _ = r.ctx.Error(err) @@ -2282,6 +2283,9 @@ func (r *ReportWriter) Write(id uint) { defer func() { tarWriter.Close() }() + filter := tar.NewFilter(reportDir) + filter.Exclude("output.js") + tarWriter.Filter = filter err = tarWriter.AssertDir(Settings.Analysis.ReportPath) if err != nil { _ = r.ctx.Error(err) diff --git a/api/bucket.go b/api/bucket.go index 2ca45908f..42d4c0934 100644 --- a/api/bucket.go +++ b/api/bucket.go @@ -231,10 +231,8 @@ func (h *BucketOwner) bucketGet(ctx *gin.Context, id uint) { return } if st.IsDir() { - filter := tar.Filter{ - Pattern: ctx.Query(Filter), - Root: path, - } + filter := tar.NewFilter(path) + filter.Include(ctx.Query(Filter)) if h.Accepted(ctx, binding.MIMEHTML) { h.getFile(ctx, m) } else { diff --git a/tar/filter.go b/tar/filter.go index 36b683fe3..63fdd5a1c 100644 --- a/tar/filter.go +++ b/tar/filter.go @@ -5,28 +5,105 @@ import ( "path/filepath" ) +// +// NewFilter returns a filter. +func NewFilter(root string) (f Filter) { + f = Filter{Root: root} + return +} + // // Filter supports glob-style filtering. type Filter struct { - Root string - Pattern string - cache map[string]bool + included FilterSet + excluded FilterSet + Root string } // // Match determines if path matches the filter. func (r *Filter) Match(path string) (b bool) { - if r.Pattern == "" { - b = true + r.included.root = r.Root + r.excluded.root = r.Root + if r.included.Len() > 0 { + included := r.included.Match(path) + if !included { + return + } + } + b = true + if r.excluded.Len() > 0 { + excluded := r.excluded.Match(path) + if excluded { + b = false + return + } + } + return +} + +// +// Include adds included patterns. +// Empty ("") patterns are ignored. +func (r *Filter) Include(patterns ...string) { + r.included.Add(patterns...) +} + +// +// Exclude adds excluded patterns. +// Empty ("") patterns are ignored. +func (r *Filter) Exclude(patterns ...string) { + r.excluded.Add(patterns...) +} + +// +// FilterSet is a collection of filter patterns. +type FilterSet struct { + root string + patterns []string + cache map[string]bool +} + +// +// Match returns true when the path matches. +func (r *FilterSet) Match(path string) (match bool) { + r.build() + _, match = r.cache[path] + return +} + +// +// Add pattern. +// Empty ("") patterns are ignored. +func (r *FilterSet) Add(patterns ...string) { + for _, p := range patterns { + if p == "" { + continue + } + r.cache = nil + r.patterns = append( + r.patterns, + p) + } +} + +// +// Len returns number of patterns. +func (r *FilterSet) Len() (n int) { + return len(r.patterns) +} + +// +// build populates the cache as needed. +func (r *FilterSet) build() { + if r.cache != nil { return } - if r.cache == nil { - r.cache = map[string]bool{} - matches, _ := filepath.Glob(pathlib.Join(r.Root, r.Pattern)) + r.cache = make(map[string]bool) + for i := range r.patterns { + matches, _ := filepath.Glob(pathlib.Join(r.root, r.patterns[i])) for _, p := range matches { r.cache[p] = true } } - _, b = r.cache[path] - return }