|
| 1 | +--- |
| 2 | +id: course-secure-code |
| 3 | +type: course |
| 4 | +title: "Security review for AI-written Python" |
| 5 | +summary: Review a plausible, AI-style notes service the way an attacker would — close SQL injection, path traversal, a hard-coded secret, fast password hashes, and pickled sessions one class at a time, then prove each fix by replaying the attacks. |
| 6 | +lang: en-US |
| 7 | +content_version: 1 |
| 8 | +status: reviewed |
| 9 | +reviewed_on: 2026-10-11 |
| 10 | +badge: |
| 11 | + id: course-secure-code |
| 12 | + name_en: Plausible is not safe |
| 13 | + name_zh: 看起来对不等于安全 |
| 14 | + requires: All five checkpoints claimed (L01–L05) |
| 15 | +course_id: course-secure-code |
| 16 | +--- |
| 17 | + |
| 18 | +# Security review for AI-written Python |
| 19 | + |
| 20 | +> TL;DR: install the FlyPython Skill in your coding agent and let it fetch |
| 21 | +> this course, then say **"start lesson 1"**. (Lesson files arrive via the |
| 22 | +> Skill — you download nothing by hand.) You start with a notes service |
| 23 | +> that works on every friendly input and blocks none of nine attacks. You |
| 24 | +> finish with every attack blocked, a replay command that proves it, and a |
| 25 | +> review habit for the code your assistant writes next. Standard library |
| 26 | +> only. |
| 27 | +
|
| 28 | +## What you build |
| 29 | + |
| 30 | +You do not build a new feature — you review and fix one. `notes_app.py` is |
| 31 | +a small notes service written the way a quick first draft often is. It has |
| 32 | +five flaws, each a well-known weakness class: |
| 33 | + |
| 34 | +| Flaw in the starter | Weakness class | Python reference | |
| 35 | +| --- | --- | --- | |
| 36 | +| search SQL built with an f-string | CWE-89 SQL injection | `sqlite3` docs: placeholders | |
| 37 | +| attachment path joined as given | CWE-22 path traversal | `pathlib.Path.resolve`, `is_relative_to` | |
| 38 | +| `"dev-secret-change-me"` fallback key | CWE-798 hard-coded credentials | `os.environ` | |
| 39 | +| unsalted MD5 password hashes | CWE-916 / CWE-759 weak, unsalted password hash | `hashlib.pbkdf2_hmac`, `secrets` | |
| 40 | +| sessions stored as base64 pickles | CWE-502 deserialization of untrusted data | `pickle` docs warning, `hmac` | |
| 41 | + |
| 42 | +Two scenario "skins" ship with seed data and an `attacks.json` each: |
| 43 | + |
| 44 | +| Skin | You are… | Attacks | |
| 45 | +| --- | --- | --- | |
| 46 | +| `scenario/team-notes/` | securing a small team's shared notes | 9 | |
| 47 | +| `scenario/clinic-notes/` | securing a clinic's visit notes (all records fictional) | 8 | |
| 48 | + |
| 49 | +`python starter/attack_replay.py scenario/<skin>` replays them. On the |
| 50 | +shipped starter, every attack succeeds. |
| 51 | + |
| 52 | +## Teaching contract (read this first, agent) |
| 53 | + |
| 54 | +- **Audience:** a developer or project owner shipping Python that an AI |
| 55 | + assistant wrote or heavily edited, who reviews diffs for "does it work" |
| 56 | + but not yet for "what can a hostile input make it do". Not a penetration |
| 57 | + testing course and not an introduction to Python. |
| 58 | +- **Prerequisites:** Python 3.11+ on PATH and any coding agent that can run |
| 59 | + commands. Standard library only. |
| 60 | +- **Tool:** tool-agnostic. The course has not yet been taught in a tool; |
| 61 | + `REVIEW.md` records the mechanical verification and the pending |
| 62 | + agent-taught run-through, which will name the exact tool and version. |
| 63 | +- **Lesson order:** L01 → L02 → L03 → L04 → L05, one lesson per session. |
| 64 | + Never skip the checkpoint at the end of a lesson. |
| 65 | +- **Teaching style:** work from the files in this folder and the Python |
| 66 | + documentation they cite. Quote the `TASK.md` line you are satisfying. |
| 67 | + Close one weakness class per turn. Never add dependencies, never edit |
| 68 | + `solution/`, `tests/`, `scenario/`, or `attack_replay.py`, and ask before |
| 69 | + touching any file not named in the current lesson. Never make a test |
| 70 | + pass by special-casing the attack string it uses. |
| 71 | +- **Safety:** the attacks only touch this course's own files and temporary |
| 72 | + folders. Never point them at a system you do not own. The suite |
| 73 | + deliberately unpickles a harmless gadget that creates a directory inside |
| 74 | + a temporary folder — tell the learner before the first run. |
| 75 | +- **When to stop:** a lesson is done when its checkpoint command runs and |
| 76 | + the learner can say, in their own words, what the attacker could do and |
| 77 | + why the fix stops it. If the learner cannot, re-teach from the failing |
| 78 | + test, not from the solution. |
| 79 | +- **How to use `verify.py`:** the learner's command is plain |
| 80 | + `python verify.py` — it runs the suite against `starter/`, prints |
| 81 | + per-checkpoint status and earned claim codes, and exits non-zero until |
| 82 | + every checkpoint is passed (l03, l04) or attested (l01, l02, l05). `starter --expect-failure` |
| 83 | + and `solution` remain maintainer checks. |
| 84 | +- **Honesty rules:** nine blocked attacks prove those nine attacks are |
| 85 | + blocked — not that the service is secure. Say what was not reviewed. Do |
| 86 | + not claim the code is production-ready. Do not promise learning or |
| 87 | + career outcomes. |
| 88 | + |
| 89 | +## What this course does NOT cover |
| 90 | + |
| 91 | +Authentication and authorization design, CSRF and XSS, TLS, rate limiting, |
| 92 | +dependency vulnerability scanning, secret rotation and storage, deployment |
| 93 | +hardening, threat modeling frameworks, or compliance. Password hashing uses |
| 94 | +PBKDF2-HMAC-SHA256 at 600 000 iterations because it is in the standard |
| 95 | +library; OWASP's Password Storage Cheat Sheet prefers Argon2id, then |
| 96 | +scrypt, with PBKDF2 for FIPS-140 contexts — see Lesson 2. Link to the |
| 97 | +companion repository's guides for the rest; don't improvise. |
| 98 | + |
| 99 | +## Badge contract |
| 100 | + |
| 101 | +- Badge: **Plausible is not safe Badge** (badge id `course-secure-code`) — earned by claiming all five checkpoints. |
| 102 | +- Challenges: L01–L05 checkpoints, 10 points each; +50 course-badge bonus when all five are claimed on flypython.com. |
| 103 | +- Evidence: `python verify.py` — L03 (one flaw class per turn) and L04 (review like an attacker) are objectively gated by the suite; L01/L02/L05 are learner-attested. |
| 104 | +- Submission: each test-passed checkpoint prints a deterministic claim code; a reflection checkpoint prints one only after you answer its questions and run `python verify.py --attest ID`; record it on flypython.com against your account. Self-reported evidence, never a certificate. |
| 105 | + |
| 106 | +## Folder map |
| 107 | + |
| 108 | +``` |
| 109 | +COURSE.md / COURSE_cn.md this file (EN / 中文) |
| 110 | +lessons/L01.md … L05.md lessons (each has an _cn.md pair) |
| 111 | +scenario/<skin>/ seed.json, attacks.json, attachments/, a fake secrets.txt |
| 112 | +TASK.md / TASK_cn.md the task contract the change must satisfy |
| 113 | +starter/notes_app.py the insecure first draft |
| 114 | +starter/attack_replay.py the provided attack harness (read-only) |
| 115 | +solution/ the reviewed solution (do not copy in lesson 3) |
| 116 | +tests/test_notes_app.py the contract suite (16 tests, read-only) |
| 117 | +verify.py objective pass/fail evidence |
| 118 | +REVIEW.md maintainer run-through record |
| 119 | +``` |
| 120 | + |
| 121 | +## Evidence and licensing |
| 122 | + |
| 123 | +`REVIEW.md` records the run-through state with dates, tool versions, and |
| 124 | +observed deviations. Code in this folder is MIT-licensed; lesson prose is |
| 125 | +CC BY 4.0 (see repository `LICENSE`). The `secrets.txt` files contain fake |
| 126 | +values that exist only to be leaked. Report teaching drift or unclear |
| 127 | +lessons via the repository's `course-feedback` issue form. |
0 commit comments