reported on 4 June 2026 https://github.com/CapSoftware/Cap/security/advisories/GHSA-5m2p-v3wg-3gm6 - no response:
Summary
The GET /api/video/ai endpoint retrieves AI-generated metadata (title, summary, chapters) for any video by ID without verifying whether the requesting user has permission to access that video. Any authenticated user can supply an arbitrary video ID and read the AI-generated content of another user's private recordings.
Details
The handler at apps/web/app/api/video/ai/route.ts (GET, lines 13-169) fetches the target video directly from the database using only the caller's authenticated session and the videoId query parameter:
const result = await db()
.select()
.from(videos)
.where(eq(videos.id, videoId));
No ownership check and no call to the VideosPolicy canView policy are made before returning the video's metadata fields. The response body includes:
- metadata.aiTitle
- metadata.summary
- metadata.chapters
- metadata.aiGenerationStatus
The correct access-guarded path used by every other video-reading endpoint is getByIdForViewing, which runs the full canView policy (owner check, org/space membership, public flag, password, email restriction). This endpoint bypasses all of that.
PoC
Preconditions: User A has a private video (public=false) with AI metadata populated. User B has no membership in User A's org or space and cannot view the video at /s/{videoId}.
-
Authenticate as User B and obtain a session token (cookie next-auth.session-token).
-
Note the video ID of User A's private recording (vid001vidtest00 in the test below). In practice an attacker would enumerate IDs or obtain one via a notification link or a shared URL they intercept.
-
Issue:
curl -s "http://<host>/api/video/ai?videoId=vid001vidtest00" \
-H "Cookie: next-auth.session-token=<User B token>"
- Response (200 OK):
{
"processing": false,
"title": "Secret Report Title",
"summary": "This is a confidential summary",
"chapters": [],
"aiGenerationStatus": "COMPLETE"
}
User B receives the AI-generated title and summary of a video they cannot open. The /s/vid001vidtest00 share page correctly shows "This video is private" to User B, confirming there is no legitimate access path.
The same endpoint will also attempt to enqueue AI generation on behalf of the requester when aiGenerationStatus is SKIPPED and transcriptionStatus is COMPLETE, consuming the video owner's AI credits without their knowledge.
Impact
Any authenticated user can read the AI-generated title, summary, and chapter list of any other user's private video recordings. These fields are derived from the audio transcript and can contain sensitive, confidential, or personally identifying information. The bug affects all self-hosted Cap deployments running the current codebase.
reported on 4 June 2026 https://github.com/CapSoftware/Cap/security/advisories/GHSA-5m2p-v3wg-3gm6 - no response:
Summary
The GET /api/video/ai endpoint retrieves AI-generated metadata (title, summary, chapters) for any video by ID without verifying whether the requesting user has permission to access that video. Any authenticated user can supply an arbitrary video ID and read the AI-generated content of another user's private recordings.
Details
The handler at apps/web/app/api/video/ai/route.ts (GET, lines 13-169) fetches the target video directly from the database using only the caller's authenticated session and the videoId query parameter:
No ownership check and no call to the VideosPolicy canView policy are made before returning the video's metadata fields. The response body includes:
The correct access-guarded path used by every other video-reading endpoint is getByIdForViewing, which runs the full canView policy (owner check, org/space membership, public flag, password, email restriction). This endpoint bypasses all of that.
PoC
Preconditions: User A has a private video (public=false) with AI metadata populated. User B has no membership in User A's org or space and cannot view the video at /s/{videoId}.
Authenticate as User B and obtain a session token (cookie next-auth.session-token).
Note the video ID of User A's private recording (vid001vidtest00 in the test below). In practice an attacker would enumerate IDs or obtain one via a notification link or a shared URL they intercept.
Issue:
{ "processing": false, "title": "Secret Report Title", "summary": "This is a confidential summary", "chapters": [], "aiGenerationStatus": "COMPLETE" }User B receives the AI-generated title and summary of a video they cannot open. The /s/vid001vidtest00 share page correctly shows "This video is private" to User B, confirming there is no legitimate access path.
The same endpoint will also attempt to enqueue AI generation on behalf of the requester when aiGenerationStatus is SKIPPED and transcriptionStatus is COMPLETE, consuming the video owner's AI credits without their knowledge.
Impact
Any authenticated user can read the AI-generated title, summary, and chapter list of any other user's private video recordings. These fields are derived from the audio transcript and can contain sensitive, confidential, or personally identifying information. The bug affects all self-hosted Cap deployments running the current codebase.