The best way to learn virtual-patch-copilot is to point it at a deliberately-vulnerable open-source app before you run it on your own code. Two great targets, both maintained as security-training labs:
| App | What it is | Good for |
|---|---|---|
| VAmPI | one small Flask API with intentional OWASP-API flaws | fastest first run (single service) |
| OWASP crAPI | a multi-service "completely ridiculous API" lab | a richer, realistic spread of findings |
There are two ways to run:
- Path A — Scan (read-only). Needs only a model API key. Makes no changes to anything — no cloud, no GitHub, no writes to the target. This is the safe on-ramp; do this first.
- Path B — Full loop behind F5 XC. Actually mitigate live, open code-fix PRs, and retire. Needs an F5 Distributed Cloud tenant and a domain you control. Advanced; do it once Path A makes sense to you.
pip install -e ".[console]"
cp .env.example .env # then add ONE model key, e.g. ANTHROPIC_API_KEYThat single key is all Path A needs. The copilot is model-independent — to run on OpenAI / Gemini /
a local Ollama model instead, set that provider's key and edit config/agents.yaml (see
MODELS.md). No XC or GitHub credentials are required to scan.
git clone https://github.com/erev0s/VAmPI ../VAmPI # small — start here
# or, for more findings:
git clone https://github.com/OWASP/crAPI ../crapiEither from the console (recommended — it walks you through the flow):
vpcopilot console # http://127.0.0.1:8787Open ① Scan, put the repo path (e.g. ../VAmPI) in Target repo, and Run scan. It
auto-advances to ② Review when done.
Or headless:
vpcopilot scan ../VAmPI --out out-vampi
vpcopilot report --open # opens a shareable HTML dashboard of the resultsscan performs no live writes — it only reads the repo and writes results to out-vampi/
(findings, triage, generated XC policies, code-fix PR drafts, and report.html). Safe to run
anywhere.
In the console's ② Review step (or report.html), for each verified finding you'll see the
severity, the effective endpoint, the recommended XC control, and — click a row — the exploit,
the offending code, and the generated band-aid. Roughly what to expect:
- VAmPI → SQL injection on the login/register endpoints, broken object-level auth (BOLA) on the
user routes, and weak/again-guessable auth — routed to
service_policy/waf/api_schema. - crAPI → BOLA (vehicle/mechanic), mass assignment, JWT problems, unauthenticated data exposure,
and OTP brute-force — a spread across
service_policy,api_schema,rate_limit,waf_data_guard.
Every finding also gets a drafted code fix (the cure) — band-aids are temporary by design.
That's the whole safe experience. You've seen what it finds and what it would do, with nothing changed anywhere.
Prefer to look before you even scan? A fully-worked, offline sample lives in
demo/out— runVPCOPILOT_OUT=demo/out vpcopilot console(no keys needed). See DEMO.md.
A scan target doesn't have to be VAmPI or crAPI, and it doesn't even have to be a repo. The same
read-only scan takes three more inputs — alone, or alongside a repo path.
Another repo. Any codebase works — the project's own demo scans the internal nimbus payments app (a Next.js service):
vpcopilot scan ../nimbus-demo/app --out out-nimbus
VPCOPILOT_OUT=out-nimbus vpcopilot console # then walk Review → RetireA set of CVEs — a dependency manifest, resolved against OSV.dev (H2). The natural "set of CVEs"
is a real dependency file: --manifest resolves every pinned package against
OSV.dev in one pass (no cloud creds; the Preview below needs no model at all). A
committed fixture pins aiohttp==3.9.1 (that's CVE-2024-23334) and friends, and deliberately
includes ranged/unpinned lines to show honest handling:
vpcopilot scan --manifest bench/fixtures/manifests/requirements.txt --out out-deps
# repeatable + polyglot: add --manifest package-lock.json --manifest pom.xmlIn the console, ① Scan → "Other inputs & tuning" → Preview (no model calls) shows the dependency
funnel before you spend a token; --min-severity / --max-advisories gate what reaches the resolve
agent, and what's held back is listed and counted, never dropped. A pinned version resolves to a
CVE; a range or an unpinned line can't be resolved to one, so it's reported in the skipped list
rather than guessed. For a single advisory:
vpcopilot scan --cve CVE-2024-23334 --out out-cve # also GHSA- / PYSEC- / GO- / RUSTSEC- idsThe advisory is resolved from OSV.dev and an agent derives its HTTP exploitation profile (or says
there isn't one). Loop --cve for a hand-picked set — one out dir each.
An OpenAPI/Swagger spec — flaws in the contract (H3).
vpcopilot scan --spec bench/fixtures/specs/pay-api.yaml --out out-spec
# add --spec alongside a repo scan to also report spec-vs-code drift
# (crAPI ships its own contract under ../crAPI/openapi-spec/)OWASP API Top 10. crAPI is built around the API Top 10, so a scan of it exercises the categories
end to end. Every finding carries its OWASP-API tag (API1:2023 … API10:2023) beside its CWE
in ② Review, and the HTML report's "Findings by OWASP API Top 10" chart shows the coverage —
including the honest (no category) bar: injection was removed from the 2023 API list, so sqli
carries a CWE and no category, and the chart still accounts for every finding. (The offline
demo/out sample is already crAPI-flavoured, so you can show the OWASP-API grouping with no scan at
all.)
This turns the recommendations into real, validated protection and closes the loop. You need:
- an F5 Distributed Cloud tenant:
XC_API_URL,XC_API_TOKEN,XC_NAMESPACEin.env - a domain you control to front the app (the examples use
*.example.com— substitute your own) - a
GITHUB_TOKEN(orgh auth login) to open code-fix PRs
Run the app somewhere reachable (e.g. docker compose up on a box), then:
vpcopilot lab-create --domain vampi.example.com --origin <app-host>:5000This creates an origin pool + a clean-slate HTTP LB and prints the DNS records to add. Once DNS
resolves and the cert issues, the app is reachable through XC at https://vampi.example.com.
The lab is built by cloning a known-good pool and LB — so every field XC requires is present — and then stripping the copy back to a clean slate with every security control off. Which objects to clone is configuration, because they have to exist in your namespace:
vpcopilot lab-create --domain vampi.example.com --origin <app-host>:5000 \
--pool-template <an-origin-pool-of-yours> --lb-template <an-http-lb-of-yours>or set VPCOPILOT_LAB_POOL_TEMPLATE / VPCOPILOT_LAB_LB_TEMPLATE once in .env. The templates are
only ever read — the copy is what gets created — so it is safe to point them at a load balancer
you would never let the tool modify. If the named object is not there, lab-create says so and
names both ways to change it rather than surfacing a bare 404.
vpcopilot consoleSet the Load balancer and Validate URL in Run settings to your lab LB + URL, then:
- ④ Mitigate — click Mitigate on a finding. With dry-run off, the copilot attaches the
band-aid, fires the finding's real exploit, refines until it's actually blocked, and shows
before 200 → after 403 BLOCKED. Leave keep live off to auto-roll-back after validating (a safe smoke), or on to keep it enforcing. - ⑤ Cure — set a PR repo you can push to, then Open PR to draft the code fix.
- ⑥ Retire — once the cure merges, Retire detaches the temporary XC control.
The equivalent CLI is in USAGE.md (apply --from-scan …, pr …, retire …).
scannever writes anywhere; onlyapply/pr/retiretouch live systems, and only behind the console/CLI human gate.- Every apply snapshots the LB, self-tests the write, validates, and rolls back on failure (or when you don't pass keep live).
- Protected LBs (
VPCOPILOT_PROTECTED_LBS, defaultnimbus-www) andnimbus-*policies refuse mutation unless you explicitly opt in. - Want to be extra-careful validating an unfamiliar app? Set
VPCOPILOT_REQUIRE_PROBE=1so a finding with no derived exploit probe is reported as not validated instead of guessed.
Prefer BIG-IP Advanced WAF to XC? The same loop runs against your own appliance. Set BIGIP_URL /
BIGIP_USER / BIGIP_PASSWORD (⚙ Setup or .env), stand up a sandbox tenant, then apply from the
console's ④ Mitigate step ("Apply on your own BIG-IP") or the CLI:
vpcopilot bigip-lab create --tenant my_app --origin <app-host>:8080 --virtual-address <vip>
vpcopilot apply-bigip --finding <id> --tenant my_app --url https://my-app --dry-runIt's written for BIG-IP admins — no AS3 or DevOps needed — in BIGIP.md.
Run F5 WAF for NGINX (App Protect)? The same finding, same band-aid runs against your own box
over SSH. Set NGINX_SSH_HOST / NGINX_SSH_USER / NGINX_SSH_KEY (⚙ Setup or .env), stand up the
copilot's vhost, then apply from the console's ④ Mitigate step ("Apply on your own NGINX") or the
CLI:
vpcopilot nginx-lab create --server vpcopilot.lab --origin <app-host>:8080
vpcopilot apply-nginx --finding <id> --url http://my-app --dry-runWritten for NGINX admins — no App Protect policy authoring needed — in NGINX.md.
It's the same command — vpcopilot scan /path/to/your-repo (or the ① Scan step). Review the
findings safely first; only go to Path B when you're ready to protect a real app behind XC.