Ekstensi browser Chromium untuk mendeteksi dan menyoroti konten judi online (judol) pada halaman web mana pun. Proyek ini dibuat untuk memenuhi Tugas Besar 3 IF2211 Strategi Algoritma.
Promosi judi online ("situs judol") sering muncul tersisip di dalam halaman web di Indonesia sebagai teks, gambar banner, atau disisipkan melalui jaringan iklan pihak ketiga. Ekstensi ini memindai setiap halaman yang dimuat browser yang kemudian:
- Mendeteksi kata kunci dan frasa terkait judi dalam teks yang terlihat,
- Menyorot kecocokan yang terdeteksi langsung di DOM halaman,
- Memblur paragraf yang berisi konten yang terdeteksi,
- Menjalankan OCR (Optical Character Recognition) pada elemen gambar untuk menangkap teks yang tertanam dalam grafik serta menggantinya dengan gambar penutup, dan
- Melaporkan statistik berupa jumlah kecocokan, frekuensi kata kunci, dan kinerja per algoritma dalam UI pop-up.
Ekstensi ini berjalan sepenuhnya secara lokal di sisi klien.
Untuk membangun dan menjalankan ekstensi secara lokal, pastikan dependensi berikut terinstal:
- Node.js (v18+)
- npm (v8+)
- Browser Chromium (Chrome, Edge, Brave, dll.)
- Di root proyek, jalankan perintah berikut:
# Instalasi dependensi
npm install
# Build ketiga bundle secara berurutan
npm run build- Setelah build selesai, buka browser Chromium dan navigasikan ke
chrome://extensions/atau pengaturan ekstensi di browser lainnya. - Pastikan "Developer mode" diaktifkan pada browser.
- Klik "Load unpacked" dan pilih folder
dist/yang baru saja dibangun. Ekstensi akan dimuat ke browser dan siap digunakan.
| Fitur | Deskripsi |
|---|---|
| Scan | Menjalankan pemindaian DOM penuh pada tab aktif saat ini. Tombol menampilkan "Scanning..." dan dinonaktifkan selama pemindaian; direset saat statistik tiba atau saat terjadi error. |
| Blur Teks | Menambahkan filter blur CSS pada container induk dari setiap elemen yang disorot, mengaburkan konten judi sembari tetap memungkinkan halaman dinavigasi. |
| OCR Gambar | Mengaktifkan pemindaian Tesseract.js pada elemen <img> dan gambar CSS background. Gambar judol yang terdeteksi diganti dengan grafik peringatan (judolss.jpeg). |
| Tooltip | Saat hover di atas highlight <mark>, menampilkan: keyword yang terdeteksi, algoritma yang menemukannya, jumlah kemunculan, dan waktu eksekusi (ms). |
| Pemilihan Algoritma | Checkbox untuk masing-masing AC, KMP, BM, Rabin-Karp, dan Weighted-Levenshtein. Perubahan tersimpan lintas sesi browser dan berlaku pada pemindaian berikutnya. RegEx selalu berjalan tanpa kondisi. |
| Dashboard Statistik | Jumlah total kecocokan, keyword unik yang ditemukan, dan elemen DOM yang disorot. Bar chart untuk 10 keyword teratas berdasarkan frekuensi dan distribusi kecocokan per algoritma. Rincian tabular jumlah kecocokan dan total waktu eksekusi per algoritma. |
| Auto Rescan | MutationObserver memantau halaman untuk perubahan DOM (di-debounce 2.000 ms) dan secara otomatis menjalankan ulang deteksi. Berguna untuk single-page application yang memperbarui konten secara dinamis tanpa navigasi penuh. |
| Scan Timestamp | Header popup menampilkan timestamp relatif ("Baru saja dipindai", "5m lalu") yang dihitung dari timestamp pemindaian yang tersimpan. |
.
├── README.md
├── keywords
│ └── keywords.txt
├── package.json
├── package-lock.json
├── public
│ ├── icons
│ └── manifest.json
├── src
│ ├── algorithms
│ │ ├── ahoCorasick.ts
│ │ ├── boyerMoore.ts
│ │ ├── index.ts
│ │ ├── kmp.ts
│ │ ├── rabinKarp.ts
│ │ ├── regexMatcher.ts
│ │ └── weightedLevenshtein.ts
│ ├── content
│ │ ├── blurManager.ts
│ │ ├── contentScript.ts
│ │ ├── domScanner.ts
│ │ ├── highlighter.ts
│ │ ├── imageReplacer.ts
│ │ ├── ocrScanner.ts
│ │ ├── rescanManager.ts
│ │ └── tooltip.ts
│ ├── core
│ │ ├── detectionEngine.ts
│ │ ├── messageBus.ts
│ │ └── storage.ts
│ ├── popup
│ │ ├── chart.ts
│ │ ├── popup.html
│ │ └── popup.ts
│ ├── styles
│ │ ├── content.css
│ │ ├── judolss.jpeg
│ │ └── popup.css
│ ├── types
│ │ ├── detection.ts
│ │ └── stats.ts
│ ├── utils
│ │ ├── keywordLoader.ts
│ │ ├── textNormalizer.ts
│ │ └── timer.ts
│ ├── main.ts
│ └── vite-env.d.ts
├── test
├── tsconfig.json
└── vite.config.ts
File: src/algorithms/kmp.ts
Pencarian string eksak, single-pattern, dengan kompleksitas linear. Menggunakan tabel LPS (Longest Proper Prefix which is also a Suffix) yang telah dihitung sebelumnya untuk menghindari perbandingan karakter yang redundan.
| Properti | Nilai |
|---|---|
| Preprocessing |
|
| Pencarian |
|
| Cache |
_lpsCache: Map<string, number[]> — dibangun sekali saat startup melalui initKMPCache()
|
| Overlap | Didukung — saat cocok, kembali ke lps[j-1] alih-alih mereset ke 0 |
Array LPS lps[i] menyimpan panjang prefix terpanjang dari pattern[0..i] yang sekaligus merupakan suffix. Ketika terjadi mismatch pada posisi j di pattern, pencarian dilanjutkan dari lps[j-1] alih-alih mengulang dari awal, sehingga memberikan jaminan kompleksitas linear.
File: src/algorithms/boyerMoore.ts
Pencarian eksak single-pattern yang memindai pattern dari kanan ke kiri dan menggunakan bad character table untuk melompati sebagian besar teks saat terjadi mismatch.
| Properti | Nilai |
|---|---|
| Preprocessing |
|
| Pencarian |
|
| Cache |
_badCharCache: Map<string, Map<string, number>> — dibangun sekali melalui initBMCache()
|
Saat terjadi mismatch pada posisi teks s + j, pattern digeser ke kanan sebesar max(1, j - badChar[text[s+j]]). Jika text[s+j] tidak muncul di pattern, pergeseran menjadi j + 1, melewatkan seluruh jendela saat ini.
File: src/algorithms/ahoCorasick.ts
Pencarian eksak multi-pattern yang menemukan semua keyword dalam satu pass
| Properti | Nilai |
|---|---|
| Preprocessing |
|
| Pencarian |
|
| Cache |
_cachedAutomaton: TrieNode — dibangun sekali melalui initACCache()
|
Failure link dihitung bottom-up melalui BFS: untuk node u yang dicapai melalui karakter c dari parent p, failure dari u adalah node yang dicapai dengan mengikuti rantai failure p hingga ditemukan node dengan edge c (atau root). Output link mempercepat pengumpulan semua keyword yang cocok pada setiap posisi dengan menghubungkan ke ancestor terdekat yang memiliki output keyword.
Automaton ini menjadi filter utama dalam pipeline deteksi. KMP dan Boyer-Moore kemudian mengonfirmasi ulang hanya subset keyword yang ditemukan automaton, yang jauh lebih kecil dari daftar penuh pada halaman yang khas.
File: src/algorithms/rabinKarp.ts
Pencarian eksak multi-pattern menggunakan polynomial rolling hash. Keyword dikelompokkan berdasarkan panjang; untuk setiap kelompok panjang, rolling hash pada jendela teks dipertahankan dan dibandingkan dengan himpunan hash keyword yang telah dihitung sebelumnya.
| Properti | Nilai |
|---|---|
| Hash |
|
| Rolling update | |
| Collision | Diverifikasi karakter per karakter saat hash cocok |
| Cache |
_rkIndex: RKIndex — dibangun sekali melalui initRKCache()
|
Aritmetika bigint digunakan di seluruh implementasi untuk menghindari overflow diam-diam pada floating-point 64-bit JavaScript untuk nilai hash yang besar.
File: src/algorithms/regexMatcher.ts
Pola tetap /[a-z]+\d{2,}/g mendeteksi token alias terkait judi yang menyisipkan angka ke dalam nama situs (misalnya, gacor99, maxwin234, slot777). Ini menangkap nama merek yang dibuat secara dinamis yang tidak dapat dienumerasi dalam daftar keyword.
File: src/algorithms/weightedLevenshtein.ts
Pencocokan string aproksimasi berbasis sliding window dengan tabel biaya substitusi yang sadar leet-speak.
| Properti | Nilai |
|---|---|
| Ukuran window yang dicoba |
|
| Ambang batas kesamaan | 0.75 — cocok dengan |
| Pasangan biaya rendah |
o↔0, a↔4, i↔1, e↔3, s↔5 (biaya 0.5 alih-alih 1.0) |
| Early exit | Jika nilai minimum baris dalam tabel DP melebihi maxDist, loop dihentikan lebih awal |
| Panjang keyword minimum | 4 karakter (keyword lebih pendek menghasilkan terlalu banyak false positive) |
| Batas panjang teks | 300 karakter (sliding window DP adalah |
Rekurensi DP adalah:
di mana
Setelah semua window diberi skor, hasil yang tumpang tindih didedup dengan mempertahankan kecocokan dengan kesamaan tertinggi per kelompok yang overlap.
Mengorkestrasikan pipeline deteksi lengkap untuk satu text node:
- Mengubah teks menjadi lowercase untuk exact matching (KMP, BM, AC, RK menjaga posisi karakter).
- Melakukan normalisasi penuh teks (
normalizeText) khusus untuk pass Levenshtein. - Menjalankan Aho-Corasick terlebih dahulu; mencatat keyword yang ditemukannya.
- Menjalankan KMP dan Boyer-Moore hanya atas subset keyword yang ditemukan AC.
- Menjalankan Rabin-Karp atas semua keyword (pendekatan hash independen).
- Selalu menjalankan regex matcher.
- Menjalankan fuzzy matching terakhir, dibatasi untuk keyword yang belum ditemukan oleh metode eksak dan teks ≤ 300 karakter.
Menelusuri DOM dengan document.createTreeWalker(root, NodeFilter.SHOW_TEXT) dan menyaring elemen:
- Elemen dengan tag yang ada dalam deny-list (
script,style,noscript,textarea,input,select,iframe,code,pre,svg,canvas,video,audio, dll.). - Elemen yang sudah memiliki class
judol-detector-*(mencegah pemindaian markup yang di-inject oleh ekstensi). - Elemen tersembunyi, yang dideteksi melalui API native
checkVisibility({ checkOpacity: true, checkVisibilityCSS: true })(Chrome 105+) dengan fallbackgetComputedStylerekursif.
Mengganti Text node yang cocok dengan DocumentFragment yang terdiri dari:
- Node
Textbiasa untuk segmen yang tidak cocok. - Elemen
<mark class="judol-detector-highlight">untuk span yang cocok.
Kecocokan yang tumpang tindih dalam text node yang sama digabung menjadi satu <mark> sebelum penggantian DOM. Setiap <mark> menyimpan metadata kaya sebagai atribut data-* (data-keyword, data-algorithm, data-count, data-execution-time-ms, data-matched-text) yang dikonsumsi oleh tooltip.
Menggunakan Tesseract.js dengan model bahasa ind (Indonesia) dan eng (Inggris). Langkah pemrosesan:
- Mengumpulkan elemen
<img>yang terlihat dengan dimensi natural ≥ 50 px; dibatasi 15 gambar per pemindaian. - Mengumpulkan elemen dengan
background-imageCSS; ukuran render minimum 100 px; dibatasi 10 per pemindaian. - Mencoba menggambar canvas secara langsung untuk gambar same-origin. Saat gagal CORS, meneruskan URL ke background service worker yang mengambilnya dan mengembalikan data URL base64.
- Memasukkan data URL ke Tesseract worker; menjalankan
runDetectionpada teks yang diekstrak. - Jika ditemukan kecocokan, mengganti sumber gambar dengan grafik peringatan
judolss.jpegyang dibundel.
Tesseract worker diinisialisasi secara lazy pada permintaan OCR pertama dan digunakan kembali pada pemindaian berikutnya.
Pembungkus tipis di atas chrome.storage.local dengan fallback in-memory berbasis Map untuk lingkungan non-ekstensi (unit test). Menyimpan:
| Key | Konten |
|---|---|
JUDOL_DETECTOR_RESULTS |
MatchResult[] dari pemindaian terakhir |
JUDOL_DETECTOR_STATS |
DetectionStats |
JUDOL_DETECTOR_LAST_SCAN |
Unix timestamp (ms) |
JUDOL_DETECTOR_SETTINGS |
DetectionSettings (blur, OCR, tooltip) |
JUDOL_DETECTOR_ALGO_PREFS |
AlgoPrefs |
normalizeText(text):
trim → lowercase → ratakan whitespace → buang karakter non-printable
Digunakan secara eksklusif untuk pass fuzzy Levenshtein. Algoritma exact-match hanya menerima versi lowercase agar posisi karakter tetap akurat untuk perhitungan offset highlight.
createTimer() → { elapsed(): number } // menggunakan performance.now()
measureTime(fn) → { result, elapsedMs } // membungkus fungsi sinkronSetiap algoritma menerima timer saat masuk dan mencap executionTimeMs pada setiap MatchResult yang dikembalikan.
Kelompok RP17845
| Nama | NIM | Handle |
|---|---|---|
| Renuno Yuqa Frinardi | 13524080 | @renuno-frinardi |
| Ariel Cornelius Sitorus | 13524085 | @Sakazu01 |
| Muhammad Akmal | 13524099 | @m-akma1 |
IF2211 Strategi Algoritma - Tugas Besar 3
Semester II Tahun Akademik 2025/2026
Program Studi Teknik Informatika
Sekolah Teknik Elektro dan Informatika
Institut Teknologi Bandung