diff --git a/keepassxc-browser/_locales/en/messages.json b/keepassxc-browser/_locales/en/messages.json index 3dc65dd..2a0780d 100644 --- a/keepassxc-browser/_locales/en/messages.json +++ b/keepassxc-browser/_locales/en/messages.json @@ -387,6 +387,10 @@ "message": "Add Username-only option for the site", "description": "Button text for adding Username-only option." }, + "popupAllowIframeButton": { + "message": "Allow Cross-Origin iframes for the site", + "description": "Button text for allowing Cross-Origin iframes option." + }, "popupErrorEncountered": { "message": "KeePassXC-Browser has encountered an error:", "description": "A text shown above error message in the popup." @@ -447,6 +451,10 @@ "message": "Only a single username field was detected. Add the URL to Site Preferences with Username-only option enabled?", "description": "Text shown when page can be added to Site Preferences with Username-only option enabled." }, + "popupIframeDetected": { + "message": "A Cross-Origin iframe was detected. Add the URL to Site Preferences with Allow Cross-Origin iframes option enabled?", + "description": "Text shown when page can be added to Site Preferences with Allow Cross-Origin iframes option enabled." + }, "rememberInfoText": { "message": "Username or password changed! Save it?", "description": "Message when username or password has changed." @@ -1007,6 +1015,10 @@ "message": "Improved Input Field Detection", "description": "Improved Input Field Detection column text." }, + "optionsColumnAllowIframes": { + "message": "Allow Cross-Origin iframes", + "description": "Allow iframes column text." + }, "optionsColumnDelete": { "message": "Delete", "description": "Site preferences list column title." @@ -1127,6 +1139,10 @@ "message": "Improved Input Field Detection allows more detailed dynamic input field detection. However, it might affect the page performance. Use with caution.", "description": "Improved Input Field Detection help text." }, + "optionsSitePreferencesAllowIframesHelpText": { + "message": "Allowing Cross-Origin iframes will enable credential retrieval for iframes from another domain. Use at your own risk.", + "description": "Allow Cross-Origin iframes help text." + }, "optionsSitePreferencesManualAddText": { "message": "Add URL manually", "description": "Label for adding site manually on Site preferences tab." diff --git a/keepassxc-browser/background/event.js b/keepassxc-browser/background/event.js index 583f8c4..ebb197b 100755 --- a/keepassxc-browser/background/event.js +++ b/keepassxc-browser/background/event.js @@ -26,18 +26,21 @@ kpxcEvent.showStatus = async function(tab, configured, internalPoll) { const errorMessage = page.tabs[tab.id]?.errorMessage ?? undefined; const usernameFieldDetected = page.tabs[tab.id]?.usernameFieldDetected ?? false; + const iframeDetected = page.tabs[tab.id]?.iframeDetected ?? false; return { - identifier: keyId, + associated: keepass.isAssociated(), + configured: configured, databaseClosed: keepass.isDatabaseClosed, - keePassXCAvailable: keepass.isKeePassXCAvailable, encryptionKeyUnrecognized: keepass.isEncryptionKeyUnrecognized, - associated: keepass.isAssociated(), error: errorMessage, - usernameFieldDetected: usernameFieldDetected, + iframeDetected: iframeDetected, + identifier: keyId, + keePassXCAvailable: keepass.isKeePassXCAvailable, showGettingStartedGuideAlert: page.settings.showGettingStartedGuideAlert, - showTroubleshootingGuideAlert: page.settings.showTroubleshootingGuideAlert + showTroubleshootingGuideAlert: page.settings.showTroubleshootingGuideAlert, + usernameFieldDetected: usernameFieldDetected }; }; @@ -177,6 +180,10 @@ kpxcEvent.onUsernameFieldDetected = async function(tab, detected) { page.tabs[tab.id].usernameFieldDetected = detected; }; +kpxcEvent.onIframeDetected = async function(tab, detected) { + page.tabs[tab.id].iframeDetected = detected; +}; + kpxcEvent.passwordGetFilled = async function() { return page.passwordFilled; }; @@ -251,6 +258,7 @@ kpxcEvent.messageHandlers = { 'get_totp': keepass.getTotp, 'hide_getting_started_guide_alert': kpxcEvent.hideGettingStartedGuideAlert, 'hide_troubleshooting_guide_alert': kpxcEvent.hideTroubleshootingGuideAlert, + 'iframe_detected': kpxcEvent.onIframeDetected, 'init_http_auth': kpxcEvent.initHttpAuth, 'is_connected': kpxcEvent.getIsKeePassXCAvailable, 'is_iframe_allowed': page.isIframeAllowed, @@ -264,6 +272,7 @@ kpxcEvent.messageHandlers = { 'page_get_manual_fill': page.getManualFill, 'page_get_redirect_count': kpxcEvent.pageGetRedirectCount, 'page_get_submitted': page.getSubmitted, + 'page_set_allow_iframes': page.setAllowIframes, 'page_set_autosubmit_performed': page.setAutoSubmitPerformed, 'page_set_login_id': page.setLoginId, 'page_set_manual_fill': page.setManualFill, diff --git a/keepassxc-browser/background/page.js b/keepassxc-browser/background/page.js index 5a0a49b..7035fb0 100755 --- a/keepassxc-browser/background/page.js +++ b/keepassxc-browser/background/page.js @@ -154,6 +154,7 @@ page.clearLogins = function(tabId) { return; } + page.tabs[tabId].allowIframes = false; page.tabs[tabId].credentials = []; page.tabs[tabId].loginList = []; page.currentRequest = {}; @@ -185,6 +186,7 @@ page.clearSubmittedCredentials = async function() { page.createTabEntry = function(tabId) { page.tabs[tabId] = { + allowIframes: false, credentials: [], errorMessage: null, loginList: [], @@ -329,10 +331,24 @@ page.updatePopup = function(tab) { browserAction.showDefault(tab); }; +page.setAllowIframes = async function(tab, args = []) { + const [ allowIframes, site ] = args; + + // Only set when main windows' URL is used + if (tab?.url === site) { + page.tabs[tab.id].allowIframes = allowIframes; + } +}; + page.isIframeAllowed = async function(tab, args = []) { const [ url, hostname ] = args; const baseDomain = await page.getBaseDomainFromUrl(hostname, url); + // Allow if exception has been set from Site Preferences + if (page.tabs[tab.id]?.allowIframes) { + return true; + } + // Allow iframe if the base domain is included in iframes' and tab's hostname const tabUrl = new URL(tab?.url); return hostname.endsWith(baseDomain) && tabUrl.hostname?.endsWith(baseDomain); diff --git a/keepassxc-browser/content/keepassxc-browser.js b/keepassxc-browser/content/keepassxc-browser.js index 9fd84d5..20a9b02 100755 --- a/keepassxc-browser/content/keepassxc-browser.js +++ b/keepassxc-browser/content/keepassxc-browser.js @@ -27,8 +27,8 @@ kpxc.singleInputEnabledForPage = false; kpxc.submitUrl = null; kpxc.url = null; -// Add page to Site Preferences with Username-only detection enabled. Set from the popup -kpxc.addToSitePreferences = async function() { +// Add page to Site Preferences with a selected option enabled. Set from the popup. +kpxc.addToSitePreferences = async function(optionName, addWildcard = false) { // Returns a predefined URL for certain sites let site = trimURL(window.top.location.href).toLowerCase(); @@ -37,24 +37,32 @@ kpxc.addToSitePreferences = async function() { for (const existingSite of kpxc.settings['sitePreferences']) { if (existingSite.url === site) { existingSite.ignore = IGNORE_NOTHING; - existingSite.usernameOnly = true; + existingSite[optionName] = true; siteExists = true; } } if (!siteExists) { // Add wildcard to the URL - site = site.slice(0, site.lastIndexOf('/') + 1) + '*'; + if (addWildcard) { + site = site.slice(0, site.lastIndexOf('/') + 1) + '*'; + } kpxc.settings['sitePreferences'].push({ url: site, ignore: IGNORE_NOTHING, - usernameOnly: true + [optionName]: true }); } await sendMessage('save_settings', kpxc.settings); - sendMessage('username_field_detected', false); + + if (optionName === 'allowIframes') { + await sendMessage('page_set_allow_iframes', [ true, site ]); + await sendMessage('iframe_detected', false); + } else if (optionName === 'usernameOnly') { + await sendMessage('username_field_detected', false); + } }; // Clears all from the content and background scripts, including autocomplete @@ -706,6 +714,7 @@ kpxc.siteIgnored = async function(condition) { currentLocation = window.self.location.href.toLowerCase(); } + // Refresh current settings for the site const currentSetting = condition || IGNORE_FULL; for (const site of kpxc.settings.sitePreferences) { if (siteMatch(site.url, currentLocation) || site.url === currentLocation) { @@ -715,6 +724,7 @@ kpxc.siteIgnored = async function(condition) { kpxc.singleInputEnabledForPage = site.usernameOnly; kpxc.improvedFieldDetectionEnabledForPage = site.improvedFieldDetection; + await sendMessage('page_set_allow_iframes', [ site.allowIframes, currentLocation ]); } } @@ -897,8 +907,10 @@ browser.runtime.onMessage.addListener(async function(req, sender) { if (req.action === 'activated_tab') { kpxc.triggerActivatedTab(); + } else if (req.action === 'add_allow_iframes_option') { + kpxc.addToSitePreferences('allowIframes'); } else if (req.action === 'add_username_only_option') { - kpxc.addToSitePreferences(); + kpxc.addToSitePreferences('usernameOnly', true); } else if (req.action === 'check_database_hash' && 'hash' in req) { kpxc.detectDatabaseChange(req); } else if (req.action === 'choose_credential_fields') { @@ -969,6 +981,7 @@ kpxc.reconnect = async function() { }; const isIframeAllowed = async function() { + sendMessage('iframe_detected', false); try { // Check for Cross-domain security error when inspecting window.top.location.href const currentLocation = window.top.location.href; @@ -980,8 +993,8 @@ const isIframeAllowed = async function() { return true; } - // TODO: Allow user to add a manual exception for iframes with this tab URL logDebug(`Error: Credential request ignored from another domain: ${window.self.location.host}`); + sendMessage('iframe_detected', true); return false; } }; diff --git a/keepassxc-browser/options/options.css b/keepassxc-browser/options/options.css index 77f3bd2..da74ffe 100644 --- a/keepassxc-browser/options/options.css +++ b/keepassxc-browser/options/options.css @@ -128,6 +128,7 @@ table tbody tr.empty:not(:nth-last-child(2)) { #tab-site-preferences td:nth-of-type(3), #tab-site-preferences td:nth-of-type(4), +#tab-site-preferences td:nth-of-type(5), table td:last-of-type { width: 1px; } diff --git a/keepassxc-browser/options/options.html b/keepassxc-browser/options/options.html index 10f4ede..25f546c 100644 --- a/keepassxc-browser/options/options.html +++ b/keepassxc-browser/options/options.html @@ -69,7 +69,7 @@ @@ -686,6 +686,7 @@