reliability + security fix sprint (vc=91)

Straw full-audit fix batch (2026-07-04). Confirmed HIGH/MED findings:

- Updater self-brick: drop the fdroid.sulkta.com leaf+E7 SPKI pins (LE
  rotates the leaf every ~90d + intermediates from a pool → a routine
  renewal was guaranteed to miss both pins and silently kill the only
  in-app update path). System-CA validation + the install-time APK
  signature gate remain. Also distinguish "index unreachable" from "up to
  date", add a 30s callTimeout + bounded index read, guard the API-26
  NotificationChannel, and setOnlyAlertOnce.
- Request POST_NOTIFICATIONS at runtime so A13+ update/media notifications
  actually post (was declared but never requested → silent no-op).
- isDebuggable=false on the shipped debug variant (closes ADB run-as dump
  of watch/search history + subs; no package-id cutover).
- Crash fixes: distinctBy(url) on moreFromChannel + related (duplicate
  LazyColumn key); back-handling driven by live nav depth so it survives
  moveTaskToBack on A12+; PlaylistsStore per-store caps + off-main
  hydration (hostile import ANR); SponsorBlock staleness fence via
  NowPlaying.refreshIfCurrent.
- CacheCap.nearest() snaps up to the nearest finite cap (defaults no longer
  resolve to Unlimited/100k on fresh installs).
- RYD/SB FFI shims wrap the uniffi call (swallow a core panic per contract).
- Rust wrapper: release panic="unwind" (was "abort", which defeated UniFFI
  catch_unwind → any core panic = whole-app SIGABRT) + a 60s wall-clock
  timeout on every blocking extractor call (unblocks the caller, bounds
  thread pileup). Pairs with the strawcore-core reliability fix.
This commit is contained in:
Cobb 2026-07-04 07:07:48 -07:00 committed by Cobb Hayes
parent 410e44305e
commit e4a8751942
18 changed files with 508 additions and 161 deletions

View file

@ -33,11 +33,8 @@ pub async fn channel_info(input: String) -> Result<ChannelInfo, StrawcoreError>
log::info!("strawcore::channel_info input_len={}", input.len());
crate::runtime::ensure_initialized();
let identifier = resolve_channel_identifier(&input)?;
let core = tokio::task::spawn_blocking(move || core_channel_info(identifier))
.await
.map_err(|e| StrawcoreError::Extractor {
msg: format!("join: {e}"),
})??;
let core = crate::runtime::run_extract("channel_info", move || core_channel_info(identifier))
.await?;
Ok(map_channel(core))
}
@ -63,11 +60,10 @@ pub async fn channel_videos_continuation(token: String) -> Result<Page, Strawcor
token.len()
);
crate::runtime::ensure_initialized();
let page = tokio::task::spawn_blocking(move || core_channel_continuation(&token))
.await
.map_err(|e| StrawcoreError::Extractor {
msg: format!("join: {e}"),
})??;
let page = crate::runtime::run_extract("channel_videos_continuation", move || {
core_channel_continuation(&token)
})
.await?;
Ok(page_from_core(page))
}