[CLAUDE] App: Plan B Hotfix Reviewer — CreateContractCommand validate ApplicableType=Contract
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m30s

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) <noreply@anthropic.com>
This commit is contained in:
pqhuy1987
2026-05-22 12:52:40 +07:00
parent 14feb6955d
commit 3e92584238

View File

@ -70,6 +70,21 @@ public class CreateContractCommandHandler(
.Select(w => (Guid?)w.Id) .Select(w => (Guid?)w.Id)
.FirstOrDefaultAsync(ct); .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. // Validate Budget link nếu có: cùng Project + Phase=DaDuyet.
if (request.BudgetId is Guid bid) if (request.BudgetId is Guid bid)
{ {