Repository navigation
Description
SEVERITY: MEDIUM
Summary
The FullRestoreFormat.restoreDatabase deletes the destination directory at line 98, before the archive
has been opened for reading, decrypted, or validated in any way. Everything that can tell the
operator the archive is unusable happens afterwards. A wrong encryption key is the worst case
because it fails silently in the cipher: the CTR stream decrypts to noise, ZipInputStream finds no
local header, the walk extracts zero entries, and the only message is the generic "Unable to perform
restore" raised at line 125. By then the database the operator meant to replace is gone.
The server already has the guarantee and does not share this code path's behaviour: ServerControlPlane.performRestore (line 2639) restores into .restore-tmp-<name>-<nanos> and calls swapRestoredDatabase only once the restore has succeeded, and deletes the temporary directory on
every failure arm. The embedded and CLI entry point, which is the only caller that can set overwriteDestination at all (-o, parsed in RestoreSettings:110), destroys first and asks
later. It is also the only entry point that can supply an encryption key, since Restore exposes no setEncryptionKey and the server never sets one, so the mistyped-key case can only arise on the
path with no staging directory.
Where (HEAD 4e6cd88)
integration/src/main/java/com/arcadedb/integration/restore/format/FullRestoreFormat.java:93-98,
the deleteintegration/src/main/java/com/arcadedb/integration/restore/format/FullRestoreFormat.java:124-125,
the generic refusal a wrong key producesintegration/src/main/java/com/arcadedb/integration/restore/RestoreSettings.java:110-113,-oserver/src/main/java/com/arcadedb/server/ServerControlPlane.java:2639-2690, the temporary
directory the server restores into, and thedeleteRecursively(tempDir)on every failure arm
Repro
A 1000-record database at prod, and an encrypted archive of a different database. The operator
types the key wrongly:
PROD database holds 1000 records, 8 files
(1) restore -o of an encrypted archive with the WRONG key, over the PROD database:
restore FAILED: RestoreException: Unable to perform restore
PROD directory now: 0 files []
DatabaseFactory.exists() on the PROD path: false
PROD cannot be opened any more: SchemaException: Type with name 'Live' was not found
The directory still exists and is empty, so a later restore database naming that path is also
refused by ServerControlPlane.databaseNameIsTaken, which asks whether the directory is present
rather than whether it holds a database.
Refutation attempted
- Is the delete deliberate, as the semantics of
-o?-omeans "replace the destination", not
"empty the destination and then find out whether there is anything to put in it". The server's own
restore, which implements the same user-facing command, stages and swaps precisely so that a
failed restore leaves the previous copy intact (its comment cites issue [server audit] restore/import database: SSRF via arbitrary URL + drop-before-restore causes total data loss on failure #5027), so the project's
own answer to this question is on record and the CLI path does not follow it. - Is the wrong-key case unreachable? It is the only case this path exists for: the server cannot
restore an encrypted archive at all, becauseRestorehas no encryption-key setter and
performRestoresets none, so the CLI with-encryptionKeyis the only way to restore one. - Does the archive get validated earlier anywhere?
openInputFile()at line 90 runs before the
delete, so a missing or unreachable file is caught. Nothing else is: the content, the key and the
structure are all examined after the destination is gone.
Suggested fix
Extract into a sibling staging directory and move into place on success, which is what ServerControlPlane.performRestore already does, so the two implementations of the same command
agree. Failing that, at minimum open and authenticate the archive before the delete: for an
encrypted archive, derive the key and confirm the first entry's local header decrypts.
Separately, report a decryption failure as one. "Unable to perform restore" is what an operator sees
for a wrong key, a corrupt header and an empty archive alike, and it is the single message that
matters most here.
The guarantee: a restore that does not complete leaves the database it was going to replace exactly
as it found it, on every entry point, not only on the server's.
Scope / confidence
Executed, verbatim output above. I did not test the HA arms of the server restore, which previous
rounds have already swept (#10689, #10881, #10965, #10974).
Dedup
Checked all 1000 lines. #10689, #10533, #10388, #10881, #10965 and #10974 are all about the server's
overwrite restore and the window in which the old copy is still served or still being written to.
None of them is about the embedded and CLI path deleting the target before reading the archive, and
none is about the wrong-key message.
Activity
test(#11050): a wrong-key overwrite restore keeps the target database…
robfrank commentedon Nov 3, 2026
The reported behavior (the
-orestore deleting the target before the archive is read) was already fixed on main by #11058 (restore extracts into a staging directory and swaps on success, keeping the old copy on failure) and #11041 (the archive tail is authenticated up front and a wrong key is named in the refusal). #11161 adds the missing regression test for the exact scenario: a wrong-key overwrite restore fails, the target database keeps all its records, and no staging directories are left behind. No production code changed.Still open: nothing.
❤️ Thanks for helping make ArcadeDB better! ArcadeDB is free and open source, sustained by its community. If it's useful to you or your company, please consider becoming a sponsor to keep development going.
test(#11050): a wrong-key overwrite restore keeps the target database…