Run the Shannon AI pentester on a disposable VM
An AI pentester for your own web apps, in a VM you delete afterwards.
Shelley handles the setup. About 15 minutes, then 1-2 hours per scan.
Prefer a terminal?
curl -fsSL https://exe.dev/deploy/shannon/prompt.txt | ssh exe.dev new --prompt=/dev/stdinShannon is an open-source AI pentester for web apps and APIs, from Keygraph. Give it a running app and that app's source code, and an agent maps the code, looks for injection, XSS, SSRF, and broken authentication and authorization, then attacks the running app in a real browser and with command-line tools. It reports only what it managed to exploit, as a PDF and Markdown report plus SARIF. Version 3.5.0 came out on October 27, 2026; it's licensed AGPL-3.0, and you pay only for the model.
Shannon's own safety guide suggests running it in a disposable virtual machine, against a disposable copy of the app. exe.dev VMs are both. The pentester gets a VM of its own, isolated from your laptop's files and keys; the model comes from exe.dev's LLM integration, so no API key ever lands on a VM; the target is a practice app this page sets up on the same VM, or your own staging app on another VM of yours, reachable privately with no credentials on either side. When the report is safely off, you delete the VM.
Warning
Test only what you own, or have written permission to test. Shannon executes real exploits: it creates users, submits forms, changes and deletes data, and makes outbound requests. Never run it against production, someone else's site, other people's exe.dev VMs, or exe.dev itself. Read Keygraph's safety guide and exe.dev's terms of service first.
Using a pentester from an exe account against an external system, or exe resources owned by another user will trigger our monitoring systems and may result in your account being locked. Only use this against your own exe resources.
What you get#
- Shannon 3.x installed, with its worker image pulled and Docker started on every boot
- Shannon using exe.dev's LLM integration, with no model key on the VM
- OWASP Juice Shop running on the VM as a practice target, restarted with the VM, with its source in
~/juice-shop - A scan config,
~/scans/juice-shop.yaml, whose rules of engagement keep Shannon on that target - A login check against Juice Shop that passes, and no scan started
What's running, and where
ssh my-shannon.exe.xyz
start scans, follow them, read reports
https://my-shannon.exe.xyz:3000/
the practice target, Juice Shop, in your browser
http://localhost:8233/ through an SSH tunnel
Temporal's web UI, for a scan's progress in detail
Deploy with Shelley#
Click Deploy and Shelley will set up Shannon on a new VM, then check that it works. No config-file wrangling required.
Prefer a terminal?
curl -fsSL \
https://exe.dev/deploy/shannon/prompt.txt \
| ssh exe.dev new --prompt=/dev/stdinWhat Shelley is told
Set up Shannon on this VM by following the exe.dev recipe at https://exe.dev/deploy/shannon.md from top to bottom.
The recipe's example VM name is my-shannon. This VM's name is the output of `hostname`: use it wherever the recipe writes my-shannon. Skip the step that creates the VM; you are on it.
You are done when all of these are true. Check each one yourself:
- Shannon 3.x installed, with its worker image pulled and Docker started on every boot
- Shannon using exe.dev's LLM integration, with no model key on the VM
- OWASP Juice Shop running on the VM as a practice target, restarted with the VM, with its source in `~/juice-shop`
- A scan config, `~/scans/juice-shop.yaml`, whose rules of engagement keep Shannon on that target
- A login check against Juice Shop that passes, and no scan started
Run only the commands in "Install by hand", from "Install Shannon" on.
Never start a pentest yourself: the only `shannon start` you run is the
one with `--validate-auth` in "Check the setup". Never point Shannon at
anything but `http://host.docker.internal:3000`. If the login check
fails, show the owner the last lines of its output and stop. Finish by
telling the owner to read "Before you scan" and then follow "Run your
first scan".
Finish with the URLs I should open.
Also at /deploy/shannon/prompt.txt. The recipe it follows is /deploy/shannon.md.
Install#
Deploy installs Shannon, starts Juice Shop as a practice target, and checks that Shannon can log in to it. It doesn't start a scan: that's yours to start. To do it yourself:
Install by hand
On your computer, create a VM and connect. Pick any free name:
ssh exe.dev new --name=my-shannon
ssh my-shannon.exe.xyz
Install Shannon#
On the VM, have Docker start on every boot, install Node.js 24 from NodeSource, and install the Shannon CLI. Then pull the worker image that matches it (about 3.6 GB on disk), so the first scan doesn't wait for it:
sudo systemctl enable --now docker.service
curl -fsSL https://deb.nodesource.com/setup_24.x | sudo -E bash -
sudo apt-get install -y nodejs
sudo npm install -g @keygraph/shannon@3
shannon version
docker pull keygraph/shannon:$(shannon version | awk '{print $2}')
shannon version prints shannon 3.5.0 or later. Keygraph's docs run the
same commands as npx @keygraph/shannon <command>; the global install
pins the major version.
Connect it to the LLM integration#
Shannon reads its model and credentials from ~/.shannon/config.toml.
Point it at exe.dev's LLM integration, which
speaks Anthropic's API at https://llm.int.exe.xyz and adds the real
credential outside the VM. Shannon insists on an API key, so give it a
placeholder:
mkdir -p ~/.shannon
install -m 600 /dev/null ~/.shannon/config.toml
cat > ~/.shannon/config.toml <<'EOF'
[core]
model = "anthropic:claude-sonnet-5"
base_url = "https://llm.int.exe.xyz"
[anthropic]
api_key = "exe-llm-integration"
EOF
Shannon refuses to read the file if anyone but you can, hence the 600.
claude-sonnet-5 is Shannon's default model and Keygraph's first
suggestion; see Use your own model key for
OpenAI and other providers.
Start a practice target#
Run OWASP Juice Shop in Docker, restarted with the VM, and put the source
of the same version in ~/juice-shop for Shannon to read. Its port, 3000,
is private: only you can open https://my-shannon.exe.xyz:3000/.
docker run -d --name juice-shop --restart unless-stopped -p 3000:3000 bkimminich/juice-shop:v20.3.0
git clone --depth 1 --branch v20.3.0 https://github.com/juice-shop/juice-shop.git ~/juice-shop
sleep 15
curl -s -X POST localhost:3000/rest/user/login -H 'Content-Type: application/json' \
-d '{"email":"jim@juice-sh.op","password":"ncc-1701"}' | head -c 40; echo
The last command prints {"authentication":{"token":...: Juice Shop is
up, and Jim, one of the customer accounts it ships with, can log in.
Shannon logs in as Jim. Juice Shop rebuilds its database every time it
starts, so anything a scan changes is gone after docker restart juice-shop,
and accounts you add are too.
Write the scan config#
The config tells Shannon what the app is, how to log in, and the rules of
engagement it must follow. Inside its container, Shannon reaches apps on
the VM at host.docker.internal. The rules keep it on Juice Shop and away
from exe.dev's own addresses:
mkdir -p ~/scans
cat > ~/scans/juice-shop.yaml <<'EOF'
description: "OWASP Juice Shop v20.3.0, a deliberately vulnerable training app, in Docker on this disposable exe.dev VM. Test data only."
rules_of_engagement: |
- The only target is http://host.docker.internal:3000. Send no requests to any other host.
- Never target exe.dev infrastructure: no requests to *.exe.xyz, *.int.exe.xyz, *.exe.dev or 169.254.169.254.
- For SSRF, prove impact against the target itself, not against third-party hosts.
authentication:
login_type: form
login_url: "http://host.docker.internal:3000/#/login"
credentials:
username: "jim@juice-sh.op"
password: "ncc-1701"
login_flow:
- "If a 'Welcome to OWASP Juice Shop' dialog is shown, click 'Dismiss'"
- "If a cookie banner is shown, click 'Me want it!'"
- "Type $username into the Email field"
- "Type $password into the Password field"
- "Click the 'Log in' button"
success_condition:
type: url_contains
value: "/search"
EOF
chmod 600 ~/scans/juice-shop.yaml
Check the setup#
Have Shannon start its Temporal service and do one real login, without attacking anything. It takes about a minute and a few cents of model use:
shannon start -u http://host.docker.internal:3000 -r ~/juice-shop \
-c ~/scans/juice-shop.yaml -w login-check --validate-auth
It ends with Validation COMPLETED and a check list whose
Preflight (LLM credentials, target URL) line says OK. That proves the
model answers through the integration, the worker container reaches the
target, and the login flow works.
Before you scan#
A scan is a real attack, so decide what it may touch:
-
The target is yours. Juice Shop on this VM is built to be attacked. For your own app, see Scan your own app.
-
Nothing else is in reach. Shannon follows the rules of engagement in the config, but it's an agent, so also limit what the VM can reach. With the egress policy set to
integrations, the VM can reach exe.dev's integrations — the LLM one, and a target reached through a VM-to-VM integration — and nothing on the internet. On your computer:ssh exe.dev egress my-shannon integrationsTraffic that never leaves the VM, like the practice target on
host.docker.internal, is unaffected. Run the login check from "Check the setup" again after changing the policy to confirm. Set it back withssh exe.dev egress my-shannon allwhen you need to install or update something. -
The model will cooperate. Anthropic and OpenAI apply safeguards to security work. Shannon checks before every scan and stops early if the provider declines, but a refusal can still end a scan partway.
Run your first scan#
On the VM:
shannon start -u http://host.docker.internal:3000 -r ~/juice-shop \
-c ~/scans/juice-shop.yaml -w juice-1
Shannon runs its checks, starts the scan in a container of its own, and returns you to the prompt. The scan takes an hour or more and carries on when you log out. From any SSH session:
shannon status juice-1 # phases and agents, done and pending
shannon logs juice-1 # the live log; Ctrl-C stops following, not the scan
shannon scans # every scan on this VM
shannon stop juice-1 # stop it
For more detail, open Temporal's web UI. It listens only on the VM's
localhost, so tunnel to it from your computer and open
http://localhost:8233/:
ssh -L 8233:localhost:8233 my-shannon.exe.xyz
If a scan is interrupted, run the same shannon start command with the same
-w name: Shannon resumes it and skips the agents that finished.
Read the report#
Each scan leaves a workspace in ~/.shannon/workspaces/<name>/, with the
report at the top:
Security-Assessment-Report.pdfandSecurity-Assessment-Report.md: each finding with the proof that it was exploited.report.sarif: the same findings for GitHub code scanning or any other SARIF reader..shannon/: logs, prompts, screenshots, and each agent's notes.
On your computer, copy the workspace off the VM:
scp -r my-shannon.exe.xyz:.shannon/workspaces/juice-1 ./shannon-juice-1
The report contains working exploits for your app. Keep it like any other secret. And review it: Keygraph says a model can still get details wrong.
Scan your own app#
Shannon needs your app's source and a running copy with test data. The copy should be disposable: a staging or review deployment, or a throwaway run on a VM. Two ways to place it:
On this VM, next to Shannon — the simplest, and the attack never leaves the VM:
- Clone your repository into
~/myapp, for example through the GitHub integration, so no token lands on the VM. Shannon mounts it read-only. - Start the app on the VM the way you run it locally, such as
docker compose up -d, with test data and test accounts only.
On another VM of yours — the natural home for a staging app, like the test and prod VMs in Dev, Test, Prod. Don't open it to the internet for this: a VM-to-VM integration gives the Shannon VM a private HTTPS address for it, with a generated key injected at exe.dev's edge so neither VM holds a credential. Serve the app on the target VM's port 8000, the web port, then on your computer:
ssh exe.dev integrations add http-proxy --name myapp --target https://my-shannon-target.exe.xyz/ --peer --attach vm:my-shannon
my-shannon-target stands for the target VM's own name. From the Shannon
VM, the app is now at https://myapp.int.exe.xyz/, and that address keeps
working with the egress policy set to integrations. (This page's own
end-to-end test ran the same shape — Shannon on one VM attacking Juice
Shop on a second over its HTTPS address — and got 29 proven findings,
including SQL injection with a forged admin session, in about an hour and
a half with claude-sonnet-5.)
Then, either way:
-
Copy
~/scans/juice-shop.yamlto~/scans/myapp.yamlandchmod 600it, since it will hold a test account's password. Changedescription, the target inrules_of_engagement—http://host.docker.internal:<port>orhttps://myapp.int.exe.xyz, and that host only — and the login. Keygraph's configuration guide covers TOTP, magic links, andrulestoavoidorfocuson paths. -
Check the login, then scan:
shannon start -u https://myapp.int.exe.xyz -r ~/myapp -c ~/scans/myapp.yaml -w myapp-check --validate-auth shannon start -u https://myapp.int.exe.xyz -r ~/myapp -c ~/scans/myapp.yaml -w myapp-1
One VM per scan#
For regular scans, keep this VM as a template and scan in copies. A copy has Shannon, the worker image, and the config already, and keeps the template's egress policy. On your computer:
ssh exe.dev cp my-shannon my-shannon-scan1
ssh my-shannon-scan1.exe.xyz
Run the scan in the copy, copy the workspace off it as in Read the report, and delete it:
ssh exe.dev rm my-shannon-scan1
To script that, the same commands are in exe.dev's HTTPS API, the Python SDK, and the MCP server, so an agent can make the VM, run the scan, and fetch the report for you.
Use your own model key#
The LLM integration bills model use to your exe.dev account, and the provider's safeguards see exe.dev's account. To use your own Anthropic or OpenAI organization, one that has done the provider's security verification, add a separate LLM integration with your key, attached only to this VM. The VM can use the key but never read it. On your computer:
printf '%s' "$ANTHROPIC_API_KEY" | ssh exe.dev integrations add llm --name shannon-llm --anthropic=byok --anthropic-key=- --openai=disabled --fireworks=disabled --attach vm:my-shannon
On the VM, point Shannon at it by changing one line in
~/.shannon/config.toml:
base_url = "https://shannon-llm.int.exe.xyz"
To use OpenAI instead, set model = "openai:gpt-5.6-sol", move the
placeholder key to an [openai] section, and end base_url in /v1.
Keygraph's AI providers guide
lists the models Shannon knows.
Update#
On the VM:
sudo npm install -g @keygraph/shannon@3
docker pull keygraph/shannon:$(shannon version | awk '{print $2}')
Shannon removes older worker images itself. Check the releases first: they sometimes change the config.
Clean up#
When you're done, keep the reports and delete the VM. On your computer:
ssh exe.dev rm my-shannon
To keep the VM but clear out old scans, shannon stop --all stops any that
are running, and shannon reset wipes Temporal's history. Workspaces are
plain directories in ~/.shannon/workspaces/. To reset Juice Shop's data,
restart it: docker restart juice-shop. When the target was a whole VM of
its own, delete that too: ssh exe.dev rm my-shannon-target.
Troubleshooting#
Your config file is readable by other users: runchmod 600 ~/.shannon/config.toml.- Preflight fails on the model: check the integration from the VM with
curl -s https://llm.int.exe.xyz/v1/messages -H 'content-type: application/json' -H 'anthropic-version: 2023-06-01' -d '{"model":"claude-sonnet-5","max_tokens":20,"messages":[{"role":"user","content":"hi"}]}'. A plain reply means the gateway is fine. Shannon checks model names against its own catalogue, so a name the integration knows may still be refused; use one from Keygraph's list. - The provider declined the security workload: the scan stops with a link to the provider's verification program. Complete it, or use your own key as above.
- The target can't be reached: from inside a container,
localhostis the container. Usehttp://host.docker.internal:<port>for apps on the VM, and make sure the app's port is published (docker ps,ss -ltn). For an app on another VM, check the integration address from the VM first:curl -s https://myapp.int.exe.xyz/. - The login check fails: write
login_flowthe way you'd log in by hand in a private window, with the exact labels and button texts. Juice Shop's welcome dialog and cookie banner are the usual culprits. - A scan stalls or fails:
shannon logs <name>shows the combined log,shannon logs <name> --list-agentsthe per-agent logs, and Temporal's UI each step's error. Running the samestartagain resumes. - After a reboot: Docker comes back with Juice Shop; Shannon starts
Temporal again with the next
shannon start.
Frequently asked questions#
What am I allowed to scan with Shannon?
Only systems you own or have written permission to test, and not production. Shannon doesn't just scan: it runs real exploits, creates users, and changes data. Scanning anyone else's site or API without permission is illegal in most places, and it breaks Keygraph's terms and exe.dev's terms of service.
Does exe.dev allow security testing from a VM?
exe.dev's terms of service have no section on security testing. They forbid breaking the law or anyone's rights, and attacking exe.dev's own servers. So: test your own apps, keep the target on the same VM or on another VM of yours, and never aim Shannon at exe.dev itself, other people's VMs, or the internet at large.
How much does a scan cost?
The VM is part of your exe.dev plan; the model is the cost. Keygraph's own benchmark put one app's scan at $6 to $115 in tokens depending on the model, and Keygraph says a full run takes 1 to 1.5 hours. Through the LLM integration it's billed to your exe.dev account; with your own key, by your provider. Our own end-to-end test — Shannon on one VM, Juice Shop on a second — cost 15 cents for the login check and about $45 for the full scan with claude-sonnet-5.
Will Anthropic or OpenAI refuse to run a pentest?
They might. Both apply safeguards to security work, and a refusal stops a scan partway. Shannon checks before every scan; through exe.dev's gateway that check passed with claude-sonnet-5 in our test, and a full scan against a practice app ran without a refusal. For regular scans, consider your own provider account that has completed Anthropic's or OpenAI's verification, through an LLM integration so the key stays off the VM. See Use your own model key.
Can Shannon test an app without its source code?
Not the open-source version: it reads the source to find attack paths, then proves them against the running app. Black-box testing is part of Keygraph's paid editions.
Why a VM rather than my laptop or CI?
Shannon's own safety guide suggests a disposable VM for the most isolation. A scan runs exploit code, a browser, and an agent that reads untrusted input; on an exe.dev VM none of that is near your laptop's files or keys, and you delete the VM when the report is out. For CI, Keygraph has a GitHub Action, which can run on a self-hosted runner.
Can I point it at an app on another VM of mine?
Yes, and that's the natural setup for a staging app: a VM-to-VM integration gives the Shannon VM a private HTTPS address for the other VM, with the credential injected at exe.dev's edge so neither VM holds a key. See Scan your own app.
Updated Nov 2, 2026 · tested end to end Nov 2, 2026 · Markdown · manifest.json · Something wrong? Tell us