05 Unsafe Control Actions¶
An Unsafe Control Action (UCA) is a control action that, in a particular context, is unsafe. STPA considers four modes per action:
- P: provided when it should not be.
- NP: not provided when it should be.
- T: provided in the wrong timing or order.
- D: applied for the wrong duration (stopped too soon, or continued too long).
Every state-changing MCP tool appears below as a control action. Each cell cites
the hazard (02-hazards.md) it realizes; "n/a" means the mode is not unsafe for
that action. The covering constraint is in 07-security-constraints.md.
Actuation control actions¶
| Control action (tool) | P (provided wrongly) | NP (not provided) | T (wrong order/timing) | D (wrong duration) |
|---|---|---|---|---|
act_shell (ssh/shell on ubuntu/windows) |
UCA-1 run privileged command without approval or against deny list -> H-1, H-3 | n/a (omission is safe) | UCA-2 execute before audit record committed -> H-2 | UCA-3 long-running command not bounded/truncated -> H-9 |
act_ansible (config apply) |
UCA-4 apply playbook without DRY_RUN + approval -> H-1, H-6 | n/a | UCA-5 apply against stale desired-state -> H-6 | n/a |
act_opentofu (infra apply) |
UCA-6 apply plan without approval or on many targets at once -> H-1, H-6 | n/a | UCA-7 apply a plan generated against drifted state -> H-6 | n/a |
act_runbook (subprocess) |
UCA-8 run a runbook whose tier was under-rated -> H-3 | n/a | UCA-9 run before precondition/audit -> H-2 | n/a |
act_talos (talosctl) |
UCA-10 issue destructive talosctl (reset/upgrade) without typed token / one-target -> H-1 | n/a | n/a | n/a |
act_talos against wrong host |
UCA-11 SSH path selected for a Talos host -> H-5 | n/a | n/a | n/a |
act_redfish (OOB power/boot) [planned] |
UCA-12 power/boot change without approval -> H-1 | n/a | UCA-13 power action mistimed (during write) -> H-6 | n/a |
act_cloud (cloud API) [planned] |
UCA-14 mutate cloud resource without approval / SSRF egress check -> H-1, H-7 | n/a | n/a | n/a |
act_kubectl (kubectl on kubernetes) [planned] |
UCA-29 apply/delete/scale without DRY_RUN + approval, or against the deny list -> H-1, H-6 | n/a | UCA-30 act with an exec-plugin or admin (unscoped) kubeconfig, or against a non-kubernetes host_type -> H-1, H-5 |
n/a |
act_helm (helm releases on kubernetes) [planned] |
UCA-31 install/upgrade/uninstall a release without DRY_RUN + approval -> H-1, H-6 | n/a | n/a | n/a |
Convergence and state control actions¶
| Control action | P | NP | T | D |
|---|---|---|---|---|
converge (apply a drift fix) |
UCA-15 converge automatically from a finding (no human gate) -> H-1, H-6 | UCA-16 fail to converge a critical safety drift and not surface it -> H-6, L-5 | UCA-17 converge against a superseded baseline -> H-6 | n/a |
supersede_fact (state write) |
UCA-18 supersede with no actor/reason, or mutate in place -> H-10 | n/a | n/a | n/a |
delete_fact (must not exist) |
UCA-19 any in-place deletion path -> H-10 | n/a | n/a | n/a |
ingest_observation (untrusted-driven state write) |
UCA-27 ingest writes observed facts without an audit record, or without arming the session untrusted latch -> H-2, H-4 | n/a | UCA-28 latch armed after a subsequent actuation already used the clean-session path -> H-4 | n/a |
Operator and boundary control actions¶
| Control action | P | NP | T | D |
|---|---|---|---|---|
approve (operator confirm) |
UCA-20 approval reused across calls / replayed -> H-1 | n/a | UCA-21 approval granted after execution started -> H-2 | UCA-22 approval not bounded to one action, reused on retry -> H-1 |
set_mode |
UCA-23 raise mode ceiling silently / per-tool override -> H-1 | n/a | n/a | n/a |
kill_switch |
n/a | UCA-24 kill switch does not stop in-flight/new execution -> H-8 | n/a | UCA-25 kill switch auto-clears without operator action -> H-8 |
serve_http (enable network) |
UCA-26 bind non-loopback without token + opt-in + SSRF filter -> H-7 | n/a | n/a | n/a |
Coverage note¶
Read-only control actions (T0 collectors, queries, skill reads) are not state
changing and are not enumerated here. Since ADR-0016 (BL-017, BL-062, BL-085) every
registered MCP tool, including the read tools and ingest_observation, routes
through the single audited path (SC-1), so each call writes one audit record; a
read that returns observed facts arms the session untrusted latch, and the ingest
arms it on the path itself (UCA-27/28). Read feedback is treated as untrusted
(SC-4). The skills dispatcher's internal reads remain outside the per-call audit.
Planned control actions: act_redfish (UCA-12, UCA-13) and act_cloud (UCA-14)
from ADR-0022/BL-089, and act_kubectl (UCA-29, UCA-30) and act_helm (UCA-31)
from ADR-0043/BL-111, have no adapter in this version; their rows are marked
[planned] and kept here so the UCA is registered before the code lands. The
kubernetes host_type the act_kubectl/act_helm rows target is itself planned
(ADR-0043): a first-class adapter is admissible only under that ADR's
scoped-static-kubeconfig contract, otherwise kubectl/helm stay a bastion-host skill.
Every UCA-1..31 maps to a covering SEC constraint in 07-security-constraints.md,
with the planned-adapter UCAs flagged there too; the flags clear when the adapters
are implemented and route through the single audited path.