CVE-2024-1626: Execution with Unnecessary Privileges (CWE-250)
value: An Insecure Direct Object Reference (IDOR) vulnerability exists in the lunary-ai/lunary repository, version 0.3.0, within the project update endpoint. The vulnerability allows authenticated users to modify the name of any project within the system without proper authorization checks, by directly referencing the project's ID in the PATCH request to the '/v1/projects/:projectId' endpoint. This issue arises because the endpoint does not verify if the provided project ID belongs to the currently authenticated user, enabling unauthorized modifications across different organizational projects.
CWE description: The product performs an operation at a privilege level that is higher than the minimum level required, which creates new weaknesses or amplifies the consequences of other weaknesses.
Published: 2024-04-16T00:15Z
Repository: https://github.com/lunary-ai/lunary
Language: TypeScript
Files changed: packages/backend/src/api/v1/projects/index.ts, packages/backend/src/utils/authorization.ts
Commit date: 2024-02-19 09:39:01 -0300
Commit message: feat: add project modification safeguard

Diff: diff --git a/packages/backend/src/api/v1/projects/index.ts b/packages/backend/src/api/v1/projects/index.ts
index 76467d7..89b16a7 100644
--- a/packages/backend/src/api/v1/projects/index.ts
+++ b/packages/backend/src/api/v1/projects/index.ts
@@ -1,6 +1,6 @@
-import { verifyProjectAccess } from "@/src/utils/authorization"
+import { checkProjectAccess } from "@/src/utils/authorization"
 import sql from "@/src/utils/db"
 import Context from "@/src/utils/koa"
 import Router from "koa-router"
 import { z } from "zod"
 
@@ -69,13 +69,12 @@ projects.post("/", async (ctx: Context) => {
 })
 
 projects.delete("/:projectId", async (ctx: Context) => {
   const { projectId } = ctx.params
   const { orgId, userId } = ctx.state
-  console.log(ctx.state)
 
-  const hasProjectAccess = await verifyProjectAccess(projectId, userId)
+  const hasProjectAccess = await checkProjectAccess(projectId, userId)
   const [user] = await sql`select * from account where id = ${userId}`
 
   if (!hasProjectAccess) {
     ctx.throw(401, "Not allowed")
   }
@@ -103,10 +102,17 @@ projects.delete("/:projectId", async (ctx: Context) => {
   }
 })
 
 projects.patch("/:projectId", async (ctx: Context) => {
   const { projectId } = ctx.params
+  const { userId } = ctx.params
+
+  const hasProjectAccess = await checkProjectAccess(projectId, userId)
+  if (!hasProjectAccess) {
+    ctx.throw(401, "Unauthorized")
+  }
+
   const bodySchema = z.object({
     name: z.string(),
   })
   const { name } = bodySchema.parse(ctx.request.body)
 
diff --git a/packages/backend/src/utils/authorization.ts b/packages/backend/src/utils/authorization.ts
index 3cf0c4b..1ea667b 100644
--- a/packages/backend/src/utils/authorization.ts
+++ b/packages/backend/src/utils/authorization.ts
@@ -1,8 +1,8 @@
 import sql from "./db"
 
-export async function verifyProjectAccess(projectId: string, userId: string) {
+export async function checkProjectAccess(projectId: string, userId: string) {
   const [{ exists: hasAccess }] = await sql`
       select exists (
         select 1 
         from project 
         where org_id = (select org_id from account where id = ${userId}) 

Vulnerable code: import { verifyProjectAccess } from "@/src/utils/authorization"
  console.log(ctx.state)
  const hasProjectAccess = await verifyProjectAccess(projectId, userId)
export async function verifyProjectAccess(projectId: string, userId: string) {
Fixed code: import { checkProjectAccess } from "@/src/utils/authorization"
  const hasProjectAccess = await checkProjectAccess(projectId, userId)
  const { userId } = ctx.params

  const hasProjectAccess = await checkProjectAccess(projectId, userId)
  if (!hasProjectAccess) {
    ctx.throw(401, "Unauthorized")
  }

export async function checkProjectAccess(projectId: string, userId: string) {