Release 2.5.3

Fixed

- Fix a possible database lockout when removing a YubiKey from a KDBX 3.1 database [#4147]
- Fix crash if Auto-Type is performed on a new entry [#4150]
- Fix crash when all entries are deleted from a group [#4156]
- Improve the reliability of clipboard clearing on Gnome [#4165]
- Do not check cmd:// URLs for valid URL syntax anymore [#4172]
- Prevent unnecessary merges for databases on network shares [#4153]
- Browser: Prevent native messaging proxy from blocking application shutdown [#4155]
- Browser: Improve website URL matching [#4134, #4177]

Added

- Browser: Enable support for Chromium-based Edge Browser [#3359]
This commit is contained in:
Janek Bevendorff 2020-01-19 21:43:19 +01:00
commit f8c962bd25
No known key found for this signature in database
GPG Key ID: 2FDEB0D40BCA5E11
35 changed files with 478 additions and 157 deletions

View File

@ -1,5 +1,22 @@
# Changelog
## 2.5.3 (2020-01-19)
### Fixed
- Fix a possible database lockout when removing a YubiKey from a KDBX 3.1 database [#4147]
- Fix crash if Auto-Type is performed on a new entry [#4150]
- Fix crash when all entries are deleted from a group [#4156]
- Improve the reliability of clipboard clearing on Gnome [#4165]
- Do not check cmd:// URLs for valid URL syntax anymore [#4172]
- Prevent unnecessary merges for databases on network shares [#4153]
- Browser: Prevent native messaging proxy from blocking application shutdown [#4155]
- Browser: Improve website URL matching [#4134, #4177]
### Added
- Browser: Enable support for Chromium-based Edge Browser [#3359]
## 2.5.2 (2020-01-04)
### Added

View File

@ -95,7 +95,7 @@ endif()
set(KEEPASSXC_VERSION_MAJOR "2")
set(KEEPASSXC_VERSION_MINOR "5")
set(KEEPASSXC_VERSION_PATCH "2")
set(KEEPASSXC_VERSION_PATCH "3")
set(KEEPASSXC_VERSION "${KEEPASSXC_VERSION_MAJOR}.${KEEPASSXC_VERSION_MINOR}.${KEEPASSXC_VERSION_PATCH}")
set(OVERRIDE_VERSION "" CACHE STRING "Override the KeePassXC Version for Snapshot builds")

View File

@ -29,7 +29,7 @@ so please check out your distribution's package list to see if KeePassXC is avai
- Using website favicons as entry icons
- Merging of databases
- Automatic reload when the database changed on disk
- Browser integration with KeePassXC-Browser using [native messaging](https://developer.chrome.com/extensions/nativeMessaging) for [Mozilla Firefox](https://addons.mozilla.org/en-US/firefox/addon/keepassxc-browser/) and [Google Chrome, Chromium, Vivaldi, or Brave](https://chrome.google.com/webstore/detail/keepassxc-browser/oboonakemofpalcgghocfoadofidjkkk)
- Browser integration with KeePassXC-Browser using [native messaging](https://developer.chrome.com/extensions/nativeMessaging) for [Mozilla Firefox](https://addons.mozilla.org/en-US/firefox/addon/keepassxc-browser/) and [Google Chrome, Chromium, Vivaldi, or Brave](https://chrome.google.com/webstore/detail/keepassxc-browser/oboonakemofpalcgghocfoadofidjkkk) and [Microsoft Edge](https://microsoftedge.microsoft.com/addons/detail/pdffhmdngciaglkoonimfcmckehcpafo)
- Synchronize passwords using KeeShare. See [Using Sharing](./docs/QUICKSTART.md#using-sharing) for more details.
- Many bug fixes

View File

@ -50,6 +50,20 @@
</screenshots>
<releases>
<release version="2.5.3" date="2020-01-19">
<description>
<ul>
<li>Fix a possible database lockout when removing a YubiKey from a KDBX 3.1 database [#4147]</li>
<li>Fix crash if Auto-Type is performed on a new entry [#4150]</li>
<li>Fix crash when all entries are deleted from a group [#4156]</li>
<li>Improve the reliability of clipboard clearing on Gnome [#4165]</li>
<li>Do not check cmd:// URLs for valid URL syntax anymore [#4172]</li>
<li>Prevent unnecessary merges for databases on network shares [#4153]</li>
<li>Browser: Prevent native messaging proxy from blocking application shutdown [#4155]</li>
<li>Browser: Improve website URL matching [#4134, #4177]</li>
</ul>
</description>
</release>
<release version="2.5.2" date="2020-01-04">
<description>
<ul>

View File

@ -1112,7 +1112,7 @@ Please consider generating a new key file.</source>
</message>
<message>
<source>Select key file</source>
<translation>Seleccioneu el fitxer de clau</translation>
<translation>Selecciona el fitxer de clau</translation>
</message>
<message>
<source>Failed to open key file: %1</source>
@ -1195,7 +1195,7 @@ To prevent this error from appearing, you must go to &quot;Database Settings / S
</message>
<message>
<source>Enter Additional Credentials (if any):</source>
<translation type="unfinished"/>
<translation>Introduïu credencials addionals (si cal):</translation>
</message>
<message>
<source>&lt;p&gt;You can use a hardware security key such as a &lt;strong&gt;YubiKey&lt;/strong&gt; or &lt;strong&gt;OnlyKey&lt;/strong&gt; with slots configured for HMAC-SHA1.&lt;/p&gt;
@ -1216,7 +1216,7 @@ To prevent this error from appearing, you must go to &quot;Database Settings / S
</message>
<message>
<source>Select key file...</source>
<translation type="unfinished"/>
<translation>Selecciona el fitxer clau...</translation>
</message>
<message>
<source>Cannot use database file as key file</source>
@ -2541,7 +2541,7 @@ Voleu deshabilitar el desat segur i provar-ho un altre cop?</translation>
</message>
<message>
<source>Toggle password generator</source>
<translation type="unfinished"/>
<translation>Commuta el generador de contrasenyes</translation>
</message>
<message>
<source>Password field</source>
@ -2829,7 +2829,7 @@ Supported extensions are: %1.</source>
</message>
<message>
<source>Toggle password generator</source>
<translation type="unfinished"/>
<translation>Commuta el generador de contrasenyes</translation>
</message>
<message>
<source>Clear fields</source>
@ -3468,7 +3468,7 @@ You can enable the DuckDuckGo website icon service in the security section of th
</message>
<message>
<source>Downloading...</source>
<translation type="unfinished"/>
<translation>Descarregant...</translation>
</message>
<message>
<source>Ok</source>
@ -3480,7 +3480,7 @@ You can enable the DuckDuckGo website icon service in the security section of th
</message>
<message>
<source>Download Failed</source>
<translation type="unfinished"/>
<translation>La descàrrega ha fallat</translation>
</message>
<message>
<source>Downloading favicons (%1/%2)...</source>
@ -3897,7 +3897,7 @@ Line %2, column %3</source>
</message>
<message>
<source>Import KeePass1 Database</source>
<translation type="unfinished"/>
<translation>Importa una base de dades de KeePass 1</translation>
</message>
</context>
<context>
@ -4093,7 +4093,7 @@ If this reoccurs, then your database file may be corrupt.</source>
</message>
<message>
<source>Imported from</source>
<translation type="unfinished"/>
<translation>Importat de</translation>
</message>
<message>
<source>Exported to</source>
@ -4195,11 +4195,11 @@ Message: %2</source>
</message>
<message>
<source>Select a key file</source>
<translation>Seleccioneu un arxiu clau</translation>
<translation>Selecciona un fitxer clau</translation>
</message>
<message>
<source>Key file selection</source>
<translation>Selecció del fitxer clau</translation>
<translation>Selecciona el fitxer clau...</translation>
</message>
<message>
<source>Browse for key file</source>
@ -4267,7 +4267,7 @@ Are you sure you want to continue with this file?</source>
</message>
<message>
<source>&amp;About</source>
<translation>&amp;Sobre</translation>
<translation>Quant &amp;a</translation>
</message>
<message>
<source>&amp;Open database...</source>
@ -4412,7 +4412,7 @@ We recommend you use the AppImage available on our downloads page.</source>
</message>
<message>
<source>&amp;Import</source>
<translation>&amp; Importa</translation>
<translation>&amp;Importa</translation>
</message>
<message>
<source>Copy att&amp;ribute...</source>
@ -4480,7 +4480,7 @@ We recommend you use the AppImage available on our downloads page.</source>
</message>
<message>
<source>Open &amp;URL</source>
<translation type="unfinished"/>
<translation>Obre la &amp;URL</translation>
</message>
<message>
<source>KeePass 1 database...</source>
@ -4488,7 +4488,7 @@ We recommend you use the AppImage available on our downloads page.</source>
</message>
<message>
<source>Import a KeePass 1 database</source>
<translation type="unfinished"/>
<translation>Importa una base de dades de KeePass 1</translation>
</message>
<message>
<source>CSV file...</source>
@ -4530,7 +4530,7 @@ Podeu esperar alguns errors i incidències menors. Aquesta versió no està pens
</message>
<message>
<source>&amp;Check for Updates...</source>
<translation type="unfinished"/>
<translation>&amp;Comprova si hi ha actualitzacions...</translation>
</message>
<message>
<source>Downlo&amp;ad all favicons</source>
@ -4538,19 +4538,19 @@ Podeu esperar alguns errors i incidències menors. Aquesta versió no està pens
</message>
<message>
<source>Sort &amp;A-Z</source>
<translation type="unfinished"/>
<translation>Ordena &amp;A-Z</translation>
</message>
<message>
<source>Sort &amp;Z-A</source>
<translation type="unfinished"/>
<translation>Ordena &amp;Z-A</translation>
</message>
<message>
<source>&amp;Password Generator</source>
<translation type="unfinished"/>
<translation>Generador de contrasenyes</translation>
</message>
<message>
<source>Download favicon</source>
<translation>Descarregua el favicon</translation>
<translation>Descarrega el favicon</translation>
</message>
<message>
<source>&amp;Export to HTML file...</source>
@ -4566,15 +4566,15 @@ Podeu esperar alguns errors i incidències menors. Aquesta versió no està pens
</message>
<message>
<source>&amp;Getting Started</source>
<translation type="unfinished"/>
<translation>Primers passos</translation>
</message>
<message>
<source>Open Getting Started Guide PDF</source>
<translation type="unfinished"/>
<translation>Obre el PDF de la guia Primers passos</translation>
</message>
<message>
<source>&amp;Online Help...</source>
<translation type="unfinished"/>
<translation>Ajuda &amp;online...</translation>
</message>
<message>
<source>Go to online documentation (opens browser)</source>
@ -4582,15 +4582,15 @@ Podeu esperar alguns errors i incidències menors. Aquesta versió no està pens
</message>
<message>
<source>&amp;User Guide</source>
<translation type="unfinished"/>
<translation>Guia d&apos;&amp;Usuari</translation>
</message>
<message>
<source>Open User Guide PDF</source>
<translation type="unfinished"/>
<translation>Obre el PDF de la Guia d&apos;usuari</translation>
</message>
<message>
<source>&amp;Keyboard Shortcuts</source>
<translation type="unfinished"/>
<translation>Dreceres de teclat</translation>
</message>
</context>
<context>
@ -4944,7 +4944,7 @@ Podeu esperar alguns errors i incidències menors. Aquesta versió no està pens
</message>
<message>
<source>Toggle password generator</source>
<translation type="unfinished"/>
<translation>Commuta la generació de contrasenyes</translation>
</message>
</context>
<context>
@ -6151,7 +6151,7 @@ Nucli: %3 %4</translation>
</message>
<message>
<source>Successfully imported database.</source>
<translation type="unfinished"/>
<translation>La base de dades s&apos;ha importat correctament.</translation>
</message>
<message>
<source>Unknown command %1</source>
@ -6438,7 +6438,7 @@ Nucli: %3 %4</translation>
</message>
<message>
<source>Enable KeepassXC Freedesktop.org Secret Service integration</source>
<translation type="unfinished"/>
<translation>Habilita la integració amb el Freedesktop.org Secret Service</translation>
</message>
<message>
<source>General</source>
@ -6446,7 +6446,7 @@ Nucli: %3 %4</translation>
</message>
<message>
<source>Show notification when credentials are requested</source>
<translation type="unfinished"/>
<translation>Mostra una notificació quan es demanen credencials</translation>
</message>
<message>
<source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If recycle bin is enabled for the database, entries will be moved to recycle bin directly. Otherwise, they will be deleted without confirmation.&lt;/p&gt;&lt;p&gt;You will still be prompted if any entries are referenced by others.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
@ -6454,7 +6454,7 @@ Nucli: %3 %4</translation>
</message>
<message>
<source>Don&apos;t confirm when entries are deleted by clients.</source>
<translation type="unfinished"/>
<translation>No confirmar quan les entrades són esborrades pel clients.</translation>
</message>
<message>
<source>Exposed database groups:</source>
@ -6470,19 +6470,19 @@ Nucli: %3 %4</translation>
</message>
<message>
<source>Manage</source>
<translation type="unfinished"/>
<translation>Gestiona</translation>
</message>
<message>
<source>Authorization</source>
<translation type="unfinished"/>
<translation>Autorització</translation>
</message>
<message>
<source>These applications are currently connected:</source>
<translation type="unfinished"/>
<translation>Aquestes aplicacions estan ara connectades:</translation>
</message>
<message>
<source>Application</source>
<translation type="unfinished"/>
<translation>Aplicació</translation>
</message>
<message>
<source>Disconnect</source>
@ -6650,7 +6650,7 @@ Nucli: %3 %4</translation>
</message>
<message>
<source>Only show warnings and errors</source>
<translation type="unfinished"/>
<translation>Mostra només els avisos i els errors</translation>
</message>
<message>
<source>Key</source>

View File

@ -7052,7 +7052,7 @@ Example: JBSWY3DPEHPK3PXP</source>
</message>
<message>
<source>You&apos;re up-to-date!</source>
<translation>Version aktuel</translation>
<translation>Version aktuell</translation>
</message>
<message>
<source>KeePassXC %1 is currently the newest version available</source>

View File

@ -739,10 +739,6 @@ Please select the correct database for saving credentials.</translation>
<source>Please see special instructions for browser extension use below</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>KeePassXC-Browser is needed for the browser integration to work. &lt;br /&gt;Download it for %1 and %2. %3</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&amp;Brave</source>
<translation type="unfinished"></translation>
@ -787,6 +783,14 @@ Please select the correct database for saving credentials.</translation>
<source>&lt;b&gt;Warning&lt;/b&gt;, the keepassxc-proxy application was not found!&lt;br /&gt;Please check the KeePassXC installation directory or confirm the custom path in advanced options.&lt;br /&gt;Browser integration WILL NOT WORK without the proxy application.&lt;br /&gt;Expected Path: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>&amp;Edge</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>KeePassXC-Browser is needed for the browser integration to work. &lt;br /&gt;Download it for %1 and %2 and %3. %4</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>BrowserService</name>

View File

@ -4519,7 +4519,7 @@ Wij raden je aan om de AppImage te gebruiken welke beschikbaar is op onze downlo
</message>
<message>
<source>Perform &amp;Auto-Type</source>
<translation>Uitvoeren &amp;Auto-Type</translation>
<translation>&amp;Auto-type uitvoeren</translation>
</message>
<message>
<source>Open &amp;URL</source>

View File

@ -878,7 +878,11 @@ Mevcut ayarlarınızı şimdi taşımak ister misiniz?</translation>
Give the connection a unique name or ID, for example:
chrome-laptop.</source>
<translation type="unfinished"/>
<translation>Aşağıdaki veritabanı için bir ilişkilendirme isteği aldınız:
%1
Bağlantıya benzersiz bir ad veya kimlik verin, örneğin:
linux-laptop.</translation>
</message>
</context>
<context>
@ -990,7 +994,7 @@ chrome-laptop.</source>
</message>
<message>
<source>Text qualification</source>
<translation type="unfinished"/>
<translation>Metin niteliği</translation>
</message>
<message>
<source>Field separation</source>
@ -1227,7 +1231,7 @@ To prevent this error from appearing, you must go to &quot;Database Settings / S
</message>
<message>
<source>Cannot use database file as key file</source>
<translation type="unfinished"/>
<translation>Veritabanı dosyası anahtar dosyası olarak kullanılamaz</translation>
</message>
<message>
<source>You cannot use your database file as a key file.
@ -1389,11 +1393,11 @@ Tarayıcı eklentisiyle uyumluluğu korumak için bu gereklidir.</translation>
</message>
<message>
<source>Stored browser keys</source>
<translation type="unfinished"/>
<translation>Saklanan tarayıcı tuşları</translation>
</message>
<message>
<source>Remove selected key</source>
<translation type="unfinished"/>
<translation>Seçili anahtarı kaldır</translation>
</message>
</context>
<context>
@ -1539,11 +1543,11 @@ Eğer bu sayı ile devam ederseniz, veritabanınız çok kolay çözülerek kır
</message>
<message>
<source>Change existing decryption time</source>
<translation type="unfinished"/>
<translation>Mevcut şifre çözme süresini değiştir</translation>
</message>
<message>
<source>Decryption time in seconds</source>
<translation type="unfinished"/>
<translation>Saniyede şifre çözme süresi</translation>
</message>
<message>
<source>Database format</source>
@ -1551,11 +1555,11 @@ Eğer bu sayı ile devam ederseniz, veritabanınız çok kolay çözülerek kır
</message>
<message>
<source>Encryption algorithm</source>
<translation type="unfinished"/>
<translation>Şifreleme algoritması</translation>
</message>
<message>
<source>Key derivation function</source>
<translation type="unfinished"/>
<translation>Anahtar türetme işlevi</translation>
</message>
<message>
<source>Transform rounds</source>
@ -1567,14 +1571,14 @@ Eğer bu sayı ile devam ederseniz, veritabanınız çok kolay çözülerek kır
</message>
<message>
<source>Parallelism</source>
<translation type="unfinished"/>
<translation>Benzerlik</translation>
</message>
</context>
<context>
<name>DatabaseSettingsWidgetFdoSecrets</name>
<message>
<source>Exposed Entries</source>
<translation type="unfinished"/>
<translation>Maruz Kalan Girdiler</translation>
</message>
<message>
<source>Don&apos;t e&amp;xpose this database</source>
@ -1582,7 +1586,7 @@ Eğer bu sayı ile devam ederseniz, veritabanınız çok kolay çözülerek kır
</message>
<message>
<source>Expose entries &amp;under this group:</source>
<translation type="unfinished"/>
<translation>Bu kümenin &amp;altındaki girdileri göster:</translation>
</message>
<message>
<source>Enable fd.o Secret Service to access these settings.</source>
@ -1653,11 +1657,11 @@ Eğer bu sayı ile devam ederseniz, veritabanınız çok kolay çözülerek kır
</message>
<message>
<source>Maximum size of history per entry</source>
<translation type="unfinished"/>
<translation>Girdi başına azami geçmiş boyutu</translation>
</message>
<message>
<source>Delete Recycle Bin</source>
<translation type="unfinished"/>
<translation>Geri Dönüşüm Kutusunu Sil</translation>
</message>
<message>
<source>Do you want to delete the current recycle bin and all its contents?
@ -1767,7 +1771,7 @@ Parola olmadan devam etmek istediğinize emin misiniz?</translation>
</message>
<message>
<source>Hover over lines with error icons for further information.</source>
<translation type="unfinished"/>
<translation>Daha fazla bilgi için farenizi hata simgeli satırların üzerine getirin.</translation>
</message>
<message>
<source>Name</source>
@ -1803,7 +1807,7 @@ Parola olmadan devam etmek istediğinize emin misiniz?</translation>
</message>
<message>
<source>no</source>
<translation type="unfinished"/>
<translation>hayır</translation>
</message>
<message>
<source>The database was modified, but the changes have not yet been saved to disk.</source>
@ -1811,15 +1815,15 @@ Parola olmadan devam etmek istediğinize emin misiniz?</translation>
</message>
<message>
<source>Number of groups</source>
<translation type="unfinished"/>
<translation>Küme sayısı</translation>
</message>
<message>
<source>Number of entries</source>
<translation type="unfinished"/>
<translation>Girdilerin sayısı</translation>
</message>
<message>
<source>Number of expired entries</source>
<translation type="unfinished"/>
<translation>Süresi dolmuş girdi sayısı</translation>
</message>
<message>
<source>The database contains entries that have expired.</source>
@ -1827,35 +1831,35 @@ Parola olmadan devam etmek istediğinize emin misiniz?</translation>
</message>
<message>
<source>Unique passwords</source>
<translation type="unfinished"/>
<translation>Benzersiz parolalar</translation>
</message>
<message>
<source>Non-unique passwords</source>
<translation type="unfinished"/>
<translation>Benzersiz olmayan parolalar</translation>
</message>
<message>
<source>More than 10% of passwords are reused. Use unique passwords when possible.</source>
<translation type="unfinished"/>
<translation>Parolaların %10&apos;undan fazlası yeniden kullanılır. Mümkünse benzersiz parolalar kullanın.</translation>
</message>
<message>
<source>Maximum password reuse</source>
<translation type="unfinished"/>
<translation>Azami parola kullanımı</translation>
</message>
<message>
<source>Some passwords are used more than three times. Use unique passwords when possible.</source>
<translation type="unfinished"/>
<translation>Bazı parolalar üç kereden fazla kullanılır. Mümkünse benzersiz parolalar kullanın.</translation>
</message>
<message>
<source>Number of short passwords</source>
<translation type="unfinished"/>
<translation>Kısa parola sayısı</translation>
</message>
<message>
<source>Recommended minimum password length is at least 8 characters.</source>
<translation type="unfinished"/>
<translation>Önerilen asgari parola uzunluğu en az 8 karakterdir.</translation>
</message>
<message>
<source>Number of weak passwords</source>
<translation type="unfinished"/>
<translation>Zayıf parola sayısı</translation>
</message>
<message>
<source>Recommend using long, randomized passwords with a rating of &apos;good&apos; or &apos;excellent&apos;.</source>
@ -1971,7 +1975,7 @@ Bu kesinlikle bir hatadır, lütfen geliştiricilere bildirin.</translation>
</message>
<message>
<source>You are about to export your database to an unencrypted file. This will leave your passwords and sensitive information vulnerable! Are you sure you want to continue?</source>
<translation type="unfinished"/>
<translation>Veritabanınızı şifrelenmemiş bir dosyaya vermek üzeresiniz. Bu, parolalarınızı ve hassas bilgilerinizi savunmasız bırakacaktır! Devam etmek istediğine emin misin?</translation>
</message>
</context>
<context>
@ -2350,7 +2354,7 @@ Güvenli kaydetme devre dışı bırakılsın ve tekrar denensin mi?</translatio
</message>
<message>
<source>Show a protected attribute</source>
<translation type="unfinished"/>
<translation>Korumalı bir özelliği göster</translation>
</message>
<message>
<source>Foreground color selection</source>
@ -2429,7 +2433,7 @@ Güvenli kaydetme devre dışı bırakılsın ve tekrar denensin mi?</translatio
</message>
<message>
<source>Custom Auto-Type sequence for this window</source>
<translation type="unfinished"/>
<translation>Bu pencere için özel Otomatik Yazım sırası</translation>
</message>
</context>
<context>
@ -2578,7 +2582,7 @@ Güvenli kaydetme devre dışı bırakılsın ve tekrar denensin mi?</translatio
</message>
<message>
<source>Expiration Presets</source>
<translation type="unfinished"/>
<translation>Son Kullanma Önayarları</translation>
</message>
<message>
<source>Expiration presets</source>
@ -2678,11 +2682,11 @@ Güvenli kaydetme devre dışı bırakılsın ve tekrar denensin mi?</translatio
</message>
<message>
<source>Remove key from agent after specified seconds</source>
<translation type="unfinished"/>
<translation>Belirtilen saniye sonra anahtarı aracıdan kaldır</translation>
</message>
<message>
<source>Browser for key file</source>
<translation type="unfinished"/>
<translation>Anahtar dosyasına göz at</translation>
</message>
<message>
<source>External key file</source>
@ -2690,7 +2694,7 @@ Güvenli kaydetme devre dışı bırakılsın ve tekrar denensin mi?</translatio
</message>
<message>
<source>Select attachment file</source>
<translation type="unfinished"/>
<translation>Ek dosyasını seç</translation>
</message>
</context>
<context>
@ -3070,7 +3074,7 @@ Bu etkilenen eklentilerin bozulmasına neden olabilir.</translation>
</message>
<message>
<source>Unique ID</source>
<translation type="unfinished"/>
<translation>Benzersiz ID</translation>
</message>
<message>
<source>Plugin data</source>
@ -4124,11 +4128,11 @@ If this reoccurs, then your database file may be corrupt.</source>
</message>
<message>
<source>Exported to</source>
<translation type="unfinished"/>
<translation>Dışa aktarıldı</translation>
</message>
<message>
<source>Synchronized with</source>
<translation type="unfinished"/>
<translation>Eşitlendi</translation>
</message>
</context>
<context>
@ -4803,7 +4807,7 @@ Bazı hatalar ve küçük sorunlar olabilir, bu sürüm şu an dağıtımda değ
<name>OpVaultReader</name>
<message>
<source>Directory .opvault must exist</source>
<translation type="unfinished"/>
<translation>Dizin .opvault mevcut olmalıdır</translation>
</message>
<message>
<source>Directory .opvault must be readable</source>
@ -6069,7 +6073,7 @@ MİB mimarisi: %2
</message>
<message>
<source>Cannot generate a password and prompt at the same time!</source>
<translation type="unfinished"/>
<translation>Parola ve komut istemi aynı anda oluşturulamaz!</translation>
</message>
<message>
<source>Adds a new group to a database.</source>
@ -6117,7 +6121,7 @@ MİB mimarisi: %2
</message>
<message>
<source>Display this help.</source>
<translation type="unfinished"/>
<translation>Bu yardımı görüntüle.</translation>
</message>
<message>
<source>Yubikey slot used to encrypt the database.</source>
@ -6189,7 +6193,7 @@ MİB mimarisi: %2
</message>
<message>
<source>Successfully imported database.</source>
<translation type="unfinished"/>
<translation>Veritabanı başarıyla içe aktarıldı.</translation>
</message>
<message>
<source>Unknown command %1</source>
@ -6245,7 +6249,7 @@ MİB mimarisi: %2
</message>
<message>
<source>Path of the group to remove.</source>
<translation type="unfinished"/>
<translation>Kaldırılacak kümenin yolu.</translation>
</message>
<message>
<source>Cannot remove root group from database.</source>
@ -6285,7 +6289,7 @@ MİB mimarisi: %2
</message>
<message>
<source>Enter password to encrypt database (optional): </source>
<translation type="unfinished"/>
<translation>Veritabanını şifrelemek için parola gir (isteğe bağlı):</translation>
</message>
<message>
<source>HIBP file, line %1: parse error</source>
@ -6496,7 +6500,7 @@ MİB mimarisi: %2
</message>
<message>
<source>Exposed database groups:</source>
<translation type="unfinished"/>
<translation>Maruz kalan veritabanı kümeleri:</translation>
</message>
<message>
<source>File Name</source>
@ -6532,7 +6536,7 @@ MİB mimarisi: %2
</message>
<message>
<source>Edit database settings</source>
<translation type="unfinished"/>
<translation>Veritabanı ayarlarını düzenle</translation>
</message>
<message>
<source>Unlock database</source>
@ -6700,7 +6704,7 @@ MİB mimarisi: %2
</message>
<message>
<source>Generate new certificate</source>
<translation type="unfinished"/>
<translation>Yeni sertifika oluştur</translation>
</message>
<message>
<source>Import existing certificate</source>

View File

@ -1,5 +1,5 @@
name: keepassxc
version: 2.5.2
version: 2.5.3
grade: stable
summary: Community-driven port of the Windows application “KeePass Password Safe”
description: |

View File

@ -588,12 +588,12 @@ QList<AutoTypeAction*> AutoType::createActionFromTemplate(const QString& tmpl, c
QList<QString> AutoType::autoTypeSequences(const Entry* entry, const QString& windowTitle)
{
QList<QString> sequenceList;
const Group* group = entry->group();
if (!entry->autoTypeEnabled()) {
if (!group || !entry->autoTypeEnabled()) {
return sequenceList;
}
const Group* group = entry->group();
do {
if (group->autoTypeEnabled() == Group::Disable) {
return sequenceList;

View File

@ -44,10 +44,11 @@ BrowserOptionDialog::BrowserOptionDialog(QWidget* parent)
m_ui->extensionLabel->setOpenExternalLinks(true);
m_ui->extensionLabel->setText(
tr("KeePassXC-Browser is needed for the browser integration to work. <br />Download it for %1 and %2. %3")
tr("KeePassXC-Browser is needed for the browser integration to work. <br />Download it for %1 and %2 and %3. %4")
.arg("<a href=\"https://addons.mozilla.org/firefox/addon/keepassxc-browser/\">Firefox</a>",
"<a href=\"https://chrome.google.com/webstore/detail/keepassxc-browser/oboonakemofpalcgghocfoadofidjkkk\">"
"Google Chrome / Chromium / Vivaldi / Brave</a>",
"<a href=\"https://microsoftedge.microsoft.com/addons/detail/pdffhmdngciaglkoonimfcmckehcpafo\">Microsoft Edge</a>",
snapInstructions));
// clang-format on
@ -123,6 +124,7 @@ void BrowserOptionDialog::loadSettings()
m_ui->chromeSupport->setChecked(settings->chromeSupport());
m_ui->chromiumSupport->setChecked(settings->chromiumSupport());
m_ui->firefoxSupport->setChecked(settings->firefoxSupport());
m_ui->edgeSupport->setChecked(settings->edgeSupport());
#ifndef Q_OS_WIN
m_ui->braveSupport->setChecked(settings->braveSupport());
m_ui->vivaldiSupport->setChecked(settings->vivaldiSupport());
@ -132,6 +134,12 @@ void BrowserOptionDialog::loadSettings()
m_ui->snapWarningLabel->setVisible(false);
#endif
// TODO: Enable when Linux version is released
#ifdef Q_OS_LINUX
m_ui->edgeSupport->setChecked(false);
m_ui->edgeSupport->setEnabled(false);
#endif
#if defined(KEEPASSXC_DIST_APPIMAGE)
m_ui->supportBrowserProxy->setChecked(true);
m_ui->supportBrowserProxy->setEnabled(false);
@ -194,6 +202,7 @@ void BrowserOptionDialog::saveSettings()
settings->setChromeSupport(m_ui->chromeSupport->isChecked());
settings->setChromiumSupport(m_ui->chromiumSupport->isChecked());
settings->setFirefoxSupport(m_ui->firefoxSupport->isChecked());
settings->setEdgeSupport(m_ui->edgeSupport->isChecked());
#ifndef Q_OS_WIN
settings->setBraveSupport(m_ui->braveSupport->isChecked());
settings->setVivaldiSupport(m_ui->vivaldiSupport->isChecked());

View File

@ -167,6 +167,16 @@
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QCheckBox" name="edgeSupport">
<property name="text">
<string>&amp;Edge</string>
</property>
<property name="checked">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
</item>

View File

@ -935,6 +935,12 @@ int BrowserService::sortPriority(const Entry* entry,
if (url.scheme().isEmpty()) {
url.setScheme("https");
}
// Add the empty path to the URL if it's missing
if (url.path().isEmpty() && !url.hasFragment() && !url.hasQuery()) {
url.setPath("/");
}
const QString entryURL = url.toString(QUrl::StripTrailingSlash);
const QString baseEntryURL =
url.toString(QUrl::StripTrailingSlash | QUrl::RemovePath | QUrl::RemoveQuery | QUrl::RemoveFragment);
@ -1046,7 +1052,12 @@ bool BrowserService::handleURL(const QString& entryUrl, const QString& url, cons
return false;
}
// Filter to match hostname in URL field
// Match the base domain
if (baseDomain(siteQUrl.host()) != baseDomain(entryQUrl.host())) {
return false;
}
// Match the subdomains with the limited wildcard
if (siteQUrl.host().endsWith(entryQUrl.host())) {
return true;
}

View File

@ -280,6 +280,17 @@ void BrowserSettings::setTorBrowserSupport(bool enabled)
HostInstaller::SupportedBrowsers::TOR_BROWSER, enabled, supportBrowserProxy(), customProxyLocation());
}
bool BrowserSettings::edgeSupport()
{
return m_hostInstaller.checkIfInstalled(HostInstaller::SupportedBrowsers::EDGE);
}
void BrowserSettings::setEdgeSupport(bool enabled)
{
m_hostInstaller.installBrowser(
HostInstaller::SupportedBrowsers::EDGE, enabled, supportBrowserProxy(), customProxyLocation());
}
bool BrowserSettings::passwordUseNumbers()
{
return config()->get("generator/Numbers", PasswordGenerator::DefaultNumbers).toBool();

View File

@ -80,6 +80,8 @@ public:
void setBraveSupport(bool enabled);
bool torBrowserSupport();
void setTorBrowserSupport(bool enabled);
bool edgeSupport();
void setEdgeSupport(bool enabled);
bool passwordUseNumbers();
void setPasswordUseNumbers(bool useNumbers);

View File

@ -31,7 +31,7 @@
HostInstaller::HostInstaller()
: HOST_NAME("org.keepassxc.keepassxc_browser")
, ALLOWED_EXTENSIONS(QStringList() << "keepassxc-browser@keepassxc.org")
, ALLOWED_ORIGINS(QStringList() << "chrome-extension://iopaggbpplllidnfmcghoonnokmjoicf/"
, ALLOWED_ORIGINS(QStringList() << "chrome-extension://pdffhmdngciaglkoonimfcmckehcpafo/"
<< "chrome-extension://oboonakemofpalcgghocfoadofidjkkk/")
#if defined(Q_OS_MACOS)
, TARGET_DIR_CHROME("/Library/Application Support/Google/Chrome/NativeMessagingHosts")
@ -40,6 +40,7 @@ HostInstaller::HostInstaller()
, TARGET_DIR_VIVALDI("/Library/Application Support/Vivaldi/NativeMessagingHosts")
, TARGET_DIR_TOR_BROWSER("/Library/Application Support/TorBrowser-Data/Browser/Mozilla/NativeMessagingHosts")
, TARGET_DIR_BRAVE("/Library/Application Support/BraveSoftware/Brave-Browser/NativeMessagingHosts")
, TARGET_DIR_EDGE("/Library/Application Support/Microsoft Edge/NativeMessagingHosts")
#elif defined(Q_OS_WIN)
// clang-format off
, TARGET_DIR_CHROME("HKEY_CURRENT_USER\\Software\\Google\\Chrome\\NativeMessagingHosts\\org.keepassxc.keepassxc_browser")
@ -49,6 +50,8 @@ HostInstaller::HostInstaller()
, TARGET_DIR_VIVALDI(TARGET_DIR_CHROME)
, TARGET_DIR_TOR_BROWSER(TARGET_DIR_FIREFOX)
, TARGET_DIR_BRAVE(TARGET_DIR_CHROME)
, TARGET_DIR_EDGE(
"HKEY_CURRENT_USER\\Software\\Microsoft\\Edge\\NativeMessagingHosts\\org.keepassxc.keepassxc_browser")
#else
, TARGET_DIR_CHROME("/.config/google-chrome/NativeMessagingHosts")
, TARGET_DIR_CHROMIUM("/.config/chromium/NativeMessagingHosts")
@ -56,6 +59,7 @@ HostInstaller::HostInstaller()
, TARGET_DIR_VIVALDI("/.config/vivaldi/NativeMessagingHosts")
, TARGET_DIR_TOR_BROWSER("/.tor-browser/app/Browser/TorBrowser/Data/Browser/.mozilla/native-messaging-hosts")
, TARGET_DIR_BRAVE("/.config/BraveSoftware/Brave-Browser/NativeMessagingHosts")
, TARGET_DIR_EDGE("/.config/microsoftedge/NativeMessagingHosts")
#endif
{
}
@ -139,8 +143,7 @@ void HostInstaller::installBrowser(SupportedBrowsers browser,
*/
void HostInstaller::updateBinaryPaths(const bool& proxy, const QString& location)
{
// Where 6 is the number of entries in the SupportedBrowsers enum declared in HostInstaller.h
for (int i = 0; i < 6; ++i) {
for (int i = 0; i <= SupportedBrowsers::EDGE; ++i) {
if (checkIfInstalled(static_cast<SupportedBrowsers>(i))) {
installBrowser(static_cast<SupportedBrowsers>(i), true, proxy, location);
}
@ -168,6 +171,8 @@ QString HostInstaller::getTargetPath(SupportedBrowsers browser) const
return TARGET_DIR_TOR_BROWSER;
case SupportedBrowsers::BRAVE:
return TARGET_DIR_BRAVE;
case SupportedBrowsers::EDGE:
return TARGET_DIR_EDGE;
default:
return QString();
}
@ -195,6 +200,8 @@ QString HostInstaller::getBrowserName(SupportedBrowsers browser) const
return "tor-browser";
case SupportedBrowsers::BRAVE:
return "brave";
case SupportedBrowsers::EDGE:
return "edge";
default:
return QString();
}

View File

@ -35,7 +35,8 @@ public:
FIREFOX = 2,
VIVALDI = 3,
TOR_BROWSER = 4,
BRAVE = 5
BRAVE = 5,
EDGE = 6
};
public:
@ -68,6 +69,7 @@ private:
const QString TARGET_DIR_VIVALDI;
const QString TARGET_DIR_TOR_BROWSER;
const QString TARGET_DIR_BRAVE;
const QString TARGET_DIR_EDGE;
};
#endif // HOSTINSTALLER_H

View File

@ -106,9 +106,9 @@ void NativeMessagingBase::readNativeMessages()
quint32 length = 0;
while (m_running.load() != 0 && !std::cin.eof()) {
length = 0;
std::cin.read(reinterpret_cast<char*>(&length), 4);
std::cin.readsome(reinterpret_cast<char*>(&length), 4);
readStdIn(length);
QThread::msleep(1);
QThread::msleep(100);
}
#endif
}

View File

@ -651,6 +651,9 @@ bool Database::challengeMasterSeed(const QByteArray& masterSeed)
bool ok = m_data.key->challenge(masterSeed, response);
if (ok && !response.isEmpty()) {
m_data.challengeResponseKey->setHash(response);
} else if (ok && response.isEmpty()) {
// no CR key present, make sure buffer is empty
m_data.challengeResponseKey.reset(new PasswordKey);
}
return ok;
}

View File

@ -143,7 +143,9 @@ QByteArray FileWatcher::calculateChecksum()
}
return hash.result();
}
return {};
// If we fail to open the file return the last known checksum, this
// prevents unnecessary merge requests on intermittent network shares
return m_fileChecksum;
});
}

View File

@ -262,7 +262,7 @@ namespace Tools
bool checkUrlValid(const QString& urlField)
{
if (urlField.isEmpty()) {
if (urlField.isEmpty() || urlField.startsWith("cmd://", Qt::CaseInsensitive)) {
return true;
}

View File

@ -44,20 +44,22 @@ Clipboard::Clipboard(QObject* parent)
connect(qApp, SIGNAL(aboutToQuit()), SLOT(clearCopiedText()));
}
void Clipboard::setText(const QString& text)
void Clipboard::setText(const QString& text, bool clear)
{
QClipboard* clipboard = QApplication::clipboard();
auto* clipboard = QApplication::clipboard();
if (!clipboard) {
qWarning("Unable to access the clipboard.");
return;
}
QMimeData* mime = new QMimeData;
auto* mime = new QMimeData;
#ifdef Q_OS_MACOS
mime->setText(text);
mime->setData("application/x-nspasteboard-concealed-type", text.toUtf8());
clipboard->setMimeData(mime, QClipboard::Clipboard);
#else
const QString secretStr = "secret";
QByteArray secretBa = secretStr.toUtf8();
mime->setText(text);
mime->setData("x-kde-passwordManagerHint", secretBa);
mime->setData("x-kde-passwordManagerHint", QByteArrayLiteral("secret"));
clipboard->setMimeData(mime, QClipboard::Clipboard);
if (clipboard->supportsSelection()) {
@ -65,7 +67,7 @@ void Clipboard::setText(const QString& text)
}
#endif
if (config()->get("security/clearclipboard").toBool()) {
if (clear && config()->get("security/clearclipboard").toBool()) {
int timeout = config()->get("security/clearclipboardtimeout").toInt();
if (timeout > 0) {
m_lastCopied = text;
@ -84,19 +86,15 @@ void Clipboard::clearCopiedText()
void Clipboard::clearClipboard()
{
QClipboard* clipboard = QApplication::clipboard();
auto* clipboard = QApplication::clipboard();
if (!clipboard) {
qWarning("Unable to access the clipboard.");
return;
}
if (clipboard->text(QClipboard::Clipboard) == m_lastCopied) {
clipboard->clear(QClipboard::Clipboard);
}
if (clipboard->supportsSelection() && (clipboard->text(QClipboard::Selection) == m_lastCopied)) {
clipboard->clear(QClipboard::Selection);
if (m_lastCopied == clipboard->text(QClipboard::Clipboard)
|| m_lastCopied == clipboard->text(QClipboard::Selection)) {
setText("", false);
}
m_lastCopied.clear();

View File

@ -32,7 +32,7 @@ class Clipboard : public QObject
Q_OBJECT
public:
void setText(const QString& text);
void setText(const QString& text, bool clear = true);
static Clipboard* instance();

View File

@ -154,6 +154,7 @@ DatabaseWidget::DatabaseWidget(QSharedPointer<Database> db, QWidget* parent)
m_shareLabel->setVisible(false);
#endif
m_previewView->setObjectName("previewWidget");
m_previewView->hide();
m_previewSplitter->addWidget(m_entryView);
m_previewSplitter->addWidget(m_previewView);
@ -552,6 +553,14 @@ void DatabaseWidget::deleteEntries(QList<Entry*> selectedEntries)
}
refreshSearch();
m_entryView->setFirstEntryActive();
auto* currentEntry = currentSelectedEntry();
if (currentEntry) {
m_previewView->setEntry(currentEntry);
} else {
m_previewView->setGroup(groupView()->currentGroup());
}
}
bool DatabaseWidget::confirmDeleteEntries(QList<Entry*> entries, bool permanent)

View File

@ -145,10 +145,12 @@ void EntryPreviewWidget::setDatabaseMode(DatabaseWidget::Mode mode)
}
if (mode == DatabaseWidget::Mode::ViewMode) {
if (m_ui->stackedWidget->currentWidget() == m_ui->pageGroup) {
if (m_currentGroup && m_ui->stackedWidget->currentWidget() == m_ui->pageGroup) {
setGroup(m_currentGroup);
} else {
} else if (m_currentEntry) {
setEntry(m_currentEntry);
} else {
hide();
}
}
}

View File

@ -77,8 +77,8 @@ private:
const QScopedPointer<Ui::EntryPreviewWidget> m_ui;
bool m_locked;
Entry* m_currentEntry;
Group* m_currentGroup;
QPointer<Entry> m_currentEntry;
QPointer<Group> m_currentGroup;
QTimer m_totpTimer;
quint8 m_selectedTabEntry;
quint8 m_selectedTabGroup;

View File

@ -104,7 +104,7 @@ add_unit_test(NAME testgroup SOURCES TestGroup.cpp
add_unit_test(NAME testkdbx2 SOURCES TestKdbx2.cpp
LIBS ${TEST_LIBRARIES})
add_unit_test(NAME testkdbx3 SOURCES TestKeePass2Format.cpp FailDevice.cpp TestKdbx3.cpp
add_unit_test(NAME testkdbx3 SOURCES TestKeePass2Format.cpp FailDevice.cpp mock/MockChallengeResponseKey.cpp TestKdbx3.cpp
LIBS testsupport ${TEST_LIBRARIES})
add_unit_test(NAME testkdbx4 SOURCES TestKeePass2Format.cpp FailDevice.cpp mock/MockChallengeResponseKey.cpp TestKdbx4.cpp

View File

@ -298,7 +298,8 @@ void TestBrowser::testSubdomainsAndPaths()
"http://login.github.com/pathtonowhere",
".github.com", // Invalid URL
"www.github.com/",
"https://github" // Invalid URL
"https://github", // Invalid URL
"https://hub.com" // Should not return
};
createEntries(urls, root);

View File

@ -31,6 +31,8 @@ QTEST_GUILESS_MAIN(TestKdbx3)
void TestKdbx3::initTestCaseImpl()
{
m_xmlDb->changeKdf(fastKdf(KeePass2::uuidToKdf(KeePass2::KDF_AES_KDBX3)));
m_kdbxSourceDb->changeKdf(fastKdf(KeePass2::uuidToKdf(KeePass2::KDF_AES_KDBX3)));
}
QSharedPointer<Database> TestKdbx3::readXml(const QString& path, bool strictMode, bool& hasError, QString& errorString)

View File

@ -29,15 +29,25 @@
#include "keys/PasswordKey.h"
#include "mock/MockChallengeResponseKey.h"
QTEST_GUILESS_MAIN(TestKdbx4)
int main(int argc, char* argv[])
{
QCoreApplication app(argc, argv);
QCoreApplication::setAttribute(Qt::AA_Use96Dpi, true);
QTEST_SET_MAIN_SOURCE_PATH
void TestKdbx4::initTestCaseImpl()
TestKdbx4Argon2 argon2Test;
TestKdbx4AesKdf aesKdfTest;
return QTest::qExec(&argon2Test, argc, argv) | QTest::qExec(&aesKdfTest, argc, argv);
}
void TestKdbx4Argon2::initTestCaseImpl()
{
m_xmlDb->changeKdf(fastKdf(KeePass2::uuidToKdf(KeePass2::KDF_ARGON2)));
m_kdbxSourceDb->changeKdf(fastKdf(KeePass2::uuidToKdf(KeePass2::KDF_ARGON2)));
}
QSharedPointer<Database> TestKdbx4::readXml(const QString& path, bool strictMode, bool& hasError, QString& errorString)
QSharedPointer<Database>
TestKdbx4Argon2::readXml(const QString& path, bool strictMode, bool& hasError, QString& errorString)
{
KdbxXmlReader reader(KeePass2::FILE_VERSION_4);
reader.setStrictMode(strictMode);
@ -47,7 +57,7 @@ QSharedPointer<Database> TestKdbx4::readXml(const QString& path, bool strictMode
return db;
}
QSharedPointer<Database> TestKdbx4::readXml(QBuffer* buf, bool strictMode, bool& hasError, QString& errorString)
QSharedPointer<Database> TestKdbx4Argon2::readXml(QBuffer* buf, bool strictMode, bool& hasError, QString& errorString)
{
KdbxXmlReader reader(KeePass2::FILE_VERSION_4);
reader.setStrictMode(strictMode);
@ -57,7 +67,7 @@ QSharedPointer<Database> TestKdbx4::readXml(QBuffer* buf, bool strictMode, bool&
return db;
}
void TestKdbx4::writeXml(QBuffer* buf, Database* db, bool& hasError, QString& errorString)
void TestKdbx4Argon2::writeXml(QBuffer* buf, Database* db, bool& hasError, QString& errorString)
{
KdbxXmlWriter writer(KeePass2::FILE_VERSION_4);
writer.writeDatabase(buf, db);
@ -65,11 +75,11 @@ void TestKdbx4::writeXml(QBuffer* buf, Database* db, bool& hasError, QString& er
errorString = writer.errorString();
}
void TestKdbx4::readKdbx(QIODevice* device,
QSharedPointer<const CompositeKey> key,
QSharedPointer<Database> db,
bool& hasError,
QString& errorString)
void TestKdbx4Argon2::readKdbx(QIODevice* device,
QSharedPointer<const CompositeKey> key,
QSharedPointer<Database> db,
bool& hasError,
QString& errorString)
{
KeePass2Reader reader;
reader.readDatabase(device, key, db.data());
@ -80,11 +90,11 @@ void TestKdbx4::readKdbx(QIODevice* device,
QCOMPARE(reader.version(), KeePass2::FILE_VERSION_4);
}
void TestKdbx4::readKdbx(const QString& path,
QSharedPointer<const CompositeKey> key,
QSharedPointer<Database> db,
bool& hasError,
QString& errorString)
void TestKdbx4Argon2::readKdbx(const QString& path,
QSharedPointer<const CompositeKey> key,
QSharedPointer<Database> db,
bool& hasError,
QString& errorString)
{
KeePass2Reader reader;
reader.readDatabase(path, key, db.data());
@ -95,7 +105,7 @@ void TestKdbx4::readKdbx(const QString& path,
QCOMPARE(reader.version(), KeePass2::FILE_VERSION_4);
}
void TestKdbx4::writeKdbx(QIODevice* device, Database* db, bool& hasError, QString& errorString)
void TestKdbx4Argon2::writeKdbx(QIODevice* device, Database* db, bool& hasError, QString& errorString)
{
if (db->kdf()->uuid() == KeePass2::KDF_AES_KDBX3) {
db->changeKdf(fastKdf(KeePass2::uuidToKdf(KeePass2::KDF_ARGON2)));
@ -110,7 +120,7 @@ void TestKdbx4::writeKdbx(QIODevice* device, Database* db, bool& hasError, QStri
}
Q_DECLARE_METATYPE(QUuid)
void TestKdbx4::testFormat400()
void TestKdbx4Argon2::testFormat400()
{
QString filename = QString(KEEPASSX_TEST_DATA_DIR).append("/Format400.kdbx");
auto key = QSharedPointer<CompositeKey>::create();
@ -135,7 +145,7 @@ void TestKdbx4::testFormat400()
QCOMPARE(entry->attachments()->value("Format400"), QByteArray("Format400\n"));
}
void TestKdbx4::testFormat400Upgrade()
void TestKdbx4Argon2::testFormat400Upgrade()
{
QFETCH(QUuid, kdfUuid);
QFETCH(QUuid, cipherUuid);
@ -193,7 +203,7 @@ void TestKdbx4::testFormat400Upgrade()
}
// clang-format off
void TestKdbx4::testFormat400Upgrade_data()
void TestKdbx4Argon2::testFormat400Upgrade_data()
{
QTest::addColumn<QUuid>("kdfUuid");
QTest::addColumn<QUuid>("cipherUuid");
@ -226,7 +236,7 @@ void TestKdbx4::testFormat400Upgrade_data()
}
// clang-format on
void TestKdbx4::testUpgradeMasterKeyIntegrity()
void TestKdbx4Argon2::testUpgradeMasterKeyIntegrity()
{
QFETCH(QString, upgradeAction);
QFETCH(quint32, expectedVersion);
@ -249,6 +259,7 @@ void TestKdbx4::testUpgradeMasterKeyIntegrity()
QScopedPointer<Database> db(new Database());
db->changeKdf(fastKdf(db->kdf()));
QCOMPARE(db->kdf()->uuid(), KeePass2::KDF_AES_KDBX3); // default is legacy AES-KDF
db->setKey(compositeKey);
// upgrade the database by a specific method
@ -309,9 +320,12 @@ void TestKdbx4::testUpgradeMasterKeyIntegrity()
QFAIL(qPrintable(reader.errorString()));
}
QCOMPARE(reader.version(), expectedVersion & KeePass2::FILE_VERSION_CRITICAL_MASK);
if (expectedVersion != KeePass2::FILE_VERSION_3) {
QVERIFY(db2->kdf()->uuid() != KeePass2::KDF_AES_KDBX3);
}
}
void TestKdbx4::testUpgradeMasterKeyIntegrity_data()
void TestKdbx4Argon2::testUpgradeMasterKeyIntegrity_data()
{
QTest::addColumn<QString>("upgradeAction");
QTest::addColumn<quint32>("expectedVersion");
@ -330,7 +344,7 @@ void TestKdbx4::testUpgradeMasterKeyIntegrity_data()
QTest::newRow("Upgrade (implicit): entry-customdata") << QString("entry-customdata") << KeePass2::FILE_VERSION_4;
}
void TestKdbx4::testCustomData()
void TestKdbx4Argon2::testCustomData()
{
Database db;
@ -424,13 +438,8 @@ void TestKdbx4::testCustomData()
QCOMPARE(newEntry->customData()->value(customDataKey2), customData2);
}
QSharedPointer<Kdf> TestKdbx4::fastKdf(QSharedPointer<Kdf> kdf)
void TestKdbx4AesKdf::initTestCaseImpl()
{
kdf->setRounds(1);
if (kdf->uuid() == KeePass2::KDF_ARGON2) {
kdf->processParameters({{KeePass2::KDFPARAM_ARGON2_MEMORY, 1024}, {KeePass2::KDFPARAM_ARGON2_PARALLELISM, 1}});
}
return kdf;
m_xmlDb->changeKdf(fastKdf(KeePass2::uuidToKdf(KeePass2::KDF_AES_KDBX4)));
m_kdbxSourceDb->changeKdf(fastKdf(KeePass2::uuidToKdf(KeePass2::KDF_AES_KDBX4)));
}

View File

@ -20,7 +20,7 @@
#include "TestKeePass2Format.h"
class TestKdbx4 : public TestKeePass2Format
class TestKdbx4Argon2 : public TestKeePass2Format
{
Q_OBJECT
@ -51,8 +51,14 @@ protected:
bool& hasError,
QString& errorString) override;
void writeKdbx(QIODevice* device, Database* db, bool& hasError, QString& errorString) override;
};
QSharedPointer<Kdf> fastKdf(QSharedPointer<Kdf> kdf);
class TestKdbx4AesKdf : public TestKdbx4Argon2
{
Q_OBJECT
protected:
void initTestCaseImpl() override;
};
#endif // KEEPASSXC_TEST_KDBX4_H

View File

@ -22,7 +22,9 @@
#include "core/Metadata.h"
#include "crypto/Crypto.h"
#include "format/KdbxXmlReader.h"
#include "keys/FileKey.h"
#include "keys/PasswordKey.h"
#include "mock/MockChallengeResponseKey.h"
#include "FailDevice.h"
#include "config-keepassx-tests.h"
@ -566,6 +568,168 @@ void TestKeePass2Format::testKdbxDeviceFailure()
QCOMPARE(errorString, QString("FAILDEVICE"));
}
Q_DECLARE_METATYPE(QSharedPointer<CompositeKey>)
void TestKeePass2Format::testKdbxKeyChange()
{
QFETCH(QSharedPointer<CompositeKey>, key1);
QFETCH(QSharedPointer<CompositeKey>, key2);
bool hasError;
QString errorString;
// write new database
QBuffer buffer;
buffer.open(QBuffer::ReadWrite);
buffer.seek(0);
QSharedPointer<Database> db(new Database());
db->changeKdf(fastKdf(KeePass2::uuidToKdf(m_kdbxSourceDb->kdf()->uuid())));
db->setRootGroup(m_kdbxSourceDb->rootGroup()->clone(Entry::CloneNoFlags, Group::CloneIncludeEntries));
db->setKey(key1);
writeKdbx(&buffer, db.data(), hasError, errorString);
QVERIFY(!hasError);
// read database
db = QSharedPointer<Database>::create();
buffer.seek(0);
readKdbx(&buffer, key1, db, hasError, errorString);
if (hasError) {
QFAIL(qPrintable(QStringLiteral("Error while reading database: ").append(errorString)));
}
QVERIFY(db.data());
// change key
db->setKey(key2);
// write database
buffer.seek(0);
writeKdbx(&buffer, db.data(), hasError, errorString);
QVERIFY(!hasError);
// read database
db = QSharedPointer<Database>::create();
buffer.seek(0);
readKdbx(&buffer, key2, db, hasError, errorString);
if (hasError) {
QFAIL(qPrintable(QStringLiteral("Error while reading database: ").append(errorString)));
}
QVERIFY(db.data());
QVERIFY(db->rootGroup() != m_kdbxSourceDb->rootGroup());
QVERIFY(db->rootGroup()->uuid() == m_kdbxSourceDb->rootGroup()->uuid());
}
void TestKeePass2Format::testKdbxKeyChange_data()
{
QTest::addColumn<QSharedPointer<CompositeKey>>("key1");
QTest::addColumn<QSharedPointer<CompositeKey>>("key2");
auto passwordKey1 = QSharedPointer<PasswordKey>::create("abc");
auto passwordKey2 = QSharedPointer<PasswordKey>::create("def");
QByteArray fileKeyBytes1("uvw");
QBuffer fileKeyBuffer1(&fileKeyBytes1);
fileKeyBuffer1.open(QBuffer::ReadOnly);
auto fileKey1 = QSharedPointer<FileKey>::create();
fileKey1->load(&fileKeyBuffer1);
QByteArray fileKeyBytes2("xzy");
QBuffer fileKeyBuffer2(&fileKeyBytes1);
fileKeyBuffer2.open(QBuffer::ReadOnly);
auto fileKey2 = QSharedPointer<FileKey>::create();
fileKey2->load(&fileKeyBuffer2);
auto crKey1 = QSharedPointer<MockChallengeResponseKey>::create(QByteArray("123"));
auto crKey2 = QSharedPointer<MockChallengeResponseKey>::create(QByteArray("456"));
// empty key
auto compositeKey0 = QSharedPointer<CompositeKey>::create();
// all in
auto compositeKey1_1 = QSharedPointer<CompositeKey>::create();
compositeKey1_1->addKey(passwordKey1);
compositeKey1_1->addKey(fileKey1);
compositeKey1_1->addChallengeResponseKey(crKey1);
auto compositeKey1_2 = QSharedPointer<CompositeKey>::create();
compositeKey1_2->addKey(passwordKey2);
compositeKey1_2->addKey(fileKey2);
compositeKey1_2->addChallengeResponseKey(crKey2);
QTest::newRow("Change: Empty Key -> Full Key") << compositeKey0 << compositeKey1_1;
QTest::newRow("Change: Full Key -> Empty Key") << compositeKey1_1 << compositeKey0;
QTest::newRow("Change: Full Key 1 -> Full Key 2") << compositeKey1_1 << compositeKey1_2;
// only password
auto compositeKey2_1 = QSharedPointer<CompositeKey>::create();
compositeKey2_1->addKey(passwordKey1);
auto compositeKey2_2 = QSharedPointer<CompositeKey>::create();
compositeKey2_2->addKey(passwordKey2);
QTest::newRow("Change: Password -> Empty Key") << compositeKey2_1 << compositeKey0;
QTest::newRow("Change: Empty Key -> Password") << compositeKey0 << compositeKey2_1;
QTest::newRow("Change: Full Key -> Password 1") << compositeKey1_1 << compositeKey2_1;
QTest::newRow("Change: Full Key -> Password 2") << compositeKey1_1 << compositeKey2_2;
QTest::newRow("Change: Password 1 -> Full Key") << compositeKey2_1 << compositeKey1_1;
QTest::newRow("Change: Password 2 -> Full Key") << compositeKey2_2 << compositeKey1_1;
QTest::newRow("Change: Password 1 -> Password 2") << compositeKey2_1 << compositeKey2_2;
// only key file
auto compositeKey3_1 = QSharedPointer<CompositeKey>::create();
compositeKey3_1->addKey(fileKey1);
auto compositeKey3_2 = QSharedPointer<CompositeKey>::create();
compositeKey3_2->addKey(fileKey2);
QTest::newRow("Change: Key File -> Empty Key") << compositeKey3_1 << compositeKey0;
QTest::newRow("Change: Empty Key -> Key File") << compositeKey0 << compositeKey3_1;
QTest::newRow("Change: Full Key -> Key File 1") << compositeKey1_1 << compositeKey3_1;
QTest::newRow("Change: Full Key -> Key File 2") << compositeKey1_1 << compositeKey3_2;
QTest::newRow("Change: Key File 1 -> Full Key") << compositeKey3_1 << compositeKey1_1;
QTest::newRow("Change: Key File 2 -> Full Key") << compositeKey3_2 << compositeKey1_1;
QTest::newRow("Change: Key File 1 -> Key File 2") << compositeKey3_1 << compositeKey3_2;
// only cr key
auto compositeKey4_1 = QSharedPointer<CompositeKey>::create();
compositeKey4_1->addChallengeResponseKey(crKey1);
auto compositeKey4_2 = QSharedPointer<CompositeKey>::create();
compositeKey4_2->addChallengeResponseKey(crKey2);
QTest::newRow("Change: CR Key -> Empty Key") << compositeKey4_1 << compositeKey0;
QTest::newRow("Change: Empty Key -> CR Key") << compositeKey0 << compositeKey4_1;
QTest::newRow("Change: Full Key -> CR Key 1") << compositeKey1_1 << compositeKey4_1;
QTest::newRow("Change: Full Key -> CR Key 2") << compositeKey1_1 << compositeKey4_2;
QTest::newRow("Change: CR Key 1 -> Full Key") << compositeKey4_1 << compositeKey1_1;
QTest::newRow("Change: CR Key 2 -> Full Key") << compositeKey4_2 << compositeKey1_1;
QTest::newRow("Change: CR Key 1 -> CR Key 2") << compositeKey4_1 << compositeKey4_2;
// rotate
QTest::newRow("Change: Password -> Key File") << compositeKey2_1 << compositeKey3_1;
QTest::newRow("Change: Key File -> Password") << compositeKey3_1 << compositeKey2_1;
QTest::newRow("Change: Password -> Key File") << compositeKey2_1 << compositeKey3_1;
QTest::newRow("Change: Key File -> Password") << compositeKey3_1 << compositeKey2_1;
QTest::newRow("Change: Password -> CR Key") << compositeKey2_1 << compositeKey4_1;
QTest::newRow("Change: CR Key -> Password") << compositeKey4_1 << compositeKey2_1;
QTest::newRow("Change: Key File -> CR Key") << compositeKey3_1 << compositeKey4_1;
QTest::newRow("Change: CR Key -> Key File") << compositeKey4_1 << compositeKey3_1;
// leave one out
auto compositeKey5_1 = QSharedPointer<CompositeKey>::create();
compositeKey5_1->addKey(fileKey1);
compositeKey5_1->addChallengeResponseKey(crKey1);
auto compositeKey5_2 = QSharedPointer<CompositeKey>::create();
compositeKey5_2->addKey(passwordKey1);
compositeKey5_2->addChallengeResponseKey(crKey1);
auto compositeKey5_3 = QSharedPointer<CompositeKey>::create();
compositeKey5_3->addKey(passwordKey1);
compositeKey5_3->addKey(fileKey1);
QTest::newRow("Change: Full Key -> No Password") << compositeKey1_1 << compositeKey5_1;
QTest::newRow("Change: No Password -> Full Key") << compositeKey5_1 << compositeKey1_1;
QTest::newRow("Change: Full Key -> No Key File") << compositeKey1_1 << compositeKey5_2;
QTest::newRow("Change: No Key File -> Full Key") << compositeKey5_2 << compositeKey1_1;
QTest::newRow("Change: Full Key -> No CR Key") << compositeKey1_1 << compositeKey5_3;
QTest::newRow("Change: No CR Key -> Full Key") << compositeKey5_3 << compositeKey1_1;
}
/**
* Test for catching mapping errors with duplicate attachments.
*/
@ -637,3 +801,17 @@ void TestKeePass2Format::testDuplicateAttachments()
QCOMPARE(db->rootGroup()->entries()[2]->attachments()->value("c2"), attachment2);
QCOMPARE(db->rootGroup()->entries()[2]->attachments()->value("c3"), attachment3);
}
/**
* @return fast "dummy" KDF
*/
QSharedPointer<Kdf> TestKeePass2Format::fastKdf(QSharedPointer<Kdf> kdf) const
{
kdf->setRounds(1);
if (kdf->uuid() == KeePass2::KDF_ARGON2) {
kdf->processParameters({{KeePass2::KDFPARAM_ARGON2_MEMORY, 1024}, {KeePass2::KDFPARAM_ARGON2_PARALLELISM, 1}});
}
return kdf;
}

View File

@ -62,6 +62,8 @@ private slots:
void testKdbxAttachments();
void testKdbxNonAsciiPasswords();
void testKdbxDeviceFailure();
void testKdbxKeyChange();
void testKdbxKeyChange_data();
void testDuplicateAttachments();
protected:
@ -84,6 +86,8 @@ protected:
QString& errorString) = 0;
virtual void writeKdbx(QIODevice* device, Database* db, bool& hasError, QString& errorString) = 0;
QSharedPointer<Kdf> fastKdf(QSharedPointer<Kdf> kdf) const;
QSharedPointer<Database> m_xmlDb;
QSharedPointer<Database> m_kdbxSourceDb;
QSharedPointer<Database> m_kdbxTargetDb;

View File

@ -54,6 +54,7 @@
#include "gui/CloneDialog.h"
#include "gui/DatabaseTabWidget.h"
#include "gui/DatabaseWidget.h"
#include "gui/EntryPreviewWidget.h"
#include "gui/FileDialog.h"
#include "gui/MessageBox.h"
#include "gui/PasswordEdit.h"
@ -967,6 +968,7 @@ void TestGui::testDeleteEntry()
QWidget* entryDeleteWidget = toolBar->widgetForAction(entryDeleteAction);
entryView->setFocus();
// Move one entry to the recycling bin
QCOMPARE(m_dbWidget->currentMode(), DatabaseWidget::Mode::ViewMode);
clickIndex(entryView->model()->index(1, 1), entryView, Qt::LeftButton);
QVERIFY(entryDeleteWidget->isVisible());
@ -979,6 +981,7 @@ void TestGui::testDeleteEntry()
QCOMPARE(entryView->model()->rowCount(), 3);
QCOMPARE(m_db->metadata()->recycleBin()->entries().size(), 1);
// Select multiple entries and move them to the recycling bin
clickIndex(entryView->model()->index(1, 1), entryView, Qt::LeftButton);
clickIndex(entryView->model()->index(2, 1), entryView, Qt::LeftButton, Qt::ControlModifier);
QCOMPARE(entryView->selectionModel()->selectedRows().size(), 2);
@ -993,6 +996,7 @@ void TestGui::testDeleteEntry()
QCOMPARE(entryView->model()->rowCount(), 1);
QCOMPARE(m_db->metadata()->recycleBin()->entries().size(), 3);
// Go to the recycling bin
QCOMPARE(groupView->currentGroup(), m_db->rootGroup());
QModelIndex rootGroupIndex = groupView->model()->index(0, 0);
clickIndex(groupView->model()->index(groupView->model()->rowCount(rootGroupIndex) - 1, 0, rootGroupIndex),
@ -1000,6 +1004,7 @@ void TestGui::testDeleteEntry()
Qt::LeftButton);
QCOMPARE(groupView->currentGroup()->name(), m_db->metadata()->recycleBin()->name());
// Delete one entry from the bin
clickIndex(entryView->model()->index(0, 1), entryView, Qt::LeftButton);
MessageBox::setNextAnswer(MessageBox::Cancel);
QTest::mouseClick(entryDeleteWidget, Qt::LeftButton);
@ -1011,6 +1016,7 @@ void TestGui::testDeleteEntry()
QCOMPARE(entryView->model()->rowCount(), 2);
QCOMPARE(m_db->metadata()->recycleBin()->entries().size(), 2);
// Select the remaining entries and delete them
clickIndex(entryView->model()->index(0, 1), entryView, Qt::LeftButton);
clickIndex(entryView->model()->index(1, 1), entryView, Qt::LeftButton, Qt::ControlModifier);
MessageBox::setNextAnswer(MessageBox::Delete);
@ -1018,6 +1024,16 @@ void TestGui::testDeleteEntry()
QCOMPARE(entryView->model()->rowCount(), 0);
QCOMPARE(m_db->metadata()->recycleBin()->entries().size(), 0);
// Ensure the entry preview widget shows the recycling group since all entries are deleted
auto* previewWidget = m_dbWidget->findChild<EntryPreviewWidget*>("previewWidget");
QVERIFY(previewWidget);
auto* groupTitleLabel = previewWidget->findChild<QLabel*>("groupTitleLabel");
QVERIFY(groupTitleLabel);
QTRY_VERIFY(groupTitleLabel->isVisible());
QVERIFY(groupTitleLabel->text().contains(m_db->metadata()->recycleBin()->name()));
// Go back to the root group
clickIndex(groupView->model()->index(0, 0), groupView, Qt::LeftButton);
QCOMPARE(groupView->currentGroup(), m_db->rootGroup());
}