mirror of
https://github.com/opensourcepos/opensourcepos.git
synced 2026-09-22 10:45:03 -04:00
fix(security): make abortEncryptionConversion fail loudly on restore failure
The rollback path restored the .env backup with a suppressed file_put_contents() and an unchecked file_get_contents(). If the restore failed after the key had already been rotated, .env was left holding the new CI4 key while the DB still held CI3-era ciphertext, so the data became undecryptable after the next restart. Now the backup read is checked for false and the restore goes through the existing atomicWriteFile() helper; either failure throws so the error is surfaced instead of silently corrupting the config. Adds a regression test that forces an unreadable backup and asserts the throw plus that .env is left untouched.
This commit is contained in:
1 parent
23f52ddefd
commit
711e5d6b3f
2 files changed
+36
-2
No files matched your search
@@ -572,9 +572,15 @@ function abortEncryptionConversion(): void
|
||||
return;
|
||||
}
|
||||
|
||||
@chmod($configPath, 0640);
|
||||
$configFile = file_get_contents($backupPath);
|
||||
@file_put_contents($configPath, $configFile);
|
||||
if ($configFile === false) {
|
||||
throw new RuntimeException(lang('Error.unable_to_read_env_file', ['filePath' => $backupPath]));
|
||||
}
|
||||
|
||||
if (!atomicWriteFile($configPath, $configFile)) {
|
||||
throw new RuntimeException(lang('Error.unable_to_persist_encryption_key', ['filePath' => $configPath]));
|
||||
}
|
||||
|
||||
log_message('info', "Restored $configPath from backup");
|
||||
}
|
||||
|
||||
|
||||
@@ -564,6 +564,34 @@ class security_helperTest extends CIUnitTestCase
|
||||
$this->assertSame("encryption.key='old'\n", file_get_contents($this->envPath));
|
||||
}
|
||||
|
||||
public function testAbortEncryptionConversionThrowsWhenBackupUnreadable(): void
|
||||
{
|
||||
// Force a failed backup read: file_exists() is true (it is a directory)
|
||||
// but file_get_contents() returns false. The restore must then throw
|
||||
// instead of silently writing an empty .env and destroying the active key.
|
||||
if (!is_dir(dirname($this->backupPath))) {
|
||||
mkdir(dirname($this->backupPath), 0750, true);
|
||||
}
|
||||
@unlink($this->backupPath);
|
||||
mkdir($this->backupPath);
|
||||
|
||||
file_put_contents($this->envPath, "encryption.key='new'\n");
|
||||
$before = (string) file_get_contents($this->envPath);
|
||||
|
||||
$threw = false;
|
||||
|
||||
try {
|
||||
abortEncryptionConversion();
|
||||
} catch (RuntimeException $e) {
|
||||
$threw = true;
|
||||
} finally {
|
||||
@rmdir($this->backupPath);
|
||||
}
|
||||
|
||||
$this->assertTrue($threw, 'a failed backup read must throw instead of failing silently');
|
||||
$this->assertSame($before, (string) file_get_contents($this->envPath), '.env must be left untouched when the backup is unreadable');
|
||||
}
|
||||
|
||||
public function testRemoveBackupDeletesBackupFile(): void
|
||||
{
|
||||
if (!is_dir(dirname($this->backupPath))) {
|
||||
|
||||
Reference in new issue
Block a user