@@ -63,7 +63,10 @@ def get_packages_in_repo(opts, owner, repo):
6363 f"The repository may be empty, or the owner/repo names may be incorrect."
6464 )
6565
66- return [pkg ["slug_perm" ] for pkg in all_packages ]
66+ return [
67+ (pkg ["slug_perm" ], pkg .get ("name" , pkg ["slug_perm" ]), pkg .get ("version" , "" ))
68+ for pkg in all_packages
69+ ]
6770
6871
6972def _has_scan_results (data ):
@@ -189,18 +192,28 @@ def _print_repo_summary_table(package_rows, severity_filter=None):
189192 table .add_column ("Total" , justify = "center" , header_style = "bold white" )
190193
191194 grand_total = 0
195+ num_sev_cols = len (severity_keys )
192196
193- for label , counts in package_rows :
194- row_total = 0
197+ for label , counts , status in package_rows :
195198 cells = [label ]
196- for _display , sev_key in severity_keys .items ():
197- count = counts .get (sev_key , 0 )
198- cells .append (_colorize_count (count , sev_key ))
199- row_total += count
200- total_style = "[bold red]" if row_total > 0 else "[dim]"
201- cells .append (f"{ total_style } { row_total } [/]" )
199+ if status == "no_scan" :
200+ cells .append ("[dim italic]Security scan not supported[/dim italic]" )
201+ cells .extend (["" ] * (num_sev_cols - 1 ))
202+ cells .append ("" )
203+ elif status == "no_issues_found" :
204+ cells .append ("[bold green]No issues found[/bold green]" )
205+ cells .extend (["" ] * (num_sev_cols - 1 ))
206+ cells .append ("" )
207+ else :
208+ row_total = 0
209+ for _display , sev_key in severity_keys .items ():
210+ count = counts .get (sev_key , 0 )
211+ cells .append (_colorize_count (count , sev_key ))
212+ row_total += count
213+ total_style = "[bold red]" if row_total > 0 else "[dim]"
214+ cells .append (f"{ total_style } { row_total } [/]" )
215+ grand_total += row_total
202216 table .add_row (* cells )
203- grand_total += row_total
204217
205218 console .print ()
206219 console .print (table )
@@ -210,7 +223,9 @@ def _print_repo_summary_table(package_rows, severity_filter=None):
210223def _collect_repo_scan_data (opts , owner , repo , slugs , severity_filter , fixable ):
211224 """Silently collect scan data for all packages with a progress bar.
212225
213- Returns list of (slug, label, counts) tuples sorted by total desc.
226+ Returns list of (slug, label, counts, status) tuples where status is one of
227+ "vulnerable", "safe", or "no_scan". Sorted: vulnerable (by count desc),
228+ then safe, then no_scan.
214229 """
215230 rows = []
216231 console = Console (stderr = True )
@@ -226,8 +241,13 @@ def _collect_repo_scan_data(opts, owner, repo, slugs, severity_filter, fixable):
226241 ) as progress :
227242 task = progress .add_task ("Scanning packages..." , total = len (slugs ))
228243
229- for slug in slugs :
244+ for slug , pkg_name_fallback , pkg_version_fallback in slugs :
230245 progress .update (task , description = f"Processing { slug } ..." )
246+ fallback_label = (
247+ f"{ pkg_name_fallback } :{ pkg_version_fallback } "
248+ if pkg_version_fallback
249+ else pkg_name_fallback
250+ )
231251
232252 try :
233253 data = get_package_scan_result (
@@ -240,35 +260,49 @@ def _collect_repo_scan_data(opts, owner, repo, slugs, severity_filter, fixable):
240260 fixable = fixable ,
241261 )
242262 except Exception : # pylint: disable=broad-exception-caught
263+ rows .append ((slug , fallback_label , {}, "no_scan" ))
243264 progress .advance (task )
244265 continue
245266
267+ # Build label from scan response metadata, fall back to list_packages data
268+ pkg_data = getattr (data , "package" , None ) if data else None
269+ pkg_name = (
270+ getattr (pkg_data , "name" , pkg_name_fallback )
271+ if pkg_data
272+ else pkg_name_fallback
273+ )
274+ pkg_version = (
275+ getattr (pkg_data , "version" , pkg_version_fallback )
276+ if pkg_data
277+ else pkg_version_fallback
278+ )
279+ label = f"{ pkg_name } :{ pkg_version } " if pkg_version else pkg_name
280+
246281 if not data or not _has_scan_results (data ):
282+ rows .append ((slug , label , {}, "no_scan" ))
247283 progress .advance (task )
248284 continue
249285
250286 # Apply filters if active
251287 if severity_filter or fixable is not None :
252288 _apply_filters (data , severity_filter , fixable )
253289
254- # Build label from package metadata
255- pkg_data = getattr (data , "package" , None )
256- pkg_name = getattr (pkg_data , "name" , slug )
257- pkg_version = getattr (pkg_data , "version" , "" )
258- label = f"{ pkg_name } :{ pkg_version } " if pkg_version else pkg_name
259-
260290 counts = _aggregate_severity_counts (data , severity_filter )
261291
262- # Skip packages where filters removed all vulnerabilities
263292 if sum (counts .values ()) > 0 :
264- rows .append ((slug , label , counts ))
293+ rows .append ((slug , label , counts , "vulnerable" ))
294+ else :
295+ rows .append ((slug , label , counts , "no_issues_found" ))
265296
266297 progress .advance (task )
267298
268- # Sort by total vulnerability count descending
269- rows .sort (key = lambda row : sum (row [2 ].values ()), reverse = True )
299+ # Sort: vulnerable first (by total desc), then safe, then no_scan
300+ vulnerable = [r for r in rows if r [3 ] == "vulnerable" ]
301+ vulnerable .sort (key = lambda r : sum (r [2 ].values ()), reverse = True )
302+ safe = [r for r in rows if r [3 ] == "no_issues_found" ]
303+ no_scan = [r for r in rows if r [3 ] == "no_scan" ]
270304
271- return rows
305+ return vulnerable + safe + no_scan
272306
273307
274308@main .command ()
@@ -363,20 +397,12 @@ def vulnerabilities(
363397 )
364398
365399 if not repo_summary_rows :
366- if severity_filter or fixable is not None :
367- click .secho (
368- f"No vulnerabilities matched the applied filters "
369- f"for any packages in '{ owner } /{ repo } '." ,
370- fg = "yellow" ,
371- err = use_stderr ,
372- )
373- else :
374- click .secho (
375- f"No vulnerability scan results found for any packages "
376- f"in '{ owner } /{ repo } '." ,
377- fg = "yellow" ,
378- err = use_stderr ,
379- )
400+ click .secho (
401+ f"No scan data could be retrieved for any packages "
402+ f"in '{ owner } /{ repo } '." ,
403+ fg = "yellow" ,
404+ err = use_stderr ,
405+ )
380406 return
381407
382408 json_output = {
@@ -386,18 +412,19 @@ def vulnerabilities(
386412 {
387413 "slug_perm" : slug_perm ,
388414 "package" : label ,
415+ "status" : status ,
389416 "vulnerabilities" : counts ,
390417 }
391- for slug_perm , label , counts in repo_summary_rows
418+ for slug_perm , label , counts , status in repo_summary_rows
392419 ],
393420 }
394421
395422 if utils .maybe_print_as_json (opts , json_output ):
396423 return
397424
398- # Table only needs label and counts
425+ # Table only needs label, counts, and status
399426 _print_repo_summary_table (
400- [(label , counts ) for _ , label , counts in repo_summary_rows ],
427+ [(label , counts , status ) for _ , label , counts , status in repo_summary_rows ],
401428 severity_filter ,
402429 )
403430 return
0 commit comments