1
0
mirror of https://github.com/bitwarden/browser synced 2026-01-27 06:43:41 +00:00

[PM-28842] Add max length validation to master password policy form (#18237)

* Update master password policy dialog to limit the minimum length to 128

* Update master password policy to use dynamic maximum length from Utils

* Add unit tests for MasterPasswordPolicyComponent to validate password length constraints and scoring
This commit is contained in:
Rui Tomé
2026-01-26 11:38:10 +00:00
committed by GitHub
parent 903026b574
commit 71db33d45d
4 changed files with 76 additions and 1 deletions

View File

@@ -32,6 +32,7 @@
formControlName="minLength"
id="minLength"
[min]="MinPasswordLength"
[max]="MaxPasswordLength"
/>
</bit-form-field>
</div>

View File

@@ -0,0 +1,69 @@
import { NO_ERRORS_SCHEMA } from "@angular/core";
import { ComponentFixture, TestBed } from "@angular/core/testing";
import { mock } from "jest-mock-extended";
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { Utils } from "@bitwarden/common/platform/misc/utils";
import { MasterPasswordPolicyComponent } from "./master-password.component";
describe("MasterPasswordPolicyComponent", () => {
let component: MasterPasswordPolicyComponent;
let fixture: ComponentFixture<MasterPasswordPolicyComponent>;
beforeEach(async () => {
await TestBed.configureTestingModule({
providers: [
{ provide: I18nService, useValue: mock<I18nService>() },
{ provide: OrganizationService, useValue: mock<OrganizationService>() },
{ provide: AccountService, useValue: mock<AccountService>() },
],
schemas: [NO_ERRORS_SCHEMA],
}).compileComponents();
fixture = TestBed.createComponent(MasterPasswordPolicyComponent);
component = fixture.componentInstance;
});
it("should accept minimum password length of 12", () => {
component.data.patchValue({ minLength: 12 });
expect(component.data.get("minLength")?.valid).toBe(true);
});
it("should accept maximum password length of 128", () => {
component.data.patchValue({ minLength: 128 });
expect(component.data.get("minLength")?.valid).toBe(true);
});
it("should reject password length below minimum", () => {
component.data.patchValue({ minLength: 11 });
expect(component.data.get("minLength")?.hasError("min")).toBe(true);
});
it("should reject password length above maximum", () => {
component.data.patchValue({ minLength: 129 });
expect(component.data.get("minLength")?.hasError("max")).toBe(true);
});
it("should use correct minimum from Utils", () => {
expect(component.MinPasswordLength).toBe(Utils.minimumPasswordLength);
expect(component.MinPasswordLength).toBe(12);
});
it("should use correct maximum from Utils", () => {
expect(component.MaxPasswordLength).toBe(Utils.maximumPasswordLength);
expect(component.MaxPasswordLength).toBe(128);
});
it("should have password scores from 0 to 4", () => {
const scores = component.passwordScores.filter((s) => s.value !== null).map((s) => s.value);
expect(scores).toEqual([0, 1, 2, 3, 4]);
});
});

View File

@@ -34,10 +34,14 @@ export class MasterPasswordPolicy extends BasePolicyEditDefinition {
})
export class MasterPasswordPolicyComponent extends BasePolicyEditComponent implements OnInit {
MinPasswordLength = Utils.minimumPasswordLength;
MaxPasswordLength = Utils.maximumPasswordLength;
data: FormGroup<ControlsOf<MasterPasswordPolicyOptions>> = this.formBuilder.group({
minComplexity: [null],
minLength: [this.MinPasswordLength, [Validators.min(Utils.minimumPasswordLength)]],
minLength: [
this.MinPasswordLength,
[Validators.min(Utils.minimumPasswordLength), Validators.max(this.MaxPasswordLength)],
],
requireUpper: [false],
requireLower: [false],
requireNumbers: [false],

View File

@@ -42,6 +42,7 @@ export class Utils {
static readonly validHosts: string[] = ["localhost"];
static readonly originalMinimumPasswordLength = 8;
static readonly minimumPasswordLength = 12;
static readonly maximumPasswordLength = 128;
static readonly DomainMatchBlacklist = new Map<string, Set<string>>([
["google.com", new Set(["script.google.com"])],
]);