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
22 changes: 15 additions & 7 deletions docs/COURSE_SPEC.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ Model, version 1.2)** package with **LMS (Learning Management System)-reported s

- **Course license: CC BY-SA 4.0** (ShareAlike, required by the Agentic PDF).
- Attribution + "changes were made" + "not endorsed by OWASP" on the About page.
- **Provenance**: generated with the **pi coding-agent harness v0.79.8** using LLM (Large Language
Model) models **glm-5.2** and **kimi k2.7-coder**. Not endorsed by OWASP.
- **Provenance**: generated with the assistance of an AI coding agent. Not endorsed by OWASP.
- All prose is written in our own words as an adaptation; quiz questions are original.

## Conventions (apply on every page and in every quiz)
Expand All @@ -38,6 +37,8 @@ Model, version 1.2)** package with **LMS (Learning Management System)-reported s
- **Scoring**: a **Configure Scores** page sets project scoring; theme quizzes (25%) + final quiz
(75%) **all count** toward the LMS score. Final quiz pass mark **80%**.
- **SCORM 1.2** export → `cmi.core.score.raw`, `cmi.core.lesson_status`, `cmi.interactions.n`.
- **Option order**: every quiz question sets `answerOrder="random"` so the answer options
shuffle on each attempt (Nottingham per-question "Answer Order" property).
- **Question types used** (all selection-based, no free text): Multiple Choice (single),
Multiple Response (select all), True/False, Matching, Categorise.
- Editor opened directly at `/edit.php?template_id=<id>`; **Publish** before export.
Expand Down Expand Up @@ -74,7 +75,7 @@ Real-world incident callout (where Appendix D has one) · Secure-coding mitigati
- Attribution to both OWASP sources (name, author, source link, license link).
- "Changes were made: this course is an adapted summary/derivative."
- "Not endorsed by OWASP."
- Provenance: pi v0.79.8, models glm-5.2 and kimi k2.7-coder.
- Provenance: generated with the assistance of an AI coding agent.

### Page 3 — Introduction to secure code development [Content]
- What secure code development is; why it matters; cost of defects left vs right of the
Expand Down Expand Up @@ -128,7 +129,7 @@ Real-world incident callout (where Appendix D has one) · Secure-coding mitigati
*An authenticated user edits a URL parameter to view another user's account* (correct).
Distractors: an attacker sends a crafted SQL (Structured Query Language) string; a server uses
a deprecated hash; an app ships with default admin credentials.
- **Multiple response**: "Which are effective mitigations for authentication failures?" ✅ MFA
- **Multiple response**: "Which are effective mitigations against authentication attacks?" ✅ MFA
(Multi-Factor Authentication); ✅ hashed + salted passwords; ✅ lockout on repeated failures;
❌ plaintext password reminders by email; ❌ client-side authorization checks.
- **True/False**: "A delegated agent should reuse the delegating user's full permissions when
Expand Down Expand Up @@ -157,8 +158,9 @@ Real-world incident callout (where Appendix D has one) · Secure-coding mitigati
XPath (XML Path) injection, expression/language injection.
- Examples: SQLi via concatenated strings, command injection, expression injection.
- Mitigations: parameterised queries / prepared statements, allowlist input validation,
escaping/encoding, ORM (Object-Relational Mapping) safe APIs, least privilege on DB
(database) accounts.
escaping/encoding, parameterised ORM (Object-Relational Mapping) query methods (an ORM is
not inherently injection-proof — never concatenate raw input into its query strings), least
privilege on DB (database) accounts.

### Page 12 — Agent Goal Hijack (Agentic, ASI01) [Content]
- Overview: manipulating an agent's objectives, task selection, or decision pathways via prompt
Expand Down Expand Up @@ -560,14 +562,20 @@ All framed with full descriptive names (no item codes as the question subject).
Memory & Context Poisoning = Agentic; Cryptographic Failures = Classic; Rogue Agents = Agentic;
Security Misconfiguration = Classic; Cascading Failures = Agentic.

### Page 44 — Course complete [Content]
- A closing page so it is unambiguous that the training is over: congratulates the learner,
reminds them their result is recorded by the LMS (Learning Management System) and that the
80% pass mark / last-attempt rule applies, and tells them they may close the window. Plain
Text page type.

### [Scores] — Configure Scores
- Project scoring enabled; theme quizzes (25%) + final quiz (75%) all count; final pass mark
80%; per-question weighting set at build time.

---

## Totals
- 43 learner-visible pages + 1 Configure Scores page.
- 44 learner-visible pages + 1 Configure Scores page.
- 20 item pages (10 classic + 10 agentic), 7 theme intros, 7 theme quizzes, 1 final quiz,
4 front-matter + 5 back-matter pages.
- All quiz questions selection-based; auto-graded; LMS-reported via SCORM 1.2.
Expand Down
74 changes: 73 additions & 1 deletion docs/COURSE_VERIFICATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ Glossary/abbreviations, References & further reading — all present.
CC BY-SA 4.0 (Creative Commons Attribution-ShareAlike 4.0); both OWASP sources
attributed (Agentic 2026 / genai.owasp.org / CC BY-SA 4.0; Top 10:2025 /
owasp.org/Top10/2025/ / CC BY 3.0); "changes were made"; "not endorsed by OWASP";
provenance (pi v0.79.8, models glm-5.2 and kimi k2.7-coder).
provenance (generated with the assistance of an AI coding agent).

### Acronym expansions ✅ (sample all present)
IDOR (Insecure Direct Object Reference), RCE (Remote Code Execution),
Expand Down Expand Up @@ -206,3 +206,75 @@ empty options, 27 `delaySecs=0`, autosave script present). A Publish
round-trip preserved the `{{BUILD_VERSION}}` placeholder intact in
`data.xml` (CKEditor does not mangle the braces). See `PROJECT_CONTEXT.md` §
"Build version stamping" for full detail.

## Feedback pass: option randomisation + content fixes (post-0.0.6)

A round of review feedback was applied to `source/data.xml` +
`source/preview.xml` (kept byte-identical). All changes are content-level and
use engine-supported attributes, so they survive the release-workflow XOT
import/export and the editor round-trip.

1. **Option order randomised per attempt** ✅ — every `<question>` now carries
`answerOrder="random"` (45/45). Nottingham's HTML5 quiz model
(`models_html5/quiz.html` ~line 165) shuffles each question's options on
quiz load/restart; scoring is computed from the shuffled array, so SCORM
tracking is unaffected. This removes the previous bias where the correct
answer was frequently the first option (most visible in the final quiz).
`answerOrder` is a declared wizard property (`quiz.xwd` "Answer Order"),
so the editor round-trips it natively (like `trackingWeight`).
2. **Theme 1 Quiz Q2 wording** ✅ — "Which are effective mitigations for
authentication failures?" → "Which are effective mitigations against
authentication attacks?" (clearer; the mitigations defend against attacks,
not "for" failures).
3. **Injection (A05:2025) page — ORM bullet** ✅ — the line "Use safe ORM
(Object-Relational Mapping) APIs and stored procedures correctly." was
replaced with "Use parameterised query APIs and stored procedures
correctly; an ORM (Object-Relational Mapping) is not automatically safe —
use its parameterised methods and never concatenate raw input into
queries." An ORM is not inherently injection-proof, so it should not be
listed as a standalone mitigation.
4. **Theme 7 Quiz Q2 — logging tool calls** ✅ — the correct answer was
inverted (False was marked correct, implying logging tool calls is *not*
recommended). Swapped so **True** is correct, matching the Theme 7 content
page ("You should log tool calls, inter-agent messages, goal state and
action sequences") and the final-quiz question that treats missing agent
logs as a Security Logging and Alerting Failure. We must be able to prove
what an agent did and why; privacy is handled by privacy-aware logging,
not by not logging.
5. **Final quiz — over-scoped CRM tool question** ✅ — the question whose
correct answer was "Tool Misuse" (the over-scoped CRM summariser) was
reframed to ask for the *principle that prevents* the risk, with
"Scope each tool to least privilege: only the data and actions the task
requires" as the correct answer. (Over-scoped tools do map to Tool Misuse
/ ASI02 per the course's own page, but the lesson the scenario teaches is
least-privilege scoping; the reframe makes that the answer.)
6. **"Course complete" page added** ✅ — a new `<text>` page
(`linkID="PG1781901000001"`, name "Course complete") was added after the
final quiz so it is unambiguous that the training is over. It is a Plain
Text page (not Bullets), so the `delaySecs="0"` count is unchanged at 27.
Top-level page count is now 44 (43 learner content/quiz pages + this
closing page). Per the SCORM completion analysis in `PROJECT_CONTEXT.md`,
this page becomes the untracked last content node (no score, no
interaction — harmless), and the Final quiz's completion is now tracked.

### Local checks (run against `source/data.xml`; XOT re-export still required per convention 9)

- 45 `<question>` elements, 45 with `answerOrder="random"`.
- 8 `<quiz>` nodes; `trackingWeight` = 1×7 + 21; `trackingMode="full"`;
`trackingPassed="80%"`; `judge="true"` ×8.
- `unmarkForCompletion="true"` ×1 (Welcome); `xPersistProgress` ×1 (autosave
script); `{{BUILD_VERSION}}` ×1 (placeholder, substituted at build time).
- `delaySecs="0"` ×27 (unchanged — the new page is Plain Text).
- 0 empty option nodes; every question has ≥2 options and ≥1 correct.
- `tools/render_preview.py` renders 44 pages (final page = "Course complete")
without error.
- `source/data.xml` and `source/preview.xml` are byte-identical.

**Pending (convention 9 — MANDATORY before merge)**: push the updated
`source/data.xml` + `source/preview.xml` into a running XOT instance,
confirm `play.php` returns 200, open the HTML5 editor to confirm a Publish
round-trip preserves `answerOrder="random"` on all 45 questions (and the
other tracking attrs), export a SCORM package, and grep the exported
`template.xml` for the checklist values in `PROJECT_CONTEXT.md`. Then walk a
quiz end-to-end (answer → Check → Next → complete → Restart) and confirm
options reorder between attempts.
58 changes: 42 additions & 16 deletions docs/PROJECT_CONTEXT.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,9 +47,13 @@ with agents and building agents.

## Current state of the course (as last exported)

- **48 learner pages** in spec order: front matter (4) → 7 themes (each: intro +
classic item page(s) + agentic item page(s) + theme quiz) → back matter (4:
secure-SDLC integration, mapping, glossary, references) → final quiz.
- **44 top-level pages** in `source/data.xml` (43 learner content/quiz pages + 1
final **"Course complete"** page added after the final quiz so it is clear the
training is over): front matter (4) → 7 themes (each: intro + classic item
page(s) + agentic item page(s) + theme quiz) → back matter (4: secure-SDLC
integration, mapping, glossary, references) → final quiz → Course complete.
(Earlier docs/reports may say 43 or 48; the authoritative count is whatever
is in `source/data.xml`.)
- **Content page types**: pages 1–8 are `text` (Plain Text); pages from Theme 2
onward are `bullets` (Bullets/Timed Content) with **`delaySecs="0"`** so they
do **not** auto-play (a deliberate fix — see "Conventions" below).
Expand All @@ -59,6 +63,14 @@ with agents and building agents.
nested `<question>` children. **45 scored questions total** (23 theme +
18 final + 4 … see `COURSE_VERIFICATION.md`). Every question has ≥2 options
and ≥1 `correct="true"`; **no empty option nodes**.
- **Option order is randomised per attempt**: every `<question>` carries
`answerOrder="random"` (the Nottingham wizard's per-question "Answer Order"
property; see `quiz.xwd` and `models_html5/quiz.html` line ~165). The HTML5
player shuffles each question's options on quiz load/restart, and scoring is
computed from the shuffled array so SCORM tracking stays correct. This
removes the previous bias where the correct answer was frequently the first
option (especially in the final quiz). The attribute is editor-declared, so
the XOT HTML5 editor round-trips it natively (like `trackingWeight`).
- **Final quiz** has **18 distinct questions** (fresh scenarios, not duplicates
of the theme-quiz questions), mix of single-answer risk-identification and
true/false.
Expand All @@ -84,8 +96,7 @@ These were agreed with the course owner and must be preserved:
Applications 2026, genai.owasp.org, CC BY-SA 4.0; OWASP Top 10:2025,
owasp.org/Top10/2025/, CC BY 3.0); a "changes were made" notice; a
"not endorsed by OWASP" notice; and **provenance**:
*"Generated with the pi coding-agent harness v0.79.8 using LLM models
glm-5.2 and kimi k2.7-coder."*
*"Generated with the assistance of an AI coding agent."*
2. **Acronym expansion**: the first time a security acronym is used **on a
page**, give the full form in brackets, e.g. `TOCTOU (Time-of-check to
time-of-use)`. Reuse the acronym thereafter on that page; expand again on the
Expand Down Expand Up @@ -125,6 +136,14 @@ These were agreed with the course owner and must be preserved:
confirm `trackingMode`, `trackingWeight`, `trackingPassed`, the question
count, and the empty-option count are all as expected). Do not merge a
`source/data.xml` change that has not passed this validation.
10. **Option order must be randomised per attempt**: every `<question>` must
carry `answerOrder="random"` (Nottingham wizard's per-question "Answer
Order" property, `quiz.xwd`; honoured by `models_html5/quiz.html`). This
shuffles each question's options on quiz load/restart and removes the
pattern of the correct answer always being the first option. Scoring is
computed from the shuffled array, so SCORM tracking is unaffected. When
adding a new question, set `answerOrder="random"` on it. Keep `data.xml`
and `preview.xml` byte-identical.

## Known deviations (acknowledged, not to "fix" without asking)

Expand Down Expand Up @@ -153,7 +172,9 @@ being sent (75, then 100). `cmi.core.lesson_status` was never reported as
**Root cause** (a Xerte Nottingham engine bug, worked around at the content
level): with `navigation="Menu with Page Controls"`, Xerte inserts a built-in
Table-of-Contents page as `x_pages[0]` **after** `toCompletePages` is built
from `currActPage` over the 43 content nodes. So every content page's
from `currActPage` over the content nodes (43 when this fix was designed;
44 now that a **Course complete** page follows the final quiz — the analysis
is identical, only the last `page_nr` shifts). So every content page's
`page_nr` (used by `exitInteraction` to mark `completedPages[i]`) is
`currActPage + 1` — a systematic **off-by-one**:

Expand All @@ -179,16 +200,19 @@ against `trackingPassed="80%"`. Verified locally with a SCORM 1.2 mock API:
a completed run sends `cmi.core.lesson_status="passed"`, `cmi.core.exit=""`,
`cmi.core.score.raw`, `score.min`, `score.max`, and `cmi.core.session_time`.

**Trade-off**: the Final quiz (`page_nr == 43`) is still not in
`toCompletePages` (there is no index 43), so its *completion* is not tracked
in `completedPages` — but its *score* is still tracked via its interactions
and `trackingWeight="21"`, so the LMS grade and pass/fail are correct. The
Welcome page is no longer "required" for completion, which is acceptable for
an intro page. A proper fix would be in the Xerte engine (`xenith.js`: build
`toCompletePages` from `page_nr` after the menu is inserted, or do not insert
the menu into `x_pages` for tracking); this content workaround is used because
the release workflow builds from the XOT engine and an engine patch would not
persist across XOT rebuilds.
**Trade-off** (with the Course complete page now present): the *last* content
node — the **Course complete** page (`page_nr == 44`), a static
acknowledgement page — is not in `toCompletePages` (there is no index 44), so
its *completion* is not tracked; this is harmless (it carries no score and no
interaction). The Final quiz (`page_nr == 43`) **is** now in
`toCompletePages`, so its completion is tracked, and its *score* is tracked
via its interactions and `trackingWeight="21"`. The Welcome page is no longer
"required" for completion, which is acceptable for an intro page. A proper
fix would be in the Xerte engine (`xenith.js`: build `toCompletePages` from
`page_nr` after the menu is inserted, or do not insert the menu into `x_pages`
for tracking); this content workaround is used because the release workflow
builds from the XOT engine and an engine patch would not persist across XOT
rebuilds.

**Do not remove `unmarkForCompletion="true"` from the Welcome page** without
a replacement fix — re-introducing the off-by-one will silently break LMS
Expand Down Expand Up @@ -392,13 +416,15 @@ unzip -p Secure_code_development_scorm.zip template.xml | grep -oE 'trackingPass
unzip -p Secure_code_development_scorm.zip template.xml > /tmp/c.xml
grep -c "Trial MCQ" /tmp/c.xml # 0
grep -oE '<question' /tmp/c.xml | wc -l # 45 (23 theme + 18 final + 4 …)
grep -oE 'answerOrder="random"' /tmp/c.xml | wc -l # 45 (every question shuffles options per attempt)
grep -oE 'trackingPassed="[^"]*"' /tmp/c.xml # 80%
grep -oE 'judge="true"' /tmp/c.xml | wc -l # 8
grep -oE 'trackingWeight="[0-9]+"' /tmp/c.xml # 7x "1" + 1x "21"
grep -oE 'trackingMode="[a-z_]+"' /tmp/c.xml # "full"
grep -oE 'delaySecs="0"' /tmp/c.xml | wc -l # 27 (all bullets pages)
grep -c 'xPersistProgress' /tmp/c.xml # 1 (SCORM autosave script present on root)
grep -oE 'unmarkForCompletion="true"' /tmp/c.xml | wc -l # 1 (Welcome page)
grep -oE 'name="Course complete"' /tmp/c.xml | wc -l # 1 (final "Course complete" page after the final quiz)
# build version: in a release export {{BUILD_VERSION}} is substituted; in source/preview it is the placeholder
grep -oE 'Version:</strong> [^<&]*' /tmp/c.xml # e.g. 'Version:</strong> v0.0.6 (...)'
grep -c '{{BUILD_VERSION}}' /tmp/c.xml # 0 in a release export (1 in committed source)
Expand Down
Loading