CVE-2024-1740 (published 2024-04-10T17:15Z)

In lunary-ai/lunary version 1.0.1, a vulnerability exists where a user removed from an organization can still read, create, modify, and delete logs by re-using an old authorization token. The lunary web application communicates with the server using an 'Authorization' token in the browser, which does not properly invalidate upon the user's removal from the organization. This allows the removed user to perform unauthorized actions on logs and access project and external user details without valid permissions.


Weakness information:
cwe_id: CWE-863
cwe_name: Incorrect Authorization
description: The product performs an authorization check when an actor attempts to access a resource or perform an action, but it does not correctly perform the check. This allows attackers to bypass intended access restrictions.
extended_description: 
            Assuming a user with a given identity, authorization is the process of determining whether that user can access a given resource, based on the user's privileges and any permissions or other access-control specifications that apply to the resource.
            When access control checks are incorrectly applied, users are able to access data or perform actions that they should not be allowed to perform. This can lead to a wide range of problems, including information exposures, denial of service, and arbitrary code execution.
         
         

Fix information:
repo_url: https://github.com/lunary-ai/lunary
commit_details: repo_url: https://github.com/lunary-ai/lunary
author: Hugues Chocart
author_date: 2024-04-08 23:35:58-03:00
msg: fix: security patches (#192)
repository: repo_url: https://github.com/lunary-ai/lunary
repo_name: lunary-ai/lunary
description: The production toolkit for LLMs. Observability, prompt management and evaluations. 
repo_language: TypeScript
owner: lunary-ai
file_changes: filename: index.ts
change_type: ModificationType.MODIFY
diff: @@ -1,15 +1,14 @@
-import Router from "koa-router"
+import { runChecksOnRun } from "@/src/checks/runChecks"
+import { checkAccess } from "@/src/utils/authorization"
+import { calcRunCost } from "@/src/utils/calcCost"
+import { getReadableDateTime } from "@/src/utils/date"
 import sql from "@/src/utils/db"
 import Context from "@/src/utils/koa"
-import { getReadableDateTime } from "@/src/utils/date"
-import { runEval } from "./utils"
-import { getEvaluation } from "./utils"
-import { calcRunCost } from "@/src/utils/calcCost"
-import { runChecksOnRun } from "@/src/checks/runChecks"
+import Router from "koa-router"
+import { RunEvent } from "lunary/types"
 import PQueue from "p-queue"
 import { PassThrough } from "stream"
-import { checkAccess } from "@/src/utils/authorization"
-import { RunEvent } from "lunary/types"
+import { runEval } from "./utils"
 
 const evaluations = new Router({ prefix: "/evaluations" })
 
@@ -20,7 +19,7 @@ evaluations.post(
   checkAccess("evaluations", "create"),
   async (ctx: Context) => {
     const { name, datasetId, checklistId, providers } = ctx.request.body as any
-    const { userId, projectId } = ctx.state
+    const { userId, projectId, orgId } = ctx.state
 
     ctx.request.socket.setTimeout(0)
     ctx.request.socket.setNoDelay(true)
@@ -37,6 +36,12 @@ evaluations.post(
       timeout: 10000,
     })
 
+    const [{ plan }] =
+      await sql`select plan, eval_allowance from org where id = ${orgId}`
+    if (plan === "free") {
+      ctx.throw(403, "You can't create evaluations on the free plan.")
+    }
+
     // TODO: transactions, but not working with because of nesting
     const evaluationToInsert = {
       name: name ? name : `Evaluation of ${getReadableDateTime()}`,

programming_language: TypeScript
filename: index.ts
change_type: ModificationType.MODIFY
diff: @@ -121,7 +121,12 @@ projects.post(
     const { projectId } = ctx.params
     const { userId } = ctx.state
 
-    // Define the schema for request body validation using Zod
+    const [hasAccess] =
+      await sql`select * from account_project where project_id = ${projectId} and account_id = ${userId}`
+    if (!hasAccess) {
+      ctx.throw(401, "Not allowed")
+    }
+
     const requestBodySchema = z.object({
       type: z.enum(["private", "public"]),
     })

programming_language: TypeScript
filename: radars.ts
change_type: ModificationType.MODIFY
diff: @@ -218,7 +218,7 @@ radars.get("/:radarId/chart", async (ctx) => {
 })
 
 radars.post("/", checkAccess("radars", "create"), async (ctx) => {
-  const { projectId, userId } = ctx.state
+  const { projectId, userId, orgId } = ctx.state
   const { description, view, checks, alerts, negative } = ctx.request.body as {
     description: string
     view: any[]
@@ -227,6 +227,12 @@ radars.post("/", checkAccess("radars", "create"), async (ctx) => {
     negative: boolean
   }
 
+  const [{ plan }] =
+    await sql`select plan, eval_allowance from org where id = ${orgId}`
+  if (plan === "free") {
+    ctx.throw(403, "You can't create evaluations on the free plan.")
+  }
+
   const [row] = await sql`
     insert into radar ${sql({
       description,

programming_language: JavaScript