Compare commits

...

87 Commits

Author SHA1 Message Date
Jonathan White
ddfc94de4b More reversions 2025-03-16 10:46:36 -04:00
Jonathan White
1239e59ee1 Bring back adopt-info 2025-03-16 08:21:55 -04:00
Jonathan White
434f31cba2 Minor tweaks 2025-03-15 20:42:14 -04:00
Jonathan White
32abcb0d52 Some more reversions 2025-03-15 18:26:55 -04:00
Jonathan White
edd921401d Revert zstd 2025-03-15 18:07:26 -04:00
Jonathan White
e98c98c6c3 Revert qt5-base 2025-03-15 13:11:38 -04:00
Jonathan White
faf45079ef Add recommended plugs for better desktop integration 2025-03-15 12:56:39 -04:00
Jonathan White
63714a6f5f Fix snap build to use latest recommendations
* Switch to core24 as default
* Switch to qt5-base from kde-neon
* Switch to using desktop-launch instead of autostart
* Use layout to map specific directories for better desktop integration
2025-03-15 12:56:19 -04:00
Emil Hemdal
31c0b23890 Add LibreWolf snap helper setup 2025-03-10 20:37:43 -04:00
Jonathan White
33a3796074 Add ability to parse tags from CSV files
* Closes #7956
2025-03-01 17:14:13 -05:00
Jonathan White
1b1643b5d1 Fix various quirks with CSV import widget and parser
* Fixes #11502 - correct improper handling of text qualifiers

* Improve layout of csv import widget
* Hide error messages when trying to import again
2025-03-01 17:14:13 -05:00
Jonathan White
244ed42231 Improve remote sync tests 2025-02-23 22:18:47 -05:00
w15dev
af2d0b1429 Enhance image attachment handling by caching loaded images and improving scaling logic 2025-02-23 17:20:34 -05:00
An-anonymous-coder
86f74a00d0 Removed newline and words with hyphen (#11409)
The newline at the end of the file was removed, as well as 4 words that contain a hyphen:
drop-in
felt-tip
t-shirt
yo-yo
2025-02-23 09:46:25 -05:00
Adrian Martin
ab6b6f36a0 Feature: HTML export from CLI tool (#11590)
This commit introduces support for exporting a KeePassXC database in
HTML format via the CLI tool. The key changes include:
- Refactoring HtmlExporter:
  - Moved HtmlExporter to the format directory and made its API
    compatible with CsvExporter.
  - Since the original HtmlExporter had a direct dependency on the
    gui/Icons functions and indirect dependencies on the
    gui/DatabaseIcons class, only the non-GUI parts were moved to
    format/HtmlExporter.
  - All icon-related functionality was encapsulated in a new child
    class, gui/HtmlGuiExporter.
    - The gui/HtmlGuiExporter retains the original functionality of the
      HtmlExporter class.
    - The format/HtmlExporter now generates HTML export without icons.
      Adding icon support to format/HtmlExporter would require moving
      icon management logic to the core, which could have broader
      implications.
- CLI integration:
  - Updated cli/Export to use format/HtmlExporter.
- GUI Integration:
  - Updated gui/export/ExportDialog to use gui/HtmlGuiExporter.
- Build System Updates:
  - Updated CMakeLists.txt to build HtmlExporter as part of core_SOURCES
    and HtmlGuiExporter as part of gui_SOURCES.
- Testing:
  - Updated TestCli to automatically verify the output of the HTML
    export.

Signed-off-by: AdriandMartin <adriandmartin@protonmail.com>
2025-02-23 08:43:06 -05:00
xboxones1
5a3289ee3c Dynamically change the status bar color depending on the current screen (#11672)
* Revert (https://github.com/keepassxreboot/keepassxc/pull/11455)
* Do not use styles for QStatusBar
* Dynamically change the status bar color depending on the current screen
2025-02-23 07:32:51 -05:00
Jonathan White
e4bb80b96c Support tearing off tags menu (#11652)
* Support tearing off tags menu
* Closes #11649 - tags menu can be torn off to set and unset tags without having to dive into the context menu every time.
* Tags menu will hide when database is locked or view is switched away from the main database view (eg, settings)
2025-02-22 20:44:12 -05:00
Jonathan White
d6e726a9cf Improve browser notice about snap/flatpak support 2025-02-22 16:42:08 -05:00
Jonathan White
03855fc411 Major enhancements to documentation
* Closes #11467 - Describe default search fields
* Closes #11468 - Fix lock database shortcut
* Closes #8259 - Add a note about 1Password OPVault specifics
* Closes #9794 - Add section anchors for easy linking
* Closes #10316 - Show how to setup managed Edge on macOS
* Closes #7805 - Document entry url handling
* Closes #9143 - Document database merge behavior
* Closes #10876 - Correct wording in browser and passkey sections

Update outdated images of the user interface. Improve wording and flow of entire documentation. Fill in missing pieces based on user feedback.

Add mention about URL wildcards
2025-02-22 16:42:08 -05:00
Jonathan White
518dd71de6 Update fuzz testing instructions
* Also fix env var name
2025-02-22 16:42:08 -05:00
Jonathan White
41b6247178 Always reset Auto-Type state on finished signal
There were a couple code paths that did not reset the state appropriately and could cause undefined behavior in the auto-type processing.
2025-02-22 06:39:02 -05:00
Jonathan White
2ea656bc27 Show Auto-Type select dialog even if window title is empty
* Fixes #11597
2025-02-22 06:39:02 -05:00
Jonathan White
253bb42ac0 Prevent AltGr showing the menubar on Windows
* Fixes #11549
2025-02-22 06:36:53 -05:00
varjolintu
0b5ae1775c Prevent using URL wildcards in TLD 2025-02-12 16:05:01 -05:00
Sami Vänttinen
9ba6ada266 Add support for URL wildcards and exact URL (#9835)
* Add support for URL wildcards with Additional URL feature

* Only check TLD if wildcard is used

* Avoid using network function in no-feature build

---------

Co-authored-by: varjolintu <sami.vanttinen@ahmala.org>
Co-authored-by: Jonathan White <support@dmapps.us>
2025-02-09 20:03:15 -05:00
Jonathan White
51e8c042af Show database public icon on tab when visible (#11725)
* Show database public icon on tab when visible

* Remove unnecessary assert
2025-02-09 20:02:44 -05:00
outfoxxed
c9a64be699 Add --minimized CLI option 2025-02-09 14:37:53 -05:00
Alexandre Petit
15ac8ac4f8 Implement function to clear all ssh-agent identities (#10649)
Fixes #8346

---------

Co-authored-by: Jonathan White <support@dmapps.us>
2025-02-01 12:01:22 -05:00
Marco Langer
8ca90a070a Fix sorting of advanced attribute list (#10091)
Sort advanced attribute list using locale aware sort.

Fixes #6175
2025-02-01 11:59:53 -05:00
vuurvli3g
811887e591 Fix issues with reloading and handling of externally modified db file (#10612)
Fixes #5290 
Fixes #9062
Fixes #8545 

* Fix data loss on failed reload

- External modifications to the db file can no longer be missed.
- Fixed dialogFinished signal of DatabaseOpenDialog was not emitted when dialog was closed via the 'X' (close) button
- For reloading with a modified db, an additional choice has been added to allow the user to ignore the changes in the file on disk.
- User is now presented with an unlock database dialog if reload fails to open the db automatically. For example when the user removed the YubiKey, failed to touch the YubiKey within the timeout period, or db pw has been changed.
- Mark db as modified when db file is gone or invalid.
- Prevent saving when db is being reloaded
- If merge is triggered by a save action, continue on with the save action after the user makes their choice

---------

Co-authored-by: vuurvlieg <vuurvli3g@protonmail.com>
Co-authored-by: Jonathan White <support@dmapps.us>
2025-02-01 11:58:45 -05:00
w15dev
81fa8d5947 Refactor YubiKey key to avoid deadlock
- Add mutex to get m_connectedKeys
- Fix deadlock when the app uses Quick Unlock and the YubiKey is unplugged
2025-02-01 08:01:31 -05:00
Kuznetsov Oleg
5ee5e4998a Improve attachment handling when changes are discarded
This change avoids a situation where the open file has changed or an entry in the application has changed (possibly to be implemented in the future) and when you open that entry the editor shows you outdated data.

* Fixes bug from previous attachments preview commit
* Fix logic error when creating new attachment
2025-02-01 07:59:02 -05:00
Jonathan White
046cb9bd70 Allow adjusting application font size
* Closes #6822
* Fix fixed font not following default font's point size
2025-01-26 07:16:55 -05:00
Jonathan White
831704f3b2 Trim file path settings fields prior to save
* Fixes #11630
2025-01-26 07:04:57 -05:00
Felix Nüsse
4d7eae34c2 make tag-suggestions case insensitive 2025-01-19 18:00:25 -05:00
Jonathan White
8d6d937b1b Update version documents 2025-01-19 09:22:11 -05:00
xavives
f5bb5985ee Add Group Path column choice in entry view
* Closes #9574
2025-01-12 18:08:06 -05:00
Martin van Zijl
6494cdbb4c Remember sort order in Autotype popup dialog
Fixes #1684.
2025-01-12 18:07:26 -05:00
sewe2000
9de623120b Fix displaying html characters in message boxes 2025-01-12 17:28:51 -05:00
Blessio
f3b08102c4 Add alternative path finding for PCSC headers/libraries
* When running the build outside of a visual studio environment, PCSC libraries may not be discoverable. This change explicitly adds Windows SDK's to the search path.
2025-01-12 17:01:46 -05:00
Matteson
29ac4da240 Add ability to expire entries from context menus (#8731)
Closes #1972

Add ability to immediately expire an entry from the context menu

---------
Co-authored-by: Jonathan White <support@dmapps.us>
2025-01-12 07:55:22 -05:00
varjolintu
832340e209 Fix setting window title as modified 2025-01-12 00:14:32 -05:00
Kuznetsov Oleg
dce34de875 Add New/Preview Entry Attachments dialog and functionality (#11637)
Closes #11506
Closes #3383

* This change adds a new opportunity to add attachments that don’t require a real file in the file system.
* Add a new dialog window to add and preview attachments and integrate it into the EntryAttachmentsWidget.
* Attachment preview support for images and plain text files.

Additional enhancements:
* Fix sizing of attachment columns
* Add padding to attachment table items
* Fix targeting of preview widget styling to not impact unintended children
2025-01-11 23:08:19 -05:00
Jonathan White
40ee047ef0 Fix crash when pressing home key on empty tags field
* Fixes #11344
2025-01-08 15:44:04 -05:00
Jonathan White
17dc022e15 Fix rendering &amp; and &quot; in preview panel
* Set plain text mode on elements that should never have styling
* Revert html escaping as a prior fix
2025-01-06 06:23:31 -05:00
Jonathan White
620abb96f2 Implement Secure Input Field mode on macOS
* Fixes #4738
* Also fixes flaky handling of caps lock detection events
2025-01-04 12:40:51 -05:00
Jonathan White
edab0faa94 Add Proton Pass importer
* Closes #10465
2025-01-03 17:45:50 -05:00
Kuznetsov Oleg
9e29b5c7b6 Show a clear error if no slots on hardware key(s) are configured (#11609)
Fixes #11543

Also fix delayed polling on window activation

---------

Co-authored-by: w15dev <w15developer@proton.me>
Co-authored-by: Jonathan White <support@dmapps.us>
2025-01-02 23:05:22 -05:00
xboxones1
2afec91e87 Minor style fixes (#11445)
* Clean up removed elements in qt stylesheets
* Disable main window when saving
* Fixed triangle size in group view

---------

Co-authored-by: Jonathan White <support@dmapps.us>
2025-01-01 22:23:27 -05:00
Jonathan White
fb022cb5e9 Fix passphrase word lists not rendering with UTF-8
* Fixes #11599
2024-12-27 18:06:17 -05:00
Stephan Heffner
e76e9d42c7 Passphrase "MIXED case" Type (#11255)
* An additional approach to create passphrases with one random word being in UPPERCASE.
* Also remove duplicate character count from passphrase generator

---------

Co-authored-by: Stephan Heffner <stephan@heffner.it>
Co-authored-by: Jonathan White <support@dmapps.us>
2024-12-25 22:56:02 -05:00
varjolintu
9670a5e74e Add CustomData::getKeyWithPrefix() 2024-12-25 21:33:19 -05:00
varjolintu
132ca42ec5 Fix renaming extension key name in Database Settings 2024-12-25 21:33:19 -05:00
Jonathan White
af0c1644a6 Add copy shortcuts to Auto-Type select dialog
* Fixes #10213
* Sets copy password/username/totp to Ctrl+Shift+1/2/3 respectively
2024-12-24 08:28:03 -05:00
Jonathan White
d60472328f Additional exclusion fields for Windows Clipboard
* Fixes #7127
2024-12-24 08:27:44 -05:00
Jonathan White
5e61db630c Fix entry preview rendering HTML
* Fixes #11538 - prevent rendering HTML in Title, Password, and URL fields in the entry preview pane.
2024-12-24 08:25:29 -05:00
Jonathan White
0cb0373f85 Fix indent on issue forms 2024-12-20 00:03:16 -05:00
Jonathan White
6bbb7dcfac Correct syntax in issue forms 2024-12-19 23:59:21 -05:00
Jonathan White
f2ed4e3840 Use Issue Forms instead of templates
Also includes minor updates to the PR template
2024-12-14 08:58:06 -05:00
dependabot[bot]
cdbff32f25 Bump golang.org/x/crypto in /utils/keepassxc-cr-recovery (#11557)
Bumps [golang.org/x/crypto](https://github.com/golang/crypto) from 0.17.0 to 0.31.0.
- [Commits](https://github.com/golang/crypto/compare/v0.17.0...v0.31.0)

---
updated-dependencies:
- dependency-name: golang.org/x/crypto
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-12-11 20:14:26 -05:00
xboxones1
f71cca4eba DarkTheme: Fix color for handles 2024-12-07 11:19:04 -05:00
Jonathan White
b1180b3341 CLI: Restore the original codepage on windows
* Fixes #11465
2024-12-03 14:32:27 -05:00
Jonathan White
9a63e80386 Fix crash on Linux when database is closed without hardware key present
* Fixes #11450
2024-11-30 15:07:15 -05:00
Jonathan White
b29993abe5 Fix crash when multiple dbus unlock calls are issued
* Fixes #11512
2024-11-30 15:07:15 -05:00
xboxones1
1d581ee027 DarkTheme: Fix separator color for TagView (#11511) 2024-11-30 20:59:47 +01:00
Jonathan White
bf856d278d Detect outdated VC Redist with MSI Installer
* Fixes #10974
2024-11-22 17:34:07 -05:00
Jonathan White
9b8163c3a4 Use better xref syntax for documentation 2024-11-13 17:51:21 -05:00
Jonathan White
b9c5869806 Implement T-CONV and T-REPLACE-RX entry placeholders
* Closes #7293
* Move existing T-CONV and T-REPLACE-RX code from AutoType to Entry. Replumb AutoType to use the entry functions.
* Improve placeholder code in various place
2024-11-13 17:51:21 -05:00
xboxones1
4acb3774e6 Hide status bar on WelcomeScreen 2024-11-13 16:53:06 -05:00
Sami Vänttinen
2fc24be331 Browser: Fix cancel with database unlock dialog (#11435) 2024-11-10 17:30:38 -05:00
Findus
bff0b93f5f Device Password fallback when Touch-ID devices are unavailable (#11410)
* Added kSecAccessControlDevicePasscode to accessControlflags when feature is enabled in settings and no biometrics are available

* Able to use either biometry or password, if touchid is unavailable

* Additional check if TouchID is enrolled:

With that we can add the "kSecAccessControlBiometryCurrentSet" Flag even though Biometry is unavailable due to closed lid or unpaired keyboard. Adding this flag when TouchID is not enrolled results in an error when trying to save the secret.

The kSecAccessControlWatch Flag for apple watch compatibility does not have this limitation. With that we can also offer quick unlock with only apple watch or password

* Fallback to quick unlock without touchid if saving key fails with selected flags, might fix quick unlock on a hackintosh
2024-11-10 17:10:04 -05:00
FischLu
ca9b88fae8 Replace legacy code on macOS builds (#11428)
* Fix macOS build error due to CGDisplayStreamCreate being deprecated.

---------

Co-authored-by: FischLu <randomDe@proton.me>
2024-11-10 09:18:04 -05:00
Jonathan White
6e81451f64 Add timeout to SSH Agent streams
* Fixes #11128
2024-11-10 09:15:16 -05:00
xboxones1
d03ffc228c Fixed background color when window is out of focus 2024-11-02 10:49:27 -04:00
Jonathan White
2738a72b43 Fix assert hit when viewing entry history
* Fixes #11371
* Adds test for showing entry history
* Improved page switching capabilities for entry edit widget
2024-10-27 06:49:10 -04:00
Jonathan White
5d24495704 Hide the menubar when menus lose focus (if toggled off)
* Fixes #10768
* Also fix menubar toggling not working if Qt version is less than 5.15
2024-10-26 11:06:20 -04:00
Jonathan White
34fe413dad Fix resolving advanced values in entry preview
* Fixes #10961
* Fixes #7410 - show content of references when previewing notes
2024-10-26 09:00:04 -04:00
Jonathan White
feafceca57 Improve related splitter UX
* Prevent group pane from being hidden just by dragging. Introduce new View menu setting to hide the group pane.
* Replace the preview panel "close" icon with a "collapse down" icon making the intention clearer.
* Better organize the view menu
2024-10-26 08:59:06 -04:00
Jonathan White
8acc54225d Reset splitter sizes on database unlock
* Attempt to avoid issue with splitters not being appropriately calculated because the main window isn't sized yet. This can happen if the main window is hidden when the database is loaded and the splitter sizes are not recorded in the config file.
2024-10-26 08:59:06 -04:00
Sami Vänttinen
6e0baf9f2c Support passkeys with Bitwarden import (#11401) 2024-10-24 20:12:47 -04:00
m4sked
95bae8377c Add SECURITY.md to guide security vulnerability reporting (#11360) 2024-10-19 12:47:49 -04:00
vallode
f07db033c6 Fix MacOS login items showing ambigious name (#11373) 2024-10-17 09:37:38 +02:00
Jonathan White
4f8c204096 Avoid hitting assert on XML export
* Fixes #11365
2024-10-15 08:16:01 -04:00
Jonathan White
02881889d5 Fix entry notes reveal button requiring two clicks sometimes 2024-10-14 09:56:17 -04:00
xboxones1
d57d167e9c Dark theme fix colors for qmenu 2024-10-11 18:10:09 -04:00
Patrick Klein
740994ed48 Add option to disable opening a browser window when double-clicking the "URL" column (#11332) 2024-10-09 06:42:33 -04:00
techninja1008
dbf9587775 Add database name, color, and icon options for unlock view (#10819)
Closes #10783

Adds three database configuration options (stored as public custom data) that allow a database to have a public name/summary, color, and/or icon to be displayed on the unlock screen. This information is configured in the Database Settings and stored in the database public custom data (ie, unencrypted).

The name/summary is stored in KPXC_PUBLIC_NAME, the color is stored in KPXC_PUBLIC_COLOR, and the icon is stored in KPXC_PUBLIC_ICON.

---------

Co-authored-by: Jonathan White <support@dmapps.us>
2024-10-07 23:42:22 -04:00
231 changed files with 6081 additions and 1820 deletions

View File

@@ -1,39 +0,0 @@
---
name: Bug Report
about: provide information about a problem
title:
labels: bug
assignees: ''
---
## Overview
[TIP]: # ( DO NOT include screenshots of your actual database! )
[NOTE]: # ( Give a BRIEF summary about your problem )
## Steps to Reproduce
[NOTE]: # ( Provide a simple set of steps to reproduce this bug. )
1.
2.
3.
## Expected Behavior
[NOTE]: # ( Tell us what you expected to happen )
## Actual Behavior
[NOTE]: # ( Tell us what actually happens )
## Context
[NOTE]: # ( Give us any additional information you may have. )
[NOTE]: # ( Paste debug info from Help → About here )
KeePassXC - VERSION
Revision: REVISION
[NOTE]: # ( Pick choices based on your environment )
Operating System: Windows/Linux/macOS
Desktop Env: Gnome/KDE/XFCE/Mate/Cinnamon
Windowing System: X11/Wayland

83
.github/ISSUE_TEMPLATE/bug_report.yml vendored Normal file
View File

@@ -0,0 +1,83 @@
name: Bug Report
description: Provide information about a problem you are experiencing.
type: Bug
body:
- type: checkboxes
attributes:
label: Have you searched for an existing issue?
description: |
Use the issue search box to see if one already exists for the bug you encountered.
Also take a moment to review our pinned issues.
options:
- label: Yes, I tried searching and reviewed the pinned issues
required: true
- type: textarea
id: summary
attributes:
label: Brief Summary
description: |
Provide an overview of the problem, include any information that may help us triage this issue.
Provide screenshots if possible, but do NOT show sensitive data (use View -> Allow Screen Capture).
validations:
required: true
- type: textarea
id: steps
attributes:
label: Steps to Reproduce
description: Provide a simple set of steps to reproduce this bug.
placeholder: |
1.
2.
3.
validations:
required: true
- type: textarea
id: expected_vs_actual
attributes:
label: Expected Versus Actual Behavior
description: Tell us what you expected to happen and what actually happened.
- type: textarea
id: debug_info
attributes:
label: KeePassXC Debug Information
placeholder: "Paste the output of: Help -> About -> Debug Info"
render: Text
- type: dropdown
id: os
attributes:
label: Operating System
description: Select your operating system.
options:
- Windows
- Linux
- macOS
- Other (BSD, Haiku, etc)
- type: dropdown
id: desktop_env
attributes:
label: Linux Desktop Environment
description: If on Linux, please select your desktop environment.
options:
- Gnome
- KDE
- XFCE
- Mate / Cinnamon
- Sway
- i3
- Other
- type: dropdown
id: window_system
attributes:
label: Linux Windowing System
description: If on Linux, please select your windowing system.
options:
- X11
- Wayland

View File

@@ -1,19 +0,0 @@
---
name: Feature Request
about: tell us about a new feature you want
title:
labels: new feature
assignees: ''
---
## Summary
[TIP]: # ( DO NOT include screenshots of your actual database! )
[NOTE]: # ( Provide a brief overview of what the new feature is all about )
## Examples
[NOTE]: # ( Show us a picture or mock-up of your proposal )
## Context
[NOTE]: # ( Why does this feature matter to you? What unique circumstances do you have? )

View File

@@ -0,0 +1,34 @@
name: Feature Request
description: Tell us about a new feature you want.
type: Feature
body:
- type: checkboxes
attributes:
label: Have you searched for an existing feature request?
description: Use the issue search box to see if one already exists for the feature you want.
options:
- label: Yes, I tried searching
required: true
- type: textarea
id: summary
attributes:
label: Brief Summary
description: |
Provide an overview of the feature you are interested in adding.
Provide screenshots if possible, but do NOT show sensitive data (use View -> Allow Screen Capture).
validations:
required: true
- type: textarea
id: example
attributes:
label: Example
description: Provide an example of how this feature would be used.
- type: textarea
id: context
attributes:
label: Context
description: Why does this feature matter to you? What unique circumstances do you have?

View File

@@ -0,0 +1,85 @@
name: Pre-Release Bug Report
description: Report an issue with pre-release code (e.g. snapshot builds).
type: Bug
labels: PRE-RELEASE BUG
assignees: droidmonkey
body:
- type: checkboxes
attributes:
label: Have you searched for an existing issue?
description: |
Use the issue search box to see if one already exists for the bug you encountered.
Also take a moment to review our pinned issues.
options:
- label: Yes, I tried searching and reviewed the pinned issues
required: true
- type: textarea
id: summary
attributes:
label: Brief Summary
description: |
Provide an overview of the problem, include any information that may help us triage this issue.
Provide screenshots if possible, but do NOT show sensitive data (use View -> Allow Screen Capture).
validations:
required: true
- type: textarea
id: steps
attributes:
label: Steps to Reproduce
description: Provide a simple set of steps to reproduce this bug.
placeholder: |
1.
2.
3.
validations:
required: true
- type: textarea
id: expected_vs_actual
attributes:
label: Expected Versus Actual Behavior
description: Tell us what you expected to happen and what actually happened.
- type: textarea
id: debug_info
attributes:
label: KeePassXC Debug Information
placeholder: "Paste the output of: Help -> About -> Debug Info"
render: Text
- type: dropdown
id: os
attributes:
label: Operating System
description: Select your operating system.
options:
- Windows
- Linux
- macOS
- Other (BSD, Haiku, etc)
- type: dropdown
id: desktop_env
attributes:
label: Linux Desktop Environment
description: If on Linux, please select your desktop environment.
options:
- Gnome
- KDE
- XFCE
- Mate / Cinnamon
- Sway
- i3
- Other
- type: dropdown
id: window_system
attributes:
label: Linux Windowing System
description: If on Linux, please select your windowing system.
options:
- X11
- Wayland

View File

@@ -1,39 +0,0 @@
---
name: Release Preview Bug report
about: report a bug with a release preview (e.g., 2.6.0-beta1)
title:
labels: PRE-RELEASE BUG
assignees: droidmonkey
---
## Overview
[TIP]: # ( DO NOT include screenshots of your actual database! )
[NOTE]: # ( Give a BRIEF summary about your problem )
## Steps to Reproduce
[NOTE]: # ( Provide a simple set of steps to reproduce this bug. )
1.
2.
3.
## Expected Behavior
[NOTE]: # ( Tell us what you expected to happen )
## Actual Behavior
[NOTE]: # ( Tell us what actually happens )
## Context
[NOTE]: # ( Give us any additional information you may have. )
[NOTE]: # ( Paste debug info from Help → About here )
KeePassXC - VERSION
Revision: REVISION
[NOTE]: # ( Pick choices based on your environment )
Operating System: Windows/Linux/macOS
Desktop Env: Gnome/KDE/XFCE/Mate/Cinnamon
Windowing System: X11/Wayland

View File

@@ -1,15 +1,15 @@
[NOTE]: # ( Describe your changes in detail, why is this change required? )
[NOTE]: # ( Explain large or complex code modifications. )
[NOTE]: # ( If it fixes an open issue, please add "Fixes #XXX" )
[NOTE]: # ( If it fixes an open issue, please add "Fixes #XXX". )
## Screenshots
[TIP]: # ( Do not include screenshots of your actual database! )
[NOTE]: # ( Do not include screenshots of your actual database! )
[TIP]: # ( Use View -> Allow Screen Capture )
## Testing strategy
[NOTE]: # ( Please describe in detail how you tested your changes. )
[TIP]: # ( We expect new code to be covered by unit tests and documented with doc blocks! )
[TIP]: # ( We expect new code to be covered by unit tests and include helpful comments. )
## Type of change

View File

@@ -1,5 +1,36 @@
# Changelog
## 2.8.0 (Pending)
* Placeholder for future release notes
## 2.7.9 (2024-06-19)
### Changes
* Passkeys: Ability to easily remove a passkey from an entry [#10777]
* Snap: Use new desktop portal for native messaging integration [#10906]
### Fixes
* Improve entry placeholder/reference feature [#10846]
* Improve CSV importing when title field isn't specified [#10843]
* Improve encrypted Bitwarden importing [#10800]
* Improve database settings UX [#10821]
* Improve handling of clipboard actions from entry preview [#10810]
* Improve group/entry view resize behavior and set sensible defaults [#10641]
* Passkeys: Fix incorrect username fill [#10874]
* Passkeys: Return additional data to the extension [#10857]
* Fix password clear timer inconsistency on unlock view [#10708]
* Fix portability check [#10760]
* Fix page overflow on HTML exports [#10735]
* Fix broken builds when using system provided zxcvbn [#10717]
* Fix copy password button when text is selected [#10853]
* Fix tab ordering on application settings pages [#10907]
* SSH Agent: Fix broken decrypt button [#10638]
* Windows: Fix ALT Auto-Type modifier [#10795]
* Windows: Fix wrong DACL memory size allocation [#10712]
* macOS: Fix monospace font sizing [#10739]
* Flatpak: Fix configuration settings off-by-one error [#10688]
* BSD: Fix compiling with libusb implementation [#10736]
## 2.7.8 (2024-05-05)
### Changes

View File

@@ -137,10 +137,12 @@ Files: share/icons/badges/2_Expired.svg
share/icons/database/C46_Help.svg
share/icons/database/C53_Apply.svg
share/icons/database/C61_Services.svg
share/icons/application/scalable/actions/proton.svg
Copyright: 2022 KeePassXC Team <team@keepassxc.org>
License: MIT
Files: share/icons/application/scalable/actions/application-exit.svg
share/icons/application/scalable/actions/arrow-collapse-down.svg
share/icons/application/scalable/actions/attributes-copy.svg
share/icons/application/scalable/actions/auto-type.svg
share/icons/application/scalable/actions/bitwarden.svg
@@ -176,6 +178,7 @@ Files: share/icons/application/scalable/actions/application-exit.svg
share/icons/application/scalable/actions/entry-delete.svg
share/icons/application/scalable/actions/entry-restore.svg
share/icons/application/scalable/actions/entry-edit.svg
share/icons/application/scalable/actions/entry-expire.svg
share/icons/application/scalable/actions/entry-new.svg
share/icons/application/scalable/actions/favicon-download.svg
share/icons/application/scalable/actions/fingerprint.svg

View File

@@ -22,12 +22,13 @@ KeePassXC has numerous features for novice and power users alike. Our goal is to
* Password generator
* Auto-Type passwords into applications
* Browser integration with Google Chrome, Mozilla Firefox, Microsoft Edge, Chromium, Vivaldi, Brave, and Tor-Browser
* Support for passkeys using the browser integration
* Entry icon download
* Import databases from CSV, 1Password, and KeePass1 formats
* Import databases from CSV, 1Password, Bitwarden, Proton Pass, and KeePass1 formats
### Advanced
* Database reports (password health, HIBP, and statistics)
* Database export to CSV and HTML formats
* Database export to CSV, XML, and HTML formats
* TOTP storage and generation
* Field references between entries
* File attachments and custom attributes

46
SECURITY.md Normal file
View File

@@ -0,0 +1,46 @@
### Reporting Security Issues
The KeePassXC team takes security vulnerabilities very seriously and appreciates your responsible disclosure efforts. We will make every effort to acknowledge your contributions and handle them promptly.
To report a security issue, please use one of the following methods:
- **GitHub Security Advisory:** Use the ["Report a Vulnerability"](https://github.com/keepassxreboot/keepassxc/security/advisories/new) tab on our GitHub repository.
- **Private Matrix Message:** Contact any of the following KeePassXC team members privately (also encrypted):
- [@droidmonkey_kpxc](https://matrix.to/#/@droidmonkey_kpxc:matrix.org)
- [@varjolintu](https://matrix.to/#/@varjolintu:matrix.org)
- [@phoerious](https://matrix.to/#/@phoerious:matrix.org)
- **Send an Email:** Send your report to team@keepassxc.org. We recommend encrypting the email if possible.
Please **DO NOT** use public channels (e.g., GitHub issues, Matrix chat channels) for initial reporting of bona fide security vulnerabilities.
Once you report a security issue, our team will respond with the next steps. After our initial reply, we will keep you informed of the progress towards a fix and full announcement. We may ask for additional information or guidance during this process. If we disagree that your report constitutes a genuine security vulnerability, we will inform you and close the report. Your report may be turned into an issue for further tracking.
If you discover vulnerabilities in third-party modules used by KeePassXC, please report them to the maintainers of the respective modules. If the vulnerability impacts KeePassXC directly, we encourage you to notify us using the above methods. We will validate if the vulnerability is exploitable from KeePassXC code; please note that not all vulnerabilities are actually exploitable and do not constitute an immediate concern for the KeePassXC application.
### Example Security Vulnerabilities
When reporting, please ensure the issue falls under what can be considered a genuine security vulnerability for KeePassXC. Some examples include:
- Unauthorized access to sensitive user data (e.g., passwords).
- Remote code execution or escalation of privileges.
- Bypassing authentication or encryption mechanisms.
- Broken or improperly implemented encryption methods.
### Counter Examples
The following issues are **not** considered security vulnerabilities:
- Bugs caused by locally modifying the application (e.g., injecting DLLs, altering code).
- Crashes or misbehavior resulting from normal use (report this as a normal issue).
- Vulnerabilities found in third-party modules (should be reported to the modules maintainers).
### CVE Reporting Policy
Please **DO NOT** submit a report to a Common Vulnerabilities and Exposures (CVE) Numbering Authority (CNA) before confirming the security vulnerability with the KeePassXC team. If we do not respond to your report within 30 days, this restriction no longer applies.
### Other Communication
For other inquiries (e.g., developer questions, user questions), please use the public channels on Matrix:
- **User's Channel:** [#keepassxc:mozilla.org](https://matrix.to/#/#keepassxc:mozilla.org)
- **Developer's Channel:** [#keepassxc-dev:mozilla.org](https://matrix.to/#/#keepassxc-dev:mozilla.org)

View File

@@ -21,16 +21,38 @@ endif()
if(NOT PCSC_FOUND)
# Search for PC/SC headers on Mac and Windows
# Additional search paths for Windows if not running in Visual Studio environment
if (WIN32)
# Resolve the ambiguity of using two names for one architechture
if(CMAKE_SYSTEM_PROCESSOR STREQUAL "AMD64" OR CMAKE_SYSTEM_PROCESSOR STREQUAL "x64")
set(ARCH_DIR "x64")
else()
set(ARCH_DIR "${CMAKE_SYSTEM_PROCESSOR}")
endif()
# Locate Windows SDK Paths
if (CMAKE_WINDOWS_KITS_10_DIR)
set(WINSDKROOTC_INCLUDE "${CMAKE_WINDOWS_KITS_10_DIR}/Include/${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}/um")
set(WINSDKROOTC_LIB "${CMAKE_WINDOWS_KITS_10_DIR}/LIB/${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}/um/${ARCH_DIR}")
else()
set(WINSDKROOTC_INCLUDE "$ENV{ProgramFiles\(x86\)}/Windows Kits/10/Include/${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}/um")
set(WINSDKROOTC_LIB "$ENV{ProgramFiles\(x86\)}/Windows Kits/10/LIB/${CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION}/um/${ARCH_DIR}")
endif()
endif()
find_path(PCSC_INCLUDE_DIRS winscard.h
HINTS
${CMAKE_C_IMPLICIT_INCLUDE_DIRECTORIES}
/usr/include/PCSC
${CMAKE_C_IMPLICIT_INCLUDE_DIRECTORIES}
/usr/include/PCSC
${WINSDKROOTC_INCLUDE}
PATH_SUFFIXES PCSC)
# MAC library is PCSC, Windows library is WinSCard
find_library(PCSC_LIBRARIES NAMES pcsclite libpcsclite WinSCard PCSC
HINTS
${CMAKE_C_IMPLICIT_LINK_DIRECTORIES})
${CMAKE_C_IMPLICIT_LINK_DIRECTORIES}
${WINSDKROOTC_LIB})
endif()
include(FindPackageHandleStandardArgs)

View File

@@ -26,8 +26,8 @@ include::topics/DownloadInstall.adoc[tags=*;!advanced]
include::topics/UserInterface.adoc[tags=*;!advanced]
include::topics/PasswordGenerator.adoc[tags=*;!advanced]
include::topics/DatabaseOperations.adoc[tags=*;!advanced]
include::topics/BrowserPlugin.adoc[tags=*;!advanced]
include::topics/PasswordGenerator.adoc[tags=*;!advanced]
include::topics/BrowserIntegration.adoc[tags=*;!advanced]

View File

@@ -6,6 +6,7 @@ KeePassXC Team <team@keepassxc.org>
:imagesdir: images
:stylesheet: styles/dark.css
:toc: left
:sectanchors:
ifdef::backend-pdf[]
:title-page:
:title-logo-image: {imagesdir}/kpxc_logo.png
@@ -23,18 +24,18 @@ include::topics/UserInterface.adoc[tags=*]
include::topics/DatabaseOperations.adoc[tags=*]
include::topics/ImportExport.adoc[tags=*]
include::topics/PasswordGenerator.adoc[tags=*]
include::topics/BrowserPlugin.adoc[tags=*]
include::topics/ImportExport.adoc[tags=*]
include::topics/KeeShare.adoc[tags=*]
include::topics/BrowserIntegration.adoc[tags=*]
include::topics/Passkeys.adoc[tags=*]
include::topics/AutoType.adoc[tags=*]
include::topics/KeeShare.adoc[tags=*]
include::topics/SSHAgent.adoc[tags=*]
include::topics/Reference.adoc[tags=*]

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 60 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 87 KiB

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 95 KiB

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 38 KiB

View File

@@ -58,6 +58,9 @@ Your database works offline and requires no internet connection.
*--pw-stdin*::
Reads password of the database from stdin.
*--minimized*::
Starts KeePassXC minimized to the system tray.
*--debug-info*::
Displays debugging information.

View File

@@ -180,7 +180,7 @@ body.toc2.toc-right{padding-left:0;padding-right:20em}}
.sect1{padding-bottom:1.25em}}
.sect1:last-child{padding-bottom:0}
.sect1+.sect1{border-top:1px solid #efefed}
#content h1>a.anchor,h2>a.anchor,h3>a.anchor,#toctitle>a.anchor,.sidebarblock>.content>.title>a.anchor,h4>a.anchor,h5>a.anchor,h6>a.anchor{position:absolute;z-index:1001;width:1.5ex;margin-left:-1.5ex;display:block;text-decoration:none!important;visibility:hidden;text-align:center;font-weight:400}
#content h1>a.anchor,h2>a.anchor,h3>a.anchor,#toctitle>a.anchor,.sidebarblock>.content>.title>a.anchor,h4>a.anchor,h5>a.anchor,h6>a.anchor{position:absolute;z-index:1001;width:2.0ex;margin-left:-1.8ex;margin-top:0.08ex;display:block;text-decoration:none!important;visibility:hidden;text-align:center;font-weight:400}
#content h1>a.anchor::before,h2>a.anchor::before,h3>a.anchor::before,#toctitle>a.anchor::before,.sidebarblock>.content>.title>a.anchor::before,h4>a.anchor::before,h5>a.anchor::before,h6>a.anchor::before{content:"\00A7";font-size:.85em;display:block;padding-top:.1em}
#content h1:hover>a.anchor,#content h1>a.anchor:hover,h2:hover>a.anchor,h2>a.anchor:hover,h3:hover>a.anchor,#toctitle:hover>a.anchor,.sidebarblock>.content>.title:hover>a.anchor,h3>a.anchor:hover,#toctitle>a.anchor:hover,.sidebarblock>.content>.title>a.anchor:hover,h4:hover>a.anchor,h4>a.anchor:hover,h5:hover>a.anchor,h5>a.anchor:hover,h6:hover>a.anchor,h6>a.anchor:hover{visibility:visible}
#content h1>a.link,h2>a.link,h3>a.link,#toctitle>a.link,.sidebarblock>.content>.title>a.link,h4>a.link,h5>a.link,h6>a.link{color:#ba3925;text-decoration:none}

View File

@@ -37,7 +37,7 @@ TIP: You can use an asterisk (`\*`) to match any value (e.g., when a window titl
.Auto-Type entry sequences
image::autotype_entry_sequences.png[]
2. _(Optional)_ Define a custom Auto-Type sequence for each window title match by selecting the _Use specific sequence for this association_ checkbox. Sequence action codes and field placeholders are detailed in the following table. Beyond the most important ones detailed below, there are additional action codes and placeholders available: xref:UserGuide.adoc#_auto_type_actions[Auto-Type Actions Reference] and xref:UserGuide.adoc#_entry_placeholders[Entry Placeholders Reference]. Action codes and placeholders are not case sensitive.
2. _(Optional)_ Define a custom Auto-Type sequence for each window title match by selecting the _Use specific sequence for this association_ checkbox. Sequence action codes and field placeholders are detailed in the following table. Beyond the most important ones detailed below, there are additional action codes and placeholders available: <<Auto-Type Actions, Auto-Type Actions Reference>> and <<Entry Placeholders, Entry Placeholders Reference>>. Action codes and placeholders are not case sensitive.
+
[grid=rows, frame=none, width=90%]
|===

View File

@@ -1,190 +1,201 @@
= KeePassXC Browser Plugin
include::.sharedheader[]
:imagesdir: ../images
// tag::content[]
== Setup Browser Integration
The KeePassXC-Browser extension is installed within your web browser so that you can automatically pull usernames and passwords from KeePassXC and populate them directly into website fields. It is a very useful and secure extension that enhances your productivity while using KeePassXC. With this extension, you do not need to manually copy the data from your KeePassXC database and paste it into the website fields.
The KeePassXC-Browser extension is available on the following web browsers:
* Google Chrome, Vivaldi, and Brave
* Mozilla Firefox and Tor-Browser
* Microsoft Edge
* Chromium
=== Install the Browser Extension
You can download the KeePassXC-Browser extension from your web browser. To download the KeePassXC-Browser extension, perform the following steps:
1. Click the link corresponding to your browser:
* https://chromewebstore.google.com/detail/keepassxc-browser/oboonakemofpalcgghocfoadofidjkkk[Chrome, Chromium, Vivaldi, and Brave]
* https://addons.mozilla.org/en-US/firefox/addon/keepassxc-browser[Mozilla Firefox and Tor-Browser]
* https://microsoftedge.microsoft.com/addons/detail/keepassxcbrowser/pdffhmdngciaglkoonimfcmckehcpafo[Microsoft Edge]
2. Click the button to install/add the extension to the browser. Accept any confirmation dialogs.
TIP: For the most up-to-date troubleshooting advice on all platforms, please read our https://github.com/keepassxreboot/keepassxc-browser/wiki/Troubleshooting-guide[Troubleshooting Guide].
// tag::advanced[]
NOTE: When Microsoft Edge is installed as a managed application, system administrators are required to deploy a custom native messaging configuration. Instructions for this are found in the advanced section below.
// end::advanced[]
=== Configure KeePassXC-Browser
To start using KeePassXC-Browser, you must configure it so that it can communicate with the KeePassXC application on your desktop.
To configure KeePassXC-Browser, perform the following steps:
1. Open the KeePassXC application on your desktop and navigate to Tools > Settings.
2. Click the Browser Integration option on the left-hand side *(1)*. The following screen appears:
+
.Browser Settings
image::browser_settings.png[]
3. Click the _Enable browser integration_ checkbox *(2)*. Then select the browsers for which you have downloaded the KeePassXC-Browser extension *(3)* and click *OK*.
4. Ensure your database is unlocked, then open (or restart) your browser.
5. Click the KeePassXC-Browser extension icon *(A)* in your browser (see figure below). A pop-up window appears.
+
.Connect Extension to KeePassXC
image::browser_extension_connect.png[,80%]
6. Click the _Connect_ button *(B)* in the pop-up window to complete integrating the KeePassXC-Browser extension with your KeePassXC desktop application.
7. You are now prompted to enter a unique name to identify the connection between this browser and your database. Enter a unique name in the field (e.g., firefox-laptop) and click the _Save and allow access_ button.
+
.Extension Association Dialog
image::browser_extension_association.png[,80%]
WARNING: If you reuse a connection name in a database, the previous browser connection will be overwritten and prevent access.
=== Using the Browser Extension
The KeePassXC-Browser extension lets you automatically populate the entries from your KeePassXC database into the fields on websites you visit. To do so, perform the following steps:
1. Open your KeePassXC desktop application and unlock your database.
2. Open your web browser. The KeePassXC-Browser extension icon in your browser window will change based on its connection state. The figure below shows the different states.
+
*(A)* KeePassXC is not running or is disconnected. +
*(B)* KeePassXC is running, but KeePassXC Browser Extension is not connected to the current database. +
*\(C)* Connected to KeePassXC, but database is locked. +
*(D)* Connected to KeePassXC and ready to use. If the icon is shown with a number, it indicates the number of credentials found for the current site.
+
.Extension Icon States
image::browser_extension_icons.png[,70%]
3. If the KeePassXC desktop application is not connected with the KeePassXC-Browser extension, click the extension icon in your web browser and click _Reload_ from the pop-up window as shown in the following screen.
+
.Reload Extension Connection
image::browser_extension_reload.png[,80%]
4. Open the URL for which you want to use with your database. If you have previously created an entry in your database then the KeePassXC-Browser Confirm Access dialog may appear:
+
.Confirm Access Dialog
image::browser_confirm_access_dialog.png[,80%]
5. Ensure the credentials you want to use are checked, then click *(A)* Remember _(optional)_, then click _Allow Selected_ *(B)*.
6. In your website, the KeePassXC icon will appear in the username field of the login form *(A)*. Click the icon to populate the field with your stored credentials. If you have more than one credential for this website, a dropdown will appear to choose the one to use.
+
.Fill Credentials
image::browser_fill_credentials.png[,80%]
=== Generate Passwords
The KeePassXC-Browser Extension also lets you generate passwords directly in your browser.
This feature can be used for websites with existing credentials as well as for new websites.
You can then choose to update/add the credentials to your KeePassXC database directly from the Browser.
1. Ensure your database is unlocked and configured to use the Browser extension as shown above.
2. Right click on a password field and from the KeePassXC sub-menu choose _Show Password Generator_. The standard KeePassXC password generator will appear.
3. Configure the password generation options and click _Apply Password_ when done. The generated password will be filled into the previously selected field.
4. When you have successfully submitted the password on the website, a popup will appear asking you to either update an existing entry or add a new one.
// tag::advanced[]
=== Browser statistics
You can see a cross-section of all browser-related settings applied to entries within a database through the Browser Statistics report. To access these, use the _Database_ -> _Database reports..._ menu option then click on _Browser Statistics_ on the left-hand menu. From here you can see all entries with URLs applied to them, explicitly allowed and denied URLs, and any entries with custom browser settings.
.Browser statistics
image::browser_statistics.png[]
=== Advanced Usage
You can configure unique browser integration behavior for each entry. This allows you to add multiple URLs to an entry, hide an entry from the browser integration, and more. To access these settings, open an entry for editing then click on _Browser Integration_ option in the left-hand menu *(1)*.
After opening the settings you can add any number of additional URLs by clicking the _Add_ button *(2)* and typing the URL in the list to the left *(3)*.
.Entry browser settings
image::browser_entry_settings.png[]
To set options for all entries within a group, edit the group and go to the browser integration section *(1)*. Here you can explicitly disable access to all entries under a group hierarchy to the browser extension. You can set other useful options for groups of entries as well.
.Group browser settings
image::browser_group_settings.png[]
Database-wide operations are available in the database settings. To access these use the _Database_ -> _Database settings..._ menu option. Click on _Browser Integration_ on the left-hand menu. From here you can disconnect all browsers, convert legacy KeePass-HTTP settings, reset all entry-level settings, and refresh the database root group ID (useful when making copies of your database file).
.Database browser settings
image::browser_database_settings.png[]
Finally, advanced application-wide settings are available in the Browser Integration tab of the application settings.
WARNING: We do not recommend changing any of these settings as they may break the browser integration plugin.
.Advanced browser settings
image::browser_advanced_settings.png[]
=== Advanced Setup
==== Custom Browser option
It is possible to enable support for a custom browser (e.g. LibreWolf, WaterFox, Arc, beta and nightly browsers, etc.) using this feature.
This feature is only available for Linux and macOS.
.Custom browser configuration
image::browser_custom_browser_configuration.png[]
The native messaging script file needed for the custom browser depends on the browser type. For Firefox based browsers like Librefox the _Browser type_ must be _Firefox_. For Arc, Opera, etc. the type must be set to _Chromium_.
_Config location_ must have the exact path for the browser's _native-messaging-hosts_ folder. If you are unsure, refer to our https://github.com/keepassxreboot/keepassxc-browser/wiki/Troubleshooting-guide#1-after-enabling-browser-integration-and-support-for-your-browser[Troubleshooting Guide] for listing of the most common paths, and a few ways for finding a path when it's not known.
When a Custom Browser has been successfully set, KeePassXC will automatically write the needed native messaging script file to the folder.
If you wish to support multiple custom browsers, you can copy the native messaging script files manually to the _native-messaging-hosts_ folder from other browsers.
==== Managed Microsoft Edge on Windows
1. Deploy *org.keepassxc.keepassxc_browser_edge.json* to, for example, `C:\ProgramData\KeepassXC` on all managed platforms.
+
----
{
"allowed_origins": [
"chrome-extension://pdffhmdngciaglkoonimfcmckehcpafo/"
],
"description": "KeePassXC integration with native messaging support",
"name": "org.keepassxc.keepassxc_browser",
"path": "C:\\Program Files\\KeePassXC\\keepassxc-proxy.exe",
"type": "stdio"
}
----
2. Configure GPO options (registry result):
+
----
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Edge\NativeMessagingHosts\org.keepassxc.keepassxc_browser]
@="C:\ProgramData\KeepassXC\org.keepassxc.keepassxc_browser_edge.json"
[HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Edge]
"NativeMessagingUserLevelHosts"=dword:00000000
[HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Edge\ExtensionInstallAllowlist]
"1"="pdffhmdngciaglkoonimfcmckehcpafo"
[HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Edge\NativeMessagingAllowlist]
"1"="org.keepassxc.keepassxc_browser"
----
// end::advanced[]
// end::content[]
= KeePassXC Browser Plugin
include::.sharedheader[]
:imagesdir: ../images
// tag::content[]
== Browser Integration
The KeePassXC-Browser extension is installed within your web browser so that you can automatically pull usernames and passwords from KeePassXC and populate them directly into website fields. It is a very useful and secure extension that enhances your productivity while using KeePassXC. With this extension, you do not need to manually copy the data from your KeePassXC database and paste it into the website fields.
The KeePassXC-Browser extension is available on the following web browsers:
* Google Chrome, Vivaldi, and Brave
* Mozilla Firefox and Tor-Browser
* Microsoft Edge
* Chromium
NOTE: On Linux, Flatpak and Snap based browsers are generally not supported. Ubuntu's Firefox Snap is currently the only known exception.
=== Install the Browser Extension
You can download the KeePassXC-Browser extension from your web browser. To download the KeePassXC-Browser extension, perform the following steps:
1. Click the link corresponding to your browser:
* https://chromewebstore.google.com/detail/keepassxc-browser/oboonakemofpalcgghocfoadofidjkkk[Chrome, Chromium, Vivaldi, and Brave]
* https://addons.mozilla.org/en-US/firefox/addon/keepassxc-browser[Mozilla Firefox and Tor-Browser]
* https://microsoftedge.microsoft.com/addons/detail/keepassxcbrowser/pdffhmdngciaglkoonimfcmckehcpafo[Microsoft Edge]
2. Click the button to install/add the extension to the browser. Accept any confirmation dialogs.
TIP: For the most up-to-date troubleshooting advice on all platforms, please read our https://github.com/keepassxreboot/keepassxc-browser/wiki/Troubleshooting-guide[Troubleshooting Guide].
// tag::advanced[]
NOTE: When Microsoft Edge is installed as a managed application, system administrators are required to deploy a custom native messaging configuration. Instructions for this are found in the advanced section below.
// end::advanced[]
=== Configure KeePassXC-Browser
To start using KeePassXC-Browser, you must configure it so that it can communicate with the KeePassXC application on your desktop.
To configure KeePassXC-Browser, perform the following steps:
1. Open the KeePassXC application on your desktop and navigate to Tools > Settings.
2. Click the Browser Integration option on the left-hand side *(1)*. The following screen appears:
+
.Browser Settings
image::browser_settings.png[]
3. Click the _Enable browser integration_ checkbox *(2)*. Then select the browsers for which you have downloaded the KeePassXC-Browser extension *(3)* and click *OK*.
4. Ensure your database is unlocked, then open (or restart) your browser.
5. Click the KeePassXC-Browser extension icon *(A)* in your browser (see figure below). A pop-up window appears.
+
.Connect Extension to KeePassXC
image::browser_extension_connect.png[,80%]
6. Click the _Connect_ button *(B)* in the pop-up window to complete integrating the KeePassXC-Browser extension with your KeePassXC desktop application.
7. You are now prompted to enter a unique name to identify the connection between this browser and your database. Enter a unique name in the field (e.g., firefox-laptop) and click the _Save and allow access_ button.
+
.Extension Association Dialog
image::browser_extension_association.png[,80%]
WARNING: If you reuse a connection name in a database, the previous browser connection will be overwritten and prevent access.
=== Using the Browser Extension
The KeePassXC-Browser extension lets you automatically populate the entries from your KeePassXC database into the fields on websites you visit. To do so, perform the following steps:
1. Open your KeePassXC desktop application and unlock your database.
2. Open your web browser. The KeePassXC-Browser extension icon in your browser window will change based on its connection state. The figure below shows the different states.
+
*(A)* KeePassXC is not running or is disconnected. +
*(B)* KeePassXC is running, but KeePassXC Browser Extension is not connected to the current database. +
*\(C)* Connected to KeePassXC, but database is locked. +
*(D)* Connected to KeePassXC and ready to use. If the icon is shown with a number, it indicates the number of credentials found for the current site.
+
.Extension Icon States
image::browser_extension_icons.png[,70%]
3. If the KeePassXC desktop application is not connected with the KeePassXC-Browser extension, click the extension icon in your web browser and click _Reload_ from the pop-up window as shown in the following screen.
+
.Reload Extension Connection
image::browser_extension_reload.png[,80%]
4. Open the URL for which you want to use with your database. If you have previously created an entry in your database then the KeePassXC-Browser Confirm Access dialog may appear:
+
.Confirm Access Dialog
image::browser_confirm_access_dialog.png[,80%]
5. Ensure the credentials you want to use are checked, then click *(A)* Remember _(optional)_, then click _Allow Selected_ *(B)*.
6. In your website, the KeePassXC icon will appear in the username field of the login form *(A)*. Click the icon to populate the field with your stored credentials. If you have more than one credential for this website, a dropdown will appear to choose the one to use.
+
.Fill Credentials
image::browser_fill_credentials.png[,80%]
=== Generate Passwords
The KeePassXC-Browser Extension also lets you generate passwords directly in your browser.
This feature can be used for websites with existing credentials as well as for new websites.
You can then choose to update/add the credentials to your KeePassXC database directly from the Browser.
1. Ensure your database is unlocked and configured to use the Browser extension as shown above.
2. Right click on a password field and from the KeePassXC sub-menu choose _Show Password Generator_. The standard KeePassXC password generator will appear.
3. Configure the password generation options and click _Apply Password_ when done. The generated password will be filled into the previously selected field.
4. When you have successfully submitted the password on the website, a popup will appear asking you to either update an existing entry or add a new one.
// tag::advanced[]
=== Browser statistics
You can see a cross-section of all browser-related settings applied to entries within a database through the Browser Statistics report. To access these, use the _Database_ -> _Database reports..._ menu option then click on _Browser Statistics_ on the left-hand menu. From here you can see all entries with URLs applied to them, explicitly allowed and denied URLs, and any entries with custom browser settings.
.Browser statistics
image::browser_statistics.png[]
=== Advanced Usage
You can configure unique browser integration behavior for each entry. This allows you to add multiple URLs to an entry, hide an entry from the browser integration, and more. To access these settings, open an entry for editing then click on _Browser Integration_ option in the left-hand menu *(1)*.
After opening the settings you can add any number of additional URLs by clicking the _Add_ button *(2)* and typing the URL in the list to the left *(3)*.
Additional URLs also supports wildcards (with KeePassXC 2.7.10 and later). You can use URLs like:
----
https://*.example.com
https://example.com/*/path
https://sub.*.example.com/path/*
----
.Entry browser settings
image::browser_entry_settings.png[]
To set options for all entries within a group, edit the group and go to the browser integration section *(1)*. Here you can explicitly disable access to all entries under a group hierarchy to the browser extension. You can set other useful options for groups of entries as well.
.Group browser settings
image::browser_group_settings.png[]
Database-wide operations are available in the database settings. To access these use the _Database_ -> _Database settings..._ menu option. Click on _Browser Integration_ on the left-hand menu. From here you can disconnect all browsers, convert legacy KeePass-HTTP settings, reset all entry-level settings, and refresh the database root group ID (useful when making copies of your database file).
.Database browser settings
image::browser_database_settings.png[]
Finally, advanced application-wide settings are available in the Browser Integration tab of the application settings.
WARNING: We do not recommend changing any of these settings as they may break the browser integration plugin.
.Advanced browser settings
image::browser_advanced_settings.png[]
=== Advanced Setup
==== Custom Browser option
It is possible to enable support for a custom browser (e.g. LibreWolf, WaterFox, Arc, beta and nightly browsers, etc.) using this feature.
This feature is only available for Linux and macOS.
.Custom browser configuration
image::browser_custom_browser_configuration.png[]
The native messaging script file needed for the custom browser depends on the browser type. For Firefox based browsers like Librefox the _Browser type_ must be _Firefox_. For Arc, Opera, etc. the type must be set to _Chromium_.
_Config location_ must have the exact path for the browser's _native-messaging-hosts_ folder. If you are unsure, refer to our https://github.com/keepassxreboot/keepassxc-browser/wiki/Troubleshooting-guide#1-after-enabling-browser-integration-and-support-for-your-browser[Troubleshooting Guide] for listing of the most common paths, and a few ways for finding a path when it's not known.
When a Custom Browser has been successfully set, KeePassXC will automatically write the needed native messaging script file to the folder.
If you wish to support multiple custom browsers, you can copy the native messaging script files manually to the _native-messaging-hosts_ folder from other browsers.
==== Managed Microsoft Edge on Windows
1. Deploy *org.keepassxc.keepassxc_browser_edge.json* to, for example, `C:\ProgramData\KeePassXC\` on all managed platforms.
+
----
{
"allowed_origins": [
"chrome-extension://pdffhmdngciaglkoonimfcmckehcpafo/"
],
"description": "KeePassXC integration with native messaging support",
"name": "org.keepassxc.keepassxc_browser",
"path": "C:\\Program Files\\KeePassXC\\keepassxc-proxy.exe",
"type": "stdio"
}
----
2. Configure GPO options (see https://learn.microsoft.com/en-us/deployedge/microsoft-edge-policies#native-messaging[Microsoft Edge Native Messaging Policies] for more information.):
+
----
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Edge\NativeMessagingHosts\org.keepassxc.keepassxc_browser]
@="C:\ProgramData\KeepassXC\org.keepassxc.keepassxc_browser_edge.json"
[HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Edge]
"NativeMessagingUserLevelHosts"=dword:00000000
[HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Edge\ExtensionInstallAllowlist]
"1"="pdffhmdngciaglkoonimfcmckehcpafo"
[HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\Edge\NativeMessagingAllowlist]
"1"="org.keepassxc.keepassxc_browser"
----
==== Managed Microsoft Edge on macOS
1. Deploy *org.keepassxc.keepassxc_browser_edge.json* to `/Library/Microsoft/Edge/NativeMessagingHosts`.
2. You may need to configure Edge to allowlist the extension and native messaging host. See https://learn.microsoft.com/en-us/deployedge/microsoft-edge-policies#native-messaging[Microsoft Edge Native Messaging Policies] for more information.
// end::advanced[]
// end::content[]

View File

@@ -36,6 +36,13 @@ NOTE: Keep this password for your database safe. Either memorize it or note it d
5. Click Done. You will be prompted to select a location to save your database file. The database file is saved on to your computer with the default `.kdbx` extension. You can store your database wherever you wish, it is fully encrypted at all times preventing unauthorized access.
=== Storing Your Database
The database file that you create might contain highly sensitive data and must be stored in a very secure way. You must make sure that the database is always protected with a strong and long password. The database file that is protected with a strong and long password is secure and encrypted while stored on your computer or cloud storage service.
Make sure that you or someone else does not accidentally delete the database file. Deletion of the database file will result in the total loss of all your information (including all your passwords!) and a lot of inconvenience to manually retrieve your logins for various web applications. Do not share the credentials to access your database file with anyone unless you absolutely trust them (spouse, child, etc.).
TIP: You can safely store your database file in the cloud (OneDrive, Dropbox, Google Drive, Nextcloud, Syncthing, etc.). The database file is always fully encrypted; unencrypted data is never written to disk and is never accessible to your cloud storage provider. We recommend using a storage service that keeps automatic backups (version history) of your database file in the event of corruption or accidental deletion.
=== Opening an Existing Database
To open an existing database, perform the following steps:
@@ -51,9 +58,11 @@ image::unlock_database.png[]
3. Enter the password for your database.
4. _(Optional)_ Browse for the Key File if you have chosen it as an additional authentication factor while creating the database. Refer to the KeePassXC User Guide for more information on setting a Key File as an additional authentication factor.
4. _(Optional)_ Click *I have a key file (A)* if you have one as an additional authentication factor for your database.
5. Click *OK*. The database opens and the following screen is displayed:
5. _(Optional)_ Plug in your configured YubiKey or OnlyKey to use it as an additional authentication factor. If you don't see it listed, press the refresh button *(B)*.
6. Click *OK*. The database opens and the following screen is displayed:
+
.Unlocked database
image::database_view.png[]
@@ -72,24 +81,13 @@ When your database is locked, you will see the following unlock dialog. Simply p
image::quick_unlock.png[]
// tag::advanced[]
=== Expired Entries
By default, KeePassXC will show entries that are expired or will be expiring within 3 days after unlocking the database. This feature allows you to change your passwords before they expire and be aware of passwords that are no longer valid. You can disable or change this feature in the Application Settings.
NOTE: By default, KeePassXC will show entries that are expired or will be expiring within 3 days after unlocking the database. This feature allows you to change your passwords before they expire and be aware of passwords that are no longer valid. You can disable or change this feature in the Application Settings.
=== Advanced Save Options
There are three ways that KeePassXC can handle database files. This behavior is set in the Application Settings under _File Operations_.
1. _(Default)_ *Safe saves* create a temporary database file alongside the existing one and atomically move it into place when all writing is complete. This prevents database corruption in the case of application crashes, loss of power, or other interruptions.
2. *Temporary file saves* create a database in the temporary files folder. This database is then moved into place overtop of the existing file. Although rare, interruptions in this move process could leave your database in an unknown state. This option is useful for overcoming poorly behaved cloud sync tools.
3. *Direct-write saves* write directly to the existing database file. This is an unsafe operation since any interruption can leave your entire database inaccessible. We only recommend using this option when interfacing with Linux GVFS services (e.g. Google Cloud on Gnome) and other types of storage services that host a virtual drive system.
In addition to these save options, KeePassXC can create a backup of your existing database file just prior to saving. This backup will be saved at the path specified in the *Backup destination* field. This path can be absolute or relative. The latter will be resolved according to the databases path. It is possible to specify a custom naming scheme with placeholders. See xref:UserGuide.adoc#_backup_path_placeholders[Backup Path Placeholders] for available placeholders and examples.
image::save_options.png[]
// end::advanced[]
=== Entry Handling
Entries in KeePassXC are the fundamental units where all your sensitive information is stored. Each entry can contain various fields such as usernames, passwords, URLs, attachments, and notes. You can create, edit, clone, and delete entries as needed. Additionally, KeePassXC supports advanced features like TOTP for two-factor authentication, custom attributes, and entry history to track changes over time. Proper management of entries ensures that your data is organized, secure, and easily accessible when needed.
=== Adding an Entry
==== Adding an Entry
All the details such as usernames, passwords, URLs, attachments, notes, and so on are stored in database entries. You can create as many entries as you want in the database.
To add an entry, perform the following step:
@@ -112,7 +110,7 @@ image::edit_entry.png[]
5. Click *OK* to add the entry to your database.
=== Editing an Entry
==== Editing an Entry
To edit the details in an entry, perform the following steps:
1. Select the entry you want to edit.
@@ -123,7 +121,7 @@ To edit the details in an entry, perform the following steps:
4. Click *OK*.
=== Adding TOTP to an Entry
==== Adding TOTP to an Entry
Timed One-Time Passwords (TOTP) are a popular choice for two-factor authentication methods. These codes are typically six digits long and change every 30 seconds. They are derived from a shared secret value and the current time. Once set up, KeePassXC can calculate TOTP codes like any authenticator app, such as Google Authenticator. The codes can be used with copy/paste, browser extension, and Auto-Type.
TIP: Your computer time must be synchronized with an internet time source to generate valid TOTP codes, https://www.nist.gov/pml/time-and-frequency-division/time-distribution/internet-time-service-its[read more here].
@@ -145,7 +143,17 @@ After an entry is configured with TOTP, you will see a clock icon in that entry'
.TOTP Usage
image::totp_usage_examples.png[]
=== Deleting an Entry
==== Entry Icons
You can select an icon to be displayed with each entry for easy identification. KeePassXC comes with a set of default icons that you can use or you can use your own custom icons. If you defined a URL with an entry, you can also download the favorite icon for that particular website.
NOTE: To delete a custom icon, go to <<Database Maintenance>> where you can purge unused icons and delete one or more icons at a time.
.Entry icon selection
image::edit_entry_icons.png[]
TIP: Each KeePass application has different default icons. If you use a mobile app or KeePass2, be aware that the default icons may not be exactly correspond to the KeePassXC icons.
==== Deleting an Entry
To delete an entry, perform the following steps:
1. Select the entry you want to delete and press the `Delete` button on your keyboard.
@@ -157,7 +165,7 @@ NOTE: You can disable the recycle bin within the Database Settings. If the recyc
3. To permanently delete the entry, navigate to the Recycle Bin, select the entry you want to delete and press the `Delete` button on your keyboard.
// tag::advanced[]
=== Clone an Entry
==== Clone an Entry
Creating a clone of an entry provides you a ready-to-use template for creating new entries with similar details of a master entry.
To create a clone of an existing entry, perform the following steps:
@@ -180,12 +188,73 @@ image::clone_entry_dialog.png[,50%]
.References in a cloned entry
image::clone_entry_references.png[]
4. You can create your own references using the xref:UserGuide.adoc#_entry_cross_reference[Entry Reference Syntax]
4. You can create your own references using the <<Entry Cross-Reference, Entry Reference Syntax>>
== Searching the Database
KeePassXC provides an enhanced and granular search features the enables you to search for specific entries in the databases using the different modifiers, wild card characters, and logical operators.
==== Entry URL Handling
KeePassXC can handle URLs in various ways. Standard URLs will be opened in your default browser. URLs that start with schemas handled by your Operating System will launch the associated application, for example `ftp://` or `ssh://`. You can also use the following URL schemas to perform specific actions:
=== Modifiers and Fields
|===
|Schema | Example | Description
|cmd://
|`cmd://ssh {USERNAME}@example.com -p 2222`
|Launches the specified command line executable with the specified arguments. The executable must be present on your PATH or an absolute path must be specified.
|kdbx://
|`kdbx://~/dbs/passwords.kdbx`
|Opens the specified database file. Set the entry's username to the keyfile path (if required) and password to the database password. The database will open in a new tab.
|===
=== Advanced Entry Handling
KeePassXC offers several advanced options for managing your database entries. Additional Attributes allow you to store extra information required by some applications and websites. Attachments enable you to attach files to entries, stored as encrypted binaries, which can be previewed directly in the application (text and images). Icons can be selected or downloaded for easy identification of entries. The Properties section lets you view basic properties such as creation, modification, and last accessed times, and retrieve an entry's UUID for references. KeePassXC also maintains a history of changes to entries, allowing you to view, restore, or delete previous versions of an entry.
==== Additional Attributes
A lot of applications and web sites now require providing additional information when you create accounts. The additional information is used to block hackers if any suspicious activity is detected. In addition, the additional information you provide can be used to reset passwords if you forget them. You can also store arbitrary information here that can be copied to the clipboard or Auto-Typed using the `{S:<ATTR_NAME>}` action code.
To protect an attribute from being displayed by default, activate the _Protect_ checkbox *(A)*. To show the contents of the attribute while keeping it protected, press the _Reveal_ button *(B)*.
.Additional attributes example
image::edit_entry_attributes.png[]
==== Attachments
You can attach files to any entry in your database by pressing the _Add_ button *(A)*. These files are added to the database and stored as encrypted binaries. You can open, save, or delete attachments from this interface *(B)*.
NOTE: When you try to open the attached file, KeePassXC extracts the attachment to a temporary file and opens it using the default application associated with the file type. After finishing viewing or editing the file, you can choose between importing or discarding the changes that you made to the temporary file. KeePassXC securely deletes the temporary file by overwriting it.
.Attachments interface
image::edit_entry_attachments.png[]
==== Foreground and Background Color
You can change the foreground *(A)* and/or background *(B)* color that this entry will use in the entry lists. Click the corresponding box to open the color picker dialog.
.Color picker dialog
image::edit_entry_colors.png[]
==== Properties
KeePassXC lets you view the basic properties such as date and time of creation, modification, and when last accessed. This is also where you can retrieve an entry's UUID for use in references.
.Entry properties view
image::edit_entry_properties.png[]
==== History
KeePassXC maintains a history of changes you make to your entries. Each time you change an entry, KeePassXC automatically creates a backup copy of the current, non-modified entry before saving the new values. You can view the changes you made previously, restore, and delete the history of changes you made. The age of the history item, the changes that were made, and the entry's size are shown in the table view.
* Show: Display this history item for review, a read-only copy of the entry will be shown.
* Restore: Reinstate the selected history item as the active entry details.
* Delete: Delete the selected history item.
* Delete All: Delete the entire history for this entry.
.Entry history view
image::edit_entry_history.png[]
NOTE: Restoring an old history item will store the current entry settings as a new history item.
// end::advanced[]
=== Search
KeePassXC provides a robust search that enables you to find specific entries in the databases using different modifiers, wild card characters, and logical operators. By default, search considers the following fields when matching your query: Title, Username, URL, Tags, and Notes. To include other fields and/or narrow your search to specific fields, you can use the search syntax described below.
==== Modifiers and Fields
[grid=rows, frame=none, width=70%]
|===
|Modifier |Description
@@ -201,14 +270,15 @@ The following fields can be searched along with their abbreviated name in parent
* Title (t)
* Username (u)
* Password (p, pw)
* URL
* URL (url)
* Notes (n)
* Attribute names and values (attr)
* Attachment (attach)
* Group (g)
* Tags (tag)
* Entry State (is:expired, is:weak)
=== Wild Card Characters and Logical Operators
==== Wild Card Characters and Logical Operators
[grid=rows, frame=none, width=70%]
|===
|Wild Card Character |Description
@@ -218,7 +288,7 @@ The following fields can be searched along with their abbreviated name in parent
|\| |Logical OR
|===
=== Sample Search Queries
==== Sample Search Queries
The following tables lists a few samples search queries for your reference:
|===
@@ -236,63 +306,39 @@ The following tables lists a few samples search queries for your reference:
|`+attr:mystring123`
|Searches all additional attributes for any name OR value equal to mystring123.
|`+tag:personal`
| Search exactly for the 'personal' tag and do not include tags such as 'my personal'.
|`is:expired is:weak`
|Searches for all expired entries with weak passwords.
|===
== Advanced Entry Options
=== Additional Attributes
A lot of applications and web sites now require providing additional information when you create accounts. The additional information is used to block hackers if any suspicious activity is detected. In addition, the additional information you provide can be used to reset passwords if you forget them. You can also store arbitrary information here that can be copied to the clipboard or Auto-Typed using the `{S:<ATTR_NAME>}` action code.
// tag::advanced[]
=== Merging Databases
KeePassXC allows you to merge entries from one database into another through the _Database_ -> _Merge From Database_ menu item. When merging, entries from the specified database will be imported into your currently open database. The merge process compares entries based on their unique identifiers (UUIDs) and modified timestamp. When an entry UUID matches, no matter which group it is in, the most recently modified version will be made the current and the previous version will be placed into the entry's history. Any new entries and/or groups will be added to the open database. This feature is useful for consolidating multiple databases or synchronizing databases from conflict files in a cloud storage system.
To protect an attribute from being displayed by default, activate the _Protect_ checkbox *(A)*. To show the contents of the attribute while keeping it protected, press the _Reveal_ button *(B)*.
NOTE: When you delete entries, a record of that deletion (the entry UUID) is stored to prevent that entry from reappearing from a merge operation. An existing entry that has the same UUID as a deleted item will be removed from the database without prompt.
.Additional attributes example
image::edit_entry_attributes.png[]
=== Advanced Save Options
There are three ways that KeePassXC can handle database files. This behavior is set in the Application Settings under _File Operations_.
=== Attachments
You can attach files to any entry in your database by pressing the _Add_ button *(A)*. These files are added to the database and stored as encrypted binaries. You can open, save, or delete attachments from this interface *(B)*.
1. _(Default)_ *Safe saves* create a temporary database file alongside the existing one and atomically move it into place when all writing is complete. This prevents database corruption in the case of application crashes, loss of power, or other interruptions.
NOTE: When you try to open the attached file, KeePassXC extracts the attachment to a temporary file and opens it using the default application associated with the file type. After finishing viewing or editing the file, you can choose between importing or discarding the changes that you made to the temporary file. KeePassXC securely deletes the temporary file by overwriting it.
2. *Temporary file saves* create a database in the temporary files folder. This database is then moved into place overtop of the existing file. Although rare, interruptions in this move process could leave your database in an unknown state. This option is useful for overcoming poorly behaved cloud sync tools.
.Attachments interface
image::edit_entry_attachments.png[]
3. *Direct-write saves* write directly to the existing database file. This is an unsafe operation since any interruption can leave your entire database inaccessible. We only recommend using this option when interfacing with Linux GVFS services (e.g. Google Cloud on Gnome) and other types of storage services that host a virtual drive system.
=== Foreground and Background Color
You can change the foreground *(A)* and/or background *(B)* color that this entry will use in the entry lists. Click the corresponding box to open the color picker dialog.
=== Database Backup Options
In addition to these save options, KeePassXC can create a backup of your existing database file just prior to saving. This backup will be saved at the path specified in the *Backup destination* field. This path can be absolute or relative. The latter will be resolved according to the databases path. It is possible to specify a custom naming scheme with placeholders. See <<Backup Path Placeholders, Backup Path Placeholders>> for available placeholders and examples.
.Color picker dialog
image::edit_entry_colors.png[]
image::save_options.png[]
=== Icons
You can select an icon to be displayed with each entry for easy identification. KeePassXC comes with a set of default icons that you can use or you can use your own custom icons. If you defined a URL with an entry, you can also download the favorite icon for that particular website.
Alternatively, backups can be created on-demand using the _Database_ -> _Save Database Backup..._ menu feature.
NOTE: To delete a custom icon, go to xref:UserGuide.adoc#_database_maintenance[Database Maintenance] where you can purge unused icons and delete one or more icons at a time.
.Saving a database backup
image::save_database_backup.png[,40%]
.Entry icon selection
image::edit_entry_icons.png[]
TIP: Each KeePass application has different default icons. If you use a mobile app or KeePass2, be aware that the default icons may not be exactly correspond to the KeePassXC icons.
=== Properties
KeePassXC lets you view the basic properties such as date and time of creation, modification, and when last accessed. This is also where you can retrieve an entry's UUID for use in references.
.Entry properties view
image::edit_entry_properties.png[]
=== History
KeePassXC maintains a history of changes you make to your entries. Each time you change an entry, KeePassXC automatically creates a backup copy of the current, non-modified entry before saving the new values. You can view the changes you made previously, restore, and delete the history of changes you made. The age of the history item, the changes that were made, and the entry's size are shown in the table view.
* Show: Display this history item for review, a read-only copy of the entry will be shown.
* Restore: Reinstate the selected history item as the active entry details.
* Delete: Delete the selected history item.
* Delete All: Delete the entire history for this entry.
.Entry history view
image::edit_entry_history.png[]
NOTE: Restoring an old history item will store the current entry settings as a new history item.
== Automatic Database Opening
=== Automatic Database Opening
You can setup one or more databases to open automatically when you unlock a single database. This is done by *(1)* defining a special group named `AutoOpen` with *(2)* entries that contain the file path and credentials for each database that should be opened. There is no limit to the number of databases that can be opened.
TIP: Case matters with auto open, the group name must be exactly `AutoOpen` and it must be a child of the root group.
@@ -329,6 +375,7 @@ image::database_settings.png[]
* *Database name:* This is the default identifier for your database and is shown in the tab bar and title bar (when active). You can change this name as desired.
* *Database description:* Provide some meaningful description for your database.
* *Default username:* Provide a default username for all new entries that you create in this database.
* *Public Databse Metadata:* Here you can set a public (unencrypted) name, icon, and color for your database. This is used on the database unlock screen to help distinguish multiple databases from each other.
* *Max history items:* This is the maximum number of history items that are stored for each entry. When you set this to 0, no history will be saved. Set this value to a low value to prevent the database from getting too large (we recommend no more than 10).
* *Max. history size:* When the history of an entry gets above this size, it is truncated. For example, this happens when entries have large attachments. Set this value small to prevent the database from getting too large (we recommend 6 MiB).
* *Use recycle bin:* Select this check-box if you want deleted entries to move to the recycle bin instead of being permanently removed. The recycle bin will be created if it does not already exist after your first deletion. To delete entries permanently, you must empty the recycle bin manually.
@@ -365,31 +412,11 @@ The following key derivation functions are supported:
* Argon2 (KDBX 4 recommended): KDBX 4, the Argon2 key derivation function can be used for transforming the composite master key (as protection against dictionary attacks). The main advantage of Argon2 over AES-KDF is that it provides a better resistance against GPU/ASIC attacks (due to being a memory-hard function). The number of iterations scales linearly with the required time. By increasing the memory parameter, GPU/ASIC attacks become harder and the required time increases. The parallelism parameter can be used to specify how many threads should be used. We recommend using Argon2id to prevent against timing-based attacks. Argon2d offers maximum compatibility with other KeePass-based apps, the default settings provide sufficient protection against any known attacks.
== Database Maintenance
=== Database Maintenance
KeePassXC offers some maintenance features that can be applied to clean up your database. Navigate to _Database_ -> _Database settings_ then click on _Maintenance_ on the left hand panel. The following screen appears. On this screen you can delete multiple icons at once and purge any unused icons in your database.
image::database_maintenance.png[]
=== Creating a YubiKey backup
It is advisable to have a backup replica YubiKey In case your main YubiKey gets damaged, lost, or stolen. The same HMAC key will need to be written to both keys. To do this you can either use the YubiKey Personalization Tool GUI or the ykpersonalize CLI tool. The steps for the CLI tool are shown:
1. Create a 20 byte HMAC key:
+
```
dd status=none if=/dev/random bs=20 count=1 | xxd -p -c 40
```
2. Write the HMAC key to slot 2 _(Set through the first switch. Out of the box the YubiKey OTP resides in slot 1)_:
+
```
ykpersonalize -2 -a -ochal-resp -ochal-hmac -ohmac-lt64 -oserial-api-visible -oallow-update
```
You will be asked to enter the HMAC key you created earlier, copy/paste they key output in the first step. Repeat step 2 for your second YubiKey using the same HMAC key from before. We recommend storing your HMAC key in a safe place (e.g., printed on paper) in case you need to recreate another key.
== Command Line Tool
KeePassXC comes with the command line tool *keepassxc-cli* to access, view, and manipulate your database directly from a terminal window. The tool is documented through a separate man page, which can be shown using `man keepassxc-cli`, or through the on-demand help using `keepassxc-cli [command] -h`. An online version of the man page is https://github.com/keepassxreboot/keepassxc/blob/master/docs/man/keepassxc-cli.1.adoc[available on GitHub].
== Remote database support
KeePassXC provides support for syncing database files that reside in a remote location. If you can download/upload the database file via a commandline tool (e.g. rsync, ssh, scp etc.) KeePassXC offers easy to use functionality to sync the remote database.
@@ -408,17 +435,4 @@ Select the remote sync command from the _Database > Remote Sync…_ menu to star
WARNING: In case the remote database is changed by another user/process after the downloading command finishes and before uploading again, those changes will be overwritten. Syncing is not an atomic operation.
// end::advanced[]
== Storing a Database File
The database file that you create might contain highly sensitive data and must be stored in a very secure way. You must make sure that the database is always protected with a strong and long password. The database file that is protected with a strong and long password is secure and encrypted while stored on your computer or cloud storage service.
Make sure that you or someone else does not accidentally delete the database file. Deletion of the database file will result in the total loss of all your information (including all your passwords!) and a lot of inconvenience to manually retrieve your logins for various web applications. Do not share the credentials to access your database file with anyone unless you absolutely trust them (spouse, child, etc.).
TIP: You can safely store your database file in the cloud (OneDrive, Dropbox, Google Drive, Nextcloud, Syncthing, etc.). The database file is always fully encrypted; unencrypted data is never written to disk and is never accessible to your cloud storage provider. We recommend using a storage service that keeps automatic backups (version history) of your database file in the event of corruption or accidental deletion.
== Backing up a Database File
It is a good practice to create copies of your database file and store the copies of your database on a different computer, smart phone, or cloud storage space such a Google Drive or Microsoft OneDrive. Backups can be created automatically by selecting the _Backup database file before saving_ option in the application settings. Additionally, you can create a backup on-demand using the _Database_ -> _Save Database Backup..._ menu feature.
.Saving a database backup
image::save_database_backup.png[,40%]
// end::content[]

View File

@@ -3,13 +3,14 @@ include::.sharedheader[]
:imagesdir: ../images
// tag::content[]
== Importing External Databases
== Importing Databases
KeePassXC allows you to import external databases from the following options:
* Comma Separated Values (.csv)
* 1Password Export (.1pux)
* 1Password Vault (.opvault)
* Bitwarden (.json)
* Proton Pass (.json)
* KeePass 1 Database (.kdb)
* Remote database (.kdbx)
@@ -32,14 +33,17 @@ image::csv_import.png[]
3. Click `Done` to complete the import. If you chose to create a new database, the New Database dialog will appear. Otherwise your entries will be nested under the group you chose for the existing database.
=== Importing 1Password Export
=== Importing from Other Applications
KeePassXC allows you to import databases from various applications including 1Password (1PUX and OPVault), Bitwarden, and Proton Pass. Each import option involves selecting the file, providing necessary credentials (if required), and choosing to import into a new or existing database. Note that CSV, 1Password Export, Bitwarden, and Proton Pass files are unencrypted and should be securely deleted after import.
==== 1Password Export
WARNING: A 1Password Export file is unencrypted and you should securely delete this file after successfully importing it into KeePassXC.
1. Open the Import Wizard as shown above. Select the 1Password Export option.
2. Click `Continue` to unlock and preview the import. Click `Done` to complete the import.
=== Importing 1Password OPVault
==== 1Password OPVault
NOTE: You must have 1Password version 7 or 8 to export your data to an OPVault. If you are using a newer version of 1Password, you should use the 1Password Export (1PUX) format instead.
Save your 1Password Vault locally to create an OPVault directory. Please see 1Password instructions on how to do this. Once an OPVault is created, perform the following steps:
@@ -48,7 +52,7 @@ Save your 1Password Vault locally to create an OPVault directory. Please see 1Pa
2. Enter the password for your vault and click `Continue` to unlock and preview the import. Click `Done` to complete the import.
=== Importing Bitwarden
==== Bitwarden
WARNING: A Bitwarden Export file may be unencrypted and you should securely delete this file after successfully importing it into KeePassXC.
1. Open the Import Wizard as shown above. Select the Bitwarden option.
@@ -57,6 +61,13 @@ WARNING: A Bitwarden Export file may be unencrypted and you should securely dele
3. Click `Continue` to unlock and preview the import. Click `Done` to complete the import.
==== Proton Pass
WARNING: A Proton Pass Export file is unencrypted and you should securely delete this file after successfully importing it into KeePassXC.
1. Open the Import Wizard as shown above. Select the Proton Pass option.
2. Click `Continue` to preview the import. Click `Done` to complete the import.
=== Importing KeePass 1 Database
KeePass 1 database is an older format of the database created using a legacy version of KeePass. KeePassXC lets your import this older format of the database and you can seamlessly start using this database in your new KeePassXC application.
@@ -86,6 +97,8 @@ Opening without importing a remote database is possible by selecting Temporary D
== Exporting Databases
KeePassXC supports multiple ways to export your database for transfer to another program or to print out and archive.
WARNING: These exports do not contain all the information in your database due to various limitations in the export format. For example, the CSV export does not support attachments, advanced attributes, Auto-Type settings, or custom icons. The XML export does not support attachments. The HTML export is mainly for printing and does not support attachments and some custom data fields.
WARNING: Exporting your database will result in all of your passwords and sensitive information being stored in an unencrypted format. We do not recommend saving your exported database for long periods of time as that can cause a compromise of sensitive information.
.Database export menu

View File

@@ -16,7 +16,7 @@ To use sharing, you need to enable it for the application.
.KeeShare Application Settings
image::keeshare_application_settings.png[]
=== Sharing Credentials
=== Setup a Shared Group
If you checked _Allow export_ in the Sharing settings you can now share a group of passwords. Sharing is always defined on a particular group. If you enable sharing on a group, every entry under this group, and its children, are shared. If you enable sharing on the root node, **every password** inside your database gets shared!
NOTE: KeeShare does not synchronize group structure after the initial share is created. At this time, KeeShare operates at the entry level; shared entries moved outside of a shared group are still synchronized.

View File

@@ -15,7 +15,8 @@ NOTE: On macOS please substitute `Ctrl` with `Cmd` (aka `⌘`).
|Save Database As | Ctrl + Shift + S
|New Database | Ctrl + Shift + N
|Close Database | Ctrl + W ; Ctrl + F4
|Lock All Databases | Ctrl + L
|Lock Current Database | Ctrl + L
|Lock All Databases | Ctrl + Shift + L
|Database Settings | Ctrl + Shift + ,
|Database Reports | Ctrl + Shift + R
|Quit | Ctrl + Q

View File

@@ -5,56 +5,56 @@ include::.sharedheader[]
// tag::content[]
== Passkeys
Passkeys are a secure way for replacing passwords that is supported by all major browser vendors and an increasing number of websites. For more information on what Passkeys are and how they work, please go to the FIDO Alliance's documentation: https://fidoalliance.org/passkeys/
Passkeys are a secure way for replacing passwords that is supported by all major browser vendors and an increasing number of websites. For more information on what passkeys are and how they work, please go to the FIDO Alliance's documentation: https://fidoalliance.org/passkeys/
=== Enabling Passkey Support
=== Browser Passkey Support
KeePassXC supports Passkeys directly through the Browser Integration service. Passkeys are only supported with the use of the KeePassXC Browser Extension and a properly connected database. To enable Passkey support on the extension, you must check the _Enable Passkeys_ option in the extension settings page.
KeePassXC supports passkeys directly through the Browser Integration service. Passkeys are only supported with the use of the KeePassXC Browser Extension and a properly connected database. To enable passkey support on the extension, you must check the _Enable Passkeys_ option in the extension settings page.
.Enable Passkey Support in the KeePassXC Browser Extension
image::passkeys_enable_from_extension.png[,75%]
Optionally, you can disable falling back to the built-in Passkey support from your browser and operating system. If left enabled, the extension will show the default Passkey dialogs if KeePassXC cannot handle the request or the request is canceled.
Optionally, you can disable falling back to the built-in passkey support from your browser and operating system. If left enabled, the extension will show the default passkey dialogs if KeePassXC cannot handle the request or the request is canceled.
=== Create a New Passkey
Creating a new Passkey and authenticating with it is a simple process. This workflow will be demonstrated using GitHub as an example site. Please note that GitHub allows two use cases for Passkeys, one for 2FA only and the other for replacement of username and password entirely. We will be configuring the latter use case in this example.
Creating a new passkey and authenticating with it is a simple process. This workflow will be demonstrated using GitHub as an example site. Please note that GitHub allows two use cases for passkeys, one for 2FA only and the other for replacement of username and password entirely. We will be configuring the latter use case in this example.
After navigating to GitHub's _Settings_ -> _Password and authentication_, there is a separate section shown for Passkeys.
After navigating to GitHub's _Settings_ -> _Password and authentication_, there is a separate section shown for passkeys.
.GitHub's Passkey Registration
image::passkeys_github_1.png[]
After clicking the _Add a Passkey_ button, the user is redirected to another page showing the actual configuration option.
After clicking the _Add a passkey_ button, the user is redirected to another page showing the actual configuration option.
.Configure Passwordless Authentication
image::passkeys_github_2.png[,50%]
Clicking the _Add Passkey_ button now shows the following popup dialog for the user, asking confirmation for creating a new Passkey.
Clicking the _Add passkey_ button now shows the following popup dialog for the user, asking confirmation for creating a new passkey.
.Passkey Registration Confirmation Dialog
image::passkeys_register_dialog.png[,30%]
After the Passkey has been registered, a new entry is created to the database under _KeePassXC-Browser Passwords_ with _(Passkey)_ added to the entry title. The entry holds additional attributes that are used for authenticating the Passkey.
After the passkey has been registered, a new entry is created to the database under _KeePassXC-Browser Passwords_ with _(passkey)_ added to the entry title. The entry holds additional attributes that are used for authenticating the passkey.
After registration, GitHub will ask a name for the Passkey. This is only relevant for the server.
After registration, GitHub will ask a name for the passkey. This is only relevant for the server.
.GitHub's Passkey Nickname
image::passkeys_github_3.png[,50%]
Now the Passkey should be shown on the GitHub's Passkey section.
Now the passkey should be shown on the GitHub's passkey section.
.Registered Passkeys on GitHub
image::passkeys_github_4.png[]
=== Login With a Passkey
The Passkey created in the previous section can now be used to login to GitHub. Instead of logging in with normal credentials, choose _Sign in with a passkey_ at the bottom of GitHub's login page.
The passkey created in the previous section can now be used to login to GitHub. Instead of logging in with normal credentials, choose _Sign in with a passkey_ at the bottom of GitHub's login page.
.GitHub's login page with a Passkey option
image::passkeys_github_5.png[,50%]
After clicking the button, KeePassXC-Browser detects the Passkeys authentication and KeePassXC shows the following dialog for confirmation.
After clicking the button, KeePassXC-Browser detects the passkeys authentication and KeePassXC shows the following dialog for confirmation.
.Passkey authentication confirmation dialog
image::passkeys_authentication_dialog.png[,50%]
@@ -66,36 +66,36 @@ After confirmation user is now authenticated and logged into GitHub.
==== Multiple Passkeys for a Site
Multiple Passkeys can be created for a single site. When registering a new Passkey with a different username, KeePassXC shows an option to register a new Passkey or update the previous one. Updating a Passkey will override the existing entry, so this option should be only used when actually needed.
Multiple passkeys can be created for a single site. When registering a new passkey with a different username, KeePassXC shows an option to register a new passkey or update the previous one. Updating a passkey will override the existing entry, so this option should be only used when actually needed.
.Passkey authentication confirmation dialog
image::passkeys_update_dialog.png[,50%]
==== Exporting Passkeys
All Passkeys in a database can be viewed and accessed from the _Database_ -> _Passkeys..._ menu item. The page shows both _Import_ and _Export_ buttons for Passkeys.
All passkeys in a database can be viewed and accessed from the _Database_ -> _Passkeys..._ menu item. The page shows both _Import_ and _Export_ buttons for passkeys.
.Passkeys Overview
image::passkeys_all_passkeys.png[]
After selecting one or more entries, the following dialog is shown. One or multiple Passkeys can be selected for export from the previously selected list of entries.
After selecting one or more entries, the following dialog is shown. One or multiple passkeys can be selected for export from the previously selected list of entries.
.Passkeys Export Dialog
image::passkeys_export_dialog.png[,65%]
Exported Passkeys are stored in JSON format using the `.passkey` file extension. The file includes all relevant information for importing a Passkey to another database or saving a backup.
Exported passkeys are stored in JSON format using the `.passkey` file extension. The file includes all relevant information for importing a passkey to another database or saving a backup.
WARNING: The exported Passkey file is unencrypted and should be securely stored.
WARNING: The exported passkey file is unencrypted and should be securely stored.
==== Importing Passkeys
An exported Passkey can be imported directly to a database or to an entry. To import directly, use the _Database_ -> _Import Passkey_ menu item.
When right-clicking an entry, a separate menu item for _Import Passkey_ is shown. This is useful if user wants to import a previously created Passkey to an existing entry.
An exported passkey can be imported directly to a database or to an entry. To import directly, use the _Database_ -> _Import Passkey_ menu item.
When right-clicking an entry, a separate menu item for _Import Passkey_ is shown. This is useful if user wants to import a previously created passkey to an existing entry.
.Import Passkey to an Entry
image::passkeys_import_passkey_to_entry.png[,50%]
After selecting a Passkey file to import, a separate dialog is shown where you can select which database, group, and entry to target. By default, the group is set to _Imported Passkeys_. The default action is to create a new entry that contains the imported Passkey.
After selecting a passkey file to import, a separate dialog is shown where you can select which database, group, and entry to target. By default, the group is set to _Imported Passkeys_. The default action is to create a new entry that contains the imported passkey.
.Passkey import dialog
image::passkeys_import_dialog.png[,65%]

View File

@@ -21,7 +21,6 @@ image::password_generator.png[]
4. Select the character-sets that you want to include in your password.
5. Use the regenerate button (Ctrl + R) to make a new password using the chosen options.
6. Use the clipboard button (Ctrl + C) to copy the generated password to the clipboard.
// tag::advanced[]
7. Click the Advanced button to specify additional conditions for your desired password.
+
.Advanced Password Generator Options
@@ -42,5 +41,4 @@ Word Count slider.
5. _(Optional)_ You can also load your own custom word lists. Click the plus sign button to the right of the wordlist selection dialog to choose a custom word list. You can download alternative lists from the https://www.eff.org/deeplinks/2016/07/new-wordlists-random-passphrases[EFF's Website] or from https://github.com/redacted/XKCD-password-generator#additional-languages[GitHub].
6. Click the Regenerate button (Ctrl + R) to generate a new random passphrase.
7. Click the Clipboard button (Ctrl + C) to copy the passphrase to the clipboard.
// end::advanced[]
// end::content[]

View File

@@ -18,6 +18,8 @@ This section contains full details on advanced features available in KeePassXC.
|{NOTES} |Notes
|{TOTP} |Current TOTP value (if configured)
|{S:&lt;ATTRIBUTE_NAME&gt;} |Value for the given attribute (case sensitive)
|{T-CONV:/&lt;PLACEHOLDER&gt;/&lt;METHOD&gt;/} |Text conversion for resolved placeholder (eg, {USERNAME}) using the following methods: UPPER, LOWER, BASE64, HEX, URI, URI-DEC
|{T-REPLACE-RX:/&lt;PLACEHOLDER&gt;/&lt;REGEX&gt;/&lt;REPLACE&gt;/} |Use a regular expression to find and replace data from a resolved placeholder (eg, {USERNAME}). Refer to match groups using $1, $2, etc.
|{URL:RMVSCM} |URL without scheme (e.g., https)
|{URL:WITHOUTSCHEME} |URL without scheme
|{URL:SCM} |URL Scheme
@@ -124,5 +126,21 @@ Use regular expressions to find and replace data from a resolved placeholder. Re
`C:\Backups\MyDatabase\01-05-2022.kdbx`
|===
=== Creating a YubiKey backup
It is advisable to have a backup replica YubiKey In case your main YubiKey gets damaged, lost, or stolen. The same HMAC key will need to be written to both keys. To do this you can either use the YubiKey Personalization Tool GUI or the ykpersonalize CLI tool. The steps for the CLI tool are shown:
1. Create a 20 byte HMAC key:
+
```
dd status=none if=/dev/random bs=20 count=1 | xxd -p -c 40
```
2. Write the HMAC key to slot 2 _(Set through the first switch. Out of the box the YubiKey OTP resides in slot 1)_:
+
```
ykpersonalize -2 -a -ochal-resp -ochal-hmac -ohmac-lt64 -oserial-api-visible -oallow-update
```
You will be asked to enter the HMAC key you created earlier, copy/paste they key output in the first step. Repeat step 2 for your second YubiKey using the same HMAC key from before. We recommend storing your HMAC key in a safe place (e.g., printed on paper) in case you need to recreate another key.
// end::content[]

View File

@@ -3,12 +3,12 @@ include::.sharedheader[]
:imagesdir: ../images
// tag::content[]
== SSH Agent integration
== SSH Agent Integration
SSH (Secure Shell) is a widely used remote secure shell protocol and is considered an industry standard for secure remote access to UNIX-like systems including Linux, BSDs, macOS and more recently even Windows received native support. SSH supports multiple types of authentication and the most widely used ones are either interactive keyboard input with a password or a public-key cryptography pair of keys.
KeePassXC SSH Agent integration is built to manage SSH keys in a secure manner by either storing them completely within your KeePassXC database or by having only the decryption key of a key file that is stored elsewhere. SSH Agent integration _does not_ provide an agent itself but works as a client for any agent implementation that is OpenSSH compatible.
=== OpenSSH agent on Linux
=== OpenSSH Agent on Linux
If you are using a modern desktop Linux distribution it is very likely the OpenSSH agent is already configured and running when you have logged in to a graphical desktop session.
This should be true for distributions like Debian, Ubuntu (including Kubuntu, Xubuntu and Lubuntu), Linux Mint, Fedora, ElementaryOS and Manjaro.
@@ -32,10 +32,10 @@ WARNING: _GNOME Keyring_ prior to release 3.27.92 had its own custom implementat
It does not support any constraints you may want to configure for an added key.
If you are running a modern distribution the custom agent has been removed and replaced with the stock OpenSSH agent which is feature complete.
=== OpenSSH agent on macOS
=== OpenSSH Agent on macOS
Apple has made OpenSSH an integrated part of macOS with automatic agent startup when it is first used. No further configuration is needed.
=== OpenSSH agent and Pageant on Windows
=== OpenSSH Agent and Pageant on Windows
The SSH Agent integration on Windows supports both _PuTTY Pageant_ and _OpenSSH for Windows 10_.
Since Pageant is currently still the most widely used implementation and is easily installable on any version of Windows, it is the default on KeePassXC.
However, Microsoft includes a native OpenSSH client implementation with Windows 10 since autumn 2018 that can be used instead. If you would like to self-manage your OpenSSH version you can use the builds offered via their official https://github.com/powershell/Win32-OpenSSH[GitHub repository].
@@ -61,7 +61,7 @@ Alternatively, you can use a _Windows PowerShell_ running as _Administrator_ to
KeePassXC and other compatible tools can now use the Windows OpenSSH agent. To use it with KeePassXC, update the settings explained in <<Setting up SSH Agent integration>>.
=== Setting up SSH Agent integration
=== Setup SSH Agent Integration
By default the SSH Agent integration plugin is disabled.
To enable integration, follow the steps below to access the settings:
@@ -78,10 +78,10 @@ On Windows, you have the option to select _Pageant_ and/or _OpenSSH for Windows_
If the value of _SSH_AUTH_SOCK_ is empty it means the agent is not properly configured and KeePassXC will be unable to connect to it unless you provide a static override path to the socket.
=== Generating a key to use with KeePassXC
=== Generating an SSH Key
KeePassXC only supports keys in the _OpenSSH_ format. On Windows, _PuTTYgen_ saves keys in its own format by default and you will need to convert them to OpenSSH format before being used. In this guide we are going to generate a standard RSA key in the default size.
==== Generating a key on Linux or macOS with _ssh-keygen_
==== Generating a key on Linux or macOS
Open a terminal window and type the following command to generate a key:
$ ssh-keygen -o -f keepassxc -C johndoe@example
@@ -116,13 +116,13 @@ With KeePassXC you only need the first file listed.
==== Generating a key on Windows
On Windows you can generate key pairs with _PuTTYgen_ and with _ssh-keygen_, depending on whether you installed PuTTY and your Windows version.
===== Using _PuTTYgen_
===== Using PuTTYgen
Please read the manual on how to use _PuTTYgen_ for details on generate a key: https://the.earth.li/~sgtatham/putty/0.74/htmldoc/Chapter8.html#pubkey-puttygen. Once generated, you must save the key in the new OpenSSH format, see image below.
.Generating a key with _PuTTYgen_
image::sshagent_puttygen.png[,70%]
===== Using _ssh-keygen_
===== Using ssh-keygen
Open _Command Prompt_ or _Windows PowerShell_ and type the following command to generate a key:
PS C:\Users\user> ssh-keygen.exe -o -f keepassxc -C johndoe@example
@@ -159,7 +159,7 @@ Now we can see two files were generated:
With KeePassXC you only need the first file listed.
=== Configuring an entry to use SSH Agent
=== Adding SSH Key to an Entry
The last step is to setup an entry to contain the SSH Agent settings and key file you generated.
1. Create a new entry, or open an existing entry in edit mode.

View File

@@ -12,11 +12,11 @@ image::main_interface.png[]
*(A) Groups* Organize your entries into discrete groups to bring order to all of your sensitive information. Groups can be nested under each other to create a hierarchy. Settings from parent groups get applied to their children. You can hide this panel on the View menu.
*(B) Tags* Dynamic groups of entries that can be quickly displayed with one click. Any number of custom tags can be added when editing an entry. This panel also includes useful pre-defined searches, such as finding expired and weak passwords.
*(B) Searches and Tags* Dynamic groups of entries that can be quickly displayed with one click. Any number of custom tags can be added when editing an entry. This panel also includes useful pre-defined and custom saved searches, such as finding expired and weak passwords.
*\(C) Entries* Entries contain all the information you want to store for a website or application you are storing in KeePassXC. This view shows all the entries in the selected group. Each column can be resized, reordered, and shown or hidden based on your preference. Right-click the header row to see all available options.
*\(C) Entries* Entries contain all the information for a website or application you are storing in KeePassXC. This view shows all the entries in the selected group. Each column can be resized, reordered, and shown or hidden based on your preference. Right-click the header row to see all available options.
*(D) Preview* Shows a preview of the selected group or entry. You can temporarily hide this preview using the close button on the right hand side or completely disabled in the application settings.
*(D) Preview* Shows a preview of the selected group or entry. You can interact with most information stored in an entry from here without opening the entry for editing. You can temporarily hide this preview using the down-arrow button on the right hand side or completely disable it from the View menu.
TIP: You can enable double-click copying of entry username and password in the Application Security Settings. This is turned off by default starting with version 2.7.0.
@@ -29,13 +29,17 @@ image::toolbar.png[]
*(A) Database* Open Database, Save Database, Lock Database +
*(B) Entries* Create Entry, Edit Entry, Delete Selected Entries +
*\(C) Entry Data* Copy Username, Copy Password, Copy URL, Perform Auto-Type +
*(D) Tools* Password Generator, Application Settings +
*(D) Tools* Database Settings, Reports, Password Generator, Application Settings +
*(E) Search*
=== Application Settings
Users can configure KeePassXC to their personal tastes with a wide variety of general and security settings that apply to the whole application. These settings are accessible from _Tools_ -> _Settings_ or the cog wheel icon from the toolbar. Settings include: startup options, file management, entry management, user interface, language, security timeouts, and convenience.
=== Screenshot Security
By default, KeePassXC prevents recordings and screenshots of the application window on Windows and macOS. This prevents inadvertent spillage of information during meetings and disallows other applications to capture the window contents. If you would like to enable screen capture temporarily, navigate to _View_ menu and select _Allow Screen Capture_. Alternatively, you can start the application with the `--allow-screencapture` command line flag.
==== Setting the Theme
=== View Options
You can customize the appearance of KeePassXC to your liking. The following options are available in the _View_ menu:
==== Themes
KeePassXC ships with light and dark themes specifically designed to meet accessibility standards. In most cases, the appropriate theme for your system will be determined automatically, but you can always set a specific theme by using the _View_ menu. When a new theme is selected you will be prompted to restart KeePassXC to apply the theme immediately.
.Setting the theme
@@ -47,8 +51,8 @@ For users with smaller screens or those who desire seeing more entries at once,
.Compact mode comparison
image::compact_mode_comparison.png[]
=== Screenshot Security
By default, KeePassXC prevents recordings and screenshots of the application window on Windows and macOS. This prevents inadvertent spillage of information during meetings and disallows other applications to capture the window contents. If you would like to enable screen capture, you must start the application with the `--allow-screencapture` command line flag.
=== Application Settings
Users can configure KeePassXC to their personal tastes with a wide variety of general and security settings that apply to the whole application. These settings are accessible from _Tools_ -> _Settings_ or the cog wheel icon from the toolbar. Settings include: startup options, file management, entry management, user interface, language, security controls, and integration settings (Auto-Type, Browser, etc).
=== Keyboard Shortcuts
include::KeyboardShortcuts.adoc[tag=content, leveloffset=+1]
@@ -77,6 +81,7 @@ Arguments:
filename(s) filenames of the password databases to open (*.kdbx)
----
=== Environment Variables
Additionally, the following environment variables may be useful when running the application:
[grid=rows, frame=none, width=75%]
@@ -91,5 +96,17 @@ Additionally, the following environment variables may be useful when running the
|QT_SCREEN_SCALE_FACTORS [list] | Specifies scale factors for each screen. See https://doc.qt.io/qt-5/highdpi.html#high-dpi-support-in-qt
|QT_SCALE_FACTOR_ROUNDING_POLICY | Control device pixel ratio rounding to the nearest integer. See https://doc.qt.io/qt-5/highdpi.html#high-dpi-support-in-qt
|===
=== Installer Options
The following options can be set when running the Windows Installer MSI in an unattended installation:
* *LAUNCHAPPONEXIT* Launch KeePassXC after install (default ON)
* *AUTOSTARTPROGRAM* KeePassXC will auto-start on login (default ON)
* *INSTALLDESKTOPSHORTCUT* A desktop icon will be installed (default OFF)
Example: `msiexec.exe /q /i KeePassXC-Y.Y.Y-WinZZ.msi AUTOSTARTPROGRAM=0`
== Command Line Tool
KeePassXC comes with the command line tool *keepassxc-cli* to access, view, and manipulate your database directly from a terminal window. The tool is documented through a separate man page, which can be shown using `man keepassxc-cli`, or through the on-demand help using `keepassxc-cli [command] -h`. An online version of the man page is https://github.com/keepassxreboot/keepassxc/blob/latest/docs/man/keepassxc-cli.1.adoc[available on GitHub].
// end::advanced[]
// end::content[]

View File

@@ -26,12 +26,13 @@ KeePassXC has numerous features for novice and power users alike. This guide wil
** Password generator
** Auto-Type passwords into applications
** Browser integration with Google Chrome, Mozilla Firefox, Microsoft Edge, Chromium, Vivaldi, Brave, and Tor-Browser
** Support for passkeys using the browser integration
** Entry icon download
** Import databases from CSV, 1Password, and KeePass1 formats
** Import databases from CSV, 1Password, Bitwarden, Proton Pass, and KeePass1 formats
* Advanced Features
** Database reports (password health, HIBP, and statistics)
** Database export to CSV and HTML formats
** Database export to CSV, XML, and HTML formats
** TOTP storage and generation
** Field references between entries
** File attachments and custom attributes

Binary file not shown.

View File

@@ -1 +0,0 @@
secret

Binary file not shown.

3
share/demo_readme.md Normal file
View File

@@ -0,0 +1,3 @@
This is a demo database to showcase some of the features of KeePassXC
The password to unlock demo.kdbx is: secret

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19.92,12.08L12,20L4.08,12.08L5.5,10.67L11,16.17V2H13V16.17L18.5,10.66L19.92,12.08M12,20H2V22H22V20H12Z" /></svg>

After

Width:  |  Height:  |  Size: 182 B

View File

@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9 8H11V14H9V8M13 1H7V3H13V1M17.03 7.39C18.26 8.93 19 10.88 19 13C19 17.97 15 22 10 22C5.03 22 1 17.97 1 13S5.03 4 10 4C12.12 4 14.07 4.74 15.62 6L17.04 4.56C17.55 5 18 5.46 18.45 5.97L17.03 7.39M17 13C17 9.13 13.87 6 10 6S3 9.13 3 13 6.13 20 10 20 17 16.87 17 13M21 7V13H23V7H21M21 17H23V15H21V17Z" /></svg>

After

Width:  |  Height:  |  Size: 377 B

View File

@@ -0,0 +1 @@
<svg viewBox="0 0 128 128" xmlns="http://www.w3.org/2000/svg" xml:space="preserve" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2"><path d="M22.5 101.382V88.3094c0-9.1756 7.4383-16.6154 16.6154-16.6154l11.528.1398c12.5786 0 21.5115-2.3424 26.7973-7.0258 5.2858-4.6835 7.9851-8.4576 7.9851-16.9566 0-6.1551-1.6287-11.224-4.7734-15.5733-3.0775-4.4165-7.1586-7.3271-12.2445-8.7317-3.2789-.8707-9.334-1.3047-18.1656-1.3047l-9.1856-.1284v28.1362H22.5V4.75l26.1364.1284c9.768 0 17.2292.4682 22.3808 1.4046 7.2271 1.2048 13.2823 3.513 18.167 6.926 4.8847 3.3445 8.7988 8.0622 11.7422 14.1516 3.0119 6.088 4.5735 12.5958 4.5735 19.89 0 12.5115-4.0382 20.301-12.0005 28.9998-7.9623 8.6303-22.9161 12.4345-43.7268 12.4345 0 0-7.5968.1384-8.716.1384-8.4061 0-15.6061 5.2016-18.5566 12.5586ZM22.5 124.2501c0-13.2305 8.192-24.0806 18.5567-24.9993v24.902L22.5 124.25Z"/></svg>

After

Width:  |  Height:  |  Size: 900 B

View File

@@ -6,6 +6,7 @@
<file>application/256x256/apps/keepassxc.png</file>
<file>application/scalable/actions/application-exit.svg</file>
<file>application/scalable/actions/arrow-collapse-down.svg</file>
<file>application/scalable/actions/attributes-copy.svg</file>
<file>application/scalable/actions/auto-type.svg</file>
<file>application/scalable/actions/bitwarden.svg</file>
@@ -39,6 +40,7 @@
<file>application/scalable/actions/edit-clear-locationbar-rtl.svg</file>
<file>application/scalable/actions/entry-clone.svg</file>
<file>application/scalable/actions/entry-delete.svg</file>
<file>application/scalable/actions/entry-expire.svg</file>
<file>application/scalable/actions/entry-restore.svg</file>
<file>application/scalable/actions/entry-edit.svg</file>
<file>application/scalable/actions/entry-new.svg</file>
@@ -71,6 +73,7 @@
<file>application/scalable/actions/password-generator.svg</file>
<file>application/scalable/actions/password-show-off.svg</file>
<file>application/scalable/actions/password-show-on.svg</file>
<file>application/scalable/actions/proton.svg</file>
<file>application/scalable/actions/qrcode.svg</file>
<file>application/scalable/actions/refresh.svg</file>
<file>application/scalable/actions/remote-sync.svg</file>

View File

@@ -52,6 +52,41 @@
</screenshots>
<releases>
<release version="2.8.0" date="2025-01-01">
<description>
<ul>
<li>Placeholder for future release notes</li>
</ul>
</description>
</release>
<release version="2.7.9" date="2024-06-19">
<description>
<ul>
<li>Passkeys: Ability to easily remove a passkey from an entry [#10777]</li>
<li>Snap: Use new desktop portal for native messaging integration [#10906]</li>
<li>Improve entry placeholder/reference feature [#10846]</li>
<li>Improve CSV importing when title field isn't specified [#10843]</li>
<li>Improve encrypted Bitwarden importing [#10800]</li>
<li>Improve database settings UX [#10821]</li>
<li>Improve handling of clipboard actions from entry preview [#10810]</li>
<li>Improve group/entry view resize behavior and set sensible defaults [#10641]</li>
<li>Passkeys: Fix incorrect username fill [#10874]</li>
<li>Passkeys: Return additional data to the extension [#10857]</li>
<li>Fix password clear timer inconsistency on unlock view [#10708]</li>
<li>Fix portability check [#10760]</li>
<li>Fix page overflow on HTML exports [#10735]</li>
<li>Fix broken builds when using system provided zxcvbn [#10717]</li>
<li>Fix copy password button when text is selected [#10853]</li>
<li>Fix tab ordering on application settings pages [#10907]</li>
<li>SSH Agent: Fix broken decrypt button [#10638]</li>
<li>Windows: Fix ALT Auto-Type modifier [#10795]</li>
<li>Windows: Fix wrong DACL memory size allocation [#10712]</li>
<li>macOS: Fix monospace font sizing [#10739]</li>
<li>Flatpak: Fix configuration settings off-by-one error [#10688]</li>
<li>BSD: Fix compiling with libusb implementation [#10736]</li>
</ul>
</description>
</release>
<release version="2.7.8" date="2024-05-05">
<description>
<ul>

View File

@@ -243,6 +243,26 @@
<source>Export KeePassXC Settings</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Small</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Normal</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Medium</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Large</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Custom</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ApplicationSettingsWidgetGeneral</name>
@@ -541,6 +561,18 @@
<source>Export settings</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Open browser on double clicking URL field in entry view</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Font size:</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Font size selection</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ApplicationSettingsWidgetSecurity</name>
@@ -677,19 +709,6 @@
<source>Entry does not have attribute for PICKCHARS: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Invalid conversion type: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Invalid conversion syntax: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Invalid regular expression syntax %1
%2</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Invalid placeholder: %1</source>
<translation type="unfinished"></translation>
@@ -1045,10 +1064,6 @@ Do you want to overwrite the passkey in %1 - %2?</source>
<source>General</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Browsers installed as snaps are currently not supported.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Enable integration for these browsers:</source>
<translation type="unfinished"></translation>
@@ -1272,6 +1287,10 @@ Do you want to overwrite the passkey in %1 - %2?</source>
<source>KeePassXC-Browser is needed for the browser integration to work. &lt;br /&gt;Download it for %1 and %2 and %3.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Browsers installed using Snap or Flatpak are not supported with exception to Firefox installed using Snap.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>CloneDialog</name>
@@ -1426,6 +1445,10 @@ Do you want to overwrite the passkey in %1 - %2?</source>
Are you sure you want to import?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Tags</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>CsvParserModel</name>
@@ -1494,6 +1517,14 @@ Backup database located at %2</source>
<source>Recycle Bin</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Database file read error.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>No file path was provided.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>DatabaseOpenDialog</name>
@@ -1673,6 +1704,10 @@ Are you sure you want to continue with this file?.</source>
<source>&lt;a href=&quot;#&quot; style=&quot;text-decoration: underline&quot;&gt;I have a key file&lt;/a&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Hardware keys found, but no slots are configured.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>DatabaseSettingWidgetMetaData</name>
@@ -2194,6 +2229,50 @@ removed from the database.</source>
<source>Autosave delay since last change checkbox</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Public Database Metadata</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Warning: the following settings are not encrypted.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Display name:</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Publically visible display name used on the unlock dialog</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Database public display name</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Display color:</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Publically visible color used on the unlock dialog</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Database public display color chooser</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Clear</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Display icon:</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Select Database Icon</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>DatabaseSettingsWidgetKeeShare</name>
@@ -2616,24 +2695,6 @@ Save changes?</source>
<source>File has changed</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>The database file has changed. Do you want to load the changes?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Merge Request</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>The database file has changed and you have unsaved changes.
Do you want to merge your changes?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Could not open the new database file while attempting to autoreload.
Error: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Disable safe saves?</source>
<translation type="unfinished"></translation>
@@ -2720,6 +2781,50 @@ Disable safe saves and try again?</source>
<source>Do you want to remove the passkey from this entry?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>The database file &quot;%1&quot; was modified externally</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Do you want to load the changes?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reload database</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reloading database</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reload canceled</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reload successful</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Reload pending user action</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>The database file &quot;%1&quot; was modified externally.&lt;br&gt;How would you like to proceed?&lt;br&gt;&lt;br&gt;Merge all changes&lt;br&gt;Ignore the changes on disk until save&lt;br&gt;Discard unsaved changes</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>The database file &quot;%1&quot; was modified externally.&lt;br&gt;How would you like to proceed?&lt;br&gt;&lt;br&gt;Merge all changes then save&lt;br&gt;Overwrite the changes on disk&lt;br&gt;Discard unsaved changes</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Database file overwritten.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Database file on disk cannot be unlocked with current credentials.&lt;br&gt;Enter new credentials and/or present hardware key to continue.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EditEntryWidget</name>
@@ -3313,6 +3418,10 @@ Would you like to correct it?</source>
<source> seconds</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Clear agent</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EditGroupWidget</name>
@@ -3766,6 +3875,19 @@ This may cause the affected plugins to malfunction.</source>
<source>Passkey</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Invalid conversion type: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Invalid conversion syntax: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Invalid regular expression syntax %1
%2</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EntryAttachments</name>
@@ -3774,6 +3896,21 @@ This may cause the affected plugins to malfunction.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EntryAttachmentsDialog</name>
<message>
<source>Form</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>File name</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>File contents...</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EntryAttachmentsModel</name>
<message>
@@ -3811,14 +3948,6 @@ This may cause the affected plugins to malfunction.</source>
<source>Remove</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Rename selected attachment</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Rename</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Open selected attachment</source>
<translation type="unfinished"></translation>
@@ -3928,6 +4057,18 @@ Error: %1</source>
Would you like to overwrite the existing attachment?</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>New</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Preview</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Failed to preview an attachment: Attachment not found</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EntryAttributesModel</name>
@@ -4126,6 +4267,10 @@ Would you like to overwrite the existing attachment?</source>
<source>Background Color</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Group Path</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>EntryPreviewWidget</name>
@@ -4635,6 +4780,14 @@ You can enable the DuckDuckGo website icon service in the security section of th
<source>KeePass1 Database</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Proton Pass (.json)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Proton Pass JSON Export</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Temporary Database</source>
<translation type="unfinished"></translation>
@@ -4651,10 +4804,6 @@ You can enable the DuckDuckGo website icon service in the security section of th
<source>Input:</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Remote Database (.kdbx)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>e.g.:
get DatabaseOnRemote.kdbx {TEMP_DATABASE}
@@ -4665,6 +4814,10 @@ The command has to exit. In case of `sftp` as last commend `exit` has to be sent
</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Remote Database (.kdbx)</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>KMessageWidget</name>
@@ -5871,6 +6024,10 @@ Expect some bugs and minor issues, this version is meant for testing purposes.</
<source>Import Passkey</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Remote S&amp;ync</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Quit Application</source>
<translation type="unfinished"></translation>
@@ -5975,6 +6132,10 @@ Expect some bugs and minor issues, this version is meant for testing purposes.</
<source>Show Password Generator</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Remove Passkey From Entry</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Perform Auto-Type: {USERNAME}</source>
<translation type="unfinished"></translation>
@@ -6120,17 +6281,33 @@ Expect some bugs and minor issues, this version is meant for testing purposes.</
<translation type="unfinished"></translation>
</message>
<message>
<source>Remote S&amp;ync</source>
<source>Show Group Panel</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Remove Passkey From Entry</source>
<source>Toggle Show Group Panel</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Setup Remote Sync</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Password Generator</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>E&amp;xpire Entry</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Clear SSH Agent</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Clear all identities in ssh-agent</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ManageDatabase</name>
@@ -6281,6 +6458,25 @@ Expect some bugs and minor issues, this version is meant for testing purposes.</
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>NewEntryAttachmentsDialog</name>
<message>
<source>Attachment name cannot be empty</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Attachment with the same name already exists</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Save attachment</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>New entry attachment</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>NixUtils</name>
<message>
@@ -6846,10 +7042,6 @@ The following data is missing:
<source>Word Count:</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Character Count:</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Word Case:</source>
<translation type="unfinished"></translation>
@@ -6862,10 +7054,6 @@ The following data is missing:
<source>Add custom wordlist</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>character</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Close</source>
<translation type="unfinished"></translation>
@@ -6979,6 +7167,10 @@ Do you want to overwrite it?</source>
<source>Characters: %1</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>MIXED case</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Excluded characters: &quot;0&quot;, &quot;1&quot;, &quot;l&quot;, &quot;I&quot;, &quot;O&quot;, &quot;|&quot;, &quot;&quot;, &quot;B&quot;, &quot;8&quot;, &quot;G&quot;, &quot;6&quot;</source>
<translation type="unfinished"></translation>
@@ -7050,6 +7242,21 @@ Do you want to overwrite it?</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>PreviewEntryAttachmentsDialog</name>
<message>
<source>Preview entry attachment</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>No preview available</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Image format not supported</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>QMessageBox</name>
<message>
@@ -7877,10 +8084,6 @@ Do you want to overwrite it?</source>
<source>Exit interactive mode.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Format to use when exporting. Available choices are &apos;xml&apos; or &apos;csv&apos;. Defaults to &apos;xml&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Exports the content of a database to standard output in the specified format.</source>
<translation type="unfinished"></translation>
@@ -8483,18 +8686,6 @@ Kernel: %3 %4</source>
<source>file empty</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>malformed string</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>missing closing quote</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1: (row, col) %2,%3</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>AES 256-bit</source>
<translation type="unfinished"></translation>
@@ -9002,6 +9193,14 @@ This option is deprecated, use --set-key-file instead.</source>
<source>Cannot generate valid passphrases because the wordlist is too short</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Encrypted files are not supported.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Proton Pass Import</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Delete plugin data?</source>
<translation type="unfinished"></translation>
@@ -9013,6 +9212,34 @@ This option is deprecated, use --set-key-file instead.</source>
<numerusform></numerusform>
</translation>
</message>
<message>
<source>Passkey</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Format to use when exporting. Available choices are &apos;xml&apos;, &apos;csv&apos; or &apos;html&apos;. Defaults to &apos;xml&apos;.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>start minimized to the system tray</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>malformed string, possible unescaped delimiter</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>missing closing delimiter</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>%1, row: %2, column: %3</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Tags</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>QtIOCompressor</name>
@@ -9148,6 +9375,13 @@ This option is deprecated, use --set-key-file instead.</source>
<source>Exclude from reports</source>
<translation type="unfinished"></translation>
</message>
<message numerus="yes">
<source>Expire Entry(s)</source>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
<message>
<source>Only show entries that have a URL</source>
<translation type="unfinished"></translation>
@@ -9174,6 +9408,14 @@ This option is deprecated, use --set-key-file instead.</source>
</context>
<context>
<name>ReportsWidgetHealthcheck</name>
<message>
<source>Show expired entries</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> (Expired)</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Hover over reason to show additional details. Double-click entries to edit.</source>
<translation type="unfinished"></translation>
@@ -9237,18 +9479,17 @@ This option is deprecated, use --set-key-file instead.</source>
<source>Exclude from reports</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Show expired entries</source>
<translation type="unfinished"></translation>
<message numerus="yes">
<source>Expire Entry(s)</source>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
<message>
<source>Show entries that have been excluded from reports</source>
<translation type="unfinished"></translation>
</message>
<message>
<source> (Expired)</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>ReportsWidgetHibp</name>
@@ -9347,6 +9588,13 @@ This option is deprecated, use --set-key-file instead.</source>
<source>Exclude from reports</source>
<translation type="unfinished"></translation>
</message>
<message numerus="yes">
<source>Expire Entry(s)</source>
<translation type="unfinished">
<numerusform></numerusform>
<numerusform></numerusform>
</translation>
</message>
</context>
<context>
<name>ReportsWidgetPasskeys</name>
@@ -9591,6 +9839,14 @@ This option is deprecated, use --set-key-file instead.</source>
<source>No agent running, cannot list identities.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Failed to remove all SSH identities from agent.</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>All SSH identities removed from agent.</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>SearchHelpWidget</name>
@@ -10174,6 +10430,10 @@ Example: JBSWY3DPEHPK3PXP</source>
<source>&lt;p&gt;If you own a &lt;a href=&quot;https://www.yubico.com/&quot;&gt;YubiKey&lt;/a&gt; or &lt;a href=&quot;https://onlykey.io&quot;&gt;OnlyKey&lt;/a&gt;, you can use it for additional security.&lt;/p&gt;&lt;p&gt;The key requires one of its slots to be programmed with &lt;a href=&quot;https://keepassxc.org/docs/#faq-yubikey-howto&quot;&gt;Challenge-Response&lt;/a&gt;.&lt;/p&gt;</source>
<translation type="unfinished"></translation>
</message>
<message>
<source>Hardware keys found, but no slots are configured</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>YubiKeyInterfacePCSC</name>

View File

@@ -115,6 +115,14 @@
<ComponentRef Id="DesktopShortcut" />
</FeatureRef>
<!-- Detect VCRedist Version -->
<Property Id="VCREDISTINSTALLED">
<RegistrySearch Id="SearchVCRedist" Root="HKLM" Key="SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\x64" Name="Version" Type="raw" Win64="yes"/>
</Property>
<Condition Message="The installed version of Visual Studio Redistributable is too old. Please install the latest version from: https://aka.ms/vs/17/release/vc_redist.x64.exe">
<![CDATA[VCREDISTINSTALLED AND VCREDISTINSTALLED > "14.32.31332.0"]]>
</Condition>
<!-- Action to launch application after installer exits -->
<Property Id="WixShellExecTarget" Value="[#CM_FP_KeePassXC.exe]" />
<CustomAction Id="LaunchApplication" BinaryKey="WixCA" DllEntry="WixShellExec" Impersonate="yes" />

View File

@@ -2006,7 +2006,6 @@ drizzly
drone
drool
droop
drop-in
dropforge
dropkick
droplet
@@ -2525,7 +2524,6 @@ feed
feel
feisty
feline
felt-tip
feminine
feminism
feminist
@@ -6637,7 +6635,6 @@ synthesis
synthetic
syrup
system
t-shirt
tabasco
tabby
tableful
@@ -7745,7 +7742,6 @@ yiddish
yield
yin
yippee
yo-yo
yodel
yoga
yogurt

View File

@@ -1,4 +1,9 @@
name: keepassxc
summary: "KeePassXC: Secure, Community-Driven Password Management"
description: |
KeePassXC is an advanced password manager that offers secure storage
in an encrypted database, with a modern, user-friendly experience that
adapts to your desktop environment.
adopt-info: keepassxc
grade: stable
base: core22
@@ -7,19 +12,30 @@ compression: lzo
apps:
keepassxc:
command: usr/bin/keepassxc
common-id: org.keepassxc.KeePassXC.desktop
# Use desktop-launch to improve integration
command: desktop-launch usr/bin/keepassxc
desktop: usr/share/applications/org.keepassxc.KeePassXC.desktop
extensions: [kde-neon]
plugs: [home, unity7, network, network-bind, removable-media, raw-usb, password-manager-service, browser-native-messaging]
autostart: org.keepassxc.KeePassXC.desktop
plugs:
- home
- unity7
- network
- network-bind
- removable-media
- raw-usb
- password-manager-service
- browser-native-messaging
cli:
command: usr/bin/keepassxc-cli
extensions: [kde-neon]
plugs: [home, removable-media, raw-usb]
proxy:
command: usr/bin/keepassxc-proxy
extensions: [kde-neon]
# Enable direct access to the native messaging host configuration files
plugs:
browser-native-messaging:
interface: personal-files
@@ -78,8 +94,8 @@ parts:
- libfreetype6
- xclip
- libkeyutils1
lint:
ignore:
- library:
- lib/**/libhistory.so*

View File

@@ -71,6 +71,7 @@ set(core_SOURCES
format/BitwardenReader.cpp
format/CsvExporter.cpp
format/CsvParser.cpp
format/HtmlExporter.cpp
format/KeePass1Reader.cpp
format/KeePass2.cpp
format/KeePass2RandomStream.cpp
@@ -90,11 +91,13 @@ set(core_SOURCES
format/OpVaultReaderAttachments.cpp
format/OpVaultReaderBandEntry.cpp
format/OpVaultReaderSections.cpp
format/ProtonPassReader.cpp
keys/CompositeKey.cpp
keys/FileKey.cpp
keys/PasswordKey.cpp
keys/ChallengeResponseKey.cpp
streams/HashedBlockStream.cpp
streams/HashingStream.cpp
streams/HmacBlockStream.cpp
streams/LayeredStream.cpp
streams/qtiocompressor.cpp
@@ -127,7 +130,7 @@ set(gui_SOURCES
gui/FileDialog.cpp
gui/Font.cpp
gui/GuiTools.cpp
gui/HtmlExporter.cpp
gui/HtmlGuiExporter.cpp
gui/IconModels.cpp
gui/KMessageWidget.cpp
gui/MainWindow.cpp
@@ -156,6 +159,8 @@ set(gui_SOURCES
gui/entry/EntryAttachmentsModel.cpp
gui/entry/EntryAttachmentsWidget.cpp
gui/entry/EntryAttributesModel.cpp
gui/entry/NewEntryAttachmentsDialog.cpp
gui/entry/PreviewEntryAttachmentsDialog.cpp
gui/entry/EntryHistoryModel.cpp
gui/entry/EntryModel.cpp
gui/entry/EntryView.cpp
@@ -386,7 +391,7 @@ target_link_libraries(keepassxc_gui
${sshagent_LIB})
if(APPLE)
target_link_libraries(keepassxc_gui "-framework Foundation -framework AppKit -framework Carbon -framework Security -framework LocalAuthentication")
target_link_libraries(keepassxc_gui "-framework Foundation -framework AppKit -framework Carbon -framework Security -framework LocalAuthentication -framework ScreenCaptureKit")
if(Qt5MacExtras_FOUND)
target_link_libraries(keepassxc_gui Qt5::MacExtras)
endif()

View File

@@ -153,6 +153,7 @@ AutoType::AutoType(QObject* parent, bool test)
#endif
}
connect(this, SIGNAL(autotypeFinished()), SLOT(resetAutoTypeState()));
connect(qApp, SIGNAL(aboutToQuit()), SLOT(unloadPlugin()));
}
@@ -353,7 +354,6 @@ void AutoType::executeAutoTypeActions(const Entry* entry,
}
}
resetAutoTypeState();
m_inAutoType.unlock();
emit autotypeFinished();
}
@@ -434,38 +434,33 @@ void AutoType::startGlobalAutoType(const QString& search)
*/
void AutoType::performGlobalAutoType(const QList<QSharedPointer<Database>>& dbList, const QString& search)
{
if (!m_plugin) {
return;
}
if (!m_inGlobalAutoTypeDialog.tryLock()) {
return;
}
if (m_windowTitleForGlobal.isEmpty()) {
m_inGlobalAutoTypeDialog.unlock();
if (!m_plugin || !m_inGlobalAutoTypeDialog.tryLock()) {
return;
}
QList<AutoTypeMatch> matchList;
bool hideExpired = config()->get(Config::AutoTypeHideExpiredEntry).toBool();
// Generate entry/sequence match list if there is a valid window title
if (!m_windowTitleForGlobal.isEmpty()) {
bool hideExpired = config()->get(Config::AutoTypeHideExpiredEntry).toBool();
for (const auto& db : dbList) {
const QList<Entry*> dbEntries = db->rootGroup()->entriesRecursive();
for (auto entry : dbEntries) {
auto group = entry->group();
if (!group || !group->resolveAutoTypeEnabled() || !entry->autoTypeEnabled()) {
continue;
}
for (const auto& db : dbList) {
const QList<Entry*> dbEntries = db->rootGroup()->entriesRecursive();
for (auto entry : dbEntries) {
auto group = entry->group();
if (!group || !group->resolveAutoTypeEnabled() || !entry->autoTypeEnabled()) {
continue;
}
if (hideExpired && entry->isExpired()) {
continue;
}
const QSet<QString> sequences = Tools::asSet(entry->autoTypeSequences(m_windowTitleForGlobal));
for (const auto& sequence : sequences) {
matchList << AutoTypeMatch(entry, sequence);
if (hideExpired && entry->isExpired()) {
continue;
}
const QSet<QString> sequences = Tools::asSet(entry->autoTypeSequences(m_windowTitleForGlobal));
for (const auto& sequence : sequences) {
matchList << AutoTypeMatch(entry, sequence);
}
}
}
} else {
qWarning() << "Auto-Type: Window title was empty from the operating system";
}
// Show the selection dialog if we always ask, have multiple matches, or no matches
@@ -493,11 +488,9 @@ void AutoType::performGlobalAutoType(const QList<QSharedPointer<Database>>& dbLi
m_windowForGlobal,
virtualMode ? AutoTypeExecutor::Mode::VIRTUAL
: AutoTypeExecutor::Mode::NORMAL);
resetAutoTypeState();
});
connect(selectDialog, &QDialog::rejected, this, [this] {
restoreWindowState();
resetAutoTypeState();
emit autotypeFinished();
});
@@ -511,10 +504,8 @@ void AutoType::performGlobalAutoType(const QList<QSharedPointer<Database>>& dbLi
} else if (!matchList.isEmpty()) {
// Only one match and not asking, do it!
executeAutoTypeActions(matchList.first().first, matchList.first().second, m_windowForGlobal);
resetAutoTypeState();
} else {
// We should never get here
resetAutoTypeState();
emit autotypeFinished();
}
}
@@ -689,74 +680,23 @@ AutoType::parseSequence(const QString& entrySequence, const Entry* entry, QStrin
} else if (placeholder.startsWith("t-conv:")) {
// Reset to the original capture to preserve case
placeholder = match.captured(3);
placeholder.replace("t-conv:", "", Qt::CaseInsensitive);
if (!placeholder.isEmpty()) {
auto sep = placeholder[0];
auto parts = placeholder.split(sep);
if (parts.size() >= 4) {
auto resolved = entry->resolveMultiplePlaceholders(parts[1]);
auto type = parts[2].toLower();
if (type == "base64") {
resolved = resolved.toUtf8().toBase64();
} else if (type == "hex") {
resolved = resolved.toUtf8().toHex();
} else if (type == "uri") {
resolved = QUrl::toPercentEncoding(resolved.toUtf8());
} else if (type == "uri-dec") {
resolved = QUrl::fromPercentEncoding(resolved.toUtf8());
} else if (type.startsWith("u")) {
resolved = resolved.toUpper();
} else if (type.startsWith("l")) {
resolved = resolved.toLower();
} else {
error = tr("Invalid conversion type: %1").arg(type);
return {};
}
for (const QChar& ch : resolved) {
actions << QSharedPointer<AutoTypeKey>::create(ch);
}
} else {
error = tr("Invalid conversion syntax: %1").arg(fullPlaceholder);
return {};
}
} else {
error = tr("Invalid conversion syntax: %1").arg(fullPlaceholder);
auto resolved = entry->resolveConversionPlaceholder(placeholder, &error);
if (!error.isEmpty()) {
return {};
}
for (const QChar& ch : resolved) {
actions << QSharedPointer<AutoTypeKey>::create(ch);
}
} else if (placeholder.startsWith("t-replace-rx:")) {
// Reset to the original capture to preserve case
placeholder = match.captured(3);
placeholder.replace("t-replace-rx:", "", Qt::CaseInsensitive);
if (!placeholder.isEmpty()) {
auto sep = placeholder[0];
auto parts = placeholder.split(sep);
if (parts.size() >= 5) {
auto resolvedText = entry->resolveMultiplePlaceholders(parts[1]);
auto resolvedSearch = entry->resolveMultiplePlaceholders(parts[2]);
auto resolvedReplace = entry->resolveMultiplePlaceholders(parts[3]);
// Replace $<num> with \\<num> to support Qt substitutions
resolvedReplace.replace(QRegularExpression(R"(\$(\d+))"), R"(\\1)");
auto searchRegex = QRegularExpression(resolvedSearch);
if (!searchRegex.isValid()) {
error = tr("Invalid regular expression syntax %1\n%2")
.arg(resolvedSearch, searchRegex.errorString());
return {};
}
auto resolved = resolvedText.replace(searchRegex, resolvedReplace);
for (const QChar& ch : resolved) {
actions << QSharedPointer<AutoTypeKey>::create(ch);
}
} else {
error = tr("Invalid conversion syntax: %1").arg(fullPlaceholder);
return {};
}
} else {
error = tr("Invalid conversion syntax: %1").arg(fullPlaceholder);
auto resolved = entry->resolveRegexPlaceholder(placeholder, &error);
if (!error.isEmpty()) {
return {};
}
for (const QChar& ch : resolved) {
actions << QSharedPointer<AutoTypeKey>::create(ch);
}
} else if (placeholder.startsWith("mode=")) {
auto mode = AutoTypeExecutor::Mode::NORMAL;
if (placeholder.endsWith("virtual")) {

View File

@@ -66,6 +66,7 @@ signals:
private slots:
void startGlobalAutoType(const QString& search);
void unloadPlugin();
void resetAutoTypeState();
private:
enum WindowState
@@ -83,7 +84,6 @@ private:
WId window = 0,
AutoTypeExecutor::Mode mode = AutoTypeExecutor::Mode::NORMAL);
void restoreWindowState();
void resetAutoTypeState();
static QList<QSharedPointer<AutoTypeAction>>
parseSequence(const QString& entrySequence, const Entry* entry, QString& error, bool syntaxOnly = false);

View File

@@ -85,6 +85,10 @@ AutoTypeSelectDialog::AutoTypeSelectDialog(QWidget* parent)
connect(m_ui->action, &QToolButton::clicked, this, &AutoTypeSelectDialog::activateCurrentMatch);
connect(m_ui->cancelButton, SIGNAL(clicked()), SLOT(reject()));
auto sortColumn = config()->get(Config::AutoTypeDialogSortColumn).toInt();
auto sortOrder = config()->get(Config::AutoTypeDialogSortOrder).toInt();
m_ui->view->sortByColumn(sortColumn, sortOrder == 0 ? Qt::AscendingOrder : Qt::DescendingOrder);
}
// Required for QScopedPointer
@@ -330,15 +334,7 @@ void AutoTypeSelectDialog::buildActionMenu()
});
#endif
// Qt 5.10 introduced a new "feature" to hide shortcuts in context menus
// Unfortunately, Qt::AA_DontShowShortcutsInContextMenus is broken, have to manually enable them
typeUsernameAction->setShortcutVisibleInContextMenu(true);
typePasswordAction->setShortcutVisibleInContextMenu(true);
typeTotpAction->setShortcutVisibleInContextMenu(true);
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
typeVirtualAction->setShortcutVisibleInContextMenu(true);
#endif
copyUsernameAction->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_1);
copyUsernameAction->setProperty(MENU_FIELD_PROP_NAME, MENU_FIELD::USERNAME);
connect(copyUsernameAction, &QAction::triggered, this, [&] {
auto entry = m_ui->view->currentMatch().first;
@@ -348,6 +344,7 @@ void AutoTypeSelectDialog::buildActionMenu()
}
});
copyPasswordAction->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_2);
copyPasswordAction->setProperty(MENU_FIELD_PROP_NAME, MENU_FIELD::PASSWORD);
connect(copyPasswordAction, &QAction::triggered, this, [&] {
auto entry = m_ui->view->currentMatch().first;
@@ -357,6 +354,7 @@ void AutoTypeSelectDialog::buildActionMenu()
}
});
copyTotpAction->setShortcut(Qt::CTRL + Qt::SHIFT + Qt::Key_3);
copyTotpAction->setProperty(MENU_FIELD_PROP_NAME, MENU_FIELD::TOTP);
connect(copyTotpAction, &QAction::triggered, this, [&] {
auto entry = m_ui->view->currentMatch().first;
@@ -365,6 +363,18 @@ void AutoTypeSelectDialog::buildActionMenu()
reject();
}
});
// Qt 5.10 introduced a new "feature" to hide shortcuts in context menus
// Unfortunately, Qt::AA_DontShowShortcutsInContextMenus is broken, have to manually enable them
typeUsernameAction->setShortcutVisibleInContextMenu(true);
typePasswordAction->setShortcutVisibleInContextMenu(true);
typeTotpAction->setShortcutVisibleInContextMenu(true);
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
typeVirtualAction->setShortcutVisibleInContextMenu(true);
#endif
copyUsernameAction->setShortcutVisibleInContextMenu(true);
copyPasswordAction->setShortcutVisibleInContextMenu(true);
copyTotpAction->setShortcutVisibleInContextMenu(true);
}
void AutoTypeSelectDialog::showEvent(QShowEvent* event)
@@ -391,6 +401,8 @@ void AutoTypeSelectDialog::showEvent(QShowEvent* event)
void AutoTypeSelectDialog::hideEvent(QHideEvent* event)
{
config()->set(Config::GUI_AutoTypeSelectDialogSize, size());
config()->set(Config::AutoTypeDialogSortColumn, m_ui->view->horizontalHeader()->sortIndicatorSection());
config()->set(Config::AutoTypeDialogSortOrder, m_ui->view->horizontalHeader()->sortIndicatorOrder());
if (!m_accepted) {
emit rejected();
}

View File

@@ -1,7 +1,7 @@
set(autotype_mac_SOURCES AutoTypeMac.cpp)
add_library(keepassxc-autotype-cocoa MODULE ${autotype_mac_SOURCES})
set_target_properties(keepassxc-autotype-cocoa PROPERTIES LINK_FLAGS "-framework Foundation -framework AppKit -framework Carbon")
set_target_properties(keepassxc-autotype-cocoa PROPERTIES LINK_FLAGS "-framework Foundation -framework AppKit -framework Carbon -framework ScreenCaptureKit")
target_link_libraries(keepassxc-autotype-cocoa ${PROGNAME} Qt5::Core Qt5::Widgets)
install(TARGETS keepassxc-autotype-cocoa

View File

@@ -58,17 +58,6 @@ const QString BrowserPasskeys::REQUIREMENT_REQUIRED = QStringLiteral("required")
const QString BrowserPasskeys::PASSKEYS_ATTESTATION_DIRECT = QStringLiteral("direct");
const QString BrowserPasskeys::PASSKEYS_ATTESTATION_NONE = QStringLiteral("none");
const QString BrowserPasskeys::KPEX_PASSKEY_USERNAME = QStringLiteral("KPEX_PASSKEY_USERNAME");
const QString BrowserPasskeys::KPEX_PASSKEY_CREDENTIAL_ID = QStringLiteral("KPEX_PASSKEY_CREDENTIAL_ID");
// For compatibility with StrongBox
const QString BrowserPasskeys::KPEX_PASSKEY_GENERATED_USER_ID = QStringLiteral("KPEX_PASSKEY_GENERATED_USER_ID");
const QString BrowserPasskeys::KPXC_PASSKEY_USERNAME = QStringLiteral("KPXC_PASSKEY_USERNAME");
const QString BrowserPasskeys::KPEX_PASSKEY_PRIVATE_KEY_PEM = QStringLiteral("KPEX_PASSKEY_PRIVATE_KEY_PEM");
const QString BrowserPasskeys::KPEX_PASSKEY_RELYING_PARTY = QStringLiteral("KPEX_PASSKEY_RELYING_PARTY");
const QString BrowserPasskeys::KPEX_PASSKEY_USER_HANDLE = QStringLiteral("KPEX_PASSKEY_USER_HANDLE");
BrowserPasskeys* BrowserPasskeys::instance()
{
return s_browserPasskeys;

View File

@@ -105,14 +105,6 @@ public:
static const QString PASSKEYS_ATTESTATION_DIRECT;
static const QString PASSKEYS_ATTESTATION_NONE;
static const QString KPXC_PASSKEY_USERNAME;
static const QString KPEX_PASSKEY_USERNAME;
static const QString KPEX_PASSKEY_CREDENTIAL_ID;
static const QString KPEX_PASSKEY_GENERATED_USER_ID;
static const QString KPEX_PASSKEY_PRIVATE_KEY_PEM;
static const QString KPEX_PASSKEY_RELYING_PARTY;
static const QString KPEX_PASSKEY_USER_HANDLE;
private:
QByteArray buildAttestationObject(const QJsonObject& credentialCreationOptions,
const QString& extensions,

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2024 KeePassXC Team <team@keepassxc.org>
* Copyright (C) 2025 KeePassXC Team <team@keepassxc.org>
* Copyright (C) 2017 Sami Vänttinen <sami.vanttinen@protonmail.com>
* Copyright (C) 2013 Francois Ferrand
*
@@ -24,6 +24,7 @@
#include "BrowserHost.h"
#include "BrowserMessageBuilder.h"
#include "BrowserSettings.h"
#include "core/EntryAttributes.h"
#include "core/Tools.h"
#include "gui/MainWindow.h"
#include "gui/MessageBox.h"
@@ -50,6 +51,7 @@
#include <QLocalSocket>
#include <QLocale>
#include <QProgressDialog>
#include <QStringView>
#include <QUrl>
const QString BrowserService::KEEPASSXCBROWSER_NAME = QStringLiteral("KeePassXC-Browser Settings");
@@ -85,6 +87,10 @@ BrowserService::BrowserService()
connect(getMainWindow(), &MainWindow::databaseUnlocked, this, &BrowserService::databaseUnlocked);
connect(getMainWindow(), &MainWindow::databaseLocked, this, &BrowserService::databaseLocked);
connect(getMainWindow(), &MainWindow::activeDatabaseChanged, this, &BrowserService::activeDatabaseChanged);
connect(getMainWindow(),
&MainWindow::databaseUnlockDialogFinished,
this,
&BrowserService::handleDatabaseUnlockDialogFinished);
setEnabled(browserSettings()->isEnabled());
}
@@ -597,7 +603,8 @@ QString BrowserService::storeKey(const QString& key)
return {};
}
contains = db->metadata()->customData()->contains(CustomData::BrowserKeyPrefix + id);
contains =
db->metadata()->customData()->contains(CustomData::getKeyWithPrefix(CustomData::BrowserKeyPrefix, id));
if (contains) {
dialogResult = MessageBox::warning(m_currentDatabaseWidget,
tr("KeePassXC - Overwrite existing key?"),
@@ -610,8 +617,8 @@ QString BrowserService::storeKey(const QString& key)
} while (contains && dialogResult == MessageBox::Cancel);
hideWindow();
db->metadata()->customData()->set(CustomData::BrowserKeyPrefix + id, key);
db->metadata()->customData()->set(QString("%1_%2").arg(CustomData::Created, id),
db->metadata()->customData()->set(CustomData::getKeyWithPrefix(CustomData::BrowserKeyPrefix, id), key);
db->metadata()->customData()->set(CustomData::getKeyWithPrefix(CustomData::Created, id),
QLocale::system().toString(Clock::currentDateTime(), QLocale::ShortFormat));
return id;
}
@@ -623,7 +630,7 @@ QString BrowserService::getKey(const QString& id)
return {};
}
return db->metadata()->customData()->value(CustomData::BrowserKeyPrefix + id);
return db->metadata()->customData()->value(CustomData::getKeyWithPrefix(CustomData::BrowserKeyPrefix, id));
}
#ifdef WITH_XC_BROWSER_PASSKEYS
@@ -763,9 +770,9 @@ QJsonObject BrowserService::showPasskeysAuthenticationPrompt(const QJsonObject&
return getPasskeyError(ERROR_PASSKEYS_UNKNOWN_ERROR);
}
const auto privateKeyPem = selectedEntry->attributes()->value(BrowserPasskeys::KPEX_PASSKEY_PRIVATE_KEY_PEM);
const auto privateKeyPem = selectedEntry->attributes()->value(EntryAttributes::KPEX_PASSKEY_PRIVATE_KEY_PEM);
const auto credentialId = passkeyUtils()->getCredentialIdFromEntry(selectedEntry);
const auto userHandle = selectedEntry->attributes()->value(BrowserPasskeys::KPEX_PASSKEY_USER_HANDLE);
const auto userHandle = selectedEntry->attributes()->value(EntryAttributes::KPEX_PASSKEY_USER_HANDLE);
auto publicKeyCredential =
browserPasskeys()->buildGetPublicKeyCredential(assertionOptions, credentialId, userHandle, privateKeyPem);
@@ -843,11 +850,11 @@ void BrowserService::addPasskeyToEntry(Entry* entry,
entry->beginUpdate();
entry->attributes()->set(BrowserPasskeys::KPEX_PASSKEY_USERNAME, username);
entry->attributes()->set(BrowserPasskeys::KPEX_PASSKEY_CREDENTIAL_ID, credentialId, true);
entry->attributes()->set(BrowserPasskeys::KPEX_PASSKEY_PRIVATE_KEY_PEM, privateKey, true);
entry->attributes()->set(BrowserPasskeys::KPEX_PASSKEY_RELYING_PARTY, rpId);
entry->attributes()->set(BrowserPasskeys::KPEX_PASSKEY_USER_HANDLE, userHandle, true);
entry->attributes()->set(EntryAttributes::KPEX_PASSKEY_USERNAME, username);
entry->attributes()->set(EntryAttributes::KPEX_PASSKEY_CREDENTIAL_ID, credentialId, true);
entry->attributes()->set(EntryAttributes::KPEX_PASSKEY_PRIVATE_KEY_PEM, privateKey, true);
entry->attributes()->set(EntryAttributes::KPEX_PASSKEY_RELYING_PARTY, rpId);
entry->attributes()->set(EntryAttributes::KPEX_PASSKEY_USER_HANDLE, userHandle, true);
entry->addTag(tr("Passkey"));
entry->endUpdate();
@@ -1042,7 +1049,7 @@ QList<Entry*> BrowserService::searchEntries(const QSharedPointer<Database>& db,
#ifdef WITH_XC_BROWSER_PASSKEYS
// With Passkeys, check for the Relying Party instead of URL
if (passkey && entry->attributes()->value(BrowserPasskeys::KPEX_PASSKEY_RELYING_PARTY) != siteUrl) {
if (passkey && entry->attributes()->value(EntryAttributes::KPEX_PASSKEY_RELYING_PARTY) != siteUrl) {
continue;
}
#endif
@@ -1065,7 +1072,8 @@ QList<Entry*> BrowserService::searchEntries(const QString& siteUrl,
// Check if database is connected with KeePassXC-Browser. If so, return browser key (otherwise empty)
auto databaseConnected = [&](const QSharedPointer<Database>& db) {
for (const StringPair& keyPair : keyList) {
QString key = db->metadata()->customData()->value(CustomData::BrowserKeyPrefix + keyPair.first);
const auto key = db->metadata()->customData()->value(
CustomData::getKeyWithPrefix(CustomData::BrowserKeyPrefix, keyPair.first));
if (!key.isEmpty() && keyPair.second == key) {
return keyPair.first;
}
@@ -1368,9 +1376,15 @@ bool BrowserService::shouldIncludeEntry(Entry* entry,
return url.endsWith("by-path/" + entry->path());
}
const auto allEntryUrls = entry->getAllUrls();
for (const auto& entryUrl : allEntryUrls) {
if (handleURL(entryUrl, url, submitUrl, omitWwwSubdomain)) {
// Handle the entry URL
if (handleURL(entry->resolveUrl(), url, submitUrl, omitWwwSubdomain)) {
return true;
}
// Handle additional URLs
const auto additionalUrls = entry->getAdditionalUrls();
for (const auto& additionalUrl : additionalUrls) {
if (handleURL(additionalUrl, url, submitUrl, omitWwwSubdomain, true)) {
return true;
}
}
@@ -1384,7 +1398,7 @@ QList<Entry*> BrowserService::getPasskeyEntries(const QString& rpId, const Strin
{
QList<Entry*> entries;
for (const auto& entry : searchEntries(rpId, "", keyList, true)) {
if (entry->hasPasskey() && entry->attributes()->value(BrowserPasskeys::KPEX_PASSKEY_RELYING_PARTY) == rpId) {
if (entry->hasPasskey() && entry->attributes()->value(EntryAttributes::KPEX_PASSKEY_RELYING_PARTY) == rpId) {
entries << entry;
}
}
@@ -1399,8 +1413,8 @@ QList<Entry*> BrowserService::getPasskeyEntriesWithUserHandle(const QString& rpI
{
QList<Entry*> entries;
for (const auto& entry : searchEntries(rpId, "", keyList, true)) {
if (entry->hasPasskey() && entry->attributes()->value(BrowserPasskeys::KPEX_PASSKEY_RELYING_PARTY) == rpId
&& entry->attributes()->value(BrowserPasskeys::KPEX_PASSKEY_USER_HANDLE) == userId) {
if (entry->hasPasskey() && entry->attributes()->value(EntryAttributes::KPEX_PASSKEY_RELYING_PARTY) == rpId
&& entry->attributes()->value(EntryAttributes::KPEX_PASSKEY_USER_HANDLE) == userId) {
entries << entry;
}
}
@@ -1425,7 +1439,7 @@ QList<Entry*> BrowserService::getPasskeyAllowedEntries(const QJsonObject& assert
// See: https://w3c.github.io/webauthn/#dom-authenticatorassertionresponse-userhandle
if (allowedCredentials.contains(passkeyUtils()->getCredentialIdFromEntry(entry))
|| (allowedCredentials.isEmpty()
&& entry->attributes()->hasKey(BrowserPasskeys::KPEX_PASSKEY_USER_HANDLE))) {
&& entry->attributes()->hasKey(EntryAttributes::KPEX_PASSKEY_USER_HANDLE))) {
entries << entry;
}
}
@@ -1458,17 +1472,35 @@ QJsonObject BrowserService::getPasskeyError(int errorCode) const
bool BrowserService::handleURL(const QString& entryUrl,
const QString& siteUrl,
const QString& formUrl,
const bool omitWwwSubdomain)
const bool omitWwwSubdomain,
const bool allowWildcards)
{
if (entryUrl.isEmpty()) {
return false;
}
bool isWildcardUrl = false;
auto tempUrl = entryUrl;
// Allows matching with exact URL and wildcards
if (allowWildcards) {
// Exact match where URL is wrapped inside " characters
if (entryUrl.startsWith("\"") && entryUrl.endsWith("\"")) {
return QStringView{entryUrl}.mid(1, entryUrl.length() - 2) == siteUrl;
}
// Replace wildcards
isWildcardUrl = entryUrl.contains("*");
if (isWildcardUrl) {
tempUrl = tempUrl.replace("*", UrlTools::URL_WILDCARD);
}
}
QUrl entryQUrl;
if (entryUrl.contains("://")) {
entryQUrl = entryUrl;
entryQUrl = tempUrl;
} else {
entryQUrl = QUrl::fromUserInput(entryUrl);
entryQUrl = QUrl::fromUserInput(tempUrl);
if (browserSettings()->matchUrlScheme()) {
entryQUrl.setScheme("https");
@@ -1508,6 +1540,11 @@ bool BrowserService::handleURL(const QString& entryUrl,
return false;
}
// Use wildcard matching instead
if (isWildcardUrl) {
return handleURLWithWildcards(entryQUrl, siteUrl);
}
// Match the base domain
if (urlTools()->getBaseDomainFromUrl(siteQUrl.host()) != urlTools()->getBaseDomainFromUrl(entryQUrl.host())) {
return false;
@@ -1521,6 +1558,46 @@ bool BrowserService::handleURL(const QString& entryUrl,
return false;
}
bool BrowserService::handleURLWithWildcards(const QUrl& entryQUrl, const QString& siteUrl)
{
auto matchWithRegex = [&](QString firstPart, const QString& secondPart, bool hostnameUsed = false) {
if (firstPart == secondPart) {
return true;
}
// If there's no wildcard with hostname, just compare directly
if (hostnameUsed && !firstPart.contains(UrlTools::URL_WILDCARD) && firstPart != secondPart) {
return false;
}
// Escape illegal characters
auto re = firstPart.replace(QRegularExpression(R"(([!\^\$\+\-\(\)@<>]))"), "\\\\1");
if (hostnameUsed) {
// Replace all host parts with wildcards
re = re.replace(QString("%1.").arg(UrlTools::URL_WILDCARD), "(.*?)");
}
// Append a + to the end of regex to match all paths after the last asterisk
if (re.endsWith(UrlTools::URL_WILDCARD)) {
re.append("+");
}
// Replace any remaining wildcards for paths
re = re.replace(UrlTools::URL_WILDCARD, "(.*?)");
return QRegularExpression(re).match(secondPart).hasMatch();
};
// Match hostname and path
QUrl siteQUrl = siteUrl;
if (!matchWithRegex(entryQUrl.host(), siteQUrl.host(), true)
|| !matchWithRegex(entryQUrl.path(), siteQUrl.path())) {
return false;
}
return true;
}
QSharedPointer<Database> BrowserService::getDatabase(const QUuid& rootGroupUuid)
{
if (!rootGroupUuid.isNull()) {
@@ -1677,6 +1754,15 @@ void BrowserService::activeDatabaseChanged(DatabaseWidget* dbWidget)
m_currentDatabaseWidget = dbWidget;
}
void BrowserService::handleDatabaseUnlockDialogFinished(bool accepted, DatabaseWidget* dbWidget)
{
// User canceled the database open dialog
if (dbWidget && !accepted && m_bringToFrontRequested) {
m_bringToFrontRequested = false;
hideWindow();
}
}
void BrowserService::processClientMessage(QLocalSocket* socket, const QJsonObject& message)
{
auto clientID = message["clientID"].toString();

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2024 KeePassXC Team <team@keepassxc.org>
* Copyright (C) 2025 KeePassXC Team <team@keepassxc.org>
* Copyright (C) 2017 Sami Vänttinen <sami.vanttinen@protonmail.com>
* Copyright (C) 2013 Francois Ferrand
*
@@ -132,6 +132,7 @@ public:
static const QString OPTION_ONLY_HTTP_AUTH;
static const QString OPTION_NOT_HTTP_AUTH;
static const QString OPTION_OMIT_WWW;
static const QString ADDITIONAL_URL;
static const QString OPTION_RESTRICT_KEY;
signals:
@@ -145,6 +146,7 @@ public slots:
private slots:
void processClientMessage(QLocalSocket* socket, const QJsonObject& message);
void handleDatabaseUnlockDialogFinished(bool accepted, DatabaseWidget* dbWidget);
private:
enum Access
@@ -198,7 +200,9 @@ private:
bool handleURL(const QString& entryUrl,
const QString& siteUrl,
const QString& formUrl,
const bool omitWwwSubdomain = false);
const bool omitWwwSubdomain = false,
const bool allowWildcards = false);
bool handleURLWithWildcards(const QUrl& entryQUrl, const QString& siteUrl);
QString getDatabaseRootUuid();
QString getDatabaseRecycleBinUuid();
void hideWindow() const;

View File

@@ -178,7 +178,7 @@ void BrowserSettingsWidget::loadSettings()
QString BrowserSettingsWidget::resolveCustomProxyLocation()
{
auto settings = browserSettings();
auto proxyLocation = m_ui->customProxyLocation->text();
auto proxyLocation = m_ui->customProxyLocation->text().trimmed();
proxyLocation = settings->replaceTildeHomePath(proxyLocation);
return proxyLocation;
}

View File

@@ -52,7 +52,7 @@
<item>
<widget class="QLabel" name="snapWarningLabel">
<property name="text">
<string>Browsers installed as snaps are currently not supported.</string>
<string>Browsers installed using Snap or Flatpak are not supported with exception to Firefox installed using Snap.</string>
</property>
</widget>
</item>

View File

@@ -18,6 +18,7 @@
#include "PasskeyUtils.h"
#include "BrowserMessageBuilder.h"
#include "BrowserPasskeys.h"
#include "core/EntryAttributes.h"
#include "core/Tools.h"
#include "gui/UrlTools.h"
@@ -389,9 +390,9 @@ QString PasskeyUtils::getCredentialIdFromEntry(const Entry* entry) const
return {};
}
return entry->attributes()->hasKey(BrowserPasskeys::KPEX_PASSKEY_GENERATED_USER_ID)
? entry->attributes()->value(BrowserPasskeys::KPEX_PASSKEY_GENERATED_USER_ID)
: entry->attributes()->value(BrowserPasskeys::KPEX_PASSKEY_CREDENTIAL_ID);
return entry->attributes()->hasKey(EntryAttributes::KPEX_PASSKEY_GENERATED_USER_ID)
? entry->attributes()->value(EntryAttributes::KPEX_PASSKEY_GENERATED_USER_ID)
: entry->attributes()->value(EntryAttributes::KPEX_PASSKEY_CREDENTIAL_ID);
}
// For compatibility with StrongBox (and other possible clients in the future)
@@ -401,7 +402,7 @@ QString PasskeyUtils::getUsernameFromEntry(const Entry* entry) const
return {};
}
return entry->attributes()->hasKey(BrowserPasskeys::KPXC_PASSKEY_USERNAME)
? entry->attributes()->value(BrowserPasskeys::KPXC_PASSKEY_USERNAME)
: entry->attributes()->value(BrowserPasskeys::KPEX_PASSKEY_USERNAME);
return entry->attributes()->hasKey(EntryAttributes::KPXC_PASSKEY_USERNAME)
? entry->attributes()->value(EntryAttributes::KPXC_PASSKEY_USERNAME)
: entry->attributes()->value(EntryAttributes::KPEX_PASSKEY_USERNAME);
}

View File

@@ -21,13 +21,14 @@
#include "Utils.h"
#include "core/Global.h"
#include "format/CsvExporter.h"
#include "format/HtmlExporter.h"
#include <QCommandLineParser>
const QCommandLineOption Export::FormatOption = QCommandLineOption(
QStringList() << "f" << "format",
QObject::tr("Format to use when exporting. Available choices are 'xml' or 'csv'. Defaults to 'xml'."),
QStringLiteral("xml|csv"));
QObject::tr("Format to use when exporting. Available choices are 'xml', 'csv' or 'html'. Defaults to 'xml'."),
QStringLiteral("xml|csv|html"));
Export::Export()
{
@@ -53,6 +54,9 @@ int Export::executeWithDatabase(QSharedPointer<Database> database, QSharedPointe
} else if (format.startsWith(QStringLiteral("csv"), Qt::CaseInsensitive)) {
CsvExporter csvExporter;
out << csvExporter.exportDatabase(database);
} else if (format.startsWith(QStringLiteral("html"), Qt::CaseInsensitive)) {
HtmlExporter htmlExporter;
out << htmlExporter.exportDatabase(database);
} else {
err << QObject::tr("Unsupported format %1").arg(format) << Qt::endl;
return EXIT_FAILURE;

View File

@@ -43,6 +43,11 @@ namespace Utils
QTextStream STDIN;
QTextStream DEVNULL;
#ifdef Q_OS_WIN
UINT origCodePage;
UINT origOutputCodePage;
#endif
void setDefaultTextStreams()
{
auto fd = new QFile();
@@ -66,6 +71,9 @@ namespace Utils
DEVNULL.setDevice(fd);
#ifdef Q_OS_WIN
origCodePage = GetConsoleCP();
origOutputCodePage = GetConsoleOutputCP();
// On Windows, we ask via keepassxc-cli.exe.manifest to use UTF-8,
// but the console code-page isn't automatically changed to match.
SetConsoleCP(GetACP());
@@ -73,6 +81,14 @@ namespace Utils
#endif
}
void resetTextStreams()
{
#ifdef Q_OS_WIN
SetConsoleCP(origCodePage);
SetConsoleOutputCP(origOutputCodePage);
#endif
}
void setStdinEcho(bool enable = true)
{
#ifdef Q_OS_WIN
@@ -210,7 +226,7 @@ namespace Utils
#ifdef __AFL_COMPILER
// Fuzz test build takes password from environment variable to
// allow non-interactive operation
const auto env = getenv("KEYPASSXC_AFL_PASSWORD");
const auto env = getenv("KEEPASSXC_AFL_PASSWORD");
return env ? env : "";
#else
auto& in = STDIN;

View File

@@ -39,6 +39,7 @@ namespace Utils
static const QStringList EntryFieldNames(QStringList() << UuidFieldName << TagsFieldName);
void setDefaultTextStreams();
void resetTextStreams();
void setStdinEcho(bool enable);
bool loadFileKey(const QString& path, QSharedPointer<FileKey>& fileKey);

View File

@@ -181,6 +181,8 @@ int main(int argc, char** argv)
QCoreApplication app(argc, argv);
QCoreApplication::setApplicationVersion(KEEPASSXC_VERSION);
// Cleanup code pages after cli exits
QObject::connect(&app, &QCoreApplication::destroyed, &app, [] { Utils::resetTextStreams(); });
Bootstrap::bootstrap(config()->get(Config::GUI_Language).toString());
Utils::setDefaultTextStreams();
@@ -218,7 +220,9 @@ int main(int argc, char** argv)
// Switch to parser.showVersion() when available (QT 5.4).
out << KEEPASSXC_VERSION << Qt::endl;
return EXIT_SUCCESS;
} else if (parser.isSet(debugInfoOption)) {
}
if (parser.isSet(debugInfoOption)) {
QString debugInfo = Tools::debugInfo().append("\n").append(Crypto::debugInfo());
out << debugInfo << Qt::endl;
return EXIT_SUCCESS;

View File

@@ -66,6 +66,7 @@ static const QHash<Config::ConfigKey, ConfigDirective> configStrings = {
{Config::UseDirectWriteSaves,{QS("UseDirectWriteSaves"), Local, false}},
{Config::SearchLimitGroup,{QS("SearchLimitGroup"), Roaming, false}},
{Config::MinimizeOnOpenUrl,{QS("MinimizeOnOpenUrl"), Roaming, false}},
{Config::OpenURLOnDoubleClick, {QS("OpenURLOnDoubleClick"), Roaming, true}},
{Config::HideWindowOnCopy,{QS("HideWindowOnCopy"), Roaming, false}},
{Config::MinimizeOnCopy,{QS("MinimizeOnCopy"), Roaming, true}},
{Config::MinimizeAfterUnlock,{QS("MinimizeAfterUnlock"), Roaming, false}},
@@ -76,6 +77,8 @@ static const QHash<Config::ConfigKey, ConfigDirective> configStrings = {
{Config::AutoTypeDelay,{QS("AutoTypeDelay"), Roaming, 25}},
{Config::AutoTypeStartDelay,{QS("AutoTypeStartDelay"), Roaming, 500}},
{Config::AutoTypeHideExpiredEntry,{QS("AutoTypeHideExpiredEntry"), Roaming, false}},
{Config::AutoTypeDialogSortColumn,{QS("AutoTypeDialogSortColumn"), Roaming, 0}},
{Config::AutoTypeDialogSortOrder,{QS("AutoTypeDialogSortOrder"), Roaming, Qt::AscendingOrder}},
{Config::GlobalAutoTypeKey,{QS("GlobalAutoTypeKey"), Roaming, 0}},
{Config::GlobalAutoTypeModifiers,{QS("GlobalAutoTypeModifiers"), Roaming, 0}},
{Config::GlobalAutoTypeRetypeTime,{QS("GlobalAutoTypeRetypeTime"), Roaming, 15}},
@@ -95,6 +98,7 @@ static const QHash<Config::ConfigKey, ConfigDirective> configStrings = {
{Config::GUI_HideMenubar, {QS("GUI/HideMenubar"), Roaming, false}},
{Config::GUI_HideToolbar, {QS("GUI/HideToolbar"), Roaming, false}},
{Config::GUI_MovableToolbar, {QS("GUI/MovableToolbar"), Roaming, false}},
{Config::GUI_HideGroupPanel, {QS("GUI/HideGroupPanel"), Roaming, false}},
{Config::GUI_HidePreviewPanel, {QS("GUI/HidePreviewPanel"), Roaming, false}},
{Config::GUI_AlwaysOnTop, {QS("GUI/GUI_AlwaysOnTop"), Local, false}},
{Config::GUI_ToolButtonStyle, {QS("GUI/ToolButtonStyle"), Roaming, Qt::ToolButtonIconOnly}},
@@ -115,6 +119,7 @@ static const QHash<Config::ConfigKey, ConfigDirective> configStrings = {
{Config::GUI_CheckForUpdatesIncludeBetas, {QS("GUI/CheckForUpdatesIncludeBetas"), Roaming, false}},
{Config::GUI_ShowExpiredEntriesOnDatabaseUnlock, {QS("GUI/ShowExpiredEntriesOnDatabaseUnlock"), Roaming, true}},
{Config::GUI_ShowExpiredEntriesOnDatabaseUnlockOffsetDays, {QS("GUI/ShowExpiredEntriesOnDatabaseUnlockOffsetDays"), Roaming, 3}},
{Config::GUI_FontSizeOffset, {QS("GUI/FontSizeOffset"), Local, 0}},
{Config::GUI_MainWindowGeometry, {QS("GUI/MainWindowGeometry"), Local, {}}},
{Config::GUI_MainWindowState, {QS("GUI/MainWindowState"), Local, {}}},

View File

@@ -49,6 +49,7 @@ public:
UseDirectWriteSaves,
SearchLimitGroup,
MinimizeOnOpenUrl,
OpenURLOnDoubleClick,
HideWindowOnCopy,
MinimizeOnCopy,
MinimizeAfterUnlock,
@@ -59,6 +60,8 @@ public:
AutoTypeDelay,
AutoTypeStartDelay,
AutoTypeHideExpiredEntry,
AutoTypeDialogSortColumn,
AutoTypeDialogSortOrder,
GlobalAutoTypeKey,
GlobalAutoTypeModifiers,
GlobalAutoTypeRetypeTime,
@@ -77,6 +80,7 @@ public:
GUI_HideMenubar,
GUI_HideToolbar,
GUI_MovableToolbar,
GUI_HideGroupPanel,
GUI_HidePreviewPanel,
GUI_AlwaysOnTop,
GUI_ToolButtonStyle,
@@ -96,6 +100,7 @@ public:
GUI_CheckForUpdatesIncludeBetas,
GUI_ShowExpiredEntriesOnDatabaseUnlock,
GUI_ShowExpiredEntriesOnDatabaseUnlockOffsetDays,
GUI_FontSizeOffset,
GUI_MainWindowGeometry,
GUI_MainWindowState,

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
* Copyright (C) 2024 KeePassXC Team <team@keepassxc.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -21,9 +21,8 @@
#include "core/Global.h"
const QString CustomData::LastModified = QStringLiteral("_LAST_MODIFIED");
const QString CustomData::Created = QStringLiteral("_CREATED");
const QString CustomData::Created = QStringLiteral("_CREATED_");
const QString CustomData::BrowserKeyPrefix = QStringLiteral("KPXC_BROWSER_");
const QString CustomData::BrowserLegacyKeyPrefix = QStringLiteral("Public Key: ");
const QString CustomData::ExcludeFromReportsLegacy = QStringLiteral("KnownBad");
const QString CustomData::FdoSecretsExposedGroup = QStringLiteral("FDO_SECRETS_EXPOSED_GROUP");
const QString CustomData::RandomSlug = QStringLiteral("KPXC_RANDOM_SLUG");
@@ -52,6 +51,15 @@ QString CustomData::value(const QString& key) const
return m_data.value(key).value;
}
QString CustomData::getKeyWithPrefix(const QString& prefix, const QString& key)
{
QString keyWithPrefix;
keyWithPrefix.reserve(prefix.length() + key.length());
keyWithPrefix.append(prefix);
keyWithPrefix.append(key);
return keyWithPrefix;
}
const CustomData::CustomDataItem& CustomData::item(const QString& key) const
{
auto item = m_data.find(key);

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2018 KeePassXC Team <team@keepassxc.org>
* Copyright (C) 2024 KeePassXC Team <team@keepassxc.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -64,11 +64,12 @@ public:
bool operator==(const CustomData& other) const;
bool operator!=(const CustomData& other) const;
static QString getKeyWithPrefix(const QString& prefix, const QString& key);
// Pre-defined keys
static const QString LastModified;
static const QString Created;
static const QString BrowserKeyPrefix;
static const QString BrowserLegacyKeyPrefix;
static const QString FdoSecretsExposedGroup;
static const QString RandomSlug;
static const QString RemoteProgramSettings;

View File

@@ -25,6 +25,7 @@
#include "format/KdbxXmlReader.h"
#include "format/KeePass2Reader.h"
#include "format/KeePass2Writer.h"
#include "streams/HashingStream.h"
#include <QFileInfo>
#include <QJsonObject>
@@ -62,8 +63,8 @@ Database::Database()
updateTagList();
});
connect(this, &Database::modified, this, [this] { updateTagList(); });
connect(this, &Database::databaseSaved, this, [this]() { updateCommonUsernames(); });
connect(m_fileWatcher, &FileWatcher::fileChanged, this, &Database::databaseFileChanged);
connect(this, &Database::databaseSaved, this, [this] { updateCommonUsernames(); });
connect(m_fileWatcher, &FileWatcher::fileChanged, this, [this] { emit databaseFileChanged(false); });
// static uuid map
s_uuidMap.insert(m_uuid, this);
@@ -106,10 +107,6 @@ QUuid Database::uuid() const
*/
bool Database::open(QSharedPointer<const CompositeKey> key, QString* error)
{
Q_ASSERT(!m_data.filePath.isEmpty());
if (m_data.filePath.isEmpty()) {
return false;
}
return open(m_data.filePath, std::move(key), error);
}
@@ -127,6 +124,13 @@ bool Database::open(QSharedPointer<const CompositeKey> key, QString* error)
*/
bool Database::open(const QString& filePath, QSharedPointer<const CompositeKey> key, QString* error)
{
if (filePath.isEmpty()) {
if (error) {
*error = tr("No file path was provided.");
}
return false;
}
QFile dbFile(filePath);
if (!dbFile.exists()) {
if (error) {
@@ -151,6 +155,20 @@ bool Database::open(const QString& filePath, QSharedPointer<const CompositeKey>
setEmitModified(false);
// update the hash of the first block
m_fileBlockHash.clear();
auto fileBlockData = dbFile.peek(kFileBlockToHashSizeBytes);
if (fileBlockData.size() != kFileBlockToHashSizeBytes) {
if (dbFile.size() >= kFileBlockToHashSizeBytes) {
if (error) {
*error = tr("Database file read error.");
}
return false;
}
} else {
m_fileBlockHash = QCryptographicHash::hash(fileBlockData, QCryptographicHash::Md5);
}
KeePass2Reader reader;
if (!reader.readDatabase(&dbFile, std::move(key), this)) {
if (error) {
@@ -260,14 +278,33 @@ bool Database::saveAs(const QString& filePath, SaveAction action, const QString&
return false;
}
if (filePath == m_data.filePath) {
// Fail-safe check to make sure we don't overwrite underlying file changes
// that have not yet triggered a file reload/merge operation.
if (!m_fileWatcher->hasSameFileChecksum()) {
if (error) {
*error = tr("Database file has unmerged changes.");
// Make sure we don't overwrite external modifications unless explicitly allowed
if (!m_ignoreFileChangesUntilSaved && !m_fileBlockHash.isEmpty() && filePath == m_data.filePath) {
QFile dbFile(filePath);
if (dbFile.exists()) {
if (!dbFile.open(QIODevice::ReadOnly)) {
if (error) {
*error = tr("Unable to open file %1.").arg(filePath);
}
return false;
}
auto fileBlockData = dbFile.read(kFileBlockToHashSizeBytes);
if (fileBlockData.size() == kFileBlockToHashSizeBytes) {
auto hash = QCryptographicHash::hash(fileBlockData, QCryptographicHash::Md5);
if (m_fileBlockHash != hash) {
if (error) {
*error = tr("Database file has unmerged changes.");
}
// emit the databaseFileChanged(true) signal async
QMetaObject::invokeMethod(this, "databaseFileChanged", Qt::QueuedConnection, Q_ARG(bool, true));
return false;
}
} else if (dbFile.size() >= kFileBlockToHashSizeBytes) {
if (error) {
*error = tr("Database file read error.");
}
return false;
}
return false;
}
}
@@ -302,7 +339,7 @@ bool Database::saveAs(const QString& filePath, SaveAction action, const QString&
SetFileAttributes(realFilePath.toStdString().c_str(), FILE_ATTRIBUTE_HIDDEN);
}
#endif
m_ignoreFileChangesUntilSaved = false;
m_fileWatcher->start(realFilePath, 30, 1);
} else {
// Saving failed, don't rewatch file since it does not represent our database
@@ -325,8 +362,12 @@ bool Database::performSave(const QString& filePath, SaveAction action, const QSt
case Atomic: {
QSaveFile saveFile(filePath);
if (saveFile.open(QIODevice::WriteOnly)) {
HashingStream hashingStream(&saveFile, QCryptographicHash::Md5, kFileBlockToHashSizeBytes);
if (!hashingStream.open(QIODevice::WriteOnly)) {
return false;
}
// write the database to the file
if (!writeDatabase(&saveFile, error)) {
if (!writeDatabase(&hashingStream, error)) {
return false;
}
@@ -334,6 +375,9 @@ bool Database::performSave(const QString& filePath, SaveAction action, const QSt
saveFile.setFileTime(createTime, QFile::FileBirthTime);
if (saveFile.commit()) {
// store the new hash
m_fileBlockHash = hashingStream.hashingResult();
// successfully saved database file
return true;
}
@@ -347,8 +391,12 @@ bool Database::performSave(const QString& filePath, SaveAction action, const QSt
case TempFile: {
QTemporaryFile tempFile;
if (tempFile.open()) {
HashingStream hashingStream(&tempFile, QCryptographicHash::Md5, kFileBlockToHashSizeBytes);
if (!hashingStream.open(QIODevice::WriteOnly)) {
return false;
}
// write the database to the file
if (!writeDatabase(&tempFile, error)) {
if (!writeDatabase(&hashingStream, error)) {
return false;
}
tempFile.close(); // flush to disk
@@ -366,6 +414,8 @@ bool Database::performSave(const QString& filePath, SaveAction action, const QSt
QFile::setPermissions(filePath, perms);
// Retain original creation time
tempFile.setFileTime(createTime, QFile::FileBirthTime);
// store the new hash
m_fileBlockHash = hashingStream.hashingResult();
return true;
} else if (backupFilePath.isEmpty() || !restoreDatabase(filePath, backupFilePath)) {
// Failed to copy new database in place, and
@@ -387,10 +437,16 @@ bool Database::performSave(const QString& filePath, SaveAction action, const QSt
// Open the original database file for direct-write
QFile dbFile(filePath);
if (dbFile.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
if (!writeDatabase(&dbFile, error)) {
HashingStream hashingStream(&dbFile, QCryptographicHash::Md5, kFileBlockToHashSizeBytes);
if (!hashingStream.open(QIODevice::WriteOnly)) {
return false;
}
if (!writeDatabase(&hashingStream, error)) {
return false;
}
dbFile.close();
// store the new hash
m_fileBlockHash = hashingStream.hashingResult();
return true;
}
if (error) {
@@ -508,6 +564,9 @@ void Database::releaseData()
m_deletedObjects.clear();
m_commonUsernames.clear();
m_tagList.clear();
m_fileBlockHash.clear();
m_ignoreFileChangesUntilSaved = false;
}
/**
@@ -644,10 +703,33 @@ void Database::setFilePath(const QString& filePath)
m_data.filePath = filePath;
// Don't watch for changes until the next open or save operation
m_fileWatcher->stop();
m_ignoreFileChangesUntilSaved = false;
emit filePathChanged(oldPath, filePath);
}
}
const QByteArray& Database::fileBlockHash() const
{
return m_fileBlockHash;
}
void Database::setIgnoreFileChangesUntilSaved(bool ignore)
{
if (m_ignoreFileChangesUntilSaved != ignore) {
m_ignoreFileChangesUntilSaved = ignore;
if (ignore) {
m_fileWatcher->pause();
} else {
m_fileWatcher->resume();
}
}
}
bool Database::ignoreFileChangesUntilSaved() const
{
return m_ignoreFileChangesUntilSaved;
}
QList<DeletedObject> Database::deletedObjects()
{
return m_deletedObjects;
@@ -1051,6 +1133,54 @@ QUuid Database::publicUuid()
return QUuid::fromRfc4122(publicCustomData()["KPXC_PUBLIC_UUID"].toByteArray());
}
QString Database::publicName()
{
return publicCustomData().value("KPXC_PUBLIC_NAME").toString();
}
void Database::setPublicName(const QString& name)
{
if (name.isEmpty()) {
publicCustomData().remove("KPXC_PUBLIC_NAME");
} else {
publicCustomData().insert("KPXC_PUBLIC_NAME", name);
}
markAsModified();
}
QString Database::publicColor()
{
return publicCustomData().value("KPXC_PUBLIC_COLOR").toString();
}
void Database::setPublicColor(const QString& color)
{
if (color.isEmpty()) {
publicCustomData().remove("KPXC_PUBLIC_COLOR");
} else {
publicCustomData().insert("KPXC_PUBLIC_COLOR", color);
}
markAsModified();
}
int Database::publicIcon()
{
if (publicCustomData().contains("KPXC_PUBLIC_ICON")) {
return publicCustomData().value("KPXC_PUBLIC_ICON").toInt();
}
return -1;
}
void Database::setPublicIcon(int iconIndex)
{
if (iconIndex < 0) {
publicCustomData().remove("KPXC_PUBLIC_ICON");
} else {
publicCustomData().insert("KPXC_PUBLIC_ICON", iconIndex);
}
markAsModified();
}
void Database::markAsTemporaryDatabase()
{
m_isTemporaryDatabase = true;

View File

@@ -75,6 +75,9 @@ public:
~Database() override;
private:
// size of the block of file data to hash for detecting external changes
static const quint32 kFileBlockToHashSizeBytes = 1024; // 1 KiB
bool writeDatabase(QIODevice* device, QString* error = nullptr);
bool backupDatabase(const QString& filePath, const QString& destinationFilePath);
bool restoreDatabase(const QString& filePath, const QString& fromBackupFilePath);
@@ -108,6 +111,17 @@ public:
QString canonicalFilePath() const;
void setFilePath(const QString& filePath);
const QByteArray& fileBlockHash() const;
void setIgnoreFileChangesUntilSaved(bool ignore);
bool ignoreFileChangesUntilSaved() const;
QString publicName();
void setPublicName(const QString& name);
QString publicColor();
void setPublicColor(const QString& color);
int publicIcon();
void setPublicIcon(int iconIndex);
Metadata* metadata();
const Metadata* metadata() const;
Group* rootGroup();
@@ -174,7 +188,7 @@ signals:
void databaseOpened();
void databaseSaved();
void databaseDiscarded();
void databaseFileChanged();
void databaseFileChanged(bool triggeredBySave);
void databaseNonDataChanged();
void tagListUpdated();
@@ -226,6 +240,8 @@ private:
void startModifiedTimer();
void stopModifiedTimer();
QByteArray m_fileBlockHash;
bool m_ignoreFileChangesUntilSaved;
QPointer<Metadata> const m_metadata;
DatabaseData m_data;
QPointer<Group> m_rootGroup;

View File

@@ -381,16 +381,32 @@ QString Entry::url() const
return m_attributes->value(EntryAttributes::URLKey);
}
QString Entry::resolveUrl() const
{
const auto entryUrl = url();
if (entryUrl.isEmpty()) {
return {};
}
return EntryAttributes::matchReference(entryUrl).hasMatch() ? resolveMultiplePlaceholders(entryUrl) : entryUrl;
}
QStringList Entry::getAllUrls() const
{
QStringList urlList;
auto entryUrl = url();
const auto entryUrl = resolveUrl();
if (!entryUrl.isEmpty()) {
urlList << (EntryAttributes::matchReference(entryUrl).hasMatch() ? resolveMultiplePlaceholders(entryUrl)
: entryUrl);
urlList << entryUrl;
}
return urlList << getAdditionalUrls();
}
QStringList Entry::getAdditionalUrls() const
{
QStringList urlList;
for (const auto& key : m_attributes->keys()) {
if (key.startsWith(EntryAttributes::AdditionalUrlAttribute)
|| key == QString("%1_RELYING_PARTY").arg(EntryAttributes::PasskeyAttribute)) {
@@ -460,6 +476,12 @@ bool Entry::willExpireInDays(int days) const
return m_data.timeInfo.expires() && m_data.timeInfo.expiryTime() < Clock::currentDateTime().addDays(days);
}
void Entry::expireNow()
{
setExpiryTime(Clock::currentDateTimeUtc());
setExpires(true);
}
bool Entry::isRecycled() const
{
const Database* db = database();
@@ -1030,9 +1052,9 @@ void Entry::updateModifiedSinceBegin()
QString Entry::resolveMultiplePlaceholdersRecursive(const QString& str, int maxDepth) const
{
static const QRegularExpression placeholderRegEx(R"(\{[^}]+\})");
static const QRegularExpression placeholderRegEx("({(?>[^{}]+?|(?1))+?})");
if (maxDepth <= 0) {
if (--maxDepth < 0) {
qWarning("Maximum depth of replacement has been reached. Entry uuid: %s", uuid().toString().toLatin1().data());
return str;
}
@@ -1043,7 +1065,7 @@ QString Entry::resolveMultiplePlaceholdersRecursive(const QString& str, int maxD
while (matches.hasNext()) {
const auto match = matches.next();
result += str.midRef(capEnd, match.capturedStart() - capEnd);
result += resolvePlaceholderRecursive(match.captured(), maxDepth - 1);
result += resolvePlaceholderRecursive(match.captured(), maxDepth);
capEnd = match.capturedEnd();
}
result += str.rightRef(str.length() - capEnd);
@@ -1052,7 +1074,7 @@ QString Entry::resolveMultiplePlaceholdersRecursive(const QString& str, int maxD
QString Entry::resolvePlaceholderRecursive(const QString& placeholder, int maxDepth) const
{
if (maxDepth <= 0) {
if (--maxDepth < 0) {
qWarning("Maximum depth of replacement has been reached. Entry uuid: %s", uuid().toString().toLatin1().data());
return placeholder;
}
@@ -1060,21 +1082,20 @@ QString Entry::resolvePlaceholderRecursive(const QString& placeholder, int maxDe
const PlaceholderType typeOfPlaceholder = placeholderType(placeholder);
switch (typeOfPlaceholder) {
case PlaceholderType::NotPlaceholder:
return resolveMultiplePlaceholdersRecursive(placeholder, maxDepth - 1);
return resolveMultiplePlaceholdersRecursive(placeholder, maxDepth);
case PlaceholderType::Unknown: {
return "{" % resolveMultiplePlaceholdersRecursive(placeholder.mid(1, placeholder.length() - 2), maxDepth - 1)
% "}";
return "{" % resolveMultiplePlaceholdersRecursive(placeholder.mid(1, placeholder.length() - 2), maxDepth) % "}";
}
case PlaceholderType::Title:
return resolveMultiplePlaceholdersRecursive(title(), maxDepth - 1);
return resolveMultiplePlaceholdersRecursive(title(), maxDepth);
case PlaceholderType::UserName:
return resolveMultiplePlaceholdersRecursive(username(), maxDepth - 1);
return resolveMultiplePlaceholdersRecursive(username(), maxDepth);
case PlaceholderType::Password:
return resolveMultiplePlaceholdersRecursive(password(), maxDepth - 1);
return resolveMultiplePlaceholdersRecursive(password(), maxDepth);
case PlaceholderType::Notes:
return resolveMultiplePlaceholdersRecursive(notes(), maxDepth - 1);
return resolveMultiplePlaceholdersRecursive(notes(), maxDepth);
case PlaceholderType::Url:
return resolveMultiplePlaceholdersRecursive(url(), maxDepth - 1);
return resolveMultiplePlaceholdersRecursive(url(), maxDepth);
case PlaceholderType::DbDir: {
QFileInfo fileInfo(database()->filePath());
return fileInfo.absoluteDir().absolutePath();
@@ -1089,7 +1110,7 @@ QString Entry::resolvePlaceholderRecursive(const QString& placeholder, int maxDe
case PlaceholderType::UrlUserInfo:
case PlaceholderType::UrlUserName:
case PlaceholderType::UrlPassword: {
const QString strUrl = resolveMultiplePlaceholdersRecursive(url(), maxDepth - 1);
const QString strUrl = resolveMultiplePlaceholdersRecursive(url(), maxDepth);
return resolveUrlPlaceholder(strUrl, typeOfPlaceholder);
}
case PlaceholderType::Totp:
@@ -1097,10 +1118,11 @@ QString Entry::resolvePlaceholderRecursive(const QString& placeholder, int maxDe
return totp();
case PlaceholderType::CustomAttribute: {
const QString key = placeholder.mid(3, placeholder.length() - 4); // {S:attr} => mid(3, len - 4)
return attributes()->hasKey(key) ? attributes()->value(key) : QString();
return attributes()->hasKey(key) ? resolveMultiplePlaceholdersRecursive(attributes()->value(key), maxDepth)
: QString();
}
case PlaceholderType::Reference:
return resolveReferencePlaceholderRecursive(placeholder, maxDepth);
return resolveReferencePlaceholderRecursive(placeholder, ++maxDepth);
case PlaceholderType::DateTimeSimple:
case PlaceholderType::DateTimeYear:
case PlaceholderType::DateTimeMonth:
@@ -1115,7 +1137,11 @@ QString Entry::resolvePlaceholderRecursive(const QString& placeholder, int maxDe
case PlaceholderType::DateTimeUtcHour:
case PlaceholderType::DateTimeUtcMinute:
case PlaceholderType::DateTimeUtcSecond:
return resolveMultiplePlaceholdersRecursive(resolveDateTimePlaceholder(typeOfPlaceholder), maxDepth - 1);
return resolveMultiplePlaceholdersRecursive(resolveDateTimePlaceholder(typeOfPlaceholder), maxDepth);
case PlaceholderType::Conversion:
return resolveMultiplePlaceholdersRecursive(resolveConversionPlaceholder(placeholder), maxDepth);
case PlaceholderType::Regex:
return resolveMultiplePlaceholdersRecursive(resolveRegexPlaceholder(placeholder), maxDepth);
}
return placeholder;
@@ -1164,9 +1190,93 @@ QString Entry::resolveDateTimePlaceholder(Entry::PlaceholderType placeholderType
return {};
}
QString Entry::resolveConversionPlaceholder(const QString& str, QString* error) const
{
if (error) {
error->clear();
}
// Extract the inner conversion from the placeholder
QRegularExpression conversionRegEx("^{?t-conv:(.*)}?$", QRegularExpression::CaseInsensitiveOption);
auto placeholder = conversionRegEx.match(str).captured(1);
if (!placeholder.isEmpty()) {
// Determine the separator character and split, include empty groups
auto sep = placeholder[0];
auto parts = placeholder.split(sep);
if (parts.size() >= 4) {
auto resolved = resolveMultiplePlaceholders(parts[1]);
auto type = parts[2].toLower();
if (type == "base64") {
resolved = resolved.toUtf8().toBase64();
} else if (type == "hex") {
resolved = resolved.toUtf8().toHex();
} else if (type == "uri") {
resolved = QUrl::toPercentEncoding(resolved.toUtf8());
} else if (type == "uri-dec") {
resolved = QUrl::fromPercentEncoding(resolved.toUtf8());
} else if (type.startsWith("u")) {
resolved = resolved.toUpper();
} else if (type.startsWith("l")) {
resolved = resolved.toLower();
} else {
if (error) {
*error = tr("Invalid conversion type: %1").arg(type);
}
return {};
}
return resolved;
}
}
if (error) {
*error = tr("Invalid conversion syntax: %1").arg(str);
}
return {};
}
QString Entry::resolveRegexPlaceholder(const QString& str, QString* error) const
{
if (error) {
error->clear();
}
// Extract the inner regex from the placeholder
QRegularExpression conversionRegEx("^{?t-replace-rx:(.*)}?$", QRegularExpression::CaseInsensitiveOption);
auto placeholder = conversionRegEx.match(str).captured(1);
if (!placeholder.isEmpty()) {
// Determine the separator character and split, include empty groups
auto sep = placeholder[0];
auto parts = placeholder.split(sep);
if (parts.size() >= 5) {
auto resolvedText = resolveMultiplePlaceholders(parts[1]);
auto resolvedSearch = resolveMultiplePlaceholders(parts[2]);
auto resolvedReplace = resolveMultiplePlaceholders(parts[3]);
// Replace $<num> with \\<num> to support Qt substitutions
resolvedReplace.replace(QRegularExpression(R"(\$(\d+))"), R"(\\1)");
auto searchRegex = QRegularExpression(resolvedSearch);
if (!searchRegex.isValid()) {
if (error) {
*error =
tr("Invalid regular expression syntax %1\n%2").arg(resolvedSearch, searchRegex.errorString());
}
return {};
}
return resolvedText.replace(searchRegex, resolvedReplace);
}
}
if (error) {
*error = tr("Invalid conversion syntax: %1").arg(str);
}
return {};
}
QString Entry::resolveReferencePlaceholderRecursive(const QString& placeholder, int maxDepth) const
{
if (maxDepth <= 0) {
if (--maxDepth < 0) {
qWarning("Maximum depth of replacement has been reached. Entry uuid: %s", uuid().toString().toLatin1().data());
return placeholder;
}
@@ -1194,7 +1304,7 @@ QString Entry::resolveReferencePlaceholderRecursive(const QString& placeholder,
// Referencing fields of other entries only works with standard fields, not with custom user strings.
// If you want to reference a custom user string, you need to place a redirection in a standard field
// of the entry with the custom string, using {S:<Name>}, and reference the standard field.
result = refEntry->resolveMultiplePlaceholdersRecursive(result, maxDepth - 1);
result = refEntry->resolveMultiplePlaceholdersRecursive(result, maxDepth);
}
return result;
@@ -1369,15 +1479,21 @@ QString Entry::resolveUrlPlaceholder(const QString& str, Entry::PlaceholderType
Entry::PlaceholderType Entry::placeholderType(const QString& placeholder) const
{
if (!placeholder.startsWith(QLatin1Char('{')) || !placeholder.endsWith(QLatin1Char('}'))) {
if (!placeholder.startsWith(QStringLiteral("{")) || !placeholder.endsWith(QStringLiteral("}"))) {
return PlaceholderType::NotPlaceholder;
}
if (placeholder.startsWith(QLatin1Literal("{S:"))) {
if (placeholder.startsWith(QStringLiteral("{S:"))) {
return PlaceholderType::CustomAttribute;
}
if (placeholder.startsWith(QLatin1Literal("{REF:"))) {
if (placeholder.startsWith(QStringLiteral("{REF:"))) {
return PlaceholderType::Reference;
}
if (placeholder.startsWith(QStringLiteral("{T-CONV:"), Qt::CaseInsensitive)) {
return PlaceholderType::Conversion;
}
if (placeholder.startsWith(QStringLiteral("{T-REPLACE-RX:"), Qt::CaseInsensitive)) {
return PlaceholderType::Regex;
}
static const QMap<QString, PlaceholderType> placeholders{
{QStringLiteral("{TITLE}"), PlaceholderType::Title},

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2024 KeePassXC Team <team@keepassxc.org>
* Copyright (C) 2025 KeePassXC Team <team@keepassxc.org>
* Copyright (C) 2010 Felix Geyer <debfx@fobos.de>
*
* This program is free software: you can redistribute it and/or modify
@@ -100,7 +100,9 @@ public:
const AutoTypeAssociations* autoTypeAssociations() const;
QString title() const;
QString url() const;
QString resolveUrl() const;
QStringList getAllUrls() const;
QStringList getAdditionalUrls() const;
QString webUrl() const;
QString displayUrl() const;
QString username() const;
@@ -126,6 +128,7 @@ public:
bool hasTotp() const;
bool isExpired() const;
bool willExpireInDays(int days) const;
void expireNow();
bool isRecycled() const;
bool isAttributeReference(const QString& key) const;
bool isAttributeReferenceOf(const QString& key, const QUuid& uuid) const;
@@ -225,7 +228,9 @@ public:
DateTimeUtcHour,
DateTimeUtcMinute,
DateTimeUtcSecond,
DbDir
DbDir,
Conversion,
Regex
};
static const int DefaultIconNumber;
@@ -244,6 +249,8 @@ public:
QString resolvePlaceholder(const QString& str) const;
QString resolveUrlPlaceholder(const QString& str, PlaceholderType placeholderType) const;
QString resolveDateTimePlaceholder(PlaceholderType placeholderType) const;
QString resolveConversionPlaceholder(const QString& str, QString* error = nullptr) const;
QString resolveRegexPlaceholder(const QString& str, QString* error = nullptr) const;
PlaceholderType placeholderType(const QString& placeholder) const;
QString resolveUrl(const QString& url) const;

View File

@@ -25,6 +25,7 @@
#include <QDesktopServices>
#include <QDir>
#include <QProcessEnvironment>
#include <QScopeGuard>
#include <QSet>
#include <QTemporaryFile>
#include <QUrl>
@@ -236,8 +237,10 @@ bool EntryAttachments::openAttachment(const QString& key, QString* errorMessage)
const bool saveOk = tmpFile.open() && tmpFile.setPermissions(QFile::ReadOwner | QFile::WriteOwner)
&& tmpFile.write(attachmentData) == attachmentData.size() && tmpFile.flush();
if (!saveOk && errorMessage) {
*errorMessage = QString("%1 - %2").arg(key, tmpFile.errorString());
if (!saveOk) {
if (errorMessage) {
*errorMessage = QString("%1 - %2").arg(key, tmpFile.errorString());
}
return false;
}
@@ -250,15 +253,35 @@ bool EntryAttachments::openAttachment(const QString& key, QString* errorMessage)
watcher->start(tmpFile.fileName(), 5);
connect(watcher.data(), &FileWatcher::fileChanged, this, &EntryAttachments::attachmentFileModified);
m_attachmentFileWatchers.insert(tmpFile.fileName(), watcher);
} else if (auto path = m_openedAttachments.value(key); m_attachmentFileWatchers.contains(path)) {
// If we are already watching an open attachment file, overwrite it with the information from the entry
auto watcher = m_attachmentFileWatchers.value(path);
watcher->stop();
QFile file(path);
auto finally = qScopeGuard([&file, &watcher, &path] {
file.close();
watcher->start(path, 5);
});
const auto attachmentData = value(key);
const bool saveOk = file.open(QIODevice::WriteOnly) && file.setPermissions(QFile::ReadOwner | QFile::WriteOwner)
&& file.write(attachmentData) == attachmentData.size() && file.flush();
if (!saveOk) {
if (errorMessage) {
*errorMessage = QString("%1 - %2").arg(key, file.errorString());
}
return false;
}
}
const bool openOk = QDesktopServices::openUrl(QUrl::fromLocalFile(m_openedAttachments.value(key)));
if (!openOk && errorMessage) {
*errorMessage = tr("Cannot open file \"%1\"").arg(key);
return false;
}
return true;
return openOk;
}
void EntryAttachments::attachmentFileModified(const QString& path)

View File

@@ -36,7 +36,20 @@ const QString EntryAttributes::SearchTextGroupName = "SearchText";
const QString EntryAttributes::RememberCmdExecAttr = "_EXEC_CMD";
const QString EntryAttributes::AdditionalUrlAttribute = "KP2A_URL";
// Passkey related attributes
const QString EntryAttributes::PasskeyAttribute = "KPEX_PASSKEY";
const QString EntryAttributes::KPEX_PASSKEY_USERNAME = QStringLiteral("KPEX_PASSKEY_USERNAME");
const QString EntryAttributes::KPEX_PASSKEY_CREDENTIAL_ID = QStringLiteral("KPEX_PASSKEY_CREDENTIAL_ID");
const QString EntryAttributes::KPEX_PASSKEY_PRIVATE_KEY_PEM = QStringLiteral("KPEX_PASSKEY_PRIVATE_KEY_PEM");
const QString EntryAttributes::KPEX_PASSKEY_RELYING_PARTY = QStringLiteral("KPEX_PASSKEY_RELYING_PARTY");
const QString EntryAttributes::KPEX_PASSKEY_USER_HANDLE = QStringLiteral("KPEX_PASSKEY_USER_HANDLE");
const QString EntryAttributes::KPEX_PASSKEY_PRIVATE_KEY_START = QStringLiteral("-----BEGIN PRIVATE KEY-----");
const QString EntryAttributes::KPEX_PASSKEY_PRIVATE_KEY_END = QStringLiteral("-----END PRIVATE KEY-----");
// For compatibility with StrongBox
const QString EntryAttributes::KPEX_PASSKEY_GENERATED_USER_ID = QStringLiteral("KPEX_PASSKEY_GENERATED_USER_ID");
const QString EntryAttributes::KPXC_PASSKEY_USERNAME = QStringLiteral("KPXC_PASSKEY_USERNAME");
EntryAttributes::EntryAttributes(QObject* parent)
: ModifiableObject(parent)

View File

@@ -64,7 +64,18 @@ public:
static const QStringList DefaultAttributes;
static const QString RememberCmdExecAttr;
static const QString AdditionalUrlAttribute;
static const QString PasskeyAttribute;
static const QString KPXC_PASSKEY_USERNAME;
static const QString KPEX_PASSKEY_USERNAME;
static const QString KPEX_PASSKEY_CREDENTIAL_ID;
static const QString KPEX_PASSKEY_GENERATED_USER_ID;
static const QString KPEX_PASSKEY_PRIVATE_KEY_PEM;
static const QString KPEX_PASSKEY_RELYING_PARTY;
static const QString KPEX_PASSKEY_USER_HANDLE;
static const QString KPEX_PASSKEY_PRIVATE_KEY_START;
static const QString KPEX_PASSKEY_PRIVATE_KEY_END;
static bool isDefaultAttribute(const QString& key);
static bool isPasskeyAttribute(const QString& key);

View File

@@ -79,17 +79,18 @@ void FileWatcher::stop()
m_fileChecksum.clear();
m_fileChecksumTimer.stop();
m_fileChangeDelayTimer.stop();
m_paused = false;
}
void FileWatcher::pause()
{
m_ignoreFileChange = true;
m_paused = true;
m_fileChangeDelayTimer.stop();
}
void FileWatcher::resume()
{
m_ignoreFileChange = false;
m_paused = false;
// Add a short delay to start in the next event loop
if (!m_fileIgnoreDelayTimer.isActive()) {
m_fileIgnoreDelayTimer.start(0);
@@ -98,7 +99,7 @@ void FileWatcher::resume()
bool FileWatcher::shouldIgnoreChanges()
{
return m_filePath.isEmpty() || m_ignoreFileChange || m_fileIgnoreDelayTimer.isActive()
return m_filePath.isEmpty() || m_ignoreFileChange || m_paused || m_fileIgnoreDelayTimer.isActive()
|| m_fileChangeDelayTimer.isActive();
}
@@ -118,7 +119,7 @@ void FileWatcher::checkFileChanged()
AsyncTask::runThenCallback([this] { return calculateChecksum(); },
this,
[this](QByteArray checksum) {
[this](const QByteArray& checksum) {
if (checksum != m_fileChecksum) {
m_fileChecksum = checksum;
m_fileChangeDelayTimer.start(0);

View File

@@ -56,6 +56,7 @@ private:
QTimer m_fileChecksumTimer;
int m_fileChecksumSizeBytes = -1;
bool m_ignoreFileChange = false;
bool m_paused = false;
};
#endif // KEEPASSXC_FILEWATCHER_H

View File

@@ -72,6 +72,7 @@ void PassphraseGenerator::setWordList(const QString& path)
}
QTextStream in(&file);
in.setCodec("UTF-8");
QString line = in.readLine();
bool isSigned = line.startsWith("-----BEGIN PGP SIGNED MESSAGE-----");
if (isSigned) {
@@ -122,6 +123,7 @@ QString PassphraseGenerator::generatePassphrase() const
}
QStringList words;
int randomIndex = randomGen()->randomUInt(static_cast<quint32>(m_wordCount));
for (int i = 0; i < m_wordCount; ++i) {
int wordIndex = randomGen()->randomUInt(static_cast<quint32>(m_wordlist.size()));
auto tmpWord = m_wordlist.at(wordIndex);
@@ -134,6 +136,9 @@ QString PassphraseGenerator::generatePassphrase() const
case TITLECASE:
tmpWord = tmpWord.replace(0, 1, tmpWord.left(1).toUpper());
break;
case MIXEDCASE:
tmpWord = i == randomIndex ? tmpWord.toUpper() : tmpWord.toLower();
break;
case LOWERCASE:
tmpWord = tmpWord.toLower();
break;

View File

@@ -30,7 +30,8 @@ public:
{
LOWERCASE,
UPPERCASE,
TITLECASE
TITLECASE,
MIXEDCASE
};
double estimateEntropy(int wordCount = 0);

View File

@@ -475,4 +475,32 @@ namespace Tools
return pattern;
}
MimeType toMimeType(const QString& mimeName)
{
static QStringList textFormats = {
"text/",
"application/json",
"application/xml",
"application/soap+xml",
"application/x-yaml",
"application/protobuf",
};
static QStringList imageFormats = {"image/"};
static auto isCompatible = [](const QString& format, const QStringList& list) {
return std::any_of(
list.cbegin(), list.cend(), [&format](const auto& item) { return format.startsWith(item); });
};
if (isCompatible(mimeName, imageFormats)) {
return MimeType::Image;
}
if (isCompatible(mimeName, textFormats)) {
return MimeType::PlainText;
}
return MimeType::Unknown;
}
} // namespace Tools

View File

@@ -114,6 +114,15 @@ namespace Tools
QVariantMap qo2qvm(const QObject* object, const QStringList& ignoredProperties = {"objectName"});
QString substituteBackupFilePath(QString pattern, const QString& databasePath);
enum class MimeType : uint8_t
{
Image,
PlainText,
Unknown
};
MimeType toMimeType(const QString& mimeName);
} // namespace Tools
#endif // KEEPASSX_TOOLS_H

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2023 KeePassXC Team <team@keepassxc.org>
* Copyright (C) 2024 KeePassXC Team <team@keepassxc.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -81,6 +81,43 @@ namespace
entry->setTotp(Totp::parseSettings(totp));
}
// Parse passkey
if (loginMap.contains("fido2Credentials")) {
const auto fido2CredentialsMap = loginMap.value("fido2Credentials").toList();
for (const auto& fido2Credentials : fido2CredentialsMap) {
const auto passkey = fido2Credentials.toMap();
// Change from UUID to base64 byte array
const auto credentialIdValue = passkey.value("credentialId").toString();
if (!credentialIdValue.isEmpty()) {
const auto credentialUuid = Tools::uuidToHex(credentialIdValue);
const auto credentialIdArray = QByteArray::fromHex(credentialUuid.toUtf8());
const auto credentialId =
credentialIdArray.toBase64(QByteArray::Base64UrlEncoding | QByteArray::OmitTrailingEquals);
entry->attributes()->set(EntryAttributes::KPEX_PASSKEY_CREDENTIAL_ID, credentialId, true);
}
// Base64 needs to be changed from URL encoding back to normal, and the result as PEM string
const auto keyValue = passkey.value("keyValue").toString();
if (!keyValue.isEmpty()) {
const auto keyValueArray =
QByteArray::fromBase64(keyValue.toUtf8(), QByteArray::Base64UrlEncoding);
auto privateKey = keyValueArray.toBase64(QByteArray::Base64Encoding);
privateKey.insert(0, EntryAttributes::KPEX_PASSKEY_PRIVATE_KEY_START.toUtf8());
privateKey.append(EntryAttributes::KPEX_PASSKEY_PRIVATE_KEY_END.toUtf8());
entry->attributes()->set(EntryAttributes::KPEX_PASSKEY_PRIVATE_KEY_PEM, privateKey, true);
}
entry->attributes()->set(EntryAttributes::KPEX_PASSKEY_USERNAME,
passkey.value("userName").toString());
entry->attributes()->set(EntryAttributes::KPEX_PASSKEY_RELYING_PARTY,
passkey.value("rpId").toString());
entry->attributes()->set(
EntryAttributes::KPEX_PASSKEY_USER_HANDLE, passkey.value("userHandle").toString(), true);
entry->addTag(QObject::tr("Passkey"));
}
}
// Set the entry url(s)
int i = 1;
for (const auto& urlObj : loginMap.value("uris").toList()) {

View File

@@ -1,4 +1,4 @@
/*
/*
* Copyright (C) 2016 Enrico Mariotti <enricomariotti@yahoo.it>
* Copyright (C) 2017 KeePassXC Team <team@keepassxc.org>
*
@@ -53,7 +53,7 @@ bool CsvParser::reparse()
return parseFile();
}
bool CsvParser::parse(QFile* device)
bool CsvParser::parse(QIODevice* device)
{
clear();
if (!device) {
@@ -66,7 +66,7 @@ bool CsvParser::parse(QFile* device)
return parseFile();
}
bool CsvParser::readFile(QFile* device)
bool CsvParser::readFile(QIODevice* device)
{
if (device->isOpen()) {
device->close();
@@ -79,6 +79,7 @@ bool CsvParser::readFile(QFile* device)
} else {
device->close();
// Normalize on newline endings
m_array.replace("\r\n", "\n");
m_array.replace("\r", "\n");
if (m_array.isEmpty()) {
@@ -121,7 +122,7 @@ bool CsvParser::parseFile()
parseRecord();
while (!m_isEof) {
if (!skipEndline()) {
appendStatusMsg(QObject::tr("malformed string"), true);
appendStatusMsg(QObject::tr("malformed string, possible unescaped delimiter"), true);
}
m_currRow++;
m_currCol = 1;
@@ -161,7 +162,7 @@ void CsvParser::parseField(CsvRow& row)
{
QString field;
peek(m_ch);
if (m_ch != m_separator && m_ch != '\n' && m_ch != '\r') {
if (m_ch != m_separator && m_ch != '\n') {
if (isQualifier(m_ch)) {
parseQuoted(field);
} else {
@@ -190,7 +191,7 @@ void CsvParser::parseQuoted(QString& s)
getChar(m_ch);
parseEscaped(s);
if (!isQualifier(m_ch)) {
appendStatusMsg(QObject::tr("missing closing quote"), true);
appendStatusMsg(QObject::tr("missing closing delimiter"), true);
}
}
@@ -391,6 +392,12 @@ int CsvParser::getCsvRows() const
void CsvParser::appendStatusMsg(const QString& s, bool isCritical)
{
m_statusMsg += QObject::tr("%1: (row, col) %2,%3").arg(s, m_currRow, m_currCol).append("\n");
if (!m_statusMsg.isEmpty()) {
m_statusMsg.append("\n");
}
m_statusMsg +=
QObject::tr("%1, row: %2, column: %3").arg(s, QString::number(m_currRow), QString::number(m_currCol));
m_isGood = !isCritical;
}

View File

@@ -22,7 +22,7 @@
#include <QBuffer>
#include <QTextStream>
class QFile;
class QIODevice;
typedef QStringList CsvRow;
typedef QList<CsvRow> CsvTable;
@@ -34,7 +34,7 @@ public:
CsvParser();
~CsvParser();
// read data from device and parse it
bool parse(QFile* device);
bool parse(QIODevice* device);
bool isFileLoaded();
// reparse the same buffer (device is not opened again)
bool reparse();
@@ -85,7 +85,7 @@ private:
void parseQuoted(QString& s);
void parseEscaped(QString& s);
void parseEscapedText(QString& s);
bool readFile(QFile* device);
bool readFile(QIODevice* device);
void reset();
void clear();
bool skipEndline();

View File

@@ -17,28 +17,13 @@
#include "HtmlExporter.h"
#include <QBuffer>
#include <QFile>
#include "core/Group.h"
#include "core/Metadata.h"
#include "gui/Icons.h"
namespace
{
QString PixmapToHTML(const QPixmap& pixmap)
{
if (pixmap.isNull()) {
return "";
}
// Based on https://stackoverflow.com/a/6621278
QByteArray a;
QBuffer buffer(&a);
pixmap.save(&buffer, "PNG");
return QString("<img src=\"data:image/png;base64,") + a.toBase64() + "\"/>";
}
QString formatEntry(const Entry& entry)
{
// Here we collect the table rows with this entry's data fields
@@ -127,15 +112,62 @@ QString HtmlExporter::errorString() const
return m_error;
}
QString HtmlExporter::groupIconToHtml(const Group* /* group */)
{
return "";
}
QString HtmlExporter::entryIconToHtml(const Entry* /* entry */)
{
return "";
}
bool HtmlExporter::exportDatabase(QIODevice* device,
const QSharedPointer<const Database>& db,
bool sorted,
bool ascending)
{
if (device->write(exportHeader(db).toUtf8()) == -1) {
m_error = device->errorString();
return false;
}
if (db->rootGroup()) {
if (device->write(exportGroup(*db->rootGroup(), QString(), sorted, ascending).toUtf8()) == -1) {
m_error = device->errorString();
return false;
}
}
if (device->write(exportFooter().toUtf8()) == -1) {
m_error = device->errorString();
return false;
}
return true;
}
QString HtmlExporter::exportDatabase(const QSharedPointer<const Database>& db, bool sorted, bool ascending)
{
QString response;
response = exportHeader(db);
if (!response.isEmpty()) {
if (db->rootGroup()) {
response.append(exportGroup(*db->rootGroup(), QString(), sorted, ascending));
}
response.append(exportFooter());
}
return response;
}
QString HtmlExporter::exportHeader(const QSharedPointer<const Database>& db)
{
const auto meta = db->metadata();
if (!meta) {
m_error = "Internal error: metadata is NULL";
return false;
return "";
}
const auto header = QString("<html>"
@@ -171,33 +203,23 @@ bool HtmlExporter::exportDatabase(QIODevice* device,
+ "</p>"
"<p><code>"
+ db->filePath().toHtmlEscaped() + "</code></p>");
const auto footer = QString("</body>"
"</html>");
if (device->write(header.toUtf8()) == -1) {
m_error = device->errorString();
return false;
}
if (db->rootGroup()) {
if (!writeGroup(*device, *db->rootGroup(), QString(), sorted, ascending)) {
return false;
}
}
if (device->write(footer.toUtf8()) == -1) {
m_error = device->errorString();
return false;
}
return true;
return header;
}
bool HtmlExporter::writeGroup(QIODevice& device, const Group& group, QString path, bool sorted, bool ascending)
QString HtmlExporter::exportFooter()
{
const auto footer = QString("</body>"
"</html>");
return footer;
}
QString HtmlExporter::exportGroup(const Group& group, QString path, bool sorted, bool ascending)
{
QString response = "";
// Don't output the recycle bin
if (&group == group.database()->metadata()->recycleBin()) {
return true;
return response;
}
if (!path.isEmpty()) {
@@ -212,8 +234,11 @@ bool HtmlExporter::writeGroup(QIODevice& device, const Group& group, QString pat
if (!group.entries().empty() || !notes.isEmpty()) {
// Header line
auto header = QString("<hr><h2>");
header.append(PixmapToHTML(Icons::groupIconPixmap(&group, IconSize::Medium)));
header.append("&nbsp;");
auto groupIcon = this->groupIconToHtml(&group);
if (!groupIcon.isEmpty()) {
header.append(groupIcon);
header.append("&nbsp;");
}
header.append(path);
header.append("</h2>\n");
@@ -224,11 +249,8 @@ bool HtmlExporter::writeGroup(QIODevice& device, const Group& group, QString pat
header.append("</p>");
}
// Output it
if (device.write(header.toUtf8()) == -1) {
m_error = device.errorString();
return false;
}
// Append it to the output
response.append(header);
}
// Begin the table for the entries in this group
@@ -242,7 +264,7 @@ bool HtmlExporter::writeGroup(QIODevice& device, const Group& group, QString pat
});
}
// Output the entries in this group
// Append to the output the entries in this group
for (const auto* entry : entries) {
auto formatted_entry = formatEntry(*entry);
@@ -252,7 +274,10 @@ bool HtmlExporter::writeGroup(QIODevice& device, const Group& group, QString pat
// Output it into our table. First the left side with
// icon and entry title ...
table += "<tr>";
table += "<td width=\"1%\">" + PixmapToHTML(Icons::entryIconPixmap(entry, IconSize::Medium)) + "</td>";
auto entryIcon = this->entryIconToHtml(entry);
if (!entryIcon.isEmpty()) {
table += "<td width=\"1%\">" + entryIcon + "</td>";
}
auto caption = "<caption>" + entry->title().toHtmlEscaped() + "</caption>";
// ... then the right side with the data fields
@@ -261,12 +286,9 @@ bool HtmlExporter::writeGroup(QIODevice& device, const Group& group, QString pat
table += "</tr>";
}
// Output the complete table of this group
// Append the complete table of this group to the output
table.append("</table>\n");
if (device.write(table.toUtf8()) == -1) {
m_error = device.errorString();
return false;
}
response.append(table);
auto children = group.children();
if (sorted) {
@@ -276,12 +298,12 @@ bool HtmlExporter::writeGroup(QIODevice& device, const Group& group, QString pat
});
}
// Recursively output the child groups
// Recursively append to the output the child groups
for (const auto* child : children) {
if (child && !writeGroup(device, *child, path, sorted, ascending)) {
return false;
if (child) {
response.append(exportGroup(*child, path, sorted, ascending));
}
}
return true;
return response;
}

View File

@@ -21,6 +21,8 @@
#include <QSharedPointer>
#include <QString>
#include "core/Group.h"
class Database;
class Group;
class QIODevice;
@@ -32,18 +34,23 @@ public:
const QSharedPointer<const Database>& db,
bool sorted = true,
bool ascending = true);
QString errorString() const;
private:
bool exportDatabase(QIODevice* device,
const QSharedPointer<const Database>& db,
bool sorted = true,
bool ascending = true);
bool writeGroup(QIODevice& device,
const Group& group,
QString path = QString(),
bool sorted = true,
bool ascending = true);
QString exportDatabase(const QSharedPointer<const Database>& db, bool sorted = true, bool ascending = true);
QString errorString() const;
virtual ~HtmlExporter() = default;
protected:
virtual QString groupIconToHtml(const Group* group);
virtual QString entryIconToHtml(const Entry* entry);
private:
QString exportGroup(const Group& group, QString path = QString(), bool sorted = true, bool ascending = true);
QString exportHeader(const QSharedPointer<const Database>& db);
QString exportFooter();
QString m_error;
};

View File

@@ -71,7 +71,8 @@ void KdbxWriter::extractDatabase(QByteArray& xmlOutput, Database* db)
QBuffer buffer;
buffer.setBuffer(&xmlOutput);
buffer.open(QIODevice::WriteOnly);
KdbxXmlWriter writer(db->formatVersion());
KdbxXmlWriter::BinaryIdxMap idxMap;
KdbxXmlWriter writer(db->formatVersion(), idxMap);
writer.disableInnerStreamProtection(true);
writer.writeDatabase(&buffer, db);
}

View File

@@ -0,0 +1,221 @@
/*
* Copyright (C) 2024 KeePassXC Team <team@keepassxc.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 or (at your option)
* version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include "ProtonPassReader.h"
#include "core/Database.h"
#include "core/Entry.h"
#include "core/Group.h"
#include "core/Metadata.h"
#include "core/Tools.h"
#include "core/Totp.h"
#include "crypto/CryptoHash.h"
#include <QFileInfo>
#include <QJsonArray>
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonParseError>
#include <QMap>
#include <QScopedPointer>
#include <QUrl>
namespace
{
Entry* readItem(const QJsonObject& item)
{
const auto itemMap = item.toVariantMap();
const auto dataMap = itemMap.value("data").toMap();
const auto metadataMap = dataMap.value("metadata").toMap();
// Create entry and assign basic values
QScopedPointer<Entry> entry(new Entry());
entry->setUuid(QUuid::createUuid());
entry->setTitle(metadataMap.value("name").toString());
entry->setNotes(metadataMap.value("note").toString());
if (itemMap.value("pinned").toBool()) {
entry->addTag(QObject::tr("Favorite", "Tag for favorite entries"));
}
// Handle specific item types
auto type = dataMap.value("type").toString();
// Login
if (type.compare("login", Qt::CaseInsensitive) == 0) {
const auto loginMap = dataMap.value("content").toMap();
entry->setUsername(loginMap.value("itemUsername").toString());
entry->setPassword(loginMap.value("password").toString());
if (loginMap.contains("totpUri")) {
auto totp = loginMap.value("totpUri").toString();
if (!totp.startsWith("otpauth://")) {
QUrl url(QString("otpauth://totp/%1:%2?secret=%3")
.arg(QString(QUrl::toPercentEncoding(entry->title())),
QString(QUrl::toPercentEncoding(entry->username())),
QString(QUrl::toPercentEncoding(totp))));
totp = url.toString(QUrl::FullyEncoded);
}
entry->setTotp(Totp::parseSettings(totp));
}
if (loginMap.contains("itemEmail")) {
entry->attributes()->set("login_email", loginMap.value("itemEmail").toString());
}
// Set the entry url(s)
int i = 1;
for (const auto& urlObj : loginMap.value("urls").toList()) {
const auto url = urlObj.toString();
if (entry->url().isEmpty()) {
// First url encountered is set as the primary url
entry->setUrl(url);
} else {
// Subsequent urls
entry->attributes()->set(
QString("%1_%2").arg(EntryAttributes::AdditionalUrlAttribute, QString::number(i)), url);
++i;
}
}
}
// Credit Card
else if (type.compare("creditCard", Qt::CaseInsensitive) == 0) {
const auto cardMap = dataMap.value("content").toMap();
entry->setUsername(cardMap.value("number").toString());
entry->setPassword(cardMap.value("verificationNumber").toString());
const QStringList attrs({"cardholderName", "pin", "expirationDate"});
const QStringList sensitive({"pin"});
for (const auto& attr : attrs) {
auto value = cardMap.value(attr).toString();
if (!value.isEmpty()) {
entry->attributes()->set("card_" + attr, value, sensitive.contains(attr));
}
}
}
// Parse extra fields
for (const auto& field : dataMap.value("extraFields").toList()) {
// Derive a prefix for attribute names using the title or uuid if missing
const auto fieldMap = field.toMap();
auto name = fieldMap.value("fieldName").toString();
if (entry->attributes()->hasKey(name)) {
name = QString("%1_%2").arg(name, QUuid::createUuid().toString().mid(1, 5));
}
QString value;
const auto fieldType = fieldMap.value("type").toString();
if (fieldType.compare("totp", Qt::CaseInsensitive) == 0) {
value = fieldMap.value("data").toJsonObject().value("totpUri").toString();
} else {
value = fieldMap.value("data").toJsonObject().value("content").toString();
}
entry->attributes()->set(name, value, fieldType.compare("hidden", Qt::CaseInsensitive) == 0);
}
// Checked expired/deleted state
if (itemMap.value("state").toInt() == 2) {
entry->setExpires(true);
entry->setExpiryTime(QDateTime::currentDateTimeUtc());
}
// Collapse any accumulated history
entry->removeHistoryItems(entry->historyItems());
// Adjust the created and modified times
auto timeInfo = entry->timeInfo();
const auto createdTime = QDateTime::fromSecsSinceEpoch(itemMap.value("createTime").toULongLong(), Qt::UTC);
const auto modifiedTime = QDateTime::fromSecsSinceEpoch(itemMap.value("modifyTime").toULongLong(), Qt::UTC);
timeInfo.setCreationTime(createdTime);
timeInfo.setLastModificationTime(modifiedTime);
timeInfo.setLastAccessTime(modifiedTime);
entry->setTimeInfo(timeInfo);
return entry.take();
}
void writeVaultToDatabase(const QJsonObject& vault, QSharedPointer<Database> db)
{
// Create groups from vaults and store a temporary map of id -> uuid
const auto vaults = vault.value("vaults").toObject().toVariantMap();
for (const auto& vaultId : vaults.keys()) {
auto vaultObj = vaults.value(vaultId).toJsonObject();
auto group = new Group();
group->setUuid(QUuid::createUuid());
group->setName(vaultObj.value("name").toString());
group->setNotes(vaultObj.value("description").toString());
group->setParent(db->rootGroup());
const auto items = vaultObj.value("items").toArray();
for (const auto& item : items) {
auto entry = readItem(item.toObject());
if (entry) {
entry->setGroup(group, false);
}
}
}
}
} // namespace
bool ProtonPassReader::hasError()
{
return !m_error.isEmpty();
}
QString ProtonPassReader::errorString()
{
return m_error;
}
QSharedPointer<Database> ProtonPassReader::convert(const QString& path)
{
m_error.clear();
QFileInfo fileinfo(path);
if (!fileinfo.exists()) {
m_error = QObject::tr("File does not exist.").arg(path);
return {};
}
// Bitwarden uses a json file format
QFile file(fileinfo.absoluteFilePath());
if (!file.open(QFile::ReadOnly)) {
m_error = QObject::tr("Cannot open file: %1").arg(file.errorString());
return {};
}
QJsonParseError error;
auto json = QJsonDocument::fromJson(file.readAll(), &error).object();
if (error.error != QJsonParseError::NoError) {
m_error =
QObject::tr("Cannot parse file: %1 at position %2").arg(error.errorString(), QString::number(error.offset));
return {};
}
file.close();
if (json.value("encrypted").toBool()) {
m_error = QObject::tr("Encrypted files are not supported.");
return {};
}
auto db = QSharedPointer<Database>::create();
db->rootGroup()->setName(QObject::tr("Proton Pass Import"));
writeVaultToDatabase(json, db);
return db;
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright (C) 2024 KeePassXC Team <team@keepassxc.org>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 or (at your option)
* version 3 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef PROTONPASS_READER_H
#define PROTONPASS_READER_H
#include <QSharedPointer>
class Database;
/*!
* Imports a Proton Pass vault in JSON format: https://proton.me/support/pass-export
*/
class ProtonPassReader
{
public:
explicit ProtonPassReader() = default;
~ProtonPassReader() = default;
QSharedPointer<Database> convert(const QString& path);
bool hasError();
QString errorString();
private:
QString m_error;
};
#endif // PROTONPASS_READER_H

View File

@@ -44,6 +44,7 @@ namespace
{
constexpr int WaitTimeoutMSec = 150;
const char BlockSizeProperty[] = "blockSize";
int g_OriginalFontSize = 0;
} // namespace
Application::Application(int& argc, char** argv)
@@ -151,12 +152,7 @@ void Application::bootstrap(const QString& uiLanguage)
{
Bootstrap::bootstrap(uiLanguage);
#ifdef Q_OS_WIN
// Qt on Windows uses "MS Shell Dlg 2" as the default font for many widgets, which resolves
// to Tahoma 8pt, whereas the correct font would be "Segoe UI" 9pt.
// Apparently, some widgets are already using the correct font. Thanks, MuseScore for this neat fix!
QApplication::setFont(QApplication::font("QMessageBox"));
#endif
applyFontSize();
osUtils->registerNativeEventFilter();
MessageBox::initializeButtonDefs();
@@ -205,6 +201,28 @@ void Application::applyTheme()
}
}
void Application::applyFontSize()
{
auto font = QApplication::font();
// Store the original font size on first call
if (g_OriginalFontSize <= 0) {
#ifdef Q_OS_WIN
// Qt on Windows uses "MS Shell Dlg 2" as the default font for many widgets, which resolves
// to Tahoma 8pt, whereas the correct font would be "Segoe UI" 9pt.
// Apparently, some widgets are already using the correct font. Thanks, MuseScore for this neat fix!
font = QApplication::font("QMessageBox");
#endif
g_OriginalFontSize = font.pointSize();
}
// Adjust application wide default font size
auto newSize = g_OriginalFontSize + qBound(-2, config()->get(Config::GUI_FontSizeOffset).toInt(), 4);
font.setPointSize(newSize);
QApplication::setFont(font);
QApplication::setFont(font, "QWidget");
}
bool Application::event(QEvent* event)
{
// Handle Apple QFileOpenEvent from finder (double click on .kdbx file)

View File

@@ -42,6 +42,7 @@ public:
~Application() override;
static void bootstrap(const QString& uiLanguage = "system");
static void applyFontSize();
void applyTheme();

View File

@@ -170,6 +170,7 @@ ApplicationSettingsWidget::ApplicationSettingsWidget(QWidget* parent)
m_generalUi->toolButtonStyleComboBox->installEventFilter(mouseWheelFilter);
m_generalUi->languageComboBox->installEventFilter(mouseWheelFilter);
m_generalUi->trayIconAppearance->installEventFilter(mouseWheelFilter);
m_generalUi->fontSizeComboBox->installEventFilter(mouseWheelFilter);
#ifdef WITH_XC_UPDATECHECK
connect(m_generalUi->checkForUpdatesOnStartupCheckBox, SIGNAL(toggled(bool)), SLOT(checkUpdatesToggled(bool)));
@@ -225,6 +226,7 @@ void ApplicationSettingsWidget::loadSettings()
m_generalUi->autoReloadOnChangeCheckBox->setChecked(config()->get(Config::AutoReloadOnChange).toBool());
m_generalUi->minimizeAfterUnlockCheckBox->setChecked(config()->get(Config::MinimizeAfterUnlock).toBool());
m_generalUi->minimizeOnOpenUrlCheckBox->setChecked(config()->get(Config::MinimizeOnOpenUrl).toBool());
m_generalUi->openUrlOnDoubleClick->setChecked(config()->get(Config::OpenURLOnDoubleClick).toBool());
m_generalUi->hideWindowOnCopyCheckBox->setChecked(config()->get(Config::HideWindowOnCopy).toBool());
hideWindowOnCopyCheckBoxToggled(m_generalUi->hideWindowOnCopyCheckBox->isChecked());
m_generalUi->minimizeOnCopyRadioButton->setChecked(config()->get(Config::MinimizeOnCopy).toBool());
@@ -264,10 +266,25 @@ void ApplicationSettingsWidget::loadSettings()
m_generalUi->toolButtonStyleComboBox->addItem(tr("Follow style"), Qt::ToolButtonFollowStyle);
int toolButtonStyleIndex =
m_generalUi->toolButtonStyleComboBox->findData(config()->get(Config::GUI_ToolButtonStyle));
if (toolButtonStyleIndex > 0) {
if (toolButtonStyleIndex >= 0) {
m_generalUi->toolButtonStyleComboBox->setCurrentIndex(toolButtonStyleIndex);
}
m_generalUi->fontSizeComboBox->clear();
m_generalUi->fontSizeComboBox->addItem(tr("Small"), -1);
m_generalUi->fontSizeComboBox->addItem(tr("Normal"), 0);
m_generalUi->fontSizeComboBox->addItem(tr("Medium"), 1);
m_generalUi->fontSizeComboBox->addItem(tr("Large"), 2);
int fontSizeIndex = m_generalUi->fontSizeComboBox->findData(config()->get(Config::GUI_FontSizeOffset));
if (fontSizeIndex >= 0) {
m_generalUi->fontSizeComboBox->setCurrentIndex(fontSizeIndex);
} else {
// Custom value entered into config file, add it to the list and select it
m_generalUi->fontSizeComboBox->addItem(tr("Custom"), config()->get(Config::GUI_FontSizeOffset).toInt());
m_generalUi->fontSizeComboBox->setCurrentIndex(m_generalUi->fontSizeComboBox->count() - 1);
}
m_generalUi->systrayShowCheckBox->setChecked(config()->get(Config::GUI_ShowTrayIcon).toBool());
systrayToggled(m_generalUi->systrayShowCheckBox->isChecked());
m_generalUi->systrayMinimizeToTrayCheckBox->setChecked(config()->get(Config::GUI_MinimizeToTray).toBool());
@@ -382,6 +399,7 @@ void ApplicationSettingsWidget::saveSettings()
config()->set(Config::AutoReloadOnChange, m_generalUi->autoReloadOnChangeCheckBox->isChecked());
config()->set(Config::MinimizeAfterUnlock, m_generalUi->minimizeAfterUnlockCheckBox->isChecked());
config()->set(Config::MinimizeOnOpenUrl, m_generalUi->minimizeOnOpenUrlCheckBox->isChecked());
config()->set(Config::OpenURLOnDoubleClick, m_generalUi->openUrlOnDoubleClick->isChecked());
config()->set(Config::HideWindowOnCopy, m_generalUi->hideWindowOnCopyCheckBox->isChecked());
config()->set(Config::MinimizeOnCopy, m_generalUi->minimizeOnCopyRadioButton->isChecked());
config()->set(Config::DropToBackgroundOnCopy, m_generalUi->dropToBackgroundOnCopyRadioButton->isChecked());
@@ -410,6 +428,7 @@ void ApplicationSettingsWidget::saveSettings()
config()->set(Config::GUI_ColorPasswords, m_generalUi->colorPasswordsCheckBox->isChecked());
config()->set(Config::GUI_ToolButtonStyle, m_generalUi->toolButtonStyleComboBox->currentData().toString());
config()->set(Config::GUI_FontSizeOffset, m_generalUi->fontSizeComboBox->currentData().toInt());
config()->set(Config::GUI_ShowTrayIcon, m_generalUi->systrayShowCheckBox->isChecked());
config()->set(Config::GUI_TrayIconAppearance, m_generalUi->trayIconAppearance->currentData().toString());

View File

@@ -59,7 +59,7 @@
<x>0</x>
<y>0</y>
<width>568</width>
<height>1153</height>
<height>1202</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout_8">
@@ -493,6 +493,9 @@
<property name="enabled">
<bool>false</bool>
</property>
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
</property>
<item>
<property name="text">
<string>Temporary file moved into place</string>
@@ -543,6 +546,16 @@
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="openUrlOnDoubleClick">
<property name="text">
<string>Open browser on double clicking URL field in entry view</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="useGroupIconOnEntryCreationCheckBox">
<property name="text">
@@ -699,22 +712,6 @@
<property name="horizontalSpacing">
<number>10</number>
</property>
<item row="1" column="2">
<widget class="QCheckBox" name="toolbarMovableCheckBox">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Movable toolbar</string>
</property>
</widget>
</item>
<item row="0" column="3">
<spacer name="horizontalSpacer_5">
<property name="orientation">
@@ -728,66 +725,6 @@
</property>
</spacer>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="toolButtonStyleComboBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="accessibleName">
<string>Toolbar button style</string>
</property>
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="toolButtonStyleLabel">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="styleSheet">
<string notr="true">margin-right: 5px</string>
</property>
<property name="text">
<string>Toolbar button style:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>toolButtonStyleComboBox</cstring>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="languageComboBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="accessibleName">
<string>Language selection</string>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="languageLabel_2">
<property name="sizePolicy">
@@ -807,6 +744,82 @@
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QCheckBox" name="toolbarMovableCheckBox">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Movable toolbar</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="toolButtonStyleComboBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="accessibleName">
<string>Toolbar button style</string>
</property>
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QComboBox" name="languageComboBox">
<property name="sizePolicy">
<sizepolicy hsizetype="Minimum" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="accessibleName">
<string>Language selection</string>
</property>
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="toolButtonStyleLabel">
<property name="enabled">
<bool>true</bool>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Toolbar button style:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>toolButtonStyleComboBox</cstring>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QLabel" name="languageLabel_3">
<property name="text">
@@ -814,6 +827,32 @@
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="fontSizeLabel">
<property name="text">
<string>Font size:</string>
</property>
<property name="alignment">
<set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
</property>
<property name="buddy">
<cstring>fontSizeComboBox</cstring>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QComboBox" name="fontSizeComboBox">
<property name="focusPolicy">
<enum>Qt::StrongFocus</enum>
</property>
<property name="accessibleName">
<string>Font size selection</string>
</property>
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
</property>
</widget>
</item>
</layout>
</item>
<item>
@@ -902,6 +941,9 @@
<property name="accessibleName">
<string>Tray icon type</string>
</property>
<property name="sizeAdjustPolicy">
<enum>QComboBox::AdjustToContents</enum>
</property>
</widget>
</item>
<item row="0" column="1">
@@ -1351,6 +1393,7 @@
<tabstop>alternativeSaveComboBox</tabstop>
<tabstop>ConfirmMoveEntryToRecycleBinCheckBox</tabstop>
<tabstop>EnableCopyOnDoubleClickCheckBox</tabstop>
<tabstop>openUrlOnDoubleClick</tabstop>
<tabstop>useGroupIconOnEntryCreationCheckBox</tabstop>
<tabstop>minimizeOnOpenUrlCheckBox</tabstop>
<tabstop>hideWindowOnCopyCheckBox</tabstop>

View File

@@ -60,6 +60,8 @@ void Clipboard::setText(const QString& text, bool clear)
mime->setData("x-kde-passwordManagerHint", QByteArrayLiteral("secret"));
#elif defined(Q_OS_WIN)
mime->setData("ExcludeClipboardContentFromMonitorProcessing", QByteArrayLiteral("1"));
mime->setData("CanIncludeInClipboardHistory", {4, '\0'});
mime->setData("CanUploadToCloudClipboard ", {4, '\0'});
#endif
if (clipboard->supportsSelection()) {

Some files were not shown because too many files have changed in this diff Show More