Skip to content

Commit 0743525

Browse files
authored
Merge pull request #24 from popey/check-grype-version
Add grype binary version checking
2 parents 64a228a + a5a8f46 commit 0743525

File tree

1 file changed

+76
-0
lines changed

1 file changed

+76
-0
lines changed

grummage.py

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import sys
66
import tempfile
77
import re
8+
import urllib.request
89

910
from textual import work
1011
from textual.app import App
@@ -24,6 +25,10 @@ def format_urls_as_markdown(text):
2425
url_pattern = r'https?://(?:[-\w.]|(?:%[\da-fA-F]{2}))+(?:/[^)\s]*)?'
2526
return re.sub(url_pattern, lambda m: f'[{m.group()}]({m.group()})', text)
2627

28+
def is_running_in_snap():
29+
"""Check if grummage is running inside a snap package."""
30+
return any(key.startswith("SNAP_") for key in os.environ)
31+
2732
def is_grype_installed():
2833
"""Check if the grype binary is available in the system's PATH."""
2934
return any(
@@ -32,6 +37,55 @@ def is_grype_installed():
3237
for path in os.environ["PATH"].split(os.pathsep)
3338
)
3439

40+
def get_grype_version():
41+
"""Get the installed grype version."""
42+
try:
43+
result = subprocess.run(
44+
["grype", "version"],
45+
capture_output=True,
46+
text=True
47+
)
48+
if result.returncode == 0:
49+
# Parse output like "Application: grype Version: 0.97.0"
50+
for line in result.stdout.splitlines():
51+
if "Version:" in line:
52+
version = line.split("Version:")[1].strip()
53+
# Remove 'v' prefix if present
54+
return version.lstrip('v')
55+
return None
56+
except Exception:
57+
return None
58+
59+
def get_latest_grype_version():
60+
"""Check the latest grype version from anchore toolbox."""
61+
try:
62+
req = urllib.request.Request(
63+
"https://toolbox-data.anchore.io/grype/releases/latest/VERSION",
64+
headers={"User-Agent": "grummage"}
65+
)
66+
with urllib.request.urlopen(req, timeout=5) as response:
67+
version = response.read().decode('utf-8').strip()
68+
# Remove 'v' prefix if present
69+
return version.lstrip('v')
70+
except Exception:
71+
return None
72+
73+
def compare_versions(current, latest):
74+
"""Compare two version strings. Returns True if latest is newer than current."""
75+
try:
76+
# Split versions into parts and compare
77+
current_parts = [int(x) for x in current.split('.')]
78+
latest_parts = [int(x) for x in latest.split('.')]
79+
80+
# Pad shorter version with zeros
81+
max_len = max(len(current_parts), len(latest_parts))
82+
current_parts.extend([0] * (max_len - len(current_parts)))
83+
latest_parts.extend([0] * (max_len - len(latest_parts)))
84+
85+
return latest_parts > current_parts
86+
except Exception:
87+
return False
88+
3589
def prompt_install_grype():
3690
"""Prompt the user to install grype if it's not installed."""
3791
response = input(
@@ -215,6 +269,28 @@ def update_loading_status(self, message):
215269
@work(thread=True, exclusive=True)
216270
def load_sbom_worker(self):
217271
"""Load SBOM and run grype analysis in worker thread."""
272+
# Check grype binary version (skip if running in snap)
273+
if not is_running_in_snap():
274+
self.app.call_from_thread(self.update_loading_status, "Checking grype version...")
275+
self.debug_log("Checking grype binary version")
276+
277+
current_version = get_grype_version()
278+
latest_version = get_latest_grype_version()
279+
280+
if current_version and latest_version:
281+
self.debug_log(f"Grype version: current={current_version}, latest={latest_version}")
282+
if compare_versions(current_version, latest_version):
283+
self.app.call_from_thread(
284+
self.notify,
285+
f"Grype update available: v{latest_version} (installed: v{current_version})",
286+
severity="warning"
287+
)
288+
self.debug_log(f"Grype update available: {current_version} -> {latest_version}")
289+
else:
290+
self.debug_log("Could not check grype version")
291+
else:
292+
self.debug_log("Running in snap, skipping grype version check")
293+
218294
# Check and update grype database if needed
219295
self.app.call_from_thread(self.update_loading_status, "Checking vulnerability database...")
220296
self.debug_log("Checking grype database status")

0 commit comments

Comments
 (0)