From 2cb5871ba3c76bfd95a257dcb877f5e0e13c8af2 Mon Sep 17 00:00:00 2001 From: Christoph Mack Date: Thu, 8 Oct 2015 21:41:04 +0200 Subject: [PATCH] Safer handling of uploaded database files during full sync. Before overwriting its version of a user's collection database file with one uploaded by the client, the server now performs a basic integrity check using SQLite's 'pragma integrity_check'. --- ankisyncd/sync_app.py | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/ankisyncd/sync_app.py b/ankisyncd/sync_app.py index 891eb0f..5d28f65 100644 --- a/ankisyncd/sync_app.py +++ b/ankisyncd/sync_app.py @@ -31,6 +31,7 @@ import zipfile import ankisyncd import anki +from anki.db import DB from anki.sync import Syncer, MediaSyncer from anki.utils import intTime, checksum, isMac from anki.consts import SYNC_ZIP_SIZE, SYNC_ZIP_COUNT @@ -405,23 +406,31 @@ class SyncApp(object): return data def operation_upload(self, col, data, session): - col.close() + # Verify integrity of the received database file before replacing our + # existing db. + temp_db_path = session.get_collection_path() + ".tmp" + with open(temp_db_path, 'wb') as f: + f.write(data) - # TODO: we should verify the database integrity before perminantly overwriting - # (ie. use a temporary file) and declaring this a success! - # - # d = DB(path) - # assert d.scalar("pragma integrity_check") == "ok" - # d.close() - # try: - with open(session.get_collection_path(), 'wb') as fd: - fd.write(data) + test_db = DB(temp_db_path) + if test_db.scalar("pragma integrity_check") != "ok": + raise HTTPBadRequest("Integrity check failed for uploaded " + "collection database file.") + test_db.close() + except sqlite.Error as e: + raise HTTPBadRequest("Uploaded collection database file is " + "corrupt.") + + # Overwrite existing db. + col.close() + try: + os.rename(temp_db_path, session.get_collection_path()) finally: col.reopen() col.load() - # run hook_upload if one is defined + # If everything went fine, run hook_upload if one is defined. if self.hook_upload is not None: self.hook_upload(col, session)