Rules of Engagement & kill switch
Activate a fail-closed RoE, list in-run approvals, and halt a pentest with the kill switch.
What you’ll build
The fail-closed control plane around a pentest: you create and activate a Rules of
Engagement that covers the assets, list pending approvals and decide them, and fire the
kill switch (distinct from cancel). Without an active RoE the orchestrator will not start
(no_active_roe).
The CLI does not create RoE — use the SDK, REST, or the web platform.
Prerequisites
- A Rank account and an API token (see Authentication).
- A pentest whose assets fall inside the allow-list you declare (or let the script create one).
pip install rank-sdk
export RANK_API_KEY=rk_...
Steps
-
Create (or reuse) a pentest, then activate a RoE that covers its assets.
authorization_refandescalation_contactsare required.activate=Trueis what makes the run legal to start.import rank client = rank.Rank() pentest = client.pentests.create( name="Engagement control", type="web", mode="automatic", assets=[ {"asset_type": "url", "asset_value": "https://example.com", "is_primary": True}, ], ) roe = client.pentests.roe.create( pentest.id, allowed_domains=["example.com"], timezone="Europe/Madrid", max_rps=10, max_concurrency=4, requires_approval_for=["exploit"], authorization_ref="ENG-2026-0042", escalation_contacts=[{"type": "email", "value": "security@example.com"}], activate=True, ) print(roe.version, roe.status) -
List pending approvals and decide them. Restricted actions pause until someone approves or denies.
scope_expansionis never auto-approved.pending = client.pentests.approvals.list(pentest.id, status="pending") for a in pending.approvals: decided = client.pentests.approvals.decide( pentest.id, a.id, decision="approve", reason="In scope for this window", ) print(a.id, a.action_class, decided.status) -
Kill is the terminal emergency stop. Cancel asks a running stream to stop (Go). Kill is the PHP control flag; evidence is preserved and the pentest cannot return to an active state.
client.pentests.kill(pentest.id, reason="Out of authorized window") # Soft stop of a running stream instead: # client.pentests.cancel(pentest.id)
-
Create the pentest, then
POSTa RoE withactivate: true.curl https://api.aleex-rank.ai/api/v2/pentests \ -H "X-API-Key: $RANK_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "Engagement control", "type": "web", "mode": "automatic", "assets": [{"asset_type": "url", "asset_value": "https://example.com", "is_primary": true}] }' curl https://api.aleex-rank.ai/api/v2/pentests/42/roe \ -H "X-API-Key: $RANK_API_KEY" \ -H "Content-Type: application/json" \ -d '{ "allowed_domains": ["example.com"], "timezone": "Europe/Madrid", "max_rps": 10, "max_concurrency": 4, "requires_approval_for": ["exploit"], "authorization_ref": "ENG-2026-0042", "escalation_contacts": [{"type": "email", "value": "security@example.com"}], "activate": true }' -
List pending approvals and decide one.
GET /api/v2/pentests/42/approvals?status=pending POST /api/v2/pentests/42/approvals/88/decide Content-Type: application/json {"decision": "approve", "reason": "In scope for this window"} -
Kill (REST control plane). Cancel is a different host:
POST https://aleex.aleex-rank.ai/pentest/42/cancel.curl https://api.aleex-rank.ai/api/v2/pentests/42/kill \ -H "X-API-Key: $RANK_API_KEY" \ -H "Content-Type: application/json" \ -d '{"reason": "Out of authorized window"}'
The approval-class vocabulary is GET /catalogs/approval-classes (client.catalogs.list_approval_classes()). Window warnings and control_stop arrive on the live stream — see Streaming.
Run it
Save the following as engagement_control.py, set RANK_API_KEY, then run
python engagement_control.py. Kill is commented out so the script is safe to run
repeatedly.
"""Engagement control — RoE, approvals, and the kill switch.
What this script does:
1. Creates a web pentest against example.com (or reuses RANK_PENTEST_ID).
2. Activates a Rules of Engagement that covers the assets.
3. Lists pending approvals and approves each one.
4. Prints how kill differs from cancel (kill is commented out).
Without an active RoE the orchestrator will not start.
Run:
pip install rank-sdk
export RANK_API_KEY=rk_...
python engagement_control.py
Optional environment variables:
RANK_PENTEST_ID Existing pentest whose assets fall inside example.com.
RANK_AUTH_REF RoE authorization_ref (default: ENG-2026-0042).
RANK_ESCALATION Escalation email (default: security@example.com).
"""
from __future__ import annotations
import os
import rank
PENTEST_ID = int(os.environ.get("RANK_PENTEST_ID", "0"))
AUTH_REF = os.environ.get("RANK_AUTH_REF", "ENG-2026-0042")
ESCALATION = os.environ.get("RANK_ESCALATION", "security@example.com")
def main() -> None:
with rank.Rank() as client:
pentest_id = PENTEST_ID
if pentest_id <= 0:
pentest = client.pentests.create(
name="Engagement control",
type="web",
mode="automatic",
assets=[
{
"asset_type": "url",
"asset_value": "https://example.com",
"is_primary": True,
},
],
)
pentest_id = pentest.id
print(f"Created pentest #{pentest_id}")
else:
print(f"Using pentest #{pentest_id}")
current = client.pentests.roe.retrieve(pentest_id)
if current.active:
print(f"Active RoE v{current.active.version} status={current.active.status}")
else:
print("No active RoE — the orchestrator will not start.")
roe = client.pentests.roe.create(
pentest_id,
allowed_domains=["example.com"],
timezone="Europe/Madrid",
max_rps=10,
max_concurrency=4,
requires_approval_for=["exploit"],
authorization_ref=AUTH_REF,
escalation_contacts=[{"type": "email", "value": ESCALATION}],
activate=True,
)
print(f"Activated RoE v{roe.version} status={roe.status}")
pending = client.pentests.approvals.list(pentest_id, status="pending")
print(f"{pending.total} pending approval(s)")
for a in pending.approvals:
print(f" [{a.id}] {a.action_class} target={a.target}")
decided = client.pentests.approvals.decide(
pentest_id, a.id,
decision="approve",
reason="In scope for this window",
)
print(f" -> {decided.status}")
print("cancel() asks a running stream to stop (Go).")
print("kill() is the terminal switch (PHP); evidence is preserved.")
print("Uncomment the next line to actually halt the run.")
# killed = client.pentests.kill(pentest_id, reason="Out of authorized window")
# print(killed.kill_requested, killed.reason)
if __name__ == "__main__":
try:
main()
except rank.AuthenticationError:
print("ERROR: invalid or missing API key. Set RANK_API_KEY.")
except rank.NotFoundError:
print("ERROR: pentest not found. Set RANK_PENTEST_ID or omit it to create one.")
except rank.APIError as exc:
print(f"API error ({exc.status_code}): {exc.message}")