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.
In this repository23 other files it gives its agents.
- android-code-securitySkill
- auth-reviewSkill
- config-hardeningSkill
- container-iac-securitySkill
- crypto-reviewSkill
- cve-researchSkill
- data-exposureSkill
- dependency-vulnsSkill
- design-threat-reviewSkill
- django-securitySkill
- dotnet-securitySkill
- flow-securitySkill
- flutter-securitySkill
- graphql-securitySkill
- injection-flawsSkill
- laravel-securitySkill
- llm-securitySkill
- mobile-securitySkill
- rails-securitySkill
- secrets-detectionSkill
- security-auditSkill
- skill-forgeSkill
- spring-securitySkill
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
- Distinctive: line 35
EVERY read path the public persona can reach, including counts and facets.
≈ “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.
Files
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 -20Record: 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+userIdfrom 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*' | headRoute-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):
- Trace the write path: what validation/sanitization runs before storage?
(
required|stringalone stores raw HTML.) - 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. - 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
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
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.

