Summary
The GET /api/events/:slug endpoint is publicly accessible (no jwtVerify call) and its response type EventDetails includes organizerId - the raw database UUID of the user who created the event. Exposing internal primary keys to unauthenticated clients is an information disclosure vulnerability that widens the attack surface for enumeration attacks on user IDs.
Affected File
apps/backend/src/routes/event.ts - GET /:slug handler (lines 123-155)
type EventDetails = {
id: string;
name: string;
slug: string;
location: string;
description: string | null;
organizerId: string; // <-- internal UUID sent to any anonymous caller
startDate: Date;
endDate: Date;
createdAt: Date;
attendeesCount: number
}
// ...
const response: EventDetails = {
id: details.id,
name: details.name,
slug: details.slug,
description: details.description,
location: details.location,
organizerId: details.organizerId, // <-- exposed here
startDate: details.startDate,
endDate: details.endDate,
createdAt: details.createdAt,
attendeesCount: details._count.attendees
}
Impact
- Information disclosure: Any anonymous visitor can harvest the internal UUID of every event organizer by iterating over event slugs.
- Enumeration risk: Internal UUIDs returned by one endpoint can be cross-referenced with other endpoints that accept a user ID parameter, potentially facilitating IDOR probes.
- Principle of least privilege violation: Public API surfaces should expose the minimum data needed. A public event page does not need the organizer's database ID - a public-facing identifier (username or display name) is sufficient.
Suggested Fix
Replace organizerId with the organizer's public username by joining the user table in the Prisma query:
const details = await app.prisma.event.findUnique({
where: { slug: paramsSlug },
include: {
_count: { select: { attendees: true } },
organizer: { select: { username: true, displayName: true } } // join user
}
})
Then update the response type and mapping:
type EventDetails = {
id: string;
name: string;
slug: string;
location: string;
description: string | null;
organizerUsername: string; // public identifier, not internal UUID
organizerDisplayName: string;
startDate: Date;
endDate: Date;
createdAt: Date;
attendeesCount: number
}
This removes the internal UUID from the public surface entirely while still giving the frontend everything it needs to display event organizer info.
Environment
apps/backend/src/routes/event.ts
- Prisma ORM with PostgreSQL
- Fastify backend
Summary
The
GET /api/events/:slugendpoint is publicly accessible (nojwtVerifycall) and its response typeEventDetailsincludesorganizerId- the raw database UUID of the user who created the event. Exposing internal primary keys to unauthenticated clients is an information disclosure vulnerability that widens the attack surface for enumeration attacks on user IDs.Affected File
apps/backend/src/routes/event.ts-GET /:slughandler (lines 123-155)Impact
Suggested Fix
Replace
organizerIdwith the organizer's publicusernameby joining the user table in the Prisma query:Then update the response type and mapping:
This removes the internal UUID from the public surface entirely while still giving the frontend everything it needs to display event organizer info.
Environment
apps/backend/src/routes/event.ts