refactor(connectivity): route ImapClient/SendWorker/IdleService through AppLog + scrub email PII (#324) #328

Closed
opened 2026-07-05 00:49:24 +00:00 by JMR-dev · 0 comments
JMR-dev commented 2026-07-05 00:49:24 +00:00 (Migrated from github.com)

Part of #324 (strangler-migrate debug logging to AppLog). Also resolves the logcat PII leak
in Backlog #297.

Sequencing: PARALLEL with the other migration areas, after the Seam ticket merges
(needs the AppLog.w(tag, msg, throwable) overload + accountLogRef(...)). Touches
mail/ImapClient, data/sync/SendWorker, push/IdleService — no collision with siblings.

Scope (files)

  • app/src/main/kotlin/org/libremail/mail/ImapClient.kt — 2 raw Log.d.
  • app/src/main/kotlin/org/libremail/data/sync/SendWorker.kt — 1 raw Log.w (PII: email).
  • app/src/main/kotlin/org/libremail/push/IdleService.kt — 2 raw (Log.i, Log.w (PII: email)).
  • Tests: ImapClientTest, SendWorkerTest (both currently mockkStatic(Log::class) — rewrite
    to assert the buffer + no-PII).

Migrate + scrub these call sites

ImapClient.kt (TAG "LibreMailIdle"):

  • :477 Log.d(TAG, "IDLE connected") -> AppLog.d(TAG, "IDLE connected"). No PII. Keep it
    account-agnostic — idle() only holds ImapConnectionParams (host/username = PII), which
    must not be logged; account attribution belongs to the IdleService caller (below).
  • :482 Log.d(TAG, "IDLE push: ${event.messages.size} new message(s)") -> AppLog.d(TAG, same).
    Message count only — safe (never log subjects/senders/content).

SendWorker.kt (TAG "SendWorker"):

  • :146 Log.w(TAG, "Graph send failed for ${account.email}; falling back to SMTP", e) ->
    PII fix (#297): AppLog.w(TAG, "Graph send failed for ${accountLogRef(account.id)}; falling back to SMTP", e).
    The throwable e may embed the email/host in its message -> the Seam StackTraceScrubber
    redacts it on buffer-record.

IdleService.kt (TAG "IdleService"):

  • :94 Log.i(TAG, "encrypted cache locked; deferring IDLE push until the app is unlocked") ->
    AppLog.i(TAG, same). No PII.
  • :215 Log.w(TAG, "IDLE for ${account.email} dropped; retrying in ${backoffMs}ms", e) ->
    PII fix (#297): AppLog.w(TAG, "IDLE for ${accountLogRef(account.id)} dropped; retrying in ${backoffMs}ms", e).

New breadcrumbs (IMAP connect/IDLE/drop, send attempt/result/fallback)

  • IdleService.watchAccount start: AppLog.i(TAG, "IDLE watch start ${accountLogRef(account.id)}").
  • SendWorker.doWork: on entry with pending work AppLog.i(TAG, "outbox drain: ${pending.size} queued");
    per message success AppLog.i(TAG, "sent ${accountLogRef(account.id)} via ${if (Graph) "Graph" else "SMTP"}");
    per message failure AppLog.w(TAG, "send failed for ${accountLogRef(account.id)}; will retry")
    (NO subject/recipients/body). The Graph->SMTP fallback line above is the fallback breadcrumb.

PII (hard rule)

Never log account.email, host, username, recipients, subject, or body. Account attribution =
accountLogRef(account.id) only. Counts/backoff/booleans are safe. This ticket is the one that
closes #297's raw-Log.w("…${account.email}…") leak to Logcat.

Test expectation (unit)

  • Rewrite SendWorkerTest + ImapClientTest to install a real RingLogBuffer via
    AppLog.install(...) instead of mocking Log. Assert: (a) the fallback / IDLE-drop /
    outbox-drain breadcrumbs are captured at the right level; (b) regression for #297 — with a
    known test account email, assert no buffer line (and no Log argument) contains that
    address. Removing the Log mock also drops import android.util.Log from these tests (needed
    by the guard-rule ticket).
  • Per repo DoD, extend a connectivity/send E2E (e.g. the GreenMail send path) to assert the send
    breadcrumb lands PII-free; IdleService per-account attribution is validated via the
    accountLogRef unit + the SendWorker/ImapClient buffer tests where a JVM Service harness
    isn't available.

Parallelism: parallel with auth/lock, DB/keystore, sync-engine, stragglers — after Seam.

Part of #324 (strangler-migrate debug logging to AppLog). **Also resolves the logcat PII leak in Backlog #297.** **Sequencing: PARALLEL** with the other migration areas, **after the Seam ticket merges** (needs the `AppLog.w(tag, msg, throwable)` overload + `accountLogRef(...)`). Touches `mail/ImapClient`, `data/sync/SendWorker`, `push/IdleService` — no collision with siblings. ## Scope (files) - `app/src/main/kotlin/org/libremail/mail/ImapClient.kt` — 2 raw `Log.d`. - `app/src/main/kotlin/org/libremail/data/sync/SendWorker.kt` — 1 raw `Log.w` **(PII: email)**. - `app/src/main/kotlin/org/libremail/push/IdleService.kt` — 2 raw (`Log.i`, `Log.w` **(PII: email)**). - Tests: `ImapClientTest`, `SendWorkerTest` (both currently `mockkStatic(Log::class)` — rewrite to assert the buffer + no-PII). ## Migrate + scrub these call sites `ImapClient.kt` (TAG "LibreMailIdle"): - `:477` `Log.d(TAG, "IDLE connected")` -> `AppLog.d(TAG, "IDLE connected")`. No PII. Keep it account-agnostic — `idle()` only holds `ImapConnectionParams` (host/username = PII), which must **not** be logged; account attribution belongs to the `IdleService` caller (below). - `:482` `Log.d(TAG, "IDLE push: ${event.messages.size} new message(s)")` -> `AppLog.d(TAG, same)`. Message **count** only — safe (never log subjects/senders/content). `SendWorker.kt` (TAG "SendWorker"): - `:146` `Log.w(TAG, "Graph send failed for ${account.email}; falling back to SMTP", e)` -> **PII fix (#297):** `AppLog.w(TAG, "Graph send failed for ${accountLogRef(account.id)}; falling back to SMTP", e)`. The throwable `e` may embed the email/host in its message -> the Seam `StackTraceScrubber` redacts it on buffer-record. `IdleService.kt` (TAG "IdleService"): - `:94` `Log.i(TAG, "encrypted cache locked; deferring IDLE push until the app is unlocked")` -> `AppLog.i(TAG, same)`. No PII. - `:215` `Log.w(TAG, "IDLE for ${account.email} dropped; retrying in ${backoffMs}ms", e)` -> **PII fix (#297):** `AppLog.w(TAG, "IDLE for ${accountLogRef(account.id)} dropped; retrying in ${backoffMs}ms", e)`. ## New breadcrumbs (IMAP connect/IDLE/drop, send attempt/result/fallback) - `IdleService.watchAccount` start: `AppLog.i(TAG, "IDLE watch start ${accountLogRef(account.id)}")`. - `SendWorker.doWork`: on entry with pending work `AppLog.i(TAG, "outbox drain: ${pending.size} queued")`; per message success `AppLog.i(TAG, "sent ${accountLogRef(account.id)} via ${if (Graph) "Graph" else "SMTP"}")`; per message failure `AppLog.w(TAG, "send failed for ${accountLogRef(account.id)}; will retry")` (NO subject/recipients/body). The Graph->SMTP fallback line above is the fallback breadcrumb. ## PII (hard rule) Never log `account.email`, host, username, recipients, subject, or body. Account attribution = `accountLogRef(account.id)` only. Counts/backoff/booleans are safe. This ticket is the one that closes #297's raw-`Log.w("…${account.email}…")` leak to Logcat. ## Test expectation (unit) - Rewrite `SendWorkerTest` + `ImapClientTest` to install a real `RingLogBuffer` via `AppLog.install(...)` instead of mocking `Log`. Assert: (a) the fallback / IDLE-drop / outbox-drain breadcrumbs are captured at the right level; (b) **regression for #297** — with a known test account email, assert **no** buffer line (and no `Log` argument) contains that address. Removing the `Log` mock also drops `import android.util.Log` from these tests (needed by the guard-rule ticket). - Per repo DoD, extend a connectivity/send E2E (e.g. the GreenMail send path) to assert the send breadcrumb lands PII-free; `IdleService` per-account attribution is validated via the `accountLogRef` unit + the `SendWorker`/`ImapClient` buffer tests where a JVM Service harness isn't available. **Parallelism:** parallel with auth/lock, DB/keystore, sync-engine, stragglers — after Seam.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: JMR-dev/LibreMail#328