agentleFS
ashrafiucseashrafiucse/security-audit-skills/skills/course-platform-security/SKILL.md

course-platform-security

Skill2 starsChanged 49 days agoMITView on GitHub

Audits course-selling / e-learning platforms for domain-specific security — catalog gating truth (draft/unpublished/private course exposure to public), preview-vs-full-content leaks, enrollment state machines (free enrollment without paid order), cohort/multi-cohort access control (student of cohort A reading cohort B), and persona-driven route checks (admin-only surfaces reachable by public/student). Use when the project sells or gates digital courses/content — look for courses, lessons, enrollments, cohorts, orders, subscriptions, previews.

The file

We split this skill into the rules it gives an agent

1 rule. About 0 are common, 1 is distinctive, and 0 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈1

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 0

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    EVERY read path the public persona can reach, including counts and facets.

    line 35

≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Course Platform Security

Domain skill built from the three personas. Walk every surface as each persona — findings live where a persona reaches something that isn't theirs.

1 — Map the domain model

rg -n -i "course|lesson|module|enrollment|cohort|preview|curriculum|subscription" -g '**/models/**' -g '**/entities/**' -g '*.prisma' -g 'schema*' | head -25
rg -n -i "(enroll|purchase|checkout|access|entitle)" -g '*routes*' -g '*controller*' | head -20

Record: course lifecycle states (draft → published → archived? private?), enrollment sources (order/webhook/admin-grant/subscription), cohort model (if any), preview mechanism, content storage/signing.

2 — Gating truth (public persona)

The public catalog must only expose what is published AND public.

rg -n "courses?\.(find|where|all|select)" -g '*.js' -g '*.ts' -g '*.py' -g '*.rb'   # census: disposition every read path
  • List/search endpoints returning draft/unpublished/private courses → High (hidden product roadmap + private catalog leaks; PR-sensitive)
  • Detail endpoints rendering unpublished courses by id (no 404 on status != published) → High — check the 404 branch actually filters status
  • Sitemap/feeds/search-index endpoints including gated courses → Medium
  • Safe shape: where({ status: 'published', visibility: 'public' }) on EVERY read path the public persona can reach, including counts and facets.

3 — Preview vs full content (public persona)

rg -n -i "preview|sample|trial|first.?lesson" -g '*controller*' -g '*routes*' -g '*resolver*' | head
  • Preview endpoints returning the FULL lesson/video/module payload → Critical (paid content free; check field selection, not just access)
  • Unsigned/expiring-missing media URLs in preview responses that also serve full content → High (URL guessing/replay)
  • "Free first lesson" logic that trusts client-sent lesson index → Medium

4 — Enrollment as a state machine (student + flow)

Ties into ../flow-security/SKILL.md (F1/F5 classes) — enrollment is the terminal artifact of order→payment:

rg -n -i "enrollment.*(create|insert|save)|grant.*access" -g '*.js' -g '*.py' | head
  • Enrollment created without a PAID order/artifact check (admin-grant paths must be separately authorized) → Critical (free enrollment)
  • Webhook-driven enrollment without signature verification → Critical
  • Self-enrollment endpoints allowing arbitrary courseId + userId from body → Critical
  • Safe shape: enrollment granted only by the payment fulfillment path (status-guarded) or an admin-grant route with role middleware.

5 — Cohort / multi-cohort access (student persona)

rg -n -i "cohort|batch|class_?id|group_?id" -g '*.js' -g '*.py' -g '*.rb'   # census: disposition every cohort-touching read
  • Materials/lessons/live-sessions fetched by courseId WITHOUT the student's cohort scope → High/Critical (cross-cohort read; per-object authz is not enough — the cohort IS the tenant; see multi-tenant census in ../auth-review/SKILL.md)
  • Cohort-switching via request body (req.body.cohortId) instead of the enrollment record → Critical
  • Progress/completion written cross-cohort (student A completing for B) → High

6 — Admin surfaces (admin persona — and everyone else)

rg -n -i "admin|manage|catalog|publish|price|coupon" -g '*routes*' -g '*controller*' | head

Route-census every admin route: course create/update/publish, price change, coupon mint, refund, user management. Any reachable without role middleware → Critical (price tamper + free-publish chains). Coupon minting and price changes are money operations — same state-machine rules as flow-security.

6.5 — Student-authored content inside admin viewports (moderation-queue XSS)

Admin surfaces don't only authorize actions — they RENDER artifacts that unprivileged users created. For every artifact a student/customer can submit that staff must open (reviews, support tickets, messages, quiz answers, assignment text, display names, uploaded file names):

  1. Trace the write path: what validation/sanitization runs before storage? (required|string alone stores raw HTML.)
  2. Trace the render path in the STAFF surface (moderation list AND detail view — the detail view is the one staff clicks). Raw echo ({!! !!}), dangerouslySetInnerHTML, or an iframe/email HTML render of it → Critical: payload fires in the staff origin, rides the session, escalates to admin account takeover.
  3. Pending/approval workflows make it worse, not better — they GUARANTEE a privileged viewer opens the attacker's content.

Tag these student→admin: XSS and cross-reference the framework skill's raw-output scan (e.g. ../laravel-security/SKILL.md Step 4).

7 — Reporting

Tag findings with the persona that reaches them (public:, student:, admin:) and the class (GATING / PREVIEW / ENROLLMENT / COHORT / ADMIN). Chains matter here: unpublished-catalog leak + preview-full-content = competitor scraping; enrollment-bypass + cohort-IDOR = free full access at scale. Cite the invariant violated, not just the line.

False-positive discipline

  • Legit "coming soon" previews intentionally showing full first lesson (check the product flag before flagging free-first-lesson)
  • Admin-grant enrollment endpoints WITH role middleware = by design
  • Internal LMS (no public persona): skip §2/§3, keep §4/§5/§6

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.