Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[RFC] Implement Freedesktop.org Secret Storage spec server side API #2726

Merged
merged 2 commits into from
May 12, 2019

Conversation

Aetf
Copy link
Contributor

@Aetf Aetf commented Feb 21, 2019

This PR adds the feature requested in #1403, adding a new plugin implementing the Freedesktop.org Secret Storage spec version 0.2. Basically this exposes a new DBus service that is described in the spec which can be used by libsecret-based applications.

Type of change

  • ✅ New feature (non-breaking change which adds functionality)

Description and Context

For motivation, please see #1403 for discussion.

For a description of the plugin, please see the README file.

There are also some modifications to existing code:

  • Add a helper class to override the message box's parent window
  • EntrySearcher can now search attribute values including custom ones
  • Extract applyGroupIconTo to be a member function of Group
  • DatabaseTabWidget::newDatabase returns the created DatabaseWidget
  • DatabaseTabWidget emits databaseOpened signal when a new tab is added
  • DatabaseWidget can delete a list of entries now
  • DatabaseWidget emits databaseReplaced signal when replacing the database
  • Add Aes128 in SymmetricCipher::algorithmIvSize
  • Add necessary slots in MainWindow to show desktop notifications

Screenshots

image
image

image

image

Testing strategy

  • There are unit tests for session negotiation, but have no idea how to test other parts, as they are directly exposed on DBus.
  • In develop, I've been successfully using the python-secretstorage as a DBus client and I find its own test cases cover most of the common usage patterns and can exercise the server quite well.
  • In addition, I tried to directly use seahorse and connecting to KeePassXC. It can successfully add/change/remove entries, as well as lock/unlock databases.
  • I've been using this as the password backend for Vivaldi for a few weeks.

Checklist:

  • ✅ I have read the CONTRIBUTING document. [REQUIRED]
  • ✅ My code follows the code style of this project. [REQUIRED]
  • ✅ All new and existing tests passed. [REQUIRED]
  • ✅ I have compiled and verified my code with -DWITH_ASAN=ON. [REQUIRED]
  • ✅ My change requires a change to the documentation, and I have updated it accordingly.
  • ✅ I have added tests to cover my changes.

@Aetf
Copy link
Contributor Author

Aetf commented Mar 20, 2019

Rebased on the latest develop branch and fixed EntrySearcher unit test.

@droidmonkey
Copy link
Member

Can you please squash a majority of your interstitial commits? It is hard to see the forest from the trees at the current state.

@Aetf
Copy link
Contributor Author

Aetf commented Mar 25, 2019

Sure, will do once I get to my computer.

@Aetf Aetf force-pushed the fdo-secret-service branch 2 times, most recently from 68657de to eb75045 Compare March 26, 2019 03:15
@Aetf
Copy link
Contributor Author

Aetf commented Mar 26, 2019

Re-ordered and squashed commits. Changes to other parts of KeepassXC are isolated into individual commits before the one last big commit adding the plugin. It should be a lot easier to read now.

@droidmonkey
Copy link
Member

Oh very nice, thank you. I will review after 2.4.1 is settled.

@Aetf
Copy link
Contributor Author

Aetf commented Mar 31, 2019

Force-pushed to fix some bugs when the database was reloaded due to external file changes. A new signal databaseReplaced is added to DatabaseWidget.

@Aetf
Copy link
Contributor Author

Aetf commented Apr 13, 2019

Rebased on develop

@droidmonkey
Copy link
Member

You have an issue in our CI, are we missing a dependency?

/opt/buildagent/work/c401303cba1b4098/src/fdosecrets/FdoSecretsPlugin.cpp:58:37: error: no member named 'get' in 'QScopedPointer<FdoSecrets::Service, QScopedPointerDeleter<FdoSecrets::Service> >'
[18:54:29]	            connect(m_secretService.get(), &Service::error, this, [this](const QString& msg) {
[18:54:29]	                    ~~~~~~~~~~~~~~~ ^
[18:54:29]	/opt/buildagent/work/c401303cba1b4098/src/fdosecrets/FdoSecretsPlugin.cpp:74:28: error: no member named 'get' in 'QScopedPointer<FdoSecrets::Service, QScopedPointerDeleter<FdoSecrets::Service> >'
[18:54:29]	    return m_secretService.get();
[18:54:29]	           ~~~~~~~~~~~~~~~ ^

@Aetf
Copy link
Contributor Author

Aetf commented Apr 17, 2019

Ah, I see. QScopedPointer::get was introduced in Qt 5.11. I'm on Qt 5.12 so it's working locally... What is our lowest Qt version requirement? I need to find a way to check I'm not using any newer APIs.

@Aetf
Copy link
Contributor Author

Aetf commented Apr 18, 2019

Remove any usages of APIs later than Qt 5.3. (I used the docker image at ci/trusty, which installs Qt 5.3.2, but isn't the requirement >= 5.2?)

@Aetf
Copy link
Contributor Author

Aetf commented Apr 18, 2019

Just figured out that I can view the build log by logging in as a guest on TeamCity... I'll fix other CI test failures in a few days.

@droidmonkey
Copy link
Member

@Aetf I haven't forgot about this. I will review this week and merge if acceptable. I'll also approve your bounty win.

@Aetf
Copy link
Contributor Author

Aetf commented May 7, 2019

No problem 😄

Aetf added 2 commits May 12, 2019 10:23
* DatabaseTabWidget::newDatabase returns the created DatabaseWidget
* Emit DatabaseTabWidget::databaseOpened signal before a new tab is added
* EntrySearcher can now search attribute values including custom ones
* Add Group::applyGroupIconTo to set the group icon on the supplied entry
* Implement desktop notifications through the system tray icon
* Add DatabaseWidget::deleteEntries to delete a list of entries
* Add Aes128 in SymmetricCipher::algorithmIvSize
* Add DatabaseWidget::databaseReplaced signal
* Add a helper class to override the message box's parent (prevent bugs)
…reboot#1403)

This plugin implements the Secret Storage specification version 0.2.
While running KeePassXC, it acts as a Secret Service server, registered
on DBus, so clients like seahorse, python-secretstorage, or other
implementations can connect and access the exposed database in KeePassXC.

Squashed commits:

- Initial code
- Add SessionAdaptor and fix build
- The skeletons for all dbus objects are in place
- Implement collection creation and deletion
- Emit collectionChanged signal
- Implement app-wise settings page
- Implement error message on GUI
- Implement settings
- Fix uuid to dbus path
- Implement app level settings
- Add freedesktop logo
- Implement database settings page
- Change database settings to a treeview
- Move all settings read/write to one place
- Rename SecretServiceOptionsPage to SettingsWidgetFdoSecrets
- Fix selected group can not be saved if the user hasn't click on the item
- Show selected group per database in app settings
- Disable editing of various readonly widgets
- Remove unused warning about non exposed database
- Fix method signature on dbus adaptors
- Fix type derived from DBusObject not recognized as QDBusContext
- Resolve a few TODOs around error handling
- Remove const when passing DBus exposed objects
- Move dismiss to PromptBase
- Implement per collection locking/unlocking
- Fix const correctness on Item::setSecret
- Implement SecretService::getSecrets
- Rework the signal connections around collections.
- Remove generateId from DBusObject
- Per spec, use encoded label as DBus object path for collections
- Fix some corner cases around collection name changes
- Implement alias
- Fix wrong alias dbus path
- Implement encryption per spec
- Cleanup SessionCipher
- Implement searchItems for SecretService
- Use Tools::uuidToHex
- Implement Item attributes and delete
- Implement createItem
- Always check if the database is unlocked before perform any operation
- Add missing ReadAlias/SetAlias on service
- Reorganize and fix OpenSession always returning empty output
- Overhaul error handling
- Make sure default alias is always present
- Remove collection aliases early in doDelete
- Handles all content types, fix setProperties not working
- Fix sometimes there is an extraneous leading zero when converting from MPI
- Fix session encryption negotiation
- Do not expose recycle bin
- Protect against the methods not called from DBus
- Also emit collectionChanged signal when lock state changes
- Show notification when entry secret is requested
- Add a README file
- Actually close session when client disconnects
- Gracefully return alternative label when collection is locked
- Reorganize, rename secretservice to fdosecrets
- Fix issues reported by clazy
- Unify UI strings and fix icon
- Implement a setting to skip confirmation when deleting entries from DBus
- Remove some unused debugging log
- Simply ignore errors when DBus context is not available. QtDBus won't set QDBusContext when deliver property get/set, and there is no way to get a QDBusMessage in property getter/setter.
- Simplify GcryptMPI using std::unique_ptr and add unit test
- Format code in fdosecrets
- Move DBusReturnImpl to details namespace
- Fix crash when locking a database: don't modify exposedGroup setting in customData when database is deleted
- Make sure Collection::searchItems works, whether it's locked or not
- Fix FdoSecrets::Collection becomes empty after a database reload
- Fix crash when looping while modifying the list
Copy link
Member

@droidmonkey droidmonkey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly this is the most beautiful code I have reviewed. I squashed all the small commits on existing code, will merge once the CI finishes.

@droidmonkey
Copy link
Member

@hifi We can use the desktop notification that was added here to notify when SSH credentials are being accessed (as an option).

This notification path would also be nice for KeeShare.

@Aetf Aetf deleted the fdo-secret-service branch May 17, 2019 18:16
@Aetf
Copy link
Contributor Author

Aetf commented May 17, 2019

@phddoom, unfortunately that behavior is intended. secret-tool search --unlock works like the following:

  • Call SearchItems to get a list of locked items, and a list of unlocked items.
  • Call Unlock on the list of locked items

However, in KeepassXC, when a database is locked, it's "gone" and there is no information left for the program to be able to search inside it. So the list of locked items is empty. So nothing will happen and the database remains locked.

@elvirolo The "exposed database groups" list enumerates exposed database groups for currently opened databases (either locked or not). You can then go from there to the per-database setting page to configure the exposed group. See my previous comment.

If you have databases open but that list is empty, it's a bug and please create a new issue for that.

@droidmonkey What is the canonical place for this kind of plugin manual or "get started" like documentation? There are indeed some cases that are not so intuitive and I'd like to help document them.

Thanks

@ghost
Copy link

ghost commented May 17, 2019

Thank you for your reply. My database is indeed open and unlocked, but the list is empty.

@Aetf
Copy link
Contributor Author

Aetf commented May 17, 2019

@elvirolo Okay. So there's indeed something went wrong. A few things to try:

  • Close the app settings page and reopen it, see if the list is populated with your database.
  • Outside of the app settings page, you can go directly to the database settings page using menu: Database -> Database settings.... Then you can use the Secret Service Integration section to adjust the exposed group for the current database.

@droidmonkey
Copy link
Member

droidmonkey commented May 17, 2019

@Aetf I am going to flesh out our documentation section of the website once I get the final products from our tech writer. The best place for documentation will be (in the future) https://github.com/keepassxreboot/keepassxreboot.github.io

As for the locked database issue, you could issue a call to bring the KeePassXC window to the front (mainWindow()->raiseWindow()) when you receive an unlock request.

@Aetf
Copy link
Contributor Author

Aetf commented May 17, 2019

@droidmonkey Cool! Let me know if anything is needed from my part documenting this plugin.

@ghost
Copy link

ghost commented May 18, 2019

@Aetf Thank you for your help. Exposing a group from the database settings page did make it work for the Nextcloud client (which uses qtkeychain). However, NetworkManager doesn't seem to be able to store Wifi secret in Keepassxc. Here are the errors which are displayed in the journal when I try to configure a connection for a specific user (which is supposed to store the secret in the user's keyring):


no secrets: No agents were available for this request.
state change: need-auth -> failed (reason 'no-secrets', sys-iface-state: 'managed')

@Aetf
Copy link
Contributor Author

Aetf commented May 23, 2019

@elvirolo Could you please post a record of dbus messages while reproducing the error using the following command?

dbus-monitor "interface='org.freedesktop.Secret.Service'" "interface='org.freedesktop.Secret.Collection'" "interface='org.freedesktop.Secret.Item'" "interface='org.freedesktop.Secret.Prompt'" > dbus.log

Please run the command right after starting KeepassXC, and make sure it's running when you use NetworkManager. Then, stop monitoring by hit ctrl-c. The log will be written in file dbus.log.

Note that the recording contains secret values passing through the interface, so be sure to NOT use your actual database file.

@ghost
Copy link

ghost commented May 24, 2019

@Aetf Thank you for your reply. Here is the content of the logfile:

signal time=1558741284.373695 sender=org.freedesktop.DBus -> destination=:1.76 serial=2 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameAcquired
   string ":1.76"
signal time=1558741284.373797 sender=org.freedesktop.DBus -> destination=:1.76 serial=4 path=/org/freedesktop/DBus; interface=org.freedesktop.DBus; member=NameLost
   string ":1.76"

@Aetf
Copy link
Contributor Author

Aetf commented Jun 1, 2019

@elvirolo Sorry for the late reply, I was busy last week...

It that all the content of the log file? It means no one ever contacted KeepassXC through the API. Could you double check if the dbus service is running when KeepassXC is open and while using NetworkManager? The command is

dbus-send --session --dest=org.freedesktop.DBus --type=method_call --print-reply  /org/freedesktop/DBus org.freedesktop.DBus.ListNames | grep 'org.freedesktop.secrets'

If it prints nothing, then the service is not running.

If the dbus service is running, then the issue is with NetworkManager, which doesn't correct use the API.

@ghost
Copy link

ghost commented Jun 4, 2019

It is indeed running (the command outputs "string "org.freedesktop.secrets""). I'll contact the NetworkManager team to try and find out what's wrong. Thanks for your help.

phoerious added a commit that referenced this pull request Oct 26, 2019
Added

- Add 'Paper Backup' aka 'Export to HTML file' to the 'Database' menu [[#3277](#3277)]
- Add statistics panel with information about the database (number of entries, number of unique passwords, etc.) to the Database Settings dialog [[#2034](#2034)]
- Add offline user manual accessible via the 'Help' menu [[#3274](#3274)]
- Add support for importing 1Password OpVault files [[#2292](#2292)]
- Implement Freedesktop.org secret storage DBus protocol so that KeePassXC can be used as a vault service by libsecret [[#2726](#2726)]
- Add support for OnlyKey as an alternative to YubiKeys (requires yubikey-personalization >= 1.20.0) [[#3352](#3352)]
- Add group sorting feature [[#3282](#3282)]
- Add feature to download favicons for all entries at once [[#3169](#3169)]
- Add word case option to passphrase generator [[#3172](#3172)]
- Add support for RFC6238-compliant TOTP hashes [[#2972](#2972)]
- Add UNIX man page for main program [[#3665](#3665)]
- Add 'Monospaced font' option to the notes field [[#3321](#3321)]
- Add support for key files in auto open [[#3504](#3504)]
- Add search field for filtering entries in Auto-Type dialog [[#2955](#2955)]
- Complete usernames based on known usernames from other entries [[#3300](#3300)]
- Parse hyperlinks in the notes field of the entry preview pane [[#3596](#3596)]
- Allow abbreviation of field names in entry search [[#3440](#3440)]
- Allow setting group icons recursively [[#3273](#3273)]
- Add copy context menu for username and password in Auto-Type dialog [[#3038](#3038)]
- Drop to background after copying a password to the clipboard [[#3253](#3253)]
- Add 'Lock databases' entry to tray icon menu [[#2896](#2896)]
- Add option to minimize window after unlocking [[#3439](#3439)]
- Add option to minimize window after opening a URL [[#3302](#3302)]
- Request accessibility permissions for Auto-Type on macOS [[#3624](#3624)]
- Browser: Add initial support for multiple URLs [[#3558](#3558)]
- Browser: Add entry-specific browser integration settings [[#3444](#3444)]
- CLI: Add offline HIBP checker (requires a downloaded HIBP dump) [[#2707](#2707)]
- CLI: Add 'flatten' option to the 'ls' command [[#3276](#3276)]
- CLI: Add password generation options to `Add` and `Edit` commands [[#3275](#3275)]
- CLI: Add XML import [[#3572](#3572)]
- CLI: Add CSV export to the 'export' command [[#3278](#3278)]
- CLI: Add `-y --yubikey` option for YubiKey [[#3416](#3416)]
- CLI: Add `--dry-run` option for merging databases [[#3254](#3254)]
- CLI: Add group commands (mv, mkdir and rmdir) [[#3313](#3313)].
- CLI: Add interactive shell mode command `open` [[#3224](#3224)]

Changed

- Redesign database unlock dialog [ [#3287](#3287)]
- Rework the entry preview panel [ [#3306](#3306)]
- Move notes to General tab on Group Preview Panel [[#3336](#3336)]
- Enable entry actions when editing an entry and cleanup entry context menu  [[#3641](#3641)]
- Improve detection of external database changes  [[#2389](#2389)]
- Warn if user is trying to use a KDBX file as a key file [[#3625](#3625)]
- Add option to disable KeePassHTTP settings migrations prompt [[#3349](#3349), [#3344](#3344)]
- Re-enabled Wayland support (no Auto-Type yet) [[#3520](#3520), [#3341](#3341)]
- Add icon to 'Toggle Window' action in tray icon menu [[3244](#3244)]
- Merge custom data between databases only when necessary [[#3475](#3475)]
- Improve various file-handling related issues when picking files using the system's file dialog [[#3473](#3473)]
- Add 'New Entry' context menu when no entries are selected [[#3671](#3671)]
- Reduce default Argon2 settings from 128 MiB and one thread per CPU core to 64 MiB and two threads to account for lower-spec mobile hardware [ [#3672](#3672)]
- Browser: Remove unused 'Remember' checkbox for HTTP Basic Auth [[#3371](#3371)]
- Browser: Show database name when pairing with a new browser [[#3638](#3638)]
- Browser: Show URL in allow access dialog [[#3639](#3639)]
- CLI: The password length option `-l` for the CLI commands `Add` and `Edit` is now `-L` [[#3275](#3275)]
- CLI: The `-u` shorthand for the `--upper` password generation option has been renamed to `-U` [[#3275](#3275)]
- CLI: Rename command `extract` to `export`. [[#3277](#3277)]

Fixed

- Improve accessibility for assistive technologies [[#3409](#3409)]
- Correctly unlock all databases if `--pw-stdin` is provided [[#2916](#2916)]
- Fix password generator issues with special characters [[#3303](#3303)]
- Fix KeePassXC interrupting shutdown procedure [[#3666](#3666)]
- Fix password visibility toggle button state on unlock dialog [[#3312](#3312)]
- Fix potential data loss if database is reloaded while user is editing an entry [[#3656](#3656)]
- Fix hard-coded background color in search help popup [[#3001](#3001)]
- Fix font choice for password preview [[#3425](#3425)]
- Fix handling of read-only files when autosave is enabled [[#3408](#3408)]
- Handle symlinks correctly when atomic saves are disabled [[#3463](#3463)]
- Enable HighDPI icon scaling on Linux [[#3332](#3332)]
- Make Auto-Type on macOS more robust and remove old Carbon API calls [[#3634](#3634), [[#3347](#3347))]
- Hide Share tab if KeePassXC is compiled without KeeShare support and other minor KeeShare improvements [[#3654](#3654), [[#3291](#3291), [#3029](#3029), [#3031](#3031), [#3236](#3236)]
- Correctly bring window to the front when clicking tray icon on macOS [[#3576](#3576)]
- Correct application shortcut created by MSI Installer on Windows [[#3296](#3296)]
- Fix crash when removing custom data [[#3508](#3508)]
- Fix placeholder resolution in URLs [[#3281](#3281)]
- Fix various inconsistencies and platform-dependent compilation bugs [[#3664](#3664), [#3662](#3662), [#3660](#3660), [#3655](#3655), [#3649](#3649), [#3417](#3417), [#3357](#3357), [#3319](#3319), [#3318](#3318), [#3304](#3304)]
- Browser: Fix potential leaking of entries through the browser integration API if multiple databases are opened [[#3480](#3480)]
- Browser: Fix password entropy calculation [[#3107](#3107)]
- Browser: Fix Windows registry settings for portable installation [[#3603](#3603)]
@kocjs
Copy link

kocjs commented Apr 10, 2020

What is the right way to disable gnome-keyring-daemon?
On my Debian Buster system with GNOME this was the only way that worked so far.
chmod 000 /usr/bin/gnome-keyring-daemon

@michaelk83
Copy link

Could you please post a record of dbus messages while reproducing the error using the following command?

dbus-monitor "interface='org.freedesktop.Secret.Service'" "interface='org.freedesktop.Secret.Collection'" "interface='org.freedesktop.Secret.Item'" "interface='org.freedesktop.Secret.Prompt'" > dbus.log

...
Note that the recording contains secret values passing through the interface, so be sure to NOT use your actual database file.

If this is still the current behavior, isn't that a security issue? A malicious script could launch dbus-monitor and then access those secret values via the log with a real database (or would that require root access?). Ideally, all communications should be encrypted, so that no secrets would be exposed. If that wasn't included in the original implementation, then that should be fixed separately.

This comment by @ntninja seems relevant (last two paragraphs):

The browser protocol is better insofar as that it forces clients to provision their own HOTP key for encryption and authentication rather than regressing to a basic “if it can connect, it's trusted for everything”. See point 4 of my proposal/notes on my ideas on how this can be implemented (within certain limits) based on the sender's identity however.

I will admit however that provisioning those keys inside the bridge however is not ideal, but it's not like we can upgrade the existing SecretStorage protocol with this property.

If this needs changes to libsecret API to implement better, then we should reach out to Stef Walter, who is the maintainer of libsecret and co-author of the Secret Service API. But this should be fixed as possible with the current API.

This libsecret issue also may be relevant.

@droidmonkey
Copy link
Member

This feature has come quite a long way since this PR. Dbus traffic is encrypted using asymmetric cryptography and we have implemented a prompt window when credentials are requested.

@michaelk83
Copy link

@droidmonkey , while we're at it, what's the situation on client isolation (point 4 of @ntninja 's plan)? One of the things that was bugging me with KWallet was the lack of such isolation: any app could get the password of any other app. I understand this is a general problem with such password stores, and is difficult to solve. But anything we can do to start moving the needle towards a solution, would be welcome IMO. It may be worthwhile to involve Stef Walter on that, at least.

@droidmonkey
Copy link
Member

There is nothing stopping an app from requesting arbitrary data from the collection. This is part of the spec, and without strict AAA guarantees at the process level I'm not sure you'll ever get there. (e.g. Process A can cryptographically prove they are really Process A and has access to this specific credential set)

We did implement the prompt to allow access which tells you which process is requesting which entry. We also create a new encryption pipe for each client.

@michaelk83
Copy link

michaelk83 commented Apr 17, 2021

We did implement the prompt to allow access which tells you which process is requesting which entry. We also create a new encryption pipe for each client.

That's already a start, which is good. But how is the process identity determined here? If it's just a string passed from the client, it's very easy to impersonate. Or more commonly, a client may neglect setting a sensible string - a known issue with KWallet.

If we can determine the process ID as has been suggested, that can be converted to the binary path. Maybe that could be tapmered-with too, if there's no tamper-proof system call for that, but that would already be more reliable than a client-specified string.

Once we have the app identity, KPXC can create a separate collection for each app, and let the user control which apps can access which collections (by default, only the one created for that app). Maybe that's already done?

Using collections for this lets binaries store more than secret. That's especially useful for interpreted scripts. This way, each script can save its own secrets in the interpreter's collection. The scripts for a particular interpreter still won't be isolated from each other, but at least they'll be isolated from other interpreters and binaries.

For further security, KPXC could fingerprint the binary on first access, and use that to control subsequent access. At its simplest, that could just be a SHA hash of the binary. That would mean that new binary versions will need to re-authenticate, but as far as I'm concerned, that's a feature. As long as the user has access control, he could tell KPXC to "let this newer version use that older version's collection". And the whole fingerprinting feature can be optional.

(In the future, there might be an online database of trusted binary fingerprints for each package, maintained by the package publishers. But that's a long way off.)

This isn't a perfect solution, but I think it's better than no isolation at all, a la KWallet.
edit: Should I post a separate feature request to discuss this further?

@droidmonkey
Copy link
Member

You should read up on all the subsequent FDO Secrets PRs from this one first. A lot of work has been done and almost everything you mention above is done already. Exception being the hashing of the binary.

@michaelk83
Copy link

A lot of work has been done and almost everything you mention above is done already. Exception being the hashing of the binary.

Awesome, thanks! I have a bunch of PRs on my reading list already. Going over them.

@phoerious phoerious added pr: new feature Pull request that adds a new feature and removed new feature labels Nov 22, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
platform: Linux pr: new feature Pull request that adds a new feature
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants