CVE-2021-38621 (published 2021-08-13T15:15Z)

The remove API in v1/controller/cloudStorage/alibabaCloud/remove/index.ts in netless Agora Flat Server before 2021-07-30 mishandles file ownership.

Severity: MEDIUM
CVSS v2 base score: 6.4 (AV:N/AC:L/Au:N/C:P/I:P/A:N)
CVSS v3 base score: 9.1, CRITICAL (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N)

Weakness information:
cwe_id: NVD-CWE-Other
cwe_name: Other
description: NVD is only using a subset of CWE for mapping instead of the entire CWE, and the weakness type is not covered by that subset.
extended_description: Insufficient Information

Fix information:
repo_url: https://github.com/netless-io/flat-server
commit_details: repo_url: https://github.com/netless-io/flat-server
author: Black-Hole
author_date: 2021-07-30 18:04:48+08:00
msg: refactor(cloudStorage): improve file onwer check in remove api (#392)
repository: repo_url: https://github.com/netless-io/flat-server
repo_name: netless-io/flat-server
description: A Node.js server for the Agora Flat open source classroom.
repo_language: TypeScript
owner: netless-io
file_changes: filename: index.ts
change_type: ModificationType.MODIFY
diff: @@ -2,8 +2,8 @@ import { createQueryBuilder, getConnection, In } from "typeorm";
 import { Region, Status } from "../../../../../constants/Project";
 import {
     CloudStorageConfigsDAO,
-    CloudStorageUserFilesDAO,
     CloudStorageFilesDAO,
+    CloudStorageUserFilesDAO,
 } from "../../../../../dao";
 import { CloudStorageFilesModel } from "../../../../../model/cloudStorage/CloudStorageFiles";
 import { CloudStorageUserFilesModel } from "../../../../../model/cloudStorage/CloudStorageUserFiles";
@@ -41,6 +41,8 @@ export class AlibabaCloudRemoveFile extends AbstractController<RequestType, Resp
         const { fileUUIDs } = this.body;
         const userUUID = this.userUUID;
 
+        await this.assertFilesOwnerIsCurrentUser();
+
         const fileInfo: FileInfoType[] = await createQueryBuilder(CloudStorageUserFilesModel, "fc")
             .addSelect("f.file_uuid", "file_uuid")
             .addSelect("f.file_name", "file_name")
@@ -60,7 +62,10 @@ export class AlibabaCloudRemoveFile extends AbstractController<RequestType, Resp
             .getRawMany();
 
         if (fileInfo.length === 0) {
-            throw new ControllerError(ErrorCode.FileNotFound);
+            return {
+                status: Status.Success,
+                data: {},
+            };
         }
 
         const cloudStorageConfigsInfo = await CloudStorageConfigsDAO().findOne(["total_usage"], {
@@ -138,6 +143,18 @@ export class AlibabaCloudRemoveFile extends AbstractController<RequestType, Resp
     public errorHandler(error: Error): ResponseError {
         return this.autoHandlerError(error);
     }
+
+    private async assertFilesOwnerIsCurrentUser(): Promise<void> {
+        const filesOwner = await CloudStorageUserFilesDAO().find(["user_uuid"], {
+            file_uuid: In(this.body.fileUUIDs),
+        });
+
+        for (const { user_uuid } of filesOwner) {
+            if (user_uuid !== this.userUUID) {
+                throw new ControllerError(ErrorCode.NotPermission);
+            }
+        }
+    }
 }
 
 interface RequestType {

programming_language: TypeScript