1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-06 00:13:28 +00:00

[PM-28950] Add enum normalizers to protect against corrupted user data in SDK mapping (#17723)

* Added normalizers to protect against corrpupted user data when mapping between client and SDK

* Added comments

* simplified secure note normalization
This commit is contained in:
SmithThe4th
2025-12-01 11:11:25 -05:00
committed by GitHub
parent 30b89d1fc2
commit 4a2858132d
9 changed files with 121 additions and 14 deletions

View File

@@ -1,3 +1,5 @@
import { UriMatchType } from "@bitwarden/sdk-internal";
/*
See full documentation at:
https://bitwarden.com/help/uri-match-detection/#match-detection-options
@@ -23,3 +25,28 @@ export type UriMatchStrategySetting = (typeof UriMatchStrategy)[keyof typeof Uri
// using uniqueness properties of object shape over Set for ease of state storability
export type NeverDomains = { [id: string]: null | { bannerIsDismissed?: boolean } };
export type EquivalentDomains = string[][];
/**
* Normalizes UriMatchStrategySetting for SDK mapping.
* @param value - The URI match strategy from user data
* @returns Valid UriMatchType or undefined if invalid
*/
export function normalizeUriMatchStrategyForSdk(
value: UriMatchStrategySetting | undefined,
): UriMatchType | undefined {
if (value == null) {
return undefined;
}
switch (value) {
case 0: // Domain
case 1: // Host
case 2: // StartsWith
case 3: // Exact
case 4: // RegularExpression
case 5: // Never
return value;
default:
return undefined;
}
}

View File

@@ -1,3 +1,5 @@
import { CipherRepromptType as SdkCipherRepromptType } from "@bitwarden/sdk-internal";
import { UnionOfValues } from "../types/union-of-values";
export const CipherRepromptType = {
@@ -6,3 +8,20 @@ export const CipherRepromptType = {
} as const;
export type CipherRepromptType = UnionOfValues<typeof CipherRepromptType>;
/**
* Normalizes a CipherRepromptType value to ensure compatibility with the SDK.
* @param value - The cipher reprompt type from user data
* @returns Valid CipherRepromptType, defaults to CipherRepromptType.None if unrecognized
*/
export function normalizeCipherRepromptTypeForSdk(
value: CipherRepromptType,
): SdkCipherRepromptType {
switch (value) {
case CipherRepromptType.None:
case CipherRepromptType.Password:
return value;
default:
return CipherRepromptType.None;
}
}

View File

@@ -1,3 +1,5 @@
import { FieldType as SdkFieldType } from "@bitwarden/sdk-internal";
const _FieldType = Object.freeze({
Text: 0,
Hidden: 1,
@@ -10,3 +12,20 @@ type _FieldType = typeof _FieldType;
export type FieldType = _FieldType[keyof _FieldType];
export const FieldType: Record<keyof _FieldType, FieldType> = _FieldType;
/**
* Normalizes a FieldType value to ensure compatibility with the SDK.
* @param value - The field type from user data
* @returns Valid FieldType, defaults to FieldType.Text if unrecognized
*/
export function normalizeFieldTypeForSdk(value: FieldType): SdkFieldType {
switch (value) {
case FieldType.Text:
case FieldType.Hidden:
case FieldType.Boolean:
case FieldType.Linked:
return value;
default:
return FieldType.Text;
}
}

View File

@@ -1,3 +1,5 @@
import { LinkedIdType as SdkLinkedIdType } from "@bitwarden/sdk-internal";
import { UnionOfValues } from "../types/union-of-values";
export type LinkedIdType = LoginLinkedId | CardLinkedId | IdentityLinkedId;
@@ -46,3 +48,25 @@ export const IdentityLinkedId = {
} as const;
export type IdentityLinkedId = UnionOfValues<typeof IdentityLinkedId>;
/**
* Normalizes a LinkedIdType value to ensure compatibility with the SDK.
* @param value - The linked ID type from user data
* @returns Valid LinkedIdType or undefined if unrecognized
*/
export function normalizeLinkedIdTypeForSdk(
value: LinkedIdType | undefined,
): SdkLinkedIdType | undefined {
if (value == null) {
return undefined;
}
// Check all valid LinkedId numeric values (100-418)
const allValidValues = [
...Object.values(LoginLinkedId),
...Object.values(CardLinkedId),
...Object.values(IdentityLinkedId),
];
return allValidValues.includes(value) ? value : undefined;
}

View File

@@ -1,3 +1,5 @@
import { SecureNoteType as SdkSecureNoteType } from "@bitwarden/sdk-internal";
import { UnionOfValues } from "../types/union-of-values";
export const SecureNoteType = {
@@ -5,3 +7,12 @@ export const SecureNoteType = {
} as const;
export type SecureNoteType = UnionOfValues<typeof SecureNoteType>;
/**
* Normalizes a SecureNoteType value to ensure compatibility with the SDK.
* @param value - The secure note type from user data
* @returns Valid SecureNoteType, defaults to SecureNoteType.Generic if unrecognized
*/
export function normalizeSecureNoteTypeForSdk(value: SecureNoteType): SdkSecureNoteType {
return SecureNoteType.Generic;
}

View File

@@ -9,7 +9,10 @@ import { Utils } from "../../../platform/misc/utils";
import Domain from "../../../platform/models/domain/domain-base";
import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key";
import { InitializerKey } from "../../../platform/services/cryptography/initializer-key";
import { CipherRepromptType } from "../../enums/cipher-reprompt-type";
import {
CipherRepromptType,
normalizeCipherRepromptTypeForSdk,
} from "../../enums/cipher-reprompt-type";
import { CipherType } from "../../enums/cipher-type";
import { conditionalEncString, encStringFrom } from "../../utils/domain-utils";
import { CipherPermissionsApi } from "../api/cipher-permissions.api";
@@ -414,10 +417,7 @@ export class Cipher extends Domain implements Decryptable<CipherView> {
creationDate: this.creationDate.toISOString(),
deletedDate: this.deletedDate?.toISOString(),
archivedDate: this.archivedDate?.toISOString(),
reprompt:
this.reprompt === CipherRepromptType.Password
? CipherRepromptType.Password
: CipherRepromptType.None,
reprompt: normalizeCipherRepromptTypeForSdk(this.reprompt),
// Initialize all cipher-type-specific properties as undefined
login: undefined,
identity: undefined,

View File

@@ -1,11 +1,16 @@
import { Jsonify } from "type-fest";
import { Field as SdkField, LinkedIdType as SdkLinkedIdType } from "@bitwarden/sdk-internal";
import { Field as SdkField } from "@bitwarden/sdk-internal";
import { EncString } from "../../../key-management/crypto/models/enc-string";
import Domain from "../../../platform/models/domain/domain-base";
import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key";
import { FieldType, LinkedIdType } from "../../enums";
import {
FieldType,
LinkedIdType,
normalizeFieldTypeForSdk,
normalizeLinkedIdTypeForSdk,
} from "../../enums";
import { conditionalEncString, encStringFrom } from "../../utils/domain-utils";
import { FieldData } from "../data/field.data";
import { FieldView } from "../view/field.view";
@@ -77,9 +82,8 @@ export class Field extends Domain {
return {
name: this.name?.toSdk(),
value: this.value?.toSdk(),
type: this.type,
// Safe type cast: client and SDK LinkedIdType enums have identical values
linkedId: this.linkedId as unknown as SdkLinkedIdType,
type: normalizeFieldTypeForSdk(this.type),
linkedId: normalizeLinkedIdTypeForSdk(this.linkedId),
};
}

View File

@@ -3,7 +3,10 @@ import { Jsonify } from "type-fest";
import { LoginUri as SdkLoginUri } from "@bitwarden/sdk-internal";
import { EncString } from "../../../key-management/crypto/models/enc-string";
import { UriMatchStrategySetting } from "../../../models/domain/domain-service";
import {
normalizeUriMatchStrategyForSdk,
UriMatchStrategySetting,
} from "../../../models/domain/domain-service";
import { Utils } from "../../../platform/misc/utils";
import Domain from "../../../platform/models/domain/domain-base";
import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key";
@@ -91,7 +94,7 @@ export class LoginUri extends Domain {
return {
uri: this.uri?.toSdk(),
uriChecksum: this.uriChecksum?.toSdk(),
match: this.match,
match: normalizeUriMatchStrategyForSdk(this.match),
};
}

View File

@@ -3,7 +3,7 @@ import { Jsonify } from "type-fest";
import { SecureNote as SdkSecureNote } from "@bitwarden/sdk-internal";
import Domain from "../../../platform/models/domain/domain-base";
import { SecureNoteType } from "../../enums";
import { normalizeSecureNoteTypeForSdk, SecureNoteType } from "../../enums";
import { SecureNoteData } from "../data/secure-note.data";
import { SecureNoteView } from "../view/secure-note.view";
@@ -46,7 +46,7 @@ export class SecureNote extends Domain {
*/
toSdkSecureNote(): SdkSecureNote {
return {
type: this.type,
type: normalizeSecureNoteTypeForSdk(this.type),
};
}