Fix: IDOR attachment validation - #623
Conversation
|
Requesting review from @leoisadev1 who has experience with the following files modified in this PR:
|
There was a problem hiding this comment.
2 issues found across 1 file
Confidence score: 2/5
- High risk due to a likely security bypass in
apps/server/convex/messages.tswhere missinguserIdskips attachment ownership validation, enabling IDOR if callers omit it. - Distinct error messages in
apps/server/convex/messages.tscan leak file existence/ownership, enabling storage ID enumeration and privacy exposure. - Pay close attention to
apps/server/convex/messages.ts- ensure ownership checks always run and error messages are normalized.
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="apps/server/convex/messages.ts">
<violation number="1" location="apps/server/convex/messages.ts:779">
P2: Information leakage: distinct error messages for "not found", "not owned", and "deleted" allow an attacker to enumerate storage IDs and learn about other users' files. Use a single generic error message for all three cases to prevent information disclosure.</violation>
<violation number="2" location="apps/server/convex/messages.ts:881">
P1: Security bypass: attachment ownership validation is silently skipped when `userId` is undefined. Since `userId` is an optional parameter, any caller that omits it will bypass the IDOR check entirely. This should throw an error if attachments are present but `userId` is missing, rather than silently proceeding.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| if (args.attachments && args.attachments.length > 0 && args.userId) { | ||
| await validateAttachmentOwnership(ctx, args.attachments, args.userId); |
There was a problem hiding this comment.
P1: Security bypass: attachment ownership validation is silently skipped when userId is undefined. Since userId is an optional parameter, any caller that omits it will bypass the IDOR check entirely. This should throw an error if attachments are present but userId is missing, rather than silently proceeding.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/server/convex/messages.ts, line 881:
<comment>Security bypass: attachment ownership validation is silently skipped when `userId` is undefined. Since `userId` is an optional parameter, any caller that omits it will bypass the IDOR check entirely. This should throw an error if attachments are present but `userId` is missing, rather than silently proceeding.</comment>
<file context>
@@ -788,6 +875,13 @@ async function insertOrUpdateMessage(
+
+ // SECURITY: Validate attachment ownership before inserting/updating message
+ // This prevents IDOR where a user references another user's storageId
+ if (args.attachments && args.attachments.length > 0 && args.userId) {
+ await validateAttachmentOwnership(ctx, args.attachments, args.userId);
+ }
</file context>
| if (args.attachments && args.attachments.length > 0 && args.userId) { | |
| await validateAttachmentOwnership(ctx, args.attachments, args.userId); | |
| if (args.attachments && args.attachments.length > 0) { | |
| if (!args.userId) { | |
| throw new Error("userId is required when sending attachments"); | |
| } | |
| await validateAttachmentOwnership(ctx, args.attachments, args.userId); |
| if (!fileUpload) { | ||
| throw new Error( | ||
| "Unauthorized: attachment references a file that does not exist in your uploads.", | ||
| ); | ||
| } | ||
|
|
||
| if (fileUpload.userId !== userId) { | ||
| throw new Error( | ||
| "Unauthorized: you do not own the referenced attachment file.", | ||
| ); | ||
| } | ||
|
|
||
| if (fileUpload.deletedAt) { | ||
| throw new Error( | ||
| "Attachment references a file that has been deleted.", |
There was a problem hiding this comment.
P2: Information leakage: distinct error messages for "not found", "not owned", and "deleted" allow an attacker to enumerate storage IDs and learn about other users' files. Use a single generic error message for all three cases to prevent information disclosure.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/server/convex/messages.ts, line 779:
<comment>Information leakage: distinct error messages for "not found", "not owned", and "deleted" allow an attacker to enumerate storage IDs and learn about other users' files. Use a single generic error message for all three cases to prevent information disclosure.</comment>
<file context>
@@ -736,6 +743,86 @@ type ErrorData = {
+ .withIndex("by_storage", (q) => q.eq("storageId", attachment.storageId))
+ .unique();
+
+ if (!fileUpload) {
+ throw new Error(
+ "Unauthorized: attachment references a file that does not exist in your uploads.",
</file context>
| if (!fileUpload) { | |
| throw new Error( | |
| "Unauthorized: attachment references a file that does not exist in your uploads.", | |
| ); | |
| } | |
| if (fileUpload.userId !== userId) { | |
| throw new Error( | |
| "Unauthorized: you do not own the referenced attachment file.", | |
| ); | |
| } | |
| if (fileUpload.deletedAt) { | |
| throw new Error( | |
| "Attachment references a file that has been deleted.", | |
| if (!fileUpload || fileUpload.userId !== userId || fileUpload.deletedAt) { | |
| throw new Error( | |
| "Unauthorized: invalid attachment reference.", | |
| ); | |
| } |
35bc1f1 to
72fa493
Compare
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Summary
Fixed IDOR vulnerability in message attachments by validating that each
storageIdbelongs to the authenticated user before accepting or resolving file URLs.Vulnerability Details
Message attachments were accepted from clients without verifying ownership of the referenced storage files. This allowed attackers to reference arbitrary
storageIdvalues and retrieve signed URLs for other users' files.Changes Made
Added
validateAttachmentOwnership(): Validates each attachment'sstorageIdagainst thefileUploadstable to ensure:Added
getVerifiedStorageIds(): For the list query path, returns only storageIds verified to belong to the requesting user, preventing URL generation for unauthorized filesUpdated
insertOrUpdateMessage(): CallsvalidateAttachmentOwnership()before inserting/updating messages with attachmentsUpdated
listquery: Filters attachment URLs to only include verified storageIds, returningnullfor unauthorized filesFiles Modified
apps/server/convex/messages.tsSecurity Impact
Summary by cubic
Fixed an IDOR in message attachments by verifying storageId ownership and only generating URLs for files owned by the authenticated user. Prevents unauthorized access to other users’ uploads.
Written for commit 72fa493. Summary will update on new commits.