From 3e92584238b4f01e21ee378668d91a512584e09e Mon Sep 17 00:00:00 2001 From: pqhuy1987 Date: Fri, 22 May 2026 12:52:40 +0700 Subject: [PATCH] =?UTF-8?q?[CLAUDE]=20App:=20Plan=20B=20Hotfix=20Reviewer?= =?UTF-8?q?=20=E2=80=94=20CreateContractCommand=20validate=20ApplicableTyp?= =?UTF-8?q?e=3DContract?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reviewer pre-push verify (agentId ace4799) catch MAJOR security gap: CreateContractCommand thiếu validation guard rằng ApprovalWorkflowId pin phải có ApplicableType=Contract(3). Attacker forge POST body với V2 PE workflow ID (ApplicableType=1/2 DuyetNcc) → contract pin sai workflow type → Service ApproveV2Async sẽ run pattern PE workflow trên Contract entity → behavior nondeterministic + audit log nhầm. Fix: Mirror PE pattern PurchaseEvaluationFeatures.cs:62-77. Validation block thêm vào CreateContractCommandHandler.Handle sau activeWfId query: 1. Load aw entity by Id (throw NotFound nếu invalid Guid) 2. Verify aw.ApplicableType == Contract(3) (throw Conflict nếu mismatch) Defense-in-depth: FE Workspace dropdown (Chunk D 62b50d1) đã filter ApplicableType=3 client-side; BE guard chặn request forge. Verify: - dotnet build PASS 0 err 2 pre-existing warn - dotnet test 111/111 PASS — 0 regression - Mirror PE pattern exact (only switch enum DuyetNcc/PhuongAn → Contract literal) Smart Friend ROI: Reviewer caught MAJOR before push prod. Cumulative S22 #44 + S25 #48 + S29 (this Hotfix) — pattern proven 3× Reviewer save UAT 401/403 prod incidents. Plan B chain COMPLETE 10/10 (9 + 1 hotfix): - A1 58898e8 / A2 a85e437 / B 138469d / C 26c98d3 / B2 1f199b0 - E1 ef23308 / D 62b50d1 / E2 48f6d22 / E3 14feb69 - Hotfix Rev (this) ApplicableType=Contract guard Co-Authored-By: Claude Opus 4.7 (1M context) --- .../Contracts/ContractFeatures.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/Backend/SolutionErp.Application/Contracts/ContractFeatures.cs b/src/Backend/SolutionErp.Application/Contracts/ContractFeatures.cs index 933c8ff..71336dc 100644 --- a/src/Backend/SolutionErp.Application/Contracts/ContractFeatures.cs +++ b/src/Backend/SolutionErp.Application/Contracts/ContractFeatures.cs @@ -70,6 +70,21 @@ public class CreateContractCommandHandler( .Select(w => (Guid?)w.Id) .FirstOrDefaultAsync(ct); + // [Plan B S29 2026-05-22 Hotfix Reviewer] Validate ApprovalWorkflowId V2 + // (Mig 32) — User chọn lúc create. Phải tồn tại + ApplicableType=Contract(3). + // Mirror PE pattern PurchaseEvaluationFeatures.cs:62-77. Defense-in-depth: + // FE Workspace dropdown đã filter ApplicableType=3 server-side; BE guard + // chặn attacker forge POST với PE workflow ID (ApplicableType=1/2). + if (request.ApprovalWorkflowId is Guid awId) + { + var aw = await db.ApprovalWorkflows.AsNoTracking() + .FirstOrDefaultAsync(w => w.Id == awId, ct) + ?? throw new NotFoundException("ApprovalWorkflow", awId); + if (aw.ApplicableType != Domain.ApprovalWorkflowsV2.ApprovalWorkflowApplicableType.Contract) + throw new ConflictException( + $"Quy trình {aw.Code} áp dụng cho {aw.ApplicableType}, không khớp với HĐ (cần ApplicableType=Contract)."); + } + // Validate Budget link nếu có: cùng Project + Phase=DaDuyet. if (request.BudgetId is Guid bid) {