From 6ae677962680f7373be7c6812d592906f42b7d77 Mon Sep 17 00:00:00 2001 From: km222uq Date: Sun, 26 Oct 2025 16:03:50 +0100 Subject: [PATCH 1/3] more error logs --- src/gitfetch/cli.py | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/gitfetch/cli.py b/src/gitfetch/cli.py index 395bef8..2b51825 100644 --- a/src/gitfetch/cli.py +++ b/src/gitfetch/cli.py @@ -441,7 +441,19 @@ def main() -> int: return 0 except Exception as e: - print(f"Error: {e}", file=sys.stderr) + # When debugging, print full traceback to help diagnose issues + # (useful when users report errors from package builds / other + # environments where the short error message is not enough). + try: + import os + import traceback + if os.environ.get('GITFETCH_DEBUG'): + traceback.print_exc() + else: + print(f"Error: {e}", file=sys.stderr) + except Exception: + # Fallback to simple message if traceback printing fails + print(f"Error: {e}", file=sys.stderr) return 1 except KeyboardInterrupt: From 38cbab8e73fb5dfe747d5acfd6026b0d536f0b60 Mon Sep 17 00:00:00 2001 From: km222uq Date: Sun, 26 Oct 2025 16:04:11 +0100 Subject: [PATCH 2/3] Better contrains and error handling around dates and time --- src/gitfetch/display.py | 27 ++++++++++++++++++++++----- 1 file changed, 22 insertions(+), 5 deletions(-) diff --git a/src/gitfetch/display.py b/src/gitfetch/display.py index 9256a61..4eb48a6 100644 --- a/src/gitfetch/display.py +++ b/src/gitfetch/display.py @@ -951,10 +951,18 @@ def _build_month_line(self, weeks_data: list) -> str: try: date_obj = datetime.fromisoformat(first_day) - except ValueError: + # Validate year is in reasonable range to avoid C int overflow + if date_obj.year < 1900 or date_obj.year > 9999: + continue + except (ValueError, OverflowError): continue - month_abbr = date_obj.strftime('%b') + try: + month_abbr = date_obj.strftime('%b') + except (ValueError, OverflowError): + # strftime can fail with years outside 1900-9999 + continue + if month_abbr != last_month: month_chars.append(month_abbr) last_month = month_abbr @@ -986,8 +994,11 @@ def _build_month_line_spaced(self, weeks_data: list) -> str: try: date_obj = datetime.fromisoformat(first_day) + # Validate year is in reasonable range to avoid C int overflow + if date_obj.year < 1900 or date_obj.year > 9999: + continue current_month = date_obj.month - except ValueError: + except (ValueError, OverflowError): continue # Check if this is a new month @@ -1003,6 +1014,9 @@ def _build_month_line_spaced(self, weeks_data: list) -> str: prev_date_obj = datetime.fromisoformat( prev_first_day ) + # Validate year is in reasonable range + if prev_date_obj.year < 1900 or prev_date_obj.year > 9999: + continue prev_month = prev_date_obj.month if current_month != prev_month: # New month - add spacing and month name @@ -1016,7 +1030,7 @@ def _build_month_line_spaced(self, weeks_data: list) -> str: needed_space = max(1, calc) month_line += " " * needed_space month_line += month_name - except ValueError: + except (ValueError, OverflowError): pass return f" {month_line}" @@ -1298,8 +1312,11 @@ def _format_date(self, date_string: str) -> str: """ try: dt = datetime.fromisoformat(date_string.replace('Z', '+00:00')) + # Validate year is in reasonable range to avoid C int overflow + if dt.year < 1900 or dt.year > 9999: + return date_string return dt.strftime('%B %d, %Y') - except (ValueError, AttributeError): + except (ValueError, AttributeError, OverflowError): return date_string def _get_contribution_block(self, count: int) -> str: From 374eb7609b4972c928a44668f5f09a5bc73b5a0a Mon Sep 17 00:00:00 2001 From: km222uq Date: Sun, 26 Oct 2025 16:16:05 +0100 Subject: [PATCH 3/3] Guardrails around cache --- src/gitfetch/cache.py | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/gitfetch/cache.py b/src/gitfetch/cache.py index ba3bbfa..addd89e 100644 --- a/src/gitfetch/cache.py +++ b/src/gitfetch/cache.py @@ -255,7 +255,27 @@ def _is_cache_expired(self, cached_at: datetime) -> bool: Returns: True if expired, False otherwise """ - expiry_time = datetime.now() - timedelta(minutes=self.cache_expiry_minutes) + # Defensive handling: ensure cache_expiry_minutes is a reasonable int + # and avoid passing an extremely large integer to timedelta which can + # raise OverflowError on some platforms (assuming on 32-bit builds). + try: + minutes = int(self.cache_expiry_minutes) + except Exception: + minutes = 15 + + # Enforce sensible bounds: minimum 1 minute, cap to MAX_MINUTES + # (10 years expressed in minutes). This prevents OverflowError while + # still allowing very long cache durations when intentionally set. + MAX_MINUTES = 5256000 # 10 years + minutes = max(1, min(minutes, MAX_MINUTES)) + + try: + expiry_time = datetime.now() - timedelta(minutes=minutes) + except OverflowError: + # In the unlikely event timedelta still overflows, treat cache as + # non-expired (safe default) to avoid crashing the program. + return False + return cached_at < expiry_time def list_cached_accounts(self) -> list[tuple[str, datetime]]: