An intentionally vulnerable full-stack task manager.
Built for ethical hackers who are done with toy labs.
Most practice labs feel like practice labs — labelled boxes, obvious hints, unrealistic setups. Todoer doesn't.
It's a real task management app: workspaces, file uploads, public feed, real-time collaboration over WebSockets, Google OAuth, email verification, password reset, and a separate admin panel. Enough attack surface to keep you busy. It also has real defences — CSRF protection, rate limiting, output sanitisation, JWT auth. Some of them work correctly. Some interact with the bugs in very interesting ways.
There are no flags. No hints. No labels. Approach it like a real target.
| Layer | Tech |
|---|---|
| Backend | Node.js + Express |
| Database | SQLite |
| Auth | JWT + cookies, Google OAuth |
| Real-time | WebSocket |
| Frontend | Vanilla HTML/CSS/JS + client-side templating |
| File handling | unzipper, tar-stream, multer |
| Admin | Separate Express service |
| Infrastructure | Docker + Docker Compose + nginx-proxy + Let's Encrypt |
No specifics — go find them yourself. But in broad strokes:
- XSS — more than one kind, more than one location
- File upload bugs
- Auth & OAuth issues
- IDOR and information disclosure
- Client-side attack chains — require chaining multiple bugs together to land
- Missing security controls — gaps hiding in plain sight
Some of the most interesting stuff doesn't work in isolation. The app has at least one multi-stage chain where every step feeds the next, using only information available from within the app itself.
Todoer is built to run on a VPS with a real domain. It uses nginx-proxy and acme-companion to handle reverse proxying and automatic HTTPS — both the app and admin panel get TLS certs via Let's Encrypt, no manual cert work needed.
- Linux (Ubuntu 22.04+ recommended)
- Docker + Docker Compose — install guide
- A domain with two DNS A records pointing to your VPS:
yourdomain.com→ VPS IPadmin.yourdomain.com→ same VPS IP
A setup script that walks you through everything interactively.
git clone https://github.com/Entit-y/Todoer
cd todoer
python3 setup.pyThe script handles: checking prerequisites, collecting config values, generating .env and docker-compose.override.yml, launching containers, and waiting for TLS certificates to provision.
You still need to do these manually before running:
- Point your DNS A records to your VPS IP
- Authenticate your sending domain in Brevo (Part A below)
- Create a Google OAuth client and consent screen (the script will remind you)
git clone https://github.com/Entit-y/Todoer
cd todoermkdir -p certs vhost.d html acme data uploadscp .env.example .env
nano .envThree things to configure: email (Brevo), Google OAuth, and admin credentials.
Todoer sends transactional emails for invite links, email verification, and password resets. It uses Brevo (free tier: 300 emails/day).
Do this in order — skipping Part A breaks delivery.
Part A: Authenticate Your Sending Domain
Brevo can add DNS records automatically for most major providers (GoDaddy, Namecheap, Cloudflare, IONOS, OVHcloud, Hostinger, Squarespace, Wix, Gandi, Dynadot, and others).
- Sign up at brevo.com — no credit card needed
- Click your account name (top-right) → Senders, Domains & IPs → Domains tab
- Click Add a domain, enter your domain, then choose Authenticate automatically
- Log in to your domain provider when prompted — Brevo adds and verifies the DNS records for you
Once your domain shows Authenticated, move to Part B.
If your provider isn't supported, choose Authenticate manually. Brevo gives you three TXT records (Brevo Code, DKIM, and DMARC) to add yourself. Use MXToolbox to check propagation while waiting.
Part B: Get SMTP Credentials
- In Brevo, click the gear icon → SMTP & API → SMTP tab
- Click Generate a new SMTP key, name it (e.g.
todoer), hit Generate - Copy the key immediately — Brevo only shows it once
BREVO_USER={random}@smtp-brevo.com # shown under "Your SMTP Settings"
BREVO_KEY=xsmtpsib-... # key you just generated
BREVO_FROM=noreply@yourdomain.com # must match your verified domainPowers the "Sign in with Google" button.
Part 1: OAuth Consent Screen
- Go to Google Cloud Console → create a new project
- Navigate to APIs & Services → OAuth consent screen
- Choose External, fill in app name + contact email, save through each step
- For test use: keep status as Testing and add your Google email as a test user. For production: publish the app via the Audience page
Part 2: Client ID & Secret
- APIs & Services → Credentials → Create Credentials → OAuth client ID
- Choose Web application, add your redirect URI:
https://yourdomain.com/auth/oauth/callback - Copy the Client ID and Secret immediately
GOOGLE_CLIENT_ID=...apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=GOCSPX-...
GOOGLE_REDIRECT_URI=https://yourdomain.com/auth/oauth/callbackADMIN_USERNAME=youradminusername
ADMIN_PASSWORD=somethingstronghereYour completed .env should look like:
BREVO_USER={random}@smtp-brevo.com
BREVO_KEY=xsmtpsib-...
BREVO_FROM=noreply@yourdomain.com
ADMIN_USERNAME=admin
ADMIN_PASSWORD=Password
GOOGLE_CLIENT_ID=....apps.googleusercontent.com
GOOGLE_CLIENT_SECRET=GOCSPX-...
GOOGLE_REDIRECT_URI=https://yourdomain.com/auth/oauth/callbackReplace every occurrence of your-domain.com with your actual domain:
# app service
- VIRTUAL_HOST=yourdomain.com
- LETSENCRYPT_HOST=yourdomain.com
- APP_URL=https://yourdomain.com
# admin service
- VIRTUAL_HOST=admin.yourdomain.com
- LETSENCRYPT_HOST=admin.yourdomain.comAlso update LETSENCRYPT_EMAIL in both services and the letsencrypt-companion section.
docker compose up -d --build| Container | Role |
|---|---|
todoer-app |
Main app (port 3000, internal) |
todoer-admin |
Admin panel (port 3001, internal) |
nginx-proxy |
Reverse proxy, TLS termination (ports 80/443) |
nginx-proxy-acme |
Auto-provisions + renews Let's Encrypt certs |
Give acme-companion a minute or two on first boot to issue certificates. Then:
- App →
https://yourdomain.com - Admin →
https://admin.yourdomain.com
docker compose ps # all four containers should show "Up"
docker compose logs app # check for startup errors
docker compose logs nginx-proxy-acme # check cert provisioning# Stop containers
docker compose down
# Full reset — wipes DB and uploads
docker compose down -v
rm -f todoer.db
rm -rf uploads/*.
├── server.js ← main app server
├── docker-compose.yml
├── todoer.db ← SQLite database (auto-created on first run)
├── uploads/ ← user-uploaded files (persisted via volume)
├── data/ ← persistent data volume
├── certs/ ← TLS certs (managed by acme-companion)
├── vhost.d/ ← nginx vhost overrides (optional)
├── acme/ ← acme.sh state
├── public/ ← frontend
│ ├── vendor/ ← self-hosted JS libraries
│ ├── fonts/ ← self-hosted fonts
│ ├── home.html
│ ├── feed.html
│ ├── files.html
│ ├── workspaces.html
│ ├── invite.html
│ ├── profile.html
│ ├── vdp.html
│ └── ...
└── admin/ ← separate admin panel service
├── server.js
└── public/
Todoer is the official practice environment for Nimbus Vault — a security research tool built for bug bounty hunters. Use it to learn the platform, test your playbooks, and sharpen your approach before going live. Safe, legal, and realistic.
Enumerate. Map the surface. Look for what doesn't fit.
Think about what happens when you combine what you find.
