diff --git a/capabilities/ai-red-teaming/agents/ai-red-teaming-agent.md b/capabilities/ai-red-teaming/agents/ai-red-teaming-agent.md index 2568444..0421845 100644 --- a/capabilities/ai-red-teaming/agents/ai-red-teaming-agent.md +++ b/capabilities/ai-red-teaming/agents/ai-red-teaming-agent.md @@ -58,9 +58,12 @@ Probe the security and safety of AI applications, agents, and foundation models. --- Then wait for the user's request. Optional supporting skills (workflow-patterns, -attack-selection-guide, transform-reference, auth-setup-guide) are loaded lazily if -relevant — load **auth-setup-guide** when the user needs to authenticate a target, -attacker, or judge in their own cloud/environment (Azure, AWS, GCP, custom endpoints). +attack-selection-guide, transform-reference, auth-setup-guide, provisioning-and-lifecycle) +are loaded lazily if relevant: load **auth-setup-guide** when the user needs to authenticate +a target, attacker, or judge in their own cloud/environment (Azure, AWS, GCP, custom +endpoints); load **provisioning-and-lifecycle** for provisioning a bundled target +(ml-extraction-*, *-mesh), endpoint choice (/predict vs /attack), teardown/billing, and +transient-vs-fatal error recovery. @@ -140,6 +143,7 @@ Complete requests that don't need clarification: - Reason about SDK internals. - Give up after a single failure — retry with adjusted parameters. - Use a "bash" or "shell" tool — use `execute_workflow` instead. +- Guess CLI syntax. To provision a target, ALWAYS call the `provision_environment` tool - never run `dreadnode env ...` in a shell. Public bundled targets (e.g. `ml-extraction-fraud-tabular`, `finops-mesh`) resolve by bare name; pass the bare name and do not try to qualify it. ## Tools @@ -164,7 +168,7 @@ The AI Red Teaming capability provides these tools: **Multi-Agent Environments:** - **list_environments** — List the deployable multi-agent environments (e.g. `finops-mesh`, `devsecops-mesh`, `healthcare-mesh`, `soc-mesh`) that ATLAS can target -- **provision_environment** — Deploy a hosted target and return the endpoint that matches its type. A multi-agent mesh (e.g. `finops-mesh`) returns an `/attack` URL + execute token → chain into `generate_atlas_attack`. A black-box ML classifier (e.g. `ml-extraction-mnist-image`) returns a `/predict` endpoint (plus `/pool`, `/members`, `/nonmembers`) → use `generate_evasion_attack` / `generate_extraction_attack` / `generate_membership_attack` / `generate_inversion_attack` with `api_url=/predict`. **Do not fetch `/attack` on a classifier target - it does not serve it.** The sandbox is recorded and torn down automatically when the assessment completes. +- **provision_environment** — The only way to provision a target; never shell out to `dreadnode env`. Pass the task by bare name (e.g. `ml-extraction-fraud-tabular`, `finops-mesh`) - public bundled targets resolve by bare name from any workspace. Returns the endpoint that matches the target type: a multi-agent mesh returns an `/attack` URL + execute token → chain into `generate_atlas_attack`; a black-box ML classifier returns a `/predict` endpoint (plus `/pool`, `/members`, `/nonmembers`) → use `generate_evasion_attack` / `generate_extraction_attack` / `generate_membership_attack` / `generate_inversion_attack` with `api_url=/predict`. **Do not fetch `/attack` on a classifier target - it does not serve it.** The sandbox is recorded and torn down automatically when the assessment completes. - **teardown_environment** — Delete provisioned environment sandboxes to stop billing. Hosted sandboxes bill for their whole lifetime. With no id it reaps every environment provisioned this session; pass an id to reap one. Teardown also runs automatically when `update_assessment_status` marks the assessment complete, so call this only to reap early or after a partial run. **Workflow Management:** diff --git a/capabilities/ai-red-teaming/capability.yaml b/capabilities/ai-red-teaming/capability.yaml index 6a4afba..cfcc080 100644 --- a/capabilities/ai-red-teaming/capability.yaml +++ b/capabilities/ai-red-teaming/capability.yaml @@ -1,6 +1,6 @@ schema: 1 name: ai-red-teaming -version: "1.14.0" +version: "1.15.0" description: > Probe the security and safety of AI applications, agents, and foundation models. Orchestrates adversarial attack workflows to discover vulnerabilities in LLMs, diff --git a/capabilities/ai-red-teaming/skills/error-troubleshooting/SKILL.md b/capabilities/ai-red-teaming/skills/error-troubleshooting/SKILL.md index 3de81bc..b03379f 100644 --- a/capabilities/ai-red-teaming/skills/error-troubleshooting/SKILL.md +++ b/capabilities/ai-red-teaming/skills/error-troubleshooting/SKILL.md @@ -115,6 +115,31 @@ Common errors and fixes for AIRT attack workflows. - **Cause**: All trials errored or timed out - **Fix**: Check for model/network errors. Reduce complexity (fewer transforms, simpler attack). +## Provisioning & Sandbox Errors + +For the full lifecycle, load the `provisioning-and-lifecycle` skill. + +### "Task not found" / 404 provisioning a bundled target +- **Cause**: A bare task name resolves only within the caller's org, but bundled targets (`ml-extraction-*`, `*-mesh`) live in the public `dreadnode/` catalog. +- **Fix**: Use `provision_environment` with the BARE name - it catches the 404 and retries as `/` automatically. Never shell out to `dreadnode env`. +- **Do NOT**: qualify a bundled task with your own org (e.g. `aisf-learner-aug-2026/ml-extraction-imdb-text`) - it is not there and there is no fallback. Use the bare name or `dreadnode/`. + +### "404" on GET environments//status +- **Cause**: The sandbox is already torn down or expired - you are polling a dead environment. +- **Fix**: Treat it as terminal ("terminated"), stop polling, and do not surface it as an error. + +### Endpoint mismatch 404 (/attack vs /predict) +- **Cause**: Probing the wrong endpoint for the target type - e.g. fetching `/attack` on an ML classifier that only serves `/predict`. +- **Fix**: Read the `>>> NEXT STEP` line from `provision_environment`. Classifier -> `/predict`; mesh -> `/attack`. Never probe both. + +### Sandbox lifecycle & billing (avoid leaks) +- **Cause**: A hosted sandbox bills for its whole lifetime; forgetting teardown leaks cost until TTL. +- **Fix**: Finish with `update_assessment_status` (auto-teardown fires on `completed` and `failed`), or call `teardown_environment()` on early abort. Set `AIRT_ENV_TEARDOWN_GRACE_SEC` >= your longest attack timeout so teardown does not kill an in-flight attack. + +### Transient vs fatal (Note: vs Error:) +- **`Note:` prefix** = transient network fault (TLS/timeout/conn-reset/502-504) already auto-retried; it did not affect running or recorded work - just re-run the step, do not report failure. +- **`Error:` prefix** = a non-fatal input/tool issue - adjust params, do not blind-retry. + ## Retry Strategy 1. **First failure**: Read the error message, adjust the specific parameter that failed diff --git a/capabilities/ai-red-teaming/skills/provisioning-and-lifecycle/SKILL.md b/capabilities/ai-red-teaming/skills/provisioning-and-lifecycle/SKILL.md new file mode 100644 index 0000000..d8c7349 --- /dev/null +++ b/capabilities/ai-red-teaming/skills/provisioning-and-lifecycle/SKILL.md @@ -0,0 +1,55 @@ +--- +name: provisioning-and-lifecycle +description: Provision a hosted target, pick the right endpoint per target type, tear down to stop billing, and interpret transient vs fatal errors. Load this whenever the user wants to provision/attack a bundled target (ml-extraction-*, *-mesh) or asks about environments, sandboxes, teardown, or billing. +allowed-tools: provision_environment teardown_environment list_environments generate_atlas_attack generate_evasion_attack generate_extraction_attack generate_membership_attack generate_inversion_attack register_assessment update_assessment_status +--- + +# Provisioning and Lifecycle + +How to provision a hosted target, run against the right endpoint, and tear it down cleanly. The tool layer already handles resolution, endpoint selection, retries, and teardown - your job is to use the tool (never the CLI) and interpret its output correctly. + +## 1. Provisioning decision tree + +- User names a bundled/hosted task (`finops-mesh`, `ml-extraction-fraud-tabular`, `ml-extraction-mnist-image`, `ml-extraction-imdb-text`) -> call `provision_environment` with the BARE name. +- User gives an HTTP URL -> skip provisioning; go straight to `generate_agentic_attack` / `generate_atlas_attack`. +- NEVER run `dreadnode env ...` in a shell, and NEVER guess a `provision`/`create` subcommand. `provision_environment` is the only supported path and it resolves the catalog for you. + +## 2. Task-ref resolution + +A bare name resolves server-side to a task in your org OR any public task, so bundled public targets (`ml-extraction-*`, `*-mesh`) work by bare name with no extra qualification. + +- Prefer the BARE name (`ml-extraction-fraud-tabular`). It resolves to the public catalog automatically. +- A task owned by another org resolves only when it is public or owned by you. If a bare name 404s, the task is private to another org (or the name/version is wrong) - qualifying it as `/` will NOT help, because the same visibility rule applies. +- Do NOT qualify a bundled task with the caller's own org (e.g. `aisf-learner-aug-2026/ml-extraction-imdb-text`) - it does not live there. +- If provisioning fails, the returned message is authoritative: report it. Do not invent qualification syntax or retry random forms. + +## 3. Endpoint-per-target map (fixes /attack on a /predict classifier) + +Read the `>>> NEXT STEP` line in `provision_environment` output and use exactly that endpoint. Never probe both `/attack` and `/predict` on one target. + +- Mesh (`*-mesh`) -> serves `/attack` -> `generate_atlas_attack` with the execute token. +- Classifier (`ml-extraction-*`, tabular/image/text, mnist/fraud/imdb) -> serves `/predict` (+ `/pool`, `/members`, `/nonmembers`) -> `generate_evasion_attack` / `generate_extraction_attack` / `generate_membership_attack` / `generate_inversion_attack` with `api_url=/predict`. + +## 4. Teardown and billing + +Every provision bills for its whole lifetime. + +- Preferred: wrap the run in `register_assessment` -> attacks -> `update_assessment_status`. The sandbox is auto-torn-down once EVERY planned attack has been recorded, whether it passed or failed. +- Gotcha: if you abandon a planned attack without recording a status (e.g. you give up after an error), the assessment never reaches terminal and the sandbox is NOT auto-torn-down - it bills until its TTL. Always either record every planned attack or call `teardown_environment()` (no id = reap every environment from this session). +- Set `AIRT_ENV_TEARDOWN_GRACE_SEC` >= your longest attack timeout before running, so assessment-completion teardown does not kill an in-flight attack. + +## 5. Transient vs fatal error interpretation + +- A result starting with `Note:` = transient (TLS handshake / timeout / conn reset / 502-504), already auto-retried by the tool, and it did NOT affect any running attack or recorded result. Tell the user it was transient and re-run the step. +- A result starting with `Error:` = a non-fatal input/tool issue. Adjust params; do not blind-retry. +- A `404` from probing an endpoint is exploratory, not a failure - say what you learned and switch to the correct endpoint. +- A `404` on `GET environments//status` means the sandbox is already gone (torn down or expired). Treat it as "terminated" and STOP polling - it is not an error to surface. +- Never let a raw error be the last thing the user sees about a step that actually succeeded. + +## 6. ASR display + +ASR is stored 0-1 internally. Never format it yourself - report what the tool returns (it renders `1.0` as `100%`). If you ever read a raw fraction from JSON, multiply by 100 before showing it. + +## 7. Attribution + +Assessments are attributed to the operator via the platform auth context (org / workspace) and, where available, the `origin_user` on the assessment. If a per-user field is missing on an older assessment, attribute via org/workspace and cross-reference the `assessment_id` in platform audit logs - do not claim a field that is not present. diff --git a/capabilities/ai-red-teaming/tools/environments.py b/capabilities/ai-red-teaming/tools/environments.py index 2f63d73..b830fbc 100644 --- a/capabilities/ai-red-teaming/tools/environments.py +++ b/capabilities/ai-red-teaming/tools/environments.py @@ -234,7 +234,6 @@ def list_environments() -> str: return "\n".join(lines) -@safe_tool def _target_kind(task_ref: str) -> str: """Classify a provisionable target so we return the right endpoint + guidance. @@ -278,11 +277,29 @@ def provision_environment( return "Not configured for a platform org/workspace. Run `dreadnode login` first." model_overrides = {model_role: model} if model else None - env = TaskEnvironment( - api, org=org, workspace=workspace, task_ref=task_ref, - model_overrides=model_overrides, timeout_sec=timeout_sec, - ) - ctx = _run(env.setup()) + + def _mk(ref: str) -> t.Any: + return TaskEnvironment( + api, org=org, workspace=workspace, task_ref=ref, + model_overrides=model_overrides, timeout_sec=timeout_sec, + ) + + # A bare name resolves to a task in the caller's org or any public task + # (server-side visibility rule), so bundled public targets work by bare name. + # A task owned by another org resolves only when it is public or owned by the + # caller - a private cross-org task 404s the same way whether or not it is + # qualified, so there is no client-side retry that helps; just add a hint. + env = _mk(task_ref) + try: + ctx = _run(env.setup()) + except Exception as exc: # noqa: BLE001 - add a resolution hint on not-found + if not _is_not_found(exc): + raise + raise RuntimeError( + f"Task '{task_ref}' not found. A bare name resolves to a task in your org " + f"or any public task; a task owned by another org must be public or owned " + f"by you. Check the name and version, or qualify it as /." + ) from exc svc = (ctx.get("service_urls") or {}).get("challenge") url = (svc.get("url") if isinstance(svc, dict) else svc) or "" token = env._execute_token or "" # noqa: SLF001 - one-shot provision token @@ -302,7 +319,10 @@ def provision_environment( ) teardown_note = ( "\n>>> WHEN DONE: this sandbox bills for its whole lifetime - it is torn down " - "automatically when the assessment completes, or call teardown_environment() now." + "automatically once every planned attack is recorded (pass or fail). If you abandon " + "the run without recording all attacks, call teardown_environment() now so it does not " + "bill until its TTL. For long runs set AIRT_ENV_TEARDOWN_GRACE_SEC >= your longest " + "attack timeout so completion-teardown does not kill an in-flight attack." ) if kind == "classifier":