Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 99 additions & 0 deletions examples/cecs-378-demo/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ Files in this directory:
| `gradescope-stats/` | Synthetic Gradescope evaluations + `reg-gradescope-stats` item analysis |
| `exam1/products/` | Per-exam reading-list study guide from `reg-exam-readinglist` |
| `gradebook-out/` | `gradebook.csv` + `gradebook.md` from `reg-gradebook import` |
| `lab-demo.recon.yaml` | `reg-lab-recon` manifest for the demo buffer-overflow lab |
| `lab-demo.rubric.yaml` | Writeup rubric consumed by `reg-lab-digest` |
| `lab-demo.report.yaml` | `reg-lab-report` render + deliver manifest |
| `recon-lab-demo/` | Pre-built **recon bundle** (10 synthetic repos) + digest/report outputs — Stage G |

---

Expand Down Expand Up @@ -446,6 +450,91 @@ counts, and flags **dead** distractors (chosen by nobody) and possible **miskeys

---

## Stage G — Grade a lab (recon → digest → report → feedback)

The lab-grading pipeline is separate from the exam flow: **`reg-lab-recon`** sweeps a
lab's student repos into a *recon bundle*, **`reg-lab-digest`** grades the writeups, and
**`reg-lab-report`** renders the instructor report and delivers signed feedback.

`reg-lab-recon` needs cloned student repos (GitHub) — see the live-infra note below. This
directory ships the bundle it would produce at **`recon-lab-demo/`** (10 synthetic
students; a buffer-overflow lab with autograded **Phase Φ / Phase Ω** + a 30-pt writeup),
so every command below runs offline and for real.

Bundle contents (`recon-lab-demo/`):

| File | Purpose |
|------|---------|
| `cohort.csv` | Part-A facts per repo (autograde `points`, `cleared` phases, triage, `doc_present`) |
| `FACTS.md` | Audit-grade facts table |
| `repos/<id>.json` | Per-repo metadata (autograde challenges, git story, links) |
| `writeups/<id>.md` | Each student's extracted `WRITEUP.md` |

### 15. Digest — grade the writeups

Emit a work-list, let the harness grade it, then merge the results:

```sh
reg-lab-digest emit --bundle recon-lab-demo --rubric lab-demo.rubric.yaml \
--out recon-lab-demo/digest_tasks.jsonl
```

```
→ digest: 9 task(s) to grade # 8 submitters + 1 not-yet-graded; the no-submission is skipped
```

`digest_tasks.jsonl` + `digest.schema.json` are the contract an **agent grader** consumes
(see [`docs/lab-digest-grader-prompt.md`](../../docs/lab-digest-grader-prompt.md)); it
returns one result line per student. A sample result set ships at
`recon-lab-demo/digest_results.jsonl` — **Jason Todd is left ungraded on purpose**, to
exercise the report's placeholder path. Merge it:

```sh
reg-lab-digest merge --bundle recon-lab-demo --rubric lab-demo.rubric.yaml \
--results recon-lab-demo/digest_results.jsonl
```

```
→ digest: merged 8 scored, 0 withheld -> recon-lab-demo/cohort.csv
```

`merge` never overwrites autograde truth — it only adds `writeup_score` / comments and
recomputes the total (capped, partial-ward-zeroed).

### 16. Report — render the instructor report

```sh
reg-lab-report render --bundle recon-lab-demo --cohort recon-lab-demo/cohort.csv \
--manifest lab-demo.report.yaml --out recon-lab-demo/REPORT.md
```

`REPORT.md` is a complete *report + feedback* document:

- **Distribution** — grade bands, score histogram, and a **ward-clear funnel** (Φ 8 / Ω 5),
counted from the `cleared` column recon emits.
- **Grade table** + four-bucket **recommendations** + a **Canvas entry sheet**.
- **Per-student feedback & grades** — a grading *scaffold*: graded rows carry the verbatim
student-facing comment (with a stripped `<!-- internal -->` note); an **ungraded
submission** (Jason Todd) renders a `> _Comments:_` placeholder for the LLM-grading
hand-off; a **non-submission** (Selina Kyle) renders `> _no submission_`.

### 17. Feedback — deliver (dry-run)

```sh
reg-lab-report deliver --cohort recon-lab-demo/cohort.csv --manifest lab-demo.report.yaml \
--skip selina-kyle --log-out recon-lab-demo/FEEDBACK_LOG.md
```

```
→ DRY-RUN: 9 repos
```

Dry-run writes a verbatim `FEEDBACK_LOG.md` and touches nothing. Adding `--execute` signs
each student's `FEEDBACK.md`, pushes it to the repo's `feedback` branch, and merges to
`main` — that needs the real repos + a signing key (live-infra note below).

---

## Commands requiring live infrastructure

These lectern commands are part of the workflow but need external systems, so they
Expand All @@ -461,6 +550,12 @@ are documented here rather than run in this self-contained demo:
- **`reg-triage`** — git-history authenticity triage over a lab's student-repo
population: `sweep` → FLAG/REVIEW/PASS, `report` → two-tier audit (needs cloned
student repos / an org to scrape).
- **`reg-lab-recon`** — sweep a lab's student repos into the recon bundle that
Stage G consumes (needs cloned student repos / a Classroom org). The pre-built
`recon-lab-demo/` is what it produces.
- **`reg-lab-report deliver --execute`** — sign each student's `FEEDBACK.md`, push it
to the `feedback` branch, and merge to `main` (needs the real repos + a GPG signing
key). The `--dry-run` form in Stage G runs offline.
- **`reg-term-finalize`** — reconcile grade distributions, flip section statuses to
finalized, roll up enrollment-weighted aggregates (needs the populated vault term tree).

Expand Down Expand Up @@ -500,6 +595,10 @@ for this example — they do not reproduce any live exam bank.
- `exam1.tex`, `cecs-378-question-bank.md`, the syllabus, and the
`gradescope-stats/` evaluations all contain fresh synthetic content written for
this demo; none reproduce any live or past CECS 378 material, grades, or roster.
- The `recon-lab-demo/` bundle (Stage G) is entirely synthetic: a fictional
`Gotham-CECS` org, fabricated buffer-overflow writeups with fake `0xffff`-range
addresses and `CECS378{…}` flags, and invented autograde/commit metadata. No real
lab source, exploit, repo, or student appears in it.
- The `@student.csulb.edu` emails in the synthetic Gradescope export are fabricated
for the demo students and resolve to no real accounts.
- No internal infrastructure hostnames, real email addresses, real course sections,
Expand Down
23 changes: 23 additions & 0 deletions examples/cecs-378-demo/lab-demo.recon.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# reg-lab-recon manifest — CECS 378 demo buffer-overflow lab.
# recon itself needs cloned student repos (GitHub) → see the live-infra note in
# the README; this directory ships the pre-built bundle recon-lab-demo/ that recon
# would produce, so the digest + report stages below run offline and for real.
assignment:
course: CECS 378
section: "01"
term: su26
name: "Lab 3 — Buffer Overflow (demo)"
org: Gotham-CECS
repo_prefix: "cecs-378-su26-01-lab-03-buffer-overflow-"
total_points: 100
autograde:
workflow: autograde.yml
branch: main
result_artifact: grading-result
result_path: grading/result.json
docs:
- { file: student/WRITEUP.md, label: writeup, summarize: true, points: 30 }
git:
profile: short-project
report:
triage: surface
17 changes: 17 additions & 0 deletions examples/cecs-378-demo/lab-demo.report.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# reg-lab-report render + deliver manifest — demo buffer-overflow lab.
course: CECS 378
section: "01"
term: su26
lab: "Lab 3 — Buffer Overflow (demo)"
org: Gotham-CECS
repo_prefix: cecs-378-su26-01-lab-03-buffer-overflow
auto_max: 70
writeup_max: 30
wards:
- { key: phi, label: "Phase Φ ACE (WALK IN)" }
- { key: omega, label: "Phase Ω ACE (COLD STEEL)" }
letter_cuts: { A: 90, B: 80, C: 70, D: 60 }
bump_band: 1.0
feedback_branch: feedback
feedback_pr: 1
default_branch: main
125 changes: 125 additions & 0 deletions examples/cecs-378-demo/lab-demo.rubric.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
# Buffer Overflow (Lab 3) — machine-readable writeup rubric (30 pts)
# Human-readable source: classes/378-478/labs/378-buffer_overflow/buffer_overflow_lab_grading_rubric.md
# Anchor philosophy: mechanism-over-outcome — "the leak gave me X, which let me do Y" earns full;
# "I ran the exploit" earns near-zero. Full/Solid/Weak/Absent → strong/adequate/weak/missing.
# NOTE: §5 craft (2) was added 2026-07-02 to reconcile the writeup to its stated 30-pt total
# (the section maxes previously summed to 28). Writeup sections carry NO requires_cleared:
# per the rubric, the writeup is graded on its merits independent of the autograded phases.

lab: "Lab 3 — Buffer Overflow (Phase Φ WALK IN / Phase Ω COLD STEEL)"
total: 30
comment_max_chars: 140
student_comment_max_chars: 600
cap: 30

sections:
- key: target
label: "§0 TARGET — environment + seed fingerprint"
max: 2
anchors:
strong: >-
States where the work was done (Codespace or local) and the dates, and
pastes the `make print-seed` line, which matches the `seed-fingerprint:`
in the WRITEUP frontmatter.
adequate: >-
Filled in but missing one element — dates absent, or the fingerprint is
pasted without the contextual env/date line.
weak: >-
Fingerprint or environment stated but it does not appear to match the
frontmatter (flag for instructor).
missing: >-
Empty or placeholder text unchanged.

- key: recon
label: "§1 RECON — gdb output + offset derivation"
max: 6
anchors:
strong: >-
Both `info frame` and `x/40wx $esp` pasted from their OWN vuln1 and vuln2
(addresses non-trivially seed-specific), and the offset derivation is
shown — not merely "the offset was 76".
adequate: >-
gdb output present and offset reasoning sketched, with small gaps (e.g.
shows the offset for vuln1 but not vuln2, or the arithmetic is implicit).
weak: >-
One of: gdb output but no offset reasoning; offset claimed with no gdb
output; output present but identical to another student's addresses
(seed collision impossible → flag for instructor).
missing: >-
No gdb output at all.

- key: phi_walk
label: "§2 Phase Φ walkthrough (exploit1.c)"
max: 6
anchors:
strong: >-
Answers all four sub-questions — why those shellcode bytes, why the NOP
sled is sized as it is, where the offset came from, and the exact line
that reads the leaked `&buffer` from vuln1's stdout and how it is used in
the payload — tied to specific lines of THEIR exploit1.c.
adequate: >-
Three of four sub-questions, or all four but shallow on one (e.g. "I used
standard execve shellcode" with no reason for those bytes).
weak: >-
One or two sub-questions; mostly restates the prompt without engaging
with their own code.
missing: >-
No walkthrough.

- key: omega_walk
label: "§3 Phase Ω walkthrough (4 sub-parts: fail-analysis, libc offsets, setuid, gadget)"
max: 9
anchors:
strong: >-
All four sub-parts full: (3a) pastes the segfault/signal AND explains the
non-executable stack (no `-z execstack`); (3b) names the tools and shows
the actual offsets for system/exit/setreuid/"/bin/sh"; (3c) correctly
explains why the naive shell drops the setuid `oracle` privilege and names
the `setreuid(geteuid(),geteuid())` fix with a man-page cite; (3d) names
the gadget-finding tool, gives the pop/pop/ret address + disassembly of
the three instructions, and why it is needed.
adequate: >-
Three of the four sub-parts solid, or all four present but thin on the
"why" (e.g. offsets shown but commands omitted; gadget address without
disassembly).
weak: >-
One or two sub-parts; mechanism vague, a wrong fix ("you need root"), or
prompt restated.
missing: >-
Section empty.

- key: aftermath
label: "§4 AFTERMATH — four reflections (1.25 each)"
max: 5
anchors:
strong: >-
All four reflections substantive: a real opinion on which phase was
harder; a no-leak counterfactual naming a concrete primitive (format
string / side channel / low-entropy brute force / partial overwrite /
"stuck without a leak"); the canary answer names `__stack_chk_fail` /
abort-before-RIP; and a SPECIFIC cited section (Phrack 49:14 / GNU Coding
Standards / TAOUP / POSIX) with a non-platitude "why it matters".
adequate: >-
Three substantive and one thin, or two substantive and two thin.
weak: >-
One substantive reflection; the rest vague or "both were hard".
missing: >-
Section empty.

- key: craft
label: "§5 craft — commented exploit code + required screenshots"
max: 2
anchors:
strong: >-
exploit1.c and exploit2.c carry student-written explanatory comments (not
just code), all four screenshots are present (phase-phi-shell/flag,
phase-omega-shell/flag), and the prose is in the student's own voice.
adequate: >-
Code commented but sparse OR one-to-two screenshots missing — evidence of
craft but incomplete.
weak: >-
Bare uncommented exploits and/or most screenshots missing.
missing: >-
No comments and no screenshots.

bonus: []
18 changes: 18 additions & 0 deletions examples/cecs-378-demo/recon-lab-demo/.cohort-spec.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"lab": "Lab 3 — Buffer Overflow (demo)",
"org": "Gotham-CECS",
"repo_prefix": "cecs-378-su26-01-lab-03-buffer-overflow-",
"auto_max": 70, "writeup_max": 30,
"students": [
{"gid":"bruce-wayne","name":"Bruce Wayne","sid":"040100101","phi":true,"omega":true,"commits":18,"spread":29.4,"triage":"PASS","triage_score":88,"sources":2,"submitted":true,"graded":true,"quality":"strong","wr":{"target":2,"recon":6,"phi_walk":6,"omega_walk":9,"aftermath":5,"craft":2}},
{"gid":"barbara-gordon","name":"Barbara Gordon","sid":"040100103","phi":true,"omega":true,"commits":22,"spread":30.1,"triage":"PASS","triage_score":86,"sources":3,"submitted":true,"graded":true,"quality":"strong","wr":{"target":2,"recon":6,"phi_walk":6,"omega_walk":8,"aftermath":5,"craft":2}},
{"gid":"dick-grayson","name":"Dick Grayson","sid":"040100102","phi":true,"omega":true,"commits":14,"spread":28.7,"triage":"PASS","triage_score":80,"sources":1,"submitted":true,"graded":true,"quality":"good","wr":{"target":2,"recon":5,"phi_walk":6,"omega_walk":7,"aftermath":5,"craft":2}},
{"gid":"kate-kane","name":"Kate Kane","sid":"040100109","phi":true,"omega":true,"commits":11,"spread":30.6,"triage":"PASS","triage_score":78,"sources":1,"submitted":true,"graded":true,"quality":"good","wr":{"target":2,"recon":5,"phi_walk":5,"omega_walk":7,"aftermath":5,"craft":2}},
{"gid":"harvey-dent","name":"Harvey Dent","sid":"040100106","phi":true,"omega":false,"commits":9,"spread":30.5,"triage":"PASS","triage_score":72,"sources":0,"submitted":true,"graded":true,"quality":"partial","wr":{"target":2,"recon":5,"phi_walk":5,"omega_walk":0,"aftermath":4,"craft":2}},
{"gid":"pamela-isley","name":"Pamela Isley","sid":"040100107","phi":true,"omega":false,"commits":6,"spread":29.9,"triage":"REVIEW","triage_score":55,"sources":0,"submitted":true,"graded":true,"quality":"partial","wr":{"target":2,"recon":4,"phi_walk":5,"omega_walk":0,"aftermath":4,"craft":2}},
{"gid":"oswald-cobblepot","name":"Oswald Cobblepot","sid":"040100105","phi":true,"omega":false,"commits":7,"spread":30.2,"triage":"PASS","triage_score":68,"sources":0,"submitted":true,"graded":true,"quality":"partial","wr":{"target":2,"recon":4,"phi_walk":4,"omega_walk":0,"aftermath":3,"craft":2}},
{"gid":"edward-nashton","name":"Edward Nashton","sid":"040100108","phi":false,"omega":false,"commits":12,"spread":30.0,"triage":"PASS","triage_score":60,"sources":1,"submitted":true,"graded":true,"quality":"phase-fail","wr":{"target":2,"recon":3,"phi_walk":4,"omega_walk":1,"aftermath":2,"craft":0}},
{"gid":"jason-todd","name":"Jason Todd","sid":"040100110","phi":true,"omega":true,"commits":10,"spread":30.3,"triage":"PASS","triage_score":75,"sources":1,"submitted":true,"graded":false,"quality":"good","wr":null},
{"gid":"selina-kyle","name":"Selina Kyle","sid":"040100104","phi":false,"omega":false,"commits":0,"spread":0,"triage":"","triage_score":0,"sources":0,"submitted":false,"graded":false,"quality":"none","wr":null}
]
}
18 changes: 18 additions & 0 deletions examples/cecs-378-demo/recon-lab-demo/FACTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Lab 3 — Buffer Overflow (demo) — Recon Facts (Part A)

Population: **10** repos · max **100** pts

> Verified record. Each row is reproducible from the repo + commit. *(synthetic demo bundle)*

| Student | Auto pts | Honor | Commits | Spread (d) | Triage | Doc | Feedback |
| --- | --: | :-: | --: | --: | :-: | :-: | :-: |
| selina-kyle | — | ✗ | — | — | — | ✗ | [PR](https://github.com/Gotham-CECS/cecs-378-su26-01-lab-03-buffer-overflow-selina-kyle/pull/1) |
| edward-nashton | 0 | ✓ | 12 | 30.0 | PASS | ✓ | [PR](https://github.com/Gotham-CECS/cecs-378-su26-01-lab-03-buffer-overflow-edward-nashton/pull/1) |
| harvey-dent | 60 | ✓ | 9 | 30.5 | PASS | ✓ | [PR](https://github.com/Gotham-CECS/cecs-378-su26-01-lab-03-buffer-overflow-harvey-dent/pull/1) |
| pamela-isley | 60 | ✓ | 6 | 29.9 | REVIEW | ✓ | [PR](https://github.com/Gotham-CECS/cecs-378-su26-01-lab-03-buffer-overflow-pamela-isley/pull/1) |
| oswald-cobblepot | 60 | ✓ | 7 | 30.2 | PASS | ✓ | [PR](https://github.com/Gotham-CECS/cecs-378-su26-01-lab-03-buffer-overflow-oswald-cobblepot/pull/1) |
| bruce-wayne | 70 | ✓ | 18 | 29.4 | PASS | ✓ | [PR](https://github.com/Gotham-CECS/cecs-378-su26-01-lab-03-buffer-overflow-bruce-wayne/pull/1) |
| barbara-gordon | 70 | ✓ | 22 | 30.1 | PASS | ✓ | [PR](https://github.com/Gotham-CECS/cecs-378-su26-01-lab-03-buffer-overflow-barbara-gordon/pull/1) |
| dick-grayson | 70 | ✓ | 14 | 28.7 | PASS | ✓ | [PR](https://github.com/Gotham-CECS/cecs-378-su26-01-lab-03-buffer-overflow-dick-grayson/pull/1) |
| kate-kane | 70 | ✓ | 11 | 30.6 | PASS | ✓ | [PR](https://github.com/Gotham-CECS/cecs-378-su26-01-lab-03-buffer-overflow-kate-kane/pull/1) |
| jason-todd | 70 | ✓ | 10 | 30.3 | PASS | ✓ | [PR](https://github.com/Gotham-CECS/cecs-378-su26-01-lab-03-buffer-overflow-jason-todd/pull/1) |
Loading
Loading