1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-16 08:13:42 +00:00

[PM-25458] Add error handling stubs & logging for critical decrypt paths (#16284)

* Add error handling stubs for critical decrypt paths

* Fix collection name decrypt

* Update docs

* address feedback

---------

Co-authored-by: Jake Fink <jfink@bitwarden.com>
This commit is contained in:
Bernd Schoolmann
2025-09-09 23:19:00 +09:00
committed by GitHub
parent 15619c6265
commit 7985487d5b
7 changed files with 73 additions and 16 deletions

View File

@@ -118,7 +118,17 @@ export class CollectionAdminView extends CollectionView {
orgKey: OrgKey,
): Promise<CollectionAdminView> {
const view = new CollectionAdminView({ ...collection });
try {
view.name = await encryptService.decryptString(new EncString(view.name), orgKey);
} catch (e) {
// Note: This should be replaced by the owning team with appropriate, domain-specific behavior.
// eslint-disable-next-line no-console
console.error(
"[CollectionAdminView/fromCollectionAccessDetails] Error decrypting collection name",
e,
);
throw e;
}
view.assigned = collection.assigned;
view.readOnly = collection.readOnly;
view.hidePasswords = collection.hidePasswords;
@@ -144,9 +154,22 @@ export class CollectionAdminView extends CollectionView {
encryptService: EncryptService,
orgKey: OrgKey,
): Promise<CollectionAdminView> {
let collectionName: string;
try {
collectionName = await encryptService.decryptString(new EncString(collection.name), orgKey);
} catch (e) {
// Note: This should be updated by the owning team with appropriate, domain specific behavior
// eslint-disable-next-line no-console
console.error(
"[CollectionAdminView/fromCollectionResponse] Failed to decrypt the collection name",
e,
);
throw e;
}
const collectionAdminView = new CollectionAdminView({
id: collection.id,
name: await encryptService.decryptString(new EncString(collection.name), orgKey),
name: collectionName,
organizationId: collection.organizationId,
});

View File

@@ -144,7 +144,15 @@ export class CollectionView implements View, ITreeNodeObject {
): Promise<CollectionView> {
const view = new CollectionView({ ...collection });
try {
view.name = await encryptService.decryptString(new EncString(collection.name), orgKey);
} catch (e) {
// Note: This should be replaced by the owning team with appropriate, domain-specific behavior.
// eslint-disable-next-line no-console
console.error("[CollectionView] Error decrypting collection name", e);
throw e;
}
view.externalId = collection.externalId;
view.type = collection.type;
view.assigned = collection.assigned;

View File

@@ -314,12 +314,18 @@ export class TokenService implements TokenServiceAbstraction {
);
}
try {
const decryptedAccessToken = await this.encryptService.decryptString(
encryptedAccessToken,
accessTokenKey,
);
return decryptedAccessToken;
} catch (e) {
// Note: This should be replaced by the owning team with appropriate, domain-specific behavior.
this.logService.error("[TokenService] Error decrypting access token", e);
throw e;
}
}
/**

View File

@@ -28,6 +28,9 @@ export abstract class EncryptService {
/**
* Decrypts an EncString to a string
* @throws IMPORTANT: This throws if decryption fails. If decryption failures are expected to happen,
* the callsite should log where the failure occurred, and handle it by domain specifc logic (e.g. show a UI error).
*
* @param encString - The EncString containing the encrypted string.
* @param key - The key to decrypt the value with
* @returns The decrypted string
@@ -36,10 +39,12 @@ export abstract class EncryptService {
abstract decryptString(encString: EncString, key: SymmetricCryptoKey): Promise<string>;
/**
* Decrypts an EncString to a Uint8Array
* @throws IMPORTANT: This throws if decryption fails. If decryption failures are expected to happen,
* the callsite should log where the failure occurred, and handle it by domain specifc logic (e.g. show a UI error).
*
* @param encString - The EncString containing the encrypted bytes.
* @param key - The key to decrypt the value with
* @returns The decrypted bytes as a Uint8Array
* @throws Error if decryption fails
* @deprecated Bytes are not the right abstraction to encrypt in. Use e.g. key wrapping or file encryption instead
*/
abstract decryptBytes(encString: EncString, key: SymmetricCryptoKey): Promise<Uint8Array>;

View File

@@ -180,9 +180,13 @@ export class EncString {
const encryptService = Utils.getContainerService().getEncryptService();
this.decryptedValue = await encryptService.decryptString(this, key);
// FIXME: Remove when updating file. Eslint update
// eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (e) {
// eslint-disable-next-line no-console
console.error(
"[EncString Generic Decrypt] failed to decrypt encstring. Context: " +
(context ?? "No context"),
e,
);
this.decryptedValue = DECRYPT_ERROR;
}
return this.decryptedValue;

View File

@@ -50,7 +50,14 @@ export class Folder extends Domain {
const folderView = new FolderView();
folderView.id = this.id;
folderView.revisionDate = this.revisionDate;
try {
folderView.name = await encryptService.decryptString(this.name, key);
} catch (e) {
// Note: This should be replaced by the owning team with appropriate, domain-specific behavior.
// eslint-disable-next-line no-console
console.error("[Folder] Error decrypting folder", e);
throw e;
}
return folderView;
}

View File

@@ -827,10 +827,14 @@ export class DefaultKeyService implements KeyServiceAbstraction {
}
return this.stateProvider.getUser(userId, USER_ENCRYPTED_PRIVATE_KEY).state$.pipe(
switchMap(
async (encryptedPrivateKey) =>
await this.decryptPrivateKey(encryptedPrivateKey, userKey),
),
switchMap(async (encryptedPrivateKey) => {
try {
return await this.decryptPrivateKey(encryptedPrivateKey, userKey);
} catch (e) {
this.logService.error("Failed to decrypt private key for user ", userId, e);
throw e;
}
}),
// Combine outerscope info with user private key
map((userPrivateKey) => ({
userKey,