fix(ext/sw): clarify cap layering + harden download path

Two small follow-ups from code review of 5217d04:

1. Document the cap-enforcement layering in the upload handler. SW
   enforces per_attachment_max_bytes via WASM (defense-in-depth);
   per_item_max_count and per-vault caps are enforced client-side
   in the popup (Task 7's attachments-disclosure).

2. Use ref.id (the validated value found on the item) instead of
   msg.attachmentId for blobPath construction in download_attachment.
   Eliminates a theoretical path-traversal surface even though the
   handler is popup-only.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
adlee-was-taken
2026-04-25 16:11:49 -04:00
parent 5217d04034
commit b9c495cdea

View File

@@ -218,6 +218,10 @@ export async function handle(
} }
const plaintext = new Uint8Array(msg.bytes); const plaintext = new Uint8Array(msg.bytes);
// Cap enforcement layering:
// - per_attachment_max_bytes: enforced here via WASM (defense-in-depth)
// - per_item_max_count, per_vault_*_cap_bytes: enforced client-side in
// the popup (Task 7's attachments-disclosure component does this).
const encrypted = state.wasm.attachment_encrypt(handle, plaintext, maxBytes); const encrypted = state.wasm.attachment_encrypt(handle, plaintext, maxBytes);
// encrypted: EncryptedAttachment — exposes .aid (string) and .bytes (Uint8Array) // encrypted: EncryptedAttachment — exposes .aid (string) and .bytes (Uint8Array)
const aid: string = encrypted.aid; const aid: string = encrypted.aid;
@@ -266,7 +270,7 @@ export async function handle(
const ref = item.attachments.find((a) => a.id === msg.attachmentId); const ref = item.attachments.find((a) => a.id === msg.attachmentId);
if (!ref) return { ok: false, error: 'attachment_not_found' }; if (!ref) return { ok: false, error: 'attachment_not_found' };
const blobPath = `attachments/${msg.attachmentId}.bin`; const blobPath = `attachments/${ref.id}.bin`;
const encBytes = await state.gitHost.getBlob(blobPath); const encBytes = await state.gitHost.getBlob(blobPath);
const decrypted = state.wasm.attachment_decrypt(handle, encBytes); const decrypted = state.wasm.attachment_decrypt(handle, encBytes);