Onboarding + make M15 gate catch the plant + M17 override (#6,#17,#18,#19,#29) (#58)

Co-authored-by: claude <claude@jpaul.io>
Co-committed-by: claude <claude@jpaul.io>
This commit was merged in pull request #58.
This commit is contained in:
2026-06-22 15:48:40 -04:00
committed by Claude (agent)
parent 06b9f8f308
commit a6a3cfdc50
5 changed files with 112 additions and 20 deletions
+15 -4
View File
@@ -1,15 +1,18 @@
"""Cloud-sync config for tasks-app — a realistic snapshot of what an AI hands you.
Asked to "sync tasks to a cloud service," a model will cheerfully produce something like this: it
works, it reads naturally, it passes lint and tests... and it has a live credential baked straight
into the source. That is the *exact* failure mode Module 15's secret-scanning gate exists to catch.
works, it reads naturally, it passes lint and tests... and it carries two planted flaws — a live
credential baked straight into the source (caught by Gate 2, secret scanning) and a weak-crypto
"signature" using MD5 (caught by Gate 3, SAST). Two different gates, two different blind spots.
DO NOT copy this pattern. The point of this file is to be caught by a scanner, not imitated.
DO NOT copy these patterns. The point of this file is to be caught by a scanner, not imitated.
The fix (read from the environment) is shown at the bottom, commented out, so you can see the
difference once Part C of the lab is done.
"""
# --- The problem the scanner should flag -------------------------------------------------------
import hashlib
# --- The problem the SECRET scanner should flag (Gate 2) ---------------------------------------
# A hardcoded API key. Looks like a normal string literal; lint and tests will never complain.
SYNC_API_KEY = "sk_live_9c3f2a7b41d84e0fa6b2c5d8e1f09a73bdac46"
SYNC_ENDPOINT = "https://api.example-task-cloud.com/v1/sync"
@@ -19,6 +22,14 @@ def sync_headers() -> dict:
return {"Authorization": f"Bearer {SYNC_API_KEY}"}
# --- The problem the SAST scanner should flag (Gate 3) -----------------------------------------
# AI-classic: "sign" the request body with a quick hash. MD5 is broken for anything
# security-relevant — a textbook weak-crypto idiom. A secret scanner won't catch this (it's not a
# secret); a SAST tool like bandit will (it's insecure code you wrote). DO NOT imitate.
def sign_payload(body: str) -> str:
return hashlib.md5(body.encode()).hexdigest()
# --- The fix (Part C) --------------------------------------------------------------------------
# Read the secret from the environment instead of committing it. Proper secret management — env
# files, secret stores, per-environment config — is Module 17. This is just enough to make the
@@ -14,6 +14,13 @@
set -u # treat unset vars as errors; we manage exit codes explicitly below.
# A security gate must fail CLOSED. If the interpreter the secret gate needs isn't here, abort with a
# non-zero exit rather than sailing past the check and reporting a false "passed".
command -v python3 >/dev/null 2>&1 || {
echo ">> python3 is required for the secret gate but was not found. Aborting." >&2
exit 2
}
status=0
echo "=== Gate 1: SCA / dependency scan (pip-audit) ==="
@@ -28,16 +35,33 @@ fi
echo
echo "=== Gate 2: secret scan (detect-secrets) ==="
# detect-secrets prints a JSON report of any secrets it finds. We treat a non-empty results set as a
# failure. `python -c` keeps this portable (no jq dependency).
# detect-secrets prints a JSON report of any secrets it finds. NOTE: with no path it scans the files
# git TRACKS, so stage the starter files (`git add`) before running this, or an untracked file is
# invisible to the gate. We parse the JSON with `python3` (no jq dependency) and fail CLOSED: the
# parser returns 0=secrets found, 1=clean, anything else=couldn't tell — and "couldn't tell" must
# count as a failure, never a silent pass.
report="$(detect-secrets scan)"
if printf '%s' "$report" | python -c 'import sys, json; sys.exit(0 if json.load(sys.stdin).get("results") else 1)'; then
echo "$report"
echo ">> SECRET gate FAILED: a credential was detected in the tree. See report above." >&2
status=1
else
echo "no secrets detected."
fi
printf '%s' "$report" | python3 -c 'import sys, json
try:
found = bool(json.load(sys.stdin).get("results"))
except Exception:
sys.exit(2)
sys.exit(0 if found else 1)'
secret_rc=$?
case "$secret_rc" in
0)
echo "$report"
echo ">> SECRET gate FAILED: a credential was detected in the tree. See report above." >&2
status=1
;;
1)
echo "no secrets detected."
;;
*)
echo ">> SECRET gate ERROR: could not parse the scan output (exit $secret_rc). Failing closed." >&2
status=1
;;
esac
echo
if [ "$status" -ne 0 ]; then