diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index 133d4cf..ef4a16e 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -25,7 +25,6 @@ ## Debug info KeePassXC - {VERSION} -keepassxc-browser - {VERSION} +KeePassXC-Browser - {VERSION} Operating system: Mac/Win/Linux Browser: Chrome/Firefox/Vivaldi/Chromium -Proxy used: YES/NO diff --git a/CHANGELOG b/CHANGELOG index 7cd8353..2d1f25f 100755 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,21 @@ +1.3.0 (07-10-2018) +========================= + - Add support for translations [#37] + - Add quick-filter bar to credentials selection popup [#252] + - Disable automatic filling and submission of HTTP basic-auth fields by default [#260] + - Do not fill password fields if a manually entered username does not match retrieved credentials [#289] + - Fix handling of websites with separate pages for username and password [#291] + - Improve detection and handling of hidden input fields [#263, #293] + - Fix updating credentials from the context menu [#256] + - Improve shortcuts and add button to open shortcut settings [#304] + - Do not mix pairing IDs across multiple databases within a session [#258] + - Ignore certain elements on a page, such as SVGs [#246] + - Do not show the full pairing key on the settings page [#242] + - Default to checking for updates every 3 days [#239] + - Hide update message when updates are turned off [#285] + - Fix discard button not being shown when selection custom login fields [#307] + - Update webextension polyfill to a newer version, which fixes various errors [#296] + 1.2.0 (29-07-2018) ========================= - Replace "Ignored Sites" with new "Site Preferences" settings page [#208] diff --git a/keepassxc-browser/_locales/en/messages.json b/keepassxc-browser/_locales/en/messages.json new file mode 100644 index 0000000..75c179f --- /dev/null +++ b/keepassxc-browser/_locales/en/messages.json @@ -0,0 +1,738 @@ +{ + "extensionDescription": { + "message": "KeePassXC integration for modern web browsers", + "description": "Name of the extension." + }, + "contextMenuFillUsernameAndPassword": { + "message": "Fill Username and Password", + "description": "Context menu item for filling both username and password." + }, + "contextMenuFillPassword": { + "message": "Fill Password Only", + "description": "Context menu item for filling password." + }, + "contextMenuFillTOTP": { + "message": "Fill TOTP", + "description": "Context menu item for filling Time-based One Time Password." + }, + "contextMenuShowPasswordGeneratorIcons": { + "message": "Show Password Generator Icons", + "description": "Show password generator icon on every password input field." + }, + "contextMenuSaveCredentials": { + "message": "Save credentials", + "description": "Save credentials using the extension popup icon." + }, + "rememberCredentialsPopup": { + "message": "Create or modify the credentials by clicking on the extension icon.", + "description": "Notification text when saving credentials from the context menu." + }, + "multipleCredentialsDetected": { + "message": "HTTP authentication with multiple credentials detected. Click on the extension icon to choose the correct one.", + "description": "Notification when HTTP Authentication has multiple credentials detected." + }, + "errorMessageUnknown": { + "message": "Unknown error.", + "description": "Unknown error." + }, + "errorMessageDatabaseNotOpened": { + "message": "Database not opened.", + "description": "Database not opened." + }, + "errorMessageDatabaseHash": { + "message": "Database hash not received.", + "description": "Database hash not received." + }, + "errorMessageClientPublicKey": { + "message": "Client public key not received.", + "description": "Client public key not received." + }, + "errorMessageDecrypt": { + "message": "Cannot decrypt message.", + "description": "Cannot decrypt message." + }, + "errorMessageTimeout": { + "message": "Timeout or not connected to KeePassXC.", + "description": "Timeout or not connected to KeePassXC." + }, + "errorMessageCanceled": { + "message": "Action canceled or denied.", + "description": "Action canceled or denied." + }, + "errorMessageEncrypt": { + "message": "Cannot encrypt message or public key not found. Is native messaging or support for your browser enabled in KeePassXC?", + "description": "Cannot encrypt message or public key not found. Is native messaging or support for your browser enabled in KeePassXC?" + }, + "errorMessageAssociate": { + "message": "KeePassXC association failed, try again.", + "description": "KeePassXC association failed, try again." + }, + "errorMessageKeyExchange": { + "message": "Key exchange was not successful.", + "description": "Key exchange was not successful." + }, + "errorMessageEncryptionKey": { + "message": "Encryption key is not recognized.", + "description": "Encryption key is not recognized." + }, + "errorMessageSavedDatabases": { + "message": "No saved databases found.", + "description": "No saved databases found." + }, + "errorMessageIncorrectAction": { + "message": "Incorrect action.", + "description": "Incorrect action." + }, + "errorMessageEmptyMessage": { + "message": "Empty message received.", + "description": "Empty message received." + }, + "errorMessageNoURL": { + "message": "No URL provided.", + "description": "No URL provided." + }, + "errorMessageNoLogins": { + "message": "No logins found.", + "description": "No logins found." + }, + "passwordGeneratorPlaceholder": { + "message": "Generated password", + "description": "Input field placeholder for generated password." + }, + "passwordGeneratorLabel": { + "message": "Also fill in the next password-field", + "description": "Checkbox text below the password generator input field." + }, + "passwordGeneratorBits": { + "message": "??? Bits", + "description": "Bits of the generated password." + }, + "passwordGeneratorTitle": { + "message": "Password Generator", + "description": "Password generator dialog title." + }, + "passwordGeneratorGenerate": { + "message": "Generate", + "description": "Generate button text in password generator." + }, + "passwordGeneratorCopy": { + "message": "Copy", + "description": "Copy button text in password generator." + }, + "passwordGeneratorFillAndCopy": { + "message": "Fill and copy", + "description": "Fill and copy button text in password generator." + }, + "passwordGeneratorErrorTooLong": { + "message": "Error:\nThe generated password is longer than the allowed length!\nIt has been cut to fit the length.\n\nPlease remember the new password!", + "description": "A warning text shown in the password generator." + }, + "passwordGeneratorGenerateText": { + "message": "Generate password", + "description": "Password icon title text." + }, + "passwordGeneratorError": { + "message": "Cannot receive generated password.
Is KeePassXC running?", + "description": "Password generator error text when KeePassXC is closed." + }, + "defineDismiss": { + "message": "Dismiss", + "description": "Dismiss button text when choosing custom login fields." + }, + "defineSkip": { + "message": "Skip", + "description": "Skip button text when choosing custom login fields." + }, + "defineAgain": { + "message": "Again", + "description": "Again button text when choosing custom login fields." + }, + "defineConfirm": { + "message": "Confirm", + "description": "Confirm button text when choosing custom login fields." + }, + "defineAlreadySelected": { + "message": "Login fields for this page are already selected and will be overwritten.", + "description": "A text shown when custom credentials fields are already set for the page." + }, + "defineDiscard": { + "message": "Discard selection", + "description": "Discard selection button text when choosing custom login fields." + }, + "defineStringField": { + "message": "String field #", + "description": "Text for string field." + }, + "defineChooseUsername": { + "message": "1. Choose a username field", + "description": "Choosing a username field text when choosing custom login fields." + }, + "defineChoosePassword": { + "message": "2. Now choose a password field", + "description": "Choosing a password field text when choosing custom login fields." + }, + "defineConfirmSelection": { + "message": "3. Confirm selection", + "description": "Confirm a selection text when choosing custom login fields." + }, + "defineHelpText": { + "message": "Please confirm your selection or choose more fields as String fields.", + "description": "Confirm a selection text when choosing custom login fields which contains string fields." + }, + "username": { + "message": "Username", + "description": "General text for username." + }, + "password": { + "message": "Password", + "description": "General text for password." + }, + "credentialsNoUsername": { + "message": "- no username -", + "description": "Shown when no username is set in the credentials." + }, + "credentialsNoLoginsFound": { + "message": "Error:\nNo logins found.", + "description": "Shown when no credentials are found for the current page." + }, + "credentialsMultipleFound": { + "message": "Error:\nMore than one login was found in KeePassXC!\nPress the KeePassXC-Browser icon for more options.", + "description": "Alert message when trying to fill username and/or password when multiple credentials are found." + }, + "credentialsNoUsernameFound": { + "message": "Error:\nNo credentials for the given username found.", + "description": "Alert message when no credentials are found for the given username." + }, + "fieldsFill": { + "message": "Error:\nCannot find fields to fill in.", + "description": "Alert message when no fields are found to fill in." + }, + "fieldsNoPasswordField": { + "message": "Error:\nUnable to find a password field.", + "description": "Message shown when no password fields are found." + }, + "rememberNothingChanged": { + "message": "Error:\nCould not detect changed credentials.", + "description": "Message shown when trying to save credentials that haven't changed." + }, + "popupTitle": { + "message": "KeePassXC - Popup", + "description": "Popup window title." + }, + "popupSettingsText": { + "message": " Settings", + "description": "Popup Settings button text." + }, + "popupChooseCredentialsText": { + "message": " Choose custom login fields for this page", + "description": "Popup credential choosing button text." + }, + "popupConnectButton": { + "message": " Connect", + "description": "Popup Connect button text." + }, + "popupReconnectButton": { + "message": " Reconnect", + "description": "Popup Reconnect button text." + }, + "popupRedetectButton": { + "message": " Redetect login fields", + "description": "Popup Redetect login fields button text." + }, + "popupReloadButton": { + "message": " Reload", + "description": "Popup Reload button text." + }, + "popupReopenButton": { + "message": " Reopen database", + "description": "Popup reopen database button text." + }, + "popupErrorEncountered": { + "message": "KeePassXC-Browser has encountered an error:", + "description": "A text shown above error message in the popup." + }, + "popupUpdateAvailable": { + "message": "You use an old version of KeePassXC.", + "description": "Popup warning message about old version of KeePassXC." + }, + "popupDownloadNewVersion": { + "message": "Please download the latest version from keepassxc.org", + "description": "Popup warning message link when KeePassXC version is not up-to-date." + }, + "popupCheckingStatus": { + "message": " Checking status...", + "description": "Checking status message in popup." + }, + "popupNotConfigured": { + "message": "KeePassXC-Browser has not been configured. Press the connect button to pair with KeePassXC.", + "description": "A popup message shown when the extension has not been connected to KeePassXC." + }, + "popupNeedReconfigure": { + "message": "KeePassXC-Browser has been disconnected from KeePassXC.", + "description": "A popup message shown when the extension has been disconnected from KeePassXC." + }, + "popupNeedReconfigureMessage": { + "message": "Press the reconnect button to establish a new connection.", + "description": "A popup message shown when reconnect is needed." + }, + "popupConfiguredNotAssociated": { + "message": "KeePassXC-Browser has not yet connected to KeePassXC but has been configured using the identifier: $1", + "description": "A popup message shown when KeePassXC is not connected but configured." + }, + "popupConfiguredAndAssociated": { + "message": "KeePassXC-Browser is connected to KeePassXC and has been configured using the following identifier: $1", + "description": "A popuo message when a connection to KeePassXC is succesful." + }, + "popupRememberInfoText": { + "message": "Username or password changed! Save it?", + "description": "Popup message when username or password has changed." + }, + "popupRememberSaving": { + "message": "Credentials will be saved in connected database with identifier $1", + "description": "A popup message when saving or updating credentials." + }, + "popupRememberNewUsername": { + "message": "The following username is currently not saved: $1", + "description": "A popup message shown when a new username is detected when saving credentials." + }, + "popupRememberUsernameExists": { + "message": "The credentials with the used username are marked bold: $1", + "description": "A popup message shown when a existing username is detected when saving credentials." + }, + "popupRememberChooseCredentials": { + "message": "Please choose the credentials you want to update.", + "description": "A popup message shown choosing what credentials user wants to update." + }, + "popupLoginText": { + "message": "Select the login information you would like to get entered into the page.", + "description": "A popup message shown when one or multiple credentials are present." + }, + "popupFilterText": { + "message": "Filter:", + "description": "Text for login credentials quick filter." + }, + "popupAuthText": { + "message": "Select the login information you would like to get logged in with.", + "description": "A popup message shown when doing HTTP Authentication with multiple credentials present." + }, + "popupMultiplePasswordFields": { + "message": "More than one password field found on this page. Right-click one of the password fields, and choose one of:", + "description": "A popup message shown when web page has multiple password fields." + }, + "popupURL": { + "message": "URL: ", + "description": "URL. Might not need any translation." + }, + "popupUsername": { + "message": "Username: ", + "description": "A username field name shown on credential update." + }, + "popupButtonNew": { + "message": " New", + "description": "New button text in popup when updating credentials." + }, + "popupButtonUpdate": { + "message": " Update", + "description": "Update button text in popup when updating credentials." + }, + "popupButtonDismiss": { + "message": " Dismiss", + "description": "Dismiss button text in popup when updating credentials." + }, + "popupButtonIgnore": { + "message": "Never ask for this page", + "description": "Ignore button text in popup or in notification when updating credentials." + }, + "popupButtonClose": { + "message": "Close", + "description": "Close button text in popup or in notification." + }, + "popupButtonDismissHttpAuth": { + "message": " Dismiss and show the default authentication dialog", + "description": "Dismiss button text when in HTTP Authentication popup." + }, + "optionsTitle": { + "message": "Settings | KeePassXC-Browser", + "description": "Options page title." + }, + "optionsGeneralSettingsTab": { + "message": "General Settings", + "description": "General Settings tab text." + }, + "optionsConnectedDatabasesTab": { + "message": "Connected Databases", + "description": "Connected Databases tab text." + }, + "optionsCustomFieldsTab": { + "message": "Custom login fields", + "description": "Saved custom login fields tab text." + }, + "optionsSitePreferencesTab": { + "message": "Site preferences", + "description": "Site preferences fields tab text." + }, + "optionsAboutTab": { + "message": "About", + "description": "About tab text." + }, + "optionsMenuGeneral": { + "message": "General", + "description": "General settings page header." + }, + "optionsMenuConnectedDatabases": { + "message": "Connected Databases", + "description": "Connected Databases page header." + }, + "optionsMenuCustomFields": { + "message": "Custom login fields", + "description": "Custom login fields page header." + }, + "optionsMenuSitePreferences": { + "message": "Site preferences", + "description": "Site preferences page header." + }, + "optionsMenuAbout": { + "message": "About", + "description": "About page header." + }, + "optionsButtonConfigureShortcuts": { + "message": " Configure shortcuts", + "description": "Keyboard shortcut button text." + }, + "optionsButtonSave": { + "message": " Save", + "description": "Save button text." + }, + "optionsButtonConnect": { + "message": " Connect", + "description": "Connect button text." + }, + "optionsButtonRemove": { + "message": " Remove", + "description": "Remove button text." + }, + "optionsButtonAdd": { + "message": " Add", + "description": "Add button text." + }, + "optionsButtonUpdate": { + "message": " Check for updates", + "description": "Check for updates button text." + }, + "optionsButtonRemoveNow": { + "message": " Yes, remove now", + "description": "Confirm button text when removing database key or custom login fields." + }, + "optionsButtonCancel": { + "message": " Cancel", + "description": "Cancel button text when removing database key or custom login fields." + }, + "optionsLabelBlinkTime": { + "message": "Blink Time:", + "description": "Blink Time option text." + }, + "optionsLabelRedirectOffset": { + "message": "Redirect Offset:", + "description": "Redirect Offset option text." + }, + "optionsLabelRedirectAllowance": { + "message": "Maximum (Number of) Redirects:", + "description": "RMaximum (Number of) Redirects options text." + }, + "optionsCheckboxUsePasswordGenerator": { + "message": "Activate password generator.", + "description": "Activate password generator checkbox text." + }, + "optionsCheckboxAutoRetrieveCredentials": { + "message": "Automatically retrieve credentials.", + "description": "Automatically retrieve credentials checkbox text." + }, + "optionsCheckboxAutoFillSingleEntry": { + "message": "Automatically fill in single-credential entries.", + "description": "Automatically fill-in single credential entry checkbox text." + }, + "optionsCheckboxAutoCompleteUsernames": { + "message": "Activate autocomplete for username fields.", + "description": "Activate autocomplete for username fields checkbox text." + }, + "optionsCheckboxShowNotifications": { + "message": "Show notifications.", + "description": "Show notifications checkbox text." + }, + "optionsSaveDomainOnly": { + "message": "Save domain only.", + "description": "Save domain only checkbox text." + }, + "optionsCheckboxShowLoginNotifications": { + "message": "Show a notification when new credentials can be saved to the database.", + "description": "Show login notifications checkbox text." + }, + "optionsCheckboxAutoFillAndSend": { + "message": "Automatically fill in HTTP Basic Auth dialogs and submit them.", + "description": "Auto fill HTTP Basic Auth dialogs and send them checkbox text." + }, + "optionsRadioText": { + "message": "Check for updates of KeePassXC:", + "description": "Text above radio buttons in the settings page." + }, + "optionsRadioThreeDays": { + "message": " every 3 days", + "description": "Radio button text." + }, + "optionsRadioWeek": { + "message": " every week", + "description": "Radio button text." + }, + "optionsRadioMonth": { + "message": " every month", + "description": "Radio button text." + }, + "optionsRadioNever": { + "message": " never", + "description": "Radio button text." + }, + "optionsKeyboardShortcutsHeader": { + "message": "Keyboard shortcuts", + "description": "Keyboard shortcut header text." + }, + "optionsBlinkTimeHelpText": { + "message": "Maximum time (ms) the icon should blink after detecting new credentials", + "description": "Blink Time option help text." + }, + "optionsRedirectOffsetHelpText": { + "message": "Minimum time (ms) the icon should blink before deactivating due to page redirects.", + "description": "Redirect Offset option help text." + }, + "optionsRedirectOffsetHelpTextSecond": { + "message": "-1 to only use blink time ignoring Maximum Redirects (old behavior)", + "description": "Redirect Offset option help text, second part." + }, + "optionsRedirectAllowanceHelpText": { + "message": "How many pages should the tab cycle through after the redirect offset before deactivating the icon", + "description": "Redirect Allowance option help text." + }, + "optionsUsePasswordGeneratorHelpText": { + "message": "Adds a button to password fields for generating a new password.", + "description": "Password Generator option help text." + }, + "optionsUsePasswordGeneratorHelpTextSecond": { + "message": "Passwords are generated by KeePassXC using your password generation profile.", + "description": "Password Generator option help text, second part." + }, + "optionsAutoRetrieveCredentialsHelpText": { + "message": "KeePassXC-Browser will immediately retrieve credentials when a tab is activated.", + "description": "Auto-Retrive Credentials option help text." + }, + "optionsAutoFillSingleEntryHelpText": { + "message": "Let KeePassXC-Browser automatically fill in credentials if it receives only a single entry.", + "description": "Auto-Fill Single Entry option help text." + }, + "optionsAutoFillSingleEntryWarning": { + "message": "Warning! Using auto-fill is not safe. Use at your own risk.", + "description": "Auto-Fill Single Entry warning text." + }, + "optionsAutocompleteUsernamesHelpText": { + "message": "Show a dropdown list containing available credentials for all username fields on a page.", + "description": "Autocomplete Usernames option help text." + }, + "optionsShowNotificationsHelpText": { + "message": "Show notifications for errors and when user interaction is required.", + "description": "Show notifications option help text." + }, + "optionsSaveDomainOnlyHelpText": { + "message": "When saving new credentials, save only the domain instead of full URL.", + "description": "Save domain only option help text." + }, + "optionsAutoFillAndSendHelpText": { + "message": "If credentials are found for a page and the login-type is an HTTP Basic Auth request, KeePassXC-Browser tries to login with the first given credentials.", + "description": "Auto-Fill And Send option help text." + }, + "optionsAutoFillAndSendHelpTextSecond": { + "message": "An HTTP Basic Auth dialog looks like this:", + "description": "HTTP Basic Auth image help text." + }, + "optionsVersionInfoText": { + "message": "KeePassXC-Browser needs KeePassXC to retrieve credentials.", + "description": "Settings page version info text." + }, + "optionsVersionDownload": { + "message": "You can download the latest stable version from $1", + "description": "Settings page download link." + }, + "optionsVersionRunning": { + "message": "You are running KeePassXC version: $1", + "description": "Settings page info text of user's KeePassXC version." + }, + "optionsLatestVersion": { + "message": "Latest available version of KeePassXC: $1", + "description": "Settings page info text about the latest KeePassXC version." + }, + "optionsDefault": { + "message": "Default: $1", + "description": "Default setting text." + }, + "optionsInfinite": { + "message": "Infinite: $1", + "description": "Infinite setting text." + }, + "optionsRecommended": { + "message": "Recommended: $1", + "description": "Recommended setting text." + }, + "optionsConnectedDatabasesText": { + "message": "The following KeePassXC databases are connected to KeePassXC-Browser.", + "description": "Info text about connected databases." + }, + "optionsConnectedDatabasesNotFound": { + "message": "No connected databases found.", + "description": "Shown in the connected databases table when there is no connected databases." + }, + "optionsDatabasesRemoveIdentifier": { + "message": "Remove identifier from database list?", + "description": "Confirmation text when removing database from the list." + }, + "optionsDatabasesRemoveIdentifierConfirmFirst": { + "message": "Do you really want to remove the following identifier from the database list?", + "description": "Confirmation text when removing database from the list." + }, + "optionsDatabasesRemoveIdentifierConfirmSecond": { + "message": "You can reconnect your database at any time.", + "description": "Confirmation text when removing database from the list, second part." + }, + "optionsDatabaseIdentifier": { + "message": "Identifier", + "description": "Database list column title." + }, + "optionsDatabaseKey": { + "message": "Key", + "description": "Database list column title." + }, + "optionsDatabaseLastUsed": { + "message": "Last used", + "description": "Database list column title." + }, + "optionsDatabaseCreated": { + "message": "Created", + "description": "Database list column title." + }, + "optionsDatabaseDelete": { + "message": "Delete", + "description": "Database list column title." + }, + "optionsColumnPageURL": { + "message": "Page URL", + "description": "Site preferences list column title." + }, + "optionsColumnIgnore": { + "message": "Ignore", + "description": "Site preferences list column title." + }, + "optionsColumnUsernameOnly": { + "message": "Username-only Detection", + "description": "Site preferences list column title." + }, + "optionsColumnDelete": { + "message": "Delete", + "description": "Site preferences list column title." + }, + "optionsSelectionNothing": { + "message": "Enable all features", + "description": "Site preferences option selection." + }, + "optionsSelectionNormal": { + "message": "Disable new/modified credentials", + "description": "Site preferences option selection." + }, + "optionsSelectionFull": { + "message": "Disable all features", + "description": "Site preferences option selection." + }, + "optionsCustomFieldsNotFound": { + "message": "No saved custom login fields found.", + "description": "Shown when no saved custom credentials are saved." + }, + "optionsCustomFieldsTabHelpTextFirst": { + "message": "If KeePassXC-Browser detects the wrong login fields, you are able to specify the correct fields yourself.", + "description": "Saved custom login fields info text, first part." + }, + "optionsCustomFieldsTabHelpTextSecond": { + "message": "Go to the page and click on the KeePassXC-Browser icon, then select ", + "description": "Saved custom login fields info text, second part." + }, + "optionsCustomFieldsTabHelpTextThird": { + "message": "On this page you can manage saved custom login fields.", + "description": "Saved custom login fields info text, third part." + }, + "optionsCustomFieldsRemove": { + "message": "Remove saved custom login fields?", + "description": "Confirmation text when removing saved custom login fields." + }, + "optionsCustomFieldsConfirmation": { + "message": "Do you really want to remove the saved custom login fields on the page: $1", + "description": "Confirmation text when removing saved custom login fields." + }, + "optionsCustomFieldsConfirmationHelpText": { + "message": "KeePassXC-Browser will automatically detect the login fields next time you visit this page.", + "description": "Part of confirmation text when removing saved custom login fields." + }, + "optionsSitePreferencesNotFound": { + "message": "No ignored sites found.", + "description": "Shown when no ignored sites are saved." + }, + "optionsSitePreferencesRemove": { + "message": "Remove site?", + "description": "Confirmation text when removing ignored sites from the list." + }, + "optionsSitePreferencesTabHelpTextFirst": { + "message": "Sites on this page have special handling methods associated with them.", + "description": "Site preferences info text, first part." + }, + "optionsSitePreferencesTabHelpTextSecond": { + "message": "To ignore new/modified credentials on a specific site, add them below or click the blinking KeePassXC-Browser icon and select", + "description": "Site preferences info text, second part." + }, + "optionsSitePreferencesTabHelpTextThird": { + "message": "If a site is fully ignored (Disable all features is selected), then the plugin will do nothing when visiting that site.", + "description": "Site preferences info text, third part." + }, + "optionsSitePreferencesTabHelpTextFourth": { + "message": "Username-only detection allows KeePassXC-Browser to fill in login details on websites with separate pages for username and password.", + "description": "Site preferences info text, fourth part." + }, + "optionsSitePreferencesManualAddText": { + "message": "Add URL manually:", + "description": "Label for adding site manually on Site preferences tab." + }, + "optionsSitePreferencesConfirmation": { + "message": "Do you really want to remove the specified site from the list: $1", + "description": "Confirmation text when removing site from site preferences." + }, + "optionsSitePreferencesConfirmationHelpText": { + "message": "KeePassXC-Browser will enable all features for this site and remove username-only detection.", + "description": "Part of confirmation text when removing site preferences." + }, + "optionsAboutChrome": { + "message": "Visit KeePassXC-Browser in the Chrome Web Store.", + "description": "About page link to Chrome Web Store." + }, + "optionsAboutMozilla": { + "message": "Visit KeePassXC-Browser in the Mozilla Add-ons directory.", + "description": "About page link to AMO." + }, + "optionsAboutGitHub": { + "message": "Visit KeePassXC-Browser on GitHub.", + "description": "About page link to GitHub." + }, + "optionsAboutExtensionVersion": { + "message": "KeePassXC-Browser Version: $1", + "description": "About page text of extension version." + }, + "optionsAboutKeePassXCVersion": { + "message": "KeePassXC Version: $1", + "description": "About page text of KeePassXC version." + }, + "optionsAboutContributors": { + "message": "Contributors:", + "description": "About page text of contributors." + } +} diff --git a/keepassxc-browser/background/browserAction.js b/keepassxc-browser/background/browserAction.js index caf095f..3f1ac52 100755 --- a/keepassxc-browser/background/browserAction.js +++ b/keepassxc-browser/background/browserAction.js @@ -252,13 +252,13 @@ browserAction.setRememberPopup = function(tabId, username, password, url, userna browserAction.show(null, {'id': id}); if (page.settings.showLoginNotifications) { - const message = 'Create or modify the credentials by clicking on the extension icon.'; + const message = tr('rememberCredentialsPopup'); const buttons = [ { - 'title': 'Close' + 'title': tr('popupButtonClose') }, { - 'title': 'Never ask for this page' + 'title': tr('popupButtonIgnore') }]; browser.notifications.create({ diff --git a/keepassxc-browser/background/event.js b/keepassxc-browser/background/event.js index 244ded0..8cba08c 100755 --- a/keepassxc-browser/background/event.js +++ b/keepassxc-browser/background/event.js @@ -206,7 +206,7 @@ kpxcEvent.onCheckUpdateKeePassXC = function(callback, tab) { }; kpxcEvent.onUpdateAvailableKeePassXC = function(callback, tab) { - callback(keepass.keePassXCUpdateAvailable()); + callback(page.settings.checkUpdateKeePassXC > 0 ? keepass.keePassXCUpdateAvailable() : false); }; kpxcEvent.onRemoveCredentialsFromTabInformation = function(callback, tab) { diff --git a/keepassxc-browser/background/httpauth.js b/keepassxc-browser/background/httpauth.js index 3209507..00663a0 100755 --- a/keepassxc-browser/background/httpauth.js +++ b/keepassxc-browser/background/httpauth.js @@ -85,7 +85,7 @@ httpAuth.loginOrShowCredentials = function(logins, details, resolve, reject) { }); } else { if (page.settings.showNotifications) { - showNotification('HTTP authentication with multiple credentials detected. Click on the extension icon to choose the correct one.'); + showNotification(tr('multipleCredentialsDetected')); } kpxcEvent.onHTTPAuthPopup(null, { 'id': details.tabId }, { 'logins': logins, 'url': details.searchUrl, 'resolve': resolve }); } diff --git a/keepassxc-browser/background/init.js b/keepassxc-browser/background/init.js index 0e015ce..7f34359 100644 --- a/keepassxc-browser/background/init.js +++ b/keepassxc-browser/background/init.js @@ -84,11 +84,11 @@ browser.tabs.onUpdated.addListener((tabId, changeInfo, tab) => { browser.runtime.onMessage.addListener(kpxcEvent.onMessage); const contextMenuItems = [ - {title: 'Fill User + Pass', action: 'fill_user_pass'}, - {title: 'Fill Pass Only', action: 'fill_pass_only'}, - {title: 'Fill TOTP', action: 'fill_totp'}, - {title: 'Show Password Generator Icons', action: 'activate_password_generator'}, - {title: 'Save credentials', action: 'remember_credentials'} + {title: tr('contextMenuFillUsernameAndPassword'), action: 'fill_user_pass'}, + {title: tr('contextMenuFillPassword'), action: 'fill_pass_only'}, + {title: tr('contextMenuFillTOTP'), action: 'fill_totp'}, + {title: tr('contextMenuShowPasswordGeneratorIcons'), action: 'activate_password_generator'}, + {title: tr('contextMenuSaveCredentials'), action: 'remember_credentials'} ]; let menuContexts = ['editable']; diff --git a/keepassxc-browser/background/keepass.js b/keepassxc-browser/background/keepass.js index 698bb36..56afbd6 100755 --- a/keepassxc-browser/background/keepass.js +++ b/keepassxc-browser/background/keepass.js @@ -56,22 +56,22 @@ const kpErrors = { NO_LOGINS_FOUND: 15, errorMessages : { - 0: { msg: 'Unknown error' }, - 1: { msg: 'Database not opened' }, - 2: { msg: 'Database hash not received' }, - 3: { msg: 'Client public key not reveiced' }, - 4: { msg: 'Cannot decrypt message' }, - 5: { msg: 'Timeout or not connected to KeePassXC' }, - 6: { msg: 'Action cancelled or denied' }, - 7: { msg: 'Cannot encrypt message or public key not found. Is native messaging or support for your browser enabled in KeePassXC?' }, - 8: { msg: 'KeePassXC association failed, try again.' }, - 9: { msg: 'Key change was not successful.' }, - 10: { msg: 'Encryption key is not recognized' }, - 11: { msg: 'No saved databases found.' }, - 12: { msg: 'Incorrect action.' }, - 13: { msg: 'Empty message received.' }, - 14: { msg: 'No URL provided.' }, - 15: { msg: 'No logins found.' } + 0: { msg: tr('errorMessageUnknown') }, + 1: { msg: tr('errorMessageDatabaseNotOpened') }, + 2: { msg: tr('errorMessageDatabaseHash') }, + 3: { msg: tr('errorMessageClientPublicKey') }, + 4: { msg: tr('errorMessageDecrypt') }, + 5: { msg: tr('errorMessageTimeout') }, + 6: { msg: tr('errorMessageCanceled') }, + 7: { msg: tr('errorMessageEncrypt') }, + 8: { msg: tr('errorMessageAssociate') }, + 9: { msg: tr('errorMessageKeyExchange') }, + 10: { msg: tr('errorMessageEncryptionKey') }, + 11: { msg: tr('errorMessageSavedDatabases') }, + 12: { msg: tr('errorMessageIncorrectAction') }, + 13: { msg: tr('errorMessageEmptyMessage') }, + 14: { msg: tr('errorMessageNoURL') }, + 15: { msg: tr('errorMessageNoLogins') } }, getError(errorCode) { @@ -371,10 +371,13 @@ keepass.associate = function(callback, tab) { const key = nacl.util.encodeBase64(keepass.keyPair.publicKey); const nonce = keepass.getNonce(); const incrementedNonce = keepass.incrementedNonce(nonce); + const idKeyPair = nacl.box.keyPair(); + const idKey = nacl.util.encodeBase64(idKeyPair.publicKey); const messageData = { action: kpAction, - key: key + key: key, + idKey: idKey }; const request = { @@ -402,7 +405,7 @@ keepass.associate = function(callback, tab) { keepass.handleError(tab, kpErrors.ASSOCIATION_FAILED); } else { - keepass.setCryptoKey(id, key); // Save the current public key as id key for the database + keepass.setCryptoKey(id, idKey); // Save the new identification public key as id key for the database keepass.associated.value = true; keepass.associated.hash = parsed.hash || 0; } diff --git a/keepassxc-browser/background/page.js b/keepassxc-browser/background/page.js index f56ebac..87da6b3 100755 --- a/keepassxc-browser/background/page.js +++ b/keepassxc-browser/background/page.js @@ -3,7 +3,7 @@ const defaultSettings = { checkUpdateKeePassXC: 3, autoCompleteUsernames: true, - autoFillAndSend: true, + autoFillAndSend: false, usePasswordGenerator: true, autoFillSingleEntry: false, autoRetrieveCredentials: true, diff --git a/keepassxc-browser/browser-polyfill.min.js b/keepassxc-browser/browser-polyfill.min.js index 47125a6..c756281 100644 --- a/keepassxc-browser/browser-polyfill.min.js +++ b/keepassxc-browser/browser-polyfill.min.js @@ -1,8 +1,8 @@ -"use strict";if("undefined"==typeof browser){this.browser=(()=>{class c extends WeakMap{constructor(m,n=void 0){super(n),this.createItem=m}get(m){return this.has(m)||this.set(m,this.createItem(m)),super.get(m)}}const d=m=>{return m&&"object"==typeof m&&"function"==typeof m.then},e=m=>{return(...n)=>{chrome.runtime.lastError?m.reject(chrome.runtime.lastError):1===n.length?m.resolve(n[0]):m.resolve(n)}},f=(m,n)=>{const o=p=>1==p?"argument":"arguments";return function(q,...r){if(r.lengthn.maxArgs)throw new Error(`Expected at most ${n.maxArgs} ${o(n.maxArgs)} for ${m}(), got ${r.length}`);return new Promise((s,t)=>{q[m](...r,e({resolve:s,reject:t}))})}},g=(m,n,o)=>{return new Proxy(n,{apply(p,q,r){return o.call(q,m,...r)}})};let h=Function.call.bind(Object.prototype.hasOwnProperty);const i=(m,n={},o={})=>{let p=Object.create(null),q={has(r,s){return s in r||s in p},get(r,s){if(s in p)return p[s];if(s in r){let u=r[s];if("function"==typeof u){if("function"==typeof n[s])u=g(r,r[s],n[s]);else if(h(o,s)){let v=f(s,o[s]);u=g(r,r[s],v)}else u=u.bind(r);}else if("object"==typeof u&&null!==u&&(h(n,s)||h(o,s)))u=i(u,n[s],o[s]);else return Object.defineProperty(p,s,{configurable:!0,enumerable:!0,get(){return r[s]},set(v){r[s]=v}}),u;return p[s]=u,u}},set(r,s,t){return s in p?p[s]=t:r[s]=t,!0},defineProperty(r,s,t){return Reflect.defineProperty(p,s,t)},deleteProperty(r,s){return Reflect.deleteProperty(p,s)}};return new Proxy(m,q)},k=new c(m=>{return"function"==typeof m?function(o,p,q){let r=!1,s=m(o,p,function(t){r=!0,q(t)});return r||!0===s?s:d(s)?(s.then(q,t=>{console.error(t),q(t)}),!0):void(void 0!==s&&q(s))}:m}),l={runtime:{onMessage:(m=>({addListener(n,o,...p){n.addListener(m.get(o),...p)},hasListener(n,o){return n.hasListener(m.get(o))},removeListener(n,o){n.removeListener(m.get(o))}}))(k)}};return i(chrome,l,{alarms:{clear:{minArgs:0,maxArgs:1},clearAll:{minArgs:0,maxArgs:0},get:{minArgs:0,maxArgs:1},getAll:{minArgs:0,maxArgs:0}},bookmarks:{create:{minArgs:1,maxArgs:1},"export":{minArgs:0,maxArgs:0},get:{minArgs:1,maxArgs:1},getChildren:{minArgs:1,maxArgs:1},getRecent:{minArgs:1,maxArgs:1},getTree:{minArgs:0,maxArgs:0},getSubTree:{minArgs:1,maxArgs:1},"import":{minArgs:0,maxArgs:0},move:{minArgs:2,maxArgs:2},remove:{minArgs:1,maxArgs:1},removeTree:{minArgs:1,maxArgs:1},search:{minArgs:1,maxArgs:1},update:{minArgs:2,maxArgs:2}},browserAction:{getBadgeBackgroundColor:{minArgs:1,maxArgs:1},getBadgeText:{minArgs:1,maxArgs:1},getPopup:{minArgs:1,maxArgs:1},getTitle:{minArgs:1,maxArgs:1},setIcon:{minArgs:1,maxArgs:1}},commands:{getAll:{minArgs:0,maxArgs:0}},contextMenus:{update:{minArgs:2,maxArgs:2},remove:{minArgs:1,maxArgs:1},removeAll:{minArgs:0,maxArgs:0}},cookies:{get:{minArgs:1,maxArgs:1},getAll:{minArgs:1,maxArgs:1},getAllCookieStores:{minArgs:0,maxArgs:0},remove:{minArgs:1,maxArgs:1},set:{minArgs:1,maxArgs:1}},downloads:{download:{minArgs:1,maxArgs:1},cancel:{minArgs:1,maxArgs:1},erase:{minArgs:1,maxArgs:1},getFileIcon:{minArgs:1,maxArgs:2},open:{minArgs:1,maxArgs:1},pause:{minArgs:1,maxArgs:1},removeFile:{minArgs:1,maxArgs:1},resume:{minArgs:1,maxArgs:1},search:{minArgs:1,maxArgs:1},show:{minArgs:1,maxArgs:1}},extension:{isAllowedFileSchemeAccess:{minArgs:0,maxArgs:0},isAllowedIncognitoAccess:{minArgs:0,maxArgs:0}},history:{addUrl:{minArgs:1,maxArgs:1},getVisits:{minArgs:1,maxArgs:1},deleteAll:{minArgs:0,maxArgs:0},deleteRange:{minArgs:1,maxArgs:1},deleteUrl:{minArgs:1,maxArgs:1},search:{minArgs:1,maxArgs:1}},i18n:{detectLanguage:{minArgs:1,maxArgs:1},getAcceptLanguages:{minArgs:0,maxArgs:0}},idle:{queryState:{minArgs:1,maxArgs:1}},management:{get:{minArgs:1,maxArgs:1},getAll:{minArgs:0,maxArgs:0},getSelf:{minArgs:0,maxArgs:0},uninstallSelf:{minArgs:0,maxArgs:1}},notifications:{clear:{minArgs:1,maxArgs:1},create:{minArgs:1,maxArgs:2},getAll:{minArgs:0,maxArgs:0},getPermissionLevel:{minArgs:0,maxArgs:0},update:{minArgs:2,maxArgs:2}},pageAction:{getPopup:{minArgs:1,maxArgs:1},getTitle:{minArgs:1,maxArgs:1},hide:{minArgs:0,maxArgs:0},setIcon:{minArgs:1,maxArgs:1},show:{minArgs:0,maxArgs:0}},runtime:{getBackgroundPage:{minArgs:0,maxArgs:0},getBrowserInfo:{minArgs:0,maxArgs:0},getPlatformInfo:{minArgs:0,maxArgs:0},openOptionsPage:{minArgs:0,maxArgs:0},requestUpdateCheck:{minArgs:0,maxArgs:0},sendMessage:{minArgs:1,maxArgs:3},sendNativeMessage:{minArgs:2,maxArgs:2},setUninstallURL:{minArgs:1,maxArgs:1}},storage:{local:{clear:{minArgs:0,maxArgs:0},get:{minArgs:0,maxArgs:1},getBytesInUse:{minArgs:0,maxArgs:1},remove:{minArgs:1,maxArgs:1},set:{minArgs:1,maxArgs:1}},managed:{get:{minArgs:0,maxArgs:1},getBytesInUse:{minArgs:0,maxArgs:1}},sync:{clear:{minArgs:0,maxArgs:0},get:{minArgs:0,maxArgs:1},getBytesInUse:{minArgs:0,maxArgs:1},remove:{minArgs:1,maxArgs:1},set:{minArgs:1,maxArgs:1}}},tabs:{create:{minArgs:1,maxArgs:1},captureVisibleTab:{minArgs:0,maxArgs:2},detectLanguage:{minArgs:0,maxArgs:1},duplicate:{minArgs:1,maxArgs:1},executeScript:{minArgs:1,maxArgs:2},get:{minArgs:1,maxArgs:1},getCurrent:{minArgs:0,maxArgs:0},getZoom:{minArgs:0,maxArgs:1},getZoomSettings:{minArgs:0,maxArgs:1},highlight:{minArgs:1,maxArgs:1},insertCSS:{minArgs:1,maxArgs:2},move:{minArgs:2,maxArgs:2},reload:{minArgs:0,maxArgs:2},remove:{minArgs:1,maxArgs:1},query:{minArgs:1,maxArgs:1},removeCSS:{minArgs:1,maxArgs:2},sendMessage:{minArgs:2,maxArgs:3},setZoom:{minArgs:1,maxArgs:2},setZoomSettings:{minArgs:1,maxArgs:2},update:{minArgs:1,maxArgs:2}},webNavigation:{getAllFrames:{minArgs:1,maxArgs:1},getFrame:{minArgs:1,maxArgs:1}},webRequest:{handlerBehaviorChanged:{minArgs:0,maxArgs:0}},windows:{create:{minArgs:0,maxArgs:1},get:{minArgs:1,maxArgs:2},getAll:{minArgs:0,maxArgs:1},getCurrent:{minArgs:0,maxArgs:1},getLastFocused:{minArgs:0,maxArgs:1},remove:{minArgs:1,maxArgs:1},update:{minArgs:2,maxArgs:2}}})})()} +(function(a,b){if("function"==typeof define&&define.amd)define("webextension-polyfill",["module"],b);else if("undefined"!=typeof exports)b(module);else{var c={exports:{}};b(c),a.browser=c.exports}})(this,function(a){"use strict";if("undefined"==typeof browser||Object.getPrototypeOf(browser)!==Object.prototype){a.exports=(e=>{const f={alarms:{clear:{minArgs:0,maxArgs:1},clearAll:{minArgs:0,maxArgs:0},get:{minArgs:0,maxArgs:1},getAll:{minArgs:0,maxArgs:0}},bookmarks:{create:{minArgs:1,maxArgs:1},get:{minArgs:1,maxArgs:1},getChildren:{minArgs:1,maxArgs:1},getRecent:{minArgs:1,maxArgs:1},getSubTree:{minArgs:1,maxArgs:1},getTree:{minArgs:0,maxArgs:0},move:{minArgs:2,maxArgs:2},remove:{minArgs:1,maxArgs:1},removeTree:{minArgs:1,maxArgs:1},search:{minArgs:1,maxArgs:1},update:{minArgs:2,maxArgs:2}},browserAction:{disable:{minArgs:0,maxArgs:1,fallbackToNoCallback:!0},enable:{minArgs:0,maxArgs:1,fallbackToNoCallback:!0},getBadgeBackgroundColor:{minArgs:1,maxArgs:1},getBadgeText:{minArgs:1,maxArgs:1},getPopup:{minArgs:1,maxArgs:1},getTitle:{minArgs:1,maxArgs:1},openPopup:{minArgs:0,maxArgs:0},setBadgeBackgroundColor:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setBadgeText:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setIcon:{minArgs:1,maxArgs:1},setPopup:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setTitle:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0}},browsingData:{remove:{minArgs:2,maxArgs:2},removeCache:{minArgs:1,maxArgs:1},removeCookies:{minArgs:1,maxArgs:1},removeDownloads:{minArgs:1,maxArgs:1},removeFormData:{minArgs:1,maxArgs:1},removeHistory:{minArgs:1,maxArgs:1},removeLocalStorage:{minArgs:1,maxArgs:1},removePasswords:{minArgs:1,maxArgs:1},removePluginData:{minArgs:1,maxArgs:1},settings:{minArgs:0,maxArgs:0}},commands:{getAll:{minArgs:0,maxArgs:0}},contextMenus:{remove:{minArgs:1,maxArgs:1},removeAll:{minArgs:0,maxArgs:0},update:{minArgs:2,maxArgs:2}},cookies:{get:{minArgs:1,maxArgs:1},getAll:{minArgs:1,maxArgs:1},getAllCookieStores:{minArgs:0,maxArgs:0},remove:{minArgs:1,maxArgs:1},set:{minArgs:1,maxArgs:1}},devtools:{inspectedWindow:{eval:{minArgs:1,maxArgs:2}},panels:{create:{minArgs:3,maxArgs:3,singleCallbackArg:!0}}},downloads:{cancel:{minArgs:1,maxArgs:1},download:{minArgs:1,maxArgs:1},erase:{minArgs:1,maxArgs:1},getFileIcon:{minArgs:1,maxArgs:2},open:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},pause:{minArgs:1,maxArgs:1},removeFile:{minArgs:1,maxArgs:1},resume:{minArgs:1,maxArgs:1},search:{minArgs:1,maxArgs:1},show:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0}},extension:{isAllowedFileSchemeAccess:{minArgs:0,maxArgs:0},isAllowedIncognitoAccess:{minArgs:0,maxArgs:0}},history:{addUrl:{minArgs:1,maxArgs:1},deleteAll:{minArgs:0,maxArgs:0},deleteRange:{minArgs:1,maxArgs:1},deleteUrl:{minArgs:1,maxArgs:1},getVisits:{minArgs:1,maxArgs:1},search:{minArgs:1,maxArgs:1}},i18n:{detectLanguage:{minArgs:1,maxArgs:1},getAcceptLanguages:{minArgs:0,maxArgs:0}},identity:{launchWebAuthFlow:{minArgs:1,maxArgs:1}},idle:{queryState:{minArgs:1,maxArgs:1}},management:{get:{minArgs:1,maxArgs:1},getAll:{minArgs:0,maxArgs:0},getSelf:{minArgs:0,maxArgs:0},setEnabled:{minArgs:2,maxArgs:2},uninstallSelf:{minArgs:0,maxArgs:1}},notifications:{clear:{minArgs:1,maxArgs:1},create:{minArgs:1,maxArgs:2},getAll:{minArgs:0,maxArgs:0},getPermissionLevel:{minArgs:0,maxArgs:0},update:{minArgs:2,maxArgs:2}},pageAction:{getPopup:{minArgs:1,maxArgs:1},getTitle:{minArgs:1,maxArgs:1},hide:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setIcon:{minArgs:1,maxArgs:1},setPopup:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},setTitle:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0},show:{minArgs:1,maxArgs:1,fallbackToNoCallback:!0}},permissions:{contains:{minArgs:1,maxArgs:1},getAll:{minArgs:0,maxArgs:0},remove:{minArgs:1,maxArgs:1},request:{minArgs:1,maxArgs:1}},runtime:{getBackgroundPage:{minArgs:0,maxArgs:0},getBrowserInfo:{minArgs:0,maxArgs:0},getPlatformInfo:{minArgs:0,maxArgs:0},openOptionsPage:{minArgs:0,maxArgs:0},requestUpdateCheck:{minArgs:0,maxArgs:0},sendMessage:{minArgs:1,maxArgs:3},sendNativeMessage:{minArgs:2,maxArgs:2},setUninstallURL:{minArgs:1,maxArgs:1}},sessions:{getDevices:{minArgs:0,maxArgs:1},getRecentlyClosed:{minArgs:0,maxArgs:1},restore:{minArgs:0,maxArgs:1}},storage:{local:{clear:{minArgs:0,maxArgs:0},get:{minArgs:0,maxArgs:1},getBytesInUse:{minArgs:0,maxArgs:1},remove:{minArgs:1,maxArgs:1},set:{minArgs:1,maxArgs:1}},managed:{get:{minArgs:0,maxArgs:1},getBytesInUse:{minArgs:0,maxArgs:1}},sync:{clear:{minArgs:0,maxArgs:0},get:{minArgs:0,maxArgs:1},getBytesInUse:{minArgs:0,maxArgs:1},remove:{minArgs:1,maxArgs:1},set:{minArgs:1,maxArgs:1}}},tabs:{captureVisibleTab:{minArgs:0,maxArgs:2},create:{minArgs:1,maxArgs:1},detectLanguage:{minArgs:0,maxArgs:1},discard:{minArgs:0,maxArgs:1},duplicate:{minArgs:1,maxArgs:1},executeScript:{minArgs:1,maxArgs:2},get:{minArgs:1,maxArgs:1},getCurrent:{minArgs:0,maxArgs:0},getZoom:{minArgs:0,maxArgs:1},getZoomSettings:{minArgs:0,maxArgs:1},highlight:{minArgs:1,maxArgs:1},insertCSS:{minArgs:1,maxArgs:2},move:{minArgs:2,maxArgs:2},query:{minArgs:1,maxArgs:1},reload:{minArgs:0,maxArgs:2},remove:{minArgs:1,maxArgs:1},removeCSS:{minArgs:1,maxArgs:2},sendMessage:{minArgs:2,maxArgs:3},setZoom:{minArgs:1,maxArgs:2},setZoomSettings:{minArgs:1,maxArgs:2},update:{minArgs:1,maxArgs:2}},topSites:{get:{minArgs:0,maxArgs:0}},webNavigation:{getAllFrames:{minArgs:1,maxArgs:1},getFrame:{minArgs:1,maxArgs:1}},webRequest:{handlerBehaviorChanged:{minArgs:0,maxArgs:0}},windows:{create:{minArgs:0,maxArgs:1},get:{minArgs:1,maxArgs:2},getAll:{minArgs:0,maxArgs:1},getCurrent:{minArgs:0,maxArgs:1},getLastFocused:{minArgs:0,maxArgs:1},remove:{minArgs:1,maxArgs:1},update:{minArgs:2,maxArgs:2}}};if(0===Object.keys(f).length)throw new Error("api-metadata.json has not been included in browser-polyfill");class g extends WeakMap{constructor(v,w=void 0){super(w),this.createItem=v}get(v){return this.has(v)||this.set(v,this.createItem(v)),super.get(v)}}const h=v=>{return v&&"object"==typeof v&&"function"==typeof v.then},i=(v,w)=>{return(...x)=>{e.runtime.lastError?v.reject(e.runtime.lastError):w.singleCallbackArg||1>=x.length?v.resolve(x[0]):v.resolve(x)}},j=v=>1==v?"argument":"arguments",k=(v,w)=>{return function(y,...z){if(z.lengthw.maxArgs)throw new Error(`Expected at most ${w.maxArgs} ${j(w.maxArgs)} for ${v}(), got ${z.length}`);return new Promise((A,B)=>{if(w.fallbackToNoCallback)try{y[v](...z,i({resolve:A,reject:B},w))}catch(C){console.warn(`${v} API method doesn't seem to support the callback parameter, `+"falling back to call it without a callback: ",C),y[v](...z),w.fallbackToNoCallback=!1,w.noCallback=!0,A()}else w.noCallback?(y[v](...z),A()):y[v](...z,i({resolve:A,reject:B},w))})}},l=(v,w,x)=>{return new Proxy(w,{apply(y,z,A){return x.call(z,v,...A)}})};let m=Function.call.bind(Object.prototype.hasOwnProperty);const n=(v,w={},x={})=>{let y=Object.create(null),z={has(B,C){return C in v||C in y},get(B,C){if(C in y)return y[C];if(C in v){let E=v[C];if("function"==typeof E){if("function"==typeof w[C])E=l(v,v[C],w[C]);else if(m(x,C)){let F=k(C,x[C]);E=l(v,v[C],F)}else E=E.bind(v);}else if("object"==typeof E&&null!==E&&(m(w,C)||m(x,C)))E=n(E,w[C],x[C]);else return Object.defineProperty(y,C,{configurable:!0,enumerable:!0,get(){return v[C]},set(F){v[C]=F}}),E;return y[C]=E,E}},set(B,C,D){return C in y?y[C]=D:v[C]=D,!0},defineProperty(B,C,D){return Reflect.defineProperty(y,C,D)},deleteProperty(B,C){return Reflect.deleteProperty(y,C)}},A=Object.create(v);return new Proxy(A,z)},o=v=>({addListener(w,x,...y){w.addListener(v.get(x),...y)},hasListener(w,x){return w.hasListener(v.get(x))},removeListener(w,x){w.removeListener(v.get(x))}});let p=!1;const q=new g(v=>{return"function"==typeof v?function(x,y,z){let B,D,A=!1,C=new Promise(G=>{B=function(H){p||(console.warn("Returning a Promise is the preferred way to send a reply from an onMessage/onMessageExternal listener, as the sendResponse will be removed from the specs (See https://developer.mozilla.org/docs/Mozilla/Add-ons/WebExtensions/API/runtime/onMessage)",new Error().stack),p=!0),A=!0,G(H)}});try{D=v(x,y,B)}catch(G){D=Promise.reject(G)}const E=!0!==D&&h(D);if(!0!==D&&!E&&!A)return!1;const F=G=>{G.then(H=>{z(H)},H=>{let I;I=H&&(H instanceof Error||"string"==typeof H.message)?H.message:"An unexpected error occurred",z({__mozWebExtensionPolyfillReject__:!0,message:I})}).catch(H=>{console.error("Failed to send onMessage rejected reply",H)})};return E?F(D):F(C),!0}:v}),r=({reject:v,resolve:w},x)=>{e.runtime.lastError?e.runtime.lastError.message==="The message port closed before a response was received."?w():v(e.runtime.lastError):x&&x.__mozWebExtensionPolyfillReject__?v(new Error(x.message)):w(x)},s=(v,w,x,...y)=>{if(y.lengthw.maxArgs)throw new Error(`Expected at most ${w.maxArgs} ${j(w.maxArgs)} for ${v}(), got ${y.length}`);return new Promise((z,A)=>{const B=r.bind(null,{resolve:z,reject:A});y.push(B),x.sendMessage(...y)})},t={runtime:{onMessage:o(q),onMessageExternal:o(q),sendMessage:s.bind(null,"sendMessage",{minArgs:1,maxArgs:3})},tabs:{sendMessage:s.bind(null,"sendMessage",{minArgs:2,maxArgs:3})}},u={clear:{minArgs:1,maxArgs:1},get:{minArgs:1,maxArgs:1},set:{minArgs:1,maxArgs:1}};return f.privacy={network:{networkPredictionEnabled:u,webRTCIPHandlingPolicy:u},services:{passwordSavingEnabled:u},websites:{hyperlinkAuditingEnabled:u,referrersEnabled:u}},n(e,t,f)})(chrome)}else a.exports=browser}); //# sourceMappingURL=browser-polyfill.min.js.map -// webextension-polyfill v.0.1.0 (https://github.com/mozilla/webextension-polyfill) +// webextension-polyfill v.0.3.1 (https://github.com/mozilla/webextension-polyfill) /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this diff --git a/keepassxc-browser/global.js b/keepassxc-browser/global.js index 23cbd06..0e8b264 100755 --- a/keepassxc-browser/global.js +++ b/keepassxc-browser/global.js @@ -90,4 +90,14 @@ var matchPatternToRegExp = function(pattern) { var siteMatch = function(site, url) { const rx = matchPatternToRegExp(site); return url.match(rx); -}; \ No newline at end of file +}; + +// Checks if URL has only scheme and host without the last / char. +var slashNeededForUrl = function(pattern) { + const matchPattern = new RegExp(`^${schemeSegment}://${hostSegment}$`); + return matchPattern.exec(pattern); +}; + +function tr(key, params) { + return browser.i18n.getMessage(key, params); +}; diff --git a/keepassxc-browser/keepassxc-browser.js b/keepassxc-browser/keepassxc-browser.js index 3d90c51..ac6a313 100755 --- a/keepassxc-browser/keepassxc-browser.js +++ b/keepassxc-browser/keepassxc-browser.js @@ -158,7 +158,12 @@ cipAutocomplete.onBlur = function() { else { const fieldId = cipFields.prepareId(jQuery(this).attr('data-cip-id')); const fields = cipFields.getCombination('username', fieldId); - if (_f(fields.password) && _f(fields.password).data('unchanged') !== true && jQuery(this).val() !== '' && _detectedFields > 1) { + const fieldValue = jQuery(this).val(); + + // Check if the manually inserted value is one of the retrieved credentials + let fieldFound = cipAutocomplete.elements.some(e => e.value === fieldValue); + + if (_f(fields.password) && _f(fields.password).data('unchanged') !== true && fieldFound && _detectedFields > 1) { cip.fillInCredentials(fields, true, true); } } @@ -232,7 +237,7 @@ cipPassword.createDialog = function() { .attr('id', 'cip-genpw-textfield-password') .attr('type', 'text') .attr('aria-describedby', 'cip-genpw-quality') - .attr('placeholder', 'Generated password') + .attr('placeholder', tr('passwordGeneratorPlaceholder')) .addClass('genpw-text ui-widget-content ui-corner-all') .on('change keypress paste textInput input', function() { jQuery('#cip-genpw-btn-clipboard:first').removeClass('btn-success'); @@ -241,7 +246,7 @@ cipPassword.createDialog = function() { .addClass('genpw-input-group-addon') .addClass('b2c-add-on') .attr('id', 'cip-genpw-quality') - .text('??? Bits'); + .text(tr('passwordGeneratorBits')); $inputGroup.append($textfieldPassword).append($quality); const $checkGroup = jQuery('
').addClass('genpw-input-group'); @@ -252,7 +257,7 @@ cipPassword.createDialog = function() { const $labelNextField = jQuery('
+ diff --git a/keepassxc-browser/popups/popup_login.html b/keepassxc-browser/popups/popup_login.html index 78f8d7c..768aff0 100644 --- a/keepassxc-browser/popups/popup_login.html +++ b/keepassxc-browser/popups/popup_login.html @@ -14,23 +14,26 @@
- - + +
- You use an old version of KeePassXC. +
- Please download the latest version from keepassxc.org. + .
-

- Select the login information you would like to get entered into the page: -

+

+
+ diff --git a/keepassxc-browser/popups/popup_login.js b/keepassxc-browser/popups/popup_login.js index 1c81866..37d9113 100644 --- a/keepassxc-browser/popups/popup_login.js +++ b/keepassxc-browser/popups/popup_login.js @@ -25,6 +25,23 @@ $(function() { }); ll.appendChild(a); } + + if (logins.length > 1) { + document.getElementById('filter-block').style = ''; + let filter = document.getElementById('login-filter'); + filter.addEventListener('keyup', (e) => { + let val = filter.value; + let re = new RegExp(val, 'i'); + let links = ll.getElementsByTagName('a'); + for (let i in links) { + if (links.hasOwnProperty(i)) { + let found = String(links[i].textContent).match(re) !== null; + links[i].style = found ? '' : 'display: none;'; + } + } + }); + filter.focus(); + } }); }); diff --git a/keepassxc-browser/popups/popup_multiple-fields.html b/keepassxc-browser/popups/popup_multiple-fields.html index 6678540..b77c611 100644 --- a/keepassxc-browser/popups/popup_multiple-fields.html +++ b/keepassxc-browser/popups/popup_multiple-fields.html @@ -1,36 +1,36 @@ - KeePassXC-Browser - Popup - - - - - - - - + KeePassXC-Browser - Popup + + + + + + + + -
-
- - - +
+
+ + + -
- You use an old version of KeePassXC. -
- Please download the latest version from keepassxc.org. -
-
+
+ +
+ . +
+
-
-

- KeePassXC-Browser found more than one password field on this page. To enter your - logins, right-click on one of the password fields, and choose either the - "Fill User + Pass" or "Fill Pass Only" command. -

-
-
+
+

+ + "", "" +

+
+
+ diff --git a/keepassxc-browser/popups/popup_remember.html b/keepassxc-browser/popups/popup_remember.html index 6d367b4..d72e836 100644 --- a/keepassxc-browser/popups/popup_remember.html +++ b/keepassxc-browser/popups/popup_remember.html @@ -17,36 +17,36 @@ .credentials .username-exists {display: none;} .small { font-weight: bold; } .small .normal { font-weight: normal; } + .info { font-weight: normal; }

- Username or password changed! Save it? +
- Url: +
- Username: +

- - - - + + + +

-

Credentials will be saved in connected database with identifier .

+

-

The used username is currently not saved!

-

The credentials with the used username are marked bold.

-

- Please choose the credentials you want to update: -

+

+

+

    + diff --git a/keepassxc-browser/popups/popup_remember.js b/keepassxc-browser/popups/popup_remember.js index 13c04ae..f189205 100644 --- a/keepassxc-browser/popups/popup_remember.js +++ b/keepassxc-browser/popups/popup_remember.js @@ -5,21 +5,26 @@ var _tab; function _initialize(tab) { _tab = tab; - // no credentials set or credentials already cleared - if (!_tab.credentials.username) { + // No credentials set or credentials already cleared + if (!_tab.credentials.username && !_tab.credentials.password) { _close(); return; } - // no existing credentials to update --> disable update-button + // No existing credentials to update --> disable Update button if (_tab.credentials.list.length === 0) { $('#btn-update').attr('disabled', true).removeClass('btn-warning'); } + // No username available. This might be because of trigger from context menu --> disable New button + if (!_tab.credentials.username && _tab.credentials.password) { + $('#btn-new').attr('disabled', true).removeClass('btn-success'); + } + let url = _tab.credentials.url; url = (url.length > 50) ? url.substring(0, 50) + '...' : url; - $('.information-url:first span:first').text(url); - $('.information-username:first span:first').text(_tab.credentials.username); + $('.information-url:first').text(url); + $('.information-username:first').text(_tab.credentials.username); $('#btn-new').click(function(e) { browser.runtime.sendMessage({ @@ -31,8 +36,13 @@ function _initialize(tab) { $('#btn-update').click(function(e) { e.preventDefault(); - // only one entry which could be updated + // Only one entry which could be updated if(_tab.credentials.list.length === 1) { + // Use the current username if it's empty + if (!_tab.credentials.username) { + _tab.credentials.username = _tab.credentials.list[0].login; + } + browser.runtime.sendMessage({ action: 'update_credentials', args: [_tab.credentials.list[0].uuid, _tab.credentials.username, _tab.credentials.password, _tab.credentials.url] @@ -58,10 +68,35 @@ function _initialize(tab) { .data('entryId', i) .click(function(e) { e.preventDefault(); + const entryId = $(this).data('entryId'); + + // Use the current username if it's empty + if (!_tab.credentials.username) { + _tab.credentials.username = _tab.credentials.list[entryId].login; + } + + // Check if the password has changed for the updated credentials browser.runtime.sendMessage({ - action: 'update_credentials', - args: [_tab.credentials.list[$(this).data('entryId')].uuid, _tab.credentials.username, _tab.credentials.password, _tab.credentials.url] - }).then(_verifyResult); + action: 'retrieve_credentials', + args: [ url, '', false, true ] + }).then((credentials) => { + if (!credentials || credentials.length !== _tab.credentials.list.length) { + _verifyResult('error'); + return; + } + + // Show a notification if the user tries to update credentials using the old password + if (credentials[entryId].password === _tab.credentials.password) { + showNotification('Error: Credentials not updated. The password has not been changed.'); + _close(); + return; + } + + browser.runtime.sendMessage({ + action: 'update_credentials', + args: [_tab.credentials.list[entryId].uuid, _tab.credentials.username, _tab.credentials.password, _tab.credentials.url] + }).then(_verifyResult); + }); }); if (_tab.credentials.usernameExists && _tab.credentials.username === _tab.credentials.list[i].login) { diff --git a/keepassxc-browser/translate.js b/keepassxc-browser/translate.js new file mode 100644 index 0000000..69c25e8 --- /dev/null +++ b/keepassxc-browser/translate.js @@ -0,0 +1,15 @@ +'use strict' + +const items = document.querySelectorAll('[data-i18n]'); +for (let item of items) { + const key = item.getAttribute('data-i18n'); + if (key) { + const placeholder = item.getAttribute('i18n-placeholder'); + const translation = placeholder ? browser.i18n.getMessage(key, placeholder) : browser.i18n.getMessage(key); + if (item.hasAttribute('href')) { + item.text = translation; + } else { + item.innerHTML = translation; + } + } +} diff --git a/keepassxc-protocol.md b/keepassxc-protocol.md index 94e7f4a..95afb06 100644 --- a/keepassxc-protocol.md +++ b/keepassxc-protocol.md @@ -8,7 +8,7 @@ Now the requests are encrypted by [TweetNaCl.js](https://github.com/dchest/tweet 3. All messages between the browser extension and KeePassXC are now encrypted. 4. When keepassxc-browser sends a message it is encrypted with KeePassXC's public key, a random generated nonce and keepassxc-browser's secret key. 5. When KeePassXC sends a message it is encrypted with keepassxc-browser's public key and an incremented nonce. -6. Databases are stored based on the current public key used with `associate`. A new key pair for data transfer is generated each time keepassxc-browser is launched. This saved key is not used again, as it's only used for identification. +6. Databases are stored with newly created public key used with `associate`. A new key pair for data transfer is generated each time keepassxc-browser is launched. This saved key is not used again, as it's only used for identification. Encrypted messages are built with these JSON parameters: - action - `test-associate`, `associate`, `get-logins`, `get-logins-count`, `set-login`... @@ -71,7 +71,8 @@ Unencrypted message: ```javascript { "action": "associate", - "key": "" + "key": "", + "idKey": "" } ```