Only presign and accept images in the caller's own conversations #81

Merged
ryan merged 1 commits from fix/image-ownership into main 2026-10-13 07:53:05 -04:00
Owner

Security fix.

  • GET /api/conversation/image/<key> returned a presigned S3 URL for any key to any logged-in user. Image keys are guessable once seen, and #79's run payloads expose them.
  • POST /api/conversation/upload-image wrote into whatever conversation_id the form named, including other users' conversations.

Every stored object lives under conversations/<conversation id>/…. Both routes now require that conversation to belong to the caller and return 404 otherwise. Keys that try to escape with .., or that aren't conversation keys, are rejected too. _get_owned_conversation now treats a malformed id as not found instead of raising.

Tests cover owned versus foreign keys, path traversal, non-UUID ids, non-conversation keys, and uploads to a foreign conversation. The new tests fail without the fix. 373 unit tests pass on main.

🤖 Generated with Claude Code

Security fix. - `GET /api/conversation/image/<key>` returned a presigned S3 URL for **any** key to any logged-in user. Image keys are guessable once seen, and #79's run payloads expose them. - `POST /api/conversation/upload-image` wrote into whatever `conversation_id` the form named, including other users' conversations. Every stored object lives under `conversations/<conversation id>/…`. Both routes now require that conversation to belong to the caller and return 404 otherwise. Keys that try to escape with `..`, or that aren't conversation keys, are rejected too. `_get_owned_conversation` now treats a malformed id as not found instead of raising. Tests cover owned versus foreign keys, path traversal, non-UUID ids, non-conversation keys, and uploads to a foreign conversation. The new tests fail without the fix. 373 unit tests pass on main. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
ryan added 1 commit 2026-10-12 23:18:43 -04:00
GET /api/conversation/image/<key> presigned any S3 key for any logged-in
user, and /upload-image wrote into whatever conversation_id the form named.
Both now require the conversation in the key (or form) to belong to the
caller and return 404 otherwise.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
ryan merged commit 61acd85954 into main 2026-10-13 07:53:05 -04:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: ryan/simbarag#81