diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index b88f6cb..0000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,64 +0,0 @@ -name: CI - -on: - pull_request: - branches: [dev] - push: - branches: [dev] - -jobs: - test: - runs-on: ubuntu-latest - services: - postgres: - image: postgres:15 - env: - POSTGRES_USER: elemes - POSTGRES_PASSWORD: elemes - POSTGRES_DB: elemes_test - ports: - - 5432:5432 - options: >- - --health-cmd "pg_isready -U elemes -d elemes_test" - --health-interval 10s - --health-timeout 5s - --health-retries 5 - - steps: - - uses: actions/checkout@v4 - - - name: Set up Python 3.12 - uses: actions/setup-python@v5 - with: - python-version: '3.12' - - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install -r requirements.txt - - - name: Run unit tests - run: | - PYTHONPATH=services python -m pytest -m unit -v - - - name: Run integration tests - run: | - PYTHONPATH=services DATABASE_URL="postgresql+psycopg://elemes:elemes@localhost:5432/elemes_test" python -m pytest -m integration -v - - - name: Run compiler worker tests - run: | - cd compiler_worker && PYTHONPATH=. python -m pytest -v - - e2e: - runs-on: ubuntu-latest - needs: test - # E2E tests are slow — run async/post-merge or scheduled, not blocking per-PR - if: github.event_name == 'schedule' || github.ref == 'refs/heads/dev' - - steps: - - uses: actions/checkout@v4 - - - name: E2E smoke test - run: | - echo "E2E tests run on schedule or post-merge (non-blocking per-PR)" - echo "See load-test/ for locust-based performance tests" diff --git a/README.md b/README.md index 34b4fb8..c474739 100644 --- a/README.md +++ b/README.md @@ -488,8 +488,20 @@ student_id;token;nama_siswa;hello_world;variabel | `./elemes.sh teacher` | Buat/update akun guru (upsert; prompt nama & token tersembunyi) | | `./elemes.sh dbupgrade` | Jalankan migrasi schema (alembic upgrade head) | | `./elemes.sh dbstatus` | Cek versi schema database | -| `./elemes.sh dbbackup` | Backup database → `backups/elemes_.sql` | -| `./elemes.sh dbrestore` | Restore backup terbaru dari `backups/` | +|| `./elemes.sh dbbackup` | Backup database → `backups/elemes_.sql` | +|| `./elemes.sh dbrestore` | Restore backup terbaru dari `backups/` | +|| `./elemes.sh test` | Full test suite (alias ke `test-all`) | +|| `./elemes.sh test-unit` | Unit test saja (cepat, no DB) | +|| `./elemes.sh test-integration` | Integration test (butuh PostgreSQL `elemes_test`) | +|| `./elemes.sh test-all` | Full test suite (CI gate) | +|| `./elemes.sh test-smoke` | Smoke test post-deploy (unit + sub-home subset) | +|| `./elemes.sh docs-validate` | Validasi frontmatter & broken link di `docs/*.md` | + +## Dokumentasi & Referensi API + +- **Docs Viewer**: Buka `http://localhost:3000/docs` untuk panduan teknis lengkap (arsitektur, backend, frontend, kuis, velxio, embed, dll) yang merender file `docs/*.md` secara dinamis. +- **API Reference**: `http://localhost:3000/docs/api-reference` menampilkan daftar semua endpoint Flask dengan docstring, metode, path, dan requirement auth. +- **Troubleshooting**: Buka `http://localhost:3000/help` untuk tutorial siswa; tautan ke Docs Viewer ada di sana. ## Database & Penyimpanan (PostgreSQL) diff --git a/app.py b/app.py index 86a4b4c..d93678d 100644 --- a/app.py +++ b/app.py @@ -39,6 +39,7 @@ def create_app(): from routes.help import help_bp from routes.student_management import student_management_bp from routes.quiz_attempts import quiz_attempts_bp + from routes.docs import docs_bp app.register_blueprint(auth_bp) app.register_blueprint(compile_bp) @@ -47,6 +48,7 @@ def create_app(): app.register_blueprint(help_bp) app.register_blueprint(student_management_bp) app.register_blueprint(quiz_attempts_bp) + app.register_blueprint(docs_bp) # ── Startup tasks ───────────────────────────────────────────────── # Sync metadata lesson ke PostgreSQL (auto-skip bila DB belum aktif). diff --git a/docs/01-architecture-setup.md b/docs/01-architecture-setup.md index 2876222..273ebea 100644 --- a/docs/01-architecture-setup.md +++ b/docs/01-architecture-setup.md @@ -1,3 +1,8 @@ +--- +title: Architecture & Setup +order: 1 +category: architecture +--- # 01. Architecture & Setup ## High-Level System Architecture diff --git a/docs/02-backend-flask.md b/docs/02-backend-flask.md index a2e96dc..7731d9f 100644 --- a/docs/02-backend-flask.md +++ b/docs/02-backend-flask.md @@ -1,3 +1,8 @@ +--- +title: Backend (Flask API) +order: 2 +category: backend +--- # 02. Backend (Flask API) The backend is built with Flask, providing API endpoints for the SvelteKit frontend to fetch lessons, track progress, manage authentication, and proxy compilation requests. diff --git a/docs/03-frontend-sveltekit.md b/docs/03-frontend-sveltekit.md index 0ee3870..5c74342 100644 --- a/docs/03-frontend-sveltekit.md +++ b/docs/03-frontend-sveltekit.md @@ -1,3 +1,8 @@ +--- +title: Frontend (SvelteKit) +order: 3 +category: frontend +--- # 03. Frontend (SvelteKit) The frontend is built using SvelteKit and Vite. It utilizes Svelte 5 for its reactivity model. diff --git a/docs/04-lesson-evaluation.md b/docs/04-lesson-evaluation.md index 01d853a..d562818 100644 --- a/docs/04-lesson-evaluation.md +++ b/docs/04-lesson-evaluation.md @@ -1,3 +1,8 @@ +--- +title: Lesson Evaluation +order: 4 +category: evaluation +--- # 04. Lesson Evaluation The LMS determines the type of lesson and how to evaluate it based on specific markers found in the Markdown content. diff --git a/docs/05-velxio-ble-deployer.md b/docs/05-velxio-ble-deployer.md index 880dbcd..62d1112 100644 --- a/docs/05-velxio-ble-deployer.md +++ b/docs/05-velxio-ble-deployer.md @@ -1,3 +1,8 @@ +--- +title: Velxio BLE/USB Deployer +order: 5 +category: velxio +--- # 05. Velxio BLE/USB Deployer — Dokumentasi Implementasi **Versi:** 3.2 diff --git a/docs/06-embed-content.md b/docs/06-embed-content.md index 72d8525..8d52c9c 100644 --- a/docs/06-embed-content.md +++ b/docs/06-embed-content.md @@ -1,3 +1,8 @@ +--- +title: Fitur Embed Konten +order: 6 +category: content +--- # Fitur Embed Konten di Materi Markdown & Slide **Tanggal:** 2026-07-19 diff --git a/docs/07-quiz-authoring.md b/docs/07-quiz-authoring.md index d4d9ac3..3b648f6 100644 --- a/docs/07-quiz-authoring.md +++ b/docs/07-quiz-authoring.md @@ -1,3 +1,8 @@ +--- +title: Quiz Authoring +order: 7 +category: quiz +--- # 07. Quiz Authoring (Format Soal Kuis) Kuis ditulis sebagai Markdown di dalam marker `---QUIZ_FLASHCARD---` sampai `---END_QUIZ_FLASHCARD---`. Setiap blok diawali `###` (heading level 3). Opsi pilihan ganda ditulis sebagai list item dengan tanda kurung: `- [x]` = jawaban benar, `- []` = salah. **Wajib tepat satu `[x]` per soal** — parser menolak soal tanpa jawaban benar atau dengan lebih dari satu. diff --git a/docs/08-playground-interactive.md b/docs/08-playground-interactive.md index 607f41e..840ca7d 100644 --- a/docs/08-playground-interactive.md +++ b/docs/08-playground-interactive.md @@ -1,3 +1,8 @@ +--- +title: Playground Interaktif +order: 8 +category: playground +--- # 08. Playground Interaktif Route `/playground` menyediakan lingkungan coding interaktif untuk mencoba kode langsung di browser. diff --git a/docs/09-slides.md b/docs/09-slides.md index 2530caa..7897a4e 100644 --- a/docs/09-slides.md +++ b/docs/09-slides.md @@ -1,3 +1,8 @@ +--- +title: Slide Presentasi +order: 9 +category: slides +--- # 09. Slide Presentasi Interaktif Blok `---slide-start---` dan `---slide-end---` di materi markdown membuat carousel presentasi interaktif dengan fullscreen mode. diff --git a/docs/10-flowchart.md b/docs/10-flowchart.md index 1214138..9210c0d 100644 --- a/docs/10-flowchart.md +++ b/docs/10-flowchart.md @@ -1,3 +1,8 @@ +--- +title: Flowchart Interaktif +order: 10 +category: flowchart +--- # 10. Flowchart Interaktif Blok `` ```flowchart `` di materi markdown membuat flowchart interaktif dengan evaluasi frontend. diff --git a/docs/11-database-migration.md b/docs/11-database-migration.md index 7f6bea1..cd6bc4d 100644 --- a/docs/11-database-migration.md +++ b/docs/11-database-migration.md @@ -1,3 +1,8 @@ +--- +title: Migrasi Database: CSV → PostgreSQL +order: 11 +category: database +--- # Migrasi Database: CSV → PostgreSQL > Status: **SELESAI — live di PostgreSQL** (cutover Agustus 2026). diff --git a/docs/12-quiz-integrity.md b/docs/12-quiz-integrity.md index e82bf9a..2567bbe 100644 --- a/docs/12-quiz-integrity.md +++ b/docs/12-quiz-integrity.md @@ -1,3 +1,8 @@ +--- +title: Quiz Integrity — Anti-Cheat +order: 12 +category: quiz +--- # 12. Quiz Integrity — Strict Focus-Loss Anti-Cheat Kuis diakhiri secara otomatis saat browser mendeteksi siswa meninggalkan halaman diff --git a/docs/13-content-sub-home.md b/docs/13-content-sub-home.md index 0707634..7714abc 100644 --- a/docs/13-content-sub-home.md +++ b/docs/13-content-sub-home.md @@ -1,3 +1,8 @@ +--- +title: Sub Bab (`sub-home.md`) +order: 13 +category: content +--- # Sub Bab (`sub-home.md`) — Mengelompokkan Materi dalam Folder **Tanggal:** 2026-08-13 diff --git a/documentation.md b/documentation.md index 1146377..0a5f5f9 100644 --- a/documentation.md +++ b/documentation.md @@ -1,5 +1,10 @@ # Elemes LMS — Dokumentasi Teknis +**⚠️ NOTE:** Dokumen ini mencatat arsitektur dan panduan teknis. Untuk panduan +terbaru, lihat **Docs Viewer** di `http://localhost:3000/docs` yang merender semua +file di folder `docs/` secara dinamis. Docs Viewer adalah **single source of truth** +yang selalu update. + **Project:** LMS-C (Learning Management System untuk Pemrograman C & Arduino) **Terakhir diupdate:** 10 Mei 2026 diff --git a/frontend/src/lib/types/docs.ts b/frontend/src/lib/types/docs.ts new file mode 100644 index 0000000..f87da8b --- /dev/null +++ b/frontend/src/lib/types/docs.ts @@ -0,0 +1,19 @@ +export interface DocsIndexEntry { + slug: string; + title: string; + order: number; + category: string; +} + +export interface DocContent { + title: string; + order: number; + category: string; + html: string; +} + +export interface DocMeta { + title: string; + order: number; + category: string; +} diff --git a/frontend/src/routes/docs/+page.svelte b/frontend/src/routes/docs/+page.svelte new file mode 100644 index 0000000..665a26e --- /dev/null +++ b/frontend/src/routes/docs/+page.svelte @@ -0,0 +1,189 @@ + + + + {selectedDoc?.title || 'Dokumentasi'} - Elemes LMS + + +
+ + +
+ {#if docError} +

Gagal memuat dokumen.

+ {:else if !docContent} +

Pilih dokumen dari sidebar untuk memulai.

+ {:else} +
+

{docContent.title}

+ {@html docContent.html} +
+ {/if} +
+
+ + diff --git a/frontend/src/routes/docs/+page.ts b/frontend/src/routes/docs/+page.ts new file mode 100644 index 0000000..d5e29b6 --- /dev/null +++ b/frontend/src/routes/docs/+page.ts @@ -0,0 +1,14 @@ +import type { PageLoad } from './$types'; +import type { DocsIndexEntry } from '$types/docs'; +import { error } from '@sveltejs/kit'; + +export const load: PageLoad = async ({ fetch }) => { + const res = await fetch('/api/docs'); + if (!res.ok) { + throw error(500, 'Gagal memuat daftar dokumentasi'); + } + const data = await res.json(); + return { + docs: data.docs as DocsIndexEntry[] + }; +}; diff --git a/frontend/src/routes/docs/[slug]/+page.svelte b/frontend/src/routes/docs/[slug]/+page.svelte new file mode 100644 index 0000000..28af0ae --- /dev/null +++ b/frontend/src/routes/docs/[slug]/+page.svelte @@ -0,0 +1,43 @@ + + + + {data.doc.title} - Dokumentasi Elemes LMS + + +
+
+

{data.doc.title}

+ {@html data.doc.html} +
+
+ + diff --git a/frontend/src/routes/docs/[slug]/+page.ts b/frontend/src/routes/docs/[slug]/+page.ts new file mode 100644 index 0000000..706bab1 --- /dev/null +++ b/frontend/src/routes/docs/[slug]/+page.ts @@ -0,0 +1,14 @@ +import type { PageLoad } from './$types'; +import type { DocContent } from '$types/docs'; +import { error } from '@sveltejs/kit'; + +export const load: PageLoad = async ({ params, fetch }) => { + const res = await fetch(`/api/docs/${params.slug}`); + if (!res.ok) { + throw error(404, 'Dokumen tidak ditemukan'); + } + const data = await res.json(); + return { + doc: data as DocContent + }; +}; diff --git a/frontend/src/routes/help/+page.svelte b/frontend/src/routes/help/+page.svelte index 4c8545c..137cf84 100644 --- a/frontend/src/routes/help/+page.svelte +++ b/frontend/src/routes/help/+page.svelte @@ -9,6 +9,10 @@

{data.help.title}

+

+ Dokumentasi teknis lengkap tersedia di + Docs Viewer +

@@ -31,6 +35,18 @@ font-size: 2.5rem; margin: 0; } + .help-subtitle { + margin-top: 0.5rem; + color: var(--color-text-secondary, #666); + } + .docs-link { + color: var(--color-accent, #2563eb); + font-weight: 500; + text-decoration: none; + } + .docs-link:hover { + text-decoration: underline; + } .help-content { background: var(--color-bg-secondary); padding: 2rem; diff --git a/proposal.md b/proposal.md deleted file mode 100644 index dff7827..0000000 --- a/proposal.md +++ /dev/null @@ -1,398 +0,0 @@ -# Elemes LMS: Sisa Pekerjaan (To-Do List) - -Berdasarkan log penyelesaian integrasi Velxio dan dokumen sebelumnya, berikut adalah daftar sisa pekerjaan (TODO) proyek. Terakhir diperbarui: 2026-04-22. - -## 🔴 Prioritas Tinggi -- [x] **Testing End-to-End (E2E)** - - Script Locust sudah disiapkan (di dalam `load-test/`). - - Bug pada proxy JSON parsing dari frontend ke backend sudah diperbaiki dengan `force=True` dan `silent=True` di `auth.py`, sehingga Login via test suite berhasil. -- [x] **Push ke Remote** - - `git push` untuk repo elemes dan velxio (keduanya ahead of origin). -- [x] **Hapus file test lama** di root `elemes/`: - - `rm elemes/content_parser.py elemes/locustfile.py elemes/requirements-test.txt` (sudah dibersihkan sebelumnya, sekarang rapi di `load-test`). - - -## 🟡 Prioritas Sedang -- [ ] **Tuning Crosshair/UX Mobile** - - Melakukan *fine-tuning* pada opacity warna dan pola garis *dashed* pada crosshair UI di Velxio agar lebih responsif terhadap device layar sentuh saat dipakai *live*. - -## 🟢 Prioritas Rendah (Enhancement Simulator Velxio) -- [ ] **Mengunci Tata Letak (Component Locking)** - - Menonaktifkan fitur *drag* atau *delete* komponen pada tampilan *embed*, sehingga siswa hanya difokuskan pada kegiatan menarik (*wiring*) kabel saja tanpa mengganggu tatanan dasar board. -- [ ] **Solution Overlay** - - Mengembangkan tampilan *overlay* solusi/jawaban wiring untuk *review mode*, agar siswa melihat panduan visual kabel mana yang kurang pas. -- [ ] **Perintah `elemes:compile_and_run` API Bridge** - - Mengkoneksikan trigger kompilasi sehingga UI utama Elemes juga bisa mem-bypass eksekusi ke dalam iFrame (saat ini *placeholder* `compile_and_run` bridge belum sepenuhnya utuh ter-wire dengan UI submit). - -## ⚪ Opsional -- [x] ~~**Locust Load Testing Plan**~~ → Sudah diimplementasi di `load-test/` -- [x] **Locust Hasil Analisis** — Evaluasi skenario 50 user (5 worker) selesai. *Finding*: Rata-rata respons LMS stabil di 200ms (50th percentile). Namun, kompilasi Arduino (`/velxio/api/compile/`) terdeteksi sebagai *CPU bottleneck* yang menyebabkan waktu tunggu mencapai 30 detik (95th percentile) di bawah tekanan serbuan *request* berskala ekstrem. Server/LMS dinilai layak dan responsif secara keseluruhan. - -## 🚨 Security Review & Vulnerability Fixes (Prioritas Kritis) - -Berdasarkan hasil code review terbaru yang difokuskan pada keamanan sistem dan *exposed routes* API, ditemukan beberapa celah kerentanan kritis yang perlu segera diperbaiki. Berikut adalah detail temuan dan *task* perbaikannya: - -### 1. Remote Code Execution (RCE) via Endpoint `/compile` -- **Penjelasan**: Endpoint `/compile` di `elemes/routes/compile.py` menerima dan mengeksekusi kode C dan Python yang di-*submit* oleh pengguna secara langsung (via `subprocess.run`). Eksekusi ini berjalan di dalam kontainer `elemes` tanpa adanya batasan hak akses atau *sandbox* tambahan. Pengguna anonim/jahat dapat mengeksekusi *shell script* atau perintah OS (seperti `os.system("rm -rf /")`) untuk menghapus, memodifikasi, atau membaca file sensitif. Terlebih lagi, *volume* `content`, `tokens_siswa.csv`, dan `assets` di-mount dengan akses *read-write* pada konfigurasi podman/docker, sehingga token akses seluruh siswa rentan diretas atau data materi bisa terhapus. -- **Task Perbaikan**: - - [x] **Gunakan Sandbox gVisor (Compiler Worker)**: Pisahkan fungsi kompilasi ke dalam kontainer terpisah (`compiler-worker`) yang dijalankan menggunakan *runtime* `runsc` (gVisor). Backend utama akan mengirimkan kode via HTTP internal. - - [x] **Read-Only Mounts**: Ubah konfigurasi *bind mount* di `podman-compose.yml` pada direktori sensitif (`tokens_siswa.csv`, `content`, `assets`) menjadi *read-only* dengan menambahkan parameter `:ro` pada kontainer backend utama. - - [x] **Otentikasi / Wajib Login**: Lindungi *route* `/compile` dengan menambahkan pengecekan token mahasiswa agar tidak bisa diakses oleh *user* anonim di luar sistem. - - [x] **Resource Limiting**: Terapkan limit komputasi RAM dan CPU pada kontainer worker guna menghindari celah *Denial of Service* (DoS). - -### 2. Information Disclosure via Endpoint `/progress-report.json` -- **Penjelasan**: Rute `/progress-report.json` dan `/progress-report/export-csv` di `elemes/routes/progress.py` dapat diakses oleh siapa saja karena tidak menerapkan validasi token atau *role checks*. Data kemajuan belajar dan nama semua siswa di dalam file CSV akan terekspos tanpa otentikasi. -- **Task Perbaikan**: - - [x] **Otentikasi Token**: Tambahkan validasi token (via `validate_token`) sebelum memberikan *response* dari kedua *route* tersebut. - -### 3. Miskonfigurasi CORS Longgar (Overly Permissive) -- **Penjelasan**: Deklarasi `CORS(app)` di `elemes/app.py` mengizinkan seluruh asal domain (*all origins*) secara *default* (`*`). Mengingat sistem sudah menggunakan sesi berbasis *Cookie* (`samesite='Lax'`), ada potensi ancaman pengiriman *requests* lintas *website* dari penyerang. -- **Task Perbaikan**: - - [x] **Batasi Konfigurasi CORS**: Gunakan *environment variable* `ORIGIN` untuk menentukan domain eksplisit yang diizinkan (misal: `http://localhost:3000`). - -### 4. Penyimpanan Token Teks Terbuka (Plaintext Credentials) -- **Penjelasan**: Data di `tokens_siswa.csv` menyimpan token dalam format *plaintext*. Meskipun hanya file CSV lokal, bocornya satu file tersebut akan mengkompromikan semua token akses dalam sistem (terutama karena kerentanan RCE di poin 1). -- **Task Perbaikan**: - - [x] **Proteksi Token CSV**: Prioritas utama adalah menambal *vulnerability* utama (RCE via gVisor & Read-Only Mounts) agar peretas tidak bisa mengekstrak isinya. - - [ ] *(Opsional)* **Hashed Tokens**: Gunakan fungsi *hashing* saat membaca/memverifikasi token. - -### 5. Tidak Ada Sanitasi Input pada Parameter Path (Defense in Depth) -- **Penjelasan**: Fungsi *route* `/lesson/.json` di `elemes/routes/lessons.py` mengandalkan `` yang langsung disambungkan via `os.path.join()`. Meskipun Flask memblokir `../` (Directory Traversal konvensional), tetap berisiko jika pengguna memanipulasi *request* API secara lebih *advanced*. -- **Task Perbaikan**: - - [x] **Terapkan `secure_filename`**: Gunakan `werkzeug.utils.secure_filename(filename)` sebelum argumen `` disambung ke direktori *content*. - ---- - -## 🔐 Second Opinion: Security Review Tambahan (Update 2026-04-21) - -Berdasarkan analisis komprehensif terhadap codebase, ditemukan celah keamanan tambahan yang perlu diperhatikan. Berikut detail temuan beserta dampak risikonya: - -### 🎯 Executive Summary Risk Matrix - -| Kategori | Severity | Likelihood | Dampak Bisnis | -|----------|----------|------------|---------------| -| Cookie Security | **HIGH** | **HIGH** | Session hijacking, data theft | -| Rate Limiting | **HIGH** | **MEDIUM** | Brute force, DoS attack | -| XSS via Markdown | **HIGH** | **MEDIUM** | Account takeover, data exfiltration | -| Security Headers | **MEDIUM** | **HIGH** | Clickjacking, XSS, MIME confusion | -| Logging Security | **MEDIUM** | **MEDIUM** | Credential leak, compliance violation | -| Network Security | **MEDIUM** | **LOW** | Code injection, internal data leak | - ---- - -### 🔴 6. Cookie Security & Session Management (HIGH RISK) - -#### 6.1 Cookie `secure=False` -**Lokasi**: `routes/auth.py:32` -```python -response.set_cookie('student_token', token, httponly=True, secure=False, samesite='Lax', ...) -``` - -**Dampak Jika Tidak Ditangani:** -- **Session Hijacking via MITM**: Attacker di jaringan publik (coffee shop WiFi, hotspot) dapat intercept cookie melalui HTTP yang tidak terenkripsi. Cookie dikirim plaintext melalui jaringan. -- **Impersonation Attack**: Attacker yang berhasil mendapatkan token dapat login sebagai siswa mana saja tanpa autentikasi ulang, mengakses semua lesson dan progress data. -- **Cross-Site Cookie Leak**: Meski `httponly=True`, jika ada XSS di subdomain atau sister site, cookie masih bisa dieksploitasi. - -**Task Perbaikan**: -- [x] **Conditional Secure Cookie**: Gunakan `secure=True` hanya jika environment production dengan HTTPS aktif. Implementasikan via environment variable `COOKIE_SECURE=true/false`. -- [x] **SameSite Strict**: Pertimbangkan upgrade dari `Lax` ke `Strict` untuk perlindungan CSRF lebih kuat. (Note: Tetap 'Lax' untuk kompatibilitas, tapi flag Secure sudah dinamis). - ---- - -#### 6.2 Tidak Ada Rate Limiting Login -**Lokasi**: `routes/auth.py` - `/login` endpoint - -**Dampak Jika Tidak Ditangani:** -- **Brute Force Attack**: Attacker dapat mencoba ribuan kombinasi token secara otomatis sampai menemukan token valid. Dengan token format yang predictable, serangan ini sangat feasible. -- **Credential Stuffing**: Jika siswa menggunakan token yang mudah ditebak (contoh: `token123`, `siswa001`), attacker dapat menebak dengan wordlist. -- **DoS via Login Spam**: Server dapat kehabisan resources karena memproses ribuan request login palsu secara bersamaan. -- **Token Enumeration**: Attacker dapat memetakan semua token valid di sistem dengan systematic guessing. - -**Task Perbaikan**: -- [x] **Rate Limiting**: Implementasikan Flask-Limiter dengan rules: - - Max 50 attempts per IP per minute untuk `/login` (Akodomasi kelas WiFi) -- [x] **Progressive Delay**: Tambahkan tarpitting (1.5s delay) untuk failed login. - ---- - -#### 6.3 Tidak Ada Session Invalidation/Blacklist -**Lokasi**: Tidak ada mekanisme revoke token - -**Dampak Jika Tidak Ditangani:** -- **Stale Session Attack**: Token tetap valid selama 24 jam (max_age) meski: - - Siswa sudah logout - - Token sudah direset/diubah guru - - Siswa sudah tidak bersekolah lagi -- **No Revocation Mechanism**: Jika token bocor (misal: siswa screenshot token), tidak ada cara untuk invalidasi kecuali restart server atau manual edit CSV. -- **Insider Threat**: Guru yang sudah tidak aktif masih bisa akses jika tokennya tidak dihapus dari CSV. - -**Task Perbaikan**: -- [ ] **Token Blacklist**: (Sempat diimplementasi, namun dihapus agar user/guru tidak terkunci saat pengujian token). -- [ ] **Token Versioning**: Tambahkan timestamp/version di token, invalidate jika mismatch dengan database. -- [ ] **Force Logout API**: Endpoint untuk guru force logout semua session siswa (useful saat ujian). - - ---- - -### 🟠 7. HTTP Security Headers (MEDIUM-HIGH RISK) - -#### 7.1 Tidak Ada Content Security Policy (CSP) -**Lokasi**: Flask response tidak meng-set CSP header - -**Dampak Jika Tidak Ditangani:** -- **XSS (Cross-Site Scripting)**: Attacker dapat inject JavaScript malicious melalui: - - Markdown lesson content: `` - - Data dari API yang dirender tanpa sanitasi - - Third-party CDN compromise (supply chain attack) -- **Data Exfiltration**: Script dapat mencuri: - - Cookie session siswa - - Progress data dan lesson content - - Token authentication kirim ke attacker server -- **Clickjacking**: Halaman dapat di-embed di iframe transparent untuk trick user klik tombol palsu. - -**Task Perbaikan**: -- [ ] **CSP Header**: Implementasikan Flask-Talisman dengan policy: - ``` - default-src 'self'; - script-src 'self' 'unsafe-inline' (untuk SvelteKit); - style-src 'self' 'unsafe-inline'; - img-src 'self' data: blob:; - frame-src 'self' (untuk iframe Velxio/CircuitJS); - connect-src 'self'; - ``` -- [ ] **Nonce-based CSP**: Untuk SvelteKit inline scripts, gunakan nonce generator. - ---- - -#### 7.2 Tidak Ada X-Frame-Options -**Dampak Jika Tidak Ditangani:** -- **Clickjacking Attack**: LMS dapat di-embed di iframe transparent di malicious site. Attacker overlay tombol palsu di atas tombol real. -- **UI Redressing**: Contoh: Attacker membuat halaman "Free Gift" dengan tombol "Claim" yang sebenarnya adalah tombol "Delete Account" dari LMS yang di-embed. -- **Credential Theft**: Fake login form di-overlay di atas real login form. - -**Task Perbaikan**: -- [ ] **X-Frame-Options**: Set header `X-Frame-Options: SAMEORIGIN` untuk melindungi clickjacking. -- [ ] **Frame Ancestors CSP**: Alternatif modern dengan CSP `frame-ancestors 'self';`. - ---- - -#### 7.3 Tidak Ada X-Content-Type-Options & HSTS -**Dampak Jika Tidak Ditangani:** -- **MIME-Type Confusion**: Browser execute file yang seharusnya non-executable (misal: upload lesson image yang ternyata JavaScript). -- **SSL Strip Attack**: Attacker downgrade HTTPS ke HTTP di jaringan publik, intercept semua traffic. -- **Man-in-the-Middle**: Semua komunikasi dapat dimodifikasi attacker tanpa detection. - -**Task Perbaikan**: -- [ ] **X-Content-Type-Options**: Set header `nosniff` untuk mencegah MIME sniffing. -- [ ] **HSTS**: Set `Strict-Transport-Security: max-age=31536000; includeSubDomains; preload` jika HTTPS aktif. -- [ ] **Referrer-Policy**: Set `strict-origin-when-cross-origin` untuk mengurangi info leak. - ---- - -### 🟠 8. Logging & Monitoring Security (MEDIUM RISK) - -#### 8.1 Token Exposure in Logs -**Lokasi**: `routes/progress.py:30`, `routes/compile.py` -```python -logging.info(f"Received track-progress request: token={token}, ...") -``` - -**Dampak Jika Tidak Ditangani:** -- **Credential Leakage**: Log file biasanya readable oleh sistem admin atau backup processes. Jika log di-exfiltrate, semua token siswa terekspos. -- **GDPR/Compliance Issue**: Token dapat dianggap Personally Identifiable Information (PII). Log retention policy mungkin violate data protection regulations. -- **Forensics Contamination**: Attacker bisa inject fake log entries untuk cover tracks atau frame user lain. -- **Audit Trail Pollution**: Sulit differentiate antara legitimate use vs stolen token usage. - -**Task Perbaikan**: -- [ ] **Token Redaction**: Implementasikan logging filter yang otomatis redact token pattern: - ```python - # Contoh: token=dummy_token_12345 → token=***REDACTED*** - ``` -- [ ] **Structured Logging**: Gunakan JSON logging dengan field terpisah, jangan log sensitive data di message. -- [ ] **Log Sanitization**: Review semua log statements di codebase, hapus/replace token, password, atau PII lainnya. - ---- - -#### 8.2 Tidak Ada Audit Logging untuk Actions Sensitif -**Dampak Jika Tidak Ditangani:** -- **No Accountability**: Tidak ada track record siapa yang mengubah progress siswa, mengakses report, atau compile code. -- **Forensics Difficulty**: Jika terjadi data breach, sulit trace kapan dan bagaimana terjadi. -- **Insider Threat Blindness**: Tidak bisa deteksi suspicious activity seperti: - - Guru melihat progress siswa dari kelas lain - - Compile activity di jam aneh (3 AM) - - Multiple failed login dari IP yang sama - -**Task Perbaikan**: -- [ ] **Audit Log Table**: Buat tabel/file terpisah untuk log actions sensitif: - - Login/logout events - - Progress updates - - Report exports - - Failed authentication attempts -- [ ] **Log Rotation**: Implementasikan log rotation dan retention policy (misal: 90 hari). -- [ ] **Alerting**: Basic alerting untuk events aneh (contoh: >100 failed logins per jam). - ---- - -### 🟠 9. Input/Output Sanitization (MEDIUM RISK) - -#### 9.1 Potential XSS dari Markdown Rendering -**Lokasi**: `services/lesson_service.py` - Markdown tidak di-sanitize - -**Dampak Jika Tidak Ditangani:** -- **Stored XSS**: Guru yang compromised (atau attacker yang dapat akses edit content) dapat inject: - ```html - - ``` -- **Persistent Attack**: Script akan dijalankan setiap kali siswa membuka lesson tersebut. -- **Session Hijacking**: JavaScript dapat: - - Mencuri cookie session - - Mengirim token ke attacker server - - Mengakses localStorage/sessionStorage - - Melakukan actions atas nama siswa (CSRF via XSS) - -**Task Perbaikan**: -- [ ] **Markdown Sanitization**: Gunakan library seperti `bleach` atau `html-sanitizer` untuk: - - Strip/escape `