From f604da13a1cedb801a2758237f8998094615d53e Mon Sep 17 00:00:00 2001 From: Opeyemi <54288773+Eeebru@users.noreply.github.com> Date: Tue, 13 Jun 2023 15:57:02 +0100 Subject: [PATCH 01/19] add more comment to missing actions (#2567) --- .github/workflows/build.yml | 2 +- .github/workflows/crowdin-pull.yml | 2 +- .github/workflows/enforce-labels.yml | 2 +- .github/workflows/release.yml | 8 ++++---- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 452fe72b8..57812aa82 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -793,7 +793,7 @@ jobs: done - name: Upload Sources - uses: crowdin/github-action@ecd7eb0ef6f3cfa16293c79e9cbc4bc5b5fd9c49 # v1.4.9 + uses: crowdin/github-action@965d501f160af7b1f88aed4c29154b0caf1e94b9 # v1.9.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }} diff --git a/.github/workflows/crowdin-pull.yml b/.github/workflows/crowdin-pull.yml index 36aaaa9ab..a2a297a19 100644 --- a/.github/workflows/crowdin-pull.yml +++ b/.github/workflows/crowdin-pull.yml @@ -30,7 +30,7 @@ jobs: secrets: "crowdin-api-token, github-gpg-private-key, github-gpg-private-key-passphrase" - name: Download translations - uses: crowdin/github-action@ecd7eb0ef6f3cfa16293c79e9cbc4bc5b5fd9c49 # v1.4.9 + uses: crowdin/github-action@965d501f160af7b1f88aed4c29154b0caf1e94b9 # v1.9.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} CROWDIN_API_TOKEN: ${{ steps.retrieve-secrets.outputs.crowdin-api-token }} diff --git a/.github/workflows/enforce-labels.yml b/.github/workflows/enforce-labels.yml index 417e34e9b..4e3127761 100644 --- a/.github/workflows/enforce-labels.yml +++ b/.github/workflows/enforce-labels.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-20.04 steps: - name: Enforce Label - uses: yogevbd/enforce-label-action@a3c219da6b8fa73f6ba62b68ff09c469b3a1c024 # v2.2.2 + uses: yogevbd/enforce-label-action@a3c219da6b8fa73f6ba62b68ff09c469b3a1c024 # 2.2.2 with: BANNED_LABELS: "hold,needs-qa" BANNED_LABELS_DESCRIPTION: "PRs with the hold or needs-qa labels cannot be merged" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f1e57bbe9..50f4128e9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -68,7 +68,7 @@ jobs: - name: Download all artifacts if: ${{ github.event.inputs.release_type != 'Dry Run' }} - uses: dawidd6/action-download-artifact@575b1e4167df67acf7e692af784566618b23c71e # v2.17.10 + uses: dawidd6/action-download-artifact@246dbf436b23d7c49e21a7ab8204ca9ecd1fe615 # v2.27.0 with: workflow: build.yml workflow_conclusion: success @@ -76,7 +76,7 @@ jobs: - name: Dry Run - Download all artifacts if: ${{ github.event.inputs.release_type == 'Dry Run' }} - uses: dawidd6/action-download-artifact@575b1e4167df67acf7e692af784566618b23c71e # v2.17.10 + uses: dawidd6/action-download-artifact@246dbf436b23d7c49e21a7ab8204ca9ecd1fe615 # v2.27.0 with: workflow: build.yml workflow_conclusion: success @@ -130,7 +130,7 @@ jobs: - name: Download F-Droid .apk artifact if: ${{ github.event.inputs.release_type != 'Dry Run' }} - uses: dawidd6/action-download-artifact@575b1e4167df67acf7e692af784566618b23c71e # v2.17.10 + uses: dawidd6/action-download-artifact@246dbf436b23d7c49e21a7ab8204ca9ecd1fe615 # v2.27.0 with: workflow: build.yml workflow_conclusion: success @@ -139,7 +139,7 @@ jobs: - name: Dry Run - Download F-Droid .apk artifact if: ${{ github.event.inputs.release_type == 'Dry Run' }} - uses: dawidd6/action-download-artifact@575b1e4167df67acf7e692af784566618b23c71e # v2.17.10 + uses: dawidd6/action-download-artifact@246dbf436b23d7c49e21a7ab8204ca9ecd1fe615 # v2.27.0 with: workflow: build.yml workflow_conclusion: success From 04e30c21462fb379cf57244dff700fd65a632cdb Mon Sep 17 00:00:00 2001 From: mpbw2 <59324545+mpbw2@users.noreply.github.com> Date: Tue, 13 Jun 2023 12:46:41 -0400 Subject: [PATCH 02/19] Update F-Droid listing author name (#2501) Update F-Droid listing author name to `Bitwarden Inc` --- store/fdroid/metadata/com.x8bit.bitwarden.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/store/fdroid/metadata/com.x8bit.bitwarden.yml b/store/fdroid/metadata/com.x8bit.bitwarden.yml index ef7c14a74..47eb42eca 100644 --- a/store/fdroid/metadata/com.x8bit.bitwarden.yml +++ b/store/fdroid/metadata/com.x8bit.bitwarden.yml @@ -7,7 +7,7 @@ SourceCode: https://github.com/bitwarden/mobile Changelog: https://github.com/bitwarden/mobile/releases WebSite: https://bitwarden.com -AuthorName: 8bit Solutions LLC +AuthorName: Bitwarden, Inc. Name: Bitwarden Summary: A secure and free password manager for all of your devices. Description: | From 1332ef7b433e8645eecfdeefdc9fe483adec5a7d Mon Sep 17 00:00:00 2001 From: mpbw2 <59324545+mpbw2@users.noreply.github.com> Date: Tue, 13 Jun 2023 13:54:28 -0400 Subject: [PATCH 03/19] Enhancement to login field detection for Android autofill (#2561) --- src/Android/Autofill/FieldCollection.cs | 41 +++++++++++++------------ 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/src/Android/Autofill/FieldCollection.cs b/src/Android/Autofill/FieldCollection.cs index a01aaad70..63d2e512b 100644 --- a/src/Android/Autofill/FieldCollection.cs +++ b/src/Android/Autofill/FieldCollection.cs @@ -12,7 +12,7 @@ namespace Bit.Droid.Autofill private List _passwordFields = null; private List _usernameFields = null; private HashSet _ignoreSearchTerms = new HashSet { "search", "find", "recipient", "edit" }; - private HashSet _usernameTerms = new HashSet { "email", "phone", "username"}; + private HashSet _usernameTerms = new HashSet { "email", "phone", "username" }; private HashSet _passwordTerms = new HashSet { "password", "pswd" }; public List AutofillIds { get; private set; } = new List(); @@ -54,15 +54,14 @@ namespace Bit.Droid.Autofill if (HintToFieldsMap.ContainsKey(View.AutofillHintPassword)) { _passwordFields.AddRange(HintToFieldsMap[View.AutofillHintPassword]); + return _passwordFields; } } - else + + _passwordFields = Fields.Where(f => FieldIsPassword(f)).ToList(); + if (!_passwordFields.Any()) { - _passwordFields = Fields.Where(f => FieldIsPassword(f)).ToList(); - if (!_passwordFields.Any()) - { - _passwordFields = Fields.Where(f => FieldHasPasswordTerms(f)).ToList(); - } + _passwordFields = Fields.Where(f => FieldHasPasswordTerms(f)).ToList(); } return _passwordFields; } @@ -87,24 +86,26 @@ namespace Bit.Droid.Autofill { _usernameFields.AddRange(HintToFieldsMap[View.AutofillHintUsername]); } + if (_usernameFields.Any()) + { + return _usernameFields; + } } - else - { - foreach (var passwordField in PasswordFields) - { - var usernameField = Fields.TakeWhile(f => f.AutofillId != passwordField.AutofillId) - .LastOrDefault(); - if (usernameField != null) - { - _usernameFields.Add(usernameField); - } - } - if (!_usernameFields.Any()) + foreach (var passwordField in PasswordFields) + { + var usernameField = Fields.TakeWhile(f => f.AutofillId != passwordField.AutofillId) + .LastOrDefault(); + if (usernameField != null) { - _usernameFields = Fields.Where(f => FieldIsUsername(f)).ToList(); + _usernameFields.Add(usernameField); } } + + if (!_usernameFields.Any()) + { + _usernameFields = Fields.Where(f => FieldIsUsername(f)).ToList(); + } return _usernameFields; } } From 98705e443f321411dd3b7c8d54581fe4462cc3c5 Mon Sep 17 00:00:00 2001 From: Federico Maccaroni Date: Tue, 13 Jun 2023 22:38:08 +0200 Subject: [PATCH 04/19] PM-2575 Fixed extension freeze when using the return button on the keyboard when unlocking the extension. Also added way to prevent multiple executions of checking the password and logging exceptions. (#2568) --- src/App/Resources/AppResources.Designer.cs | 2 +- src/App/Resources/AppResources.resx | 2 +- .../LockPasswordViewController.cs | 3 +- .../BaseLockPasswordViewController.cs | 176 ++++++++++-------- 4 files changed, 99 insertions(+), 84 deletions(-) diff --git a/src/App/Resources/AppResources.Designer.cs b/src/App/Resources/AppResources.Designer.cs index 51a6cfb8c..f4bf3ff61 100644 --- a/src/App/Resources/AppResources.Designer.cs +++ b/src/App/Resources/AppResources.Designer.cs @@ -6435,7 +6435,7 @@ namespace Bit.App.Resources { } /// - /// Looks up a localized string similar to Unlocking may fail due to insufficient memory. Decrease your KDF memory settings to resolve. + /// Looks up a localized string similar to Unlocking may fail due to insufficient memory. Decrease your KDF memory settings to resolve.. /// public static string UnlockingMayFailDueToInsufficientMemoryDecreaseYourKDFMemorySettingsToResolve { get { diff --git a/src/App/Resources/AppResources.resx b/src/App/Resources/AppResources.resx index f4bae0727..8fa9d4375 100644 --- a/src/App/Resources/AppResources.resx +++ b/src/App/Resources/AppResources.resx @@ -2635,6 +2635,6 @@ Do you want to switch to this account? Master password re-prompt help - Unlocking may fail due to insufficient memory. Decrease your KDF memory settings to resolve + Unlocking may fail due to insufficient memory. Decrease your KDF memory settings to resolve. diff --git a/src/iOS.Autofill/LockPasswordViewController.cs b/src/iOS.Autofill/LockPasswordViewController.cs index a2cad5568..7f5df4c7e 100644 --- a/src/iOS.Autofill/LockPasswordViewController.cs +++ b/src/iOS.Autofill/LockPasswordViewController.cs @@ -1,5 +1,6 @@ using System; using Bit.App.Controls; +using Bit.Core.Utilities; using Bit.iOS.Core.Utilities; using UIKit; @@ -44,7 +45,7 @@ namespace Bit.iOS.Autofill partial void SubmitButton_Activated(UIBarButtonItem sender) { - var task = CheckPasswordAsync(); + CheckPasswordAsync().FireAndForget(); } partial void CancelButton_Activated(UIBarButtonItem sender) diff --git a/src/iOS.Core/Controllers/BaseLockPasswordViewController.cs b/src/iOS.Core/Controllers/BaseLockPasswordViewController.cs index db0a946c2..0edccc732 100644 --- a/src/iOS.Core/Controllers/BaseLockPasswordViewController.cs +++ b/src/iOS.Core/Controllers/BaseLockPasswordViewController.cs @@ -36,6 +36,7 @@ namespace Bit.iOS.Core.Controllers private bool _passwordReprompt = false; private bool _usesKeyConnector; private bool _biometricUnlockOnly = false; + private bool _checkingPassword; protected bool autofillExtension = false; @@ -154,7 +155,7 @@ namespace Bit.iOS.Core.Controllers MasterPasswordCell.TextField.ReturnKeyType = UIReturnKeyType.Go; MasterPasswordCell.TextField.ShouldReturn += (UITextField tf) => { - CheckPasswordAsync().GetAwaiter().GetResult(); + CheckPasswordAsync().FireAndForget(); return true; }; if (_pinLock) @@ -208,108 +209,121 @@ namespace Bit.iOS.Core.Controllers MasterPasswordCell.TextField.BecomeFirstResponder(); } } - + protected async Task CheckPasswordAsync() { - if (string.IsNullOrWhiteSpace(MasterPasswordCell.TextField.Text)) - { - var alert = Dialogs.CreateAlert(AppResources.AnErrorHasOccurred, - string.Format(AppResources.ValidationFieldRequired, - _pinLock ? AppResources.PIN : AppResources.MasterPassword), - AppResources.Ok); - PresentViewController(alert, true, null); - return; - } - - var email = await _stateService.GetEmailAsync(); - var kdfConfig = await _stateService.GetActiveUserCustomDataAsync(a => new KdfConfig(a?.Profile)); - var inputtedValue = MasterPasswordCell.TextField.Text; - - // HACK: iOS extensions have constrained memory, given how it works Argon2Id, it's likely to crash - // the extension depending on the argon2id memory configured. - // So, we warn the user and advise to decrease the configured memory letting them the option to continue, if wanted. - if (kdfConfig.Type == KdfType.Argon2id - && - kdfConfig.Memory > Constants.MaximumArgon2IdMemoryBeforeExtensionCrashing - && - !await _platformUtilsService.ShowDialogAsync(AppResources.UnlockingMayFailDueToInsufficientMemoryDecreaseYourKDFMemorySettingsToResolve, AppResources.Warning, AppResources.Continue, AppResources.Cancel)) + if (_checkingPassword) { return; } + _checkingPassword = true; - if (_pinLock) + try { - var failed = true; - try + if (string.IsNullOrWhiteSpace(MasterPasswordCell.TextField.Text)) { - if (_isPinProtected) + var alert = Dialogs.CreateAlert(AppResources.AnErrorHasOccurred, + string.Format(AppResources.ValidationFieldRequired, + _pinLock ? AppResources.PIN : AppResources.MasterPassword), + AppResources.Ok); + PresentViewController(alert, true, null); + return; + } + + var email = await _stateService.GetEmailAsync(); + var kdfConfig = await _stateService.GetActiveUserCustomDataAsync(a => new KdfConfig(a?.Profile)); + var inputtedValue = MasterPasswordCell.TextField.Text; + + // HACK: iOS extensions have constrained memory, given how it works Argon2Id, it's likely to crash + // the extension depending on the argon2id memory configured. + // So, we warn the user and advise to decrease the configured memory letting them the option to continue, if wanted. + if (kdfConfig.Type == KdfType.Argon2id + && + kdfConfig.Memory > Constants.MaximumArgon2IdMemoryBeforeExtensionCrashing + && + !await _platformUtilsService.ShowDialogAsync(AppResources.UnlockingMayFailDueToInsufficientMemoryDecreaseYourKDFMemorySettingsToResolve, AppResources.Warning, AppResources.Continue, AppResources.Cancel)) + { + return; + } + + if (_pinLock) + { + var failed = true; + try { - var key = await _cryptoService.MakeKeyFromPinAsync(inputtedValue, email, - kdfConfig, - await _stateService.GetPinProtectedKeyAsync()); - var encKey = await _cryptoService.GetEncKeyAsync(key); - var protectedPin = await _stateService.GetProtectedPinAsync(); - var decPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), encKey); - failed = decPin != inputtedValue; - if (!failed) + if (_isPinProtected) { + var key = await _cryptoService.MakeKeyFromPinAsync(inputtedValue, email, + kdfConfig, + await _stateService.GetPinProtectedKeyAsync()); + var encKey = await _cryptoService.GetEncKeyAsync(key); + var protectedPin = await _stateService.GetProtectedPinAsync(); + var decPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), encKey); + failed = decPin != inputtedValue; + if (!failed) + { + await AppHelpers.ResetInvalidUnlockAttemptsAsync(); + await SetKeyAndContinueAsync(key); + } + } + else + { + var key2 = await _cryptoService.MakeKeyFromPinAsync(inputtedValue, email, + kdfConfig); + failed = false; await AppHelpers.ResetInvalidUnlockAttemptsAsync(); - await SetKeyAndContinueAsync(key); + await SetKeyAndContinueAsync(key2); } } - else + catch { - var key2 = await _cryptoService.MakeKeyFromPinAsync(inputtedValue, email, - kdfConfig); - failed = false; - await AppHelpers.ResetInvalidUnlockAttemptsAsync(); - await SetKeyAndContinueAsync(key2); + failed = true; } - } - catch - { - failed = true; - } - if (failed) - { - await HandleFailedCredentialsAsync(); - } - } - else - { - var key2 = await _cryptoService.MakeKeyAsync(inputtedValue, email, kdfConfig); - - var storedKeyHash = await _cryptoService.GetKeyHashAsync(); - if (storedKeyHash == null) - { - var oldKey = await _secureStorageService.GetAsync("oldKey"); - if (key2.KeyB64 == oldKey) + if (failed) { - var localKeyHash = await _cryptoService.HashPasswordAsync(inputtedValue, key2, HashPurpose.LocalAuthorization); - await _secureStorageService.RemoveAsync("oldKey"); - await _cryptoService.SetKeyHashAsync(localKeyHash); + await HandleFailedCredentialsAsync(); } } - var passwordValid = await _cryptoService.CompareAndUpdateKeyHashAsync(inputtedValue, key2); - if (passwordValid) - { - if (_isPinProtected) - { - var protectedPin = await _stateService.GetProtectedPinAsync(); - var encKey = await _cryptoService.GetEncKeyAsync(key2); - var decPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), encKey); - var pinKey = await _cryptoService.MakePinKeyAysnc(decPin, email, - kdfConfig); - await _stateService.SetPinProtectedKeyAsync(await _cryptoService.EncryptAsync(key2.Key, pinKey)); - } - await AppHelpers.ResetInvalidUnlockAttemptsAsync(); - await SetKeyAndContinueAsync(key2, true); - } else { - await HandleFailedCredentialsAsync(); + var key2 = await _cryptoService.MakeKeyAsync(inputtedValue, email, kdfConfig); + + var storedKeyHash = await _cryptoService.GetKeyHashAsync(); + if (storedKeyHash == null) + { + var oldKey = await _secureStorageService.GetAsync("oldKey"); + if (key2.KeyB64 == oldKey) + { + var localKeyHash = await _cryptoService.HashPasswordAsync(inputtedValue, key2, HashPurpose.LocalAuthorization); + await _secureStorageService.RemoveAsync("oldKey"); + await _cryptoService.SetKeyHashAsync(localKeyHash); + } + } + var passwordValid = await _cryptoService.CompareAndUpdateKeyHashAsync(inputtedValue, key2); + if (passwordValid) + { + if (_isPinProtected) + { + var protectedPin = await _stateService.GetProtectedPinAsync(); + var encKey = await _cryptoService.GetEncKeyAsync(key2); + var decPin = await _cryptoService.DecryptToUtf8Async(new EncString(protectedPin), encKey); + var pinKey = await _cryptoService.MakePinKeyAysnc(decPin, email, + kdfConfig); + await _stateService.SetPinProtectedKeyAsync(await _cryptoService.EncryptAsync(key2.Key, pinKey)); + } + await AppHelpers.ResetInvalidUnlockAttemptsAsync(); + await SetKeyAndContinueAsync(key2, true); + } + else + { + await HandleFailedCredentialsAsync(); + } } } + finally + { + _checkingPassword = false; + } } private async Task HandleFailedCredentialsAsync() From 52843b4181e05ac4bbfb60a7431fa72537f92228 Mon Sep 17 00:00:00 2001 From: ifernandezdiaz Date: Wed, 14 Jun 2023 09:34:38 -0300 Subject: [PATCH 05/19] [PM-2544] Adding AutomationIDs for CipherAddEditViewPage elements (#2564) * Adding AutomationIDs for Add/Edit Items page * Adding IDs to CustomFields * Adding Matt's suggestions * Adding newest suggestions --- .../BooleanCustomFieldItemLayout.xaml | 9 +- .../HiddenCustomFieldItemLayout.xaml | 12 +- .../LinkedCustomFieldItemLayout.xaml | 12 +- .../TextCustomFieldItemLayout.xaml | 15 +- src/App/Pages/Vault/CipherAddEditPage.xaml | 169 ++++++++++++------ 5 files changed, 142 insertions(+), 75 deletions(-) diff --git a/src/App/Lists/ItemLayouts/CustomFields/BooleanCustomFieldItemLayout.xaml b/src/App/Lists/ItemLayouts/CustomFields/BooleanCustomFieldItemLayout.xaml index c50cf53eb..6c7d22ed7 100644 --- a/src/App/Lists/ItemLayouts/CustomFields/BooleanCustomFieldItemLayout.xaml +++ b/src/App/Lists/ItemLayouts/CustomFields/BooleanCustomFieldItemLayout.xaml @@ -33,7 +33,8 @@ StyleClass="box-label" Grid.Row="0" Grid.Column="0" - IsVisible="{Binding IsEditing, Mode=OneWay, Converter={StaticResource inverseBool}}" /> + IsVisible="{Binding IsEditing, Mode=OneWay, Converter={StaticResource inverseBool}}" + AutomationId="BooleanCustomFieldNameLabel" /> + ColumnSpacing="10" + AutomationId="GeneratedPasswordRow"> @@ -71,12 +74,14 @@ Grid.Column="0" Grid.Row="0" StyleClass="list-title, list-title-platform, text-html" - Text="{Binding Password, Mode=OneWay, Converter={StaticResource coloredPassword}}" /> + Text="{Binding Password, Mode=OneWay, Converter={StaticResource coloredPassword}}" + AutomationId="GeneratedPasswordValue" /> diff --git a/src/App/Pages/Vault/CiphersPage.xaml b/src/App/Pages/Vault/CiphersPage.xaml index 36b9e01c0..f35d7dc6f 100644 --- a/src/App/Pages/Vault/CiphersPage.xaml +++ b/src/App/Pages/Vault/CiphersPage.xaml @@ -41,7 +41,8 @@ HorizontalOptions="FillAndExpand" TextChanged="SearchBar_TextChanged" SearchButtonPressed="SearchBar_SearchButtonPressed" - Placeholder="{Binding PageTitle}" /> + Placeholder="{Binding PageTitle}" + AutomationId="SearchBar" /> @@ -91,7 +92,8 @@ Source="empty_items_state" />