vc=69: audit-fix sprint round 2 (regressions on round 1)
Round-2 audit caught four real regressions on round-1 fixes plus a
handful of MEDs. This sprint fixes them.
H1 — FeedRefreshWorker exception class
Round 1 wrapped subscriptionFeed in try/catch IOException, but
UniFFI generates StrawcoreException (kotlin.Exception, not
IOException). The retry path was dead code. Catch
StrawcoreException.Network instead — the variant our error.rs
maps NetworkError::Transport into.
H2 — enrichJob terminal emit cancellation race
withContext(Dispatchers.Default) { mergeFromCache(...) } has no
suspension points so a cancel arriving mid-merge isn't observed
until the next suspending call. Without a guard, the non-suspending
_ui.update lands AFTER clearInMemoryCache() and resurrects the
cleared items. Add coroutineContext.ensureActive() after each
withContext hop, before the emit. Applied on both the refresh
terminal emit and the enrich terminal emit.
H6 — enrichVisibleItems shows stale subscriptions
The channelsSnapshot captured at refresh-end is ~2s stale by the
time the enrich terminal emit runs. If the user unsubscribed from
X in that window, X's items still appear on the feed for one
frame. Re-read Subscriptions at the terminal step and intersect
with the snapshot.
R-H3 — extract_channel_id substring match
Round 1 used trimmed_lower.find(prefix) which matches ANY position.
evil.com/?redir=https://www.youtube.com/channel/UCxxx silently
rewrote to the embedded channel ID. strip_prefix() anchors at byte
0. ASCII-only prefix means byte indices align in trimmed_lower vs
trimmed.
R-H2 — String::from_utf8 silent-drop
YouTube ships mojibake titles in the wild. Strict from_utf8
returned None on any bad byte, dropping the entire channel from the
feed with only a quiet None. Switch to from_utf8_lossy — quick-xml
tolerates U+FFFD replacement chars and the per-entry skip-on-empty
handles broken entries.
R-H1 — read_capped_body per-chunk size sanity
HTTP allows arbitrarily large single chunks. Reject any chunk
exceeding the whole body cap before adding it to the buffer, so a
hostile server can't get us to allocate a hyper Bytes larger than
the cap.
M3 — Avatar URL validation
ch.avatar is extractor-emitted; a poisoned channel page could ship
data:image/svg+xml,<svg>...<script> or javascript: URLs. Validate
http(s):// scheme before persisting to Subscriptions and before
surfacing via VideoDetail.uploaderAvatar.
M4 — ChannelViewModel dual loadedUrl
Same shape VideoDetail's round-1 fix declared unsafe. Move
loadedUrl into ChannelUiState, drop the field, use _ui.value
snapshot at top of load() and _ui.value.loadedUrl for the fence.
Rejected-URL path also stamps loadedUrl so the gate is coherent.
This commit is contained in:
parent
5f2ba264b0
commit
23fb6f52b0
6 changed files with 98 additions and 26 deletions
|
|
@ -55,6 +55,6 @@ const val NEWPIPE_APPLICATION_ID_NEW = "net.newpipe.app"
|
|||
// vc=19 / 0.1.0-AE — rust pipeline cutover. Extraction via
|
||||
// strawcore-core (Sulkta-Coop/strawcore) via the UniFFI wrapper; no
|
||||
// NewPipeExtractor in the runtime path.
|
||||
const val STRAW_VERSION_CODE = 68
|
||||
const val STRAW_VERSION_NAME = "0.1.0-CB"
|
||||
const val STRAW_VERSION_CODE = 69
|
||||
const val STRAW_VERSION_NAME = "0.1.0-CC"
|
||||
const val STRAW_APPLICATION_ID = "com.sulkta.straw"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue