A Rust port of vt_analysis.sh — a command-line tool for scanning files and URLs against VirusTotal via the vt-cli tool.
- File scanning — single files, directories, or glob patterns
- URL scanning — submit and retrieve URL analysis reports
- Batch mode — scan multiple targets in one run with a consolidated summary table
- Smart caching — results cached to
~/.vt_cache.json(24h TTL for files, 1h for URLs) - Hash deduplication — skips upload if VirusTotal already knows the file by SHA-256
- Polling — optional
--waitflag to block until analysis completes - Sandbox behaviour — DNS lookups, IP traffic, HTTP conversations, dropped files, permissions
- YARA matches — crowdsourced YARA rule hits
- VT Intelligence — Sigma rules, IDS/IPS alerts, threat severity (premium API)
- HTML report — self-contained dark-themed report with engine results table
- Markdown log — auto-appended scan log with verdict badges
- Webhook notifications — Slack-compatible POST on scan completion
- Recursive archive scanning — unpacks ZIP/JAR/APK and scans inner files
- CI/CD integration —
--thresholdflag sets exit code2when detections meet or exceed N
| Tool | Purpose | Install |
|---|---|---|
vt-cli |
VirusTotal API client | See link |
sha256sum |
File hashing | Usually pre-installed on Linux/macOS |
| Tool | Purpose |
|---|---|
unzip |
Recursive archive scanning (--recursive) |
curl |
Webhook notifications (--notify) |
file |
MIME-type detection for archive scanning |
Configure vt-cli with your API key before use:
vt config apikey YOUR_API_KEY_HEREGet a free API key at virustotal.com.
- Rust 1.70+ (
rustuprecommended)
# Clone or copy the project
git clone <your-repo-url>
cd vt_analysis
# Build release binary
cargo build --release
# Optionally install to PATH
cargo install --path .The compiled binary will be at ./target/release/vt_analysis.
vt_analysis <file|dir|glob|url> [OPTIONS]
# Single file
vt_analysis suspicious.apk
# Directory (scans all files one level deep)
vt_analysis ./samples/
# Glob pattern
vt_analysis *.apk
# URL (auto-detected)
vt_analysis https://suspicious-site.com
# URL (explicit flag)
vt_analysis --url https://suspicious-site.com| Flag | Short | Description |
|---|---|---|
--wait |
-w |
Poll until scan completes (recommended) |
--output <file> |
-o |
Save raw JSON report to file |
--threshold <n> |
-t |
Exit code 2 if malicious detections ≥ n |
| Flag | Description |
|---|---|
--html <file> |
Generate self-contained HTML report |
--behaviour |
Show sandbox behaviour summary (files only) |
--yara |
Show crowdsourced YARA rule matches (files only) |
--intel |
Show VT Intelligence enrichment (premium API) |
--all |
Enable --behaviour + --yara + --intel |
| Flag | Description |
|---|---|
--notify <webhook_url> |
POST summary to Slack/webhook on completion |
--recursive |
Unpack ZIP/JAR/APK archives and scan inner files |
--no-cache |
Skip local cache, always re-query VirusTotal |
--log <file> |
Append scan result to markdown log (default: vt_scan_log.md) |
--no-log |
Disable markdown logging entirely |
-h, --help |
Show help |
# Scan a file and wait for results
vt_analysis suspicious.apk --wait
# Scan a URL
vt_analysis https://suspicious-site.com --wait
# Full report with all enrichment + HTML output
vt_analysis malware.apk --wait --all --html report.html
# Scan multiple files, fail CI if any have ≥ 1 detection
vt_analysis *.apk --wait --threshold 1
# Scan a URL explicitly with webhook notification
vt_analysis --url https://malware.example.com --wait --notify https://hooks.slack.com/...
# Scan without using cache and save raw JSON
vt_analysis sample.exe --wait --no-cache --output result.json
# Recursive archive scan
vt_analysis bundle.zip --wait --recursive| Code | Meaning |
|---|---|
0 |
All scans completed; no threshold exceeded |
1 |
Fatal error (missing dependency, no input, etc.) |
2 |
One or more targets exceeded the --threshold detection count |
Results are cached at ~/.vt_cache.json:
- Files — cached for 24 hours (keyed by SHA-256)
- URLs — cached for 1 hour (keyed by base64url of the URL)
Use --no-cache to bypass the cache and always re-query VirusTotal.
By default, each scan result is appended to vt_scan_log.md in the current directory:
| Scanned At | Type | Target | Verdict | Detections | Identifier | Report |
|---------------------|---------|---------------|----------------|------------|------------|---------|
| 2025-01-01 12:00:00 | 📄 File | malware.apk | 🔴 MALICIOUS | 42 / 72 | `abc123…` | [View] |
| 2025-01-01 12:01:00 | 🌐 URL | http://bad… | 🟢 CLEAN | 0 / 93 | `xyz789…` | [View] |Disable with --no-log, or specify a different path with --log <file>.
--html report.html produces a self-contained, dark-themed report with:
- Verdict banner (colour-coded)
- Detection stat cards
- Full engine results table (colour-coded by category)
- Direct link to VirusTotal
| Crate | Purpose |
|---|---|
clap |
CLI argument parsing |
serde_json |
JSON parsing (replaces jq) |
sha2 + hex |
SHA-256 hashing (replaces sha256sum) |
base64 |
Base64 encode/decode |
chrono |
Timestamps and formatting |
colored |
Terminal colour output |
reqwest |
HTTP webhook notifications (replaces curl) |
home |
Cross-platform $HOME resolution |
glob |
Glob pattern expansion |
tempfile |
Safe temporary file handling |
anyhow |
Ergonomic error handling |
| Area | Bash | Rust |
|---|---|---|
| JSON parsing | External jq process |
serde_json in-process |
| SHA-256 | External sha256sum process |
sha2 crate in-process |
| Error handling | Manual ` | |
| Associative arrays | declare -A |
HashMap<String, String> |
| Globals | Bash variables | Config + BatchState structs |
| Type safety | None | Full Rust type system |
The vt-cli binary is still invoked as an external subprocess, identical to the bash version, as there is no official Rust VirusTotal client library.
MIT