From 4f082dda8452d790f494e021472ce1165afdc7d3 Mon Sep 17 00:00:00 2001 From: varjolintu Date: Sat, 29 Apr 2023 10:45:28 +0300 Subject: [PATCH] Draft 290423 --- .eslintrc | 1 + keepassxc-browser/background/event.js | 15 ++++- keepassxc-browser/background/keepass.js | 9 ++- .../background/legacyProtocol.js | 11 ++-- keepassxc-browser/background/protocol.js | 60 ++++++++++++------- keepassxc-browser/common/global.js | 7 +++ keepassxc-browser/content/banner.js | 10 ++-- keepassxc-browser/content/pwgen.js | 25 ++++---- 8 files changed, 87 insertions(+), 51 deletions(-) diff --git a/.eslintrc b/.eslintrc index d54feef..be18b4a 100644 --- a/.eslintrc +++ b/.eslintrc @@ -87,6 +87,7 @@ "assertSearchField": true, "assertSearchForm": true, "assertTOTPField": true, + "AddCredentials": true, "AssociatedAction": true, "Autocomplete": true, "bootstrap": true, diff --git a/keepassxc-browser/background/event.js b/keepassxc-browser/background/event.js index 3aed74f..df439d2 100755 --- a/keepassxc-browser/background/event.js +++ b/keepassxc-browser/background/event.js @@ -16,6 +16,17 @@ kpxcEvent.getColorTheme = async function(tab) { }; kpxcEvent.getConnectedDatabase = async function() { + /*let hash = keepass.associated.hash; + + // Check if one database is open but not active + // TODO: Does not work if two are open and active one is closed and non-associated + const associatedDatabases = keepass.databaseStatuses.statuses.filter(ds => ds.associated); + if (associatedDatabases.length === 1 + && keepass.databaseAssociationStatuses.isAnyAssociated + && !keepass.associated.value) { + hash = associatedDatabases[0].hash; + }*/ + return Promise.resolve({ count: Object.keys(keepass.keyRing).length, identifier: (keepass.keyRing[keepass.associated.hash]) ? keepass.keyRing[keepass.associated.hash].id : null @@ -49,7 +60,7 @@ kpxcEvent.getStatus = async function(tab, args = []) { configured = response.isAnyAssociated; } else { // TODO: This does not update when db is locked or just opened - configured = keepass.databaseAssosiationStatuses?.isAnyAssociated; // ? + configured = keepass.databaseAssociationStatuses?.isAnyAssociated; // ? } } else { if (!internalPoll) { @@ -238,7 +249,7 @@ kpxcEvent.showStatus = async function(tab, configured, internalPoll) { associated: keepass.isAssociated(), configured: configured, databaseClosed: keepass.isDatabaseClosed, - databaseAssociationStatuses: keepass.databaseAssosiationStatuses, + databaseAssociationStatuses: keepass.databaseAssociationStatuses, encryptionKeyUnrecognized: keepass.isEncryptionKeyUnrecognized, error: errorMessage || null, identifier: keyId, diff --git a/keepassxc-browser/background/keepass.js b/keepassxc-browser/background/keepass.js index 330e204..ab55336 100755 --- a/keepassxc-browser/background/keepass.js +++ b/keepassxc-browser/background/keepass.js @@ -1,8 +1,9 @@ 'use strict'; const keepass = {}; -keepass.associated = { 'value': false, 'hash': null }; -keepass.databaseAssosiationStatuses = {}; +keepass.associated = { value: false, hash: null }; +keepass.databaseAssociationStatuses = {}; +keepass.databaseStatuses = []; keepass.keyPair = { publicKey: null, secretKey: null }; keepass.serverPublicKey = ''; keepass.clientID = ''; @@ -104,6 +105,10 @@ keepass.createNewGroup = async function(tab, args = []) { return keepass.protocolV2 ? await protocol.createNewGroup(tab, args) : await keepassProtocol.createNewGroup(tab, args); }; +keepass.generatePassword = async function(tab, args = []) { + return keepass.protocolV2 ? await protocol.generatePassword(tab, args) : await keepassProtocol.generatePassword(tab, args); +}; + keepass.getCredentials = async function(tab, args = []) { return keepass.protocolV2 ? await protocol.getCredentials(tab, args) : await keepassProtocol.retrieveCredentials(tab, args); }; diff --git a/keepassxc-browser/background/legacyProtocol.js b/keepassxc-browser/background/legacyProtocol.js index 37a5471..93ef4bc 100644 --- a/keepassxc-browser/background/legacyProtocol.js +++ b/keepassxc-browser/background/legacyProtocol.js @@ -239,16 +239,15 @@ keepassProtocol.getDatabaseHash = async function(tab, args = []) { connectedKeys: Object.keys(keepass.keyRing) // This will be removed in the future }; - // Why is this here? - /*const encrypted = protocolClient.encrypt(messageData, nonce); + const encrypted = protocolClient.encrypt(messageData, nonce); if (encrypted.length <= 0) { keepass.handleError(tab, kpErrors.PUBLIC_KEY_NOT_FOUND); keepass.updateDatabaseHashToContent(); return keepass.databaseHash; - }*/ + } try { - const request = keepassClient.buildRequest(kpAction, protocolClient.encrypt(messageData, nonce), nonce, keepass.clientID, triggerUnlock); + const request = keepassClient.buildRequest(kpAction, encrypted, nonce, keepass.clientID, triggerUnlock); const response = await keepassClient.sendNativeMessage(request, enableTimeout); if (response.message && response.nonce) { const res = protocolClient.decrypt(response.message, response.nonce); @@ -562,12 +561,12 @@ keepassProtocol.updateCredentials = async function(tab, args = []) { // KeePassXC versions lower than 2.5.0 will have an empty parsed.error let successMessage = response.error; if (response.error === 'success' || response.error === '') { - successMessage = entryId ? 'updated' : 'created'; + successMessage = entryId ? AddCredentials.UPDATED : AddCredentials.CREATED; } return successMessage; } else { - return 'error'; + return AddCredentials.ERROR; } } catch (err) { logError(`updateCredentials failed: ${err}`); diff --git a/keepassxc-browser/background/protocol.js b/keepassxc-browser/background/protocol.js index e512d65..739e51b 100644 --- a/keepassxc-browser/background/protocol.js +++ b/keepassxc-browser/background/protocol.js @@ -100,12 +100,14 @@ protocol.createNewGroup = async function(tab, args = []) { keepass.clearErrorMessage(tab); const [ groupName ] = args; - const [ dbid ] = keepass.getCryptoKey(); + //const [ dbid ] = keepass.getCryptoKey(); const messageData = { action: kpActions.CREATE_NEW_GROUP, - id: dbid, + //id: dbid, groupName: groupName, + keys: protocol.getKeys(), // Added + hash: keepass.databaseHash // Added }; try { @@ -128,25 +130,27 @@ protocol.createNewGroup = async function(tab, args = []) { protocol.generatePassword = async function(tab, args = []) { if (!keepass.isConnected) { - return []; + return undefined; } if (!keepass.compareVersion(keepass.requiredKeePassXC, keepass.currentKeePassXC)) { - return []; + return undefined; } - // TODO: Return '' or [] ..? let password; const messageData = { action: kpActions.GENERATE_PASSWORD, - clientID: keepass.clientID, - requestID: protocolClient.getRequestId() // Needed? }; try { const response = await protocolClient.sendMessage(tab, messageData); if (response) { + if (response.error && response.errorCode) { + keepass.handleError(tab, response.errorCode); + return undefined; + } + password = response.entries ?? response.password; keepass.updateLastUsed(keepass.databaseHash); // ? } else { @@ -156,7 +160,7 @@ protocol.generatePassword = async function(tab, args = []) { return password; } catch (err) { logError(`generatePassword failed: ${err}`); - return []; + return undefined; } }; @@ -220,18 +224,24 @@ protocol.getDatabaseGroups = async function(tab, args = []) { keepass.clearErrorMessage(tab); - const [ dbid ] = keepass.getCryptoKey(); + //const [ dbid ] = keepass.getCryptoKey(); let groups = []; const messageData = { action: kpActions.GET_DATABASE_GROUPS, - id: dbid + //id: dbid, + keys: protocol.getKeys(), // Added + hash: keepass.databaseHash // Added }; try { - // TODO: Handle errors const response = await protocolClient.sendMessage(tab, messageData); if (response) { + if (response.error && response.errorCode) { + keepass.handleError(tab, response.errorCode); + return []; + } + groups = response.groups; groups.defaultGroup = page.settings.defaultGroup; groups.defaultGroupAlwaysAsk = page.settings.defaultGroupAlwaysAsk; @@ -300,7 +310,9 @@ protocol.getTotp = async function(tab, args = []) { const messageData = { action: kpActions.GET_TOTP, - uuid: uuid + uuid: uuid, + keys: protocol.getKeys(), // Added + hash: keepass.databaseHash // Added }; try { @@ -405,6 +417,10 @@ protocol.testAssociationFromDatabaseStatuses = async function(tab, args = []) { console.log('Current one is not associated'); } + // Current association status + keepass.associated.hash = currentDatabaseStatus[0]?.hash; + keepass.associated.value = isCurrentAssociated; + // This should be true only if all databases are locked keepass.isDatabaseClosed = areAllLocked; // ? @@ -413,7 +429,8 @@ protocol.testAssociationFromDatabaseStatuses = async function(tab, args = []) { result.databaseHash = databaseStatuses.hash; result.isAnyAssociated = isAnyAssociated; - keepass.databaseAssosiationStatuses = result; + keepass.databaseStatuses = databaseStatuses; + keepass.databaseAssociationStatuses = result; return result; }; @@ -427,11 +444,13 @@ protocol.updateCredentials = async function(tab, args = []) { const messageData = { action: kpActions.CREATE_CREDENTIALS, - id: dbid, + hash: keepass.databaseHash, // Added + keys: protocol.getKeys(), // Added + //id: dbid, // Needed? login: username, password: password, url: url, - submitUrl: url + submitUrl: url, }; if (entryId) { @@ -448,18 +467,15 @@ protocol.updateCredentials = async function(tab, args = []) { } try { - // TODO: Check response messages const response = await protocolClient.sendMessage(tab, messageData); if (response) { - // KeePassXC versions lower than 2.5.0 will have an empty parsed.error - let successMessage = response.error; - if (response?.result === true || response.error === '') { - successMessage = entryId ? 'updated' : 'created'; + if (response?.result === true) { + return entryId ? AddCredentials.UPDATED : AddCredentials.CREATED; } - return successMessage; + return AddCredentials.CANCELED; } else { - return 'error'; + return AddCredentials.ERROR; } } catch (err) { logError(`updateCredentials failed: ${err}`); diff --git a/keepassxc-browser/common/global.js b/keepassxc-browser/common/global.js index fe84b65..a4dc05e 100755 --- a/keepassxc-browser/common/global.js +++ b/keepassxc-browser/common/global.js @@ -56,6 +56,13 @@ const ManualFill = { BOTH: 2 }; +const AddCredentials = { + CANCELED: 0, + CREATED: 1, + ERROR: 2, + UPDATED: 3 +}; + // Match hostname or path with wildcards const matchWithRegex = function(firstUrlPart, secondUrlPart, hostnameUsed = false) { if (firstUrlPart === secondUrlPart) { diff --git a/keepassxc-browser/content/banner.js b/keepassxc-browser/content/banner.js index f860068..56f10ea 100644 --- a/keepassxc-browser/content/banner.js +++ b/keepassxc-browser/content/banner.js @@ -281,7 +281,7 @@ kpxcBanner.updateCredentials = async function(credentials = {}) { args: [ url, '', true ] // Sets triggerUnlock to true }).then(async creds => { if (!creds || creds.length !== credentials.list.length) { - kpxcBanner.verifyResult('error'); + kpxcBanner.verifyResult(AddCredentials.ERROR); return; } @@ -302,15 +302,15 @@ kpxcBanner.updateCredentials = async function(credentials = {}) { }; kpxcBanner.verifyResult = async function(code) { - if (code === 'error') { + if (code === AddCredentials.ERROR) { kpxcUI.createNotification('error', tr('rememberErrorCannotSaveCredentials')); - } else if (code === 'created') { + } else if (code === AddCredentials.CREATED) { kpxcUI.createNotification('success', tr('rememberCredentialsSaved', kpxcBanner.credentials.username || tr('rememberEmptyUsername'))); await kpxc.retrieveCredentials(true); // Forced reload - } else if (code === 'updated') { + } else if (code === AddCredentials.UPDATED) { kpxcUI.createNotification('success', tr('rememberCredentialsUpdated', kpxcBanner.credentials.username || tr('rememberEmptyUsername'))); await kpxc.retrieveCredentials(true); // Forced reload - } else if (code === 'canceled') { + } else if (code === AddCredentials.CANCELED) { kpxcUI.createNotification('warning', tr('rememberCredentialsNotSaved')); } else { kpxcUI.createNotification('error', tr('rememberErrorDatabaseClosed')); diff --git a/keepassxc-browser/content/pwgen.js b/keepassxc-browser/content/pwgen.js index 2c24909..20c9e19 100644 --- a/keepassxc-browser/content/pwgen.js +++ b/keepassxc-browser/content/pwgen.js @@ -303,18 +303,9 @@ kpxcPasswordDialog.fill = function(e) { } } - kpxcPasswordDialog.input.value = password.value; - kpxcPasswordDialog.input.dispatchEvent(new Event('keydown', { bubbles: true })); - kpxcPasswordDialog.input.dispatchEvent(new Event('keyup', { bubbles: true })); - kpxcPasswordDialog.input.dispatchEvent(new Event('input', { bubbles: true })); - kpxcPasswordDialog.input.dispatchEvent(new Event('change', { bubbles: true })); - + fillPassword(kpxcPasswordDialog.input, password.value); if (kpxcPasswordDialog.nextField) { - kpxcPasswordDialog.nextField.value = password.value; - kpxcPasswordDialog.nextField.dispatchEvent(new Event('keydown', { bubbles: true })); - kpxcPasswordDialog.nextField.dispatchEvent(new Event('keyup', { bubbles: true })); - kpxcPasswordDialog.nextField.dispatchEvent(new Event('input', { bubbles: true })); - kpxcPasswordDialog.nextField.dispatchEvent(new Event('change', { bubbles: true })); + fillPassword(kpxcPasswordDialog.nextField, password.value); } }; @@ -339,9 +330,7 @@ kpxcPasswordDialog.newFill = function(elem, password) { } } - elem.value = password; - elem.dispatchEvent(new Event('input', { bubbles: true })); - elem.dispatchEvent(new Event('change', { bubbles: true })); + fillPassword(elem, password); }; kpxcPasswordDialog.copyPasswordToClipboard = function() { @@ -405,3 +394,11 @@ const useKeePassXCPasswordGenerator = async function() { return result; }; + +const fillPassword = function(elem, password) { + elem.value = password; + elem.dispatchEvent(new Event('keydown', { bubbles: true })); + elem.dispatchEvent(new Event('keyup', { bubbles: true })); + elem.dispatchEvent(new Event('input', { bubbles: true })); + elem.dispatchEvent(new Event('change', { bubbles: true })); +};