feat(peer): validate manifests before download mutation

Why:
- Remote and UI-echoed file descriptions could reach transaction and storage
  code one entry at a time, so a hostile late path could mutate earlier files.
- Per-file consensus also accepted malformed peer lists and let duplicate rows
  inflate a source's vote.

What:
- Add a complete protocol-7 manifest adapter with catalog-root confinement,
  portable path and alias rules, reserved-path protection, shape and size caps,
  symlink/reparse inspection, and zero-mutation tests.
- Keep download selection in the peer core, validate every peer manifest before
  consensus, and pass only the validated manifest into storage/orchestration.
- Canonicalize locally advertised paths, cap exact chunk receives, and preserve
  the local-only install fast path.
- Record the chosen safety limits and follow-up ownership/catalog decisions.

Test Plan:
- just clippy
- just test
- just frontend-test
- just build
- just fmt (Rust/TOML/Prettier completed; rumdl reports 39 pre-existing issues)
- git diff --cached --check
This commit is contained in:
ddidderr committed 2026-08-09 17:51:11 +02:00
1 parent 9268de2371
commit a6ed60a538
12 files changed
+1376 -137

No files matched your search

+5 -20
View File
@@ -238,14 +238,10 @@ pub enum PeerCommand {
/// Request the latest peer-advertised file details for an update.
FetchLatestFromPeers { id: String },
/// Download game files.
DownloadGameFiles {
id: String,
file_descriptions: Vec<GameFileDescription>,
},
DownloadGameFiles { id: String },
/// Download game files with an explicit install policy.
DownloadGameFilesWithOptions {
id: String,
file_descriptions: Vec<GameFileDescription>,
install_after_download: bool,
},
/// Stream archive-expanded bytes directly into `local/` without keeping root archives.
@@ -458,26 +454,15 @@ async fn handle_peer_commands(
handle_get_game_command(ctx, tx_notify_ui, id, GameDetailSource::LatestPeersOnly)
.await;
}
PeerCommand::DownloadGameFiles {
id,
file_descriptions,
} => {
handle_download_game_files_command(ctx, tx_notify_ui, id, file_descriptions, true)
.await;
PeerCommand::DownloadGameFiles { id } => {
handle_download_game_files_command(ctx, tx_notify_ui, id, true).await;
}
PeerCommand::DownloadGameFilesWithOptions {
id,
file_descriptions,
install_after_download,
} => {
handle_download_game_files_command(
ctx,
tx_notify_ui,
id,
file_descriptions,
install_after_download,
)
.await;
handle_download_game_files_command(ctx, tx_notify_ui, id, install_after_download)
.await;
}
PeerCommand::StreamInstallGame { id } => {
handlers::handle_stream_install_game_command(ctx, tx_notify_ui, id).await;