Visitar URL original
An overwrite restore deletes the target database before a byte of the archive has been read, so a mistyped encryption key destroys it and the operator is told only "Unable to perform restore" · Issue #11050 · ArcadeData/arcadedb · GitHub
Skip to content

An overwrite restore deletes the target database before a byte of the archive has been read, so a mistyped encryption key destroys it and the operator is told only "Unable to perform restore" #11050

Closed

Description

@ruispereira

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 delete
  • integration/src/main/java/com/arcadedb/integration/restore/format/FullRestoreFormat.java:124-125,
    the generic refusal a wrong key produces
  • integration/src/main/java/com/arcadedb/integration/restore/RestoreSettings.java:110-113, -o
  • server/src/main/java/com/arcadedb/server/ServerControlPlane.java:2639-2690, the temporary
    directory the server restores into, and the deleteRecursively(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? -o means "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, because Restore has no encryption-key setter and
    performRestore sets none, so the CLI with -encryptionKey is 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

  1. added this to the 26.11.1 milestone on Oct 31, 2026
  2. self-assigned this
    on Nov 1, 2026
  3. added a commit that references this issue on Nov 3, 2026
  4. robfrank commented on Nov 3, 2026

    @robfrank
    Collaborator

    The reported behavior (the -o restore 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.

  5. removed
    in progressThe team is actively working on this issue
    on Nov 3, 2026
  6. added a commit that references this issue on Nov 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

backup-restoreBackup, restore, and exportbugseverity:criticalData loss, corruption, crash/hang, security hole, or product unusable

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions