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('') .append($checkboxNextField) .addClass('cip-genpw-label') - .append(' also fill in the next password-field'); + .append(tr('passwordGeneratorLabel')); $checkGroup.append($labelNextField); $inputDiv.append($inputGroup).append($checkGroup); @@ -273,12 +278,12 @@ cipPassword.createDialog = function() { resizable: false, minWidth: 300, minHeight: 80, - title: 'Password Generator', + title: tr('passwordGeneratorTitle'), classes: {'ui-dialog': 'ui-corner-all'}, buttons: { 'Generate': { - text: 'Generate', + text: tr('passwordGeneratorGenerate'), id: 'cip-genpw-btn-generate', click: (e) => { e.preventDefault(); @@ -291,7 +296,7 @@ cipPassword.createDialog = function() { }, 'Copy': { - text: 'Copy', + text: tr('passwordGeneratorCopy'), id: 'cip-genpw-btn-clipboard', click: (e) => { e.preventDefault(); @@ -300,7 +305,7 @@ cipPassword.createDialog = function() { }, 'Fill & copy': { - text: 'Fill & copy', + text: tr('passwordGeneratorFillAndCopy'), id: 'cip-genpw-btn-fillin', click: (e) => { e.preventDefault(); @@ -315,7 +320,7 @@ cipPassword.createDialog = function() { $password = $password.substring(0, field.attr('maxlength')); jQuery('input#cip-genpw-textfield-password:first').val($password); jQuery('#cip-genpw-btn-clipboard:first').removeClass('b2c-btn-success'); - const 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!'; + alert(tr('passwordGeneratorErrorTooLong')); browser.runtime.sendMessage({ action: 'show_notification', args: [message] @@ -362,7 +367,7 @@ cipPassword.createIcon = function(field) { .addClass('kpxc') .addClass('cip-genpw-icon') .addClass($className) - .attr('title', 'Generate password') + .attr('title', tr('passwordGeneratorGenerateText')) .css('z-index', '9999') .css('width', $size) .css('height', $size) @@ -446,7 +451,7 @@ cipPassword.callbackGeneratedPassword = function(entries) { } else { if (jQuery('div#cip-genpw-error:first').length === 0) { - jQuery('button#cip-genpw-btn-generate:first').after('Cannot receive generated password.Is KeePassXC opened?'); + jQuery('button#cip-genpw-btn-generate:first').after('' + tr('passwordGeneratorError') + ''); jQuery('input#cip-genpw-textfield-password:first').parent().hide(); jQuery('input#cip-genpw-checkbox-next-field:first').parent('label').hide(); jQuery('button#cip-genpw-btn-generate').hide(); @@ -586,13 +591,13 @@ cipDefine.initDescription = function() { const $help = jQuery('').addClass('b2c-chooser-help').attr('id', 'b2c-help'); $description.append($help); - const $btnDismiss = jQuery('').text('Dismiss').attr('id', 'b2c-btn-dismiss') + const $btnDismiss = jQuery('').text(tr('defineDismiss')).attr('id', 'b2c-btn-dismiss') .addClass('btn').addClass('btn-danger') .click(function(e) { jQuery('div#b2c-backdrop').remove(); jQuery('div#b2c-cipDefine-fields').remove(); }); - const $btnSkip = jQuery('').text('Skip').attr('id', 'b2c-btn-skip') + const $btnSkip = jQuery('').text(tr('defineSkip')).attr('id', 'b2c-btn-skip') .addClass('btn').addClass('btn-info') .css('margin-right', '5px') .click(function() { @@ -607,7 +612,7 @@ cipDefine.initDescription = function() { cipDefine.markAllStringFields(jQuery('#b2c-cipDefine-fields')); } }); - const $btnAgain = jQuery('').text('Again').attr('id', 'b2c-btn-again') + const $btnAgain = jQuery('').text(tr('defineAgain')).attr('id', 'b2c-btn-again') .addClass('btn').addClass('btn-warning') .css('margin-right', '5px') .click(function(e) { @@ -616,7 +621,7 @@ cipDefine.initDescription = function() { cipDefine.markAllUsernameFields(jQuery('#b2c-cipDefine-fields')); }) .hide(); - const $btnConfirm = jQuery('').text('Confirm').attr('id', 'b2c-btn-confirm') + const $btnConfirm = jQuery('').text(tr('defineConfirm')).attr('id', 'b2c-btn-confirm') .addClass('btn').addClass('btn-primary') .css('margin-right', '15px') .click(function(e) { @@ -661,10 +666,10 @@ cipDefine.initDescription = function() { const location = cip.getDocumentLocation(); if (cip.settings['defined-custom-fields'] && cip.settings['defined-custom-fields'][location]) { - const $p = jQuery('').html('For this page credential fields are already selected and will be overwritten.'); + const $p = jQuery('').html(tr('defineAlreadySelected') + ''); const $btnDiscard = jQuery('') .attr('id', 'btn-warning') - .text('Discard selection') + .text(tr('defineDiscard')) .css('margin-top', '5px') .addClass('btn') .addClass('btn-sm') @@ -709,7 +714,7 @@ cipDefine.isFieldSelected = function($cipId) { cipDefine.markAllUsernameFields = function($chooser) { cipDefine.eventFieldClick = function(e) { cipDefine.selection.username = jQuery(this).data('cip-id'); - jQuery(this).addClass('b2c-fixed-username-field').text('Username').unbind('click'); + jQuery(this).addClass('b2c-fixed-username-field').text(tr('username')).unbind('click'); cipDefine.prepareStep2(); cipDefine.markAllPasswordFields(jQuery('#b2c-cipDefine-fields')); }; @@ -719,7 +724,7 @@ cipDefine.markAllUsernameFields = function($chooser) { cipDefine.markAllPasswordFields = function($chooser) { cipDefine.eventFieldClick = function(e) { cipDefine.selection.password = jQuery(this).data('cip-id'); - jQuery(this).addClass('b2c-fixed-password-field').text('Password').unbind('click'); + jQuery(this).addClass('b2c-fixed-password-field').text(tr('password')).unbind('click'); cipDefine.prepareStep3(); cipDefine.markAllStringFields(jQuery('#b2c-cipDefine-fields')); }; @@ -730,7 +735,7 @@ cipDefine.markAllStringFields = function($chooser) { cipDefine.eventFieldClick = function(e) { cipDefine.selection.fields[jQuery(this).data('cip-id')] = true; const count = Object.keys(cipDefine.selection.fields).length; - jQuery(this).addClass('b2c-fixed-string-field').text('String field #' + String(count)).unbind('click'); + jQuery(this).addClass('b2c-fixed-string-field').text(tr('defineStringField') + String(count)).unbind('click'); jQuery('button#b2c-btn-confirm:first').addClass('b2c-btn-primary').attr('disabled', false); }; cipDefine.markFields($chooser, cipFields.inputQueryPattern + ', select'); @@ -761,7 +766,7 @@ cipDefine.prepareStep1 = function() { jQuery('div#b2c-cipDefine-fields').removeData('username'); jQuery('div#b2c-cipDefine-fields').removeData('password'); jQuery('div.b2c-fixed-field', jQuery('div#b2c-cipDefine-fields')).remove(); - jQuery('div:first', jQuery('div#b2c-cipDefine-description')).text('1. Choose a username field'); + jQuery('div:first', jQuery('div#b2c-cipDefine-description')).text(tr('defineChooseUsername')); jQuery('button#b2c-btn-skip:first').data('step', '1').show(); jQuery('button#b2c-btn-confirm:first').hide(); jQuery('button#b2c-btn-again:first').hide(); @@ -770,7 +775,7 @@ cipDefine.prepareStep1 = function() { cipDefine.prepareStep2 = function() { jQuery('div#b2c-help').text('').css('margin-bottom', 0); jQuery('div.b2c-fixed-field:not(.b2c-fixed-username-field)', jQuery('div#b2c-cipDefine-fields')).remove(); - jQuery('div:first', jQuery('div#b2c-cipDefine-description')).text('2. Now choose a password field'); + jQuery('div:first', jQuery('div#b2c-cipDefine-description')).text(tr('defineChoosePassword')); jQuery('button#b2c-btn-skip:first').data('step', '2'); jQuery('button#b2c-btn-again:first').show(); }; @@ -780,11 +785,11 @@ cipDefine.prepareStep3 = function() { jQuery('button#b2c-btn-confirm:first').removeClass('b2c-btn-primary').attr('disabled', true); } - jQuery('div#b2c-help').html('Please confirm your selection or choose more fields as String fields.').css('margin-bottom', '5px'); + jQuery('div#b2c-help').html(tr('defineHelpText')).css('margin-bottom', '5px'); jQuery('div.b2c-fixed-field:not(.b2c-fixed-password-field,.b2c-fixed-username-field)', jQuery('div#b2c-cipDefine-fields')).remove(); jQuery('button#b2c-btn-confirm:first').show(); jQuery('button#b2c-btn-skip:first').data('step', '3').hide(); - jQuery('div:first', jQuery('div#b2c-cipDefine-description')).text('3. Confirm selection'); + jQuery('div:first', jQuery('div#b2c-cipDefine-description')).text(tr('defineConfirmSelection')); }; @@ -902,7 +907,7 @@ cipFields.isVisible = function(field) { } // Check element position and size - if (rect.x < 0 || rect.y < 0 || rect.width < 16 || rect.height < 16) { + if (rect.x < 0 || rect.y < 0 || rect.width < 8 || rect.height < 8) { return false; } @@ -1234,17 +1239,27 @@ cipObserverHelper.ignoredNode = function(target) { }; cipObserverHelper.getInputs = function(target) { + // Ignores target element if it's not an element node if (cipObserverHelper.ignoredNode(target)) { return []; } - const input = target.getElementsByTagName('input'); - if (input.length === 0 || input.length > _maximumInputs) { + // Filter out any input fields with type 'hidden' right away + let inputFields = []; + Array.from(target.getElementsByTagName('input')).forEach(e => { + if (e.type !== 'hidden') { + inputFields.push(e); + } + }); + + // Do not allow more visible inputs than _maximumInputs (default value: 100) + if (inputFields.length === 0 || inputFields.length > _maximumInputs) { return []; } + // Only include input fields that match with cipObserverHelper.inputTypes let inputs = []; - for (const i of input) { + for (const i of inputFields) { if (cipObserverHelper.inputTypes.includes(i.getAttribute('type'))) { inputs.push(i); } @@ -1257,16 +1272,14 @@ cipObserverHelper.getId = function(target) { }; cipObserverHelper.ignoredElement = function(target) { - // Ignore SVG elements - if (target.nodeName === 'svg' || - target.nodeName === 'g' || - (target.parentNode && - (target.parentNode.nodeName === 'svg' || target.parentNode.nodeName === 'g'))) { + // Ignore elements that do not have a className (including SVG) + if (typeof target.className !== 'string') { return true; } // Ignore KeePassXC-Browser classes - if (target.className && (target.className.includes('kpxc') || target.className.includes('ui-helper'))) { + if (target.className && target.className !== undefined && + (target.className.includes('kpxc') || target.className.includes('ui-helper'))) { return true; } @@ -1566,7 +1579,7 @@ cip.preparePageForMultipleCredentials = function(credentials) { cipAutocomplete.elements = []; let visibleLogin; for (let i = 0; i < credentials.length; i++) { - visibleLogin = (credentials[i].login.length > 0) ? credentials[i].login : '- no username -'; + visibleLogin = (credentials[i].login.length > 0) ? credentials[i].login : tr('credentialsNoUsername'); usernames.push(visibleLogin + ' (' + credentials[i].name + ')'); const item = { label: visibleLogin + ' (' + credentials[i].name + ')', @@ -1704,7 +1717,7 @@ cip.fillInFromActiveElement = function(suppressWarnings, passOnly = false) { if (passOnly) { if (!_f(combination.password)) { - const message = 'Error:\nUnable to find a password field'; + const message = tr('fieldsNoPasswordField'); browser.runtime.sendMessage({ action: 'show_notification', args: [message] @@ -1795,7 +1808,7 @@ cip.setValueWithChange = function(field, value) { cip.fillIn = function(combination, onlyPassword, suppressWarnings) { // no credentials available if (cip.credentials.length === 0 && !suppressWarnings) { - const message = 'Error:\nNo logins found.'; + const message = tr('credentialsNoLoginsFound'); browser.runtime.sendMessage({ action: 'show_notification', args: [message] @@ -1809,7 +1822,7 @@ cip.fillIn = function(combination, onlyPassword, suppressWarnings) { // exactly one pair of credentials available if (cip.credentials.length === 1) { let filledIn = false; - if (uField && !onlyPassword) { + if (uField && (!onlyPassword || _singleInputEnabledForPage)) { cip.setValueWithChange(uField, cip.credentials[0].login); browser.runtime.sendMessage({ action: 'page_set_login_id', args: [0] @@ -1834,7 +1847,7 @@ cip.fillIn = function(combination, onlyPassword, suppressWarnings) { if (!filledIn) { if (!suppressWarnings) { - const message = 'Error:\nCannot find fields to fill in.'; + const message = tr('fieldsFill'); browser.runtime.sendMessage({ action: 'show_notification', args: [message] @@ -1870,7 +1883,7 @@ cip.fillIn = function(combination, onlyPassword, suppressWarnings) { if (!filledIn) { if (!suppressWarnings) { - const message = 'Error:\nCannot find fields to fill in.'; + const message = tr('fieldsFill'); browser.runtime.sendMessage({ action: 'show_notification', args: [message] @@ -1932,7 +1945,7 @@ cip.fillIn = function(combination, onlyPassword, suppressWarnings) { } else if (countPasswords < 1) { if (!suppressWarnings) { - const message = 'Error:\nNo credentials for given username found.'; + const message = tr('credentialsNoUsernameFound'); browser.runtime.sendMessage({ action: 'show_notification', args: [message] @@ -1981,7 +1994,7 @@ cip.contextMenuRememberCredentials = function() { } if (!cip.rememberCredentials(usernameValue, passwordValue)) { - const message = 'Error:\nCould not detect changed credentials.'; + const message = tr('rememberNothingChanged'); browser.runtime.sendMessage({ action: 'show_notification', args: [message] @@ -2055,9 +2068,13 @@ cip.ignoreSite = function(sites) { return; } - const site = sites[0]; + let site = sites[0]; cip.initializeSitePreferences(); + if (slashNeededForUrl(site)) { + site += '/'; + } + // Check if the site already exists let siteExists = false; for (const existingSite of cip.settings['sitePreferences']) { diff --git a/keepassxc-browser/manifest.json b/keepassxc-browser/manifest.json index a571d1b..ea0b2ff 100755 --- a/keepassxc-browser/manifest.json +++ b/keepassxc-browser/manifest.json @@ -1,109 +1,110 @@ { - "manifest_version": 2, - "name": "KeePassXC-Browser", - "version": "1.2.0", - "description": "KeePassXC integration for modern web browsers", - "author": "KeePassXC Team", - "icons": { - "16": "icons/keepassxc_16x16.png", - "48": "icons/keepassxc_48x48.png", - "128": "icons/keepassxc_128x128.png" - }, + "manifest_version": 2, + "name": "KeePassXC-Browser", + "version": "1.3.0", + "description": "__MSG_extensionDescription__", + "author": "KeePassXC Team", + "icons": { + "16": "icons/keepassxc_16x16.png", + "48": "icons/keepassxc_48x48.png", + "128": "icons/keepassxc_128x128.png" + }, - "browser_action": { - "default_icon": { - "19": "icons/keepassxc_19x19.png", - "38": "icons/keepassxc_38x38.png" - }, - "default_title": "KeePassXC-Browser", - "default_popup": "popups/popup.html" - }, - "options_ui": { - "page": "options/options.html", - "open_in_tab": true - }, - "background": { - "scripts": [ - "browser-polyfill.min.js", - "global.js", - "background/nacl.min.js", - "background/nacl-util.min.js", - "background/keepass.js", - "background/httpauth.js", - "background/browserAction.js", - "background/page.js", - "background/event.js", - "background/init.js" - ] - }, - "content_scripts": [ - { - "matches": [ - "" - ], - "exclude_matches": [ - "*://*/*.xml", - "*://*/*.xsd" - ], - "js": [ - "browser-polyfill.min.js", - "global.js", - "jquery-3.3.1.min.js", - "jquery-ui.min.js", - "keepassxc-browser.js" - ], - "css": [ - "jquery-ui.min.css", - "keepassxc-browser.css" - ], - "run_at": "document_idle", - "all_frames": true - } - ], - "commands": { - "fill-username-password": { - "description": "Insert username + password", - "suggested_key": { - "default": "Alt+Shift+U", - "mac": "MacCtrl+Shift+U" - } - }, - "fill-password": { - "description": "Insert a password", - "suggested_key": { - "default": "Alt+Shift+I", - "mac": "MacCtrl+Shift+I" - } - }, + "browser_action": { + "default_icon": { + "19": "icons/keepassxc_19x19.png", + "38": "icons/keepassxc_38x38.png" + }, + "default_title": "KeePassXC-Browser", + "default_popup": "popups/popup.html" + }, + "options_ui": { + "page": "options/options.html", + "open_in_tab": true + }, + "background": { + "scripts": [ + "browser-polyfill.min.js", + "global.js", + "background/nacl.min.js", + "background/nacl-util.min.js", + "background/keepass.js", + "background/httpauth.js", + "background/browserAction.js", + "background/page.js", + "background/event.js", + "background/init.js" + ] + }, + "content_scripts": [ + { + "matches": [ + "" + ], + "exclude_matches": [ + "*://*/*.xml", + "*://*/*.xsd" + ], + "js": [ + "browser-polyfill.min.js", + "global.js", + "jquery-3.3.1.min.js", + "jquery-ui.min.js", + "keepassxc-browser.js" + ], + "css": [ + "jquery-ui.min.css", + "keepassxc-browser.css" + ], + "run_at": "document_idle", + "all_frames": true + } + ], + "commands": { + "fill-username-password": { + "description": "__MSG_contextMenuFillUsernameAndPassword__", + "suggested_key": { + "default": "Alt+Shift+U", + "mac": "MacCtrl+Shift+U" + } + }, + "fill-password": { + "description": "__MSG_contextMenuFillPassword__", + "suggested_key": { + "default": "Alt+Shift+I", + "mac": "MacCtrl+Shift+I" + } + }, "fill-totp": { - "description": "Insert a TOTP", + "description": "__MSG_contextMenuFillTOTP__", "suggested_key": { "default": "Alt+Shift+T", "mac": "MacCtrl+Shift+T" } } - }, - "web_accessible_resources": [ - "icons/key.png" - ], - "permissions": [ - "activeTab", - "contextMenus", - "clipboardWrite", - "nativeMessaging", + }, + "web_accessible_resources": [ + "icons/key.png" + ], + "permissions": [ + "activeTab", + "contextMenus", + "clipboardWrite", + "nativeMessaging", "notifications", - "storage", - "tabs", - "webRequest", - "webRequestBlocking", - "https://*/*", - "http://*/*", - "https://api.github.com/" - ], - "applications": { - "gecko": { - "id": "keepassxc-browser@keepassxc.org", - "strict_min_version": "52.0" - } - } + "storage", + "tabs", + "webRequest", + "webRequestBlocking", + "https://*/*", + "http://*/*", + "https://api.github.com/" + ], + "applications": { + "gecko": { + "id": "keepassxc-browser@keepassxc.org", + "strict_min_version": "52.0" + } + }, + "default_locale": "en" } diff --git a/keepassxc-browser/options/options.html b/keepassxc-browser/options/options.html index 60ba638..646a8da 100644 --- a/keepassxc-browser/options/options.html +++ b/keepassxc-browser/options/options.html @@ -1,7 +1,7 @@ - Settings | KeePassXC-Browser + @@ -17,71 +17,72 @@ - General - Connected Databases - Custom credential fields - Site preferences - About + + + + + - General settings + + - If you just want to insert username + password into the fields where your focus is, press Ctrl + Shift + UAlt + Shift + U. - - If you only want to insert the password, just press Ctrl + Shift + IAlt + Shift + I. - You can customize these shortcuts on chrome://extensions/configureCommands page + : error + : error + : error + - Blink Time: + - Save + - What is the maximum time (ms) the icon should blink after detecting new credentials + - Default: 7500, Infinite: -1 + , - Redirect Offset: + - Save + - What is the minimum time (ms) the icon should blink before deactivating due to page redirects. + - -1 to only use Blink Time ignoring Redirect Allowance (old behavior) + - Default: -1, Recommended: 2000 + , - Redirect Allowance: + - Save + - How many pages should the tab cycle through after the redirect offset before deactivating the icon + - Default: 1 + 1 @@ -89,53 +90,53 @@ - Activate password generator. + - For all password-fields there will be an icon added to generate a new password. + - It is generated by KeePassXC with the profile for automatically generated passwords for new entries. + - Automatically retrieve credentials. + - KeePassXC-Browser will immediately retrieve the credentials when the tab is activated. + - Automatically fill-in single credentials entry. + - If KeePassXC-Browser does only receive a single entry from KeePassXC it automatically fills this credentials into the found credential fields. - Warning! Using auto-fill is not safe. Use at your own risk. + + - Activate autocomplete for username fields. + - For all username fields on a page a dropdown list appears which contains all available credentials. + - Show notifications. + - Show notifications for errors and when user interaction is required. + - Show a notification when new credentials can be saved to the database. + @@ -143,44 +144,43 @@ - Save domain only. + - When saving new credentials save only the domain instead of full URL. + - Check for updates of KeePassXC: - - every 3 days - every week - every month - never + + + + + - KeePassXC-Browser needs KeePassXC to retrieve credentials. + - You can download the latest stable version from here: https://keepassxc.org/ + - You are running KeePassXC version: + - Latest available version of KeePassXC: - Check for updates + + - Auto fill HTTP Auth dialogs and send them. + - If credentials are found for a page and the login-type is an HTTP Auth request, KeePassXC-Browser tries to login with the first given credentials. + - An HTTP Auth dialog looks like this: + @@ -189,36 +189,34 @@ - Connected databases + - - The following KeePassXC databases are connected to KeePassXC-Browser. - + - Identifier - Key - Last used - Created - Delete + + + + + - No connected databases found. + - Remove + - Connect + @@ -226,15 +224,16 @@ × - Remove identifier from database list? + - Do you really want to remove the identifier from the database list? - You can reconnect your database at any time. + + + @@ -243,29 +242,29 @@ - Custom credential fields + - If KeePassXC-Browser detects the wrong credential fields, you are able to specify the correct fields by yourself. + - Go to the page and click on the KeePassXC-Browser-Icon, now select Choose custom credential fields for this page. + . - On this page you can manage saved custom credential fields. + - Page URL - Delete + + - No saved custom credential fields found. + - Remove + @@ -274,15 +273,15 @@ × - Remove saved custom credential fields? + - DDo you really want to remove the saved custom credential fields on the page: ? - KeePassXC-Browser will automatically detect the credential fields next time you visit this page. + + @@ -291,24 +290,24 @@ - Site preferences + - Sites on this page have special handling methods associated with them. + - To ignore new/modified credentials on a specific site, add it below or click the blinking KeePassXC-Browser icon and select Never ask for this page. + . - If a site is fully ignored (Disable all features is selected), then the plugin will do nothing when visiting that site. + - Enabling the Username-Only Detection feature allows KeePassXC-Browser to fill-in pages on sites that do present a separated username and password input. + - Add URL manually: + - Add + @@ -316,27 +315,27 @@ - Page URL - Ignore - Username-Only Detection - Delete + + + + - No sites found. + - Enable all features - Disable new/modified credentials - Disable all features + + + - Remove + @@ -345,15 +344,15 @@ × - Remove site? + - Do you really want to remove the specified site from the list? - KeePassXC-Browser will enable all features for this site and remove username-only detection. + + @@ -362,30 +361,30 @@ - About + - Visit extension's page in Chrome Web store + - Visit extension's page in Mozilla's Add-ons page + - Visit project's page in GitHub. + . - KeePassXC-Browser Version: + - KeePassXC Version: + - Contributors: + Perry Nguyen, Lukas Schulze, - Andy Brandt, Sami Vänttinen - and KeePassXC Team. + Andy Brandt, Sami Vänttinen, + KeePassXC Team. @@ -393,5 +392,7 @@ (C) 2017-2018 - KeePassXC Team + + diff --git a/keepassxc-browser/options/options.js b/keepassxc-browser/options/options.js index 58e582b..bccce52 100644 --- a/keepassxc-browser/options/options.js +++ b/keepassxc-browser/options/options.js @@ -118,6 +118,18 @@ options.initGeneralSettings = function() { $('#blinkMinTimeout').val(options.settings['blinkMinTimeout']); $('#allowedRedirect').val(options.settings['allowedRedirect']); + browser.commands.getAll().then(function(commands) { + commands.forEach(function(command) { + var shortcut = document.getElementById(`${command.name}-shortcut`); + if (!shortcut) return; + shortcut.textContent = command.shortcut || 'not configured'; + }); + }); + + $('#configureCommands').click(function(){ + browser.tabs.create({ url: 'chrome://extensions/configureCommands' }); + }); + $('#blinkTimeoutButton').click(function(){ const blinkTimeout = $.trim($('#blinkTimeout').val()); const blinkTimeoutval = blinkTimeout !== '' ? Number(blinkTimeout) : defaultSettings.blinkTimeout; @@ -156,6 +168,10 @@ options.showKeePassXCVersions = function(response) { $('#tab-general-settings button.checkUpdateKeePassXC:first').attr('disabled', false); }; +options.getPartiallyHiddenKey = function(key) { + return !key ? 'Error' : (key.substr(0, 8) + '*'.repeat(10)); +}; + options.initConnectedDatabases = function() { $('#dialogDeleteConnectedDatabase').modal({keyboard: true, show: false, backdrop: true}); $('#tab-connected-databases tr.clone:first button.delete:first').click(function(e) { @@ -193,7 +209,7 @@ options.initConnectedDatabases = function() { $('a.dropdown-toggle:first img:first', tr).attr('src', '/icons/19x19/icon_normal_19x19.png'); tr.children('td:first').text(options.keyRing[hash].id); - tr.children('td:eq(1)').text(options.keyRing[hash].key); + tr.children('td:eq(1)').text(options.getPartiallyHiddenKey(options.keyRing[hash].key)); const lastUsed = (options.keyRing[hash].lastUsed) ? new Date(options.keyRing[hash].lastUsed).toLocaleString() : 'unknown'; tr.children('td:eq(2)').text(lastUsed); const date = (options.keyRing[hash].created) ? new Date(options.keyRing[hash].created).toLocaleDateString() : 'unknown'; @@ -310,7 +326,7 @@ options.initSitePreferences = function() { trClone.removeClass('clone'); // Fills the last / char if needed. This ensures the compatibility with Match Patterns - if (options.slashNeededForUrl(value)) { + if (slashNeededForUrl(value)) { value += '/'; } @@ -366,7 +382,7 @@ options.initSitePreferences = function() { $('#tab-site-preferences table tbody:first').append(tr); } } - + if ($('#tab-site-preferences table tbody:first tr').length > 2) { $('#tab-site-preferences table tbody:first tr.empty:first').hide(); } else { @@ -379,22 +395,4 @@ options.initAbout = function() { if (isFirefox()) { $('#chrome-only').remove(); } - - if (navigator.platform === 'MacIntel') { - $('#default-user-shortcut').hide(); - $('#default-pass-shortcut').hide(); - $('#mac-user-shortcut').show(); - $('#mac-pass-shortcut').show(); - } else { - $('#mac-user-shortcut').hide(); - $('#mac-pass-shortcut').hide(); - $('#default-user-shortcut').show(); - $('#default-pass-shortcut').show(); - } -}; - -// Checks if URL has only scheme and host without the last / char. -options.slashNeededForUrl = function(pattern) { - const matchPattern = new RegExp(`^${schemeSegment}://${hostSegment}$`); - return matchPattern.exec(pattern); }; diff --git a/keepassxc-browser/popups/popup.css b/keepassxc-browser/popups/popup.css index 4902487..3827486 100644 --- a/keepassxc-browser/popups/popup.css +++ b/keepassxc-browser/popups/popup.css @@ -4,6 +4,9 @@ body { background-color: #eee; font-size: 15px; padding: 8px; + min-width: 440px; + max-width: 460px; + width: auto; } .container { min-width: 440px; @@ -59,3 +62,11 @@ body { float: right; height: 30px; } +#login-filter { + outline:none; + border-radius: 4px 0 0 4px; + border: 1px solid #ccc; + margin-bottom: 5px; + padding: 2px 10px; + width: 100%; +} \ No newline at end of file diff --git a/keepassxc-browser/popups/popup.html b/keepassxc-browser/popups/popup.html index d049a0a..496bb84 100644 --- a/keepassxc-browser/popups/popup.html +++ b/keepassxc-browser/popups/popup.html @@ -1,6 +1,6 @@ - KeePassXC - Popup + @@ -14,86 +14,70 @@ - Settings - Choose custom credential fields for this page + + - You use an old version of KeePassXC. + - Please download the latest version from keepassxc.org. + . - Checking status... + - - KeePassXC-Browser has not been configured. - Press the connect button to register and pair with KeePassXC. - + - Connect + - - KeePassXC-Browser has been disconnected from KeePassXC. - - - - Press the reconnect button to establish a new connection. + + + + - Reconnect + - - KeePassXC-Browser has been configured using the identifier - and has not yet - connected to KeePassXC. - + - - KeePassXC-Browser has been configured using the identifier - "" and is successfully - connected to KeePassXC. - + - Redetect credential fields + - - KeePassXC-Browser has encountered an error: - + - Reload + - - KeePassXC-Browser has encountered an error: - + - Reopen database + + diff --git a/keepassxc-browser/popups/popup_httpauth.html b/keepassxc-browser/popups/popup_httpauth.html index 0adeec7..2a1c773 100644 --- a/keepassxc-browser/popups/popup_httpauth.html +++ b/keepassxc-browser/popups/popup_httpauth.html @@ -14,26 +14,25 @@ - Settings - Choose custom credential fields for this page + + - You use an old version of KeePassXC. + - Please download the latest version from keepassxc.org. + . + - - - Select the login information you would like to get logged in with: - + - Dismiss and show the default authentication dialog + + 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 @@ - Settings - Choose custom credential fields for this page + + - 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: - + + + + + +
').html('For this page credential fields are already selected and will be overwritten.'); + const $p = jQuery('
').html(tr('defineAlreadySelected') + ''); const $btnDiscard = jQuery('') .attr('id', 'btn-warning') - .text('Discard selection') + .text(tr('defineDiscard')) .css('margin-top', '5px') .addClass('btn') .addClass('btn-sm') @@ -709,7 +714,7 @@ cipDefine.isFieldSelected = function($cipId) { cipDefine.markAllUsernameFields = function($chooser) { cipDefine.eventFieldClick = function(e) { cipDefine.selection.username = jQuery(this).data('cip-id'); - jQuery(this).addClass('b2c-fixed-username-field').text('Username').unbind('click'); + jQuery(this).addClass('b2c-fixed-username-field').text(tr('username')).unbind('click'); cipDefine.prepareStep2(); cipDefine.markAllPasswordFields(jQuery('#b2c-cipDefine-fields')); }; @@ -719,7 +724,7 @@ cipDefine.markAllUsernameFields = function($chooser) { cipDefine.markAllPasswordFields = function($chooser) { cipDefine.eventFieldClick = function(e) { cipDefine.selection.password = jQuery(this).data('cip-id'); - jQuery(this).addClass('b2c-fixed-password-field').text('Password').unbind('click'); + jQuery(this).addClass('b2c-fixed-password-field').text(tr('password')).unbind('click'); cipDefine.prepareStep3(); cipDefine.markAllStringFields(jQuery('#b2c-cipDefine-fields')); }; @@ -730,7 +735,7 @@ cipDefine.markAllStringFields = function($chooser) { cipDefine.eventFieldClick = function(e) { cipDefine.selection.fields[jQuery(this).data('cip-id')] = true; const count = Object.keys(cipDefine.selection.fields).length; - jQuery(this).addClass('b2c-fixed-string-field').text('String field #' + String(count)).unbind('click'); + jQuery(this).addClass('b2c-fixed-string-field').text(tr('defineStringField') + String(count)).unbind('click'); jQuery('button#b2c-btn-confirm:first').addClass('b2c-btn-primary').attr('disabled', false); }; cipDefine.markFields($chooser, cipFields.inputQueryPattern + ', select'); @@ -761,7 +766,7 @@ cipDefine.prepareStep1 = function() { jQuery('div#b2c-cipDefine-fields').removeData('username'); jQuery('div#b2c-cipDefine-fields').removeData('password'); jQuery('div.b2c-fixed-field', jQuery('div#b2c-cipDefine-fields')).remove(); - jQuery('div:first', jQuery('div#b2c-cipDefine-description')).text('1. Choose a username field'); + jQuery('div:first', jQuery('div#b2c-cipDefine-description')).text(tr('defineChooseUsername')); jQuery('button#b2c-btn-skip:first').data('step', '1').show(); jQuery('button#b2c-btn-confirm:first').hide(); jQuery('button#b2c-btn-again:first').hide(); @@ -770,7 +775,7 @@ cipDefine.prepareStep1 = function() { cipDefine.prepareStep2 = function() { jQuery('div#b2c-help').text('').css('margin-bottom', 0); jQuery('div.b2c-fixed-field:not(.b2c-fixed-username-field)', jQuery('div#b2c-cipDefine-fields')).remove(); - jQuery('div:first', jQuery('div#b2c-cipDefine-description')).text('2. Now choose a password field'); + jQuery('div:first', jQuery('div#b2c-cipDefine-description')).text(tr('defineChoosePassword')); jQuery('button#b2c-btn-skip:first').data('step', '2'); jQuery('button#b2c-btn-again:first').show(); }; @@ -780,11 +785,11 @@ cipDefine.prepareStep3 = function() { jQuery('button#b2c-btn-confirm:first').removeClass('b2c-btn-primary').attr('disabled', true); } - jQuery('div#b2c-help').html('Please confirm your selection or choose more fields as String fields.').css('margin-bottom', '5px'); + jQuery('div#b2c-help').html(tr('defineHelpText')).css('margin-bottom', '5px'); jQuery('div.b2c-fixed-field:not(.b2c-fixed-password-field,.b2c-fixed-username-field)', jQuery('div#b2c-cipDefine-fields')).remove(); jQuery('button#b2c-btn-confirm:first').show(); jQuery('button#b2c-btn-skip:first').data('step', '3').hide(); - jQuery('div:first', jQuery('div#b2c-cipDefine-description')).text('3. Confirm selection'); + jQuery('div:first', jQuery('div#b2c-cipDefine-description')).text(tr('defineConfirmSelection')); }; @@ -902,7 +907,7 @@ cipFields.isVisible = function(field) { } // Check element position and size - if (rect.x < 0 || rect.y < 0 || rect.width < 16 || rect.height < 16) { + if (rect.x < 0 || rect.y < 0 || rect.width < 8 || rect.height < 8) { return false; } @@ -1234,17 +1239,27 @@ cipObserverHelper.ignoredNode = function(target) { }; cipObserverHelper.getInputs = function(target) { + // Ignores target element if it's not an element node if (cipObserverHelper.ignoredNode(target)) { return []; } - const input = target.getElementsByTagName('input'); - if (input.length === 0 || input.length > _maximumInputs) { + // Filter out any input fields with type 'hidden' right away + let inputFields = []; + Array.from(target.getElementsByTagName('input')).forEach(e => { + if (e.type !== 'hidden') { + inputFields.push(e); + } + }); + + // Do not allow more visible inputs than _maximumInputs (default value: 100) + if (inputFields.length === 0 || inputFields.length > _maximumInputs) { return []; } + // Only include input fields that match with cipObserverHelper.inputTypes let inputs = []; - for (const i of input) { + for (const i of inputFields) { if (cipObserverHelper.inputTypes.includes(i.getAttribute('type'))) { inputs.push(i); } @@ -1257,16 +1272,14 @@ cipObserverHelper.getId = function(target) { }; cipObserverHelper.ignoredElement = function(target) { - // Ignore SVG elements - if (target.nodeName === 'svg' || - target.nodeName === 'g' || - (target.parentNode && - (target.parentNode.nodeName === 'svg' || target.parentNode.nodeName === 'g'))) { + // Ignore elements that do not have a className (including SVG) + if (typeof target.className !== 'string') { return true; } // Ignore KeePassXC-Browser classes - if (target.className && (target.className.includes('kpxc') || target.className.includes('ui-helper'))) { + if (target.className && target.className !== undefined && + (target.className.includes('kpxc') || target.className.includes('ui-helper'))) { return true; } @@ -1566,7 +1579,7 @@ cip.preparePageForMultipleCredentials = function(credentials) { cipAutocomplete.elements = []; let visibleLogin; for (let i = 0; i < credentials.length; i++) { - visibleLogin = (credentials[i].login.length > 0) ? credentials[i].login : '- no username -'; + visibleLogin = (credentials[i].login.length > 0) ? credentials[i].login : tr('credentialsNoUsername'); usernames.push(visibleLogin + ' (' + credentials[i].name + ')'); const item = { label: visibleLogin + ' (' + credentials[i].name + ')', @@ -1704,7 +1717,7 @@ cip.fillInFromActiveElement = function(suppressWarnings, passOnly = false) { if (passOnly) { if (!_f(combination.password)) { - const message = 'Error:\nUnable to find a password field'; + const message = tr('fieldsNoPasswordField'); browser.runtime.sendMessage({ action: 'show_notification', args: [message] @@ -1795,7 +1808,7 @@ cip.setValueWithChange = function(field, value) { cip.fillIn = function(combination, onlyPassword, suppressWarnings) { // no credentials available if (cip.credentials.length === 0 && !suppressWarnings) { - const message = 'Error:\nNo logins found.'; + const message = tr('credentialsNoLoginsFound'); browser.runtime.sendMessage({ action: 'show_notification', args: [message] @@ -1809,7 +1822,7 @@ cip.fillIn = function(combination, onlyPassword, suppressWarnings) { // exactly one pair of credentials available if (cip.credentials.length === 1) { let filledIn = false; - if (uField && !onlyPassword) { + if (uField && (!onlyPassword || _singleInputEnabledForPage)) { cip.setValueWithChange(uField, cip.credentials[0].login); browser.runtime.sendMessage({ action: 'page_set_login_id', args: [0] @@ -1834,7 +1847,7 @@ cip.fillIn = function(combination, onlyPassword, suppressWarnings) { if (!filledIn) { if (!suppressWarnings) { - const message = 'Error:\nCannot find fields to fill in.'; + const message = tr('fieldsFill'); browser.runtime.sendMessage({ action: 'show_notification', args: [message] @@ -1870,7 +1883,7 @@ cip.fillIn = function(combination, onlyPassword, suppressWarnings) { if (!filledIn) { if (!suppressWarnings) { - const message = 'Error:\nCannot find fields to fill in.'; + const message = tr('fieldsFill'); browser.runtime.sendMessage({ action: 'show_notification', args: [message] @@ -1932,7 +1945,7 @@ cip.fillIn = function(combination, onlyPassword, suppressWarnings) { } else if (countPasswords < 1) { if (!suppressWarnings) { - const message = 'Error:\nNo credentials for given username found.'; + const message = tr('credentialsNoUsernameFound'); browser.runtime.sendMessage({ action: 'show_notification', args: [message] @@ -1981,7 +1994,7 @@ cip.contextMenuRememberCredentials = function() { } if (!cip.rememberCredentials(usernameValue, passwordValue)) { - const message = 'Error:\nCould not detect changed credentials.'; + const message = tr('rememberNothingChanged'); browser.runtime.sendMessage({ action: 'show_notification', args: [message] @@ -2055,9 +2068,13 @@ cip.ignoreSite = function(sites) { return; } - const site = sites[0]; + let site = sites[0]; cip.initializeSitePreferences(); + if (slashNeededForUrl(site)) { + site += '/'; + } + // Check if the site already exists let siteExists = false; for (const existingSite of cip.settings['sitePreferences']) { diff --git a/keepassxc-browser/manifest.json b/keepassxc-browser/manifest.json index a571d1b..ea0b2ff 100755 --- a/keepassxc-browser/manifest.json +++ b/keepassxc-browser/manifest.json @@ -1,109 +1,110 @@ { - "manifest_version": 2, - "name": "KeePassXC-Browser", - "version": "1.2.0", - "description": "KeePassXC integration for modern web browsers", - "author": "KeePassXC Team", - "icons": { - "16": "icons/keepassxc_16x16.png", - "48": "icons/keepassxc_48x48.png", - "128": "icons/keepassxc_128x128.png" - }, + "manifest_version": 2, + "name": "KeePassXC-Browser", + "version": "1.3.0", + "description": "__MSG_extensionDescription__", + "author": "KeePassXC Team", + "icons": { + "16": "icons/keepassxc_16x16.png", + "48": "icons/keepassxc_48x48.png", + "128": "icons/keepassxc_128x128.png" + }, - "browser_action": { - "default_icon": { - "19": "icons/keepassxc_19x19.png", - "38": "icons/keepassxc_38x38.png" - }, - "default_title": "KeePassXC-Browser", - "default_popup": "popups/popup.html" - }, - "options_ui": { - "page": "options/options.html", - "open_in_tab": true - }, - "background": { - "scripts": [ - "browser-polyfill.min.js", - "global.js", - "background/nacl.min.js", - "background/nacl-util.min.js", - "background/keepass.js", - "background/httpauth.js", - "background/browserAction.js", - "background/page.js", - "background/event.js", - "background/init.js" - ] - }, - "content_scripts": [ - { - "matches": [ - "" - ], - "exclude_matches": [ - "*://*/*.xml", - "*://*/*.xsd" - ], - "js": [ - "browser-polyfill.min.js", - "global.js", - "jquery-3.3.1.min.js", - "jquery-ui.min.js", - "keepassxc-browser.js" - ], - "css": [ - "jquery-ui.min.css", - "keepassxc-browser.css" - ], - "run_at": "document_idle", - "all_frames": true - } - ], - "commands": { - "fill-username-password": { - "description": "Insert username + password", - "suggested_key": { - "default": "Alt+Shift+U", - "mac": "MacCtrl+Shift+U" - } - }, - "fill-password": { - "description": "Insert a password", - "suggested_key": { - "default": "Alt+Shift+I", - "mac": "MacCtrl+Shift+I" - } - }, + "browser_action": { + "default_icon": { + "19": "icons/keepassxc_19x19.png", + "38": "icons/keepassxc_38x38.png" + }, + "default_title": "KeePassXC-Browser", + "default_popup": "popups/popup.html" + }, + "options_ui": { + "page": "options/options.html", + "open_in_tab": true + }, + "background": { + "scripts": [ + "browser-polyfill.min.js", + "global.js", + "background/nacl.min.js", + "background/nacl-util.min.js", + "background/keepass.js", + "background/httpauth.js", + "background/browserAction.js", + "background/page.js", + "background/event.js", + "background/init.js" + ] + }, + "content_scripts": [ + { + "matches": [ + "" + ], + "exclude_matches": [ + "*://*/*.xml", + "*://*/*.xsd" + ], + "js": [ + "browser-polyfill.min.js", + "global.js", + "jquery-3.3.1.min.js", + "jquery-ui.min.js", + "keepassxc-browser.js" + ], + "css": [ + "jquery-ui.min.css", + "keepassxc-browser.css" + ], + "run_at": "document_idle", + "all_frames": true + } + ], + "commands": { + "fill-username-password": { + "description": "__MSG_contextMenuFillUsernameAndPassword__", + "suggested_key": { + "default": "Alt+Shift+U", + "mac": "MacCtrl+Shift+U" + } + }, + "fill-password": { + "description": "__MSG_contextMenuFillPassword__", + "suggested_key": { + "default": "Alt+Shift+I", + "mac": "MacCtrl+Shift+I" + } + }, "fill-totp": { - "description": "Insert a TOTP", + "description": "__MSG_contextMenuFillTOTP__", "suggested_key": { "default": "Alt+Shift+T", "mac": "MacCtrl+Shift+T" } } - }, - "web_accessible_resources": [ - "icons/key.png" - ], - "permissions": [ - "activeTab", - "contextMenus", - "clipboardWrite", - "nativeMessaging", + }, + "web_accessible_resources": [ + "icons/key.png" + ], + "permissions": [ + "activeTab", + "contextMenus", + "clipboardWrite", + "nativeMessaging", "notifications", - "storage", - "tabs", - "webRequest", - "webRequestBlocking", - "https://*/*", - "http://*/*", - "https://api.github.com/" - ], - "applications": { - "gecko": { - "id": "keepassxc-browser@keepassxc.org", - "strict_min_version": "52.0" - } - } + "storage", + "tabs", + "webRequest", + "webRequestBlocking", + "https://*/*", + "http://*/*", + "https://api.github.com/" + ], + "applications": { + "gecko": { + "id": "keepassxc-browser@keepassxc.org", + "strict_min_version": "52.0" + } + }, + "default_locale": "en" } diff --git a/keepassxc-browser/options/options.html b/keepassxc-browser/options/options.html index 60ba638..646a8da 100644 --- a/keepassxc-browser/options/options.html +++ b/keepassxc-browser/options/options.html @@ -1,7 +1,7 @@ - Settings | KeePassXC-Browser + @@ -17,71 +17,72 @@ - General - Connected Databases - Custom credential fields - Site preferences - About + + + + + - General settings + + - If you just want to insert username + password into the fields where your focus is, press Ctrl + Shift + UAlt + Shift + U. - - If you only want to insert the password, just press Ctrl + Shift + IAlt + Shift + I. - You can customize these shortcuts on chrome://extensions/configureCommands page + : error + : error + : error + - Blink Time: + - Save + - What is the maximum time (ms) the icon should blink after detecting new credentials + - Default: 7500, Infinite: -1 + , - Redirect Offset: + - Save + - What is the minimum time (ms) the icon should blink before deactivating due to page redirects. + - -1 to only use Blink Time ignoring Redirect Allowance (old behavior) + - Default: -1, Recommended: 2000 + , - Redirect Allowance: + - Save + - How many pages should the tab cycle through after the redirect offset before deactivating the icon + - Default: 1 + 1 @@ -89,53 +90,53 @@ - Activate password generator. + - For all password-fields there will be an icon added to generate a new password. + - It is generated by KeePassXC with the profile for automatically generated passwords for new entries. + - Automatically retrieve credentials. + - KeePassXC-Browser will immediately retrieve the credentials when the tab is activated. + - Automatically fill-in single credentials entry. + - If KeePassXC-Browser does only receive a single entry from KeePassXC it automatically fills this credentials into the found credential fields. - Warning! Using auto-fill is not safe. Use at your own risk. + + - Activate autocomplete for username fields. + - For all username fields on a page a dropdown list appears which contains all available credentials. + - Show notifications. + - Show notifications for errors and when user interaction is required. + - Show a notification when new credentials can be saved to the database. + @@ -143,44 +144,43 @@ - Save domain only. + - When saving new credentials save only the domain instead of full URL. + - Check for updates of KeePassXC: - - every 3 days - every week - every month - never + + + + + - KeePassXC-Browser needs KeePassXC to retrieve credentials. + - You can download the latest stable version from here: https://keepassxc.org/ + - You are running KeePassXC version: + - Latest available version of KeePassXC: - Check for updates + + - Auto fill HTTP Auth dialogs and send them. + - If credentials are found for a page and the login-type is an HTTP Auth request, KeePassXC-Browser tries to login with the first given credentials. + - An HTTP Auth dialog looks like this: + @@ -189,36 +189,34 @@ - Connected databases + - - The following KeePassXC databases are connected to KeePassXC-Browser. - + - Identifier - Key - Last used - Created - Delete + + + + + - No connected databases found. + - Remove + - Connect + @@ -226,15 +224,16 @@ × - Remove identifier from database list? + - Do you really want to remove the identifier from the database list? - You can reconnect your database at any time. + + + @@ -243,29 +242,29 @@ - Custom credential fields + - If KeePassXC-Browser detects the wrong credential fields, you are able to specify the correct fields by yourself. + - Go to the page and click on the KeePassXC-Browser-Icon, now select Choose custom credential fields for this page. + . - On this page you can manage saved custom credential fields. + - Page URL - Delete + + - No saved custom credential fields found. + - Remove + @@ -274,15 +273,15 @@ × - Remove saved custom credential fields? + - DDo you really want to remove the saved custom credential fields on the page: ? - KeePassXC-Browser will automatically detect the credential fields next time you visit this page. + + @@ -291,24 +290,24 @@ - Site preferences + - Sites on this page have special handling methods associated with them. + - To ignore new/modified credentials on a specific site, add it below or click the blinking KeePassXC-Browser icon and select Never ask for this page. + . - If a site is fully ignored (Disable all features is selected), then the plugin will do nothing when visiting that site. + - Enabling the Username-Only Detection feature allows KeePassXC-Browser to fill-in pages on sites that do present a separated username and password input. + - Add URL manually: + - Add + @@ -316,27 +315,27 @@ - Page URL - Ignore - Username-Only Detection - Delete + + + + - No sites found. + - Enable all features - Disable new/modified credentials - Disable all features + + + - Remove + @@ -345,15 +344,15 @@ × - Remove site? + - Do you really want to remove the specified site from the list? - KeePassXC-Browser will enable all features for this site and remove username-only detection. + + @@ -362,30 +361,30 @@ - About + - Visit extension's page in Chrome Web store + - Visit extension's page in Mozilla's Add-ons page + - Visit project's page in GitHub. + . - KeePassXC-Browser Version: + - KeePassXC Version: + - Contributors: + Perry Nguyen, Lukas Schulze, - Andy Brandt, Sami Vänttinen - and KeePassXC Team. + Andy Brandt, Sami Vänttinen, + KeePassXC Team. @@ -393,5 +392,7 @@ (C) 2017-2018 - KeePassXC Team + + diff --git a/keepassxc-browser/options/options.js b/keepassxc-browser/options/options.js index 58e582b..bccce52 100644 --- a/keepassxc-browser/options/options.js +++ b/keepassxc-browser/options/options.js @@ -118,6 +118,18 @@ options.initGeneralSettings = function() { $('#blinkMinTimeout').val(options.settings['blinkMinTimeout']); $('#allowedRedirect').val(options.settings['allowedRedirect']); + browser.commands.getAll().then(function(commands) { + commands.forEach(function(command) { + var shortcut = document.getElementById(`${command.name}-shortcut`); + if (!shortcut) return; + shortcut.textContent = command.shortcut || 'not configured'; + }); + }); + + $('#configureCommands').click(function(){ + browser.tabs.create({ url: 'chrome://extensions/configureCommands' }); + }); + $('#blinkTimeoutButton').click(function(){ const blinkTimeout = $.trim($('#blinkTimeout').val()); const blinkTimeoutval = blinkTimeout !== '' ? Number(blinkTimeout) : defaultSettings.blinkTimeout; @@ -156,6 +168,10 @@ options.showKeePassXCVersions = function(response) { $('#tab-general-settings button.checkUpdateKeePassXC:first').attr('disabled', false); }; +options.getPartiallyHiddenKey = function(key) { + return !key ? 'Error' : (key.substr(0, 8) + '*'.repeat(10)); +}; + options.initConnectedDatabases = function() { $('#dialogDeleteConnectedDatabase').modal({keyboard: true, show: false, backdrop: true}); $('#tab-connected-databases tr.clone:first button.delete:first').click(function(e) { @@ -193,7 +209,7 @@ options.initConnectedDatabases = function() { $('a.dropdown-toggle:first img:first', tr).attr('src', '/icons/19x19/icon_normal_19x19.png'); tr.children('td:first').text(options.keyRing[hash].id); - tr.children('td:eq(1)').text(options.keyRing[hash].key); + tr.children('td:eq(1)').text(options.getPartiallyHiddenKey(options.keyRing[hash].key)); const lastUsed = (options.keyRing[hash].lastUsed) ? new Date(options.keyRing[hash].lastUsed).toLocaleString() : 'unknown'; tr.children('td:eq(2)').text(lastUsed); const date = (options.keyRing[hash].created) ? new Date(options.keyRing[hash].created).toLocaleDateString() : 'unknown'; @@ -310,7 +326,7 @@ options.initSitePreferences = function() { trClone.removeClass('clone'); // Fills the last / char if needed. This ensures the compatibility with Match Patterns - if (options.slashNeededForUrl(value)) { + if (slashNeededForUrl(value)) { value += '/'; } @@ -366,7 +382,7 @@ options.initSitePreferences = function() { $('#tab-site-preferences table tbody:first').append(tr); } } - + if ($('#tab-site-preferences table tbody:first tr').length > 2) { $('#tab-site-preferences table tbody:first tr.empty:first').hide(); } else { @@ -379,22 +395,4 @@ options.initAbout = function() { if (isFirefox()) { $('#chrome-only').remove(); } - - if (navigator.platform === 'MacIntel') { - $('#default-user-shortcut').hide(); - $('#default-pass-shortcut').hide(); - $('#mac-user-shortcut').show(); - $('#mac-pass-shortcut').show(); - } else { - $('#mac-user-shortcut').hide(); - $('#mac-pass-shortcut').hide(); - $('#default-user-shortcut').show(); - $('#default-pass-shortcut').show(); - } -}; - -// Checks if URL has only scheme and host without the last / char. -options.slashNeededForUrl = function(pattern) { - const matchPattern = new RegExp(`^${schemeSegment}://${hostSegment}$`); - return matchPattern.exec(pattern); }; diff --git a/keepassxc-browser/popups/popup.css b/keepassxc-browser/popups/popup.css index 4902487..3827486 100644 --- a/keepassxc-browser/popups/popup.css +++ b/keepassxc-browser/popups/popup.css @@ -4,6 +4,9 @@ body { background-color: #eee; font-size: 15px; padding: 8px; + min-width: 440px; + max-width: 460px; + width: auto; } .container { min-width: 440px; @@ -59,3 +62,11 @@ body { float: right; height: 30px; } +#login-filter { + outline:none; + border-radius: 4px 0 0 4px; + border: 1px solid #ccc; + margin-bottom: 5px; + padding: 2px 10px; + width: 100%; +} \ No newline at end of file diff --git a/keepassxc-browser/popups/popup.html b/keepassxc-browser/popups/popup.html index d049a0a..496bb84 100644 --- a/keepassxc-browser/popups/popup.html +++ b/keepassxc-browser/popups/popup.html @@ -1,6 +1,6 @@ - KeePassXC - Popup + @@ -14,86 +14,70 @@ - Settings - Choose custom credential fields for this page + + - You use an old version of KeePassXC. + - Please download the latest version from keepassxc.org. + . - Checking status... + - - KeePassXC-Browser has not been configured. - Press the connect button to register and pair with KeePassXC. - + - Connect + - - KeePassXC-Browser has been disconnected from KeePassXC. - - - - Press the reconnect button to establish a new connection. + + + + - Reconnect + - - KeePassXC-Browser has been configured using the identifier - and has not yet - connected to KeePassXC. - + - - KeePassXC-Browser has been configured using the identifier - "" and is successfully - connected to KeePassXC. - + - Redetect credential fields + - - KeePassXC-Browser has encountered an error: - + - Reload + - - KeePassXC-Browser has encountered an error: - + - Reopen database + + diff --git a/keepassxc-browser/popups/popup_httpauth.html b/keepassxc-browser/popups/popup_httpauth.html index 0adeec7..2a1c773 100644 --- a/keepassxc-browser/popups/popup_httpauth.html +++ b/keepassxc-browser/popups/popup_httpauth.html @@ -14,26 +14,25 @@ - Settings - Choose custom credential fields for this page + + - You use an old version of KeePassXC. + - Please download the latest version from keepassxc.org. + . + - - - Select the login information you would like to get logged in with: - + - Dismiss and show the default authentication dialog +
- If you just want to insert username + password into the fields where your focus is, press Ctrl + Shift + UAlt + Shift + U. - - If you only want to insert the password, just press Ctrl + Shift + IAlt + Shift + I. - You can customize these shortcuts on chrome://extensions/configureCommands page + : error + : error + : error
Ctrl + Shift + UAlt + Shift + U
Ctrl + Shift + IAlt + Shift + I
chrome://extensions/configureCommands
- Check for updates of KeePassXC: - - every 3 days - every week - every month - never +
- The following KeePassXC databases are connected to KeePassXC-Browser. -
Do you really want to remove the identifier from the database list?
You can reconnect your database at any time.
- If KeePassXC-Browser detects the wrong credential fields, you are able to specify the correct fields by yourself. + - Go to the page and click on the KeePassXC-Browser-Icon, now select Choose custom credential fields for this page. + . - On this page you can manage saved custom credential fields. +
DDo you really want to remove the saved custom credential fields on the page: ?
KeePassXC-Browser will automatically detect the credential fields next time you visit this page.
- Sites on this page have special handling methods associated with them. + - To ignore new/modified credentials on a specific site, add it below or click the blinking KeePassXC-Browser icon and select Never ask for this page. + . - If a site is fully ignored (Disable all features is selected), then the plugin will do nothing when visiting that site. + - Enabling the Username-Only Detection feature allows KeePassXC-Browser to fill-in pages on sites that do present a separated username and password input. +
Do you really want to remove the specified site from the list?
KeePassXC-Browser will enable all features for this site and remove username-only detection.
- Visit extension's page in Chrome Web store +
- Visit extension's page in Mozilla's Add-ons page +
- Visit project's page in GitHub. + .
- KeePassXC-Browser Version: +
- KeePassXC Version: +
- Contributors: + Perry Nguyen, Lukas Schulze, - Andy Brandt, Sami Vänttinen - and KeePassXC Team. + Andy Brandt, Sami Vänttinen, + KeePassXC Team.
(C) 2017-2018 - KeePassXC Team
Checking status...
- KeePassXC-Browser has not been configured. - Press the connect button to register and pair with KeePassXC. -
- KeePassXC-Browser has been disconnected from KeePassXC. -
- Press the reconnect button to establish a new connection. +
+
- KeePassXC-Browser has been configured using the identifier - and has not yet - connected to KeePassXC. -
- KeePassXC-Browser has been configured using the identifier - "" and is successfully - connected to KeePassXC. -
- KeePassXC-Browser has encountered an error: -
- Select the login information you would like to get logged in with: -
- Dismiss and show the default authentication dialog +
- Select the login information you would like to get entered into the page: -