Catch the cleartext the moment obfuscated JavaScript unpacks in a real browser.
Evaluator drives Chromium, hooks a JS function you choose (window.eval, String.fromCharCode, JSON.stringify, …), and prints the arguments that pass through that hook — often the deobfuscated payload Magecart-style skimmers hide until runtime.
compromised / demo page
│
▼
obfuscated JS ──packs──▶ window.eval(cleartext)
│ │
│ ▼
│ evaluator hook + optional filter
│ (-s / --regex / --rules)
▼
screenshots + payloads you can read
| You get | It solves |
|---|---|
| Live web UI + screenshot stream | “What did this page actually eval?” |
| Rust CLI (spawns Node evaluate/batch) | Batch / interactive hunts without guessing at packed source |
| Rust MCP tools for agents | Same evaluate path from Cursor / automation |
| Local demo fixtures + Willem rules | Reproduce the VB2021 story on localhost before you touch live shops |
Live: https://evaluator.interchouette.net
| Doc | Description |
|---|---|
CHANGELOG.md |
Release notes |
evaluator/MAGECART.md |
Magecart / Grelos product notes |
evaluator/README.md |
Rust CLI details |
docker/ |
Dockerfile / Compose (Hub overview lives in private .cursor/scripts/DOCKERHUB.md) |
Typically helps with deobfuscating patterns like this Stack Overflow case using String.fromCharCode, window.eval, or other functions like JSON.stringify.
Static scanners (Willem’s rules, VT + YARA) see files on disk. Skimmers often stay opaque until the browser runs them. Evaluator is the complementary runtime tool: after the packer calls eval, you see grelos_v, checkout, gate URLs, and friends in the hook output.
Full product notes (warnings, canary vs demos, batch): evaluator/MAGECART.md — start there for Magecart / Grelos context.
- Jérôme Segura (Malwarebytes), Hunting web skimmers with VirusTotal and YARA, VB2021 — PDF
- Product notes (Magecart / Grelos / Blogspot demo fixtures):
evaluator/MAGECART.md - Astra — signs of hacked OpenCart / Magento / PrestaShop stores (malicious JS): getastra.com article
- Willem de Groot — magento-malware-scanner frontend rules:
rules/frontend.txt(local snapshot:evaluator/rules/frontend.txt)
Canary (hook smoke test — W3Schools eval):
evaluator evaluate \
--url 'https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_eval' \
--fn window.eval
# expect a payload like: x * ySame idea in the browser (production gateway):
https://evaluator.interchouette.net/evaluate/?url=https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_eval&function=window.eval
Research fixtures under evaluator/archive/fixtures/. Serve only on localhost. Do not expose them through Nest, Express, the Docker public image, or any internet-facing route. Details: DEOBFUSCATED.md, MAGECART.md.
| # | Obfuscated | Deobfuscated | What you learn |
|---|---|---|---|
| 1 | demo1shop.html |
demo1shop.deobfuscated.js |
Neutral shop JS; historical _0xd419 hex packer → eval → checkout / cart |
| 2 | demo2grelos.html |
demo2grelos.deobfuscated.js |
Grelos-shaped marker; light teaching packer |
| 3 | demo3grelos.html |
demo3grelos.deobfuscated.js |
Same marker; Magento-era _0x hex-table → eval (demo‑1 packing family) |
cd rust/archive/fixtures && python3 -m http.server 8765
# or: ./smoke-demos.shevaluator evaluate --url http://127.0.0.1:8765/demo1shop.html --fn window.eval -s checkout,cart --excerpt
evaluator evaluate --url http://127.0.0.1:8765/demo2grelos.html --fn window.eval -s grelos_v --excerpt
evaluator evaluate --url http://127.0.0.1:8765/demo3grelos.html --fn window.eval -s grelos_v --excerptFilter live/batch hits with keywords, regex, or Willem’s rules:
evaluator batch -p rust/archive/All-Live-Magento-Sites.csv \
-f window.eval -n 1 \
--rules rust/rules/frontend.txtAuthorized research only. If you confirm a live compromise, report it responsibly to the merchant / hoster.
Architecture
Browser (Angular SPA)
→ Node serve :4000 (Express: static, WS evaluate, GET /evaluate)
→ Nest API :3333 (/api/functions)
→ Chromium via libs/util/puppeteer
Rust CLI / Rust MCP
→ spawn Node evaluate | batch (same entry as serve; no :4000 required)
Image: interchouette/evaluator — web + Chromium + Rust CLI + Rust MCP.
Compose profile: web (ports 4000 + 9790).
| Mode | Behavior |
|---|---|
web / default |
Nest + Node serve :4000; MCP HTTP sidecar if ENABLE_MCP=1 |
ENABLE_MCP=0 |
Web only |
mcp |
evaluator-mcp stdio |
mcp --http |
MCP HTTP only |
evaluator / interactive |
Rust prompt; spawn Node evaluate/batch |
evaluate / batch |
Rust → one Node child; no MCP sidecar |
How the pieces fit the hunt:
| Approach | What it sees | Strength |
|---|---|---|
| Willem frontend/backend rules | File / CMS / static JS | Fast signature match on disk |
| Segura VB2021 (VT + YARA) | Submitted HTML/JS corpus, gates | Hunt infrastructure at scale |
| Evaluator | Live hooked call arguments | Sees post-eval cleartext in a real browser |
API — Quick Start
Use
evaluate/?url=[site url]&function=[function to evaluate]
Example (dev FE on port 4200)
http://localhost:4200/evaluate/?url=https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_eval&function=window.eval
Example (production)
https://evaluator.interchouette.net/evaluate/?url=https://www.w3schools.com/jsref/tryit.asp?filename=tryjsref_eval&function=window.eval
Production / Docker Node gateway listens on port 4000. A screenshot of the website is included in the response stream.
Docker — pull, run, build, CI/CD
Images (primary):
- Docker Hub:
interchouette/evaluator(web + CLI + MCP; Playwright default, orUSE_PUPPETEER=1) - GHCR:
ghcr.io/interchouette-itc/evaluator - Personal GHCR (optional):
ghcr.io/groussac/evaluator
The former Hub mirror gregoshop/evaluator is deprecated and is no longer published. No separate evaluator-base. MCP HTTP is on by default (ENABLE_MCP=0 to disable).
make docker-pull-dev
make docker-runVisit http://localhost:4000/
make docker-build-dev # or make docker-buildOptional local packaging after cd www && npm run build: make docker-build.
CLI / MCP (built into the all-in-one image; CLI does not need web up):
# One-shot evaluate
make docker-run-cli ARGS='evaluate --url https://example.com --fn window.eval'
# CSV batch
make docker-run-cli ARGS='batch -p /data/archive/test.csv -n 1 -f window.eval'
# Interactive: docker run -it --rm --network host interchouette/evaluator:dev evaluator
make docker-run-mcp # stdio (evaluator-mcp)
# MCP HTTP is published on :9790 with make docker-run (ENABLE_MCP=1)| Workflow | Trigger | What |
|---|---|---|
ci.yml |
PR / push to dev |
www/: npm ci + npm run build |
docker-build-push-dev.yml |
manual | monolith :dev + :latest → Hub + GHCR; then Render via RENDER_DEPLOY_HOOK (Hub Overview: python3 .cursor/scripts/sync-hub-description.py) |
release.yml |
GitHub Release vX.Y.Z |
attach host evaluator + evaluator-mcp binaries; push monolith :X.Y.Z + :latest → Hub + GHCR; Render redeploy |
Secret RENDER_DEPLOY_HOOK = full Render Deploy Hook URL (repo secret, not an app env). Without it, Hub still updates; Render stays on the old digests until a manual redeploy.
To publish a release: bump both www/package.json and rust/Cargo.toml to the same X.Y.Z, tag vX.Y.Z, create the GitHub Release. Workflow validates the tag against both versions.
Development — host Node, build, test
- Node.js
>=24on the host (CI/Docker use Node 26; agents must not install another) - npm
>=11 - Docker web image: single
node:26-trixie-slimmulti-stage build with distro Chromium - Evaluate engine: Playwright by default on distro Chromium (
PUPPETEER_EXECUTABLE_PATH); setUSE_PUPPETEER=1for Puppeteer
cd www && npm installcd www && npm startDev UI: http://localhost:4200/ (API proxied; Nest backend on 3333).
cd www && npm run buildArtifacts land in www/dist/. Serve production Node gateway + Nest:
cd www && npm run serveThen open http://localhost:4000/
cd www && npm testRust CLI — host, no Docker required
Same shape as sibling ITC crates (make build / make run / make install):
make build # → rust/target/debug/evaluator (+ evaluator-mcp)
make install # → ~/.cargo/bin (features apps)
make run ARGS='evaluate --url http://127.0.0.1:8765/demo1shop.html --fn window.eval'Needs cd www && npm run build (Node entry at www/dist/evaluator/server/server.js) + Chromium.
| Mode | Command |
|---|---|
| Interactive | make run or evaluator |
| One-shot | evaluator evaluate --url … [--fn …] |
| CSV batch | evaluator batch -p archive/test.csv -f window.eval -n 1 |
See evaluator/README.md for details.
MCP — agent tools + engine advice
Rust MCP (evaluator-mcp, rmcp): tools evaluate, list_functions, and batch (urls[] and/or local CSV path with a Domain column; max 50 URLs). Tools spawn the shared Node entry - no :4000 required. Large CSVs stay on the Rust CLI (evaluator batch -p …).
Prefer MCP HTTP over stdio (stdio pays a Docker spawn cost on cold start).
| Endpoint | Auth | When |
|---|---|---|
http://127.0.0.1:9790/mcp |
none | Local sidecar (Docker -p 9790:9790) |
http://mcp:mcp@127.0.0.1:4000/mcp |
basic MCP_USER/MCP_PWD (defaults mcp/mcp) |
Via Express proxy on the web port |
https://mcp:mcp@evaluator.interchouette.net/mcp |
same boat defaults | Public demo (Render: only web PORT is public; use /mcp, not :9790) |
Defaults are public by design for the shared demo. Override MCP_USER/MCP_PWD only for a private deploy and share creds out-of-band — do not reuse DB_*. Set ENABLE_MCP_PROXY=0 to disable the gateway route.
Cursor HTTP example:
{ "url": "https://mcp:mcp@evaluator.interchouette.net/mcp" }make run-mcp-http # host: evaluator-mcp --http (:9790)
make run-mcp # host: stdio
make docker-run-mcp # image stdioSame page and hook: JSON.stringify on https://cursor.com. Warmup discarded; wall times to gateway /evaluate (what both MCP transports call). Stdio numbers below include a cold docker run --rm per call.
| Combo | Median | Notes |
|---|---|---|
| MCP HTTP + Playwright | ~9.5 s | Recommended default |
| MCP HTTP + Puppeteer | ~11.3 s | Opt in with USE_PUPPETEER=1 |
| MCP stdio + Playwright | ~13.6 s | ~+4 s Docker spawn vs HTTP |
| MCP stdio + Puppeteer | ~15.6 s | Slowest of the four |
Takeaways:
- Prefer Playwright (default; unset or
USE_PUPPETEER=0). - Prefer MCP HTTP over stdio when available — same evaluate work, no spawn tax. Local
:9790or proxiedhttps://mcp:mcp@host/mcp. - Hit counts and screenshots were comparable across engines (~75–84 console hits).
GPL-3.0-or-later — see LICENSE.
If you see typos or errors, open a pull request on the default working branch. Thanks.

