fix(ergasia): download filesystem safety (reconciliation, disk guard, zip-slip) - #491
Merged
Conversation
… zip-slip) - #360: reconcile_persisted_torrents rebuilds torrent_map from live torrents and a persisted download_id<->librqbit_id side-table, so persisted downloads stay manageable after restart. - #365: get_available_space returns a Result and propagates errors instead of returning u64::MAX and silently bypassing the disk-space guard. - #367: replace the blocking df subprocess and synchronous extraction with fs2::available_space and off-executor (spawn_blocking) extraction. - #366: the reported ExtractedFile.path is derived from the sanitized actual write location. - #452: zip extraction pre-scans and atomically refuses the whole archive on any symlink entry, absolute path, or path-traversal (zip-slip) before writing anything. - #453: add the Seeding->Failed and Completed->Deleted state transitions. - #454: enforce a configurable max_decompression_ratio (default 100x) against the declared uncompressed size to stop decompression bombs. Closes #360 Closes #365 Closes #366 Closes #367 Closes #452 Closes #453 Closes #454 Gate-Passed: kanon 0.1.5 +stages:fmt,check,clippy,nextest,lint sha:49f3752052b311213d980fccb7ee6b1d313bc337
forkwright
added a commit
that referenced
this pull request
Jul 2, 2026
…it (#493) #491 (ergasia) changed DownloadEngine::extract to async (impl Future) so extraction runs off the executor; #489 (syntaxis) merged a test MockEngine whose extract was still sync. The two PRs were each internally consistent but ergasia was tested pre-syntaxis, so the stale mock only broke the workspace build once both landed. Make the mock extract async. Gate-Passed: kanon 0.1.5 +stages:fmt,check,clippy,nextest,lint sha:77b78bec2dcb0e1e949146db9e7a348590cc816e Co-authored-by: forkwright <noreply@forkwright.dev>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #360, #365, #366, #367, #452, #453, #454.
Changes
archive.extract()which does not sanitize symlink entries or absolute paths #452 (security) — zip-slip. Zip extraction did not sanitize symlink entries or absolute/traversal paths. A pre-scan (no decompression) now atomically refuses the whole archive before any write on any symlink entry, absolute path, or path-traversal (enclosed_name) — three layered checks.max_decompression_ratio(default 100x) now guards the declared uncompressed size.reconcile_persisted_torrentsnever rebuildstorrent_map, making all persisted downloads unmanageable after restart #360 — restart reconciliation.reconcile_persisted_torrentsrebuildstorrent_mapfrom live torrents plus a persisteddownload_id↔librqbit_idside-table, so persisted downloads stay manageable after restart.get_available_spacefailure silently bypasses the disk-space guard by returningu64::MAX#365 / Blockingdfsubprocess and synchronous extraction pipeline run on async executor threads #367 — disk guard.get_available_spacereturns a Result (wasu64::MAXon failure, silently bypassing the guard); the blockingdfsubprocess and synchronous extraction move off the async executor.ExtractedFile.pathdiverges from the actual written location when entry names contain unsafe components #366 — the reportedExtractedFile.pathis derived from the sanitized write location.Seeding→FailedandCompleted→Deletedstate transitions are added.Verification
kanon gate --fullgreen (fmt, check, advisory-parity, cargo-deny, clippy workspace, nextest, kanon lint).