fix(security): V-01,V-02,V-04,V-05,V-06 - multiple vulnerability fixes
- V-01: CalDAV unbounded body limit → MAX_CALDAV_BODY = 1MB - V-02: CardDAV unbounded body limit → MAX_CARDDAV_BODY = 1MB - V-04: Batch operations without max size → MAX_BATCH_SIZE = 1000 - V-05: WOPI get_editor_url IDOR → authorize_wopi_access with ownership check - V-06: JWT secret without entropy validation → panic <16, warn 16-31, accept >=32
This commit is contained in:
@@ -14,6 +14,10 @@ use crate::application::services::batch_operations::{
|
||||
use crate::interfaces::api::handlers::ApiResult;
|
||||
use crate::interfaces::middleware::auth::AuthUser;
|
||||
|
||||
/// Maximum number of items allowed in a single batch request.
|
||||
/// Prevents fan-out amplification attacks and database connection exhaustion.
|
||||
const MAX_BATCH_SIZE: usize = 1_000;
|
||||
|
||||
/// Shared state for the batch handler
|
||||
#[derive(Clone)]
|
||||
pub struct BatchHandlerState {
|
||||
@@ -143,6 +147,15 @@ pub async fn move_files_batch(
|
||||
)
|
||||
.into_response());
|
||||
}
|
||||
if request.file_ids.len() > MAX_BATCH_SIZE {
|
||||
return Ok((
|
||||
StatusCode::BAD_REQUEST,
|
||||
Json(serde_json::json!({
|
||||
"error": format!("Batch size {} exceeds maximum of {}", request.file_ids.len(), MAX_BATCH_SIZE)
|
||||
})),
|
||||
)
|
||||
.into_response());
|
||||
}
|
||||
|
||||
// Execute batch operation
|
||||
let result = state
|
||||
@@ -187,6 +200,15 @@ pub async fn copy_files_batch(
|
||||
)
|
||||
.into_response());
|
||||
}
|
||||
if request.file_ids.len() > MAX_BATCH_SIZE {
|
||||
return Ok((
|
||||
StatusCode::BAD_REQUEST,
|
||||
Json(serde_json::json!({
|
||||
"error": format!("Batch size {} exceeds maximum of {}", request.file_ids.len(), MAX_BATCH_SIZE)
|
||||
})),
|
||||
)
|
||||
.into_response());
|
||||
}
|
||||
|
||||
// Execute batch operation
|
||||
let result = state
|
||||
@@ -231,6 +253,15 @@ pub async fn delete_files_batch(
|
||||
)
|
||||
.into_response());
|
||||
}
|
||||
if request.file_ids.len() > MAX_BATCH_SIZE {
|
||||
return Ok((
|
||||
StatusCode::BAD_REQUEST,
|
||||
Json(serde_json::json!({
|
||||
"error": format!("Batch size {} exceeds maximum of {}", request.file_ids.len(), MAX_BATCH_SIZE)
|
||||
})),
|
||||
)
|
||||
.into_response());
|
||||
}
|
||||
|
||||
// Execute batch operation
|
||||
let result = state
|
||||
@@ -283,6 +314,15 @@ pub async fn delete_folders_batch(
|
||||
)
|
||||
.into_response());
|
||||
}
|
||||
if request.folder_ids.len() > MAX_BATCH_SIZE {
|
||||
return Ok((
|
||||
StatusCode::BAD_REQUEST,
|
||||
Json(serde_json::json!({
|
||||
"error": format!("Batch size {} exceeds maximum of {}", request.folder_ids.len(), MAX_BATCH_SIZE)
|
||||
})),
|
||||
)
|
||||
.into_response());
|
||||
}
|
||||
|
||||
// Execute batch operation
|
||||
let result = state
|
||||
@@ -335,6 +375,15 @@ pub async fn create_folders_batch(
|
||||
)
|
||||
.into_response());
|
||||
}
|
||||
if request.folders.len() > MAX_BATCH_SIZE {
|
||||
return Ok((
|
||||
StatusCode::BAD_REQUEST,
|
||||
Json(serde_json::json!({
|
||||
"error": format!("Batch size {} exceeds maximum of {}", request.folders.len(), MAX_BATCH_SIZE)
|
||||
})),
|
||||
)
|
||||
.into_response());
|
||||
}
|
||||
|
||||
// Transform the format for the service
|
||||
let folders = request
|
||||
@@ -386,6 +435,15 @@ pub async fn get_files_batch(
|
||||
)
|
||||
.into_response());
|
||||
}
|
||||
if request.file_ids.len() > MAX_BATCH_SIZE {
|
||||
return Ok((
|
||||
StatusCode::BAD_REQUEST,
|
||||
Json(serde_json::json!({
|
||||
"error": format!("Batch size {} exceeds maximum of {}", request.file_ids.len(), MAX_BATCH_SIZE)
|
||||
})),
|
||||
)
|
||||
.into_response());
|
||||
}
|
||||
|
||||
// Execute batch operation
|
||||
let result = state
|
||||
@@ -430,6 +488,15 @@ pub async fn get_folders_batch(
|
||||
)
|
||||
.into_response());
|
||||
}
|
||||
if request.folder_ids.len() > MAX_BATCH_SIZE {
|
||||
return Ok((
|
||||
StatusCode::BAD_REQUEST,
|
||||
Json(serde_json::json!({
|
||||
"error": format!("Batch size {} exceeds maximum of {}", request.folder_ids.len(), MAX_BATCH_SIZE)
|
||||
})),
|
||||
)
|
||||
.into_response());
|
||||
}
|
||||
|
||||
// Execute batch operation
|
||||
let result = state
|
||||
@@ -495,6 +562,16 @@ pub async fn trash_batch(
|
||||
)
|
||||
.into_response());
|
||||
}
|
||||
let combined_size = request.file_ids.len() + request.folder_ids.len();
|
||||
if combined_size > MAX_BATCH_SIZE {
|
||||
return Ok((
|
||||
StatusCode::BAD_REQUEST,
|
||||
Json(serde_json::json!({
|
||||
"error": format!("Batch size {} exceeds maximum of {}", combined_size, MAX_BATCH_SIZE)
|
||||
})),
|
||||
)
|
||||
.into_response());
|
||||
}
|
||||
|
||||
let mut all_successful: Vec<String> = Vec::new();
|
||||
let mut all_failed: Vec<FailedOperation> = Vec::new();
|
||||
@@ -597,6 +674,15 @@ pub async fn move_folders_batch(
|
||||
)
|
||||
.into_response());
|
||||
}
|
||||
if request.folder_ids.len() > MAX_BATCH_SIZE {
|
||||
return Ok((
|
||||
StatusCode::BAD_REQUEST,
|
||||
Json(serde_json::json!({
|
||||
"error": format!("Batch size {} exceeds maximum of {}", request.folder_ids.len(), MAX_BATCH_SIZE)
|
||||
})),
|
||||
)
|
||||
.into_response());
|
||||
}
|
||||
|
||||
let result = state
|
||||
.batch_service
|
||||
@@ -637,6 +723,13 @@ pub async fn download_batch(
|
||||
"No file or folder IDs provided".to_string(),
|
||||
));
|
||||
}
|
||||
let combined_size = request.file_ids.len() + request.folder_ids.len();
|
||||
if combined_size > MAX_BATCH_SIZE {
|
||||
return Err((
|
||||
StatusCode::BAD_REQUEST,
|
||||
format!("Batch size {} exceeds maximum of {}", combined_size, MAX_BATCH_SIZE),
|
||||
));
|
||||
}
|
||||
|
||||
let temp_file = state
|
||||
.batch_service
|
||||
|
||||
Reference in New Issue
Block a user