From 5e5c15883cabf638f99c57a55234bee44c00636a Mon Sep 17 00:00:00 2001 From: Sebastian Livoni <29739749+sebastianlivoni@users.noreply.github.com> Date: Sun, 23 Feb 2025 02:37:12 +0100 Subject: [PATCH 01/14] Safari Support --- build.js | 1 + dist/manifest_chromium.json | 56 ++--- dist/manifest_firefox.json | 4 +- dist/manifest_safari.json | 192 ++++++++++++++++++ keepassxc-browser/background/client.js | 7 + keepassxc-browser/background/keepass.js | 2 + keepassxc-browser/common/global.js | 5 + keepassxc-browser/content/banner.js | 2 +- .../content/custom-fields-banner.js | 2 +- keepassxc-browser/content/pwgen.js | 2 +- keepassxc-browser/content/totp-field.js | 2 +- keepassxc-browser/content/ui.js | 2 +- keepassxc-browser/content/username-field.js | 8 +- keepassxc-browser/css/banner.css | 18 ++ keepassxc-browser/css/notification.css | 10 + keepassxc-browser/css/pwgen.css | 5 + keepassxc-browser/css/totp.css | 5 + keepassxc-browser/css/username.css | 18 ++ keepassxc-browser/options/options.js | 6 + keepassxc-browser/popups/popup.css | 9 + keepassxc-browser/popups/popup_functions.js | 2 + 21 files changed, 320 insertions(+), 38 deletions(-) create mode 100644 dist/manifest_safari.json diff --git a/build.js b/build.js index c3b739050..67554d605 100755 --- a/build.js +++ b/build.js @@ -10,6 +10,7 @@ const DEFAULT = 'manifest_default.json'; const BROWSERS = { 'Firefox': 'manifest_firefox.json', 'Chromium': 'manifest_chromium.json', + 'Safari': 'manifest_safari.json' }; const getVersion = async () => { diff --git a/dist/manifest_chromium.json b/dist/manifest_chromium.json index 477cf6c67..1c6fdef89 100755 --- a/dist/manifest_chromium.json +++ b/dist/manifest_chromium.json @@ -33,7 +33,7 @@ "background": { "service_worker": "background/background_service.js" }, - "content_scripts": [ + "content_scripts": [ { "matches": [ "" @@ -129,30 +129,32 @@ "description": "__MSG_popupReloadButton__" } }, - "web_accessible_resources": [{ - "resources": [ - "icons/disconnected.svg", - "icons/help.svg", - "icons/keepassxc.svg", - "icons/key.svg", - "icons/locked.svg", - "icons/otp.svg", - "css/autocomplete.css", - "css/banner.css", - "css/button.css", - "css/colors.css", - "css/define.css", - "css/notification.css", - "css/pwgen.css", - "css/username.css", - "css/totp.css", - "content/passkeys.js" - ], - "matches": [ - "https://*/*", - "http://*/*" - ] - }], + "web_accessible_resources": [ + { + "resources": [ + "icons/disconnected.svg", + "icons/help.svg", + "icons/keepassxc.svg", + "icons/key.svg", + "icons/locked.svg", + "icons/otp.svg", + "css/autocomplete.css", + "css/banner.css", + "css/button.css", + "css/colors.css", + "css/define.css", + "css/notification.css", + "css/pwgen.css", + "css/username.css", + "css/totp.css", + "content/passkeys.js" + ], + "matches": [ + "https://*/*", + "http://*/*" + ] + } + ], "permissions": [ "activeTab", "clipboardWrite", @@ -177,7 +179,7 @@ "http://*/*" ], "storage": { - "managed_schema": "managed_storage.json" + "managed_schema": "managed_storage.json" }, "default_locale": "en" -} +} \ No newline at end of file diff --git a/dist/manifest_firefox.json b/dist/manifest_firefox.json index 8fa755761..0ca117b3e 100644 --- a/dist/manifest_firefox.json +++ b/dist/manifest_firefox.json @@ -183,7 +183,7 @@ } }, "storage": { - "managed_schema": "managed_storage.json" + "managed_schema": "managed_storage.json" }, "default_locale": "en" -} +} \ No newline at end of file diff --git a/dist/manifest_safari.json b/dist/manifest_safari.json new file mode 100644 index 000000000..fce4f77db --- /dev/null +++ b/dist/manifest_safari.json @@ -0,0 +1,192 @@ +{ + "manifest_version": 3, + "name": "KeePassXC-Browser", + "version": "1.9.7", + "version_name": "1.9.7", + "minimum_chrome_version": "93", + "description": "__MSG_extensionDescription__", + "author": "KeePassXC Team", + "action": { + "default_icon": { + "16": "icons/keepassxc_16x16.png", + "18": "icons/keepassxc_18x18.png", + "19": "icons/keepassxc_19x19.png", + "32": "icons/keepassxc_32x32.png", + "36": "icons/keepassxc_36x36.png", + "38": "icons/keepassxc_38x38.png", + "64": "icons/keepassxc_64x64.png" + }, + "default_title": "KeePassXC-Browser", + "default_popup": "popups/popup.html" + }, + "options_ui": { + "page": "options/options.html", + "open_in_tab": true + }, + "background": { + "scripts": [ + "common/browser-polyfill.min.js", + "common/global.js", + "common/sites.js", + "background/nacl.min.js", + "background/nacl-util.min.js", + "background/client.js", + "background/keepass.js", + "background/httpauth.js", + "background/offscreen.js", + "background/browserAction.js", + "background/page.js", + "background/event.js", + "background/init.js" + ] + }, + "content_scripts": [ + { + "matches": [ + "" + ], + "exclude_matches": [ + "*://*/*.xml*", + "file:///*.xml*" + ], + "js": [ + "common/browser-polyfill.min.js", + "common/global.js", + "common/sites.js", + "content/ui.js", + "content/banner.js", + "content/autocomplete.js", + "content/credential-autocomplete.js", + "content/custom-fields-banner.js", + "content/fields.js", + "content/fill.js", + "content/form.js", + "content/icons.js", + "content/keepassxc-browser.js", + "content/observer-helper.js", + "content/pwgen.js", + "content/totp-autocomplete.js", + "content/totp-field.js", + "content/username-field.js" + ], + "run_at": "document_idle", + "all_frames": true + }, + { + "matches": [ + "" + ], + "exclude_matches": [ + "*://*/*.xml*", + "file:///*.xml*" + ], + "js": [ + "content/passkeys-inject.js", + "content/passkeys-utils.js" + ], + "run_at": "document_start", + "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+P", + "mac": "MacCtrl+Shift+P" + } + }, + "fill_totp": { + "description": "__MSG_contextMenuFillTOTP__", + "suggested_key": { + "default": "Alt+Shift+O", + "mac": "MacCtrl+Shift+O" + } + }, + "show_password_generator": { + "description": "__MSG_contextMenuShowPasswordGenerator__", + "suggested_key": { + "default": "Alt+Shift+G", + "mac": "MacCtrl+Shift+G" + } + }, + "save_credentials": { + "description": "__MSG_contextMenuSaveCredentials__" + }, + "redetect_fields": { + "description": "__MSG_popupRedetectButton__" + }, + "choose_credential_fields": { + "description": "__MSG_popupChooseCredentialsText__" + }, + "retrive_credentials_forced": { + "description": "__MSG_popupReopenButton__" + }, + "request_autotype": { + "description": "__MSG_contextMenuRequestGlobalAutoType__" + }, + "reload_extension": { + "description": "__MSG_popupReloadButton__" + } + }, + "web_accessible_resources": [ + { + "resources": [ + "icons/disconnected.svg", + "icons/help.svg", + "icons/keepassxc.svg", + "icons/key.svg", + "icons/locked.svg", + "icons/otp.svg", + "css/autocomplete.css", + "css/banner.css", + "css/button.css", + "css/colors.css", + "css/define.css", + "css/notification.css", + "css/pwgen.css", + "css/username.css", + "css/totp.css", + "content/passkeys.js" + ], + "matches": [ + "https://*/*", + "http://*/*" + ] + } + ], + "permissions": [ + "activeTab", + "clipboardWrite", + "contextMenus", + "cookies", + "nativeMessaging", + "notifications", + "offscreen", + "storage", + "tabs", + "webNavigation", + "webRequest", + "webRequestAuthProvider", + "webRequestBlocking" + ], + "content_security_policy": { + "extension_pages": "script-src 'self'" + }, + "host_permissions": [ + "", + "https://*/*", + "http://*/*" + ], + "storage": { + "managed_schema": "managed_storage.json" + }, + "default_locale": "en" +} \ No newline at end of file diff --git a/keepassxc-browser/background/client.js b/keepassxc-browser/background/client.js index c98b01dec..109b4d3cd 100644 --- a/keepassxc-browser/background/client.js +++ b/keepassxc-browser/background/client.js @@ -180,6 +180,8 @@ keepassClient.sendNativeMessage = async function(request, enableTimeout = false, }; keepassClient.handleNativeMessage = async function(response) { + if (response?.name === "proxy_message") return // Ignoring KeePassXC App Broadcast Message + // Parse through the message buffer to find the corresponding Promise. await navigator.locks.request('messageBuffer', async (lock) => { const message = messageBuffer.getMessage(response); @@ -404,6 +406,11 @@ function onDisconnected() { } keepassClient.onNativeMessage = function(response) { + // Due to limitiations on SFSafariApplication.dispatchMessage this is needed + if (response?.name === 'proxy_message') { + response = response.userInfo + } + // Handle database lock/unlock status if (response.action === kpActions.DATABASE_LOCKED || response.action === kpActions.DATABASE_UNLOCKED) { keepass.updateDatabase(); diff --git a/keepassxc-browser/background/keepass.js b/keepassxc-browser/background/keepass.js index e4bf97938..8953766e3 100755 --- a/keepassxc-browser/background/keepass.js +++ b/keepassxc-browser/background/keepass.js @@ -16,6 +16,7 @@ keepass.cacheTimeout = 30 * 1000; // Milliseconds keepass.databaseHash = ''; keepass.previousDatabaseHash = ''; keepass.reconnectLoop = null; +keepass.isSafari = isSafari(); const kpActions = { SET_LOGIN: 'set-login', @@ -340,6 +341,7 @@ keepass.getDatabaseHash = async function(tab, args = []) { if (response.message && response.nonce) { const res = keepassClient.decrypt(response.message, response.nonce); if (!res) { + console.log(response) keepass.handleError(tab, kpErrors.CANNOT_DECRYPT_MESSAGE); return ''; } diff --git a/keepassxc-browser/common/global.js b/keepassxc-browser/common/global.js index d97a8a7c6..5230b4e78 100755 --- a/keepassxc-browser/common/global.js +++ b/keepassxc-browser/common/global.js @@ -34,6 +34,11 @@ const isEdge = function() { return navigator.userAgent.indexOf('Edg') !== -1; }; +const isSafari = function() { + return navigator.userAgent.indexOf('Safari') !== -1 + && (navigator.userAgent.indexOf('Chrome') === -1 || navigator.userAgent.indexOf('Chromium') === -1); +}; + const showNotification = function(message) { browser.notifications.create({ 'type': 'basic', diff --git a/keepassxc-browser/content/banner.js b/keepassxc-browser/content/banner.js index eb732a322..115d3dd5b 100644 --- a/keepassxc-browser/content/banner.js +++ b/keepassxc-browser/content/banner.js @@ -62,7 +62,7 @@ kpxcBanner.create = async function(credentials = {}) { const bannerInfo = kpxcUI.createElement('div', 'banner-info'); const bannerButtons = kpxcUI.createElement('div', 'banner-buttons'); - const className = (isFirefox() ? 'kpxc-banner-icon-moz' : 'kpxc-banner-icon'); + const className = (isSafari() ? 'kpxc-banner-icon-safari' : (isFirefox() ? 'kpxc-banner-icon-moz' : 'kpxc-banner-icon')); const icon = kpxcUI.createElement('span', className, { 'alt': 'logo' }); const infoText = kpxcUI.createElement('span', '', {}, tr('rememberInfoText')); diff --git a/keepassxc-browser/content/custom-fields-banner.js b/keepassxc-browser/content/custom-fields-banner.js index 299899c56..efa6c8350 100644 --- a/keepassxc-browser/content/custom-fields-banner.js +++ b/keepassxc-browser/content/custom-fields-banner.js @@ -90,7 +90,7 @@ kpxcCustomLoginFieldsBanner.create = async function() { const bannerInfo = kpxcUI.createElement('div', 'banner-info'); const bannerButtons = kpxcUI.createElement('div', 'banner-buttons'); - const iconClassName = isFirefox() ? 'kpxc-banner-icon-moz' : 'kpxc-banner-icon'; + const iconClassName = isSafari() ? 'kpxc-banner-icon-safari' : (isFirefox() ? 'kpxc-banner-icon-moz' : 'kpxc-banner-icon'); const icon = kpxcUI.createElement('span', iconClassName); const infoText = kpxcUI.createElement('span', '', {}, tr('defineChooseCustomLoginFieldText')); const separator = kpxcUI.createElement('div', 'kpxc-separator'); diff --git a/keepassxc-browser/content/pwgen.js b/keepassxc-browser/content/pwgen.js index 38ceb1675..20f84fc7e 100644 --- a/keepassxc-browser/content/pwgen.js +++ b/keepassxc-browser/content/pwgen.js @@ -49,7 +49,7 @@ PasswordIcon.prototype.initField = function(field) { }; PasswordIcon.prototype.createIcon = function(field) { - const className = (isFirefox() ? 'key-moz' : 'key'); + const className = isSafari() ? 'key-safari' : (isFirefox() ? 'key-moz' : 'key'); const size = (field.offsetHeight > 28) ? 24 : 16; const offset = kpxcUI.calculateIconOffset(field, size); diff --git a/keepassxc-browser/content/totp-field.js b/keepassxc-browser/content/totp-field.js index 2df744567..1bb0376a4 100644 --- a/keepassxc-browser/content/totp-field.js +++ b/keepassxc-browser/content/totp-field.js @@ -137,7 +137,7 @@ TOTPFieldIcon.prototype.initField = async function(field, segmented) { }; TOTPFieldIcon.prototype.createIcon = function(field, segmented = false) { - const className = (isFirefox() ? 'moz' : 'default'); + const className = (isSafari() ? 'safari' : (isFirefox() ? 'moz' : 'default')); // Size the icon dynamically, but not greater than 24 or smaller than 14 const size = Math.max(Math.min(24, field.offsetHeight - 4), 14); diff --git a/keepassxc-browser/content/ui.js b/keepassxc-browser/content/ui.js index 49379ce8a..db67e0b08 100644 --- a/keepassxc-browser/content/ui.js +++ b/keepassxc-browser/content/ui.js @@ -353,7 +353,7 @@ kpxcUI.createNotification = function(type, message) { const notification = kpxcUI.createElement('div', 'kpxc-notification kpxc-notification-' + type, {}); type = type.charAt(0).toUpperCase() + type.slice(1) + '!'; - const className = (isFirefox() ? 'kpxc-banner-icon-moz' : 'kpxc-banner-icon'); + const className = (isSafari() ? 'kpxc-banner-icon-safari' : (isFirefox() ? 'kpxc-banner-icon-moz' : 'kpxc-banner-icon')); const icon = kpxcUI.createElement('span', className, { 'alt': 'logo' }); const label = kpxcUI.createElement('span', 'kpxc-label', {}, type); const msg = kpxcUI.createElement('span', '', {}, message); diff --git a/keepassxc-browser/content/username-field.js b/keepassxc-browser/content/username-field.js index 9fe9fb761..db0bfba5c 100644 --- a/keepassxc-browser/content/username-field.js +++ b/keepassxc-browser/content/username-field.js @@ -44,7 +44,7 @@ class UsernameFieldIcon extends Icon { this.observer.disconnect(); } - this.icon.classList.remove('lock', 'lock-moz', 'unlock', 'unlock-moz', 'disconnected', 'disconnected-moz'); + this.icon.classList.remove('lock', 'lock-moz', 'lock-safari', 'unlock', 'unlock-moz', 'unlock-safari', 'disconnected', 'disconnected-moz', 'disconnected-safari'); this.icon.classList.add(getIconClassName(state)); this.icon.title = getIconText(state); @@ -134,12 +134,12 @@ const iconClicked = async function(field, icon) { const getIconClassName = function(state = DatabaseState.UNLOCKED) { if (state === DatabaseState.LOCKED) { - return (isFirefox() ? 'lock-moz' : 'lock'); + return (isSafari() ? 'lock-safari' : isFirefox() ? 'lock-moz' : 'lock'); } else if (state === DatabaseState.DISCONNECTED) { - return (isFirefox() ? 'disconnected-moz' : 'disconnected'); + return (isSafari() ? 'disconnected-safari' : isFirefox() ? 'disconnected-moz' : 'disconnected'); } - return (isFirefox() ? 'unlock-moz' : 'unlock'); + return (isSafari() ? 'unlock-safari' : isFirefox() ? 'unlock-moz' : 'unlock'); }; const getIconText = function(state) { diff --git a/keepassxc-browser/css/banner.css b/keepassxc-browser/css/banner.css index 797952d79..f78889c15 100644 --- a/keepassxc-browser/css/banner.css +++ b/keepassxc-browser/css/banner.css @@ -77,6 +77,16 @@ div.kpxc-banner .kpxc-banner-icon-moz { background-size: contain; } +div.kpxc-banner .kpxc-banner-icon-safari { + width: 24px; + height: 24px; + overflow: hidden; + + + background: url('safari-web-extension://__MSG_@@extension_id__/icons/keepassxc.svg') right no-repeat; + background-size: contain; +} + div.kpxc-banner .kpxc-help-icon { width: 24px; height: 24px; @@ -93,6 +103,14 @@ div.kpxc-banner .kpxc-help-icon-moz { background-size: contain; } +div.kpxc-banner .kpxc-help-icon-safari { + width: 24px; + height: 24px; + overflow: hidden; + background: url('safari-web-extension://__MSG_@@extension_id__/icons/help.svg') right no-repeat; + background-size: contain; +} + .kpxc-separator { border-left: 1px solid #ccc; height: 100% !important; diff --git a/keepassxc-browser/css/notification.css b/keepassxc-browser/css/notification.css index 1bb815f33..7cebdeaf0 100644 --- a/keepassxc-browser/css/notification.css +++ b/keepassxc-browser/css/notification.css @@ -43,6 +43,16 @@ background-size: contain; } +.kpxc-notification .kpxc-banner-icon-safari { + width: 24px; + height: 24px; + padding: 10px; + margin-right: 4px; + overflow: hidden; + background: url('safari-web-extension://__MSG_@@extension_id__/icons/keepassxc.svg') right no-repeat; + background-size: contain; +} + .kpxc-notification .kpxc-label { font-weight: bold; } diff --git a/keepassxc-browser/css/pwgen.css b/keepassxc-browser/css/pwgen.css index 64b574dac..6ddf55a14 100644 --- a/keepassxc-browser/css/pwgen.css +++ b/keepassxc-browser/css/pwgen.css @@ -12,3 +12,8 @@ background: url('moz-extension://__MSG_@@extension_id__/icons/key.svg') right no-repeat; background-size: contain; } + +.kpxc-pwgen-icon.key-safari { + background: url('safari-web-extension://__MSG_@@extension_id__/icons/key.svg') right no-repeat; + background-size: contain; +} \ No newline at end of file diff --git a/keepassxc-browser/css/totp.css b/keepassxc-browser/css/totp.css index 828113267..380815c3f 100644 --- a/keepassxc-browser/css/totp.css +++ b/keepassxc-browser/css/totp.css @@ -11,4 +11,9 @@ .kpxc-totp-icon.moz { background: url('moz-extension://__MSG_@@extension_id__/icons/otp.svg') right no-repeat; background-size: contain; +} + +.kpxc-totp-icon.safari { + background: url('safari-web-extension://__MSG_@@extension_id__/icons/otp.svg') right no-repeat; + background-size: contain; } \ No newline at end of file diff --git a/keepassxc-browser/css/username.css b/keepassxc-browser/css/username.css index 6b2e32af6..69cfad25d 100644 --- a/keepassxc-browser/css/username.css +++ b/keepassxc-browser/css/username.css @@ -13,6 +13,11 @@ background-size: contain; } +.kpxc-username-icon.disconnected-safari { + background: url('safari-web-extension://__MSG_@@extension_id__/icons/disconnected.svg') right no-repeat; + background-size: contain; +} + .kpxc-username-icon.lock { background: url('chrome-extension://__MSG_@@extension_id__/icons/locked.svg') right no-repeat; background-size: contain; @@ -23,6 +28,13 @@ background-size: contain; } +.kpxc-username-icon.lock-safari { + background: url('safari-web-extension://__MSG_@@extension_id__/icons/locked.svg') right no-repeat; + + + background-size: contain; +} + .kpxc-username-icon.unlock { background: url('chrome-extension://__MSG_@@extension_id__/icons/keepassxc.svg') right no-repeat; background-size: contain; @@ -32,3 +44,9 @@ background: url('moz-extension://__MSG_@@extension_id__/icons/keepassxc.svg') right no-repeat; background-size: contain; } + + +.kpxc-username-icon.unlock-safari { + background: url('safari-web-extension://__MSG_@@extension_id__/icons/keepassxc.svg') right no-repeat; + background-size: contain; +} \ No newline at end of file diff --git a/keepassxc-browser/options/options.js b/keepassxc-browser/options/options.js index 3009696df..2e958bf73 100644 --- a/keepassxc-browser/options/options.js +++ b/keepassxc-browser/options/options.js @@ -718,6 +718,12 @@ const getBrowserId = function() { startPos = navigator.userAgent.indexOf('/', startPos) + 1; const version = navigator.userAgent.substring(startPos, navigator.userAgent.indexOf('Safari')); return 'Chrome/Chromium ' + version; + } else if (navigator.userAgent.indexOf('Safari') > -1) { + let startPos = navigator.userAgent.indexOf('Version'); + + startPos = navigator.userAgent.indexOf('/', startPos) + 1; + const version = navigator.userAgent.substring(startPos, navigator.userAgent.indexOf('Safari')); + return 'Safari ' + version; } return 'Other/Unknown'; diff --git a/keepassxc-browser/popups/popup.css b/keepassxc-browser/popups/popup.css index 3f0929fea..8e38231a7 100644 --- a/keepassxc-browser/popups/popup.css +++ b/keepassxc-browser/popups/popup.css @@ -153,6 +153,15 @@ code { width: 2.5rem; } +#choose-custom-login-fields-button-safari { + background-image: url('safari-web-extension://__MSG_@@extension_id__/icons/custom_login_fields.svg'); + background-position: center; + background-repeat: no-repeat; + background-size: 70%; + height: 31px; + width: 2.5rem; +} + #lock-database-button { display: none; width: 2.5rem; diff --git a/keepassxc-browser/popups/popup_functions.js b/keepassxc-browser/popups/popup_functions.js index 82ad4543a..6cf135658 100644 --- a/keepassxc-browser/popups/popup_functions.js +++ b/keepassxc-browser/popups/popup_functions.js @@ -18,6 +18,8 @@ async function initSettings() { const customLoginFieldsButton = document.body.querySelector('#settings #choose-custom-login-fields-button'); if (isFirefox()) { customLoginFieldsButton.id = 'choose-custom-login-fields-button-moz'; + } else if (isSafari()) { + customLoginFieldsButton.id = 'choose-custom-login-fields-button-safari'; } customLoginFieldsButton.addEventListener('click', async () => { From 919ccec2b9696e98e61e21e88f638a03781f6799 Mon Sep 17 00:00:00 2001 From: Sebastian Livoni <29739749+sebastianlivoni@users.noreply.github.com> Date: Wed, 9 Apr 2025 19:13:04 +0200 Subject: [PATCH 02/14] Remove webrequest from Safari manifest due to Hide Autofill HTTP Auth dialogs option being unavailable in Safari --- dist/manifest_safari.json | 6 +----- keepassxc-browser/background/httpauth.js | 6 ++++++ keepassxc-browser/options/options.html | 5 +++++ keepassxc-browser/options/options.js | 10 ++++++++++ 4 files changed, 22 insertions(+), 5 deletions(-) diff --git a/dist/manifest_safari.json b/dist/manifest_safari.json index fce4f77db..ed79d8737 100644 --- a/dist/manifest_safari.json +++ b/dist/manifest_safari.json @@ -171,11 +171,7 @@ "notifications", "offscreen", "storage", - "tabs", - "webNavigation", - "webRequest", - "webRequestAuthProvider", - "webRequestBlocking" + "tabs" ], "content_security_policy": { "extension_pages": "script-src 'self'" diff --git a/keepassxc-browser/background/httpauth.js b/keepassxc-browser/background/httpauth.js index 4b5e61c82..687e19c1e 100755 --- a/keepassxc-browser/background/httpauth.js +++ b/keepassxc-browser/background/httpauth.js @@ -6,6 +6,12 @@ httpAuth.requests = []; httpAuth.pendingCallbacks = []; httpAuth.init = function() { + if (isSafari()) { + // browser.webRequest is not supported in Safari Web Extensions. + logError('httpAuth.init error: Autofill HTTP Auth dialogs is unsupported in Safari'); + return; + } + let handleReq = httpAuth.handleRequestPromise; let reqType = 'blocking'; diff --git a/keepassxc-browser/options/options.html b/keepassxc-browser/options/options.html index 3e3769c48..8c3f804f5 100644 --- a/keepassxc-browser/options/options.html +++ b/keepassxc-browser/options/options.html @@ -276,6 +276,11 @@

+ + diff --git a/keepassxc-browser/options/options.js b/keepassxc-browser/options/options.js index 2e958bf73..b536ad589 100644 --- a/keepassxc-browser/options/options.js +++ b/keepassxc-browser/options/options.js @@ -98,9 +98,19 @@ options.initGeneralSettings = async function() { $('#defaultGroupButtonReset').disabled = true; } + // Autofill HTTP Auth dialogs is unsupported in Safari Web Extensions. + if (checkbox.name === 'autoFillAndSend' && isSafari()) { + checkbox.disabled = true; + } + checkbox.addEventListener('click', changeCheckboxValue); } + // Hide the Safari-only alert on non-Safari browsers + if (!isSafari()) { + $('.safari-only').hide() + } + $('#tab-general-settings input[type=radio]#checkUpdateThreeDays').value = CHECK_UPDATE_THREE_DAYS; $('#tab-general-settings input[type=radio]#checkUpdateOneWeek').value = CHECK_UPDATE_ONE_WEEK; $('#tab-general-settings input[type=radio]#checkUpdateOneMonth').value = CHECK_UPDATE_ONE_MONTH; From 142eb6f0e4707cec85e0f5e6a89b69b93f865f7f Mon Sep 17 00:00:00 2001 From: Sebastian Livoni <29739749+sebastianlivoni@users.noreply.github.com> Date: Wed, 9 Apr 2025 19:35:38 +0200 Subject: [PATCH 03/14] Hide updates tab in options when using Safari Web Extension --- keepassxc-browser/options/options.html | 2 +- keepassxc-browser/options/options.js | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/keepassxc-browser/options/options.html b/keepassxc-browser/options/options.html index 960b71eb9..aeb8f9b2c 100644 --- a/keepassxc-browser/options/options.html +++ b/keepassxc-browser/options/options.html @@ -405,7 +405,7 @@

-
+
diff --git a/keepassxc-browser/options/options.js b/keepassxc-browser/options/options.js index 723b4d7ea..ad6fdd5a9 100644 --- a/keepassxc-browser/options/options.js +++ b/keepassxc-browser/options/options.js @@ -109,6 +109,8 @@ options.initGeneralSettings = async function() { // Hide the Safari-only alert on non-Safari browsers if (!isSafari()) { $('.safari-only').hide() + } else { + $('.hide-safari').hide() } $('#tab-general-settings input[type=radio]#checkUpdateThreeDays').value = CHECK_UPDATE_THREE_DAYS; From febd61bb551bc4c53fc3cfcdbca6ab347b6b6cf7 Mon Sep 17 00:00:00 2001 From: Sebastian Livoni <29739749+sebastianlivoni@users.noreply.github.com> Date: Wed, 9 Apr 2025 22:09:19 +0200 Subject: [PATCH 04/14] Add option for only building and output safari web extension --- build.js | 18 +++++++++++++++++- keepassxc-browser/options/options.js | 2 +- 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/build.js b/build.js index 67554d605..4f39ecbc6 100755 --- a/build.js +++ b/build.js @@ -55,6 +55,10 @@ const createZipFile = async (fileName, path) => { const version = await getVersion(); for (const browser in BROWSERS) { + if (params.includes('--safari-web-extension') && browser != 'Safari') { + continue; + } + console.log(`KeePassXC-Browser: Creating extension package for ${browser}`); const fileName = await getDestinationFilename(BROWSERS[browser], version); @@ -65,7 +69,19 @@ const createZipFile = async (fileName, path) => { await fs.rm(fileName); } - await createZipFile(fileName, DEST); + if (params.includes('--safari-web-extension') && browser == 'Safari' && params.includes('-o')) { + let outputPath = params[params.indexOf('-o') + 1] + + if (!outputPath) { + console.error("No ouput path specified!"); + return; + } + + await fs.cpSync(DEST, outputPath, { recursive: true }) + } else { + await createZipFile(fileName, DEST); + } + console.log('Done'); } diff --git a/keepassxc-browser/options/options.js b/keepassxc-browser/options/options.js index ad6fdd5a9..7711a1da9 100644 --- a/keepassxc-browser/options/options.js +++ b/keepassxc-browser/options/options.js @@ -214,7 +214,7 @@ options.initGeneralSettings = async function() { $('#configureCommands').addEventListener('click', function() { const scheme = isEdge() ? 'edge' : 'chrome'; browser.tabs.create({ - url: isFirefox() ? browser.runtime.getURL('options/shortcuts.html') : `${scheme}://extensions/shortcuts` + url: (isFirefox() || isSafari()) ? browser.runtime.getURL('options/shortcuts.html') : `${scheme}://extensions/shortcuts` }); }); From 84d024f37819733ef827f57a660e93a589914ac4 Mon Sep 17 00:00:00 2001 From: Sebastian Livoni <29739749+sebastianlivoni@users.noreply.github.com> Date: Fri, 11 Apr 2025 15:05:02 +0200 Subject: [PATCH 05/14] Hiding unsupported features in Safari --- keepassxc-browser/background/httpauth.js | 2 +- keepassxc-browser/options/options.html | 12 +++++++++--- keepassxc-browser/options/options.js | 18 ++++++++++-------- 3 files changed, 20 insertions(+), 12 deletions(-) diff --git a/keepassxc-browser/background/httpauth.js b/keepassxc-browser/background/httpauth.js index 687e19c1e..264f40376 100755 --- a/keepassxc-browser/background/httpauth.js +++ b/keepassxc-browser/background/httpauth.js @@ -8,7 +8,7 @@ httpAuth.pendingCallbacks = []; httpAuth.init = function() { if (isSafari()) { // browser.webRequest is not supported in Safari Web Extensions. - logError('httpAuth.init error: Autofill HTTP Auth dialogs is unsupported in Safari'); + logError('httpAuth.init error: Autofill HTTP Auth dialogs is not supported in Safari'); return; } diff --git a/keepassxc-browser/options/options.html b/keepassxc-browser/options/options.html index aeb8f9b2c..b967a53af 100644 --- a/keepassxc-browser/options/options.html +++ b/keepassxc-browser/options/options.html @@ -166,12 +166,17 @@

: error
: error

-

+

+ +
@@ -282,8 +287,8 @@

- @@ -874,6 +879,7 @@


Lukas Schulze
Stefan Sundin
dynobo +
Sebastian Livoni

diff --git a/keepassxc-browser/options/options.js b/keepassxc-browser/options/options.js index 7711a1da9..ee8891047 100644 --- a/keepassxc-browser/options/options.js +++ b/keepassxc-browser/options/options.js @@ -98,7 +98,7 @@ options.initGeneralSettings = async function() { $('#defaultGroupButtonReset').disabled = true; } - // Autofill HTTP Auth dialogs is unsupported in Safari Web Extensions. + // Autofill HTTP Auth dialogs is not supported in Safari Web Extensions. if (checkbox.name === 'autoFillAndSend' && isSafari()) { checkbox.disabled = true; } @@ -106,13 +106,6 @@ options.initGeneralSettings = async function() { checkbox.addEventListener('click', changeCheckboxValue); } - // Hide the Safari-only alert on non-Safari browsers - if (!isSafari()) { - $('.safari-only').hide() - } else { - $('.hide-safari').hide() - } - $('#tab-general-settings input[type=radio]#checkUpdateThreeDays').value = CHECK_UPDATE_THREE_DAYS; $('#tab-general-settings input[type=radio]#checkUpdateOneWeek').value = CHECK_UPDATE_ONE_WEEK; $('#tab-general-settings input[type=radio]#checkUpdateOneMonth').value = CHECK_UPDATE_ONE_MONTH; @@ -323,6 +316,9 @@ options.initGeneralSettings = async function() { siteListing.append(document.createElement('br')); } } + + // Show and hide Safari specific features + options.showHideSafariSelectors() }; // Also hides/disables any options with KeePassXC versions that are too old @@ -799,6 +795,12 @@ options.createWarning = function(elem, text) { }, 5000); }; +options.showHideSafariSelectors = function() { + let selector = isSafari() ? '.hide-safari' : '.show-safari' + + document.querySelectorAll(selector).forEach((elem) => elem.hide()) +} + const getBrowserId = function() { if (navigator.userAgent.indexOf('Firefox') > -1) { return 'Mozilla Firefox ' + navigator.userAgent.substr(navigator.userAgent.lastIndexOf('/') + 1); From 506976f7ec1b590f4edbe6f777caebf6d795fa11 Mon Sep 17 00:00:00 2001 From: Sebastian Livoni <29739749+sebastianlivoni@users.noreply.github.com> Date: Fri, 11 Apr 2025 15:30:50 +0200 Subject: [PATCH 06/14] Fix isSafari --- keepassxc-browser/common/global.js | 3 +-- keepassxc-browser/options/options.js | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/keepassxc-browser/common/global.js b/keepassxc-browser/common/global.js index 5230b4e78..430cc1b8d 100755 --- a/keepassxc-browser/common/global.js +++ b/keepassxc-browser/common/global.js @@ -35,8 +35,7 @@ const isEdge = function() { }; const isSafari = function() { - return navigator.userAgent.indexOf('Safari') !== -1 - && (navigator.userAgent.indexOf('Chrome') === -1 || navigator.userAgent.indexOf('Chromium') === -1); + return browser.runtime.getURL('').startsWith("safari") }; const showNotification = function(message) { diff --git a/keepassxc-browser/options/options.js b/keepassxc-browser/options/options.js index ee8891047..14c7a0bf8 100644 --- a/keepassxc-browser/options/options.js +++ b/keepassxc-browser/options/options.js @@ -207,7 +207,7 @@ options.initGeneralSettings = async function() { $('#configureCommands').addEventListener('click', function() { const scheme = isEdge() ? 'edge' : 'chrome'; browser.tabs.create({ - url: (isFirefox() || isSafari()) ? browser.runtime.getURL('options/shortcuts.html') : `${scheme}://extensions/shortcuts` + url: isFirefox() ? browser.runtime.getURL('options/shortcuts.html') : `${scheme}://extensions/shortcuts` }); }); From 69664b51f1dadad6c9b1d426bec9a26d5a69151d Mon Sep 17 00:00:00 2001 From: Sebastian Livoni <29739749+sebastianlivoni@users.noreply.github.com> Date: Fri, 11 Apr 2025 15:50:02 +0200 Subject: [PATCH 07/14] Change isSafari to use cached result and hide notifications option --- keepassxc-browser/common/global.js | 11 ++++++++--- keepassxc-browser/options/options.html | 2 +- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/keepassxc-browser/common/global.js b/keepassxc-browser/common/global.js index 430cc1b8d..0d4fb8830 100755 --- a/keepassxc-browser/common/global.js +++ b/keepassxc-browser/common/global.js @@ -34,9 +34,14 @@ const isEdge = function() { return navigator.userAgent.indexOf('Edg') !== -1; }; -const isSafari = function() { - return browser.runtime.getURL('').startsWith("safari") -}; +let cachedIsSafari = null; + +function isSafari() { + if (cachedIsSafari === null) { + cachedIsSafari = browser.runtime.getURL('').startsWith("safari"); + } + return cachedIsSafari; +} const showNotification = function(message) { browser.notifications.create({ diff --git a/keepassxc-browser/options/options.html b/keepassxc-browser/options/options.html index b967a53af..730b3e038 100644 --- a/keepassxc-browser/options/options.html +++ b/keepassxc-browser/options/options.html @@ -125,7 +125,7 @@

-
+
From bbba6db09e2aa8245dd1ee2b5555b4e310bad5cd Mon Sep 17 00:00:00 2001 From: Sebastian Livoni <29739749+sebastianlivoni@users.noreply.github.com> Date: Sat, 12 Apr 2025 00:16:26 +0200 Subject: [PATCH 08/14] Use single quotes ' instead of " --- build.js | 2 +- keepassxc-browser/background/client.js | 2 +- keepassxc-browser/common/global.js | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/build.js b/build.js index 4f39ecbc6..4314d5880 100755 --- a/build.js +++ b/build.js @@ -73,7 +73,7 @@ const createZipFile = async (fileName, path) => { let outputPath = params[params.indexOf('-o') + 1] if (!outputPath) { - console.error("No ouput path specified!"); + console.error('No ouput path specified!'); return; } diff --git a/keepassxc-browser/background/client.js b/keepassxc-browser/background/client.js index 109b4d3cd..37c2c89ca 100644 --- a/keepassxc-browser/background/client.js +++ b/keepassxc-browser/background/client.js @@ -180,7 +180,7 @@ keepassClient.sendNativeMessage = async function(request, enableTimeout = false, }; keepassClient.handleNativeMessage = async function(response) { - if (response?.name === "proxy_message") return // Ignoring KeePassXC App Broadcast Message + if (response?.name === 'proxy_message') return // Ignoring KeePassXC App Broadcast Message // Parse through the message buffer to find the corresponding Promise. await navigator.locks.request('messageBuffer', async (lock) => { diff --git a/keepassxc-browser/common/global.js b/keepassxc-browser/common/global.js index 0d4fb8830..af6800496 100755 --- a/keepassxc-browser/common/global.js +++ b/keepassxc-browser/common/global.js @@ -38,7 +38,7 @@ let cachedIsSafari = null; function isSafari() { if (cachedIsSafari === null) { - cachedIsSafari = browser.runtime.getURL('').startsWith("safari"); + cachedIsSafari = browser.runtime.getURL('').startsWith('safari'); } return cachedIsSafari; } From 84108c0324710e249b50c09de391bf65146e2314 Mon Sep 17 00:00:00 2001 From: Sebastian Livoni <29739749+sebastianlivoni@users.noreply.github.com> Date: Sat, 12 Apr 2025 00:33:46 +0200 Subject: [PATCH 09/14] Use locales instead of plain english text. --- keepassxc-browser/_locales/en/messages.json | 8 ++++++++ keepassxc-browser/options/options.html | 4 ++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/keepassxc-browser/_locales/en/messages.json b/keepassxc-browser/_locales/en/messages.json index 7a9e07835..2f09c8f81 100644 --- a/keepassxc-browser/_locales/en/messages.json +++ b/keepassxc-browser/_locales/en/messages.json @@ -1377,5 +1377,13 @@ "lockDatabase": { "message": "Lock database", "description": "Lock database button title text." + }, + "optionsUnsupportedKeyboardShortcutsSafari": { + "message": "Changing keyboard shortcuts is not supported in Safari.", + "description": "Info message about unsupported ability to change keyboard shortcuts in Safari." + }, + "optionsUnsupportedAutofillHTTPAuthDialogsSafari": { + "message": "Autofill HTTP Auth dialogs is not supported in Safari.", + "description": "Info message about autofilling HTTP Auth dialogs is unsupported in Safari." } } diff --git a/keepassxc-browser/options/options.html b/keepassxc-browser/options/options.html index 730b3e038..97e58d4dd 100644 --- a/keepassxc-browser/options/options.html +++ b/keepassxc-browser/options/options.html @@ -175,7 +175,7 @@

@@ -289,7 +289,7 @@

From 00959f28c306fdf774254bbf7fa3113ba1db73a7 Mon Sep 17 00:00:00 2001 From: Sebastian Livoni <29739749+sebastianlivoni@users.noreply.github.com> Date: Sat, 12 Apr 2025 00:35:21 +0200 Subject: [PATCH 10/14] Leave the empty line change at the end --- dist/manifest_chromium.json | 2 +- dist/manifest_firefox.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dist/manifest_chromium.json b/dist/manifest_chromium.json index abf3fbdd8..df0fdb56e 100755 --- a/dist/manifest_chromium.json +++ b/dist/manifest_chromium.json @@ -181,4 +181,4 @@ "managed_schema": "managed_storage.json" }, "default_locale": "en" -} \ No newline at end of file +} diff --git a/dist/manifest_firefox.json b/dist/manifest_firefox.json index bec39ff31..9eae09b9a 100644 --- a/dist/manifest_firefox.json +++ b/dist/manifest_firefox.json @@ -185,4 +185,4 @@ "managed_schema": "managed_storage.json" }, "default_locale": "en" -} \ No newline at end of file +} From dc041db87015e0ee6093593167e40f8225e17ee0 Mon Sep 17 00:00:00 2001 From: Sebastian Livoni <29739749+sebastianlivoni@users.noreply.github.com> Date: Sat, 12 Apr 2025 00:36:12 +0200 Subject: [PATCH 11/14] Remove debug console.log message --- keepassxc-browser/background/keepass.js | 1 - 1 file changed, 1 deletion(-) diff --git a/keepassxc-browser/background/keepass.js b/keepassxc-browser/background/keepass.js index 8953766e3..59e3b179f 100755 --- a/keepassxc-browser/background/keepass.js +++ b/keepassxc-browser/background/keepass.js @@ -341,7 +341,6 @@ keepass.getDatabaseHash = async function(tab, args = []) { if (response.message && response.nonce) { const res = keepassClient.decrypt(response.message, response.nonce); if (!res) { - console.log(response) keepass.handleError(tab, kpErrors.CANNOT_DECRYPT_MESSAGE); return ''; } From 39436f243eecca2e2b6e5dc7d674b7770eb88d49 Mon Sep 17 00:00:00 2001 From: Sebastian Livoni <29739749+sebastianlivoni@users.noreply.github.com> Date: Sat, 12 Apr 2025 09:29:32 +0200 Subject: [PATCH 12/14] strict comparisons --- build.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.js b/build.js index 4314d5880..517c53445 100755 --- a/build.js +++ b/build.js @@ -55,7 +55,7 @@ const createZipFile = async (fileName, path) => { const version = await getVersion(); for (const browser in BROWSERS) { - if (params.includes('--safari-web-extension') && browser != 'Safari') { + if (params.includes('--safari-web-extension') && browser !== 'Safari') { continue; } @@ -69,7 +69,7 @@ const createZipFile = async (fileName, path) => { await fs.rm(fileName); } - if (params.includes('--safari-web-extension') && browser == 'Safari' && params.includes('-o')) { + if (params.includes('--safari-web-extension') && browser === 'Safari' && params.includes('-o')) { let outputPath = params[params.indexOf('-o') + 1] if (!outputPath) { From d769f734ca7a6019fcc6209b8e8ee9c9941f57b5 Mon Sep 17 00:00:00 2001 From: Sebastian Livoni <29739749+sebastianlivoni@users.noreply.github.com> Date: Sat, 12 Apr 2025 09:30:15 +0200 Subject: [PATCH 13/14] add missing empty line --- keepassxc-browser/css/pwgen.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/keepassxc-browser/css/pwgen.css b/keepassxc-browser/css/pwgen.css index 6ddf55a14..6e148849c 100644 --- a/keepassxc-browser/css/pwgen.css +++ b/keepassxc-browser/css/pwgen.css @@ -16,4 +16,4 @@ .kpxc-pwgen-icon.key-safari { background: url('safari-web-extension://__MSG_@@extension_id__/icons/key.svg') right no-repeat; background-size: contain; -} \ No newline at end of file +} From 70656606db264da34154de820c23dcd8dabbdcd4 Mon Sep 17 00:00:00 2001 From: Sebastian Livoni <29739749+sebastianlivoni@users.noreply.github.com> Date: Sun, 8 Jun 2025 00:06:39 +0200 Subject: [PATCH 14/14] Update submodule --- dist/manifest_safari.json | 7 ++++--- keepassxc-browser/background/httpauth.js | 2 -- keepassxc-browser/background/page.js | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/dist/manifest_safari.json b/dist/manifest_safari.json index ed79d8737..897c6d0d8 100644 --- a/dist/manifest_safari.json +++ b/dist/manifest_safari.json @@ -1,8 +1,8 @@ { "manifest_version": 3, "name": "KeePassXC-Browser", - "version": "1.9.7", - "version_name": "1.9.7", + "version": "1.9.8", + "version_name": "1.9.8", "minimum_chrome_version": "93", "description": "__MSG_extensionDescription__", "author": "KeePassXC Team", @@ -171,7 +171,8 @@ "notifications", "offscreen", "storage", - "tabs" + "tabs", + "webNavigation" ], "content_security_policy": { "extension_pages": "script-src 'self'" diff --git a/keepassxc-browser/background/httpauth.js b/keepassxc-browser/background/httpauth.js index 264f40376..71c08c29f 100755 --- a/keepassxc-browser/background/httpauth.js +++ b/keepassxc-browser/background/httpauth.js @@ -7,8 +7,6 @@ httpAuth.pendingCallbacks = []; httpAuth.init = function() { if (isSafari()) { - // browser.webRequest is not supported in Safari Web Extensions. - logError('httpAuth.init error: Autofill HTTP Auth dialogs is not supported in Safari'); return; } diff --git a/keepassxc-browser/background/page.js b/keepassxc-browser/background/page.js index 5a9f8a030..e0be3b5ee 100755 --- a/keepassxc-browser/background/page.js +++ b/keepassxc-browser/background/page.js @@ -75,7 +75,7 @@ page.initSettings = async function() { } catch (err) { logError('page.initSettings error: ' + err); } - } else { + } else if (!isSafari()) { chrome.storage.managed.get('settings').then((managedSettings) => { if (managedSettings?.settings) { debugLogMessage('Managed settings found.');