feat(tests): require evidence upload before phase transitions
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled

Backend:
- submit_red_evidence: raises InvalidOperationError if no Red Team
  evidence file has been uploaded for the test
- submit_blue_evidence: raises InvalidOperationError if no Blue Team
  evidence file has been uploaded

Frontend:
- 'Submit to Blue Team' button: disabled + '⚠ Upload evidence first'
  hint when test.red_evidences is empty
- 'Submit for Review' button: same for test.blue_evidences
- Native tooltip on disabled buttons explains the requirement
- Buttons re-enable automatically after the first file is uploaded

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
kitos
2026-06-02 14:27:15 +02:00
parent 2b41b191bd
commit b4a264f2bd
2 changed files with 58 additions and 20 deletions

View File

@@ -111,21 +111,27 @@ export default function TestDetailHeader({
);
}
// Red Team in red_executing -> Submit to Blue Team
// Red Team in red_executing -> Submit to Blue Team (requires ≥1 red evidence)
if (
test.state === "red_executing" &&
(role === "red_tech" || role === "red_lead" || role === "admin")
) {
const hasRedEvidence = (test.red_evidences?.length ?? 0) > 0;
buttons.push(
<button
key="submit-red"
onClick={onSubmitRed}
disabled={isTransitioning}
className="flex items-center gap-1.5 rounded-lg bg-indigo-600 px-4 py-2 text-sm font-medium text-white hover:bg-indigo-500 transition-colors disabled:opacity-50"
>
{isTransitioning ? <Loader2 className="h-4 w-4 animate-spin" /> : <Send className="h-4 w-4" />}
Submit to Blue Team
</button>,
<div key="submit-red" className="flex flex-col items-end gap-1">
<button
onClick={onSubmitRed}
disabled={isTransitioning || !hasRedEvidence}
title={!hasRedEvidence ? "Upload at least one Red Team evidence file before submitting" : undefined}
className="flex items-center gap-1.5 rounded-lg bg-indigo-600 px-4 py-2 text-sm font-medium text-white hover:bg-indigo-500 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
>
{isTransitioning ? <Loader2 className="h-4 w-4 animate-spin" /> : <Send className="h-4 w-4" />}
Submit to Blue Team
</button>
{!hasRedEvidence && (
<span className="text-[10px] text-orange-400"> Upload evidence first</span>
)}
</div>,
);
}
@@ -149,16 +155,23 @@ export default function TestDetailHeader({
</button>,
);
} else {
// Submit for Review requires ≥1 blue evidence
const hasBlueEvidence = (test.blue_evidences?.length ?? 0) > 0;
buttons.push(
<button
key="submit-blue"
onClick={onSubmitBlue}
disabled={isTransitioning}
className="flex items-center gap-1.5 rounded-lg bg-blue-600 px-4 py-2 text-sm font-medium text-white hover:bg-blue-500 transition-colors disabled:opacity-50"
>
{isTransitioning ? <Loader2 className="h-4 w-4 animate-spin" /> : <Send className="h-4 w-4" />}
Submit for Review
</button>,
<div key="submit-blue" className="flex flex-col items-end gap-1">
<button
onClick={onSubmitBlue}
disabled={isTransitioning || !hasBlueEvidence}
title={!hasBlueEvidence ? "Upload at least one Blue Team evidence file before submitting" : undefined}
className="flex items-center gap-1.5 rounded-lg bg-blue-600 px-4 py-2 text-sm font-medium text-white hover:bg-blue-500 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
>
{isTransitioning ? <Loader2 className="h-4 w-4 animate-spin" /> : <Send className="h-4 w-4" />}
Submit for Review
</button>
{!hasBlueEvidence && (
<span className="text-[10px] text-orange-400"> Upload evidence first</span>
)}
</div>,
);
}
}