Skip to content

Commit a26ff7c

Browse files
committed
improvied table colour rendering for severities. Shows total as well now for single packages
1 parent 77279dd commit a26ff7c

File tree

3 files changed

+67
-27
lines changed

3 files changed

+67
-27
lines changed

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
88

99
## [Unreleased]
1010

11+
### Added
12+
13+
- Added repository-level vulnerability summary (`cloudsmith vulnerabilities OWNER/REPO`)
14+
- Aggregates scan results across all packages into a single color-coded table
15+
- Packages sorted by total vulnerability count (descending)
16+
- Packages without scan results are silently omitted
17+
- Supports `--severity` and `--fixable/--non-fixable` filters
18+
1119
## [1.16.0] - 2026-03-24
1220

1321
### Added

cloudsmith_cli/cli/commands/vulnerabilities.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ def _collect_repo_scan_data(opts, owner, repo, slugs, severity_filter, fixable):
195195
TextColumn("[progress.percentage]{task.percentage:>3.0f}%"),
196196
TextColumn("({task.completed}/{task.total})"),
197197
console=console,
198-
transient=True, # remove progress bar when done
198+
transient=True,
199199
) as progress:
200200
task = progress.add_task("Scanning packages...", total=len(slugs))
201201

@@ -216,7 +216,6 @@ def _collect_repo_scan_data(opts, owner, repo, slugs, severity_filter, fixable):
216216
progress.advance(task)
217217
continue
218218

219-
# Skip packages with no scan data
220219
if not data or not _has_scan_results(data):
221220
progress.advance(task)
222221
continue
@@ -232,7 +231,11 @@ def _collect_repo_scan_data(opts, owner, repo, slugs, severity_filter, fixable):
232231
label = f"{pkg_name}:{pkg_version}" if pkg_version else pkg_name
233232

234233
counts = _aggregate_severity_counts(data, severity_filter)
235-
rows.append((slug, label, counts))
234+
235+
# Skip packages where filters removed all vulnerabilities
236+
if sum(counts.values()) > 0:
237+
rows.append((slug, label, counts))
238+
236239
progress.advance(task)
237240

238241
# Sort by total vulnerability count descending

cloudsmith_cli/core/api/vulnerabilities.py

Lines changed: 53 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,39 @@
22

33
import click
44
import cloudsmith_api
5+
from rich.console import Console
6+
from rich.table import Table
57

68
from ...cli import utils
79
from .. import ratelimits
810
from .exceptions import catch_raise_api_exception
911
from .init import get_api_client
1012

13+
# Severity color mapping for consistent styling
14+
SEVERITY_COLORS = {
15+
"critical": "red",
16+
"high": "bright_red",
17+
"medium": "yellow",
18+
"low": "blue",
19+
"unknown": "dim white",
20+
}
21+
1122

1223
def get_vulnerabilities_api():
1324
"""Get the vulnerabilities API client."""
1425
return get_api_client(cloudsmith_api.VulnerabilitiesApi)
1526

1627

28+
def _colorize_count(count, severity_key):
29+
"""Return a rich-styled count string, colored only when count > 0."""
30+
if count > 0:
31+
color = SEVERITY_COLORS.get(severity_key, "white")
32+
return f"[{color}]{count}[/{color}]"
33+
return f"[dim]{count}[/dim]"
34+
35+
1736
def _print_vulnerabilities_summary_table(data, severity_filter, total_filtered_vulns):
18-
"""Print vulnerabilities as a table."""
37+
"""Print vulnerabilities as a color-coded table."""
1938

2039
severity_keys = {
2140
"Critical": "critical",
@@ -29,10 +48,6 @@ def _print_vulnerabilities_summary_table(data, severity_filter, total_filtered_v
2948
allowed = [s.strip().lower() for s in severity_filter.split(",")]
3049
severity_keys = {k: v for k, v in severity_keys.items() if v in allowed}
3150

32-
headers = [{"header": "Package", "justify": "left", "style": "cyan"}]
33-
for key in severity_keys.keys():
34-
headers.append({"header": key, "justify": "center", "style": "white"})
35-
3651
# Get package name and version for the target label
3752
pkg_data = getattr(data, "package", None)
3853
pkg_name = getattr(pkg_data, "name", "Unknown")
@@ -53,29 +68,43 @@ def _print_vulnerabilities_summary_table(data, severity_filter, total_filtered_v
5368
elif "unknown" in counts:
5469
counts["unknown"] += 1
5570

56-
# Create the single summary row
57-
row = [target_label]
58-
for _header, key in severity_keys.items():
59-
row.append(str(counts[key]))
60-
61-
rows = [row]
62-
63-
click.echo()
64-
click.echo()
65-
66-
utils.rich_print_table(headers=headers, rows=rows, title="Vulnerabilities Summary")
71+
# Build the rich table
72+
console = Console()
73+
table = Table(
74+
title="Vulnerabilities Summary",
75+
show_header=True,
76+
header_style="bold",
77+
show_lines=True,
78+
border_style="bright_black",
79+
padding=(0, 1),
80+
)
81+
82+
table.add_column("Package", justify="left", style="cyan", no_wrap=True)
83+
for display_name, sev_key in severity_keys.items():
84+
color = SEVERITY_COLORS.get(sev_key, "white")
85+
table.add_column(display_name, justify="center", header_style=f"bold {color}")
86+
table.add_column("Total", justify="center", header_style="bold white")
87+
88+
# Build the row
89+
row_total = 0
90+
cells = [target_label]
91+
for _display, sev_key in severity_keys.items():
92+
count = counts.get(sev_key, 0)
93+
cells.append(_colorize_count(count, sev_key))
94+
row_total += count
95+
total_style = "[bold red]" if row_total > 0 else "[dim]"
96+
cells.append(f"{total_style}{row_total}[/]")
97+
table.add_row(*cells)
98+
99+
console.print()
100+
console.print(table)
67101

68102
if severity_filter:
69-
filters = severity_filter.upper()
70-
click.echo(
71-
f"\nTotal Vulnerabilities: {getattr(data, 'num_vulnerabilities', 0)}"
103+
console.print(
104+
f"\nFiltered Vulnerabilities: [bold]{total_filtered_vulns}[/bold]\n"
72105
)
73-
click.echo(f"\nTotal {filters} Vulnerabilities: {total_filtered_vulns}")
74106
else:
75-
click.echo(
76-
f"\nTotal Vulnerabilities: {getattr(data, 'num_vulnerabilities', 0)}"
77-
)
78-
click.echo()
107+
console.print(f"\nTotal Vulnerabilities: [bold]{row_total}[/bold]\n")
79108

80109

81110
def _print_vulnerabilities_assessment_table(data, severity_filter=None):

0 commit comments

Comments
 (0)