From 4a3bb4b24110dadc45d905203738766cfd1bc049 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rui=20Tom=C3=A9?= <108268980+r-tome@users.noreply.github.com> Date: Wed, 25 Jan 2023 14:00:46 +0000 Subject: [PATCH] [EC-639] Replacing apostrophe char for email values in Policies API request (#4390) * [EC-639] Replacing single quote char for email values in Policies API request * [EC-639] Added Utils.encodeRFC3986URIComponent and used in PolicyApiService and TwoFactorAuthenticatorComponent * [EC-639] Added unit tests for Utils.encodeRFC3986URIComponent --- .../two-factor-authenticator.component.ts | 3 ++- libs/common/spec/misc/utils.spec.ts | 17 +++++++++++++++++ libs/common/src/misc/utils.ts | 12 ++++++++++++ .../src/services/policy/policy-api.service.ts | 3 ++- 4 files changed, 33 insertions(+), 2 deletions(-) diff --git a/apps/web/src/app/settings/two-factor-authenticator.component.ts b/apps/web/src/app/settings/two-factor-authenticator.component.ts index 652ee24a14..90825f98be 100644 --- a/apps/web/src/app/settings/two-factor-authenticator.component.ts +++ b/apps/web/src/app/settings/two-factor-authenticator.component.ts @@ -7,6 +7,7 @@ import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUti import { StateService } from "@bitwarden/common/abstractions/state.service"; import { UserVerificationService } from "@bitwarden/common/abstractions/userVerification/userVerification.service.abstraction"; import { TwoFactorProviderType } from "@bitwarden/common/enums/twoFactorProviderType"; +import { Utils } from "@bitwarden/common/misc/utils"; import { UpdateTwoFactorAuthenticatorRequest } from "@bitwarden/common/models/request/update-two-factor-authenticator.request"; import { TwoFactorAuthenticatorResponse } from "@bitwarden/common/models/response/two-factor-authenticator.response"; import { AuthResponse } from "@bitwarden/common/types/authResponse"; @@ -99,7 +100,7 @@ export class TwoFactorAuthenticatorComponent element: document.getElementById("qr"), value: "otpauth://totp/Bitwarden:" + - encodeURIComponent(email) + + Utils.encodeRFC3986URIComponent(email) + "?secret=" + encodeURIComponent(this.key) + "&issuer=Bitwarden", diff --git a/libs/common/spec/misc/utils.spec.ts b/libs/common/spec/misc/utils.spec.ts index fb57c33fff..e2fb27a1eb 100644 --- a/libs/common/spec/misc/utils.spec.ts +++ b/libs/common/spec/misc/utils.spec.ts @@ -309,4 +309,21 @@ describe("Utils Service", () => { expect(Utils.recordToMap(map as any)).toEqual(map); }); }); + + describe("encodeRFC3986URIComponent", () => { + it("returns input string with expected encoded chars", () => { + expect(Utils.encodeRFC3986URIComponent("test'user@example.com")).toBe( + "test%27user%40example.com" + ); + expect(Utils.encodeRFC3986URIComponent("(test)user@example.com")).toBe( + "%28test%29user%40example.com" + ); + expect(Utils.encodeRFC3986URIComponent("testuser!@example.com")).toBe( + "testuser%21%40example.com" + ); + expect(Utils.encodeRFC3986URIComponent("Test*User@example.com")).toBe( + "Test%2AUser%40example.com" + ); + }); + }); }); diff --git a/libs/common/src/misc/utils.ts b/libs/common/src/misc/utils.ts index 48deeefcd3..821f02d0a0 100644 --- a/libs/common/src/misc/utils.ts +++ b/libs/common/src/misc/utils.ts @@ -486,6 +486,18 @@ export class Utils { return Object.assign(destination, source) as unknown as Merge; } + /** + * encodeURIComponent escapes all characters except the following: + * alphabetic, decimal digits, - _ . ! ~ * ' ( ) + * https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/encodeURIComponent#encoding_for_rfc3986 + */ + static encodeRFC3986URIComponent(str: string): string { + return encodeURIComponent(str).replace( + /[!'()*]/g, + (c) => `%${c.charCodeAt(0).toString(16).toUpperCase()}` + ); + } + private static isMobile(win: Window) { let mobile = false; ((a) => { diff --git a/libs/common/src/services/policy/policy-api.service.ts b/libs/common/src/services/policy/policy-api.service.ts index 98848692dd..5b15f0199e 100644 --- a/libs/common/src/services/policy/policy-api.service.ts +++ b/libs/common/src/services/policy/policy-api.service.ts @@ -5,6 +5,7 @@ import { PolicyApiServiceAbstraction } from "../../abstractions/policy/policy-ap import { InternalPolicyService } from "../../abstractions/policy/policy.service.abstraction"; import { StateService } from "../../abstractions/state.service"; import { PolicyType } from "../../enums/policyType"; +import { Utils } from "../../misc/utils"; import { PolicyData } from "../../models/data/policy.data"; import { MasterPasswordPolicyOptions } from "../../models/domain/master-password-policy-options"; import { PolicyRequest } from "../../models/request/policy.request"; @@ -54,7 +55,7 @@ export class PolicyApiService implements PolicyApiServiceAbstraction { "token=" + encodeURIComponent(token) + "&email=" + - encodeURIComponent(email) + + Utils.encodeRFC3986URIComponent(email) + "&organizationUserId=" + organizationUserId, null,