Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ dist
tmp
out-tsc
coverage
db/
database.db
.env
.env.*
Expand All @@ -30,7 +31,6 @@ evaluator/target

# Local tooling / docs not needed in image
*.md
!README.md
Makefile
.dockerignore
docker/*.md
Expand Down
9 changes: 0 additions & 9 deletions .github/workflows/docker-build-push-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,15 +54,6 @@ jobs:
echo
echo "Render deploy requested"

- name: Docker Hub description
uses: peter-evans/dockerhub-description@v5
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
repository: interchouette/evaluator
short-description: evaluator.interchouette.net — JS eval (Playwright / Puppeteer), web + CLI + MCP
readme-filepath: docker/DOCKERHUB.md

- name: Log in to GHCR
uses: docker/login-action@v4
with:
Expand Down
9 changes: 0 additions & 9 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -110,15 +110,6 @@ jobs:
echo
echo "Render deploy requested"

- name: Docker Hub description
uses: peter-evans/dockerhub-description@v5
with:
username: ${{ secrets.DOCKER_USERNAME }}
password: ${{ secrets.DOCKER_PASSWORD }}
repository: interchouette/evaluator
short-description: evaluator.interchouette.net — JS eval (Playwright / Puppeteer), web + CLI + MCP
readme-filepath: docker/DOCKERHUB.md

- name: Log in to GHCR
uses: docker/login-action@v4
with:
Expand Down
4 changes: 1 addition & 3 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,10 @@ testem.log
Thumbs.db

# runtime / local data
/db/
database.db
/data/

*/target/
.github_credentials
.cursor

# local env (never commit)
.env
Expand Down
11 changes: 10 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
.PHONY: help \
build build-release check check-lib install clean run run-mcp run-mcp-http \
build build-release check check-lib install clean run run-batch-capped run-mcp run-mcp-http \
docker-build docker-build-no-cache docker-build-dev \
docker-push-dev docker-push-dev-hub docker-push-dev-ghcr-personal docker-push-dev-ghcr-itc \
docker-push-release docker-push-release-hub \
Expand Down Expand Up @@ -41,6 +41,8 @@ help:
@echo " make install cargo install --path evaluator --features apps"
@echo " make run ARGS='…' cargo run --bin evaluator --features apps -- …"
@echo " e.g. make run ARGS='evaluate --url http://127.0.0.1:8765/demo1shop.html --fn window.eval'"
@echo " make run-batch-capped same as run, wrapped in systemd --scope MemoryMax=$(MEMORY_MAX)"
@echo " e.g. make run-batch-capped ARGS='batch -p archive/test.csv -f window.eval'"
@echo " make run-mcp cargo run --bin evaluator-mcp (stdio)"
@echo " make run-mcp-http cargo run --bin evaluator-mcp -- --http"
@echo " (needs: npm run build + Chromium / PUPPETEER_EXECUTABLE_PATH)"
Expand Down Expand Up @@ -82,6 +84,13 @@ clean:
run:
cd evaluator && $(CARGO) run $(CARGO_FLAGS) --bin $(CLI_BIN) -- $(if $(strip $(ARGS)),$(ARGS),--help)

# Cap batch RAM so the OOM killer hits the job scope, not Cursor/desktop.
MEMORY_MAX ?= 4G
run-batch-capped:
@test -n "$(strip $(ARGS))" || (echo 'usage: make run-batch-capped ARGS="batch -p …"' >&2; exit 2)
systemd-run --user --scope -p MemoryMax=$(MEMORY_MAX) --quiet -- \
$(MAKE) run ARGS='$(ARGS)'

run-mcp:
cd evaluator && $(CARGO) run $(CARGO_FLAGS) --bin evaluator-mcp -- $(ARGS)

Expand Down
12 changes: 8 additions & 4 deletions apps/evaluator/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,10 @@ export function createApp(): express.Express {

app.use('/db', auth);
app.get('/db/database.db', (_req: Request, res: Response) => {
res.sendFile(join(process.cwd(), 'database.db'), {
const sqlitePath =
process.env['SQLITE_PATH']?.trim() ||
join(process.cwd(), 'db', 'database.db');
res.sendFile(sqlitePath, {
headers: { 'Cache-Control': 'no-cache' },
});
});
Expand Down Expand Up @@ -219,20 +222,21 @@ async function runBatchCli(argv: string[]): Promise<void> {
process.exit(2);
}
void concurrency; // one browser, sequential (plan)
const outcomes = await runBatch({
// Never take/emit screenshots in batch — base64 PNGs OOM the host.
const count = await runBatch({
urls,
fn,
screenshot: false,
onSite: (site, outcome) => {
console.log(
JSON.stringify({
url: site,
results: outcome.results,
screenshot: outcome.screenshot,
})
);
},
});
console.error(`[evaluate] batch done count=${outcomes.length}`);
console.error(`[evaluate] batch done count=${count}`);
}

async function main(): Promise<void> {
Expand Down
106 changes: 0 additions & 106 deletions docker/DOCKERHUB.md

This file was deleted.

8 changes: 4 additions & 4 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -51,17 +51,17 @@ RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
ca-certificates \
&& groupadd --system --gid 1001 app \
&& useradd --system --uid 1001 --gid app --create-home --home-dir /home/app app \
&& mkdir -p /app/data \
&& chown -R app:app /app/data
&& mkdir -p /app/db \
&& chown -R app:app /app/db

ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true \
PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium \
PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1 \
USE_PUPPETEER=0 \
PORT=4000 \
NODE_ENV=production \
SQLITE_PATH=/app/data/database.db \
FUNCTIONS_PATH=/app/data/functions.json \
SQLITE_PATH=/app/db/database.db \
FUNCTIONS_PATH=/app/db/functions.json \
ENABLE_MCP=1 \
EVALUATOR_MCP_ADDR=0.0.0.0:9790 \
EVALUATOR_NODE_ENTRY=/app/dist/evaluator/server/server.js
Expand Down
6 changes: 3 additions & 3 deletions docker/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ services:
- PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium
- PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1
- USE_PUPPETEER=0
- SQLITE_PATH=/app/data/database.db
- FUNCTIONS_PATH=/app/data/functions.json
- SQLITE_PATH=/app/db/database.db
- FUNCTIONS_PATH=/app/db/functions.json
volumes:
- evaluator-data:/app/data
- evaluator-data:/app/db
- ../evaluator:/data:ro

volumes:
Expand Down
44 changes: 44 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Changelog

All notable changes to this project are documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Changed

- Docs live under [`docs/`](.) (`README.md`, this changelog); Docker Hub Overview maintained privately in `.cursor/scripts/DOCKERHUB.md`
- Runtime SQLite default path is `db/database.db` (Docker: `/app/db/database.db`)

## [1.0.0] - 2026-08

First Interchouette / ITC packaging line for the modernized monorepo (`package.json` / `evaluator/Cargo.toml` `1.0.0`).

### Added

- All-in-one Docker image `interchouette/evaluator` (Angular SPA + Nest API + Node serve/evaluate/batch + Chromium + Rust CLI + Rust MCP)
- Rust CLI (`evaluator`): interactive prompt, one-shot `evaluate`, CSV `batch`
- Rust MCP (`evaluator-mcp`, mcpkit): tools `evaluate`, `list_functions`, `batch` (stdio / Streamable HTTP)
- Express `/mcp` proxy with dedicated basic auth (`MCP_USER` / `MCP_PWD`); public demo boat defaults
- Playwright as default evaluate engine; `USE_PUPPETEER=1` to opt into Puppeteer
- Magecart / Grelos teaching fixtures (`demo1`–`demo3`) and product notes
- Hooked-function payload filters: keywords, regex, Willem frontend rules; `--excerpt` snippets
- Host Make targets for Rust CLI/MCP (`make build` / `run` / `install` / `run-mcp` / `run-mcp-http`)
- CI: `ci.yml`, manual `docker-build-push-dev.yml`, GitHub Release `release.yml` (Hub + GHCR; attach CLI/MCP binaries; optional Render deploy hook)
- GPL-3.0-or-later `LICENSE`

### Changed

- Default MCP HTTP port **9790** (was 9788, earlier 8788)
- Dropped separate `evaluator-base` / `evaluator-tools` images; one monolith image
- Node 24+ / Actions majors refreshed; Docker Rust builder 1.88; Node image `node:26-trixie-slim`
- Live deployment pointed at [evaluator.interchouette.net](https://evaluator.interchouette.net)

### Removed

- Public repo `docker/DOCKERHUB.md` (Hub description synced from private `.cursor/scripts`)

[Unreleased]: https://github.com/Interchouette-ITC/evaluator/compare/v1.0.0...HEAD
[1.0.0]: https://github.com/Interchouette-ITC/evaluator/releases/tag/v1.0.0
31 changes: 21 additions & 10 deletions README.md → docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,17 @@ compromised / demo page

**Live:** [https://evaluator.interchouette.net](https://evaluator.interchouette.net)


## Docs

| Doc | Description |
| --- | --- |
| [`CHANGELOG.md`](CHANGELOG.md) | Release notes |
| [`evaluator/MAGECART.md`](../evaluator/MAGECART.md) | Magecart / Grelos product notes |
| [`evaluator/README.md`](../evaluator/README.md) | Rust CLI details |
| [`docker/`](../docker/) | Dockerfile / Compose (Hub overview lives in private `.cursor/scripts/DOCKERHUB.md`) |


Typically helps with deobfuscating patterns like [this Stack Overflow case](https://stackoverflow.com/questions/32977908/how-can-i-deobfuscate-this-javascript) using `String.fromCharCode`, `window.eval`, or other functions like `JSON.stringify`.

---
Expand All @@ -34,14 +45,14 @@ Typically helps with deobfuscating patterns like [this Stack Overflow case](http

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`](evaluator/MAGECART.md)** — start there for Magecart / Grelos context.
Full product notes (warnings, canary vs demos, batch): **[`evaluator/MAGECART.md`](../evaluator/MAGECART.md)** — start there for Magecart / Grelos context.

### References

- Jérôme Segura (Malwarebytes), *Hunting web skimmers with VirusTotal and YARA*, VB2021 — [PDF](https://vblocalhost.com/uploads/VB2021-Segura.pdf)
- Product notes (Magecart / Grelos / Blogspot demo fixtures): [`evaluator/MAGECART.md`](evaluator/MAGECART.md)
- Product notes (Magecart / Grelos / Blogspot demo fixtures): [`evaluator/MAGECART.md`](../evaluator/MAGECART.md)
- Astra — signs of hacked OpenCart / Magento / PrestaShop stores (malicious JS): [getastra.com article](https://www.getastra.com/e/malware/infections/the-presence-of-these-malicious-javascript-are-the-sign-of-hacked-opencart-magento-or-prestashop-store)
- Willem de Groot — magento-malware-scanner frontend rules: [`rules/frontend.txt`](https://github.com/gwillem/magento-malware-scanner/blob/master/rules/frontend.txt) (local snapshot: [`evaluator/rules/frontend.txt`](evaluator/rules/frontend.txt))
- Willem de Groot — magento-malware-scanner frontend rules: [`rules/frontend.txt`](https://github.com/gwillem/magento-malware-scanner/blob/master/rules/frontend.txt) (local snapshot: [`evaluator/rules/frontend.txt`](../evaluator/rules/frontend.txt))

<details open>
<summary><strong>Screenshots</strong> — web UI in action</summary>
Expand Down Expand Up @@ -75,13 +86,13 @@ https://evaluator.interchouette.net/evaluate/?url=https://www.w3schools.com/jsre

## Demos — illustrate the PDF story on localhost

Research fixtures under [`evaluator/archive/fixtures/`](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`](evaluator/archive/fixtures/DEOBFUSCATED.md), [`MAGECART.md`](evaluator/MAGECART.md).
Research fixtures under [`evaluator/archive/fixtures/`](../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`](../evaluator/archive/fixtures/DEOBFUSCATED.md), [`MAGECART.md`](../evaluator/MAGECART.md).

| # | Obfuscated | Deobfuscated | What you learn |
| --- | --- | --- | --- |
| **1** | [`demo1shop.html`](evaluator/archive/fixtures/demo1shop.html) | [`demo1shop.deobfuscated.js`](evaluator/archive/fixtures/demo1shop.deobfuscated.js) | Neutral shop JS; historical `_0xd419` hex packer → `eval` → `checkout` / `cart` |
| **2** | [`demo2grelos.html`](evaluator/archive/fixtures/demo2grelos.html) | [`demo2grelos.deobfuscated.js`](evaluator/archive/fixtures/demo2grelos.deobfuscated.js) | Grelos-shaped marker; light teaching packer |
| **3** | [`demo3grelos.html`](evaluator/archive/fixtures/demo3grelos.html) | [`demo3grelos.deobfuscated.js`](evaluator/archive/fixtures/demo3grelos.deobfuscated.js) | Same marker; Magento-era `_0x` hex-table → `eval` (demo‑1 packing family) |
| **1** | [`demo1shop.html`](../evaluator/archive/fixtures/demo1shop.html) | [`demo1shop.deobfuscated.js`](../evaluator/archive/fixtures/demo1shop.deobfuscated.js) | Neutral shop JS; historical `_0xd419` hex packer → `eval` → `checkout` / `cart` |
| **2** | [`demo2grelos.html`](../evaluator/archive/fixtures/demo2grelos.html) | [`demo2grelos.deobfuscated.js`](../evaluator/archive/fixtures/demo2grelos.deobfuscated.js) | Grelos-shaped marker; light teaching packer |
| **3** | [`demo3grelos.html`](../evaluator/archive/fixtures/demo3grelos.html) | [`demo3grelos.deobfuscated.js`](../evaluator/archive/fixtures/demo3grelos.deobfuscated.js) | Same marker; Magento-era `_0x` hex-table → `eval` (demo‑1 packing family) |

```bash
cd evaluator/archive/fixtures && python3 -m http.server 8765
Expand Down Expand Up @@ -212,7 +223,7 @@ make docker-run-mcp # stdio (evaluator-mcp)
| Workflow | Trigger | What |
| --- | --- | --- |
| `ci.yml` | PR / push to `dev` | `npm ci` + `npm run build` |
| `docker-build-push-dev.yml` | manual | monolith `:dev` + `:latest` → Hub + GHCR; then Render via `RENDER_DEPLOY_HOOK` |
| `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.
Expand Down Expand Up @@ -284,7 +295,7 @@ Needs `npm run build` (Node entry at `dist/evaluator/server/server.js`) + Chromi
| One-shot | `evaluator evaluate --url … [--fn …]` |
| CSV batch | `evaluator batch -p archive/test.csv -f window.eval -n 1` |

See [evaluator/README.md](evaluator/README.md) for details.
See [evaluator/README.md](../evaluator/README.md) for details.

</details>

Expand Down Expand Up @@ -338,7 +349,7 @@ Takeaways:

## License

[GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html) — see [`LICENSE`](LICENSE).
[GPL-3.0-or-later](https://www.gnu.org/licenses/gpl-3.0.html) — see [`LICENSE`](../LICENSE).

### Security

Expand Down
Loading