vc=87: audit #2 fix sprint — close two vc=86 regressions + MED/LOW

Fixes from the second code audit (which adversarially refute-verified every
HIGH/CRIT — 0 survived; these are the confirmed MED/LOW):

- Duplicate-key crash (M-1 + M-4): the subs feed, Search, and Channel lists
  key their LazyColumn by video url, but the sources weren't fully deduped —
  a video appearing twice (collab/cross-post in two subscribed feeds, or the
  same videoId across two search/channel shelves) made a duplicate Compose key
  → IllegalArgumentException → screen crash. Subs merge + Search/Channel FIRST
  page now distinctBy(url); the continuation paths already did. (M-4 was a
  regression of the vc=86 key additions, which only deduped the append path.)
- IosSafe end-of-chunk roll (M-5): the vc=86 change set chunkRemaining=0 on
  inner EOF expecting the NEXT read() to roll, but Media3 stops calling read()
  after a -1, so a LENGTH_UNSET inner still truncated to the first chunk. The
  roll now happens WITHIN read() (loop) with a progress guard so a no-progress
  chunk can't spin; folds in the zero-length-chunk EOF case too.
- SearchCacheStore.clear() is now atomic (updateAndGet) so a concurrent
  record() can't resurrect a just-cleared entry on disk (M-3).
- Gate the YtRelated autoplay branch through isAllowedYtUrl to match the
  universal-allowlist invariant (L-11); no-op-guard setLatestKnownVersion
  (L-13); drop two unused TrackSelectionParameters imports (L-3).

Deferred (low-risk hygiene, none a crash): SharedPreferences write-ordering
serialization (M-2), the migration's dead Http.kt sweep (L-2), a stale-comment
pass. Verified: full Android compileDebugKotlin green.
This commit is contained in:
Sulkta 2026-06-21 14:30:58 -07:00
parent d6399b367d
commit 378634078e
10 changed files with 129 additions and 54 deletions

View file

@ -9,6 +9,28 @@ const val STRAW_SDK_TARGET = 35
// Sulkta fork — Straw
//
// vc=87 / 0.1.0-CU — audit #2 fix sprint (closes two vc=86 regressions + more):
// * FIX a duplicate-key crash: the Subs feed, Search, and Channel lists key
// their LazyColumn by the video url, but the sources weren't fully deduped
// — a video that appears twice (a collab/cross-post in two subscribed
// feeds, or the same videoId across two search/channel shelves) produced a
// duplicate Compose key → IllegalArgumentException → screen crash. Now the
// subs merge + the Search/Channel FIRST page dedup by url (the continuation
// paths already did). The vc=86 key additions only deduped the append path.
// * Complete the IosSafe end-of-chunk roll: the vc=86 change set
// chunkRemaining=0 on inner EOF expecting the NEXT read() to roll, but
// Media3 stops calling read() after a -1, so a LENGTH_UNSET inner still
// truncated to the first chunk. Now the roll happens WITHIN read() (loop),
// with a progress guard so a no-progress chunk can't spin.
// * SearchCacheStore.clear() is now atomic (updateAndGet), so a concurrent
// record() can't resurrect a just-cleared entry on disk.
// * Gate the YtRelated autoplay branch through isAllowedYtUrl to match the
// stated universal-allowlist invariant; no-op-guard setLatestKnownVersion;
// drop two unused TrackSelectionParameters imports.
// (Deferred to a follow-up: SharedPreferences write-ordering serialization
// across the stores, the migration's dead Http.kt sweep, and a stale-comment
// pass — all low-risk hygiene, none a crash.)
//
// vc=86 / 0.1.0-CT — audit-fix sprint (code-audit HIGH H2 + 5 MED/LOW):
// * Audio-only toggle no longer drops your max-resolution cap. Both the
// fullscreen button and the detail "Audio" pill rebuilt the track-
@ -225,6 +247,6 @@ const val STRAW_SDK_TARGET = 35
// vc=19 / 0.1.0-AE — rust pipeline cutover. Extraction via
// strawcore-core (Sulkta-OSS/strawcore) via the UniFFI wrapper; no
// NewPipeExtractor in the runtime path.
const val STRAW_VERSION_CODE = 86
const val STRAW_VERSION_NAME = "0.1.0-CT"
const val STRAW_VERSION_CODE = 87
const val STRAW_VERSION_NAME = "0.1.0-CU"
const val STRAW_APPLICATION_ID = "com.sulkta.straw"