From 99bd92a3ca918d9dcae4ae6ffa3dad7162f8fe17 Mon Sep 17 00:00:00 2001 From: Yaojia Wang Date: Tue, 4 Nov 2025 19:50:15 +0100 Subject: [PATCH] fix(backend): Remove TenantId injection vulnerability in CreateProjectCommand MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CRITICAL SECURITY FIX: Removed client-provided TenantId parameter from CreateProjectCommand to prevent tenant impersonation attacks. Changes: - Removed TenantId property from CreateProjectCommand - Injected ITenantContext into CreateProjectCommandHandler - Now retrieves authenticated TenantId from JWT token via TenantContext - Prevents malicious users from creating projects under other tenants Security Impact: - Before: Client could provide any TenantId (HIGH RISK) - After: TenantId extracted from authenticated JWT token (SECURE) Note: CreateEpic, CreateStory, and CreateTask commands were already secure as they inherit TenantId from parent entities loaded via Global Query Filters. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../Commands/CreateProject/CreateProjectCommand.cs | 1 - .../CreateProject/CreateProjectCommandHandler.cs | 12 +++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/colaflow-api/src/Modules/ProjectManagement/ColaFlow.Modules.ProjectManagement.Application/Commands/CreateProject/CreateProjectCommand.cs b/colaflow-api/src/Modules/ProjectManagement/ColaFlow.Modules.ProjectManagement.Application/Commands/CreateProject/CreateProjectCommand.cs index d0f5ed3..5113581 100644 --- a/colaflow-api/src/Modules/ProjectManagement/ColaFlow.Modules.ProjectManagement.Application/Commands/CreateProject/CreateProjectCommand.cs +++ b/colaflow-api/src/Modules/ProjectManagement/ColaFlow.Modules.ProjectManagement.Application/Commands/CreateProject/CreateProjectCommand.cs @@ -8,7 +8,6 @@ namespace ColaFlow.Modules.ProjectManagement.Application.Commands.CreateProject; /// public sealed record CreateProjectCommand : IRequest { - public Guid TenantId { get; init; } public string Name { get; init; } = string.Empty; public string Description { get; init; } = string.Empty; public string Key { get; init; } = string.Empty; diff --git a/colaflow-api/src/Modules/ProjectManagement/ColaFlow.Modules.ProjectManagement.Application/Commands/CreateProject/CreateProjectCommandHandler.cs b/colaflow-api/src/Modules/ProjectManagement/ColaFlow.Modules.ProjectManagement.Application/Commands/CreateProject/CreateProjectCommandHandler.cs index 9766bd5..1b6f5be 100644 --- a/colaflow-api/src/Modules/ProjectManagement/ColaFlow.Modules.ProjectManagement.Application/Commands/CreateProject/CreateProjectCommandHandler.cs +++ b/colaflow-api/src/Modules/ProjectManagement/ColaFlow.Modules.ProjectManagement.Application/Commands/CreateProject/CreateProjectCommandHandler.cs @@ -1,5 +1,6 @@ using MediatR; using ColaFlow.Modules.ProjectManagement.Application.DTOs; +using ColaFlow.Modules.ProjectManagement.Application.Common.Interfaces; using ColaFlow.Modules.ProjectManagement.Domain.Aggregates.ProjectAggregate; using ColaFlow.Modules.ProjectManagement.Domain.Repositories; using ColaFlow.Modules.ProjectManagement.Domain.ValueObjects; @@ -12,14 +13,19 @@ namespace ColaFlow.Modules.ProjectManagement.Application.Commands.CreateProject; /// public sealed class CreateProjectCommandHandler( IProjectRepository projectRepository, - IUnitOfWork unitOfWork) + IUnitOfWork unitOfWork, + ITenantContext tenantContext) : IRequestHandler { private readonly IProjectRepository _projectRepository = projectRepository ?? throw new ArgumentNullException(nameof(projectRepository)); private readonly IUnitOfWork _unitOfWork = unitOfWork ?? throw new ArgumentNullException(nameof(unitOfWork)); + private readonly ITenantContext _tenantContext = tenantContext ?? throw new ArgumentNullException(nameof(tenantContext)); public async Task Handle(CreateProjectCommand request, CancellationToken cancellationToken) { + // Get authenticated tenant ID from JWT token + var tenantId = _tenantContext.GetCurrentTenantId(); + // Check if project key already exists var existingProject = await _projectRepository.GetByKeyAsync(request.Key, cancellationToken); if (existingProject != null) @@ -27,9 +33,9 @@ public sealed class CreateProjectCommandHandler( throw new DomainException($"Project with key '{request.Key}' already exists"); } - // Create project aggregate + // Create project aggregate with authenticated tenant ID var project = Project.Create( - TenantId.From(request.TenantId), + TenantId.From(tenantId), request.Name, request.Description, request.Key,