From 591f44438a1ff30c269b084117aefa2e8c0bdfcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rui=20Tom=C3=A9?= <108268980+r-tome@users.noreply.github.com> Date: Tue, 25 Jun 2024 02:09:09 +0100 Subject: [PATCH] =?UTF-8?q?[AC-1609]=C2=A0Update=20domain=20name=20validat?= =?UTF-8?q?ion=20regex=20to=20support=20single=20letter=20name=20(#9336)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../validators/domain-name.validator.spec.ts | 37 +++++++++++++++++++ .../validators/domain-name.validator.ts | 30 +++++++-------- 2 files changed, 51 insertions(+), 16 deletions(-) create mode 100644 bitwarden_license/bit-web/src/app/admin-console/organizations/manage/domain-verification/domain-add-edit-dialog/validators/domain-name.validator.spec.ts diff --git a/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/domain-verification/domain-add-edit-dialog/validators/domain-name.validator.spec.ts b/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/domain-verification/domain-add-edit-dialog/validators/domain-name.validator.spec.ts new file mode 100644 index 0000000000..44eaa17875 --- /dev/null +++ b/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/domain-verification/domain-add-edit-dialog/validators/domain-name.validator.spec.ts @@ -0,0 +1,37 @@ +import { AbstractControl, ValidationErrors } from "@angular/forms"; + +import { domainNameValidator } from "./domain-name.validator"; + +describe("domainNameValidator", () => { + let validatorFn: (control: AbstractControl) => ValidationErrors | null; + const errorMessage = "Invalid domain name"; + + beforeEach(() => { + validatorFn = domainNameValidator(errorMessage); + }); + + const testCases = [ + { value: "e.com", expected: null }, + { value: "example.com", expected: null }, + { value: "sub.example.com", expected: null }, + { value: "sub.sub.example.com", expected: null }, + { value: "example.co.uk", expected: null }, + { value: "example", expected: { invalidDomainName: { message: errorMessage } } }, + { value: "-example.com", expected: { invalidDomainName: { message: errorMessage } } }, + { value: "example-.com", expected: { invalidDomainName: { message: errorMessage } } }, + { value: "example..com", expected: { invalidDomainName: { message: errorMessage } } }, + { value: "http://example.com", expected: { invalidDomainName: { message: errorMessage } } }, + { value: "www.example.com", expected: { invalidDomainName: { message: errorMessage } } }, + { value: "", expected: null }, + { value: "x".repeat(64) + ".com", expected: { invalidDomainName: { message: errorMessage } } }, + ]; + + describe("run test cases", () => { + testCases.forEach(({ value, expected }) => { + test(`should return ${JSON.stringify(expected)} for value "${value}"`, () => { + const control = { value } as AbstractControl; + expect(validatorFn(control)).toEqual(expected); + }); + }); + }); +}); diff --git a/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/domain-verification/domain-add-edit-dialog/validators/domain-name.validator.ts b/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/domain-verification/domain-add-edit-dialog/validators/domain-name.validator.ts index e49ca16e19..55682c891f 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/domain-verification/domain-add-edit-dialog/validators/domain-name.validator.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/organizations/manage/domain-verification/domain-add-edit-dialog/validators/domain-name.validator.ts @@ -13,24 +13,22 @@ export function domainNameValidator(errorMessage: string): ValidatorFn { // We do not want any prefixes per industry standards. // Must support top-level domains and any number of subdomains. - // / # start regex - // ^ # start of string - // (?!(http(s)?:\/\/|www\.)) # negative lookahead to check if input doesn't match "http://", "https://" or "www." - // [a-zA-Z0-9] # first character must be a letter or a number - // [a-zA-Z0-9-]{0,61} # domain name can have 0 to 61 characters that are letters, numbers, or hyphens - // [a-zA-Z0-9] # domain name must end with a letter or a number - // (?: # start of non-capturing group (subdomain sections are optional) - // \. # subdomain must have a period - // [a-zA-Z0-9] # first character of subdomain must be a letter or a number - // [a-zA-Z0-9-]{0,61} # subdomain can have 0 to 61 characters that are letters, numbers, or hyphens - // [a-zA-Z0-9] # subdomain must end with a letter or a number - // )* # end of non-capturing group (subdomain sections are optional) - // \. # domain name must have a period - // [a-zA-Z]{2,} # domain name must have at least two letters (the domain extension) - // $/ # end of string + // / # start regex + // ^ # start of string + // (?!(http(s)?:\/\/|www\.)) # negative lookahead to check if input doesn't match "http://", "https://" or "www." + // ( # start of capturing group for the entire domain + // [a-zA-Z0-9] # first character of domain must be a letter or a number + // ( # start of optional group for subdomain or domain section + // [a-zA-Z0-9-]{0,61} # subdomain/domain section can have 0 to 61 characters that are letters, numbers, or hyphens + // [a-zA-Z0-9] # subdomain/domain section must end with a letter or a number + // )? # end of optional group for subdomain or domain section + // \. # subdomain/domain section must have a period + // )+ # end of capturing group for the entire domain, repeatable for subdomains + // [a-zA-Z]{2,} # domain name must have at least two letters (the domain extension) + // $/ # end of string const validDomainNameRegex = - /^(?!(http(s)?:\/\/|www\.))[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9](?:\.[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9])*\.[a-zA-Z]{2,}$/; + /^(?!(http(s)?:\/\/|www\.))([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/; const invalid = !validDomainNameRegex.test(control.value);