[FIX] Supply chain: coerenza risk_score + submit atomico (findings review)

Bug #1 (semantica): submitPublicQuestionnaire scriveva risk_score=100-score (rischio) e
sovrascriveva criticality, divergendo da assessSupplier (risk_score=compliance, alto=buono).
Ora: risk_score=score (compliance), security_requirements_met (soglia 70) settato, criticality
NON toccata (è la criticità del fornitore, non l'esito questionario).
Bug #4 (atomicita): UPDATE ... WHERE status='sent' + rowCount()==0 -> 409. Due submit concorrenti
con lo stesso token non completano due volte.
Verificato E2E: submit 201 (score 94 -> risk_score=94, sec_req_met=1, criticality=high invariata),
re-submit -> 409.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
DevEnv nis2-agile 2026-05-30 11:56:20 +02:00
parent 9d3f8936e1
commit a3f821122a

View File

@ -261,13 +261,25 @@ class SupplyChainController extends BaseController
$score = $maxScore > 0 ? (int) round($earned / $maxScore * 100) : 0;
$riskLevel = $score >= 80 ? 'low' : ($score >= 60 ? 'medium' : ($score >= 40 ? 'high' : 'critical'));
Database::query(
'UPDATE supplier_questionnaires SET status=?, answers=?, score=?, risk_level=?, completed_at=NOW() WHERE id=?',
['completed', json_encode($clean, JSON_UNESCAPED_UNICODE), $score, $riskLevel, $q['id']]
// Completamento ATOMICO: vincola lo UPDATE a status='sent' così due submit
// concorrenti con lo stesso token non possono completare due volte (la seconda
// tocca 0 righe -> 409). Previene il doppio-submit senza transazione esplicita.
$upd = Database::query(
'UPDATE supplier_questionnaires SET status=?, answers=?, score=?, risk_level=?, completed_at=NOW()
WHERE id=? AND status=?',
['completed', json_encode($clean, JSON_UNESCAPED_UNICODE), $score, $riskLevel, $q['id'], 'sent']
);
if ($upd->rowCount() === 0) {
$this->jsonError('Questionario gia compilato', 409, 'ALREADY_COMPLETED');
}
// Coerenza con assessSupplier: suppliers.risk_score = punteggio di COMPLIANCE
// (alto = buono), e security_requirements_met soglia 70. NON sovrascriviamo
// suppliers.criticality (è la criticità del fornitore, non l'esito del questionario).
Database::query(
'UPDATE suppliers SET risk_score=?, criticality=?, last_assessment_date=CURDATE() WHERE id=? AND organization_id=?',
[100 - $score, $riskLevel, $q['supplier_id'], $q['organization_id']]
'UPDATE suppliers SET risk_score=?, security_requirements_met=?, last_assessment_date=CURDATE()
WHERE id=? AND organization_id=?',
[$score, $score >= 70 ? 1 : 0, $q['supplier_id'], $q['organization_id']]
);
$this->jsonSuccess(['score' => $score, 'risk_level' => $riskLevel], 'Questionario inviato. Grazie.', 201);