Skip to content

Latest commit

 

History

5 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

LabForge

A local dashboard to spin up (and tear down) a full set of deliberately vulnerable practice labs — one click per lab, live logs, no memorizing docker run flags.

LabForge wraps 12 well-known vulnerable web/API training apps (OWASP Juice Shop, WebGoat, crAPI, DVWA, and more) behind a single Flask + vanilla JS dashboard. Toggle a lab on, watch it pull/build in real time, click through to it, toggle it off when you're done.

⚠️ These are intentionally vulnerable applications. Only run LabForge on a machine/network you control, never expose these ports to the public internet, and tear labs down when you're not actively using them. This project automates starting known-vulnerable apps for learning purposes — it doesn't harden or sandbox them beyond normal Docker container isolation.


Features

  • One-click start/stop per lab, plus "Engage All" / "Abort All" for the whole set.
  • Non-blocking — starting one lab (even a slow multi-GB pull) never freezes the dashboard or blocks other labs from starting at the same time.
  • Live log streaming per lab, right on its card — see what's actually happening during a pull/build instead of staring at a spinner.
  • Live status polling against docker ps, so the dashboard reflects reality even if you start/stop something from your own terminal.
  • Info cards for every lab — stack, difficulty, a rough first-run time estimate, and specific things to go practice (e.g. "BOLA", "JWT algorithm confusion", "NoSQL injection").
  • Search + filters (by category, or "active only").
  • Built to be readable and hackable — it's ~2 files, no build step, no framework lock-in.

LabForge dashboard

Labs included

Lab Stack Difficulty Default URL
crAPI Node.js / Python / Go microservices Intermediate localhost:8888
VAmPI Python / Flask Beginner localhost:5000
DVGA (GraphQL) Python / Flask / GraphQL Intermediate localhost:5013
OWASP Juice Shop Node.js / Angular Beginner–Advanced localhost:3000
WrongSecrets Java / Spring Intermediate localhost:8085
DVWA PHP / MySQL Beginner localhost:8081
WebGoat + WebWolf Java / Spring Boot Beginner–Intermediate localhost:8082/WebGoat
NodeGoat Node.js / Express / MongoDB Intermediate localhost:4000
Mutillidae II PHP / MySQL Beginner localhost:8084/mutillidae
bWAPP PHP / MySQL Beginner–Intermediate localhost:8086/install.php
XVWA PHP / MySQL Intermediate localhost:8087/xvwa
RailsGoat Ruby on Rails Intermediate localhost:3001

Requirements

  • Docker (running, with enough disk space — expect 15-20 GB total if you pull everything)
  • Python 3.9+
  • ~2 GB+ free RAM to run more than a couple of labs concurrently

Quick start

git clone <your-repo-url> labforge
cd labforge
pip install -r requirements.txt --break-system-packages   # drop the flag if using a venv
python3 app.py

Open http://localhost:7070.

First time using a lab, run install for the ones that need a git clone (crAPI, VAmPI, DVGA, NodeGoat, RailsGoat) — everything else pulls a prebuilt image directly:

./setup_all_labs.sh install all

Then just use the dashboard toggles, or drive it from the CLI directly:

./setup_all_labs.sh start <lab>      # or 'all'
./setup_all_labs.sh stop <lab>       # or 'all'
./setup_all_labs.sh status
./setup_all_labs.sh clean            # nuke all containers + cloned repos

How it works

app.py is a thin Flask layer over setup_all_labs.sh — every button in the UI just calls the same script you could run from a terminal. The one thing worth knowing about the implementation:

Every start/stop runs in a background thread and returns immediately. Early versions of this ran subprocess.run() synchronously and made the HTTP request wait for the whole docker pull/build to finish — for multi-GB pulls that's minutes, and since Flask's dev server isn't threaded by default, the entire app froze for that whole window: other lab toggles, the status poll, everything queued up behind it. Now each action spawns a thread, the server runs threaded=True, and the frontend polls a small /api/job/<lab> endpoint for live output. You can start several labs at once and the UI stays responsive throughout.

Known quirks (and what's already handled for you)

A handful of these labs have upstream setup quirks that aren't obvious until something silently doesn't work. LabForge already works around all of these — noting them here in case you extend the script further or hit something new:

  • "Engage All" used to silently skip labs after the first failure — the script runs under set -e, and calling each lab's start function bare meant one failure killed the whole batch. Every call in the all sequence is now individually guarded.
  • crAPI's docker-compose.yml lives under deploy/docker/, not the repo root — running compose from the wrong directory fails with no configuration file provided.
  • DVGA binds to 127.0.0.1 inside its container by default — needs -e WEB_HOST=0.0.0.0 or Docker's port-forward has nothing to reach, giving "connection reset."
  • RailsGoat needs a database setup step (rails db:prepare / db:setup) between build and up, or the Rails process crashes immediately on boot from a missing schema. Its compose file also publishes to host port 3000, which collides with Juice Shop — this gets remapped to 3001 automatically, even for a repo cloned before this fix.
  • Old/interrupted clones used to get stuck forever — install steps only checked whether the target folder existed, so a broken partial clone would report "already cloned, skip" on every future run. They now check for the actual file each lab needs and re-clone if it's missing.
  • XVWA's upstream repo has no docker-compose.yml at all (manual LAMP setup only), and the first replacement image tried (tuxotron/xvwa) turned out to be built in a Docker image format modern Docker refuses to pull. It now uses brightsec/xvwa, a same-source rebuild in a current format. It's linux/amd64-only, so it runs under emulation (slower) on Apple Silicon.
  • Security Shepherd was removed. Its image doesn't auto-launch its own services and proved unreliable even after scripting the documented manual startup sequence. If you want it, OWASP's official setup builds it from source via Maven — see the SecurityShepherd wiki.

If a lab still won't behave, check its container's actual logs before assuming the dashboard is at fault:

docker ps -a --filter "name=<container>"
docker logs --tail 80 <container>

Project structure

labforge/
├── app.py                 # Flask backend + background job engine
├── setup_all_labs.sh      # does the actual docker/git work, callable standalone
├── requirements.txt
└── templates/
    └── index.html         # the whole frontend (HTML/CSS/JS, no build step)

Contributing

Issues and PRs welcome — especially if an upstream lab image changes again and something in here breaks. If you add a lab, please match the existing pattern: a start_<lab>/stop_<lab> pair in setup_all_labs.sh, a matching entry in LABS in app.py, and a guard in the all sequence.

License

MIT — see LICENSE.

Credits

LabForge just orchestrates other people's excellent work. All credit for the actual vulnerable applications goes to their respective maintainers: OWASP (crAPI, Juice Shop, WebGoat, NodeGoat, Mutillidae II, RailsGoat, Security Shepherd), erev0s (VAmPI), dolevf (DVGA), and the WrongSecrets, DVWA, bWAPP, and XVWA projects. Go star their repos too.

About

A lightweight local dashboard to spin up 12+ vulnerable web & API security labs (Juice Shop, crAPI, DVWA, WebGoat) with a single click. No port clashes, live Docker logs.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages