Back to path
BeginnerLedgerline · Project 1 of 12 ~3h· 5 milestones

Run it and map how it could be attacked

Somewhere in this repo is an IDOR, a query you can inject, a secret sitting in git history, and a container running wider open than it should.

Threat modeling with STRIDEOWASP Top 10 2021 mappingAttack surface enumerationDocker Compose for local stacksAPI testing with curlReading Express route and SQL code for riskAuthorized security testing on your own instanceSecurity documentation as a living artifact

What you'll build

You end this rung with Ledgerline running locally under docker compose, two test accounts you control, a committed security/attack-surface.md route inventory, and a security/threat-model.md that names assets, trust boundaries, a STRIDE pass, an OWASP Top 10 2021 mapping, and a ranked top-risks list, including one IDOR you personally reproduced with curl against your own instance.

See how we teach, before you sign up

You don't just get code dumped on you. Every starter file and every solution is explained line-by-line, in plain English. Here's one real file from this project:

docker-compose.ymlyaml
version: "3.9"
services:
  postgres:
    image: postgres:16-alpine
    environment:
      POSTGRES_USER: ledgerline
      POSTGRES_PASSWORD: ${DB_PASSWORD}
      POSTGRES_DB: ledgerline
    ports:
      - "5432:5432"
    volumes:
      - pgdata:/var/lib/postgresql/data
  app:
    build: .
    ports:
      - "3000:3000"
    environment:
      DATABASE_URL: postgres://ledgerline:${DB_PASSWORD}@postgres:5432/ledgerline
      JWT_SECRET: ${JWT_SECRET}
    depends_on:
      - postgres
volumes:
  pgdata:

Reading this file

  • image: postgres:16-alpinePinned tag, not latest, so your local rebuilds stay reproducible.
  • POSTGRES_PASSWORD: ${DB_PASSWORD}Comes from your local .env, never commit a real value here.
  • JWT_SECRET: ${JWT_SECRET}The signing secret for auth tokens, treat it as sensitive from day one, even in dev.
  • depends_on:The app waits for the postgres container to start, but not for it to be ready to accept queries, that gap is a common first-boot ECONNREFUSED.

Boots the two-container local stack you use for every milestone in this rung.

That's 1 of 9 explained code blocks in this single project.

The build, milestone by milestone

  1. 1

    Boot it and become two test users

    5 guided steps

    You cannot threat model an app you have only read. Using it once as a real customer, with two separate identities, is what lets you later recognize a boundary crossing when you cause one on purpose.

  2. 2

    Map the attack surface before touching a scanner

    6 guided steps

    A scanner finds instances of known bug classes. Before you run one, you need your own map of where those classes could live, in your own words, tied to real routes, so a scan result later means something to you instead of being a number to trust blindly.

  3. 3

    STRIDE the real routes, not the app in the abstract

    5 guided steps

    STRIDE gives you a checklist so you do not just rate the app on vibes. Applied per route it turns a vague worry into a specific, arguable claim you can defend or later disprove with tooling.

  4. 4

    Map each finding to its real OWASP Top 10 2021 category

    5 guided steps

    STRIDE tells you what kind of threat it is. The OWASP name ties it to an industry-standard vocabulary that a reviewer, an auditor, or the tooling you add in later rungs (Semgrep, ZAP) will also speak.

  5. 5

    Write threat-model.md as a living document

    6 guided steps

    A threat model that lives in your head, or gets written once and never touched again, is worthless by rung 4. This document is the one artifact that survives across all 12 rungs, every later rung edits it instead of starting fresh.

What's inside when you start

4 starter files, ready to clone
5 guided milestones
5 full reference solutions
9 code blocks explained line-by-line
5 "is it working?" checks
6 interview questions it prepares you for

You'll walk away with

A running local Ledgerline instance under docker compose with two independently created test users
security/attack-surface.md, a route-by-route inventory noting auth, inputs, and raw SQL usage
A reproduced, documented IDOR (Alice's token reading Bob's balance) with the exact curl command that proves it
security/threat-model.md with assets, trust boundaries, a STRIDE table, and an OWASP Top 10 2021 mapping
A ranked top-risks list with each risk labeled confirmed or unconfirmed
The threat model committed to git as the baseline every later rung will edit

This is portfolio-grade. Build it free.

Sign up to unlock every milestone step-by-step, the code skeletons, full reference solutions, and checkable tasks, with your progress saved as you build.

Start building