Skip to content

[PM-38866] feat: Integrate SDK-based password generation into AutoFill extension - #2828

Merged
fedemkr merged 9 commits into
mainfrom
PM-38866/integrate-password-generation-sdk
Jul 21, 2026
Merged

[PM-38866] feat: Integrate SDK-based password generation into AutoFill extension#2828
fedemkr merged 9 commits into
mainfrom
PM-38866/integrate-password-generation-sdk

Conversation

@fedemkr

@fedemkr fedemkr commented Jun 24, 2026

Copy link
Copy Markdown
Member

🎟️ Tracking

https://bitwarden.atlassian.net/browse/PM-38866

📔 Objective

Integrates the Bitwarden SDK into the AutoFill extension for iOS 26.2+
password generation requests:

  • No-UI path (PM-29569): AppProcessor.generatePasswordCredential(request:)
    calls GeneratorRepository.getEffectivePasswordGenerationOptions(rules:) to
    apply developer-provided password rules and org policies, then generates a
    password or passphrase via the SDK.
  • With-UI path (PM-29570): VaultCoordinator.showGeneratePassword() extracts
    password rules from the extension mode and passes them to the generator view,
    so the UI pre-selects options consistent with the site's rules.
  • Centralization (PM-38866): GeneratorRepository gains
    getEffectivePasswordGenerationOptions(rules:) — a single entry point that
    merges SDK-parsed rules with org policy and user preferences, replacing the
    scattered hardcoded PasswordGeneratorRequest placeholder.

@fedemkr fedemkr added the ai-review Request a Claude code review label Jun 24, 2026
@github-actions github-actions Bot added app:password-manager Bitwarden Password Manager app context app:authenticator Bitwarden Authenticator app context t:feature labels Jun 24, 2026
@fedemkr
fedemkr changed the base branch from main to PM-29570/extension-password-generation-with-ui June 24, 2026 21:31
@github-actions

github-actions Bot commented Jun 24, 2026

Copy link
Copy Markdown
Contributor

🤖 Bitwarden Claude Code Review

Overall Assessment: APPROVE

Reviewed the SDK-based password generation integration into the AutoFill extension across the no-UI (AppProcessor.generatePasswordCredential) and with-UI (VaultCoordinator/AppCoordinator → generator) paths, plus the new GeneratorRepository.getEffectivePasswordGenerationOptions(rules:) centralization. The PasswordGenerationOptions merge logic (apply, setMin*) correctly enforces most-restrictive semantics, and the previously-flagged inverted setMinLength comparison is now fixed via max(...). Test coverage is thorough, including forced-rules floors, policy interaction, and savePasswordHistory gating.

Code Review Details

No new blocking findings. Notes on items reviewed and intentionally not flagged:

  • The prior CRITICAL on setMinLength (inverted comparison) is resolved by the max(...) rewrite.
  • The prior QUESTION on forcedPasswordRules enforcement after load was answered by the author ("That's ok").
  • Site password rules force type = .password even over an org policy that forces .passphrase (GeneratorRepository.swift:250-254). This is a defensible product choice for the generate-password credential flow and closely tracks the already-answered thread, so it is left to the author's judgment.
  • initialRoute intentionally bypasses the .didStart unlock flow, consistent with savePasswordHistory: false; UInt8 conversions are bounded by the SDK (≤255) and UI (≤128).

Comment on lines 98 to 106
mutating func setMinLength(_ minimumLength: Int) {
if let length, length < minimumLength {
self.length = minimumLength
} else if length == nil {
if let length {
if length > minimumLength {
self.length = minimumLength
}
} else {
length = minimumLength
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CRITICAL: Inverted comparison breaks minimum-length enforcement (org policy + password rules).

Details and fix

This PR inverted the comparison in setMinLength. The previous implementation raised a too-short length up to the minimum; the new one lowers length down to minimumLength when the current length is larger, and does nothing when the current length is shorter — the opposite of "set minimum".

Impact:

  • PolicyService.applyPasswordGenerationPolicy (PolicyService.swift:350) calls setMinLength(minLength) to enforce an organization's minimum password-length policy. With this change, when the org policy minimum (e.g. 20) exceeds the user's saved length (e.g. 14), the policy is not enforced and a shorter password is generated. When the policy minimum is below the saved length, the length is truncated down, weakening the user's chosen length.
  • apply(_:) (this file, line 179) calls setMinLength(Int(request.length)) for AutoFill site rules, so site-required lengths are likewise ignored.

This also contradicts the unchanged existing test test_setMinLength (PasswordGenerationOptionsTests.swift:64, expects raise-up) and the new test_apply_raisesLength / test_apply_preservesHigherMinimums, plus the apply(_:) doc ("raised to the maximum of the two values"). All four sibling setMin* functions still raise up.

mutating func setMinLength(_ minimumLength: Int) {
    if let length, length < minimumLength {
        self.length = minimumLength
    } else if length == nil {
        length = minimumLength
    }
}

@fedemkr
fedemkr marked this pull request as ready for review June 24, 2026 21:37
@fedemkr
fedemkr requested review from a team and matt-livefront as code owners June 24, 2026 21:37
@github-actions github-actions Bot removed the app:authenticator Bitwarden Authenticator app context label Jun 24, 2026
Comment thread BitwardenShared/Core/Tools/Repositories/TestHelpers/MockGeneratorRepository.swift Outdated
Comment thread BitwardenShared/Core/Tools/Repositories/GeneratorRepository.swift Outdated
Comment thread BitwardenShared/UI/Tools/Generator/Generator/GeneratorState.swift Outdated
Comment thread BitwardenShared/UI/Vault/Vault/VaultCoordinator.swift Outdated
Comment thread BitwardenShared/Core/Tools/Models/Domain/PasswordGenerationOptions.swift Outdated
Comment thread BitwardenShared/Core/Tools/Models/Domain/PasswordGenerationOptions.swift Outdated
@fedemkr
fedemkr requested a review from matt-livefront July 8, 2026 19:56
@codecov

codecov Bot commented Jul 8, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 66.66667% with 50 lines in your changes missing coverage. Please review.
✅ Project coverage is 81.23%. Comparing base (7ecd504) to head (4f57d4f).

Files with missing lines Patch % Lines
.../Core/Tools/Repositories/GeneratorRepository.swift 16.12% 26 Missing ⚠️
...ools/Models/Domain/PasswordGenerationOptions.swift 74.50% 13 Missing ⚠️
...llExtension/CredentialProviderViewController.swift 0.00% 6 Missing ⚠️
...nShared/UI/Platform/Application/AppProcessor.swift 80.00% 3 Missing ⚠️
...entialProviderContext/CredentialProviderMode.swift 66.66% 2 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2828      +/-   ##
==========================================
- Coverage   81.26%   81.23%   -0.04%     
==========================================
  Files        1028     1029       +1     
  Lines       66273    66378     +105     
==========================================
+ Hits        53858    53919      +61     
- Misses      12415    12459      +44     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Base automatically changed from PM-29570/extension-password-generation-with-ui to main July 8, 2026 21:27
fedemkr added 5 commits July 13, 2026 17:06
… flow

Password generation doesn't require vault access, so skip the auth/unlock
screen by passing an initialRoute directly to AppProcessor.start and guard
saveGeneratedValue against adding to password history when the vault is not
unlocked.
- Unify all setMin* methods in PasswordGenerationOptions to
  `field = max(field ?? minimum, minimum)` for consistency; also fix
  the inverted condition in setMinLength and a wrong param name in the
  setMinSpecial doc comment
- Simplify MockGeneratorRepository.passwordRulesRequestResult from
  Result<PasswordGeneratorRequest, Error> to PasswordGeneratorRequest?
- Extract CredentialProviderMode.generatePasswordRules computed property
  and add CredentialProviderModeTests
…tial

A merge conflict resolution in f292ba1 reintroduced the
unlockVaultWithNeverlockKey() call that had been intentionally removed
in 3713d75, since password generation only relies on the SDK/local
settings and has no vault dependency. This restores that fix so
AppProcessorTests.test_generatePasswordCredential passes again.
…edPasswordRules, add Info.plist capabilities
@fedemkr
fedemkr force-pushed the PM-38866/integrate-password-generation-sdk branch from b5150e0 to c55da60 Compare July 14, 2026 12:15
options: &passwordOptions,
)
let (passwordOptions, isPolicyInEffect) = try await services.generatorRepository
.getEffectivePasswordGenerationOptions(rules: state.forcedPasswordRules)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

QUESTION: forcedPasswordRules enforcement appears to be dropped after this initial load.

Details

forcedPasswordRules is only consumed here in loadGeneratorOptions(). On .appeared, reloadGeneratorOptions() runs (setting state.isPolicyInEffect from the rules-derived value), then generateValue(shouldSavePassword: true) immediately calls validatePasswordOptionsAndApplyPolicies(), which recomputes state.isPolicyInEffect from applyPasswordGenerationPolicy (org policy only) and re-applies only the org policy — the site rules are not re-applied.

Two consequences:

  1. The rules-derived isPolicyInEffect == true returned by getEffectivePasswordGenerationOptions is overwritten on the first generation, so the "policy in effect" indicator/locking for site rules effectively never persists in the with-UI flow.
  2. Since validateOptions()/org-policy application no longer treat the site-rule minimums as floors, a user lowering a slider below a rule minimum won't be re-clamped on regeneration.

Is this intended (rules seed the initial options, user may freely override), or should the site rules remain enforced across regenerations? If the former, the true return value from getEffectivePasswordGenerationOptions when rules are applied may be misleading for the UI path.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's ok

let view = try XCTUnwrap((action.view as? UIHostingController<VaultAutofillListView>)?.rootView)
XCTAssertEqual(view.store.state.group, .identity)
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

QUESTION: This PR deletes test_navigateTo_flightRecorderSettings, which covered existing behavior unrelated to SDK password generation.

Details

The .flightRecorderSettings route and its handler in VaultCoordinator.swift (delegate?.switchToSettingsTab(route: .about)) still exist and are unchanged by this PR. Removing this test drops coverage for that path with no corresponding production change — this looks like an accidental deletion (possibly a rebase artifact). Was this intentional? If not, please restore the test.

…tions

forcedPasswordRules was only applied once during loadGeneratorOptions.
validatePasswordOptionsAndApplyPolicies (called on every generation) only
applied org policy, so a user could lower sliders below site-rule minimums
after the initial load, and isPolicyInEffect was overwritten to false for
rules-only flows.

Exposes passwordRulesRequest on GeneratorRepository so the processor can
cache the parsed constraint on load and re-apply it as floors on each call
to validatePasswordOptionsAndApplyPolicies, without affecting isPolicyInEffect
(which remains org-policy-only).

Also fixes pre-existing test failures introduced in c55da60 where the mock's
getEffectivePasswordGenerationOptions bypassed getPasswordGenerationOptions.
Comment thread BitwardenShared/UI/Vault/Vault/VaultCoordinatorTests.swift
Comment thread BitwardenShared/Core/Tools/Repositories/GeneratorRepository.swift Outdated
@fedemkr
fedemkr requested a review from matt-livefront July 20, 2026 20:11
@fedemkr
fedemkr merged commit 8aa7502 into main Jul 21, 2026
22 checks passed
@fedemkr
fedemkr deleted the PM-38866/integrate-password-generation-sdk branch July 21, 2026 16:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-review Request a Claude code review app:password-manager Bitwarden Password Manager app context t:feature

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants