Skip to content

fix: validate Netlify update response before use#62

Open
LeGeRyChEeSe wants to merge 1 commit into
mainfrom
fix/validate-netlify-update-response
Open

fix: validate Netlify update response before use#62
LeGeRyChEeSe wants to merge 1 commit into
mainfrom
fix/validate-netlify-update-response

Conversation

@LeGeRyChEeSe

Copy link
Copy Markdown
Owner

Problème

Le contrôle de mise à jour a deux chemins : GitHub Releases (primaire) et Netlify (secours, uniquement sur 404 GitHub). Le chemin Netlify désérialise la réponse serveur directement en UpdateInfo (champs Kotlin non-nullables) via Retrofit/Gson.

Cause

Gson alloue les data classes Kotlin sans passer par leur constructeur (Unsafe.allocateInstance en l'absence de constructeur sans arguments accessible). Un JSON serveur incomplet/malformé peut donc laisser des champs déclarés non-nullables (version, downloadUrl, …) à null au runtime, malgré le type Kotlin. Les utiliser (.lowercase(), .startsWith(...)) déclenche une NPE différée. Cette NPE est rattrapée par le catch (Exception) générique existant (pas de crash), mais échoue silencieusement sans message pour l'utilisateur — contrairement aux autres branches d'erreur qui affichent un message explicite.

Le pattern défensif existait déjà pour checksum (updateInfo.checksum.isNullOrEmpty(), MainViewModel.kt ligne ~1961) mais pas pour version/downloadUrl.

Correctif

Ajout d'une validation explicite juste après réception de la réponse Netlify, avec isNullOrBlank() (et non .isBlank(), qui NPE aussi sur une valeur secrètement nulle) — même style que la validation checksum déjà en place. En cas de réponse invalide, message d'erreur explicite affiché à l'utilisateur au lieu d'un échec silencieux.

Vérification

  • Build : scripts\gradlew.bat :app:assembleDevDebug → succès.
  • Smoke-test émulateur (Pixel_7, com.vrhub.debug) : lancement à froid sans crash, logcat propre.
  • Pas de test JVM ajouté : checkNetlifyUpdatesWithFallback est une méthode privée profondément intégrée à MainViewModel (AndroidViewModel avec de nombreuses dépendances), sans infrastructure de test existante pour ce flux ; l'ajouter isolément n'aurait pas été raisonnable dans le cadre de ce correctif ciblé.

Réf : docs/sonnet5-specs/01-bug-audit-backlog.md#b4--désérialisation-réseau-gsonretrofit

checkNetlifyUpdatesWithFallback() deserializes UpdateInfo directly via
Retrofit/Gson. Gson allocates Kotlin data classes without calling their
constructor, so a malformed/incomplete server response can leave
non-null fields (version, downloadUrl) holding null at runtime despite
their declared type. Consuming them (.lowercase(), .startsWith()) would
throw a deferred NPE, silently swallowed by the generic catch-all with
no error shown to the user.

Validate with the null-safe isNullOrBlank() (not .isBlank(), which
would itself NPE on a secretly-null value) before use, same pattern
already applied to the checksum field just below.
@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown

Test Results

0 tests   0 ✅  0s ⏱️
0 suites  0 💤
0 files    0 ❌

Results for commit 63f1af2.

@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown

PR Validation Summary ✅

Status: All checks passed!
Core Duration: 4m 35s ✅ (Target < 5m)

Check Status Details
Lint ✅ Pass N/A Errors, N/A Warnings
Unit Tests ✅ Pass JUnit Reports
Build ✅ Pass assembleDevDebug

Reports and artifacts (including Lint HTML) are available in the Artifacts Tab of the Action Run Summary

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant