1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-20 03:13:55 +00:00

[PM-24978] Corrupt Attachment Keys (#17790)

* display translated content for attachments that cannot be downloaded

* consume decryption failure from the sdk for attachments

* add decryption errors from sdk

* only show fix attachment issues for when key is null and it does not have a decryption failure

* separate decryption failure state in view
This commit is contained in:
Nick Krantz
2026-02-11 10:31:38 -06:00
committed by GitHub
parent 3f3fc6f984
commit f20686cdf4
12 changed files with 140 additions and 62 deletions

View File

@@ -47,6 +47,12 @@ export class Attachment extends Domain {
if (this.key != null) {
view.key = await this.decryptAttachmentKey(decryptionKey);
view.encryptedKey = this.key; // Keep the encrypted key for the view
// When the attachment key couldn't be decrypted, mark a decryption error
// The file won't be able to be downloaded in these cases
if (!view.key) {
view.hasDecryptionError = true;
}
}
return view;

View File

@@ -2,7 +2,7 @@ import { Jsonify } from "type-fest";
import { AttachmentView as SdkAttachmentView } from "@bitwarden/sdk-internal";
import { EncString } from "../../../key-management/crypto/models/enc-string";
import { DECRYPT_ERROR, EncString } from "../../../key-management/crypto/models/enc-string";
import { View } from "../../../models/view/view";
import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key";
import { Attachment } from "../domain/attachment";
@@ -18,6 +18,7 @@ export class AttachmentView implements View {
* The SDK returns an encrypted key for the attachment.
*/
encryptedKey: EncString | undefined;
private _hasDecryptionError?: boolean;
constructor(a?: Attachment) {
if (!a) {
@@ -41,6 +42,14 @@ export class AttachmentView implements View {
return 0;
}
get hasDecryptionError(): boolean {
return this._hasDecryptionError || this.fileName === DECRYPT_ERROR;
}
set hasDecryptionError(value: boolean) {
this._hasDecryptionError = value;
}
static fromJSON(obj: Partial<Jsonify<AttachmentView>>): AttachmentView {
const key = obj.key == null ? null : SymmetricCryptoKey.fromJSON(obj.key);
@@ -76,7 +85,10 @@ export class AttachmentView implements View {
/**
* Converts the SDK AttachmentView to a AttachmentView.
*/
static fromSdkAttachmentView(obj: SdkAttachmentView): AttachmentView | undefined {
static fromSdkAttachmentView(
obj: SdkAttachmentView,
failure = false,
): AttachmentView | undefined {
if (!obj) {
return undefined;
}
@@ -90,6 +102,7 @@ export class AttachmentView implements View {
// TODO: PM-23005 - Temporary field, should be removed when encrypted migration is complete
view.key = obj.decryptedKey ? SymmetricCryptoKey.fromString(obj.decryptedKey) : undefined;
view.encryptedKey = obj.key ? new EncString(obj.key) : undefined;
view._hasDecryptionError = failure;
return view;
}

View File

@@ -280,6 +280,17 @@ export class CipherView implements View, InitializerMetadata {
return undefined;
}
const attachments = obj.attachments?.map((a) => AttachmentView.fromSdkAttachmentView(a)!) ?? [];
if (obj.attachmentDecryptionFailures?.length) {
obj.attachmentDecryptionFailures.forEach((attachment) => {
const attachmentView = AttachmentView.fromSdkAttachmentView(attachment, true);
if (attachmentView) {
attachments.push(attachmentView);
}
});
}
const cipherView = new CipherView();
cipherView.id = uuidAsString(obj.id);
cipherView.organizationId = uuidAsString(obj.organizationId);
@@ -295,8 +306,7 @@ export class CipherView implements View, InitializerMetadata {
cipherView.edit = obj.edit;
cipherView.viewPassword = obj.viewPassword;
cipherView.localData = fromSdkLocalData(obj.localData);
cipherView.attachments =
obj.attachments?.map((a) => AttachmentView.fromSdkAttachmentView(a)!) ?? [];
cipherView.attachments = attachments;
cipherView.fields = obj.fields?.map((f) => FieldView.fromSdkFieldView(f)!) ?? [];
cipherView.passwordHistory =
obj.passwordHistory?.map((ph) => PasswordHistoryView.fromSdkPasswordHistoryView(ph)!) ?? [];