Skip to content

High-performance Rust HTTP virus scanner (ClamAV CVD) - #1

Merged
eldadfux merged 8 commits into
mainfrom
cursor/rust-clamav-http-scanner-4d71
Aug 18, 2026
Merged

High-performance Rust HTTP virus scanner (ClamAV CVD)#1
eldadfux merged 8 commits into
mainfrom
cursor/rust-clamav-http-scanner-4d71

Conversation

@eldadfux

@eldadfux eldadfux commented Aug 18, 2026

Copy link
Copy Markdown
Member

Native Rust HTTP virus scanner that parses ClamAV public CVD/CLD databases (no libclamav), streams file and hash uploads, and hot-reloads verified signature updates with zero downtime.

Playground

TanStack Start + shadcn UI in a second Docker container, wired through docker-compose.yml:

docker compose up --build
  • UI: http://127.0.0.1:3000 — scan files, hash lookups, batch hashes
  • API: http://127.0.0.1:8080
  • Responses are parsed into verdicts, signature names, sizes, hashes, and scan time (duration_us)
  • Same-origin /api/* proxies to defender (DEFENDER_URL)

CI

GitHub Actions matching other Appwrite Docker image repos (console, assistant):

  • Testscargo test --locked plus playground npm test / typecheck
  • Build — multi-arch (linux/amd64, linux/arm64) Docker build on PRs/main, push: false
  • Release — publishing a GitHub Release logs into Docker Hub with vars.DOCKERHUB_USERNAME / secrets.DOCKERHUB_TOKEN and pushes appwrite/defender with semver tags (x.y.z, x.y, x)

Virus database sizes (18 Aug 2026)

File On disk Unpacked Header signatures
main.cvd 84.95 MiB (89,072,577 B) 225.39 MiB 3,287,027
daily.cvd 22.34 MiB (23,426,416 B) 82.14 MiB 355,605
bytecode.cvd 0.27 MiB (281,702 B) 1.24 MiB 80 (not executed)
Total baked (main+daily) 107.56 MiB 307.5 MiB

Loaded engine (main+daily, PUA off): ~540k file hashes, ~102k body, ~307k logical. RSS ≈ 1.4 GiB.

HTTP e2e benchmarks (live TCP, Criterion, 4× x86_64)

Synthetic engine:

Endpoint Time Throughput
GET /health 36 µs 27.7 k req/s
POST /scan EICAR 45 µs 22.3 k req/s
POST /scan/hash 45 µs 22.4 k req/s
POST /scan clean 64 KiB 241 µs 259 MiB/s
POST /scan clean 1 MiB 3.37 ms 296 MiB/s
EICAR ×32 concurrent 583 µs/batch 54.9 k req/s

Official daily.cvd over HTTP: EICAR 44.5 µs; hash lookup 43.6 µs; clean 64 KiB 667 µs (93.7 MiB/s); EICAR ×16 concurrent 54.0 k req/s.

What it does

  • Parses the 512-byte ClamAV-VDB header, gzip+tar body, MD5 integrity, and official RSA cli_versig
  • Loads .hdb/.hsb (Hash:Size:Name), .mdb/.msb (Size:Hash:Name), .ndb, .ldb, FP/ignore
  • Streamable POST /scan, POST /scan/hash, POST /scan/hashes
  • Background updater verifies CVDs and arc-swaps the engine with zero downtime
  • Docker bakes current official DBs at build time
Open in Web Open in Cursor 

cursoragent and others added 7 commits August 18, 2026 11:25
Parse official ClamAV CVD/CLD containers (header, MD5, RSA dsig), load
hash/NDB/LDB signatures, and serve streamable file and hash scans over
HTTP. Bake databases into the Docker image and hot-swap verified updates
without restarting the server.

Co-authored-by: Eldad A. Fux <eldadfux@users.noreply.github.com>
Use a single Aho-Corasick pass with sparse logical-signature counts so
scans no longer allocate a 269k-row matrix. Document release-mode
throughput and official daily.cvd load time.

Co-authored-by: Eldad A. Fux <eldadfux@users.noreply.github.com>
Bind a live TCP listener in Criterion and drive /scan, /scan/hash, and
concurrent uploads with keep-alive HTTP. Record on-disk and unpacked
sizes for main/daily/bytecode, and parse PE section hashes as Size:MD5:Name.

Co-authored-by: Eldad A. Fux <eldadfux@users.noreply.github.com>
Match other Appwrite image repos: cargo tests on PRs, a multi-arch
Docker build without push, and a Release workflow that publishes
appwrite/defender to Docker Hub on GitHub Releases.

Co-authored-by: Eldad A. Fux <eldadfux@users.noreply.github.com>
Ship a shadcn/TanStack Start UI in its own container that proxies
defender, parses scan/hash JSON into structured verdicts, and
documents the payload fields. docker compose up runs scanner + UI.

Co-authored-by: Eldad A. Fux <eldadfux@users.noreply.github.com>
- Change default port mapping in docker-compose to allow for environment variable override.
- Update README to reflect new port and clarify UI functionality.
- Rename playground to Defender UI for consistency and clarity.
- Enhance scan result display with clearer status messages and technical details.
- Refactor code for better organization and readability, including the introduction of an accordion for technical details.
- Adjust manifest and route titles for better alignment with the application purpose.

Co-authored-by: Eldad A. Fux <eldadfux@users.noreply.github.com>
- Update README to reflect the new application name and provide a clearer description.
- Remove the manifest.json file as it is no longer needed.
- Introduce a new font-face for improved typography in the UI.
- Refactor theme toggle component to improve rendering logic.
- Enhance API call functions for better error handling and response management.
- Update various components to improve layout and loading states.

Co-authored-by: Eldad A. Fux <eldadfux@users.noreply.github.com>
@eldadfux
eldadfux marked this pull request as ready for review August 18, 2026 13:41
@greptile-apps

greptile-apps Bot commented Aug 18, 2026

Copy link
Copy Markdown

Greptile Summary

The PR adds a native Rust HTTP virus scanner, CVD parsing and updates, container/release automation, and a browser playground. The target-filtering fix closes the reported unknown-file false-positive path but makes several supported target-specific signature classes unreachable.

  • Implements streaming file and hash scanning backed by parsed ClamAV databases
  • Adds verified database loading, background updates, and atomic engine replacement
  • Adds Docker, CI/release workflows, tests, benchmarks, and a TanStack playground
  • Tightens NDB/LDB target filtering

Confidence Score: 4/5

The PR is not yet safe to merge because target-specific signatures for several supported formats are now skipped unconditionally.

The strict target predicate receives Any for HTML, OLE2, mail, text, Flash, Java, graphics, and other inputs because detect_kind cannot emit those target types, so matching NDB/LDB signatures produce false-negative scan verdicts.

Files Needing Attention: src/engine/mod.rs and src/signatures/ndb.rs

Important Files Changed

Filename Overview
src/engine/mod.rs Implements core scanning and target filtering, but strict equality suppresses signatures for every target type that detect_kind cannot identify.
src/signatures/ndb.rs Parses the complete target-type range, exposing the mismatch with the engine's narrower file classifier.
src/signatures/ldb.rs Parses logical-signature targets that are consistently guarded by the engine's target predicate.
src/http/mod.rs Adds the streaming HTTP scan and hash endpoints with no accepted follow-up finding.
src/updater/mod.rs Adds verified background database updates and atomic engine replacement with no accepted follow-up finding.
playground/src/lib/defender-proxy.ts Adds the playground's same-origin Defender proxy; no follow-up-eligible finding was retained.

Fix all with Greploop Fix All in Claude Code Fix All in Codex

Prompt To Fix All With AI
### Issue 1
src/engine/mod.rs:561-563
**Target signatures become unreachable**

When an HTML, OLE2, mail, text, Flash, Java, graphics, or other file is scanned, `detect_kind` returns `Any`, so this strict equality rejects signatures targeting the file's actual format and causes matching malware to receive a clean verdict.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Reviews (2): Last reviewed commit: "Refactor target matching logic and add c..." | Re-trigger Greptile

Comment thread src/engine/mod.rs
- Simplify the target_ok function by removing unnecessary condition.
- Introduce a new test case to validate behavior of target matching for unknown file types.
- Ensure that the scan results for different target types are correctly asserted in the tests.
Comment thread src/engine/mod.rs
@eldadfux
eldadfux merged commit de03326 into main Aug 18, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants