Fix CI crash in screenshot generator when latest-release fetch fails
What changed, and why it matters
This is a test-only fix for a CI screenshot generator. It stops the test suite from crashing when GitHub's public API rate-limits an unauthenticated version-lookup request. There is no security vulnerability in the actual SeedSigner wallet or user-facing code; the change only adds a fallback placeholder so screenshots keep rendering during automated tests.
No security action required. As a hardening suggestion, consider caching the latest release tag between CI runs or using an authenticated GitHub token to reduce rate-limit failures, but this is a reliability improvement rather than a security fix.
Security signals we found
Unauthenticated third-party API call (GitHub) at import time
Rate-limit-induced None values caused TypeError in test rendering
Fix is defensive fallback only; no input validation or cryptographic change
Code is inside tests/ and not part of the production wallet runtime
Evidence from the diff
The commit modifies tests/screenshot_generator/generator.py. At module import time the generator calls VersionUtils._fetch_latest_seedsigner_release_tag(), an unauthenticated api.github.com request. On shared CI runners this frequently returns (None, None) due to the 60 req/hr/IP rate limit. Previously the code printed a message and left the Nones in place, which later crashed the Version mock when rendering OpeningSplash and the Version settings screen (None subscripting / len(None)). The patch substitutes a placeholder version string and epoch datetime when either value is None, so the ‘release build’ screenshots render. The real fetch path is unchanged when it succeeds.
Changed components
tests/screenshot_generator/generator.pyInspect captured patch +10 / −2
### tests/screenshot_generator/generator.py
@@ -7,6 +7,7 @@
import time
from contextlib import contextmanager
from dataclasses import dataclass
+from datetime import datetime
from PIL import ImageFont
from unittest.mock import Mock, patch, MagicMock
@@ -116,10 +117,17 @@ def add_op_return_to_psbt(psbt: PSBT, raw_payload_data: bytes):
MULTISIG_WALLET_DESCRIPTOR = """wsh(sortedmulti(1,[22bde1a9/48h/1h/0h/2h]tpubDFfsBrmpj226ZYiRszYi2qK6iGvh2vkkghfGB2YiRUVY4rqqedHCFEgw12FwDkm7rUoVtq9wLTKc6BN2sxswvQeQgp7m8st4FP8WtP8go76/{0,1}/*,[73c5da0a/48h/1h/0h/2h]tpubDFH9dgzveyD8zTbPUFuLrGmCydNvxehyNdUXKJAQN8x4aZ4j6UZqGfnqFrD4NqyaTVGKbvEW54tsvPTK2UoSbCC1PJY8iCNiwTL3RWZEheQ/{0,1}/*))#3jhtf6yx"""
-# Grab the most recent release version info
+# Grab the most recent release version info for the "release build" splash screenshots.
(latest_release_version_name, latest_release_version_timestamp) = VersionUtils._fetch_latest_seedsigner_release_tag()
if not latest_release_version_name or not latest_release_version_timestamp:
- print("Could not fetch latest release version from GitHub")
+ # The fetch can fail (offline, or the unauthenticated GitHub API rate-limit that
+ # shared CI runners regularly hit). Substitute a placeholder so the "release build"
+ # screenshots still render, rather than feeding None into the Version mock (which
+ # would otherwise crash both the splash screen and the Version settings screen on
+ # None slicing / len()).
+ print("Could not fetch latest release version from GitHub; using a placeholder")
+ latest_release_version_name = latest_release_version_name or "version_not_available"
+ latest_release_version_timestamp = latest_release_version_timestamp or datetime(1970, 1, 1)
# Wrap QRDisplayScreen's `render_brightness_tip` in a simple View + Screen so weWhy this scored 17/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.