fix(security): patch 3 vulnerabilities — IDOR, ownership bypass, XSS

V1: Add owner-scoped folder pagination (list_folders_by_owner_paginated)
  - New method in FolderRepository trait, PG implementation, service & handler
  - Prevents IDOR by filtering folder listings to authenticated user

V2: Enforce ownership checks on folder mutations
  - rename_folder, move_folder, delete_folder now require caller_id
  - Service verifies folder.owner_id == caller_id (returns 404 on mismatch)
  - Propagated to folder_handler, batch_handler, batch_operations, webdav_handler
  - delete_folder_with_trash upgraded from OptionalAuthUser to AuthUser
  - download_folder_zip now checks ownership before streaming

V3: Fix XSS in frontend via DOM APIs
  - sharedView.js: innerHTML → createElement + textContent
  - contextMenus.js: innerHTML → DOM construction for share dialog

Cleanup: removed unused OptionalAuthUser import, updated all stubs/mocks
This commit is contained in:
Dionisio
2026-02-16 00:22:42 +01:00
parent 66b4acd9d6
commit 5a679dfc90
25 changed files with 684 additions and 269 deletions
+23 -2
View File
@@ -268,6 +268,17 @@ impl FolderRepository for StubFolderStoragePort {
Ok((Vec::new(), Some(0)))
}
async fn list_folders_by_owner_paginated(
&self,
_parent_id: Option<&str>,
_owner_id: &str,
_offset: usize,
_limit: usize,
_include_total: bool,
) -> Result<(Vec<Folder>, Option<usize>), DomainError> {
Ok((Vec::new(), Some(0)))
}
async fn rename_folder(&self, _id: &str, _new_name: String) -> Result<Folder, DomainError> {
Ok(Folder::default())
}
@@ -374,19 +385,29 @@ impl FolderUseCase for StubFolderUseCase {
Ok(PaginatedResponseDto::new(Vec::new(), 0, 10, 0))
}
async fn list_folders_for_owner_paginated(
&self,
_parent_id: Option<&str>,
_owner_id: &str,
_pagination: &PaginationRequestDto,
) -> Result<PaginatedResponseDto<FolderDto>, DomainError> {
Ok(PaginatedResponseDto::new(Vec::new(), 0, 10, 0))
}
async fn rename_folder(
&self,
_id: &str,
_dto: RenameFolderDto,
_caller_id: &str,
) -> Result<FolderDto, DomainError> {
Ok(FolderDto::default())
}
async fn move_folder(&self, _id: &str, _dto: MoveFolderDto) -> Result<FolderDto, DomainError> {
async fn move_folder(&self, _id: &str, _dto: MoveFolderDto, _caller_id: &str) -> Result<FolderDto, DomainError> {
Ok(FolderDto::default())
}
async fn delete_folder(&self, _id: &str) -> Result<(), DomainError> {
async fn delete_folder(&self, _id: &str, _caller_id: &str) -> Result<(), DomainError> {
Ok(())
}
}