fix(peer): sanitize legacy library index migration
The selected game root could supply an unbounded legacy library index whose revision was copied verbatim into app-owned state. A revision of `u64::MAX` made every later checked revision advance fail even after the original root was removed. Read only a bounded regular, non-link file whose identity stays stable across the read. Deserialize it before publication, preserve its cached game data, and reset its stale revision authority to zero. Existing app-owned state still wins without reading the legacy source, and invalid input remains in place for recovery. Test Plan: - `just test` -- passed outside the sandbox; 494 peer tests and all workspace targets passed. - The initial sandboxed `just test` reached an unrelated Unix-socket permission denial, then passed unchanged with the required socket permission. - `git diff --cached --check` -- passed.
This commit is contained in:
1 parent
0ed04f64e9
commit
7de373afb6
2 files changed
+174
-10
No files matched your search
@@ -191,6 +191,14 @@ struct LibraryIndex {
|
||||
games: HashMap<String, GameIndexEntry>,
|
||||
}
|
||||
|
||||
/// Converts a legacy selected-root cache into app-owned state without carrying
|
||||
/// its stale revision authority across the trust boundary.
|
||||
pub(crate) fn normalize_migrated_library_index(bytes: &[u8]) -> serde_json::Result<Vec<u8>> {
|
||||
let mut index: LibraryIndex = serde_json::from_slice(bytes)?;
|
||||
index.revision = 0;
|
||||
serde_json::to_vec(&index)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
struct GameIndexEntry {
|
||||
summary: LocalGameSummary,
|
||||
@@ -1043,6 +1051,29 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn migrated_library_index_preserves_games_but_resets_revision_authority() {
|
||||
let original = test_library_index(u64::MAX, "game", 77);
|
||||
let bytes = serde_json::to_vec(&original).expect("legacy index should serialize");
|
||||
|
||||
let normalized =
|
||||
normalize_migrated_library_index(&bytes).expect("legacy index should normalize");
|
||||
let migrated: LibraryIndex =
|
||||
serde_json::from_slice(&normalized).expect("normalized index should deserialize");
|
||||
|
||||
assert_eq!(migrated.revision, 0);
|
||||
assert_eq!(migrated.games.len(), 1);
|
||||
assert_eq!(
|
||||
migrated
|
||||
.games
|
||||
.get("game")
|
||||
.expect("game should be preserved")
|
||||
.summary
|
||||
.size,
|
||||
77,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn legacy_fingerprint_defaults_download_recovery_to_false() {
|
||||
let fingerprint: GameFingerprint = serde_json::from_value(serde_json::json!({
|
||||
|
||||
Reference in new issue
Block a user