Summary
Harden the chat-image upload flow by introducing a server-verifiable confirmation claim for uploaded chat image keys. The WebSocket IMAGE-message path must require and consume this claim before accepting a bare S3 key.
Background and rationale
PR #88 intentionally keeps the MVP flow as:
- REST
presign
- Client uploads directly to storage
- REST
confirm validates the uploaded object
- Client sends the key through the existing WebSocket IMAGE-message flow
The current confirm operation does not leave state that the WebSocket path can verify. Therefore, a client can bypass confirm and send a bare key directly over WebSocket. The chat image key must also remain bound to its intended ImageCategory.CHAT and party scope.
Required changes
- Extend the upload confirmation/storage contract so chat-image confirmation validates the expected
ImageCategory.CHAT and party scope for the key.
- Persist or otherwise securely tag a confirmed-upload claim that binds at least:
- the upload key,
- the party,
- the uploader/user,
- the chat image category,
- and an expiry or equivalent lifecycle constraint.
- Update the WebSocket IMAGE-message handling path (including
ChatService.saveMessage() or its appropriate boundary) to:
- retain existing participant and message validation,
- require a valid confirmed-upload claim for the supplied key,
- verify that the claim is usable in the current party/user context,
- consume/invalidate the claim atomically when the IMAGE message is accepted, preventing replay.
- Define cleanup/expiry behavior for unused confirmed claims and uploaded objects as appropriate.
- Add unit/integration tests for valid confirmation, bypass attempts, cross-party/cross-user attempts, expired claims, and replay attempts.
Affected areas
src/main/java/com/leets/tdd/chat/service/ChatImageService.java
- Chat WebSocket IMAGE-message handling, including the existing
ChatService.saveMessage() flow as applicable
ImageStorageService and/or the upload-confirmation persistence/claim component
- Related DTOs, repositories, and tests
Acceptance criteria
- A bare image key that was never successfully confirmed is rejected by the WebSocket IMAGE path.
- A key confirmed for a different party, user, or image category is rejected.
- A valid claim is accepted only once and is consumed when the corresponding IMAGE message is accepted.
- Expired or invalidated claims are rejected.
- Existing authorization and upload-content validation remain enforced.
- Automated tests cover the listed authorization, bypass, expiry, and replay scenarios.
Backlinks
Summary
Harden the chat-image upload flow by introducing a server-verifiable confirmation claim for uploaded chat image keys. The WebSocket IMAGE-message path must require and consume this claim before accepting a bare S3 key.
Background and rationale
PR #88 intentionally keeps the MVP flow as:
presignconfirmvalidates the uploaded objectThe current
confirmoperation does not leave state that the WebSocket path can verify. Therefore, a client can bypassconfirmand send a bare key directly over WebSocket. The chat image key must also remain bound to its intendedImageCategory.CHATand party scope.Required changes
ImageCategory.CHATand party scope for the key.ChatService.saveMessage()or its appropriate boundary) to:Affected areas
src/main/java/com/leets/tdd/chat/service/ChatImageService.javaChatService.saveMessage()flow as applicableImageStorageServiceand/or the upload-confirmation persistence/claim componentAcceptance criteria
Backlinks