bug(native): untracked backup overwrite can change another game's owned save path
I maintainer di solito rispondono entro 1 giorno
Valutazione
- Difficoltà
- 4/5
- Tempo stimato
- 3-5 giorni
- Idoneità per principianti
- 48/100
- Tipo di issue
- Bug
- Chiarezza
- Specificata chiaramente
- Stato di attività
- Attiva
- Stack tecnologico
- go, sqlite
- Ambito
- backend-api-design, databases
Direzione di ricerca
Start with internal/api/routes_backup.go and trace the /api/backup/restore flow, especially the order of CheckRestoreTarget, extraction, and TrackGame. Review the ownership predicate proposed in PR #131 and run the three regression cases linked in the issue. Done means conflicting primary, named, and nested roots are refused before extraction with owner bytes unchanged, while valid unclaimed imports still work.
Scritto dal modello di indicizzazione a partire dal testo della issue.
Descrizione
Summary
A portable v2 backup for an untracked game can be imported with mode: "overwrite" into a folder already owned by a different tracked game. The importer extracts archived bytes before it establishes authoritative game/root ownership. An eventual registration refusal is too late: the tracked owner's live files have already changed.
Affected source: Liquid-co/OpenSave:dev-public@a0f6032b8ba36da25e3e0e5e90c5ace9ba0c1fc7 on Linux amd64. The result is confirmed with synthetic files and actual localhost native HTTP export/restore plus SQLite. No real game saves were used.
Related but distinct: upstream #130 / PR #131 correct ordinary registration ownership, but a backup import must enforce that check before extracting. This does not duplicate snapshot-ID collision reports.
Independent reproduction
Public Go regression · Reproduction guide
git checkout repro/native-root-lineage-import-20261030
go test -count=1 -race -v ./internal/api -run '^TestRegressionUntrackedImportCannotOverwriteOwnedRoot$'
Three separate test cases exercise the exact primary, exact named config, and nested under named config save-root scenarios:
- Export an untracked synthetic backup containing
ARCHIVED-OLDat a chosen temporary location. - Track a different game as native owner of that exact or enclosing save root. Replace its live
slot.savcontents withOWNER-LIVE. - Call
POST /api/backup/restorewith{sourcePath: archive, mode: "overwrite"}. - Read the owner's actual file bytes and verify a conflicting second native game was not registered.
| Scenario | Required result | Stock dev-public |
|---|---|---|
| Destination equals another game's primary root | Preserve OWNER-LIVE; refuse/skip |
slot.sav overwritten with archived bytes |
| Destination equals another game's mapped named root | Preserve OWNER-LIVE; refuse/skip |
slot.sav overwritten |
| Destination nested inside another game's mapped named root | Preserve OWNER-LIVE; refuse/skip |
slot.sav overwritten |
In the primary case a subsequent registration can fail after extraction. For named/nested roots, conflicting registration also occurs on stock in the observed fixture. The test's decisive safety assertion is that the owner's file bytes remain unchanged.
Root cause
internal/api/routes_backup.go performs CheckRestoreTarget (directory suitability), then snapshot.UnzipRoots (filesystem mutation), then TrackGame (ownership admission). The imported game's own write gate cannot stand in for the existing other game's ownership.
Bounded suggested fix
For untracked overwrite imports, perform authoritative path-ownership refusal before any mutation, including primary/named/nested roots, resolvable aliases and inventory-read failure. Reuse the transactional ownership predicate proposed in PR #131, and hold a bounded native admission/claim through extraction and registration to avoid time-of-check/time-of-use races. Preserve valid unclaimed imports, tracked-game restore semantics, and the existing pre-import safety backup.
Do not introduce a new save-sync engine, a permanent managed-game class or a general first-copy coordinator.
Acceptance criteria
- All three exact-stock tests fail for the intended overwrite and pass with original owner bytes preserved; no second owner is registered.
- Physical path aliases, unreadable inventory, and simultaneous native registration/root edits fail closed before extraction.
- Valid unclaimed untracked imports and existing tracked-game imports continue to work.
- A returned extraction/registration error restores the pre-import safety copy rather than reporting a successful import; distinguish this from unverified power-loss/SIGKILL durability.
- Linux Go/race tests and supported platform builds pass; native Windows lock semantics should be checked separately if used.
Evidence limit: Reproduced native writes to disposable files only. No evidence of real user-save loss, deployed exploitation, or a complete crash-restart durability guarantee.
Independent public reproducer and original report: https://github.com/Seagulls/OpenSave/issues/15.
- Lingua principale
- Go
- Stelle
- 225
- Fork
- 9
- Merge medio
- 1g 23h
- PR unite (30g)
- 76
Preparare l'ambiente
- Include un Dockerfile o un file Docker Compose
- Nessun modello di pull request
- Nessuna guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di Liquid-co/OpenSave
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
Liquid-co/OpenSave#149 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 5/5 Più di una settimana Idoneità per principianti 20/100
I maintainer di solito rispondono entro 1 giorno
-
bug(native): delayed sync-complete can contaminate a re-added named root's lineageForse già presa Una pull request collegata a questa issue è aperta o già unita. Aperta
Difficoltà 4/5 3-5 giorni Idoneità per principianti 48/100
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 4/5 3-5 giorni Idoneità per principianti 48/100
I maintainer di solito rispondono entro 1 giorno
-
Feature request: Allow atomic placement of offered games with AutoSync disabledForse già presa Una pull request collegata a questa issue è aperta o già unita. Aperta
Difficoltà 4/5 3-5 giorni Idoneità per principianti 22/100
Liquid-co/OpenSave#40 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
Tutte le issue di Liquid-co/OpenSave
Issue simili
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 75/100
I maintainer di solito rispondono entro 1 giorno
-
agent-butler-finding chore
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 88/100
jordansmall/spindrift#6695 ·
I maintainer di solito rispondono entro 1 giorno
-
skills-init: commit-SHA skill refs never start (git checkout -- <sha> reads the SHA as a pathspec)Aperta
Difficoltà 1/5 Meno di un'ora Idoneità per principianti 84/100
kagent-dev/kagent#3571 ·
I maintainer di solito rispondono entro 1 giorno
-
book-layout.spec fails when the similar-books rail holds other projects' booksForse già presa @praetorianer777 l’ha presa oggi. Aperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
praetorianer777/GoTome#373 ·
I maintainer di solito rispondono entro 1 giorno
-
bug
Difficoltà 2/5 1-3 ore Idoneità per principianti 80/100