163 lines
4.2 KiB
TypeScript
163 lines
4.2 KiB
TypeScript
|
|
/**
|
||
|
|
* Mission types + query-key factory.
|
||
|
|
*
|
||
|
|
* A mission is a *snapshot* of one or more scenario templates: the backend
|
||
|
|
* copies template fields into mission_* tables at creation time, and template
|
||
|
|
* edits after that point do not propagate. Types here mirror the server-side
|
||
|
|
* dataclasses in `app/services/missions.py`.
|
||
|
|
*/
|
||
|
|
|
||
|
|
export type MissionStatus = 'draft' | 'in_progress' | 'completed' | 'archived';
|
||
|
|
export type MissionRoleHint = 'red' | 'blue';
|
||
|
|
export type MissionTestState =
|
||
|
|
| 'pending'
|
||
|
|
| 'executed'
|
||
|
|
| 'reviewed_by_blue'
|
||
|
|
| 'skipped'
|
||
|
|
| 'blocked';
|
||
|
|
export type MissionVisibilityMode = 'whitebox' | 'titles_only' | 'executed_only';
|
||
|
|
export type MissionMitreKind = 'tactic' | 'technique' | 'subtechnique';
|
||
|
|
export type MissionOpsecLevel = 'low' | 'medium' | 'high';
|
||
|
|
|
||
|
|
export interface MissionMember {
|
||
|
|
user_id: string;
|
||
|
|
user_email: string;
|
||
|
|
user_display_name: string | null;
|
||
|
|
role_hint: MissionRoleHint;
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface MissionMitreTag {
|
||
|
|
kind: MissionMitreKind;
|
||
|
|
external_id: string;
|
||
|
|
name: string;
|
||
|
|
url: string | null;
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface MissionTest {
|
||
|
|
id: string;
|
||
|
|
position: number;
|
||
|
|
snapshot_name: string;
|
||
|
|
snapshot_description: string | null;
|
||
|
|
snapshot_objective: string | null;
|
||
|
|
snapshot_procedure_md: string | null;
|
||
|
|
snapshot_prerequisites_md: string | null;
|
||
|
|
snapshot_expected_red_md: string | null;
|
||
|
|
snapshot_expected_blue_md: string | null;
|
||
|
|
snapshot_opsec_level: MissionOpsecLevel;
|
||
|
|
snapshot_tags: string[];
|
||
|
|
snapshot_expected_iocs: string[];
|
||
|
|
state: MissionTestState;
|
||
|
|
executed_at: string | null;
|
||
|
|
executed_at_overridden: boolean;
|
||
|
|
mitre_tags: MissionMitreTag[];
|
||
|
|
source_test_template_id: string | null;
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface MissionScenario {
|
||
|
|
id: string;
|
||
|
|
position: number;
|
||
|
|
snapshot_name: string;
|
||
|
|
snapshot_description: string | null;
|
||
|
|
tests: MissionTest[];
|
||
|
|
source_scenario_template_id: string | null;
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface MissionListItem {
|
||
|
|
id: string;
|
||
|
|
name: string;
|
||
|
|
client_target: string | null;
|
||
|
|
date_start: string | null;
|
||
|
|
date_end: string | null;
|
||
|
|
status: MissionStatus;
|
||
|
|
description_md: string | null;
|
||
|
|
visibility_mode: MissionVisibilityMode;
|
||
|
|
scenarios_count: number;
|
||
|
|
tests_count: number;
|
||
|
|
members_count: number;
|
||
|
|
deleted_at: string | null;
|
||
|
|
created_at: string;
|
||
|
|
updated_at: string;
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface Mission extends MissionListItem {
|
||
|
|
scenarios: MissionScenario[];
|
||
|
|
members: MissionMember[];
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface MissionListResponse {
|
||
|
|
items: MissionListItem[];
|
||
|
|
total: number;
|
||
|
|
limit: number;
|
||
|
|
offset: number;
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface MissionFilters {
|
||
|
|
q?: string;
|
||
|
|
status?: MissionStatus | '';
|
||
|
|
client?: string;
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface MemberPayload {
|
||
|
|
user_id: string;
|
||
|
|
role_hint: MissionRoleHint;
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface CreateMissionPayload {
|
||
|
|
name: string;
|
||
|
|
client_target?: string | null;
|
||
|
|
date_start?: string | null;
|
||
|
|
date_end?: string | null;
|
||
|
|
description_md?: string | null;
|
||
|
|
scenario_template_ids?: string[];
|
||
|
|
members?: MemberPayload[];
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface UpdateMissionPayload {
|
||
|
|
name?: string;
|
||
|
|
client_target?: string | null;
|
||
|
|
date_start?: string | null;
|
||
|
|
date_end?: string | null;
|
||
|
|
description_md?: string | null;
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface AddScenariosPayload {
|
||
|
|
scenario_template_ids: string[];
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface SetMembersPayload {
|
||
|
|
members: MemberPayload[];
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface TransitionPayload {
|
||
|
|
status: MissionStatus;
|
||
|
|
}
|
||
|
|
|
||
|
|
export const missionKeys = {
|
||
|
|
list: (filters?: MissionFilters) => ['missions', 'list', filters ?? {}] as const,
|
||
|
|
detail: (id: string) => ['missions', 'detail', id] as const,
|
||
|
|
};
|
||
|
|
|
||
|
|
export function buildMissionQueryString(filters: MissionFilters | undefined): string {
|
||
|
|
if (!filters) return '';
|
||
|
|
const params = new URLSearchParams();
|
||
|
|
if (filters.q) params.set('q', filters.q);
|
||
|
|
if (filters.status) params.set('status', filters.status);
|
||
|
|
if (filters.client) params.set('client', filters.client);
|
||
|
|
const s = params.toString();
|
||
|
|
return s ? `?${s}` : '';
|
||
|
|
}
|
||
|
|
|
||
|
|
export const MISSION_STATUS_ACCENT: Record<MissionStatus, 'cyan' | 'orange' | 'green' | 'teal'> = {
|
||
|
|
draft: 'cyan',
|
||
|
|
in_progress: 'orange',
|
||
|
|
completed: 'green',
|
||
|
|
archived: 'teal',
|
||
|
|
};
|
||
|
|
|
||
|
|
export const MISSION_STATUS_LABEL: Record<MissionStatus, string> = {
|
||
|
|
draft: 'Draft',
|
||
|
|
in_progress: 'In Progress',
|
||
|
|
completed: 'Completed',
|
||
|
|
archived: 'Archived',
|
||
|
|
};
|