fix: 修复数据库解密Silent Failure导致数据丢失
- 移除静默忽略解密错误的逻辑,始终快速失败 - 添加详细的SystemCrypto初始化和解密过程日志 - 修复CommonJS require语法错误 - 确保数据库解密失败时不会创建空数据库 问题根源:异步初始化竞争条件 + Silent Failure掩盖真实错误 修复后:解密失败会明确报错,防止数据丢失 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -31,9 +31,23 @@ let sqlite: Database.Database; // Module-level sqlite instance
|
|||||||
// Async initialization function to handle SystemCrypto and DatabaseFileEncryption
|
// Async initialization function to handle SystemCrypto and DatabaseFileEncryption
|
||||||
async function initializeDatabaseAsync(): Promise<void> {
|
async function initializeDatabaseAsync(): Promise<void> {
|
||||||
// Initialize SystemCrypto database key first
|
// Initialize SystemCrypto database key first
|
||||||
|
databaseLogger.info("Initializing SystemCrypto database key...", {
|
||||||
|
operation: "db_init_systemcrypto",
|
||||||
|
envKeyAvailable: !!process.env.DATABASE_KEY,
|
||||||
|
envKeyLength: process.env.DATABASE_KEY?.length || 0,
|
||||||
|
});
|
||||||
|
|
||||||
const systemCrypto = SystemCrypto.getInstance();
|
const systemCrypto = SystemCrypto.getInstance();
|
||||||
await systemCrypto.initializeDatabaseKey();
|
await systemCrypto.initializeDatabaseKey();
|
||||||
|
|
||||||
|
// Verify key is available after initialization
|
||||||
|
const dbKey = await systemCrypto.getDatabaseKey();
|
||||||
|
databaseLogger.info("SystemCrypto database key initialized", {
|
||||||
|
operation: "db_init_systemcrypto_complete",
|
||||||
|
keyLength: dbKey.length,
|
||||||
|
keyAvailable: !!dbKey,
|
||||||
|
});
|
||||||
|
|
||||||
if (enableFileEncryption) {
|
if (enableFileEncryption) {
|
||||||
try {
|
try {
|
||||||
// Check if encrypted database exists
|
// Check if encrypted database exists
|
||||||
@@ -43,15 +57,31 @@ async function initializeDatabaseAsync(): Promise<void> {
|
|||||||
{
|
{
|
||||||
operation: "db_memory_load",
|
operation: "db_memory_load",
|
||||||
encryptedPath: encryptedDbPath,
|
encryptedPath: encryptedDbPath,
|
||||||
|
fileSize: fs.statSync(encryptedDbPath).size,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
// Decrypt database content to memory buffer (now async)
|
// Decrypt database content to memory buffer (now async)
|
||||||
|
databaseLogger.info("Starting database decryption...", {
|
||||||
|
operation: "db_decrypt_start",
|
||||||
|
encryptedPath: encryptedDbPath,
|
||||||
|
});
|
||||||
|
|
||||||
const decryptedBuffer =
|
const decryptedBuffer =
|
||||||
await DatabaseFileEncryption.decryptDatabaseToBuffer(encryptedDbPath);
|
await DatabaseFileEncryption.decryptDatabaseToBuffer(encryptedDbPath);
|
||||||
|
|
||||||
|
databaseLogger.info("Database decryption successful", {
|
||||||
|
operation: "db_decrypt_success",
|
||||||
|
decryptedSize: decryptedBuffer.length,
|
||||||
|
isSqlite: decryptedBuffer.slice(0, 16).toString().startsWith('SQLite format 3'),
|
||||||
|
});
|
||||||
|
|
||||||
// Create in-memory database from decrypted buffer
|
// Create in-memory database from decrypted buffer
|
||||||
memoryDatabase = new Database(decryptedBuffer);
|
memoryDatabase = new Database(decryptedBuffer);
|
||||||
|
|
||||||
|
databaseLogger.info("In-memory database created from decrypted buffer", {
|
||||||
|
operation: "db_memory_create_success",
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
memoryDatabase = new Database(":memory:");
|
memoryDatabase = new Database(":memory:");
|
||||||
isNewDatabase = true;
|
isNewDatabase = true;
|
||||||
@@ -101,15 +131,22 @@ async function initializeDatabaseAsync(): Promise<void> {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
databaseLogger.error("Failed to initialize memory database", error, {
|
databaseLogger.error("Failed to initialize memory database", error, {
|
||||||
operation: "db_memory_init_failed",
|
operation: "db_memory_init_failed",
|
||||||
|
errorMessage: error instanceof Error ? error.message : "Unknown error",
|
||||||
|
errorStack: error instanceof Error ? error.stack : undefined,
|
||||||
|
encryptedDbExists: DatabaseFileEncryption.isEncryptedDatabaseFile(encryptedDbPath),
|
||||||
|
databaseKeyAvailable: !!process.env.DATABASE_KEY,
|
||||||
|
databaseKeyLength: process.env.DATABASE_KEY?.length || 0,
|
||||||
});
|
});
|
||||||
|
|
||||||
// If file encryption is critical, fail fast
|
// 🔥 CRITICAL: Never silently ignore database decryption failures!
|
||||||
if (process.env.DB_FILE_ENCRYPTION_REQUIRED === "true") {
|
// This causes complete data loss for users
|
||||||
throw error;
|
console.error("🚨 DATABASE DECRYPTION FAILED - THIS IS CRITICAL!");
|
||||||
}
|
console.error("Error details:", error instanceof Error ? error.message : error);
|
||||||
|
console.error("Encrypted file exists:", DatabaseFileEncryption.isEncryptedDatabaseFile(encryptedDbPath));
|
||||||
|
console.error("DATABASE_KEY available:", !!process.env.DATABASE_KEY);
|
||||||
|
|
||||||
memoryDatabase = new Database(":memory:");
|
// Always fail fast on decryption errors - data integrity is critical
|
||||||
isNewDatabase = true;
|
throw new Error(`Database decryption failed: ${error instanceof Error ? error.message : "Unknown error"}. This prevents data loss.`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
memoryDatabase = new Database(":memory:");
|
memoryDatabase = new Database(":memory:");
|
||||||
|
|||||||
Reference in New Issue
Block a user