mirror of
https://github.com/keepassxreboot/keepassxc-browser.git
synced 2026-03-11 08:54:43 +00:00
Merge pull request #2079 from keepassxreboot/feature/top-level_domain_check
Add support for TLD check and allowing Cross-Origin iframes option
This commit is contained in:
commit
d321dbf675
14 changed files with 248 additions and 44 deletions
|
|
@ -28,6 +28,7 @@ KeePassXC-Browser extension requests the following permissions:
|
|||
| ----- | ----- |
|
||||
| `activeTab` | To get URL of the current tab |
|
||||
| `contextMenus` | To show context menu items |
|
||||
| `cookies` | To access browser's internal Public Suffix List |
|
||||
| `clipboardWrite` | Allows password to be copied from password generator to clipboard |
|
||||
| `nativeMessaging` | Allows communication with KeePassXC application |
|
||||
| `notifications` | To show browser notifications |
|
||||
|
|
@ -42,7 +43,7 @@ KeePassXC-Browser extension requests the following permissions:
|
|||
|
||||
## Protocol
|
||||
|
||||
The details about the messaging protocol used with the browser extension and KeePassXC can be found [here](keepassxc-protocol.md).
|
||||
Check [keepassxc-protocol](keepassxc-protocol.md) for the details about the messaging protocol used between the browser extension and KeePassXC.
|
||||
|
||||
## Translations
|
||||
|
||||
|
|
|
|||
|
|
@ -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."
|
||||
|
|
@ -1015,6 +1023,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."
|
||||
|
|
@ -1135,6 +1147,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."
|
||||
|
|
|
|||
|
|
@ -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,8 +258,10 @@ 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,
|
||||
'load_keyring': kpxcEvent.onLoadKeyRing,
|
||||
'load_settings': kpxcEvent.onLoadSettings,
|
||||
'lock_database': kpxcEvent.lockDatabase,
|
||||
|
|
@ -263,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,
|
||||
|
|
|
|||
|
|
@ -582,7 +582,7 @@ keepass.requestAutotype = async function(tab, args = []) {
|
|||
|
||||
const kpAction = kpActions.REQUEST_AUTOTYPE;
|
||||
const nonce = keepassClient.getNonce();
|
||||
const search = getTopLevelDomainFromUrl(args[0]);
|
||||
const search = page.getTopLevelDomainFromUrl(args[0]);
|
||||
|
||||
const messageData = {
|
||||
action: kpAction,
|
||||
|
|
|
|||
|
|
@ -155,6 +155,7 @@ page.clearLogins = function(tabId) {
|
|||
return;
|
||||
}
|
||||
|
||||
page.tabs[tabId].allowIframes = false;
|
||||
page.tabs[tabId].credentials = [];
|
||||
page.tabs[tabId].loginList = [];
|
||||
page.currentRequest = {};
|
||||
|
|
@ -186,6 +187,7 @@ page.clearSubmittedCredentials = async function() {
|
|||
|
||||
page.createTabEntry = function(tabId) {
|
||||
page.tabs[tabId] = {
|
||||
allowIframes: false,
|
||||
credentials: [],
|
||||
errorMessage: null,
|
||||
loginList: [],
|
||||
|
|
@ -330,6 +332,101 @@ 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);
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the top level domain from URL.
|
||||
* @param {string} domain Current iframe's hostname
|
||||
* @param {string} url Current iframe's full URL
|
||||
* @returns {string} TLD e.g. https://another.example.co.uk -> co.uk
|
||||
*/
|
||||
page.getTopLevelDomainFromUrl = async function(domain, url) {
|
||||
// A simple check for IPv4 address. TLD cannot be numeric, and if hostname is just numbers, it's probably an IPv4.
|
||||
// TODO: Handle IPv6 addresses. Is there some internal API for these?
|
||||
if (!isNaN(Number(domain?.replaceAll('.', '')))) {
|
||||
return domain;
|
||||
}
|
||||
|
||||
// Only loop the amount of different domain parts found
|
||||
const numberOfDomainParts = domain?.split('.')?.length;
|
||||
for (let i = 0; i < numberOfDomainParts; ++i) {
|
||||
// Cut the first part from host
|
||||
const index = domain?.indexOf('.');
|
||||
if (index < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Check if dummy cookie's domain/TLD matches with public suffix list.
|
||||
// If setting the cookie fails, TLD has been found.
|
||||
try {
|
||||
domain = domain?.substring(index + 1);
|
||||
const reply = await browser.cookies.set({
|
||||
domain: domain,
|
||||
name: 'kpxc',
|
||||
sameSite: 'strict',
|
||||
url: url,
|
||||
value: ''
|
||||
});
|
||||
|
||||
// Delete the temporary cookie immediately
|
||||
if (reply) {
|
||||
await browser.cookies.remove({
|
||||
name: 'kpxc',
|
||||
url: url
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
return domain;
|
||||
}
|
||||
}
|
||||
|
||||
return domain;
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the base domain of URL or hostname.
|
||||
* Up-to-date list can be found: https://publicsuffix.org/list/public_suffix_list.dat
|
||||
* @param {string} domain Current iframe's hostname
|
||||
* @param {string} url Current iframe's full URL
|
||||
* @returns {string} The base domain, e.g. https://another.example.co.uk -> example.co.uk
|
||||
*/
|
||||
page.getBaseDomainFromUrl = async function(hostname, url) {
|
||||
const tld = await page.getTopLevelDomainFromUrl(hostname, url);
|
||||
if (tld.length === 0 || tld === hostname) {
|
||||
return hostname;
|
||||
}
|
||||
|
||||
// Remove the top level domain part from the hostname, e.g. https://another.example.co.uk -> https://another.example
|
||||
const finalDomain = hostname.slice(0, hostname.indexOf(tld) - 1);
|
||||
// Split the URL and select the last part, e.g. https://another.example -> example
|
||||
let baseDomain = finalDomain.split('.')?.at(-1);
|
||||
// Append the top level domain back to the URL, e.g. example -> example.co.uk
|
||||
baseDomain = baseDomain + '.' + tld;
|
||||
|
||||
return baseDomain;
|
||||
};
|
||||
|
||||
const createContextMenuItem = function({ action, args, ...options }) {
|
||||
return browser.contextMenus.create({
|
||||
contexts: menuContexts,
|
||||
|
|
@ -345,6 +442,7 @@ const createContextMenuItem = function({ action, args, ...options }) {
|
|||
});
|
||||
};
|
||||
|
||||
|
||||
const logDebug = function(message, extra) {
|
||||
if (page.settings.debugLogging) {
|
||||
debugLogMessage(message, extra);
|
||||
|
|
|
|||
|
|
@ -111,19 +111,6 @@ const slashNeededForUrl = function(pattern) {
|
|||
return matchPattern.exec(pattern);
|
||||
};
|
||||
|
||||
// Returns the top level domain, e.g. https://another.example.co.uk -> example.co.uk
|
||||
// This is done because a top level domain will probably give better matches with Auto-Type than a full hostname.
|
||||
const getTopLevelDomainFromUrl = function(hostname) {
|
||||
const domainRegex = new RegExp(/(\w+).(com|net|org|edu|co)*(.\w+)$/g);
|
||||
const domainMatch = domainRegex.exec(hostname);
|
||||
|
||||
if (domainMatch) {
|
||||
return domainMatch[0];
|
||||
}
|
||||
|
||||
return hostname;
|
||||
};
|
||||
|
||||
function tr(key, params) {
|
||||
return browser.i18n.getMessage(key, params);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -584,7 +592,7 @@ kpxc.rememberCredentialsFromContextMenu = async function() {
|
|||
// The basic function for retrieving credentials from KeePassXC.
|
||||
// Credential Banner can force the retrieval for reloading new/modified credentials.
|
||||
kpxc.retrieveCredentials = async function(force = false) {
|
||||
if (!isIframeAllowed()) {
|
||||
if (!await isIframeAllowed()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
|
|
@ -623,7 +631,7 @@ kpxc.retrieveCredentialsCallback = async function(credentials) {
|
|||
// If credentials are not received, request them again
|
||||
kpxc.receiveCredentialsIfNecessary = async function() {
|
||||
if (kpxc.credentials.length === 0 && !_called.retrieveCredentials) {
|
||||
if (!isIframeAllowed()) {
|
||||
if (!await isIframeAllowed()) {
|
||||
return [];
|
||||
}
|
||||
|
||||
|
|
@ -710,6 +718,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) {
|
||||
|
|
@ -719,6 +728,7 @@ kpxc.siteIgnored = async function(condition) {
|
|||
|
||||
kpxc.singleInputEnabledForPage = site.usernameOnly;
|
||||
kpxc.improvedFieldDetectionEnabledForPage = site.improvedFieldDetection;
|
||||
await sendMessage('page_set_allow_iframes', [ site.allowIframes, currentLocation ]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -901,8 +911,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') {
|
||||
|
|
@ -972,13 +984,21 @@ kpxc.reconnect = async function() {
|
|||
return true;
|
||||
};
|
||||
|
||||
// Check for Cross-domain security error when inspecting window.top.location.href
|
||||
const isIframeAllowed = 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;
|
||||
return true;
|
||||
} catch (err) {
|
||||
// Inspect iframe using TLD and the tab's original URL
|
||||
const allowed = await sendMessage('is_iframe_allowed', [ window.location.href, window.location.hostname ]);
|
||||
if (allowed) {
|
||||
return true;
|
||||
}
|
||||
|
||||
logDebug(`Error: Credential request ignored from another domain: ${window.self.location.host}`);
|
||||
sendMessage('iframe_detected', true);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -145,6 +145,7 @@
|
|||
"permissions": [
|
||||
"activeTab",
|
||||
"contextMenus",
|
||||
"cookies",
|
||||
"clipboardWrite",
|
||||
"nativeMessaging",
|
||||
"notifications",
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@
|
|||
</ul>
|
||||
</div>
|
||||
<footer class="px-3 mt-4 mb-1 text-uppercase position-absolute small text-muted">
|
||||
© 2017-2023 - KeePassXC Team
|
||||
© 2017-2024 - KeePassXC Team
|
||||
</footer>
|
||||
</nav>
|
||||
|
||||
|
|
@ -695,6 +695,7 @@
|
|||
<li><span data-i18n="optionsSitePreferencesTabHelpTextThird"></span></li>
|
||||
<li><span data-i18n="optionsSitePreferencesTabHelpTextFourth"></span></li>
|
||||
<li><span data-i18n="optionsSitePreferencesImprovedInputFieldDetectionHelpText"></span></li>
|
||||
<li><span data-i18n="optionsSitePreferencesAllowIframesHelpText"></span></li>
|
||||
</ul>
|
||||
</div>
|
||||
<hr class="mt-0">
|
||||
|
|
@ -720,6 +721,7 @@
|
|||
<th scope="col"><span data-i18n="optionsColumnIgnore"></span></th>
|
||||
<th scope="col"><span data-i18n="optionsColumnUsernameOnly"></span></th>
|
||||
<th scope="col"><span data-i18n="optionsColumnImprovedInputFieldDetection"></span></th>
|
||||
<th scope="col"><span data-i18n="optionsColumnAllowIframes"></span></th>
|
||||
<th scope="col"><span data-i18n="optionsColumnDelete"></span></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
|
@ -739,6 +741,7 @@
|
|||
</td>
|
||||
<td><input class="form-check-input" type="checkbox" name="usernameOnly" value="false" data-i18n="[title]optionsColumnUsernameOnly"></td>
|
||||
<td><input class="form-check-input" type="checkbox" name="improvedFieldDetection" value="false" data-i18n="[title]optionsColumnImprovedInputFieldDetection"></td>
|
||||
<td><input class="form-check-input" type="checkbox" name="allowIframes" value="false" data-i18n="[title]optionsColumnAllowIframes"></td>
|
||||
<td class="text-nowrap">
|
||||
<button class="btn btn-sm delete btn-danger btn">
|
||||
<i class="fa fa-remove" aria-hidden="true"></i>
|
||||
|
|
|
|||
|
|
@ -395,8 +395,12 @@ options.initConnectedDatabases = function() {
|
|||
row.setAttribute('hash', hash);
|
||||
row.setAttribute('id', 'tr-cd-' + hash);
|
||||
|
||||
const lastUsed = (options.keyRing[hash].lastUsed) ? new Date(options.keyRing[hash].lastUsed).toLocaleString() : 'unknown';
|
||||
const date = (options.keyRing[hash].created) ? new Date(options.keyRing[hash].created).toLocaleDateString() : 'unknown';
|
||||
const lastUsed = options.keyRing[hash].lastUsed
|
||||
? new Date(options.keyRing[hash].lastUsed).toLocaleString()
|
||||
: 'unknown';
|
||||
const date = options.keyRing[hash].created
|
||||
? new Date(options.keyRing[hash].created).toLocaleDateString()
|
||||
: 'unknown';
|
||||
|
||||
row.children[0].textContent = options.keyRing[hash].id;
|
||||
row.children[1].textContent = options.getPartiallyHiddenKey(options.keyRing[hash].key);
|
||||
|
|
@ -513,6 +517,8 @@ options.initSitePreferences = function() {
|
|||
site.usernameOnly = this.checked;
|
||||
} else if (this.name === 'improvedFieldDetection') {
|
||||
site.improvedFieldDetection = this.checked;
|
||||
} else if (this.name === 'allowIframes') {
|
||||
site.allowIframes = this.checked;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -533,7 +539,7 @@ options.initSitePreferences = function() {
|
|||
options.saveSettings();
|
||||
};
|
||||
|
||||
const addNewRow = function(rowClone, newIndex, url, ignore, usernameOnly, improvedFieldDetection) {
|
||||
const addNewRow = function(rowClone, newIndex, url, ignore, usernameOnly, improvedFieldDetection, allowIframes) {
|
||||
const row = rowClone.cloneNode(true);
|
||||
row.setAttribute('url', url);
|
||||
row.setAttribute('id', 'tr-scf' + newIndex);
|
||||
|
|
@ -544,7 +550,9 @@ options.initSitePreferences = function() {
|
|||
row.children[2].children['usernameOnly'].addEventListener('change', checkboxClicked);
|
||||
row.children[3].children['improvedFieldDetection'].checked = improvedFieldDetection;
|
||||
row.children[3].children['improvedFieldDetection'].addEventListener('change', checkboxClicked);
|
||||
row.children[4].addEventListener('click', removeButtonClicked);
|
||||
row.children[4].children['allowIframes'].checked = allowIframes;
|
||||
row.children[4].children['allowIframes'].addEventListener('change', checkboxClicked);
|
||||
row.children[5].addEventListener('click', removeButtonClicked);
|
||||
|
||||
$('#tab-site-preferences table tbody').append(row);
|
||||
};
|
||||
|
|
@ -604,10 +612,16 @@ options.initSitePreferences = function() {
|
|||
const rowClone = $('#tab-site-preferences table tr.clone').cloneNode(true);
|
||||
rowClone.classList.remove('clone', 'd-none');
|
||||
|
||||
addNewRow(rowClone, newIndex, value, IGNORE_NOTHING, false, false);
|
||||
addNewRow(rowClone, newIndex, value, IGNORE_NOTHING, false, false, false);
|
||||
$('#tab-site-preferences table tbody tr.empty').hide();
|
||||
|
||||
options.settings['sitePreferences'].push({ url: value, ignore: IGNORE_NOTHING, usernameOnly: false, improvedFieldDetection: false });
|
||||
options.settings['sitePreferences'].push({
|
||||
url: value,
|
||||
ignore: IGNORE_NOTHING,
|
||||
usernameOnly: false,
|
||||
improvedFieldDetection: false,
|
||||
allowIframes: false,
|
||||
});
|
||||
options.saveSettings();
|
||||
manualUrl.value = '';
|
||||
});
|
||||
|
|
@ -617,7 +631,15 @@ options.initSitePreferences = function() {
|
|||
let counter = 1;
|
||||
if (options.settings['sitePreferences']) {
|
||||
for (const site of options.settings['sitePreferences']) {
|
||||
addNewRow(rowClone, counter, site.url, site.ignore, site.usernameOnly, site.improvedFieldDetection);
|
||||
addNewRow(
|
||||
rowClone,
|
||||
counter,
|
||||
site.url,
|
||||
site.ignore,
|
||||
site.usernameOnly,
|
||||
site.improvedFieldDetection,
|
||||
site.allowIframes,
|
||||
);
|
||||
++counter;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -130,6 +130,17 @@
|
|||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="iframe-detected" style="display: none">
|
||||
<hr>
|
||||
<p data-i18n="popupIframeDetected"></p>
|
||||
<div class="right-align">
|
||||
<button id="allow-iframe-button" class="btn btn-sm btn-primary">
|
||||
<i class="fa fa-plus" aria-hidden="true"></i>
|
||||
<span data-i18n="popupAllowIframeButton"></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -57,6 +57,10 @@ function statusResponse(r) {
|
|||
$('#username-field-detected').show();
|
||||
}
|
||||
|
||||
if (r.iframeDetected) {
|
||||
$('#iframe-detected').show();
|
||||
}
|
||||
|
||||
reloadCount = 0;
|
||||
}
|
||||
}
|
||||
|
|
@ -137,6 +141,12 @@ const sendMessageToTab = async function(message) {
|
|||
$('#username-field-detected').hide();
|
||||
});
|
||||
|
||||
$('#allow-iframe-button').addEventListener('click', async () => {
|
||||
await sendMessageToTab('add_allow_iframes_option');
|
||||
await sendMessageToTab('redetect_fields');
|
||||
$('#iframe-detected').hide();
|
||||
});
|
||||
|
||||
$('#getting-started-alert-close-button').addEventListener('click', async () => {
|
||||
await browser.runtime.sendMessage({
|
||||
action: 'hide_getting_started_guide_alert'
|
||||
|
|
|
|||
|
|
@ -72,19 +72,43 @@ async function testGeneral() {
|
|||
assertRegex(siteMatch(m[0], m[1]), m[2], testCard, `siteMatch() for ${m[1]}`);
|
||||
}
|
||||
|
||||
// Base domain parsing (window.location.hostname)
|
||||
// TODO: Enable these tests later. Not sure how to tests cookies API which requires to be running in the
|
||||
// background script.
|
||||
// Top-Level-Domain tests
|
||||
/*const tldUrls = [
|
||||
[ 'another.example.co.uk', 'co.uk' ],
|
||||
[ 'www.example.com', 'com' ],
|
||||
[ 'github.com', 'com' ],
|
||||
[ 'test.net', 'net' ],
|
||||
[ 'so.many.subdomains.co.jp', 'co.jp' ],
|
||||
[ '192.168.0.1', '192.168.0.1' ],
|
||||
[ 'www.nic.ar','ar' ],
|
||||
[ 'no.no.no', 'no' ],
|
||||
[ 'www.blogspot.com.ar', 'blogspot.com.ar' ], // blogspot.com.ar is a TLD
|
||||
[ 'jap.an.ide.kyoto.jp', 'ide.kyoto.jp' ], // ide.kyoto.jp is a TLD
|
||||
];
|
||||
|
||||
for (const d of tldUrls) {
|
||||
kpxcAssert(page.getTopLevelDomainFromUrl(d[0]), d[1], testCard, 'getTopLevelDomainFromUrl() for ' + d[0]);
|
||||
}
|
||||
|
||||
// Base domain tests
|
||||
const domains = [
|
||||
[ 'another.example.co.uk', 'example.co.uk' ],
|
||||
[ 'www.example.com', 'example.com' ],
|
||||
[ 'test.net', 'test.net' ],
|
||||
[ 'so.many.subdomains.co.jp', 'subdomains.co.jp' ],
|
||||
[ 'test.site.example.com.au', 'example.com.au' ],
|
||||
[ '192.168.0.1', '192.168.0.1' ]
|
||||
[ '192.168.0.1', '192.168.0.1' ],
|
||||
[ 'www.nic.ar', 'nic.ar' ],
|
||||
[ 'www.blogspot.com.ar', 'www.blogspot.com.ar' ], // blogspot.com.ar is a TLD
|
||||
[ 'www.arpa', 'www.arpa' ],
|
||||
[ 'jap.an.ide.kyoto.jp', 'an.ide.kyoto.jp' ], // ide.kyoto.jp is a TLD
|
||||
[ 'kobe.jp', 'kobe.jp' ],
|
||||
];
|
||||
|
||||
for (const d of domains) {
|
||||
kpxcAssert(getTopLevelDomainFromUrl(d[0]), d[1], testCard, 'getBaseDomainFromUrl() for ' + d[0]);
|
||||
}
|
||||
kpxcAssert(page.getBaseDomainFromUrl(d[0]), d[1], testCard, 'getBaseDomainFromUrl() for ' + d[0]);
|
||||
}*/
|
||||
}
|
||||
|
||||
// Input field matching (keepassxc-browser.js)
|
||||
|
|
|
|||
Loading…
Reference in a new issue