From 145af86b0e1aa8256c694893653eb1bb3e36ebac Mon Sep 17 00:00:00 2001 From: UnKnoWn <9733856+UnKnoWn-Consortium@users.noreply.github.com> Date: Fri, 26 May 2023 03:44:47 +0800 Subject: [PATCH 1/3] feat(bitwarden): added field for server url during setup for self-hosted servers fix(bitwarden): fixed confusing cache logic that does not cache credential request responses properly refactor(prompt): retrofitted the `createPrompt` function with promise refactor(prompt): updated `createPrompt` usage for the promise retrofit refactor: unwrapped AutofillSetup as individual functions and exported the `initialize` function that is used externally refactor: unwrapped PasswordManagers as individual functions and exported functions that are used externally refactor: replaced some usages of `var` with `let` and `const` --- js/autofillSetup.js | 43 +++--- js/passwordManager/bitwarden.js | 127 ++++++++-------- js/passwordManager/passwordCapture.js | 55 +++---- js/passwordManager/passwordManager.js | 210 +++++++++++++------------- main/prompt.js | 71 ++++----- main/viewManager.js | 42 +++--- 6 files changed, 275 insertions(+), 273 deletions(-) diff --git a/js/autofillSetup.js b/js/autofillSetup.js index ea80a30fc..21ce4b26f 100644 --- a/js/autofillSetup.js +++ b/js/autofillSetup.js @@ -2,29 +2,30 @@ const setupDialog = require('passwordManager/managerSetup.js') const settings = require('util/settings/settings.js') const PasswordManagers = require('passwordManager/passwordManager.js') -const AutofillSetup = { - checkSettings: function () { - const manager = PasswordManagers.getActivePasswordManager() +async function checkSettings () { + const manager = PasswordManagers.getActivePasswordManager() + + if (!manager) { + return + } + + try { + const configured = await manager.checkIfConfigured() + if (!configured) { + setupDialog.show(manager) + } + } catch (e) { + console.error(e) + } +} + +function initialize () { + settings.listen('passwordManager', manager => { if (!manager) { return } - - manager.checkIfConfigured().then((configured) => { - if (!configured) { - setupDialog.show(manager) - } - }).catch((err) => { - console.error(err) - }) - }, - initialize: function () { - settings.listen('passwordManager', function (manager) { - if (manager) { - // Trigger the check on browser launch and after manager is enabled - AutofillSetup.checkSettings() - } - }) - } + checkSettings() + }) } -module.exports = AutofillSetup +module.exports = { initialize } diff --git a/js/passwordManager/bitwarden.js b/js/passwordManager/bitwarden.js index 78b6994d5..0689cac6f 100644 --- a/js/passwordManager/bitwarden.js +++ b/js/passwordManager/bitwarden.js @@ -1,7 +1,9 @@ -const ProcessSpawner = require('util/process.js') -const path = require('path') + +const { join } = require('path') const fs = require('fs') -var { ipcRenderer } = require('electron') +const { ipcRenderer } = require('electron') + +const ProcessSpawner = require('util/process.js') // Bitwarden password manager. Requires session key to unlock the vault. class Bitwarden { @@ -12,18 +14,17 @@ class Bitwarden { } getDownloadLink () { - switch (window.platformType) { - case 'mac': - return 'https://vault.bitwarden.com/download/?app=cli&platform=macos' - case 'windows': - return 'https://vault.bitwarden.com/download/?app=cli&platform=windows' - case 'linux': - return 'https://vault.bitwarden.com/download/?app=cli&platform=linux' + if (window.platformType === 'mac') { + return 'https://vault.bitwarden.com/download/?app=cli&platform=macos' } + if (window.platformType === 'windows') { + return 'https://vault.bitwarden.com/download/?app=cli&platform=windows' + } + return 'https://vault.bitwarden.com/download/?app=cli&platform=linux' } getLocalPath () { - return path.join(window.globalArgs['user-data-path'], 'tools', (platformType === 'windows' ? 'bw.exe' : 'bw')) + return join(window.globalArgs['user-data-path'], 'tools', (platformType === 'windows' ? 'bw.exe' : 'bw')) } getSetupMode () { @@ -41,7 +42,7 @@ class Bitwarden { try { await fs.promises.access(localPath, fs.constants.X_OK) local = true - } catch (e) { } + } catch { } if (local) { return localPath } @@ -71,7 +72,7 @@ class Bitwarden { // Tries to get a list of credential suggestions for a given domain name. async getSuggestions (domain) { - if (this.lastCallList[domain] != null) { + if (this.lastCallList[domain]) { return this.lastCallList[domain] } @@ -84,12 +85,13 @@ class Bitwarden { throw new Error() } - this.lastCallList[domain] = this.loadSuggestions(command, domain).then(suggestions => { - this.lastCallList[domain] = null + try { + const suggestions = await this.loadSuggestions(command, domain) + this.lastCallList[domain] = suggestions return suggestions - }).catch(ex => { + } catch (e) { this.lastCallList[domain] = null - }) + } return this.lastCallList[domain] } @@ -97,19 +99,17 @@ class Bitwarden { // Loads credential suggestions for given domain name. async loadSuggestions (command, domain) { try { - const process = new ProcessSpawner(command, ['list', 'items', '--url', this.sanitize(domain), '--session', this.sessionKey]) - const data = await process.execute() - - const matches = JSON.parse(data) - const credentials = matches.map(match => { - const { login: { username, password } } = match - return { username, password, manager: 'Bitwarden' } - }) - - return credentials - } catch (ex) { - const { error, data } = ex - console.error('Error accessing Bitwarden CLI. STDOUT: ' + data + '. STDERR: ' + error) + const process = new ProcessSpawner( + command, + ['list', 'items', '--url', domain.replace(/[^a-zA-Z0-9.-]/g, ''), '--session', this.sessionKey] + ) + const matches = JSON.parse(await process.execute()) + return matches.map( + ({ login: { username, password } }) => + ({ username, password, manager: 'Bitwarden' }) + ) + } catch ({ error, data }) { + console.error(`Error accessing Bitwarden CLI. STDOUT: ${data}. STDERR: ${error}`) return [] } } @@ -118,9 +118,8 @@ class Bitwarden { try { const process = new ProcessSpawner(command, ['sync', '--session', this.sessionKey]) await process.execute() - } catch (ex) { - const { error, data } = ex - console.error('Error accessing Bitwarden CLI. STDOUT: ' + data + '. STDERR: ' + error) + } catch ({ error, data }) { + console.error(`Error accessing Bitwarden CLI. STDOUT: ${data}. STDERR: ${error}`) } } @@ -138,17 +137,17 @@ class Bitwarden { await this.forceSync(this.path) return true - } catch (ex) { - const { error, data } = ex + } catch (err) { + const { error, data } = err - console.error('Error accessing Bitwarden CLI. STDOUT: ' + data + '. STDERR: ' + error) + console.error(`Error accessing Bitwarden CLI. STDOUT: ${data}. STDERR: ${error}`) if (error.includes('not logged in')) { await this.signInAndSave() return await this.unlockStore(password) } - throw ex + throw err } } @@ -161,41 +160,43 @@ class Bitwarden { console.warn(e) } - // show credentials dialog - - var signInFields = [ + // show ask-for-credential dialog + const signInFields = [ + { placeholder: 'Server URL (Leave blank for the default Bitwarden server)', id: 'url', type: 'text' }, { placeholder: 'Client ID', id: 'clientID', type: 'password' }, { placeholder: 'Client Secret', id: 'clientSecret', type: 'password' } ] - const credentials = ipcRenderer.sendSync('prompt', { - text: l('passwordManagerBitwardenSignIn'), - values: signInFields, - ok: l('dialogConfirmButton'), - cancel: l('dialogSkipButton'), - width: 500, - height: 260 - }) - - for (const key in credentials) { - if (credentials[key] === '') { - throw new Error('no credentials entered') + const credentials = ipcRenderer.sendSync( + 'prompt', + { + text: l('passwordManagerBitwardenSignIn'), + values: signInFields, + ok: l('dialogConfirmButton'), + cancel: l('dialogSkipButton'), + width: 500, + height: 260 } - } + ) - const process = new ProcessSpawner(path, ['login', '--apikey'], { - BW_CLIENTID: credentials.clientID.trim(), - BW_CLIENTSECRET: credentials.clientSecret.trim() - }) + if (credentials.clientID === '' || credentials.clientSecret === '') { + throw new Error('no credentials entered') + } - await process.execute() + credentials.url = credentials.url || 'bitwarden.com' - return true - } + const process1 = new ProcessSpawner(path, ['config', 'server', credentials.url.trim()]) + await process1.execute() - // Basic domain name cleanup. Removes any non-ASCII symbols. - sanitize (domain) { - return domain.replace(/[^a-zA-Z0-9.-]/g, '') + const process2 = new ProcessSpawner( + path, + ['login', '--apikey'], + { + BW_CLIENTID: credentials.clientID.trim(), + BW_CLIENTSECRET: credentials.clientSecret.trim() + } + ) + await process2.execute() } } diff --git a/js/passwordManager/passwordCapture.js b/js/passwordManager/passwordCapture.js index 06af18cc8..65c2923ea 100644 --- a/js/passwordManager/passwordCapture.js +++ b/js/passwordManager/passwordCapture.js @@ -13,7 +13,8 @@ const passwordCapture = { closeButton: document.getElementById('password-capture-ignore'), currentDomain: null, barHeight: 0, - showCaptureBar: function (username, password) { + + showCaptureBar (username, password) { passwordCapture.description.textContent = l('passwordCaptureSavePassword').replace('%s', passwordCapture.currentDomain) passwordCapture.bar.hidden = false @@ -27,7 +28,8 @@ const passwordCapture = { passwordCapture.barHeight = passwordCapture.bar.getBoundingClientRect().height webviews.adjustMargin([passwordCapture.barHeight, 0, 0, 0]) }, - hideCaptureBar: function () { + + hideCaptureBar () { webviews.adjustMargin([passwordCapture.barHeight * -1, 0, 0, 0]) passwordCapture.bar.hidden = true @@ -35,7 +37,8 @@ const passwordCapture = { passwordCapture.passwordInput.value = '' passwordCapture.currentDomain = null }, - togglePasswordVisibility: function () { + + togglePasswordVisibility () { if (passwordCapture.passwordInput.type === 'password') { passwordCapture.passwordInput.type = 'text' passwordCapture.revealButton.classList.remove('carbon:view') @@ -46,8 +49,9 @@ const passwordCapture = { passwordCapture.revealButton.classList.remove('carbon:view-off') } }, - handleRecieveCredentials: function (tab, args, frameId) { - var domain = args[0][0] + + async handleRecieveCredentials (tab, args, frameId) { + let domain = args[0][0] if (domain.startsWith('www.')) { domain = domain.slice(4) } @@ -56,30 +60,29 @@ const passwordCapture = { return } - var username = args[0][1] || '' - var password = args[0][2] || '' + const username = args[0][1] || '' + const password = args[0][2] || '' - PasswordManagers.getConfiguredPasswordManager().then(function (manager) { - if (!manager || !manager.saveCredential) { - // the password can't be saved - return + const manager = await PasswordManagers.getConfiguredPasswordManager() + if (!manager || !manager.saveCredential) { + // the password can't be saved + return + } + + // check if this username/password combo is already saved + const credentials = await manager.getSuggestions(domain) + const alreadyExists = credentials.some(cred => cred.username === username && cred.password === password) + if (!alreadyExists) { + if (!passwordCapture.bar.hidden) { + passwordCapture.hideCaptureBar() } - // check if this username/password combo is already saved - manager.getSuggestions(domain).then(function (credentials) { - var alreadyExists = credentials.some(cred => cred.username === username && cred.password === password) - if (!alreadyExists) { - if (!passwordCapture.bar.hidden) { - passwordCapture.hideCaptureBar() - } - - passwordCapture.currentDomain = domain - passwordCapture.showCaptureBar(username, password) - } - }) - }) + passwordCapture.currentDomain = domain + passwordCapture.showCaptureBar(username, password) + } }, - initialize: function () { + + initialize () { passwordCapture.usernameInput.placeholder = l('username') passwordCapture.passwordInput.placeholder = l('password') @@ -106,7 +109,7 @@ const passwordCapture = { // the bar can change height when the window is resized, so the webview needs to be resized in response window.addEventListener('resize', function () { if (!passwordCapture.bar.hidden) { - var oldHeight = passwordCapture.barHeight + const oldHeight = passwordCapture.barHeight passwordCapture.barHeight = passwordCapture.bar.getBoundingClientRect().height webviews.adjustMargin([passwordCapture.barHeight - oldHeight, 0, 0, 0]) } diff --git a/js/passwordManager/passwordManager.js b/js/passwordManager/passwordManager.js index d09ec8499..bc309e6a3 100644 --- a/js/passwordManager/passwordManager.js +++ b/js/passwordManager/passwordManager.js @@ -9,126 +9,122 @@ const Bitwarden = require('js/passwordManager/bitwarden.js') const OnePassword = require('js/passwordManager/onePassword.js') const Keychain = require('js/passwordManager/keychain.js') -const PasswordManagers = { - // List of supported password managers. Each password manager is expected to - // have getSuggestions(domain) method that returns a Promise with credentials - // suggestions matching given domain name. - managers: [ - new Bitwarden(), - new OnePassword(), - new Keychain() - ], - // Returns an active password manager, which is the one that is selected in app's - // settings. - getActivePasswordManager: function () { - if (PasswordManagers.managers.length === 0) { - return null +const managers = [ + new Bitwarden(), + new OnePassword(), + new Keychain() +] + +function getActivePasswordManager () { + if (managers.length === 0) { + return null + } + + const managerSetting = settings.get('passwordManager') + if (managerSetting === null) { + return managers.find(({ name }) => name === 'Built-in password manager') + } + return managers.find(({ name }) => name === managerSetting.name) +} + +async function getConfiguredPasswordManager () { + const manager = getActivePasswordManager() + if (!manager) { + return null + } + + const configured = await manager.checkIfConfigured() + if (!configured) { + return null + } + + return manager +} + +// Shows a prompt dialog for password store's master password. +async function promptForMasterPassword (manager) { + return new Promise((resolve, reject) => { + const { password } = ipcRenderer.sendSync('prompt', { + text: l('passwordManagerUnlock').replace('%p', manager.name), + values: [{ placeholder: l('password'), id: 'password', type: 'password' }], + ok: l('dialogConfirmButton'), + cancel: l('dialogSkipButton'), + height: 175 + }) + if (password === null || password === '') { + reject(new Error('No password provided')) + } else { + resolve(password) } + }) +} - const managerSetting = settings.get('passwordManager') - if (managerSetting == null) { - return PasswordManagers.managers.find(mgr => mgr.name === 'Built-in password manager') +async function unlock (manager) { + let success = false + while (!success) { + let password + try { + password = await promptForMasterPassword(manager) + } catch (e) { + // dialog was canceled + break + } + try { + success = await manager.unlockStore(password) + } catch (e) { + // incorrect password, prompt again } + } + return success +} - return PasswordManagers.managers.find(mgr => mgr.name === managerSetting.name) - }, - getConfiguredPasswordManager: async function () { - const manager = PasswordManagers.getActivePasswordManager() +// Binds IPC events. +function initialize () { + // Called when page preload script detects a form with username and password. + webviews.bindIPC('password-autofill', async (tab, args, frameId, frameURL) => { + const manager = await getConfiguredPasswordManager() if (!manager) { - return null + return } - const configured = await manager.checkIfConfigured() - if (!configured) { - return null + // it's important to use frameURL here and not the tab URL, because the domain of the + // requesting iframe may not match the domain of the top-level page + let hostname = new URL(frameURL).hostname + + if (!manager.isUnlocked()) { + await unlock(manager) } - return manager - }, - // Shows a prompt dialog for password store's master password. - promptForMasterPassword: async function (manager) { - return new Promise((resolve, reject) => { - const { password } = ipcRenderer.sendSync('prompt', { - text: l('passwordManagerUnlock').replace('%p', manager.name), - values: [{ placeholder: l('password'), id: 'password', type: 'password' }], - ok: l('dialogConfirmButton'), - cancel: l('dialogSkipButton'), - height: 175 - }) - if (password === null || password === '') { - reject(new Error('No password provided')) - } else { - resolve(password) - } - }) - }, - unlock: async function (manager) { - let success = false - while (!success) { - let password - try { - password = await PasswordManagers.promptForMasterPassword(manager) - } catch (e) { - // dialog was canceled - break - } - try { - success = await manager.unlockStore(password) - } catch (e) { - // incorrect password, prompt again - } + if (hostname.startsWith('www.')) { + hostname = hostname.slice(4) } - return success - }, - // Binds IPC events. - initialize: function () { - // Called when page preload script detects a form with username and password. - webviews.bindIPC('password-autofill', function (tab, args, frameId, frameURL) { - // it's important to use frameURL here and not the tab URL, because the domain of the - // requesting iframe may not match the domain of the top-level page - const hostname = new URL(frameURL).hostname - - PasswordManagers.getConfiguredPasswordManager().then(async (manager) => { - if (!manager) { - return - } - - if (!manager.isUnlocked()) { - await PasswordManagers.unlock(manager) - } - - var formattedHostname = hostname - if (formattedHostname.startsWith('www.')) { - formattedHostname = formattedHostname.slice(4) - } - - manager.getSuggestions(formattedHostname).then(credentials => { - if (credentials != null) { - webviews.callAsync(tab, 'sendToFrame', [frameId, 'password-autofill-match', { - credentials, - hostname - }]) - } - }).catch(e => { - console.error('Failed to get password suggestions: ' + e.message) - }) - }) - }) - webviews.bindIPC('password-autofill-check', function (tab, args, frameId) { - if (PasswordManagers.getActivePasswordManager()) { - webviews.callAsync(tab, 'sendToFrame', [frameId, 'password-autofill-enabled']) + try { + const credentials = await manager.getSuggestions(hostname) + if (credentials !== null) { + webviews.callAsync(tab, 'sendToFrame', [frameId, 'password-autofill-match', { + credentials, + hostname + }]) } - }) + } catch (e) { + console.error(`Failed to get password suggestions: ${e.message}`) + } + }) - keybindings.defineShortcut('fillPassword', function () { - webviews.callAsync(tabs.getSelected(), 'send', ['password-autofill-shortcut']) - }) + webviews.bindIPC('password-autofill-check', function (tab, args, frameId) { + if (getActivePasswordManager()) { + webviews.callAsync(tab, 'sendToFrame', [frameId, 'password-autofill-enabled']) + } + }) - statistics.registerGetter('passwordManager', function () { - return PasswordManagers.getActivePasswordManager().name - }) - } + keybindings.defineShortcut('fillPassword', function () { + webviews.callAsync(tabs.getSelected(), 'send', ['password-autofill-shortcut']) + }) + + statistics.registerGetter('passwordManager', function () { + return getActivePasswordManager().name + }) } -module.exports = PasswordManagers +module.exports = { getActivePasswordManager, getConfiguredPasswordManager, initialize } diff --git a/main/prompt.js b/main/prompt.js index f85ffba11..f253d5159 100644 --- a/main/prompt.js +++ b/main/prompt.js @@ -1,44 +1,47 @@ /* Simple input prompt. */ -var promptAnswer -var promptOptions +let promptAnswer +let promptOptions -function createPrompt (options, callback) { +function createPrompt (options) { promptOptions = options const { parent, width = 360, height = 140 } = options - var promptWindow = new BrowserWindow({ - width: width, - height: height, - parent: parent != null ? parent : windows.getCurrent(), - show: false, - modal: true, - alwaysOnTop: true, - title: options.title, - autoHideMenuBar: true, - frame: false, - webPreferences: { - nodeIntegration: true, - sandbox: false, - contextIsolation: false - } + return new Promise(resolve => { + let promptWindow = new BrowserWindow({ + width: width, + height: height, + parent: parent != null ? parent : windows.getCurrent(), + show: false, + modal: true, + alwaysOnTop: true, + title: options.title, + autoHideMenuBar: true, + frame: false, + webPreferences: { + nodeIntegration: true, + sandbox: false, + contextIsolation: false + } + }) + + promptWindow.on('closed', () => { + promptWindow = null + resolve(promptAnswer) + }) + + // Load the HTML dialog box + promptWindow.loadURL('file://' + __dirname + '/pages/prompt/index.html') + promptWindow.once('ready-to-show', () => { promptWindow.show() }) }) - - promptWindow.on('closed', () => { - promptWindow = null - callback(promptAnswer) - }) - - // Load the HTML dialog box - promptWindow.loadURL('file://' + __dirname + '/pages/prompt/index.html') - promptWindow.once('ready-to-show', () => { promptWindow.show() }) } -ipc.on('show-prompt', function (options, callback) { - createPrompt(options, callback) +ipc.on('show-prompt', (options, callback) => { + const result = createPrompt(options) + callback(result) }) -ipc.on('open-prompt', function (event) { +ipc.on('open-prompt', event => { event.returnValue = JSON.stringify({ label: promptOptions.text, ok: promptOptions.ok, @@ -48,12 +51,10 @@ ipc.on('open-prompt', function (event) { }) }) -ipc.on('close-prompt', function (event, data) { +ipc.on('close-prompt', (event, data) => { promptAnswer = data }) -ipc.on('prompt', function (event, data) { - createPrompt(data, function (result) { - event.returnValue = result - }) +ipc.on('prompt', async (event, data) => { + event.returnValue = await createPrompt(data) }) diff --git a/main/viewManager.js b/main/viewManager.js index 9a77b66a5..37dbe2fa4 100644 --- a/main/viewManager.js +++ b/main/viewManager.js @@ -1,9 +1,9 @@ const BrowserView = electron.BrowserView -var viewMap = {} // id: view -var viewStateMap = {} // id: view state +const viewMap = {} // id: view +const viewStateMap = {} // id: view state -var temporaryPopupViews = {} // id: view +const temporaryPopupViews = {} // id: view const defaultViewWebPreferences = { nodeIntegration: false, @@ -134,25 +134,25 @@ function createView (existingViewId, id, webPreferencesString, boundsString, eve }) // Open a login prompt when site asks for http authentication - view.webContents.on('login', (event, authenticationResponseDetails, authInfo, callback) => { - if (authInfo.scheme !== 'basic') { // Only for basic auth - return + view.webContents.on( + 'login', + async (event, authenticationResponseDetails, authInfo, callback) => { + if (authInfo.scheme !== 'basic') { // Only for basic auth + return + } + event.preventDefault() + const { username, password } = await createPrompt({ + text: l('loginPromptTitle').replace('%h', authInfo.host).replace('%r', authInfo.realm), + values: [{ placeholder: l('username'), id: 'username', type: 'text' }, + { placeholder: l('password'), id: 'password', type: 'password' }], + ok: l('dialogConfirmButton'), + cancel: l('dialogSkipButton'), + width: 400, + height: 200 + }) + callback(username, password) } - event.preventDefault() - var title = l('loginPromptTitle').replace('%h', authInfo.host).replace('%r', authInfo.realm) - createPrompt({ - text: title, - values: [{ placeholder: l('username'), id: 'username', type: 'text' }, - { placeholder: l('password'), id: 'password', type: 'password' }], - ok: l('dialogConfirmButton'), - cancel: l('dialogSkipButton'), - width: 400, - height: 200 - }, function (result) { - // resend request with auth credentials - callback(result.username, result.password) - }) - }) + ) // show an "open in app" prompt for external protocols From 317ab4df0f0da056aa6e46a60a783315bf3c9265 Mon Sep 17 00:00:00 2001 From: UnKnoWn <9733856+UnKnoWn-Consortium@users.noreply.github.com> Date: Tue, 30 May 2023 04:13:04 +0800 Subject: [PATCH 2/3] feat(bitwarden): errors from `loadSuggestions` in `getSuggestions` are now thrown directly fix(passwordCapture): renamed typo `handleRecieveCredentials` to `handleReceivedCredentials` and updated usage refactor(passwordCapture): updated error handling for `getSuggestions` in `handleReceivedCredentials` --- js/passwordManager/bitwarden.js | 9 +----- js/passwordManager/passwordCapture.js | 40 +++++++++++++++------------ js/passwordManager/passwordManager.js | 2 +- 3 files changed, 24 insertions(+), 27 deletions(-) diff --git a/js/passwordManager/bitwarden.js b/js/passwordManager/bitwarden.js index 0689cac6f..bf387be4d 100644 --- a/js/passwordManager/bitwarden.js +++ b/js/passwordManager/bitwarden.js @@ -85,14 +85,7 @@ class Bitwarden { throw new Error() } - try { - const suggestions = await this.loadSuggestions(command, domain) - this.lastCallList[domain] = suggestions - return suggestions - } catch (e) { - this.lastCallList[domain] = null - } - + this.lastCallList[domain] = await this.loadSuggestions(command, domain) return this.lastCallList[domain] } diff --git a/js/passwordManager/passwordCapture.js b/js/passwordManager/passwordCapture.js index 65c2923ea..5062ff532 100644 --- a/js/passwordManager/passwordCapture.js +++ b/js/passwordManager/passwordCapture.js @@ -50,7 +50,7 @@ const passwordCapture = { } }, - async handleRecieveCredentials (tab, args, frameId) { + async handleReceivedCredentials (tab, args, frameId) { let domain = args[0][0] if (domain.startsWith('www.')) { domain = domain.slice(4) @@ -60,25 +60,29 @@ const passwordCapture = { return } - const username = args[0][1] || '' - const password = args[0][2] || '' + try { + const username = args[0][1] || '' + const password = args[0][2] || '' - const manager = await PasswordManagers.getConfiguredPasswordManager() - if (!manager || !manager.saveCredential) { - // the password can't be saved - return - } - - // check if this username/password combo is already saved - const credentials = await manager.getSuggestions(domain) - const alreadyExists = credentials.some(cred => cred.username === username && cred.password === password) - if (!alreadyExists) { - if (!passwordCapture.bar.hidden) { - passwordCapture.hideCaptureBar() + const manager = await PasswordManagers.getConfiguredPasswordManager() + if (!manager || !manager.saveCredential) { + // the password can't be saved + return } - passwordCapture.currentDomain = domain - passwordCapture.showCaptureBar(username, password) + // check if this username/password combo is already saved + const credentials = await manager.getSuggestions(domain) + const alreadyExists = credentials.some(cred => cred.username === username && cred.password === password) + if (!alreadyExists) { + if (!passwordCapture.bar.hidden) { + passwordCapture.hideCaptureBar() + } + + passwordCapture.currentDomain = domain + passwordCapture.showCaptureBar(username, password) + } + } catch (e) { + console.error(`Failed to get password suggestions: ${e.message}`) } }, @@ -86,7 +90,7 @@ const passwordCapture = { passwordCapture.usernameInput.placeholder = l('username') passwordCapture.passwordInput.placeholder = l('password') - webviews.bindIPC('password-form-filled', passwordCapture.handleRecieveCredentials) + webviews.bindIPC('password-form-filled', passwordCapture.handleReceivedCredentials) passwordCapture.saveButton.addEventListener('click', function () { if (passwordCapture.usernameInput.checkValidity() && passwordCapture.passwordInput.checkValidity()) { diff --git a/js/passwordManager/passwordManager.js b/js/passwordManager/passwordManager.js index bc309e6a3..e3253dd36 100644 --- a/js/passwordManager/passwordManager.js +++ b/js/passwordManager/passwordManager.js @@ -101,7 +101,7 @@ function initialize () { try { const credentials = await manager.getSuggestions(hostname) - if (credentials !== null) { + if (credentials) { webviews.callAsync(tab, 'sendToFrame', [frameId, 'password-autofill-match', { credentials, hostname From 332110ee902bf9df2d361a7ce80accb82fad1b3f Mon Sep 17 00:00:00 2001 From: UnKnoWn <9733856+UnKnoWn-Consortium@users.noreply.github.com> Date: Tue, 30 May 2023 04:53:46 +0800 Subject: [PATCH 3/3] fix(prompt): fixed incorrect usage of the promisified `createPrompt` in the event handler for `show-prompt` --- main/prompt.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/main/prompt.js b/main/prompt.js index f253d5159..019621864 100644 --- a/main/prompt.js +++ b/main/prompt.js @@ -36,8 +36,8 @@ function createPrompt (options) { }) } -ipc.on('show-prompt', (options, callback) => { - const result = createPrompt(options) +ipc.on('show-prompt', async (options, callback) => { + const result = await createPrompt(options) callback(result) })