1
0
mirror of https://github.com/bitwarden/browser synced 2026-02-09 05:00:10 +00:00

Create a folder for each attachment using its attachmentId as it's name

This is to prevent name-collision for ciphers that have attachments with the same name.
This commit is contained in:
Daniel James Smith
2025-04-25 06:41:03 +02:00
parent f7c38ef38a
commit a6ada8e942
2 changed files with 11 additions and 11 deletions

View File

@@ -380,18 +380,18 @@ describe("VaultExportService", () => {
expect(attachment).toBeDefined();
});
it("If a ciphers attachments filename is already present in the zipfile then append underscore and attachmentId for the new filename", async () => {
it("For each cipher attachment create a dedicated folder with the attachmentId as the name to put the attachment in", async () => {
const cipherData = new CipherData();
cipherData.id = "mock-id";
const cipherView = new CipherView(new Cipher(cipherData));
// Create 1st attachment with the same filename for the cipher
const attachmentView = new AttachmentView(new Attachment(new AttachmentData()));
attachmentView.id = "id-of-file-1";
attachmentView.fileName = "mock-file-name";
attachmentView.fileName = "mock-file-name.txt";
// Create 2nd attachment with the same filename for the cipher
const attachmentView2 = new AttachmentView(new Attachment(new AttachmentData()));
attachmentView2.id = "id-of-file-2";
attachmentView2.fileName = "mock-file-name";
attachmentView2.fileName = "mock-file-name.txt";
cipherView.attachments = [attachmentView, attachmentView2];
cipherService.getAllDecrypted.mockResolvedValue([cipherView]);
folderService.getAllDecryptedFromState.mockResolvedValue([]);
@@ -409,10 +409,12 @@ describe("VaultExportService", () => {
expect(exportedVault.type).toBe("application/zip");
const exportZip = exportedVault as ExportedVaultAsBlob;
const zip = await JSZip.loadAsync(exportZip.data);
const attachment = await zip.file("attachments/mock-id/mock-file-name")?.async("blob");
const attachment = await zip
.file("attachments/mock-id/id-of-file-1/mock-file-name.txt")
?.async("blob");
expect(attachment).toBeDefined();
const attachment2 = await zip
.file("attachments/mock-id/mock-file-name_id-of-file-2")
.file("attachments/mock-id/id-of-file-2/mock-file-name.txt")
?.async("blob");
expect(attachment2).toBeDefined();
});

View File

@@ -118,12 +118,10 @@ export class IndividualVaultExportService
const response = await this.downloadAttachment(cipher.id, attachment.id);
const decBuf = await this.decryptAttachment(cipher, attachment, response);
// To prevent files with the same name from overwriting each other, we append the attachment id to the file name,
// if a file with the same name already exists in the folder
if (cipherFolder.file(attachment.fileName) != null) {
attachment.fileName += `_${attachment.id}`;
}
cipherFolder.file(attachment.fileName, decBuf);
// To prevent files with the same name from overwriting each other, a folder with the attachmentId is created and then the attachment is saved within it
// This also has the benefit of retaining the attachmentId, which is useful when restoring backups
const attachmentIdFolder = cipherFolder.folder(attachment.id);
attachmentIdFolder.file(attachment.fileName, decBuf);
}
}