fix(modules-1,15,17): onboarding step, make M15 gate actually catch the plant, M17 .env override
- M1: add a no-git "Get the course materials" step (download+unzip; clone noted as Module 8) so Part A's paths resolve without assuming git. URL flagged Verify-before-publish (swap to public host before publishing). - M15: security gate was failing OPEN on python3-only systems (bare `python`) and missing the UNTRACKED config.py, so the planted secret passed green. Now guards python3, fails CLOSED on any non-clean exit, and stages files so the planted SYNC_API_KEY + typosquat dep are actually caught. - M15: correct the false "Bandit flags the API key" claim (B105-107 need password-named ids); add an honest MD5 (B324) flaw so the SAST demo fires. Planted secret/deps preserved. - M17: require the .env loader to use setdefault so Part D's override demo works; explain precedence. Hardcoded "before" anti-pattern left intact. Closes #6 Closes #17 Closes #18 Closes #19 Closes #29 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TfzV5QvtPDz8LJS3Pu5VLT
This commit is contained in:
@@ -332,7 +332,9 @@ config per environment.
|
||||
> *"Refactor `sync.py` so it reads `TASKS_API_KEY` and `APP_ENV` from environment variables
|
||||
> instead of hardcoding them. Pick the backend URL from `APP_ENV` (dev/staging/prod). Fail loudly
|
||||
> with a clear message if `TASKS_API_KEY` is missing. Don't add any third-party dependency — load
|
||||
> the `.env` file with a few lines of plain Python."*
|
||||
> the `.env` file with a few lines of plain Python, and make sure the loader does **not**
|
||||
> overwrite a variable that's already set in the environment, so a value passed on the command
|
||||
> line still wins."*
|
||||
|
||||
You're looking for a result shaped like this (read the diff before you accept it):
|
||||
|
||||
@@ -372,6 +374,15 @@ config per environment.
|
||||
grep -n "sk-live" sync.py # should print nothing
|
||||
```
|
||||
|
||||
**Why `setdefault` and not plain assignment?** The loader uses `os.environ.setdefault(key, value)`,
|
||||
which sets a variable *only if it isn't already set*. That precedence is load-bearing: a value the
|
||||
environment already supplies — like an `APP_ENV` you pass on the command line — wins over the
|
||||
`.env` file. A loader that writes `os.environ[key] = value` instead **clobbers** anything already
|
||||
there, so the file silently overrides your command line and Part D's override demo does nothing.
|
||||
This matches the real-world dotenv default (`override=False`): the file fills in gaps, it doesn't
|
||||
stomp on what's already in the environment. If the AI hands you plain assignment, that's the
|
||||
correction to make.
|
||||
|
||||
### Part D — Run it from the environment
|
||||
|
||||
5. Run it reading from your `.env`:
|
||||
@@ -395,7 +406,9 @@ config per environment.
|
||||
```
|
||||
|
||||
Watch the backend URL change with `APP_ENV` while the source never does. That's config in the
|
||||
environment.
|
||||
environment. **If the URL *doesn't* change, your loader is clobbering variables that were already
|
||||
set** — it's using `os.environ[key] = value` where it needs `os.environ.setdefault(...)` (see
|
||||
Part C). Fix the loader so the command line wins, and the override takes effect.
|
||||
|
||||
### Part E — Commit, and verify the secret didn't tag along
|
||||
|
||||
|
||||
Reference in New Issue
Block a user