A Chrome extension that detects and blocks malicious JavaScript in real time using an Explainable Boosting Machine (EBM) — an intrinsically interpretable ML model that produces human-readable explanations alongside every decision.
Live backend: https://xai-malwarejs-backend.onrender.com
Every script and network request intercepted by the extension is sent to a two-stage pipeline:
[Web Page JS]
│
▼
[injected_hook.js] — overrides eval(), Function(), fetch(), XHR, setTimeout in page context
│ window.postMessage()
▼
[interceptor.js] — content script, relays messages out of page context
│ chrome.runtime.sendMessage()
▼
[service_worker.js] — applies local heuristic pre-filter; sends to backend if needed
│ POST /analyze
▼
[Flask backend] — EBM classifier → local explanation (both in < 10 ms)
│ { decision, score, top_features, explanation }
▼
[Popup / banner] — shows verdict, risk score, and top contributing signals
Stage 1 — Fast pre-filter: Scripts with no suspicious signals are allowed instantly without hitting the backend.
Stage 2 — EBM classifier: The remaining scripts are scored by an Explainable Boosting Machine. EBM is intrinsically interpretable — it produces exact feature contributions (not post-hoc approximations) with negligible extra cost.
Network requests (fetch / XHR) use a separate rule-based path that checks for credential exfiltration patterns (password fields, auth tokens, cookie values in cross-origin POST bodies).
| Feature | Description |
|---|---|
| Real-time blocking | Intercepts eval, Function(), fetch, XHR, setTimeout before execution |
| XAI explanations | EBM feature contributions shown per detection — no black box |
| Heuristic fallback | Works offline; backend is optional |
| Network analysis | Detects credential exfiltration in outbound requests |
| Live popup UI | Risk gauge, top signals, full feature grid, intercept feed |
| Configurable | Threshold slider, block vs. flag mode, notifications |
| Export | Download full intercept log as JSON |
XAI-MalwareJs/
│
├── extension/ ← Load this folder into Chrome
│ ├── manifest.json ← Manifest V3
│ ├── icons/
│ ├── content/
│ │ ├── injected_hook.js ← Runs in page context (MAIN world)
│ │ └── interceptor.js ← Runs in extension context; relays messages
│ ├── background/
│ │ └── service_worker.js ← Heuristic scoring + backend calls + log storage
│ └── popup/ ← Built output of ui/ — do not edit directly
│
├── ui/ ← Popup source (React + TypeScript + Tailwind)
│ ├── src/
│ │ ├── App.tsx
│ │ ├── components/ ← StatusHero, Feed, DetailPanel, Settings, …
│ │ └── lib/bridge.ts ← chrome.runtime messaging (+ mock for npm run dev)
│ └── vite.config.ts
│
├── backend/
│ ├── app.py ← Flask API (EBM classifier + local explanation)
│ ├── requirements.txt
│ ├── XAI_New_2026.ipynb ← Model training notebook
│ └── models/
│ ├── ebm_model.pkl ← Trained EBM (interpret library)
│ └── feature_columns.pkl ← Feature order used at training time
│
└── make_icons.py ← One-time icon generation
The extension works immediately with the hosted backend — no local setup required.
- Open Chrome →
chrome://extensions - Enable Developer mode (top-right toggle)
- Click Load unpacked → select the
extension/folder - The shield icon appears in your toolbar
The extension works without the backend. put https://xai-malwarejs-backend.onrender.com into .env file
To get proper ML + SHAP explanations:
cd backend
pip install -r requirements.txt
python app.pyThen set the backend URL in the extension Settings to http://localhost:5000.
cd ui
npm install
npm run build # outputs to extension/popup/For fast iteration outside Chrome:
npm run dev # serves popup with mock data at http://localhost:5173/popup.htmlBase URL: https://xai-malwarejs-backend.onrender.com
{ "status": "ok", "model": true }Accepts a feature payload extracted by the extension. Returns a decision, risk score, and explanation.
curl -X POST https://xai-malwarejs-backend.onrender.com/analyze \
-H "Content-Type: application/json" \
-d '{
"source": "eval",
"url": "https://example.com",
"eval_count": 3,
"entropy": 6.8,
"atob_count": 1,
"fromcharcode_count": 5,
"hex_escape_count": 45,
"code_length": 1200,
"line_count": 3
}'Response:
{
"decision": "BLOCK",
"score": 0.91,
"top_features": [["eval() calls", 3], ["Code entropy (obfuscation signal)", 6.8]],
"explanation": "Risk score: 91%. Primary signals: eval() calls, Code entropy. Script classified as malicious.",
"stage": "ebm-classifier",
"classify_ms": 4.2
}Returns the last N intercepts (default 100). Add ?limit=N to change.
{ "total": 412, "blocked": 38, "allowed": 374, "block_rate": 0.092, "avg_score": 0.14 }Submit a correction label for a logged intercept.
The model uses 10 features extracted from each intercepted script:
| Feature | Description |
|---|---|
length |
Total character count of the script |
num_lines |
Number of lines |
entropy |
Shannon entropy — high values indicate obfuscation |
eval_count |
eval() calls |
function_count |
new Function() constructor calls |
atob_count |
atob() base64-decode calls |
fromcharcode_count |
String.fromCharCode() calls |
hex_escape_count |
\x41-style hex escape sequences |
long_var_names |
Long identifier / string patterns (obfuscation proxy) |
url_count |
Embedded URLs in code |
The model was trained and exported in backend/XAI_New_2026.ipynb.
Open backend/XAI_New_2026.ipynb in Jupyter and run all cells. The notebook trains an EBM on the labelled dataset and exports ebm_model.pkl and feature_columns.pkl to backend/models/.
Extension not intercepting anything
- Go to
chrome://extensions→ check for errors on the XAI card - Open DevTools on any page → Console → look for
[XAI Hook] Installed - Some pages with strict CSP block script injection — the test page has no CSP
Popup shows "heuristic mode"
- This is normal when the backend is cold-starting on Render (free tier sleeps after inactivity) — it wakes up within ~30 seconds
- Or set a local backend URL in Settings
Too many false positives
- Raise the detection threshold in Settings (e.g. 70%)
- Switch to "Flag" mode (observe without blocking)
Contributions are welcome. Please open an issue before submitting a pull request for significant changes.
- Fork the repo
- Create a feature branch (
git checkout -b feature/your-feature) - Commit your changes
- Push and open a pull request
MIT — see LICENSE.