diff --git a/keepassxc-browser/background/browserAction.js b/keepassxc-browser/background/browserAction.js index 3d0f486..6847bd9 100755 --- a/keepassxc-browser/background/browserAction.js +++ b/keepassxc-browser/background/browserAction.js @@ -206,8 +206,8 @@ browserAction.setRememberPopup = function(tabId, username, password, url, userna const settings = item.settings; // Don't show anything if the site is in the ignore list - for (const site in settings.ignoredSites) { - if (site === url) { + for (const site of settings.ignoredSites) { + if (siteMatch(site.url, url)) { return; } } diff --git a/keepassxc-browser/global.js b/keepassxc-browser/global.js index 55456cd..8fe6432 100755 --- a/keepassxc-browser/global.js +++ b/keepassxc-browser/global.js @@ -15,3 +15,74 @@ var showNotification = function(message) { 'message': message }); }; + +/** + * Transforms a valid match pattern into a regular expression + * which matches all URLs included by that pattern. + * + * @param {string} pattern The pattern to transform. + * @return {RegExp} The pattern's equivalent as a RegExp. + * @throws {TypeError} If the pattern is not a valid MatchPattern + * + * https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Match_patterns + */ +var matchPatternToRegExp = function(pattern) { + if (pattern === '') { + return (/^(?:http|https|file|ftp|app):\/\//); + } + + const schemeSegment = '(\\*|http|https|ws|wss|file|ftp)'; + const hostSegment = '(\\*|(?:\\*\\.)?(?:[^/*]+))?'; + const pathSegment = '(.*)'; + const matchPatternRegExp = new RegExp( + `^${schemeSegment}://${hostSegment}/${pathSegment}$` + ); + + let match = matchPatternRegExp.exec(pattern); + if (!match) { + throw new TypeError('"${pattern}" is not a valid MatchPattern'); + } + + let [, scheme, host, path] = match; + if (!host) { + throw new TypeError('"${pattern}" does not have a valid host'); + } + + let regex = '^'; + + if (scheme === '*') { + regex += '(http|https)'; + } else { + regex += scheme; + } + + regex += '://'; + + if (host && host === '*') { + regex += '[^/]+?'; + } else if (host) { + if (host.match(/^\*\./)) { + regex += '[^/]*?'; + host = host.substring(2); + } + regex += host.replace(/\./g, '\\.'); + } + + if (path) { + if (path === '*') { + regex += '(/.*)?'; + } else if (path.charAt(0) !== '/') { + regex += '/'; + regex += path.replace(/\./g, '\\.').replace(/\*/g, '.*?'); + regex += '/?'; + } + } + + regex += '$'; + return new RegExp(regex); +}; + +var siteMatch = function(site, url) { + const rx = matchPatternToRegExp(site); + return url.match(rx); +}; \ No newline at end of file diff --git a/keepassxc-browser/keepassxc-browser.js b/keepassxc-browser/keepassxc-browser.js index 687d82a..cc0408e 100755 --- a/keepassxc-browser/keepassxc-browser.js +++ b/keepassxc-browser/keepassxc-browser.js @@ -1315,6 +1315,16 @@ cip.initCredentialFields = function(forceCall) { browser.runtime.sendMessage({ 'action': 'page_clear_logins', args: [_called.clearLogins] }).then(() => { _called.clearLogins = true; + + // Ignore sites with full ignore + if (cip.settings.ignoredSites) { + for (const site of cip.settings.ignoredSites) { + if (site.fullIgnore && siteMatch(site.url, document.location.href)) { + return; + } + } + } + const inputs = cipFields.getAllFields(); if (inputs.length === 0) { return; @@ -1932,14 +1942,20 @@ cip.ignoreSite = function(sites) { return; } - const site = sites[0]; - if (!cip.settings['ignoredSites']) { - cip.settings['ignoredSites'] = {}; + // Delete previously created Object if it exists. It will be replaced by an Array + if (cip.settings['ignoredSites'] !== null && cip.settings['ignoredSites'].constructor === Object) { + delete cip.settings['ignoredSites']; } - cip.settings['ignoredSites'][site] = { - url: site - }; + const site = sites[0]; + if (!cip.settings['ignoredSites']) { + cip.settings['ignoredSites'] = []; + } + + cip.settings['ignoredSites'].push({ + url: site, + fullIgnore: false + }); browser.runtime.sendMessage({ action: 'save_settings', diff --git a/keepassxc-browser/options/options.css b/keepassxc-browser/options/options.css index 47a9cd9..5af1947 100644 --- a/keepassxc-browser/options/options.css +++ b/keepassxc-browser/options/options.css @@ -3,6 +3,10 @@ body { padding-bottom: 60px; } +table td { + width: 100%; +} + /* Custom container */ .container { margin: 0 auto; @@ -111,6 +115,10 @@ h2+hr { margin-right: 5px; } +#ignoreUrl { + width: 75%; +} + tr.clone { display: none; } diff --git a/keepassxc-browser/options/options.html b/keepassxc-browser/options/options.html index 597b2af..9f5e745 100644 --- a/keepassxc-browser/options/options.html +++ b/keepassxc-browser/options/options.html @@ -297,21 +297,36 @@ Sites in this list are ignored when new credentials are detected.
Go to the page with new credentials, click the blinking KeePassXC-Browser icon or the notification and select Never ask for this page. +
+ If full ignore is enabled, no input fields are detected and no credentials are received for that site.

+
+
+ +
+
+ + +
+
+
+
+ - + +
Page URL DeleteFull ignore
No ignored sites found.No ignored sites found.
diff --git a/keepassxc-browser/options/options.js b/keepassxc-browser/options/options.js index 19bfbf2..eec34da 100644 --- a/keepassxc-browser/options/options.js +++ b/keepassxc-browser/options/options.js @@ -52,9 +52,9 @@ options.saveSettingsPromise = function() { } options.saveSetting = function(name) { - const $id = '#' + name; - $($id).closest('.control-group').removeClass('error').addClass('success'); - setTimeout(() => { $($id).closest('.control-group').removeClass('success'); }, 2500); + const id = '#' + name; + $(id).closest('.control-group').removeClass('error').addClass('success'); + setTimeout(() => { $(id).closest('.control-group').removeClass('success'); }, 2500); browser.storage.local.set({'settings': options.settings}); browser.runtime.sendMessage({ @@ -168,10 +168,10 @@ options.initConnectedDatabases = function() { $('#dialogDeleteConnectedDatabase .modal-footer:first button.yes:first').click(function(e) { $('#dialogDeleteConnectedDatabase').modal('hide'); - const $hash = $('#dialogDeleteConnectedDatabase').data('hash'); - $('#tab-connected-databases #tr-cd-' + $hash).remove(); + const hash = $('#dialogDeleteConnectedDatabase').data('hash'); + $('#tab-connected-databases #tr-cd-' + hash).remove(); - delete options.keyRing[$hash]; + delete options.keyRing[hash]; options.saveKeyRing(); if ($('#tab-connected-databases table tbody:first tr').length > 2) { @@ -183,22 +183,22 @@ options.initConnectedDatabases = function() { $('#tab-connected-databases tr.clone:first .dropdown-menu:first').width('230px'); - const $trClone = $('#tab-connected-databases table tr.clone:first').clone(true); - $trClone.removeClass('clone'); + const trClone = $('#tab-connected-databases table tr.clone:first').clone(true); + trClone.removeClass('clone'); for (let hash in options.keyRing) { - const $tr = $trClone.clone(true); - $tr.data('hash', hash); - $tr.attr('id', 'tr-cd-' + hash); + const tr = trClone.clone(true); + tr.data('hash', hash); + tr.attr('id', 'tr-cd-' + hash); - $('a.dropdown-toggle:first img:first', $tr).attr('src', '/icons/19x19/icon_normal_19x19.png'); + $('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:first').text(options.keyRing[hash].id); + tr.children('td:eq(1)').text(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); + tr.children('td:eq(2)').text(lastUsed); const date = (options.keyRing[hash].created) ? new Date(options.keyRing[hash].created).toLocaleDateString() : 'unknown'; - $tr.children('td:eq(3)').text(date); - $('#tab-connected-databases table tbody:first').append($tr); + tr.children('td:eq(3)').text(date); + $('#tab-connected-databases table tbody:first').append(tr); } if ($('#tab-connected-databases table tbody:first tr').length > 2) { @@ -227,11 +227,11 @@ options.initSpecifiedCredentialFields = function() { $('#dialogDeleteSpecifiedCredentialFields .modal-footer:first button.yes:first').click(function(e) { $('#dialogDeleteSpecifiedCredentialFields').modal('hide'); - const $url = $('#dialogDeleteSpecifiedCredentialFields').data('url'); - const $trId = $('#dialogDeleteSpecifiedCredentialFields').data('tr-id'); - $('#tab-specified-fields #' + $trId).remove(); + const url = $('#dialogDeleteSpecifiedCredentialFields').data('url'); + const trId = $('#dialogDeleteSpecifiedCredentialFields').data('tr-id'); + $('#tab-specified-fields #' + trId).remove(); - delete options.settings['defined-credential-fields'][$url]; + delete options.settings['defined-credential-fields'][url]; options.saveSettings(); if ($('#tab-specified-fields table tbody:first tr').length > 2) { @@ -241,17 +241,17 @@ options.initSpecifiedCredentialFields = function() { } }); - const $trClone = $('#tab-specified-fields table tr.clone:first').clone(true); - $trClone.removeClass('clone'); + const trClone = $('#tab-specified-fields table tr.clone:first').clone(true); + trClone.removeClass('clone'); let counter = 1; for (let url in options.settings['defined-credential-fields']) { - const $tr = $trClone.clone(true); - $tr.data('url', url); - $tr.attr('id', 'tr-scf' + counter); + const tr = trClone.clone(true); + tr.data('url', url); + tr.attr('id', 'tr-scf' + counter); ++counter; - $tr.children('td:first').text(url); - $('#tab-specified-fields table tbody:first').append($tr); + tr.children('td:first').text(url); + $('#tab-specified-fields table tbody:first').append(tr); } if ($('#tab-specified-fields table tbody:first tr').length > 2) { @@ -271,14 +271,61 @@ options.initIgnoredSites = function() { $('#dialogDeleteIgnoredSite').modal('show'); }); + $('#tab-ignored-sites tr.clone:first input[type=checkbox]:first').change(function() { + const url = $(this).closest('tr').data('url'); + for (let site of options.settings['ignoredSites']) { + if (site.url === url) { + site.fullIgnore = $(this).is(':checked'); + } + } + options.saveSettings(); + }); + + $("#ignoreUrl").keyup(function(event) { + if (event.keyCode === 13) { + $("#ignoreManualAddButton").click(); + } + }); + + $('#ignoreManualAddButton').click(function(e) { + e.preventDefault(); + const value = $('#ignoreUrl').val(); + if (value.length > 10 && value.length <= 2000) { + if (options.settings['ignoredSites'] === undefined) { + options.settings['ignoredSites'] = []; + } + + const newValue = options.settings['ignoredSites'].length + 1; + const trClone = $('#tab-ignored-sites table tr.clone:first').clone(true); + trClone.removeClass('clone'); + + const tr = trClone.clone(true); + tr.data('url', value); + tr.attr('id', 'tr-scf' + newValue); + tr.children('td:first').text(value); + tr.children('td:nth-child(3)').children('input[type=checkbox]').attr('checked', false); + $('#tab-ignored-sites table tbody:first').append(tr); + $('#tab-ignored-sites table tbody:first tr.empty:first').hide(); + + options.settings['ignoredSites'].push({url: value, fullIgnore: false}); + options.saveSettings(); + + $('#ignoreUrl').val(''); + } + }); + $('#dialogDeleteIgnoredSite .modal-footer:first button.yes:first').click(function(e) { $('#dialogDeleteIgnoredSite').modal('hide'); - const $url = $('#dialogDeleteIgnoredSite').data('url'); - const $trId = $('#dialogDeleteIgnoredSite').data('tr-id'); - $('#tab-ignored-sites #' + $trId).remove(); + const url = $('#dialogDeleteIgnoredSite').data('url'); + const trId = $('#dialogDeleteIgnoredSite').data('tr-id'); + $('#tab-ignored-sites #' + trId).remove(); - delete options.settings['ignoredSites'][$url]; + for (let i = 0; i < options.settings['ignoredSites'].length; ++i) { + if (options.settings['ignoredSites'][i].url === url) { + options.settings['ignoredSites'].splice(i, 1); + } + } options.saveSettings(); if ($('#tab-ignored-sites table tbody:first tr').length > 2) { @@ -288,19 +335,22 @@ options.initIgnoredSites = function() { } }); - const $trClone = $('#tab-ignored-sites table tr.clone:first').clone(true); - $trClone.removeClass('clone'); + const trClone = $('#tab-ignored-sites table tr.clone:first').clone(true); + trClone.removeClass('clone'); let counter = 1; - for (let url in options.settings['ignoredSites']) { - const $tr = $trClone.clone(true); - $tr.data('url', url); - $tr.attr('id', 'tr-scf' + counter); - ++counter; + if (options.settings['ignoredSites']){ + for (let site of options.settings['ignoredSites']) { + const tr = trClone.clone(true); + tr.data('url', site.url); + tr.attr('id', 'tr-scf' + counter); + ++counter; - $tr.children('td:first').text(url); - $('#tab-ignored-sites table tbody:first').append($tr); + tr.children('td:first').text(site.url); + tr.children('td:nth-child(3)').children('input[type=checkbox]').attr('checked', site.fullIgnore); + $('#tab-ignored-sites table tbody:first').append(tr); + } } - + if ($('#tab-ignored-sites table tbody:first tr').length > 2) { $('#tab-ignored-sites table tbody:first tr.empty:first').hide(); } else {