diff --git a/docs/06-embed-content.md b/docs/06-embed-content.md new file mode 100644 index 0000000..40a3344 --- /dev/null +++ b/docs/06-embed-content.md @@ -0,0 +1,187 @@ +# Fitur Embed Konten di Materi Markdown & Slide + +**Tanggal:** 2026-07-19 +**Status:** Implementasi selesai +**Lokasi kode:** Backend `services/lesson_service.py` + Frontend (CSS only) + +--- + +## 1. Latar Belakang + +Elemes memerlukan fitur agar author materi dapat menyisipkan konten *embedded* (iframe dari platform luar) langsung dari markdown — baik di tubuh materi maupun di dalam slide presentasi. Contoh penggunaan: video YouTube, desain Canva, Google Docs, Figma, widget Scratch, dll. + +### Infrastruktur yang Sudah Ada + +Elemes **sudah punya** pipeline markdown→embed untuk `circuit` dan `flowchart`: +- **Backend** (`services/lesson_service.py`): regex fence ```circuit``` → `
`, lalu `md.markdown()` render jadi HTML. +- **Frontend** (`src/lib/actions/render*Embeds.ts`): `IntersectionObserver` ganti div → ` +
+Judul by Author +``` +```` + +**Kelebihan:** +- User kontrol penuh (aspect ratio, style, link credit) — embed code dari platform resmi sudah optimize. +- Support Canva, YouTube, Google Docs, Figma, Scratch, dll sekaligus — tanpa hardcode transform per-platform. +- Lebih fleksibel: author bisa kustomisasi wrapper, caption, dll. + +**Konsekuensi keamanan:** Raw HTML = potensi XSS. Wajib **sanitize** sebelum render. Tanpa sanitize, author bisa sisipkan ` +```""" + out = _process_embed_embeds(md) + # Script tags are stripped by bleach; inner text remains but is harmless + assert '' not in out.lower() + + +def test_embed_strips_onclick(): + md = """```embed +
+```""" + out = _process_embed_embeds(md) + assert 'onclick' not in out + assert 'alert' not in out + + +def test_embed_blocked_domain(): + md = """```embed + +```""" + out = _process_embed_embeds(md) + assert 'embed-error' in out + + +def test_embed_non_https_iframe(): + md = """```embed + +```""" + out = _process_embed_embeds(md) + assert 'embed-error' in out + + +def test_embed_empty_rejected(): + md = """```embed + +```""" + out = _process_embed_embeds(md) + assert 'embed-error' in out + assert 'kosong' in out + + +def test_embed_no_embed_unchanged(): + md = "# Heading\n\nparagraf biasa" + assert _process_embed_embeds(md) == md + + +def test_embed_youtube_html(): + md = """```embed + +```""" + out = _process_embed_embeds(md) + assert 'youtube.com' in out + assert ' +```""" + out = _process_embed_embeds(md) + assert 'javascript' not in out.lower()