@@ -28,10 +28,20 @@ const (
2828)
2929
3030type celFilterAnalyzer struct {
31- program cel.Program
31+ program cel.Program
32+ failOnMatch bool
33+
34+ packages map [string ]* models.Package
35+
36+ stat struct {
37+ manifests int
38+ packages int
39+ matched int
40+ err int
41+ }
3242}
3343
34- func NewCelFilterAnalyzer (filter string ) (Analyzer , error ) {
44+ func NewCelFilterAnalyzer (filter string , failOnMatch bool ) (Analyzer , error ) {
3545 env , err := cel .NewEnv (
3646 cel .Variable (filterInputVarPkg , cel .DynType ),
3747 cel .Variable (filterInputVarVulns , cel .DynType ),
@@ -55,7 +65,10 @@ func NewCelFilterAnalyzer(filter string) (Analyzer, error) {
5565 return nil , err
5666 }
5767
58- return & celFilterAnalyzer {program : prog }, nil
68+ return & celFilterAnalyzer {program : prog ,
69+ failOnMatch : failOnMatch ,
70+ packages : make (map [string ]* models.Package ),
71+ }, nil
5972}
6073
6174func (f * celFilterAnalyzer ) Name () string {
@@ -65,32 +78,22 @@ func (f *celFilterAnalyzer) Name() string {
6578func (f * celFilterAnalyzer ) Analyze (manifest * models.PackageManifest ,
6679 handler AnalyzerEventHandler ) error {
6780
68- tbl := table .NewWriter ()
69- tbl .SetStyle (table .StyleLight )
70- tbl .SetOutputMirror (os .Stdout )
71- tbl .AppendHeader (table.Row {"Ecosystem" , "Package" , "Version" ,
72- "Latest" , "Source" })
73-
74- filterStat := struct {
75- total int
76- matched int
77- err int
78- }{}
79-
8081 logger .Infof ("CEL filtering manifest: %s" , manifest .Path )
82+ f .stat .manifests += 1
83+
8184 for _ , pkg := range manifest .Packages {
82- filterStat . total += 1
85+ f . stat . packages += 1
8386
8487 filterInput , err := f .buildFilterInput (pkg )
8588 if err != nil {
86- filterStat .err += 1
89+ f . stat .err += 1
8790 logger .Errorf ("Failed to convert package to filter input: %v" , err )
8891 continue
8992 }
9093
9194 serializedInput , err := f .serializeFilterInput (filterInput )
9295 if err != nil {
93- filterStat .err += 1
96+ f . stat .err += 1
9497 logger .Errorf ("Failed to serialize filter input: %v" , err )
9598 continue
9699 }
@@ -105,7 +108,7 @@ func (f *celFilterAnalyzer) Analyze(manifest *models.PackageManifest,
105108 })
106109
107110 if err != nil {
108- filterStat .err += 1
111+ f . stat .err += 1
109112 logger .Errorf ("Failed to evaluate CEL for %s:%v : %v" ,
110113 pkg .PackageDetails .Name ,
111114 pkg .PackageDetails .Version , err )
@@ -114,24 +117,59 @@ func (f *celFilterAnalyzer) Analyze(manifest *models.PackageManifest,
114117
115118 if (reflect .TypeOf (out ).Kind () == reflect .Bool ) &&
116119 (reflect .ValueOf (out ).Bool ()) {
117- filterStat .matched += 1
118- tbl .AppendRow (table.Row {pkg .PackageDetails .Ecosystem ,
119- pkg .PackageDetails .Name ,
120- pkg .PackageDetails .Version ,
121- f .pkgLatestVersion (pkg ),
122- f .pkgSource (pkg ),
123- })
120+
121+ // Avoid duplicates added to the table
122+ if _ , ok := f .packages [pkg .Id ()]; ok {
123+ continue
124+ }
125+
126+ f .stat .matched += 1
127+ f .packages [pkg .Id ()] = pkg
124128 }
125129 }
126130
131+ return f .notifyCaller (manifest , handler )
132+ }
133+
134+ func (f * celFilterAnalyzer ) Finish () error {
135+ tbl := table .NewWriter ()
136+ tbl .SetStyle (table .StyleLight )
137+ tbl .SetOutputMirror (os .Stdout )
138+ tbl .AppendHeader (table.Row {"Ecosystem" , "Package" , "Version" ,
139+ "Source" })
140+
141+ for _ , pkg := range f .packages {
142+ tbl .AppendRow (table.Row {pkg .PackageDetails .Ecosystem ,
143+ pkg .PackageDetails .Name ,
144+ pkg .PackageDetails .Version ,
145+ f .pkgSource (pkg ),
146+ })
147+ }
148+
127149 fmt .Printf ("%s\n " , text .Bold .Sprint ("Filter evaluated with " ,
128- filterStat .matched , " out of " , filterStat .total , " matched and " ,
129- filterStat .err , " error(s)" ))
150+ f .stat .matched , " out of " , f .stat .packages , " uniquely matched and " ,
151+ f .stat .err , " error(s) " , "across " , f .stat .manifests ,
152+ " manifest(s)" ))
130153
131154 tbl .Render ()
132155 return nil
133156}
134157
158+ func (f * celFilterAnalyzer ) notifyCaller (manifest * models.PackageManifest ,
159+ handler AnalyzerEventHandler ) error {
160+ if f .failOnMatch && (f .stat .matched > 0 ) {
161+ handler (& AnalyzerEvent {
162+ Source : f .Name (),
163+ Type : ET_AnalyzerFailOnError ,
164+ Manifest : manifest ,
165+ Err : fmt .Errorf ("failed due to filter match on %s" ,
166+ manifest .Path ),
167+ })
168+ }
169+
170+ return nil
171+ }
172+
135173// TODO: Fix this JSON round-trip problem by directly configuring CEL env to
136174// work with Protobuf messages
137175func (f * celFilterAnalyzer ) serializeFilterInput (fi * filterinput.FilterInput ) (map [string ]interface {}, error ) {
0 commit comments