A corporate expense reimbursement web application — built as an intentionally vulnerable target for security training, coursework, and practice assessments.
Employees submit expense claims with receipt attachments, managers approve claims from their team, and Finance Admins handle high-value approvals, company-wide reporting, and user administration.
This application is intentionally vulnerable and is for educational / training use only.
- Do not deploy this on a public-facing network, cloud instance with a public IP, or any environment reachable from the open internet.
- Do not reuse any code, patterns, or dependency versions from this project in a production system.
- Run it inside an isolated environment — a host-only/internal virtual network, an isolated VLAN, or behind a VPN mesh (e.g. Tailscale) limited to trusted devices.
- All data (users, expenses, financial details) is synthetic and seeded for demonstration purposes. Nothing in this app represents a real person, company, or transaction.
- No warranty of any kind is provided. Use entirely at your own risk.
- Backend: Node.js, Express
- Database: PostgreSQL (via Sequelize ORM)
- Views: EJS (server-rendered)
- File handling: Multer (uploads), ImageMagick + Ghostscript (receipt normalization/watermarking)
- Auth: JWT (cookie-based sessions)
- Containerization: Docker + Docker Compose
- Employee/Manager/Finance Admin role-based accounts
- Expense claim submission with receipt attachments and comment threads
- Manager approval queue for direct reports; Finance Admin queue for high-value claims
- Company-wide expense search/filtering
- Department spend and employee history reporting (Finance Admin)
- Bulk employee onboarding via CSV (Finance Admin)
- User account administration
Requirements: Docker and Docker Compose installed.
git clone <your-repo-url>
cd expenseflow
cp .env.example .env
# Review/edit .env if needed — defaults work out of the box for local use
docker compose up --buildOnce the containers are up, seed the database with demo accounts and sample data:
docker compose exec app node src/seed.jsThe app will be available at http://localhost:3000 (or the host/port you configured — see Environment Variables).
Confirm it's running:
curl http://localhost:3000/health
# {"status":"UP"}Requirements: Node.js 20+, PostgreSQL 14+, ImageMagick + Ghostscript installed on the host.
git clone <your-repo-url>
cd expenseflow
cp .env.example .env
# Edit .env with your local Postgres connection details
npm install
node src/seed.js
npm start| Variable | Description | Default |
|---|---|---|
NODE_ENV |
development or production |
development |
PORT |
App listen port | 3000 |
HOST |
App bind address | 0.0.0.0 |
DB_HOST |
PostgreSQL host | localhost |
DB_PORT |
PostgreSQL port | 5432 |
DB_USER |
PostgreSQL user | expenseflow |
DB_PASS |
PostgreSQL password | expensepassword |
DB_NAME |
PostgreSQL database name | expenseflow |
JWT_SECRET |
JWT signing secret (access token) | — |
JWT_REFRESH_SECRET |
JWT signing secret (refresh token) | — |
SMTP_MOCK |
If true, outgoing email is logged instead of sent |
true |
Change
DB_PASS,JWT_SECRET, andJWT_REFRESH_SECRETfrom their defaults for anything beyond quick local testing.
All seeded accounts use the password password123 unless changed.
| Role | Department | |
|---|---|---|
admin@expenseflow.local |
Finance Admin | Finance |
manager1@expenseflow.local |
Manager | Engineering |
manager2@expenseflow.local |
Manager | Sales |
employee1@expenseflow.local … employee15@expenseflow.local |
Employee | Engineering / Sales |
expenseflow/
├── Dockerfile
├── docker-compose.yml
├── package.json
├── .env.example
└── src/
├── server.js # App entry point
├── database.js # Sequelize/Postgres connection
├── seed.js # Demo data seeding script
├── middleware/ # Auth & role-check middleware
├── models/ # Sequelize models
├── routes/ # Express route handlers
└── views/ # EJS templates
GET /health → {"status": "UP"}
Uploaded receipt attachments are stored in a Docker volume so they persist
across container restarts. Run docker compose down -v if you want a
completely clean slate (this also wipes the database).
docker compose down -v
docker compose up --build
docker compose exec app node src/seed.jsMIT — see LICENSE.
This project is provided for educational and authorized security-training purposes only. The authors are not responsible for any misuse or damage caused by this software. By using this project you agree to only deploy it in environments you own or are explicitly authorized to test, and to keep it isolated from production networks and the public internet.