diff --git a/.eslintrc b/.eslintrc index cf89cf1..5ddc93e 100644 --- a/.eslintrc +++ b/.eslintrc @@ -102,6 +102,7 @@ "CHECK_UPDATE_ONE_WEEK": "readonly", "CHECK_UPDATE_THREE_DAYS": "readonly", "cloneInto": "readonly", + "compareVersion": "readonly", "createResult": "readonly", "createStylesheet": "readonly", "DatabaseState": "readonly", diff --git a/keepassxc-browser/background/event.js b/keepassxc-browser/background/event.js index 10cb1ae..253ec71 100755 --- a/keepassxc-browser/background/event.js +++ b/keepassxc-browser/background/event.js @@ -206,8 +206,8 @@ kpxcEvent.pageClearLogins = async function(tab, alreadyCalled) { } }; -kpxcEvent.compareVersion = async function(tab, args = []) { - return keepass.compareVersion(args[0], args[1]); +kpxcEvent.compareMultipleVersions = async function(tab, args = []) { + return keepass.compareMultipleVersions(args[0], args[1]); }; kpxcEvent.getIsKeePassXCAvailable = async function() { @@ -241,7 +241,7 @@ kpxcEvent.messageHandlers = { 'banner_set_position': page.setBannerPosition, 'check_database_hash': keepass.checkDatabaseHash, 'check_update_keepassxc': kpxcEvent.onCheckUpdateKeePassXC, - 'compare_version': kpxcEvent.compareVersion, + 'compare_versions': kpxcEvent.compareMultipleVersions, 'create_new_group': keepass.createNewGroup, 'enable_automatic_reconnect': keepass.enableAutomaticReconnect, 'disable_automatic_reconnect': keepass.disableAutomaticReconnect, diff --git a/keepassxc-browser/background/keepass.js b/keepassxc-browser/background/keepass.js index 25c53d2..e4bf979 100755 --- a/keepassxc-browser/background/keepass.js +++ b/keepassxc-browser/background/keepass.js @@ -172,7 +172,7 @@ keepass.generatePassword = async function(tab) { return ''; } - if (!keepass.compareVersion(keepass.requiredKeePassXC, keepass.currentKeePassXC)) { + if (!compareVersion(keepass.requiredKeePassXC, keepass.currentKeePassXC)) { return ''; } @@ -230,7 +230,7 @@ keepass.associate = async function(tab) { const response = await keepassClient.sendMessage(kpAction, tab, messageData, nonce, false, true); if (response) { // Use public key as identification key with older KeePassXC releases - const savedKey = keepass.compareVersion('2.3.4', keepass.currentKeePassXC) ? idKey : key; + const savedKey = compareVersion('2.3.4', keepass.currentKeePassXC) ? idKey : key; keepass.setCryptoKey(response.id, savedKey); // Save the new identification public key as id key for the database keepass.associated.value = true; keepass.associated.hash = response.hash || 0; @@ -544,7 +544,7 @@ keepass.createNewGroup = async function(tab, args = []) { keepass.getTotp = async function(tab, args = []) { const [ uuid, oldTotp ] = args; - if (!keepass.compareVersion('2.6.1', keepass.currentKeePassXC, true)) { + if (!compareVersion('2.6.1', keepass.currentKeePassXC, true)) { return oldTotp; } @@ -786,7 +786,7 @@ keepass.enableAutomaticReconnect = async function() { if (!page.settings.autoReconnect || (navigator.platform.toLowerCase().includes('win') && keepass.currentKeePassXC - && !keepass.compareVersion('2.3.4', keepass.currentKeePassXC))) { + && !compareVersion('2.3.4', keepass.currentKeePassXC))) { return; } @@ -869,7 +869,7 @@ keepass.keePassXCUpdateAvailable = async function() { await keepass.checkForNewKeePassXCVersion(); } - return keepass.compareVersion(keepass.currentKeePassXC, keepass.latestKeePassXC.version, false); + return compareVersion(keepass.currentKeePassXC, keepass.latestKeePassXC.version, false); } return false; @@ -945,25 +945,18 @@ keepass.updateDatabaseHashToContent = async function() { } }; -keepass.compareVersion = function(minimum, current, canBeEqual = true) { - if (!minimum || !current) { - return false; +// Expects an array of versions to compare +keepass.compareMultipleVersions = function(versions, current, canBeEqual = true) { + if (!Array.isArray(versions)) { + return {}; } - // Handle beta/snapshot builds as stable version - const snapshot = '-snapshot'; - const beta = '-beta'; - if (current.endsWith(snapshot)) { - current = current.slice(0, -snapshot.length); + const result = {}; + for (const version of versions) { + result[version] = compareVersion(version, current, canBeEqual); } - if (current.endsWith(beta)) { - current = current.slice(0, -beta.length); - } - - const min = minimum.split('.', 3).map(s => s.padStart(4, '0')).join('.'); - const cur = current.split('.', 3).map(s => s.padStart(4, '0')).join('.'); - return (canBeEqual ? (min <= cur) : (min < cur)); + return result; }; const removeDuplicateEntries = function(arr) { diff --git a/keepassxc-browser/common/global.js b/keepassxc-browser/common/global.js index 141a73c..d97a8a7 100755 --- a/keepassxc-browser/common/global.js +++ b/keepassxc-browser/common/global.js @@ -61,6 +61,27 @@ const ManualFill = { BOTH: 2 }; +const compareVersion = function(minimum, current, canBeEqual = true) { + if (!minimum || !current || minimum?.indexOf('.') === -1 || current?.indexOf('.') === -1) { + return false; + } + + // Handle beta/snapshot builds as stable version + const snapshot = '-snapshot'; + const beta = '-beta'; + if (current.endsWith(snapshot)) { + current = current.slice(0, -snapshot.length); + } + + if (current.endsWith(beta)) { + current = current.slice(0, -beta.length); + } + + const min = minimum.split('.', 3).map(s => s.padStart(4, '0')).join('.'); + const cur = current.split('.', 3).map(s => s.padStart(4, '0')).join('.'); + return (canBeEqual ? (min <= cur) : (min < cur)); +}; + // Checks if element's nodeName matches const matchesWithNodeName = function(elem, name) { // Don't allow undefined element or 'name' @@ -164,6 +185,7 @@ const getCurrentTab = async function() { // Exports for tests if (typeof module === 'object') { module.exports = { + compareVersion, matchesWithNodeName, siteMatch, slashNeededForUrl, diff --git a/keepassxc-browser/content/pwgen.js b/keepassxc-browser/content/pwgen.js index 429a407..38ceb16 100644 --- a/keepassxc-browser/content/pwgen.js +++ b/keepassxc-browser/content/pwgen.js @@ -150,9 +150,9 @@ const isPasswordGeneratorSupported = async function() { }); const result = await browser.runtime.sendMessage({ - action: 'compare_version', - args: [ '2.7.0', response.current ] + action: 'compare_versions', + args: [ [ '2.7.0' ], response.current ] }); - return result; + return result['2.7.0'] || false; }; diff --git a/keepassxc-browser/options/options.js b/keepassxc-browser/options/options.js index 3af2ea5..19e5b80 100644 --- a/keepassxc-browser/options/options.js +++ b/keepassxc-browser/options/options.js @@ -324,48 +324,40 @@ options.showKeePassXCVersions = async function(response) { $('#tab-about span.kpxcVersion').textContent = response.current; $('#tab-general-settings button.checkUpdateKeePassXC').disabled = false; - // Hide/disable certain options with older KeePassXC versions than 2.6.0 - const version260Result = await browser.runtime.sendMessage({ - action: 'compare_version', - args: [ '2.6.0', response.current ] + const versionResults = await browser.runtime.sendMessage({ + action: 'compare_versions', + args: [ + [ + '2.6.0', + '2.7.0', + '2.7.7', + '2.7.10' + ], + response.current + ], }); - if (version260Result) { + // Hide/disable certain options with older KeePassXC versions than 2.6.0 + if (versionResults['2.6.0']) { $('#tab-general-settings #versionRequiredAlert').hide(); } else { $('#tab-general-settings #showGroupNameInAutocomplete').disabled = true; } // Hide certain options with older KeePassXC versions than 2.7.0 - const version270Result = await browser.runtime.sendMessage({ - action: 'compare_version', - args: [ '2.7.0', response.current ] - }); - - if (!version270Result) { + if (!versionResults['2.7.0']) { $('#tab-general-settings #downloadFaviconAfterSaveFormGroup').hide(); } // Hide certain options with older KeePassXC versions than 2.7.7 - const version277Result = await browser.runtime.sendMessage({ - action: 'compare_version', - args: [ '2.7.7', response.current ] - }); - - if (!version277Result) { + if (!versionResults['2.7.7']) { $('#tab-general-settings #passkeysOptionsCard').hide(); } - const version2710Result = await browser.runtime.sendMessage({ - action: 'compare_version', - args: [ '2.7.10', response.current ] - }); - // Hide passkeys default group option with KeePassXC version < 2.7.10 - if (!version2710Result) { + if (!versionResults['2.7.10']) { $('#tab-general-settings #passkeysDefaultGroup').hide(); } - }; options.getPartiallyHiddenKey = function(key) { diff --git a/tests/global.spec.ts b/tests/global.spec.ts index 870465d..c30c5dc 100644 --- a/tests/global.spec.ts +++ b/tests/global.spec.ts @@ -1,11 +1,27 @@ import { test, expect } from '@playwright/test'; import { + compareVersion, matchesWithNodeName, siteMatch, slashNeededForUrl, trimURL } from '../keepassxc-browser/common/global.js'; +test('Test compareVersion()', async ({ page }) => { + // compareVersion(minimum, current) + expect(compareVersion('2.7.0', '2.7.0')).toBe(true); + expect(compareVersion('2.7.1', '2.7.0')).toBe(false); + expect(compareVersion('2.8.0', '2.7.0')).toBe(false); + expect(compareVersion('2.7.9', '2.7.9-snapshot')).toBe(true); + expect(compareVersion('2.7.9', '2.7.9-beta')).toBe(true); + expect(compareVersion('2.7.9-snapshot', '2.7.9')).toBe(false); // Snapshot cannot be the minimum version + expect(compareVersion('2.7.9-beta', '2.7.9')).toBe(false); // Beta cannot be the minimum version + expect(compareVersion('faulty', '2.7.0')).toBe(false); + expect(compareVersion('2.7.0', 'faulty')).toBe(false); + expect(compareVersion('2.7.0.0.0.0.0', '2.7.0.0.0.0.0')).toBe(true); + expect(compareVersion('2.7.0.0', '2.7.0.1')).toBe(true); +}); + test('Test matchesWithNodeName()', async ({ page }) => { const elem1 = { nodeName: 'INPUT' }; const elem2 = { nodeName: 'input' };