Skip to content

fix(onboarding): honor the insecure-HTTP opt-in during discovery (regression from #20)#21

Merged
fabiodalez-dev merged 1 commit into
mainfrom
fix/insecure-http-onboarding-guard
Jul 7, 2026
Merged

fix(onboarding): honor the insecure-HTTP opt-in during discovery (regression from #20)#21
fabiodalez-dev merged 1 commit into
mainfrom
fix/insecure-http-onboarding-guard

Conversation

@fabiodalez-dev

@fabiodalez-dev fabiodalez-dev commented Jul 7, 2026

Copy link
Copy Markdown
Owner

Found by an emulator smoke test of #20, before cutting a release.

Regression: the CleartextGuardInterceptor (added in #20 to close a CodeRabbit gap) reads SessionStore.allowInsecureHttp, which is only persisted when an instance is committed. During onboarding discovery the instance isn't committed yet, so the flag was still false and the guard blocked the /health probe to a plain-HTTP host even with the "Allow insecure HTTP" switch ON. The toggle affected the derived URL and isTransportAllowed(), but not the actual request gate — so a self-hoster (issue #16) could enable the toggle and still never get past onboarding.

Fix: a transient in-memory SessionStore.pendingAllowInsecureHttp, set by AuthRepository.discover() from the onboarding toggle. The NetworkModule guard now allows cleartext when EITHER the persisted flag OR the pending flag is set. Reset on clearAll() (forget instance). Coil/PDF keep using the persisted flag only (they run post-commit).

Verified on the emulator (android-35):

  • Toggle OFF + http://<non-loopback> → no request issued (guard blocks) → "Couldn't reach that address".
  • Toggle ON + same URL → the GET …/health is actually attempted (guard passes).
  • Loopback HTTP (http://10.0.2.2:8081) discovery returns 200 OK and the discovery card renders, as before.
  • App boots clean; authenticated Home + catalog + Book Club load over the network.

assembleDebug passes; existing NetworkUrlTest unaffected.

Summary by CodeRabbit

  • Bug Fixes
    • Migliorata la fase di onboarding/discovery per consentire correttamente le connessioni HTTP non sicure quando l’opzione è stata appena attivata.
    • Ridotti i casi in cui il controllo di rete poteva bloccare un probe iniziale verso host in HTTP.
    • Il consenso temporaneo per HTTP non sicuro viene ora azzerato al termine del reset sessione, evitando comportamenti incoerenti tra una scoperta e la successiva.

… just after commit

Emulator smoke test caught a real regression from the cleartext-gate hardening: the
CleartextGuardInterceptor reads SessionStore.allowInsecureHttp, which is only PERSISTED
when an instance is committed. During onboarding discovery the instance isn't committed
yet, so the flag was still false and the guard blocked the /health probe to a plain-HTTP
host even with the "Allow insecure HTTP" switch ON — the toggle affected the derived URL
and isTransportAllowed() but not the actual request gate, so discovery could never succeed.

Add a transient, in-memory SessionStore.pendingAllowInsecureHttp set by AuthRepository.discover()
from the onboarding toggle; the NetworkModule guard now allows cleartext when EITHER the
persisted flag or the pending flag is set. Reset on clearAll() (forget instance).

Verified on the emulator: with the toggle OFF a plain-HTTP host to a non-loopback address
issues no request (guard blocks → "Couldn't reach"); with it ON the GET is actually attempted.
Loopback HTTP (10.0.2.2) discovery returns 200 as before; HTTPS is unaffected.
@coderabbitai

coderabbitai Bot commented Jul 7, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

Introdotto un flag transitorio in memoria pendingAllowInsecureHttp in SessionStore, impostato da AuthRepository.discover in base al parametro allowInsecure e verificato da NetworkModule per sbloccare temporaneamente le connessioni HTTP in chiaro durante la fase di discovery/onboarding, prima del commit della sessione.

Changes

Flag pendingAllowInsecureHttp per onboarding

Layer / File(s) Summary
Definizione e reset del flag
app/src/main/java/com/pinakes/app/data/store/SessionStore.kt
Aggiunta la proprietà pubblica @Volatile var pendingAllowInsecureHttp: Boolean, non persistita in SharedPreferences, e azzerata in clearAll().
Impostazione durante la discovery
app/src/main/java/com/pinakes/app/data/repository/AuthRepository.kt
discover imposta session.pendingAllowInsecureHttp dal parametro allowInsecure prima della derivazione URL e della chiamata /health.
Sblocco del transporto in chiaro
app/src/main/java/com/pinakes/app/data/network/NetworkModule.kt
CleartextGuardInterceptor consente HTTP in chiaro se session.allowInsecureHttp oppure session.pendingAllowInsecureHttp è vero.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

  • fabiodalez-dev/Pinakes-Android#20: Modifica lo stesso percorso di discovery/onboarding con opt-in per HTTP non sicuro tramite SessionStore, AuthRepository.discover e NetworkModule.

Poem

Un coniglio scava un tunnel sicuro,
ma per un attimo, il chiaro non è oscuro 🐰
pendingAllowInsecureHttp fa da guardia gentile,
solo per l'onboarding, poi torna allo stile!
Salti di gioia tra bit e byte,
discovery fatta, tutto a posto stanotte 🌙

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Il titolo descrive correttamente il fix del consenso HTTP insicuro durante la discovery onboarding.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/insecure-http-onboarding-guard

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/src/main/java/com/pinakes/app/data/repository/AuthRepository.kt (1)

32-54: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

pendingAllowInsecureHttp non viene mai resettato dopo la richiesta di discovery.

session.pendingAllowInsecureHttp viene impostato a riga 35 ma non c'è nessun reset a false dopo la chiamata api.health() (né in caso di successo né di fallimento/eccezione). Poiché okHttpClient in NetworkModule è un singleton condiviso da tutte le richieste dell'app (login, catalog, bookclub, ecc.), non solo dalla discovery, un flag rimasto bloccato a true (es. utente attiva il toggle, la discovery fallisce o viene abbandonata senza un successivo discover() con allowInsecure=false) indebolisce il cleartext-guard per qualsiasi host HTTP per l'intera durata del processo, ben oltre l'ambito della singola richiesta di onboarding per cui è stato pensato.

In aggiunta, essendo uno stato mutabile condiviso a livello di sessione (non scoped per singola richiesta), chiamate concorrenti a discover() possono generare una race: un'invocazione può sovrascrivere il flag prima che la richiesta HTTP dell'altra invocazione venga effettivamente eseguita.

Consiglio di limitare l'ambito del flag alla durata della singola chiamata con un try/finally.

🔒 Fix proposto: limitare l'ambito del flag transitorio alla singola richiesta
     suspend fun discover(rawInstanceUrl: String, allowInsecure: Boolean = false): ApiResult<HealthDiscovery> {
         // Carry the onboarding toggle into the cleartext gate: the instance isn't committed
         // yet, so the persisted flag is still false and would block a plain-HTTP probe.
         session.pendingAllowInsecureHttp = allowInsecure
-        val apiBaseUrl = NetworkModule.deriveApiBaseUrl(rawInstanceUrl, allowInsecure)
-        val origin = NetworkModule.deriveOrigin(rawInstanceUrl, allowInsecure)
-        val api = network.api(apiBaseUrl)
-        return when (val res = apiCall { api.health() }) {
-            is ApiResult.Success -> ApiResult.Success(
-                HealthDiscovery(
-                    health = res.data,
-                    origin = origin,
-                    apiBaseUrl = apiBaseUrl,
-                    insecureTransport = res.meta?.warning == "insecure_transport" ||
-                        res.meta?.https == false,
-                    transportAllowed = NetworkModule.isTransportAllowed(apiBaseUrl, allowInsecure),
-                    allowInsecure = allowInsecure,
-                ),
-                res.meta,
-            )
-            is ApiResult.Failure -> res
-        }
+        try {
+            val apiBaseUrl = NetworkModule.deriveApiBaseUrl(rawInstanceUrl, allowInsecure)
+            val origin = NetworkModule.deriveOrigin(rawInstanceUrl, allowInsecure)
+            val api = network.api(apiBaseUrl)
+            return when (val res = apiCall { api.health() }) {
+                is ApiResult.Success -> ApiResult.Success(
+                    HealthDiscovery(
+                        health = res.data,
+                        origin = origin,
+                        apiBaseUrl = apiBaseUrl,
+                        insecureTransport = res.meta?.warning == "insecure_transport" ||
+                            res.meta?.https == false,
+                        transportAllowed = NetworkModule.isTransportAllowed(apiBaseUrl, allowInsecure),
+                        allowInsecure = allowInsecure,
+                    ),
+                    res.meta,
+                )
+                is ApiResult.Failure -> res
+            }
+        } finally {
+            session.pendingAllowInsecureHttp = false
+        }
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/main/java/com/pinakes/app/data/repository/AuthRepository.kt` around
lines 32 - 54, The temporary `session.pendingAllowInsecureHttp` flag in
`AuthRepository.discover` is left set after the discovery request, so it should
be scoped to only that call. Wrap the `api.health()` invocation in `discover()`
with a `try/finally` and always reset `pendingAllowInsecureHttp` back to false
in the `finally` block so it is cleared on both success and failure. Keep the
change localized to `discover` and use the existing
`session.pendingAllowInsecureHttp` field and `NetworkModule`/`apiCall` flow as
the reference points.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@app/src/main/java/com/pinakes/app/data/repository/AuthRepository.kt`:
- Around line 32-54: The temporary `session.pendingAllowInsecureHttp` flag in
`AuthRepository.discover` is left set after the discovery request, so it should
be scoped to only that call. Wrap the `api.health()` invocation in `discover()`
with a `try/finally` and always reset `pendingAllowInsecureHttp` back to false
in the `finally` block so it is cleared on both success and failure. Keep the
change localized to `discover` and use the existing
`session.pendingAllowInsecureHttp` field and `NetworkModule`/`apiCall` flow as
the reference points.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 47620bd2-d185-4a7b-86db-92d26ab9ef2f

📥 Commits

Reviewing files that changed from the base of the PR and between 1f2c9d8 and f4fe397.

📒 Files selected for processing (3)
  • app/src/main/java/com/pinakes/app/data/network/NetworkModule.kt
  • app/src/main/java/com/pinakes/app/data/repository/AuthRepository.kt
  • app/src/main/java/com/pinakes/app/data/store/SessionStore.kt

@fabiodalez-dev fabiodalez-dev merged commit 92a1281 into main Jul 7, 2026
2 checks passed
@fabiodalez-dev fabiodalez-dev deleted the fix/insecure-http-onboarding-guard branch July 7, 2026 05:43
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