@@ -55,8 +55,55 @@ func ListVulnerabilities(opts ...ListVulnerabilityOptions) (*securityhub.ListVul
5555 if err != nil {
5656 return nil , err
5757 }
58-
5958 listFlags .WithTag = true
59+
60+ if listFlags .Fixable || listFlags .Exclude != "" {
61+ excludeMap := parseVulnerabilityExcludeMap (listFlags .Exclude )
62+ var res []* models.VulnerabilityItem
63+ start := int ((listFlags .Page - 1 ) * listFlags .PageSize )
64+ end := int (listFlags .Page * listFlags .PageSize )
65+ count := 0
66+ for page := int64 (1 ); count < end ; page ++ {
67+ response , err := client .Securityhub .ListVulnerabilities (ctx , & securityhub.ListVulnerabilitiesParams {
68+ Page : & page ,
69+ PageSize : & listFlags .PageSize ,
70+ Q : & q ,
71+ WithTag : & listFlags .WithTag ,
72+ })
73+ if err != nil {
74+ return nil , err
75+ }
76+ if len (response .Payload ) == 0 {
77+ break
78+ }
79+
80+ response .Payload = filterVulnerabilities (
81+ response .Payload ,
82+ listFlags .Fixable ,
83+ listFlags .Exclude != "" ,
84+ excludeMap ,
85+ )
86+
87+ for _ , vul := range response .Payload {
88+ if count >= start && count < end {
89+ res = append (res , vul )
90+ }
91+ count ++
92+ if count >= end {
93+ break
94+ }
95+ }
96+ }
97+ if len (res ) == 0 {
98+ return & securityhub.ListVulnerabilitiesOK {
99+ Payload : nil ,
100+ }, nil
101+ }
102+ return & securityhub.ListVulnerabilitiesOK {
103+ Payload : res ,
104+ }, nil
105+ }
106+
60107 response , err := client .Securityhub .ListVulnerabilities (ctx , & securityhub.ListVulnerabilitiesParams {
61108 Page : & listFlags .Page ,
62109 PageSize : & listFlags .PageSize ,
@@ -66,40 +113,10 @@ func ListVulnerabilities(opts ...ListVulnerabilityOptions) (*securityhub.ListVul
66113 if err != nil {
67114 return nil , err
68115 }
69-
70- if listFlags .Fixable {
71- response .Payload = slices .DeleteFunc (response .Payload , func (vul * models.VulnerabilityItem ) bool {
72- return vul .FixedVersion == ""
73- })
74- }
75-
76- if listFlags .Exclude != "" {
77- excludeMap := make (map [string ]string )
78- for _ , query := range strings .Split (listFlags .Exclude , "," ) {
79- parts := strings .SplitN (strings .TrimSpace (query ), "=" , 2 )
80- if len (parts ) == 2 {
81- excludeMap [strings .TrimSpace (parts [0 ])] = strings .TrimSpace (parts [1 ])
82- }
83- }
84-
85- response .Payload = slices .DeleteFunc (response .Payload , func (vul * models.VulnerabilityItem ) bool {
86- if val , ok := excludeMap ["cve_id" ]; ok && vul .CVEID == val {
87- return true
88- }
89- if val , ok := excludeMap ["severity" ]; ok && strings .EqualFold (vul .Severity , val ) {
90- return true
91- }
92- if val , ok := excludeMap ["package" ]; ok && vul .Package == val {
93- return true
94- }
95- if val , ok := excludeMap ["repository_name" ]; ok && vul .RepositoryName == val {
96- return true
97- }
98- if val , ok := excludeMap ["digest" ]; ok && vul .Digest == val {
99- return true
100- }
101- return false
102- })
116+ if len (response .Payload ) == 0 {
117+ response .Payload = nil
118+ response .XTotalCount = 0
119+ return response , nil
103120 }
104121
105122 return response , nil
@@ -111,9 +128,31 @@ func buildVulnerabilityQuery(opts ListVulnerabilityOptions) (string, error) {
111128 queries = append (queries , fmt .Sprintf ("cve_id=%s" , opts .CVEID ))
112129 }
113130 if opts .CVSSScore != "" {
114- queries = append (queries , fmt .Sprintf ("cvss_score_v3=%s" , opts .CVSSScore ))
131+ cvssRange := opts .CVSSScore
132+ if strings .Contains (cvssRange , "~" ) {
133+ parts := strings .Split (cvssRange , "~" )
134+ if len (parts ) == 2 {
135+ cvssRange = fmt .Sprintf ("[%s~%s]" , parts [0 ], parts [1 ])
136+ } else {
137+ return "" , fmt .Errorf ("invalid cvss score range: %s. Expected format: min~max" , cvssRange )
138+ }
139+ } else {
140+ cvssRange = fmt .Sprintf ("[%s~%s]" , opts .CVSSScore , opts .CVSSScore )
141+ }
142+ queries = append (queries , fmt .Sprintf ("cvss_score_v3=%s" , cvssRange ))
115143 }
116144 if opts .Severity != "" {
145+ allowedSeverities := map [string ]bool {
146+ "Critical" : true ,
147+ "High" : true ,
148+ "Medium" : true ,
149+ "Low" : true ,
150+ "n/a" : true ,
151+ "None" : true ,
152+ }
153+ if ! allowedSeverities [opts .Severity ] {
154+ return "" , fmt .Errorf ("invalid severity value: %s. Allowed values are: Low, Medium, High, Critical, n/a, None" , opts .Severity )
155+ }
117156 queries = append (queries , fmt .Sprintf ("severity=%s" , opts .Severity ))
118157 }
119158 if opts .Repository != "" {
@@ -136,8 +175,55 @@ func buildVulnerabilityQuery(opts ListVulnerabilityOptions) (string, error) {
136175 queries = append (queries , fmt .Sprintf ("digest=%s" , opts .Digest ))
137176 }
138177 if opts .Q != "" {
178+ // FIXME: Q parameter needs to be converted to standard query format
179+ // This will be addressed in draft PR #731
139180 queries = append (queries , opts .Q )
140181 }
141182
142183 return strings .Join (queries , "," ), nil
143184}
185+
186+ func parseVulnerabilityExcludeMap (exclude string ) map [string ]string {
187+ excludeMap := make (map [string ]string )
188+ for _ , query := range strings .Split (exclude , "," ) {
189+ parts := strings .SplitN (strings .TrimSpace (query ), "=" , 2 )
190+ if len (parts ) == 2 {
191+ excludeMap [strings .TrimSpace (parts [0 ])] = strings .TrimSpace (parts [1 ])
192+ }
193+ }
194+ return excludeMap
195+ }
196+
197+ func filterVulnerabilities (
198+ payload []* models.VulnerabilityItem ,
199+ fixable bool ,
200+ excludeProvided bool ,
201+ excludeMap map [string ]string ,
202+ ) []* models.VulnerabilityItem {
203+ return slices .DeleteFunc (payload , func (vul * models.VulnerabilityItem ) bool {
204+ if fixable && vul .FixedVersion == "" {
205+ return true
206+ }
207+
208+ if ! excludeProvided {
209+ return false
210+ }
211+
212+ if val , ok := excludeMap ["cve_id" ]; ok && vul .CVEID == val {
213+ return true
214+ }
215+ if val , ok := excludeMap ["severity" ]; ok && vul .Severity == val {
216+ return true
217+ }
218+ if val , ok := excludeMap ["package" ]; ok && vul .Package == val {
219+ return true
220+ }
221+ if val , ok := excludeMap ["repository_name" ]; ok && vul .RepositoryName == val {
222+ return true
223+ }
224+ if val , ok := excludeMap ["digest" ]; ok && vul .Digest == val {
225+ return true
226+ }
227+ return false
228+ })
229+ }
0 commit comments