From 4cf852d2fc706535eebc0e1893e60642b5435902 Mon Sep 17 00:00:00 2001 From: Iulian Onofrei <5748627+revolter@users.noreply.github.com> Date: Sat, 6 Jun 2026 17:35:09 +0300 Subject: [PATCH 1/9] Add missing explicit QSettings import It is used to load the dotenv file. --- src/sqlitedb.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sqlitedb.cpp b/src/sqlitedb.cpp index 329a57012..1a6ea70fa 100644 --- a/src/sqlitedb.cpp +++ b/src/sqlitedb.cpp @@ -16,6 +16,7 @@ #include #include #include +#include #include #include From 79742c897aa61d62a8373c15f4f364e53fee192b Mon Sep 17 00:00:00 2001 From: Iulian Onofrei <5748627+revolter@users.noreply.github.com> Date: Sat, 6 Jun 2026 17:39:58 +0300 Subject: [PATCH 2/9] Remove superfluous local variable --- src/sqlitedb.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sqlitedb.cpp b/src/sqlitedb.cpp index 1a6ea70fa..6abc49ec2 100644 --- a/src/sqlitedb.cpp +++ b/src/sqlitedb.cpp @@ -479,8 +479,7 @@ bool DBBrowserDB::tryEncryptionSettings(const QString& filePath, bool* encrypted // Being in a while loop, we don't want to check the same file multiple times if (!isDotenvChecked) { - QFile databaseFile(filePath); - QFileInfo databaseFileInfo(databaseFile); + QFileInfo databaseFileInfo(filePath); QString databaseDirectoryPath = databaseFileInfo.dir().path(); QString databaseFileName(databaseFileInfo.fileName()); From 7a5b409540fd94fa508a2af4c1dd75e9df362d39 Mon Sep 17 00:00:00 2001 From: Iulian Onofrei <5748627+revolter@users.noreply.github.com> Date: Sat, 6 Jun 2026 18:30:26 +0300 Subject: [PATCH 3/9] Fix local variables not being marked as constants --- src/sqlitedb.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/sqlitedb.cpp b/src/sqlitedb.cpp index 6abc49ec2..491986622 100644 --- a/src/sqlitedb.cpp +++ b/src/sqlitedb.cpp @@ -479,31 +479,31 @@ bool DBBrowserDB::tryEncryptionSettings(const QString& filePath, bool* encrypted // Being in a while loop, we don't want to check the same file multiple times if (!isDotenvChecked) { - QFileInfo databaseFileInfo(filePath); + const QFileInfo databaseFileInfo(filePath); - QString databaseDirectoryPath = databaseFileInfo.dir().path(); - QString databaseFileName(databaseFileInfo.fileName()); + const QString databaseDirectoryPath = databaseFileInfo.dir().path(); + const QString databaseFileName(databaseFileInfo.fileName()); - QString dotenvFilePath = databaseDirectoryPath + "/.env"; - QSettings dotenv(dotenvFilePath, QSettings::IniFormat); + const QString dotenvFilePath = databaseDirectoryPath + "/.env"; + const QSettings dotenv(dotenvFilePath, QSettings::IniFormat); - QVariant passwordValue = dotenv.value(databaseFileName); + const QVariant passwordValue = dotenv.value(databaseFileName); foundDotenvPassword = !passwordValue.isNull(); isDotenvChecked = true; if (foundDotenvPassword) { - std::string password = passwordValue.toString().toStdString(); + const std::string password = passwordValue.toString().toStdString(); - QVariant keyFormatValue = dotenv.value(databaseFileName + "_keyFormat", QVariant(CipherSettings::KeyFormats::Passphrase)); - CipherSettings::KeyFormats keyFormat = CipherSettings::getKeyFormat(keyFormatValue.toInt()); + const QVariant keyFormatValue = dotenv.value(databaseFileName + "_keyFormat", QVariant(CipherSettings::KeyFormats::Passphrase)); + const CipherSettings::KeyFormats keyFormat = CipherSettings::getKeyFormat(keyFormatValue.toInt()); - int pageSize = dotenv.value(databaseFileName + "_pageSize", enc_default_page_size).toInt(); - int kdfIterations = dotenv.value(databaseFileName + "_kdfIter", enc_default_kdf_iter).toInt(); - int plaintextHeaderSize = dotenv.value(databaseFileName + "_plaintextHeaderSize", enc_default_plaintext_header_size).toInt(); - std::string hmacAlgorithm = dotenv.value(databaseFileName + "_hmacAlgorithm", QString::fromStdString(enc_default_hmac_algorithm)).toString().toStdString(); - std::string kdfAlgorithm = dotenv.value(databaseFileName + "_kdfAlgorithm", QString::fromStdString(enc_default_kdf_algorithm)).toString().toStdString(); + const int pageSize = dotenv.value(databaseFileName + "_pageSize", enc_default_page_size).toInt(); + const int kdfIterations = dotenv.value(databaseFileName + "_kdfIter", enc_default_kdf_iter).toInt(); + const int plaintextHeaderSize = dotenv.value(databaseFileName + "_plaintextHeaderSize", enc_default_plaintext_header_size).toInt(); + const std::string hmacAlgorithm = dotenv.value(databaseFileName + "_hmacAlgorithm", QString::fromStdString(enc_default_hmac_algorithm)).toString().toStdString(); + const std::string kdfAlgorithm = dotenv.value(databaseFileName + "_kdfAlgorithm", QString::fromStdString(enc_default_kdf_algorithm)).toString().toStdString(); cipherSettings->setKeyFormat(keyFormat); cipherSettings->setPassword(password); From 9ea6cded1f3a5b933bb13fe99479b50cf3f365d6 Mon Sep 17 00:00:00 2001 From: Iulian Onofrei <5748627+revolter@users.noreply.github.com> Date: Sat, 6 Jun 2026 18:45:04 +0300 Subject: [PATCH 4/9] Fix superfluously trying to parse missing dotenv files --- src/sqlitedb.cpp | 51 +++++++++++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 24 deletions(-) diff --git a/src/sqlitedb.cpp b/src/sqlitedb.cpp index 491986622..b198eeab0 100644 --- a/src/sqlitedb.cpp +++ b/src/sqlitedb.cpp @@ -485,33 +485,36 @@ bool DBBrowserDB::tryEncryptionSettings(const QString& filePath, bool* encrypted const QString databaseFileName(databaseFileInfo.fileName()); const QString dotenvFilePath = databaseDirectoryPath + "/.env"; - const QSettings dotenv(dotenvFilePath, QSettings::IniFormat); - const QVariant passwordValue = dotenv.value(databaseFileName); + if (QFile::exists(dotenvFilePath)) { + const QSettings dotenv(dotenvFilePath, QSettings::IniFormat); - foundDotenvPassword = !passwordValue.isNull(); - isDotenvChecked = true; + const QVariant passwordValue = dotenv.value(databaseFileName); - if (foundDotenvPassword) - { - const std::string password = passwordValue.toString().toStdString(); - - const QVariant keyFormatValue = dotenv.value(databaseFileName + "_keyFormat", QVariant(CipherSettings::KeyFormats::Passphrase)); - const CipherSettings::KeyFormats keyFormat = CipherSettings::getKeyFormat(keyFormatValue.toInt()); - - const int pageSize = dotenv.value(databaseFileName + "_pageSize", enc_default_page_size).toInt(); - const int kdfIterations = dotenv.value(databaseFileName + "_kdfIter", enc_default_kdf_iter).toInt(); - const int plaintextHeaderSize = dotenv.value(databaseFileName + "_plaintextHeaderSize", enc_default_plaintext_header_size).toInt(); - const std::string hmacAlgorithm = dotenv.value(databaseFileName + "_hmacAlgorithm", QString::fromStdString(enc_default_hmac_algorithm)).toString().toStdString(); - const std::string kdfAlgorithm = dotenv.value(databaseFileName + "_kdfAlgorithm", QString::fromStdString(enc_default_kdf_algorithm)).toString().toStdString(); - - cipherSettings->setKeyFormat(keyFormat); - cipherSettings->setPassword(password); - cipherSettings->setPageSize(pageSize); - cipherSettings->setKdfIterations(kdfIterations); - cipherSettings->setHmacAlgorithm("HMAC_" + hmacAlgorithm); - cipherSettings->setKdfAlgorithm("PBKDF2_HMAC_" + kdfAlgorithm); - cipherSettings->setPlaintextHeaderSize(plaintextHeaderSize); + foundDotenvPassword = !passwordValue.isNull(); + isDotenvChecked = true; + + if (foundDotenvPassword) + { + const std::string password = passwordValue.toString().toStdString(); + + const QVariant keyFormatValue = dotenv.value(databaseFileName + "_keyFormat", QVariant(CipherSettings::KeyFormats::Passphrase)); + const CipherSettings::KeyFormats keyFormat = CipherSettings::getKeyFormat(keyFormatValue.toInt()); + + const int pageSize = dotenv.value(databaseFileName + "_pageSize", enc_default_page_size).toInt(); + const int kdfIterations = dotenv.value(databaseFileName + "_kdfIter", enc_default_kdf_iter).toInt(); + const int plaintextHeaderSize = dotenv.value(databaseFileName + "_plaintextHeaderSize", enc_default_plaintext_header_size).toInt(); + const std::string hmacAlgorithm = dotenv.value(databaseFileName + "_hmacAlgorithm", QString::fromStdString(enc_default_hmac_algorithm)).toString().toStdString(); + const std::string kdfAlgorithm = dotenv.value(databaseFileName + "_kdfAlgorithm", QString::fromStdString(enc_default_kdf_algorithm)).toString().toStdString(); + + cipherSettings->setKeyFormat(keyFormat); + cipherSettings->setPassword(password); + cipherSettings->setPageSize(pageSize); + cipherSettings->setKdfIterations(kdfIterations); + cipherSettings->setHmacAlgorithm("HMAC_" + hmacAlgorithm); + cipherSettings->setKdfAlgorithm("PBKDF2_HMAC_" + kdfAlgorithm); + cipherSettings->setPlaintextHeaderSize(plaintextHeaderSize); + } } } From 2f6e03dbc9ca9e02e5c92c6eb9343118b8ec33b5 Mon Sep 17 00:00:00 2001 From: Iulian Onofrei <5748627+revolter@users.noreply.github.com> Date: Sat, 6 Jun 2026 17:26:29 +0300 Subject: [PATCH 5/9] Add walking up parent directories when loading dotenv files Previously, only a `.env` file next to the database was checked. Now parent directories are searched until a matching password entry is found. Related-to: 3cdc65a6 Co-authored-by: Cursor --- src/sqlitedb.cpp | 66 +++++++++++++++++++++++++++--------------------- 1 file changed, 37 insertions(+), 29 deletions(-) diff --git a/src/sqlitedb.cpp b/src/sqlitedb.cpp index b198eeab0..c5c4acbe4 100644 --- a/src/sqlitedb.cpp +++ b/src/sqlitedb.cpp @@ -481,41 +481,49 @@ bool DBBrowserDB::tryEncryptionSettings(const QString& filePath, bool* encrypted if (!isDotenvChecked) { const QFileInfo databaseFileInfo(filePath); - const QString databaseDirectoryPath = databaseFileInfo.dir().path(); + QDir searchDirectory(databaseFileInfo.absoluteDir()); const QString databaseFileName(databaseFileInfo.fileName()); - const QString dotenvFilePath = databaseDirectoryPath + "/.env"; - - if (QFile::exists(dotenvFilePath)) { - const QSettings dotenv(dotenvFilePath, QSettings::IniFormat); - - const QVariant passwordValue = dotenv.value(databaseFileName); + while (true) + { + const QString dotenvFilePath = searchDirectory.filePath(".env"); + if (QFile::exists(dotenvFilePath)) + { + const QSettings dotenv(dotenvFilePath, QSettings::IniFormat); + const QVariant passwordValue = dotenv.value(databaseFileName); - foundDotenvPassword = !passwordValue.isNull(); - isDotenvChecked = true; + foundDotenvPassword = !passwordValue.isNull(); - if (foundDotenvPassword) - { - const std::string password = passwordValue.toString().toStdString(); - - const QVariant keyFormatValue = dotenv.value(databaseFileName + "_keyFormat", QVariant(CipherSettings::KeyFormats::Passphrase)); - const CipherSettings::KeyFormats keyFormat = CipherSettings::getKeyFormat(keyFormatValue.toInt()); - - const int pageSize = dotenv.value(databaseFileName + "_pageSize", enc_default_page_size).toInt(); - const int kdfIterations = dotenv.value(databaseFileName + "_kdfIter", enc_default_kdf_iter).toInt(); - const int plaintextHeaderSize = dotenv.value(databaseFileName + "_plaintextHeaderSize", enc_default_plaintext_header_size).toInt(); - const std::string hmacAlgorithm = dotenv.value(databaseFileName + "_hmacAlgorithm", QString::fromStdString(enc_default_hmac_algorithm)).toString().toStdString(); - const std::string kdfAlgorithm = dotenv.value(databaseFileName + "_kdfAlgorithm", QString::fromStdString(enc_default_kdf_algorithm)).toString().toStdString(); - - cipherSettings->setKeyFormat(keyFormat); - cipherSettings->setPassword(password); - cipherSettings->setPageSize(pageSize); - cipherSettings->setKdfIterations(kdfIterations); - cipherSettings->setHmacAlgorithm("HMAC_" + hmacAlgorithm); - cipherSettings->setKdfAlgorithm("PBKDF2_HMAC_" + kdfAlgorithm); - cipherSettings->setPlaintextHeaderSize(plaintextHeaderSize); + if (foundDotenvPassword) + { + const std::string password = passwordValue.toString().toStdString(); + + const QVariant keyFormatValue = dotenv.value(databaseFileName + "_keyFormat", QVariant(CipherSettings::KeyFormats::Passphrase)); + const CipherSettings::KeyFormats keyFormat = CipherSettings::getKeyFormat(keyFormatValue.toInt()); + + const int pageSize = dotenv.value(databaseFileName + "_pageSize", enc_default_page_size).toInt(); + const int kdfIterations = dotenv.value(databaseFileName + "_kdfIter", enc_default_kdf_iter).toInt(); + const int plaintextHeaderSize = dotenv.value(databaseFileName + "_plaintextHeaderSize", enc_default_plaintext_header_size).toInt(); + const std::string hmacAlgorithm = dotenv.value(databaseFileName + "_hmacAlgorithm", QString::fromStdString(enc_default_hmac_algorithm)).toString().toStdString(); + const std::string kdfAlgorithm = dotenv.value(databaseFileName + "_kdfAlgorithm", QString::fromStdString(enc_default_kdf_algorithm)).toString().toStdString(); + + cipherSettings->setKeyFormat(keyFormat); + cipherSettings->setPassword(password); + cipherSettings->setPageSize(pageSize); + cipherSettings->setKdfIterations(kdfIterations); + cipherSettings->setHmacAlgorithm("HMAC_" + hmacAlgorithm); + cipherSettings->setKdfAlgorithm("PBKDF2_HMAC_" + kdfAlgorithm); + cipherSettings->setPlaintextHeaderSize(plaintextHeaderSize); + + break; + } } + + if (!searchDirectory.cdUp()) + break; } + + isDotenvChecked = true; } if(foundDotenvPassword) From b81c21ee2f0f1997545713235e318c322671bda0 Mon Sep 17 00:00:00 2001 From: Iulian Onofrei <5748627+revolter@users.noreply.github.com> Date: Fri, 12 Jun 2026 08:57:29 +0300 Subject: [PATCH 6/9] Extract dotenv SQLCipher settings loader into a lambda Co-authored-by: Cursor --- src/sqlitedb.cpp | 64 ++++++++++++++++++++++++++---------------------- 1 file changed, 35 insertions(+), 29 deletions(-) diff --git a/src/sqlitedb.cpp b/src/sqlitedb.cpp index c5c4acbe4..b69cbdaac 100644 --- a/src/sqlitedb.cpp +++ b/src/sqlitedb.cpp @@ -484,39 +484,45 @@ bool DBBrowserDB::tryEncryptionSettings(const QString& filePath, bool* encrypted QDir searchDirectory(databaseFileInfo.absoluteDir()); const QString databaseFileName(databaseFileInfo.fileName()); + auto tryLoadDotenv = [&](const QString& dotenvFilePath) -> bool { + if (!QFile::exists(dotenvFilePath)) + return false; + + const QSettings dotenv(dotenvFilePath, QSettings::IniFormat); + const QVariant passwordValue = dotenv.value(databaseFileName); + + if (passwordValue.isNull()) + return false; + + const std::string password = passwordValue.toString().toStdString(); + + const QVariant keyFormatValue = dotenv.value(databaseFileName + "_keyFormat", QVariant(CipherSettings::KeyFormats::Passphrase)); + const CipherSettings::KeyFormats keyFormat = CipherSettings::getKeyFormat(keyFormatValue.toInt()); + + const int pageSize = dotenv.value(databaseFileName + "_pageSize", enc_default_page_size).toInt(); + const int kdfIterations = dotenv.value(databaseFileName + "_kdfIter", enc_default_kdf_iter).toInt(); + const int plaintextHeaderSize = dotenv.value(databaseFileName + "_plaintextHeaderSize", enc_default_plaintext_header_size).toInt(); + const std::string hmacAlgorithm = dotenv.value(databaseFileName + "_hmacAlgorithm", QString::fromStdString(enc_default_hmac_algorithm)).toString().toStdString(); + const std::string kdfAlgorithm = dotenv.value(databaseFileName + "_kdfAlgorithm", QString::fromStdString(enc_default_kdf_algorithm)).toString().toStdString(); + + cipherSettings->setKeyFormat(keyFormat); + cipherSettings->setPassword(password); + cipherSettings->setPageSize(pageSize); + cipherSettings->setKdfIterations(kdfIterations); + cipherSettings->setHmacAlgorithm("HMAC_" + hmacAlgorithm); + cipherSettings->setKdfAlgorithm("PBKDF2_HMAC_" + kdfAlgorithm); + cipherSettings->setPlaintextHeaderSize(plaintextHeaderSize); + + return true; + }; + while (true) { const QString dotenvFilePath = searchDirectory.filePath(".env"); - if (QFile::exists(dotenvFilePath)) + if (tryLoadDotenv(dotenvFilePath)) { - const QSettings dotenv(dotenvFilePath, QSettings::IniFormat); - const QVariant passwordValue = dotenv.value(databaseFileName); - - foundDotenvPassword = !passwordValue.isNull(); - - if (foundDotenvPassword) - { - const std::string password = passwordValue.toString().toStdString(); - - const QVariant keyFormatValue = dotenv.value(databaseFileName + "_keyFormat", QVariant(CipherSettings::KeyFormats::Passphrase)); - const CipherSettings::KeyFormats keyFormat = CipherSettings::getKeyFormat(keyFormatValue.toInt()); - - const int pageSize = dotenv.value(databaseFileName + "_pageSize", enc_default_page_size).toInt(); - const int kdfIterations = dotenv.value(databaseFileName + "_kdfIter", enc_default_kdf_iter).toInt(); - const int plaintextHeaderSize = dotenv.value(databaseFileName + "_plaintextHeaderSize", enc_default_plaintext_header_size).toInt(); - const std::string hmacAlgorithm = dotenv.value(databaseFileName + "_hmacAlgorithm", QString::fromStdString(enc_default_hmac_algorithm)).toString().toStdString(); - const std::string kdfAlgorithm = dotenv.value(databaseFileName + "_kdfAlgorithm", QString::fromStdString(enc_default_kdf_algorithm)).toString().toStdString(); - - cipherSettings->setKeyFormat(keyFormat); - cipherSettings->setPassword(password); - cipherSettings->setPageSize(pageSize); - cipherSettings->setKdfIterations(kdfIterations); - cipherSettings->setHmacAlgorithm("HMAC_" + hmacAlgorithm); - cipherSettings->setKdfAlgorithm("PBKDF2_HMAC_" + kdfAlgorithm); - cipherSettings->setPlaintextHeaderSize(plaintextHeaderSize); - - break; - } + foundDotenvPassword = true; + break; } if (!searchDirectory.cdUp()) From 8814966a8d23fa4d3ea0a94a29e73d855aa379b3 Mon Sep 17 00:00:00 2001 From: Iulian Onofrei <5748627+revolter@users.noreply.github.com> Date: Fri, 12 Jun 2026 08:58:10 +0300 Subject: [PATCH 7/9] Prefer DB4S dotenv files for SQLCipher passwords Use a local .db4s.env when present, otherwise preserve legacy local .env lookup before walking parents for .db4s.env. Co-authored-by: Cursor --- src/sqlitedb.cpp | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/src/sqlitedb.cpp b/src/sqlitedb.cpp index b69cbdaac..1449bfde1 100644 --- a/src/sqlitedb.cpp +++ b/src/sqlitedb.cpp @@ -481,7 +481,7 @@ bool DBBrowserDB::tryEncryptionSettings(const QString& filePath, bool* encrypted if (!isDotenvChecked) { const QFileInfo databaseFileInfo(filePath); - QDir searchDirectory(databaseFileInfo.absoluteDir()); + const QDir databaseDirectory(databaseFileInfo.absoluteDir()); const QString databaseFileName(databaseFileInfo.fileName()); auto tryLoadDotenv = [&](const QString& dotenvFilePath) -> bool { @@ -516,17 +516,25 @@ bool DBBrowserDB::tryEncryptionSettings(const QString& filePath, bool* encrypted return true; }; - while (true) + const QString db4sEnvPath = databaseDirectory.filePath(".db4s.env"); + const bool hasDB4SEnv = QFile::exists(db4sEnvPath); + + if (hasDB4SEnv) { - const QString dotenvFilePath = searchDirectory.filePath(".env"); - if (tryLoadDotenv(dotenvFilePath)) + foundDotenvPassword = tryLoadDotenv(db4sEnvPath); + } else { + // Deprecated: legacy .env in the database directory only. Remove once .db4s.env is adopted. + foundDotenvPassword = tryLoadDotenv(databaseDirectory.filePath(".env")); + + QDir searchDirectory(databaseDirectory); + while (!foundDotenvPassword && searchDirectory.cdUp()) { - foundDotenvPassword = true; - break; + if (tryLoadDotenv(searchDirectory.filePath(".db4s.env"))) + { + foundDotenvPassword = true; + break; + } } - - if (!searchDirectory.cdUp()) - break; } isDotenvChecked = true; From 45daafd5bad83c5604d035265b431d0e04d2a0f3 Mon Sep 17 00:00:00 2001 From: Iulian Onofrei <5748627+revolter@users.noreply.github.com> Date: Fri, 12 Jun 2026 09:24:38 +0300 Subject: [PATCH 8/9] Add preference for SQLCipher parent dotenv lookup Expose an opt-in setting for walking parent folders when searching for DB4S SQLCipher dotenv files. Co-authored-by: Cursor --- src/PreferencesDialog.cpp | 2 ++ src/PreferencesDialog.ui | 40 +++++++++++++++++++++++++++++---------- src/Settings.cpp | 4 ++++ 3 files changed, 36 insertions(+), 10 deletions(-) diff --git a/src/PreferencesDialog.cpp b/src/PreferencesDialog.cpp index 7c98f9905..775acc654 100644 --- a/src/PreferencesDialog.cpp +++ b/src/PreferencesDialog.cpp @@ -100,6 +100,7 @@ void PreferencesDialog::loadSettings() ui->spinStructureFontSize->setValue(Settings::getValue("db", "fontsize").toInt()); ui->watcherCheckBox->setChecked(Settings::getValue("db", "watcher").toBool()); + ui->checkSQLCipherParentDotenvLookup->setChecked(Settings::getValue("db", "sqlcipherparentdotenvlookup").toBool()); // Gracefully handle the preferred Data Browser font not being available int matchingFont = ui->comboDataBrowserFont->findText(Settings::getValue("databrowser", "font").toString(), Qt::MatchExactly); @@ -199,6 +200,7 @@ void PreferencesDialog::saveSettings(bool accept) Settings::setValue("db", "defaultfieldtype", ui->defaultFieldTypeComboBox->currentIndex()); Settings::setValue("db", "fontsize", ui->spinStructureFontSize->value()); Settings::setValue("db", "watcher", ui->watcherCheckBox->isChecked()); + Settings::setValue("db", "sqlcipherparentdotenvlookup", ui->checkSQLCipherParentDotenvLookup->isChecked()); Settings::setValue("checkversion", "enabled", ui->checkUpdates->isChecked()); diff --git a/src/PreferencesDialog.ui b/src/PreferencesDialog.ui index 45f9f1ee3..bef09cc63 100644 --- a/src/PreferencesDialog.ui +++ b/src/PreferencesDialog.ui @@ -752,16 +752,36 @@ in new project file - - - - enabled - - - true - - - + + + + enabled + + + true + + + + + + + When enabled, DB4S searches parent folders for .db4s.env files, which can slow down opening encrypted databases. + + + Search parent folders for .db4s.env SQLCipher passwords + + + checkSQLCipherParentDotenvLookup + + + + + + + enabled + + + diff --git a/src/Settings.cpp b/src/Settings.cpp index 6fe1b5d7f..856680d32 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -163,6 +163,10 @@ QVariant Settings::getDefaultValue(const std::string& group, const std::string& if(group == "db" && name == "watcher") return false; + // db/sqlcipherparentdotenvlookup? + if(group == "db" && name == "sqlcipherparentdotenvlookup") + return false; + // exportcsv/firstrowheader? if(group == "exportcsv" && name == "firstrowheader") return true; From 23806c696328b7f71e312752611794247c54d3fc Mon Sep 17 00:00:00 2001 From: Iulian Onofrei <5748627+revolter@users.noreply.github.com> Date: Fri, 12 Jun 2026 09:20:26 +0300 Subject: [PATCH 9/9] Gate SQLCipher parent dotenv lookup behind preference Keep parent .db4s.env searching opt-in so encrypted database opens avoid unnecessary directory walking by default. Co-authored-by: Cursor --- src/sqlitedb.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/sqlitedb.cpp b/src/sqlitedb.cpp index 1449bfde1..6e58e94ab 100644 --- a/src/sqlitedb.cpp +++ b/src/sqlitedb.cpp @@ -526,13 +526,17 @@ bool DBBrowserDB::tryEncryptionSettings(const QString& filePath, bool* encrypted // Deprecated: legacy .env in the database directory only. Remove once .db4s.env is adopted. foundDotenvPassword = tryLoadDotenv(databaseDirectory.filePath(".env")); - QDir searchDirectory(databaseDirectory); - while (!foundDotenvPassword && searchDirectory.cdUp()) + // Parent folder search is opt-in because it can slow down opening encrypted databases. + if (!foundDotenvPassword && Settings::getValue("db", "sqlcipherparentdotenvlookup").toBool()) { - if (tryLoadDotenv(searchDirectory.filePath(".db4s.env"))) + QDir searchDirectory(databaseDirectory); + while (searchDirectory.cdUp()) { - foundDotenvPassword = true; - break; + if (tryLoadDotenv(searchDirectory.filePath(".db4s.env"))) + { + foundDotenvPassword = true; + break; + } } } }