Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ model User {
worldID String @unique
name String?
profilePicture String?
isAdmin Boolean @default(false)
pollsCreatedCount Int @default(0)
pollsParticipatedCount Int @default(0)
createdAt DateTime @default(now())
Expand Down
5 changes: 3 additions & 2 deletions src/poll/poll.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -550,7 +550,7 @@ export class PollService {
async deletePoll(pollId: number, worldID: string) {
const user = await this.databaseService.user.findUnique({
where: { worldID },
select: { id: true },
select: { id: true, isAdmin: true },
})
if (!user) {
throw new UserNotFoundException()
Expand All @@ -561,7 +561,8 @@ export class PollService {
if (!poll) {
throw new PollNotFoundException()
}
if (poll.authorUserId !== user.id) {
// Allow deletion if user is admin or if user is the poll author
if (!user.isAdmin && poll.authorUserId !== user.id) {
throw new UnauthorizedActionException()
}

Expand Down
5 changes: 5 additions & 0 deletions src/user/user.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,4 +71,9 @@ export class UserController {
async getUserCount(@Query() query: GetCountDto): Promise<number> {
return await this.userService.getUserCount(query)
}

@Get('listAdmins')
async listAdmins(): Promise<string[]> {
return await this.userService.listAdmins()
}
}
4 changes: 4 additions & 0 deletions src/user/user.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ export class UserDataResponseDto {
@IsString()
@IsOptional()
name?: string | null

@IsBoolean()
@IsNotEmpty()
isAdmin: boolean
}

export class GetUserActivitiesDto {
Expand Down
10 changes: 10 additions & 0 deletions src/user/user.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ export class UserService {
worldID: true,
name: true,
profilePicture: true,
isAdmin: true,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Verify if admin status should be publicly exposed.

The isAdmin field is now included in the database query for user data retrieval. While the implementation is correct, consider whether admin status should be publicly accessible through the getUserData endpoint.


🏁 Script executed:

#!/bin/bash
# Description: Check if getUserData endpoint has authorization restrictions
# Expected: Find controller method and any authorization decorators

ast-grep --pattern $'getUserData($$$) {
  $$$
}'

# Also check for any authorization decorators in the user controller
rg -A 5 -B 5 "getUserData|@.*Auth|@.*Guard" src/user/user.controller.ts

Length of output: 505


Restrict public exposure of admin status

The getUserData endpoint in src/user/user.controller.ts is publicly accessible (no authorization decorators), which means the new isAdmin flag can be fetched by any caller. Exposing admin status in an open API poses a security risk and may aid malicious reconnaissance.

• File: src/user/user.controller.ts
– Method: getUserData (no @UseGuards(...) or equivalent)
• Snippet:

 @Get('getUserData')
- async getUserData(
+ @UseGuards(AuthGuard)    // <-- add appropriate guard
+ @ApiBearerAuth()         // <-- document secured endpoint
+ @ApiOperation({ summary: 'Retrieve user data (authenticated)' })
  async getUserData(

Please either restrict this endpoint with proper authorization checks (e.g. JWT/session guard) or remove isAdmin from the public response.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/user/user.service.ts at line 100, the isAdmin field is included in the
user data returned by the getUserData endpoint, which is publicly accessible
without authorization in src/user/user.controller.ts. To fix this, either remove
the isAdmin field from the data returned by getUserData to prevent exposing
admin status publicly, or add proper authorization guards (e.g., JWT or session
guards) to the getUserData controller method to restrict access only to
authorized users.

pollsCreatedCount: true,
pollsParticipatedCount: true,
},
Expand All @@ -110,6 +111,7 @@ export class UserService {
worldID: user.worldID,
worldProfilePic: user.profilePicture,
name: user.name,
isAdmin: user.isAdmin,
}
}

Expand Down Expand Up @@ -456,4 +458,12 @@ export class UserService {

return await this.databaseService.user.count({ where })
}

async listAdmins(): Promise<string[]> {
const admins = await this.databaseService.user.findMany({
where: { isAdmin: true },
select: { worldID: true },
})
return admins.map(admin => admin.worldID)
}
Comment on lines +462 to +468
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Critical: Missing authorization for admin list endpoint.

The listAdmins method returns worldIDs of all admin users without any authorization checks. This could expose sensitive information about system administrators to unauthorized users.

Consider adding proper authorization to restrict access to this endpoint:

+ // Add authorization decorator in controller
+ @UseGuards(AdminGuard) // or similar authorization mechanism
  async listAdmins(): Promise<string[]> {
    const admins = await this.databaseService.user.findMany({
      where: { isAdmin: true },
      select: { worldID: true },
    })
    return admins.map(admin => admin.worldID)
  }

Additionally, verify if returning admin worldIDs is necessary or if the endpoint should be restricted to specific use cases.


🏁 Script executed:

#!/bin/bash
# Description: Check how this method is used in the controller and if there are authorization guards
# Expected: Find the controller method and any authorization decorators

rg -A 10 -B 5 "listAdmins" src/user/user.controller.ts
rg -A 5 -B 5 "@.*Guard|@.*Auth" src/user/user.controller.ts

Length of output: 384


Critical: Secure the listAdmins endpoint with proper authorization

The controller’s listAdmins handler is currently marked @Public() and exposes all admin worldIDs without any guard.

• File: src/user/user.controller.ts
Method: async listAdmins()

Proposed fix – restrict access to authenticated admins only:

-  @Get('listAdmins')
-  @Public()
+  @Get('listAdmins')
+  @UseGuards(JwtAuthGuard, RolesGuard)    // or your existing AdminGuard
+  @Roles('admin')                        // ensure only users with isAdmin=true
  async listAdmins(): Promise<string[]> {
    return await this.userService.listAdmins();
  }

• Remove the @Public() decorator.
• Apply your authentication guard (e.g. JwtAuthGuard) and a roles/admin guard.
• Verify that your RolesGuard (or AdminGuard) checks user.isAdmin.
• Double-check whether returning a list of all admin worldIDs is strictly necessary or can be scoped further.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/user/user.service.ts lines 462 to 468, the listAdmins method returns all
admin worldIDs without any authorization checks, exposing sensitive data. To fix
this, remove the @Public() decorator from the corresponding controller method in
src/user/user.controller.ts, add authentication guards like JwtAuthGuard, and
apply a roles or admin guard that verifies user.isAdmin. Also, review if
returning all admin worldIDs is necessary or if the data should be scoped or
restricted further to minimize exposure.

}