| id | course-secure-code-l04 | |||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| type | course | |||||||||||||||
| title | Lesson 4: Review like an attacker | |||||||||||||||
| summary | Green tests and a 9/9 replay are the floor. Replay the second skin, run a static scanner and note what it misses, attack one thing the suite never tried, and write down what is still unreviewed. | |||||||||||||||
| lang | en-US | |||||||||||||||
| content_version | 1 | |||||||||||||||
| status | reviewed | |||||||||||||||
| reviewed_on | 2026-10-11 | |||||||||||||||
| hints |
|
Both suites pass, both skins replay fully blocked, and you hold a written record of what was reviewed, what a scanner did and did not catch, and what nobody has reviewed yet.
blocked=9/9 is easy to over-read. It means nine specific attacks are
blocked. A real attacker tries a tenth. Security review is the habit of
asking what the tests did not try — and writing the answer down instead of
assuming it.
Run the maintainer pair and both replays yourself:
python verify.py starter
python verify.py solution
python starter/attack_replay.py scenario/team-notes
python starter/attack_replay.py scenario/clinic-notesThe suite only replays team-notes. clinic-notes uses different
payloads — x' OR 1=1 --, an injected owner instead of an injected
query, scans/../../secrets.txt, an empty key. If your fixes are
mechanisms, it prints blocked=8/8 without any change. If it does not,
one of your fixes was a filter.
Then do the review the suite cannot do:
-
Run a static scanner, then read what it missed. If you have Ruff (the repository's development dependencies include it), run its flake8-bandit rules on your fix:
ruff check --isolated --select S starter/notes_app.py
Your fix should report nothing. For comparison, on the original starter Ruff 0.16.10 reports S608 (SQL built from strings), S324 (MD5, twice), and S301 (
pickle.loads) — and nothing for the path join or the"dev-secret-change-me"fallback. If you committed a baseline, run the same command on it to see this yourself. A scanner is a fast first pass that catches known patterns; it is not a review. -
Attack one thing the suite never tried. Pick one: a search query with Unicode case (
Évsé), an attachment name that is a directory, a session token with a valid signature over a JSON list, a password that is 10 000 characters long. Predict, run, and write down the result. -
Read the diff for what it removed.
git diffthe starter against your baseline. Confirmpickleandmd5are gone and that noexcept Exceptionswallows aSecurityError.
Write the security evidence record for this change:
- Verified: the commands you ran, both replays, the scanner output.
- Not verified: at least — authentication and authorization as a whole,
how the session token is transported (cookie flags, TLS), rate limiting
on login, key storage and rotation, dependency vulnerabilities, and
every attack not in
attacks.json. - Known limits: anything you found in step 2 that the contract never promised, and the password-hash choice (PBKDF2 rather than Argon2id).
Run python verify.py. This checkpoint's code prints once the starter
suite passes; the maintainer pair above must both be green for the gate to
mean what it says. You pass the lesson when you can answer:
- Did
clinic-notespass without changes? If not, which fix was a filter? - Which two flaw classes did the scanner miss on the original starter, and why would a pattern-based tool miss them?
- What did your extra attack do, and is it on your "not verified" list?
Both green runs, both replay outputs, the scanner output, and your three-line evidence record.