fix(tests): lock editing for operators until timer starts
Some checks failed
Aegis CI / lint-and-test (push) Has been cancelled

red_tech can only edit procedure/tool/summary when the test is in
red_executing state (after pressing Start Execution). In draft state they
see a read-only view and an orange hint 'Press Start Execution to begin
editing — the timer must be running first.'

blue_tech can only edit when blue_work_started_at is set (after pressing
Start Evaluation). Before that they see an indigo hint 'Press Start
Evaluation to begin editing — pick up the test first.'

red_lead, blue_lead and admin are unaffected — they retain full edit
access in all applicable states including draft.
This commit is contained in:
kitos
2026-06-03 08:14:02 +02:00
parent 46722aec19
commit 2bbc65993c

View File

@@ -118,18 +118,46 @@ export default function TeamTabs({
enabled: !!test.technique_mitre_id, enabled: !!test.technique_mitre_id,
}); });
// Leads and admins can edit during both draft and executing phases.
// Operators (red_tech) may only edit once execution has started —
// the timer must be running before they can document the attack.
const canEditRed = const canEditRed =
RED_EDITABLE_STATES.includes(test.state) && (test.state === "red_executing" &&
(role === "red_tech" || role === "red_lead" || role === "admin"); (role === "red_tech" || role === "red_lead" || role === "admin")) ||
(test.state === "draft" && (role === "red_lead" || role === "admin"));
// Blue operators may only edit after they explicitly pick up the test
// (Start Evaluation pressed → blue_work_started_at is set).
// Blue leads and admins can edit at any point during blue_evaluating.
const canEditBlue = const canEditBlue =
BLUE_EDITABLE_STATES.includes(test.state) && BLUE_EDITABLE_STATES.includes(test.state) &&
(role === "blue_tech" || role === "blue_lead" || role === "admin"); ((role === "blue_lead" || role === "admin") ||
(role === "blue_tech" && !!test.blue_work_started_at));
// Hint messages shown to operators when editing is locked
const redLockedHint =
test.state === "draft" && role === "red_tech"
? "Press Start Execution to begin editing — the timer must be running first."
: null;
const blueLockedHint =
BLUE_EDITABLE_STATES.includes(test.state) &&
role === "blue_tech" &&
!test.blue_work_started_at
? "Press Start Evaluation to begin editing — pick up the test first."
: null;
// ── Red Team Tab ───────────────────────────────────────────────── // ── Red Team Tab ─────────────────────────────────────────────────
const renderRedTab = () => ( const renderRedTab = () => (
<div className="space-y-6"> <div className="space-y-6">
{/* Locked hint for red_tech in draft state */}
{redLockedHint && (
<div className="flex items-center gap-2 rounded-lg border border-orange-500/30 bg-orange-500/5 px-4 py-3 text-sm text-orange-400">
<span className="text-base"></span>
{redLockedHint}
</div>
)}
{/* Procedure */} {/* Procedure */}
<div> <div>
<label className="mb-1.5 block text-sm font-medium text-gray-300"> <label className="mb-1.5 block text-sm font-medium text-gray-300">
@@ -262,6 +290,13 @@ export default function TeamTabs({
const renderBlueTab = () => ( const renderBlueTab = () => (
<div className="space-y-6"> <div className="space-y-6">
{/* Locked hint for blue_tech before Start Evaluation */}
{blueLockedHint && (
<div className="flex items-center gap-2 rounded-lg border border-indigo-500/30 bg-indigo-500/5 px-4 py-3 text-sm text-indigo-400">
<span className="text-base"></span>
{blueLockedHint}
</div>
)}
{/* Detection Result */} {/* Detection Result */}
<div> <div>
<label className="mb-2 block text-sm font-medium text-gray-300"> <label className="mb-2 block text-sm font-medium text-gray-300">