CVE-2023-40020 (published 2023-08-14T21:15Z)

PrivateUploader is an open source image hosting server written in Vue and TypeScript. In affected versions `app/routes/v3/admin.controller.ts` did not correctly verify whether the user was an administrator (High Level) or moderator (Low Level) causing the request to continue processing. The response would be a 403 with ADMIN_ONLY, however, next() would call leading to any updates/changes in the route to process. This issue has been addressed in version 3.2.49. Users are advised to upgrade. There are no known workarounds for this vulnerability.\n

CVSS v3 base score: 8.3, HIGH (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:H/A:H)

Weakness information:
cwe_id: CWE-287
cwe_name: Improper Authentication
description: When an actor claims to have a given identity, the product does not prove or insufficiently proves that the claim is correct.
extended_description: 
            
			
         
         

Fix information:
repo_url: https://github.com/PrivateUploader/PrivateUploader
commit_details: repo_url: https://github.com/PrivateUploader/PrivateUploader
author: Troplo
author_date: 2023-08-12 16:36:33+10:00
msg: 3.2.49
repository: repo_url: https://github.com/PrivateUploader/PrivateUploader
repo_name: Flowinity/Flowinity
description: Flowinity (formerly PrivateUploader) is the ultimate open source image uploader and file storage solution.
repo_language: TypeScript
owner: PrivateUploader
file_changes: filename: admin.controller.ts
change_type: ModificationType.MODIFY
diff: @@ -35,15 +35,24 @@ import { OauthApp } from "@app/models/oauthApp.model"
 
 @Service()
 @Middleware({ type: "before" })
-@UseBefore(Auth("*"))
 class HighLevel implements ExpressMiddlewareInterface {
   async use(
     request: RequestAuth,
     response: Response,
     next: (err?: any) => any
   ) {
-    await authSystem("admin", false, request, response, next)
-    if (!request.user || !request.user.administrator) throw Errors.ADMIN_ONLY
+    const session = await authSystem(
+      "admin",
+      false,
+      request,
+      response,
+      () => {}
+    )
+    if (!session || !session.user?.administrator) {
+      throw Errors.ADMIN_ONLY
+    } else {
+      next()
+    }
   }
 }
 
@@ -55,12 +64,15 @@ class LowLevel implements ExpressMiddlewareInterface {
     response: Response,
     next: (err?: any) => any
   ) {
-    await authSystem("admin", false, request, response, next)
+    await authSystem("admin", false, request, response, () => {})
     if (
       !request.user ||
       (!request.user?.administrator && !request.user?.moderator)
-    )
+    ) {
       throw Errors.ADMIN_ONLY
+    } else {
+      next()
+    }
   }
 }
 

programming_language: TypeScript
filename: workspace.controller.ts
change_type: ModificationType.MODIFY
diff: @@ -195,9 +195,13 @@ export class WorkspaceControllerV3 {
     const zip: JSZip = new JSZip()
     for (const { dataValues } of folder.children) {
       try {
-        console.log(type)
         const data = await this.noteService.downloadNote(dataValues, type)
-        zip.file(`${dataValues.name.replaceAll("/", "-")}.${type}`, data)
+        zip.file(
+          `${dataValues.name
+            .replaceAll("/", "-")
+            .replaceAll("\\", "-")}.${type}`,
+          data
+        )
       } catch (e) {
         console.error(e)
       }

programming_language: TypeScript
filename: admin.service.ts
change_type: ModificationType.MODIFY
diff: @@ -427,7 +427,7 @@ export class AdminService {
   async updatePlanId(userId: number, planId: number) {
     const user = await User.findByPk(userId)
     if (!user) throw Errors.USER_NOT_FOUND
-    if (userId === 6 && planId === 6) {
+    if (userId === 6 && planId === 6 && config.officialInstance) {
       throw Errors.HANDLED_BY_PAYMENT_PROVIDER
     }
     await User.update(

programming_language: TypeScript
filename: note.service.ts
change_type: ModificationType.MODIFY
diff: @@ -486,10 +486,11 @@ export class NoteService {
       ]
     })
     if (!folder) throw Errors.NOT_FOUND
+    console.log(folder.name)
     const workspace = await this.getWorkspace(
       folder.workspaceId,
       userId,
-      "folder"
+      "workspace"
     )
     if (!workspace) throw Errors.NOT_FOUND
     return folder

programming_language: TypeScript
filename: setup.ts
change_type: ModificationType.MODIFY
diff: @@ -12,7 +12,11 @@ export const HostnameValidator = z.string().refine((value) => {
 export const TpuConfigValidator = z.object({
   hostnameWithProtocol: z.string().url(),
   hostname: HostnameValidator,
-  maintenance: z.boolean(),
+  maintenance: z.object({
+    enabled: z.boolean(),
+    message: z.string().nullable().optional(),
+    statusPage: z.string().nullable().optional()
+  }),
   siteName: z.string(),
   release: z.string(),
   storage: z.string(),

programming_language: TypeScript
filename: package.json
change_type: ModificationType.MODIFY
diff: @@ -1,6 +1,6 @@
 {
   "name": "tpu-frontend",
-  "version": "3.2.48",
+  "version": "3.2.49",
   "private": true,
   "author": "Troplo <troplo@troplo.com>",
   "type": "module",

programming_language: JavaScript