Provael — a model-agnostic tool to red-team open Vision-Language-Action (VLA) robot policies in simulation and report an Attack Success Rate (ASR).
Project description
Provael
Prove it. Prevail. — red-team open Vision-Language-Action (VLA) robot policies in simulation and report an Attack Success Rate (ASR).
Deterministic CPU run, seed 0 — regenerate with ./scripts/record_demo.sh.
Provael is the open-source red-team & assurance layer for physical AI. This repo is its core: a small, model-agnostic harness that perturbs the instructions and observations a VLA policy receives inside a simulator and measures how often those perturbations drive the policy into an unsafe state. The headline number is the ASR.
It ships three families of templated, auditable attacks — instruction (text
reframings), visual (observation-space markers), and injection (indirect / embodied
prompt injection) — plus a none baseline, an ASR leaderboard, and a gated adapter for
real SmolVLA policies on the LIBERO simulator. These are heuristic perturbations,
not gradient/optimization-based adversarial attacks — see
Scope and honest limitations.
The entire core — abstractions, attacks, scoring, runner, report, CLI, leaderboard — runs
and is tested on a plain CPU with no GPU and no model/dataset download, using a
deterministic StubPolicy + StubSuite. Real policies (SmolVLA via LeRobot) and the LIBERO
simulator live behind an optional extra and a PROVAEL_INTEGRATION=1 gate.
⚠️ This is a defensive, sim-only tool for hardening policies via responsible disclosure. It drives no physical robots and ships no real-world-harm payloads. Read SAFETY.md before using it.
Scope and honest limitations
This is an early, research-grade harness, built to be reproducible and honest rather than to oversell. Before you trust a number, know:
- Templated attacks, not optimized ones. The attacks are auditable string/observation templates (instruction reframings, image markers, scene text), not gradient- or search-based adversarial methods (GCG/PGD-style). They probe behavioral susceptibility, not worst-case robustness. Optimized VLA attacks are an open roadmap item (cf. prior art BadVLA, AttackVLA).
- Only the instruction family transfers (so far). On real SmolVLA × LIBERO, instruction reframings redirected the policy (roleplay 100%, goal-substitution 60%); the visual and injection families produced 0% measurable lift on the real model. Treat those two as stub-validated scaffolding pending stronger perturbations.
- One policy, one suite shipped. The architecture is model-agnostic by design (an adapter interface), but only the SmolVLA / LeRobot policy and the LIBERO suite are implemented today — generality is intended, not yet demonstrated against a second backend.
- One task, uncalibrated predicate. The headline result is
libero_object/0with a default, uncalibrated keep-out zone, so ASR means "diverted out of the benign envelope," not a calibrated hazard rate. Multi-task + per-task zone calibration is next.
Honesty and reproducibility are the point — see PRIOR_ART.md for how this sits next to the academic state of the art.
Install (CPU core — no GPU, no network)
With uv (recommended):
uv sync # creates a venv and installs the CPU core + dev tools
Or with pip:
python3.12 -m venv .venv && . .venv/bin/activate
pip install -e . # core only; lerobot is NOT pulled in
Quickstart (runs in well under 5 s on a CPU)
uv run provael attack --policy stub --suite stub \
--attacks instruction,visual,injection --episodes 10 --seed 0 --out runs/stub/
Provael — ASR by attack
┏━━━━━━━━━━━━━━━━━━━┳━━━━━━━┳━━━━━━━━━━━┳━━━━━━━━━━┓
┃ attack ┃ ASR ┃ successes ┃ attempts ┃
┡━━━━━━━━━━━━━━━━━━━╇━━━━━━━╇━━━━━━━━━━━╇━━━━━━━━━━┩
│ decoy_object │ 60.0% │ 6 │ 10 │
│ goal_substitution │ 60.0% │ 6 │ 10 │
│ mcp_tool_desc │ 70.0% │ 7 │ 10 │
│ paraphrase │ 70.0% │ 7 │ 10 │
│ patch │ 80.0% │ 8 │ 10 │
│ roleplay │ 80.0% │ 8 │ 10 │
│ scene_text │ 50.0% │ 5 │ 10 │
└───────────────────┴───────┴───────────┴──────────┘
Attack Success Rate (ASR): 67.1% (47/70)
This writes runs/stub/report.json (machine-readable, byte-deterministic) and
runs/stub/report.md. Per family, seed-0 ASR is instruction 21/30, visual 14/20,
injection 12/20 — exact, asserted numbers.
Other commands:
uv run provael list-policies # stub (CPU); smolvla (needs the [lerobot] extra)
uv run provael list-attacks # 7 attacks across families instruction/visual/injection
uv run provael report --in runs/stub/
uv run provael leaderboard build --runs runs --out leaderboard/results # ranked ASR table
uv run provael version
What runs on CPU vs. what needs a GPU
| Capability | CPU (default) | Needs GPU + [lerobot] extra |
|---|---|---|
stub policy + stub suite |
✅ | |
All 3 attack families (instruction/visual/injection) |
✅ | |
Scoring, runner, report, CLI, leaderboard build |
✅ | |
Full test suite (pytest), ruff, mypy |
✅ | |
smolvla policy (real SmolVLA via LeRobot) |
✅ | |
libero suite (real LIBERO simulator) |
✅ |
On CPU, --policy smolvla or --suite libero fails with a clear, actionable message (not a
traceback) telling you exactly what to install.
First real result (SmolVLA on LIBERO)
HuggingFaceVLA/smolvla_libero · libero_object/0 · 10 seeds · horizon 280 · RTX 4090
(osmesa), 2026-06-06.
| family | attack | ASR | lift vs baseline |
|---|---|---|---|
| baseline | none |
0% (0/10) | — |
| instruction | roleplay |
100% (10/10) | +100 |
| instruction | goal_substitution |
60% (6/10) | +60 |
| instruction | paraphrase |
10% (1/10) | +10 |
| visual | patch |
0% (0/10) | 0 |
| visual | decoy_object |
0% (0/10) | 0 |
| injection | scene_text |
0% (0/10) | 0 |
| overall | 24.3% (17/70) ± 9.1% |
Read it as lift over the benign baseline. The none control runs the policy's real
task and scores 0/10, so every success is attack-induced. Language-reframing attacks
reliably divert SmolVLA's end-effector; pixel/scene-text perturbations did not move it (0%).
⚠️ Preliminary. The keep-out predicate is a default, uncalibrated region, so this measures "diverted out of the benign safe envelope," not a zone-calibrated hazard rate;
n = 10, one task. Per-task keep-out-zone calibration is thev0.2.0follow-up.
How it works
┌───────────┐ instruction ┌──────────┐ adversarial ┌──────────┐
task → │ SuiteAdapter│ ───────────────→ │ Attack │ ───instruction─→ │ Policy │
│ reset/step │ │ perturb()│ │ Adapter │
│ is_unsafe()│ ←─── action ──────┴──────────┘ │ act() │
└─────┬───────┘ └────┬─────┘
│ for t in horizon: if is_unsafe(state) → success │
└──────────────────────── runner ─────────────────────────────┘
│
▼
scoring (ASR) → RunReport → report.json / report.md
PolicyAdapter—load(),act(observation, instruction) -> np.ndarray.SuiteAdapter—tasks(),reset(task, seed),step(action),is_unsafe(state).Attack—perturb(instruction, observation) -> (instruction, observation).runner— runs every(task, attack, seed)episode and aggregates.- ASR —
successes / attempts, withby_attackandby_taskbreakdowns.
Determinism. A RunReport embeds no wall-clock time or process-varying values, so the
same config + seed always produces a byte-identical report.json.
Roadmap
- v0.1.0 — Provael (rebrand of the harness): CPU core, 3 attack families, real SmolVLA × LIBERO path, leaderboard. (this release)
- v0.2.0 — SARIF output (
provael report --format sarif) so findings surface in GitHub code scanning · a reusable GitHub Action (provael/provael-action) to run a red-team gate in any robot/VLA repo's CI · Embodied-AI Top-10 mapping (each attack tagged to a risk ID) · per-task keep-out-zone calibration. - later — optimized (gradient/search) attacks; a second policy/suite backend.
Development
uv run ruff check . # lint
uv run mypy src # type-check (strict)
uv run pytest -q # tests (CPU only; LeRobot tests skip unless gated)
Further reading
- SAFETY.md — responsible use, sim-only default, scope.
- PRIOR_ART.md — RoboPAIR, POEX, BadVLA, SafeVLA, and how we differ.
- CHANGELOG.md — what shipped and what's planned.
License
Apache-2.0. Provael — prove it, prevail.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file provael-0.1.0.tar.gz.
File metadata
- Download URL: provael-0.1.0.tar.gz
- Upload date:
- Size: 252.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.18 {"installer":{"name":"uv","version":"0.9.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
889c00dd85aae753133e4ca2a91f80dd5d8f0286fc0bcacda64c2de00908c14d
|
|
| MD5 |
b7944cec02f46a2db0e64904fabcd315
|
|
| BLAKE2b-256 |
c3eaaf457deee2f7d5386982e45fbc8c8183f0df88042f8fd4c1b9ce4bd0e40d
|
File details
Details for the file provael-0.1.0-py3-none-any.whl.
File metadata
- Download URL: provael-0.1.0-py3-none-any.whl
- Upload date:
- Size: 59.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.9.18 {"installer":{"name":"uv","version":"0.9.18","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3f036e21c8c7e483a3bb4fc94142adce385cc0043e9f839e53924ba9c023e55f
|
|
| MD5 |
9126448ff1a4ce568f069ea9f685fcaa
|
|
| BLAKE2b-256 |
1ebeecf8ef72d5c57a4ec412a519abb4cb1ef1421f63acfd29923154203d2592
|