#[derive(Debug, Default, serde::Deserialize)]
pub(super) struct IncidentMonitorScenarioRunInput {
#[serde(default)]
pub pack: Option<crate::IncidentMonitorScenarioPack>,
#[serde(default)]
pub scenario_ids: Vec<String>,
}
fn incident_monitor_scenario_admin_guard(
state_token: Option<&str>,
headers: &HeaderMap,
) -> Option<Response> {
if incident_monitor_assessment_has_scoped_intake_key_header(headers) {
return Some(
(
StatusCode::FORBIDDEN,
Json(json!({
"error": "Incident Monitor scenario packs require an admin API token, not a scoped intake key",
"code": "INCIDENT_MONITOR_SCENARIO_PACKS_ADMIN_REQUIRED",
})),
)
.into_response(),
);
}
if let Some(required_token) = state_token {
if !incident_monitor_assessment_request_has_api_token(headers, required_token) {
return Some(
(
StatusCode::UNAUTHORIZED,
Json(json!({
"error": "Incident Monitor scenario packs require the full admin API token",
"code": "INCIDENT_MONITOR_SCENARIO_PACKS_ADMIN_REQUIRED",
})),
)
.into_response(),
);
}
}
None
}
pub(super) async fn list_incident_monitor_scenario_packs(
State(state): State<AppState>,
headers: HeaderMap,
) -> Response {
if let Some(denied) =
incident_monitor_scenario_admin_guard(state.api_token().await.as_deref(), &headers)
{
return denied;
}
let pack = crate::default_scenario_pack();
Json(json!({
"schema_version": 1,
"scope": {
"source": "incident_monitor_adversarial_scenario_packs",
"read_only": true,
"dry_run": true,
"mutates_external_systems": false,
"admin_invoked": true,
},
"packs": [pack],
"note": "Scenario packs run only in dry-run/sandbox mode and never mutate external systems. Probes must be authorized and bounded to systems you operate.",
}))
.into_response()
}
pub(super) async fn run_incident_monitor_scenario_packs(
State(state): State<AppState>,
Extension(tenant_context): Extension<TenantContext>,
headers: HeaderMap,
Json(input): Json<IncidentMonitorScenarioRunInput>,
) -> Response {
if let Some(denied) =
incident_monitor_scenario_admin_guard(state.api_token().await.as_deref(), &headers)
{
return denied;
}
let mut pack = input.pack.unwrap_or_else(crate::default_scenario_pack);
if !input.scenario_ids.is_empty() {
pack.scenarios
.retain(|scenario| input.scenario_ids.contains(&scenario.scenario_id));
}
let status = state.incident_monitor_status_snapshot().await;
let result = crate::incident_monitor_scenarios::run_incident_monitor_scenario_pack(
&status,
&tenant_context,
&pack,
);
Json(json!({
"schema_version": 1,
"scope": {
"source": "incident_monitor_adversarial_scenario_packs",
"read_only": true,
"dry_run": true,
"mutates_external_systems": false,
"admin_invoked": true,
"tenant_context": tenant_context,
},
"pack": result,
}))
.into_response()
}