From 86d60f1a85579d162333b71a7285cc04d0387c60 Mon Sep 17 00:00:00 2001 From: Huy-DNA Date: Wed, 13 Nov 2024 00:20:56 +0700 Subject: [PATCH 1/3] fix: disallow moving and removing ancestor folder --- lib/path/index.ts | 4 ++++ services/files.ts | 13 +++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/lib/path/index.ts b/lib/path/index.ts index 06fa93d..107060f 100644 --- a/lib/path/index.ts +++ b/lib/path/index.ts @@ -37,6 +37,10 @@ export class VirtualPath { return this.path === ''; } + isAncestor (other: VirtualPath): boolean { + return other.path.startsWith(this.path + '/'); + } + isHome (username: string): boolean { return this.equals(VirtualPath.homeDir(username)); } diff --git a/services/files.ts b/services/files.ts index 19dbde8..592e427 100644 --- a/services/files.ts +++ b/services/files.ts @@ -1,6 +1,7 @@ import path from 'path-browserify'; import { Err, Ok, type Diagnostic, type Result } from './types'; import { FilePostErrorCode } from '~/lib'; +import type { VirtualPath } from '~/lib/path'; export enum UserKind { OWNER = 'owner', @@ -118,9 +119,13 @@ export const fileService = { }, async removeFile (filename: string): Promise> { const { cwd } = useCwdStore(); + const resolvedPath = cwd.value.resolve(filename); + if (resolvedPath.isAncestor(cwd.value as VirtualPath)) { + return new Err({ code: 1, message: 'Cannot move ancestor folder' }); + } const res = await $fetch('/api/files', { method: 'delete', - query: { name: cwd.value.resolve(filename).toString() }, + query: { name: resolvedPath.toString() }, credentials: 'include', }); if (res.error) { @@ -185,10 +190,14 @@ export const fileService = { }, async moveFile (src: string, dest: string, umask: string): Promise> { const { cwd } = useCwdStore(); + const resolvedSrc = cwd.value.resolve(src); + if (resolvedSrc.isAncestor(cwd.value as VirtualPath)) { + return new Err({ code: 1, message: 'Cannot move ancestor folder' }); + } const res = await $fetch('/api/files/mv', { method: 'post', body: { - src: cwd.value.resolve(src).toString(), + src: resolvedSrc.toString(), dest: cwd.value.resolve(dest).toString(), permission_bits: umask, }, From 4e2a771fde3f3360b920aff8bb0ac75a4495b1fe Mon Sep 17 00:00:00 2001 From: Huy-DNA Date: Wed, 13 Nov 2024 00:23:34 +0700 Subject: [PATCH 2/3] fix: disallow moving, copying, removing a folder to its decendant --- services/files.ts | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/services/files.ts b/services/files.ts index 592e427..e4d0906 100644 --- a/services/files.ts +++ b/services/files.ts @@ -121,7 +121,7 @@ export const fileService = { const { cwd } = useCwdStore(); const resolvedPath = cwd.value.resolve(filename); if (resolvedPath.isAncestor(cwd.value as VirtualPath)) { - return new Err({ code: 1, message: 'Cannot move ancestor folder' }); + return new Err({ code: 1, message: 'Cannot remove ancestor folder' }); } const res = await $fetch('/api/files', { method: 'delete', @@ -191,14 +191,18 @@ export const fileService = { async moveFile (src: string, dest: string, umask: string): Promise> { const { cwd } = useCwdStore(); const resolvedSrc = cwd.value.resolve(src); + const resolvedDest = cwd.value.resolve(dest); if (resolvedSrc.isAncestor(cwd.value as VirtualPath)) { return new Err({ code: 1, message: 'Cannot move ancestor folder' }); } + if (resolvedSrc.isAncestor(resolvedDest)) { + return new Err({ code: 1, message: 'Cannot move a folder to its descendant' }); + } const res = await $fetch('/api/files/mv', { method: 'post', body: { src: resolvedSrc.toString(), - dest: cwd.value.resolve(dest).toString(), + dest: resolvedDest.toString(), permission_bits: umask, }, credentials: 'include', @@ -211,11 +215,16 @@ export const fileService = { }, async copyFile (src: string, dest: string, umask: string): Promise> { const { cwd } = useCwdStore(); + const resolvedSrc = cwd.value.resolve(src); + const resolvedDest = cwd.value.resolve(dest); + if (resolvedSrc.isAncestor(resolvedDest)) { + return new Err({ code: 1, message: 'Cannot copy a folder to its descendant' }); + } const res = await $fetch('/api/files/cp', { method: 'post', body: { - src: cwd.value.resolve(src).toString(), - dest: cwd.value.resolve(dest).toString(), + src: resolvedSrc.toString(), + dest: resolvedDest.toString(), permission_bits: umask, }, credentials: 'include', From 6a5f55510541a06c4c4b128722a54a48211d12f7 Mon Sep 17 00:00:00 2001 From: Huy-DNA Date: Wed, 13 Nov 2024 00:26:14 +0700 Subject: [PATCH 3/3] fix: prevent mv-ing cp-ing a folder to its descendant in the backend --- lib/responseCodes.ts | 2 ++ server/api/files/cp.post.ts | 3 +++ server/api/files/mv.post.ts | 3 +++ 3 files changed, 8 insertions(+) diff --git a/lib/responseCodes.ts b/lib/responseCodes.ts index 3bf5e74..f974a25 100644 --- a/lib/responseCodes.ts +++ b/lib/responseCodes.ts @@ -33,6 +33,7 @@ export enum FileCpErrorCode { INVALID_PARAM = 1000, INVALID_BODY = 1001, NOT_ENOUGH_PRIVILEGE = 2000, + CP_TO_DESCENDANT = 2001, SRC_NOT_FOUND = 3000, DEST_NOT_FOUND = 3001, INVALID_COPY_FOLDER_TO_FILE = 3002, @@ -77,6 +78,7 @@ export enum FileMvErrorCode { INVALID_PARAM = 1000, INVALID_BODY = 1001, NOT_ENOUGH_PRIVILEGE = 2000, + MV_TO_DESCENDANT = 2001, SRC_NOT_FOUND = 3000, DEST_NOT_FOUND = 3001, INVALID_MV_FOLDER_TO_FILE = 3002, diff --git a/server/api/files/cp.post.ts b/server/api/files/cp.post.ts index 7136100..22de459 100644 --- a/server/api/files/cp.post.ts +++ b/server/api/files/cp.post.ts @@ -26,6 +26,9 @@ export default defineEventHandler(async (event) => { if (!dest.isValid()) { return { error: { code: FileCpErrorCode.INVALID_BODY, message: 'Expect the "dest" param to be valid path' } }; } + if (src.isAncestor(dest)) { + return { error: { code: FileCpErrorCode.CP_TO_DESCENDANT, message: 'Cannot copy a folder to it descendant' } }; + } try { await db.serializable(dbPool, async (dbClient) => { diff --git a/server/api/files/mv.post.ts b/server/api/files/mv.post.ts index fb9f993..63b1413 100644 --- a/server/api/files/mv.post.ts +++ b/server/api/files/mv.post.ts @@ -25,6 +25,9 @@ export default defineEventHandler(async (event) => { if (!dest.isValid()) { return { error: { code: FileMvErrorCode.INVALID_BODY, message: 'Expect the "dest" param to be valid path' } }; } + if (src.isAncestor(dest)) { + return { error: { code: FileMvErrorCode.MV_TO_DESCENDANT, message: 'Cannot move a folder to it descendant' } }; + } try { await db.serializable(dbPool, async (dbClient) => {