Post

VirusTotal Watcher

VirusTotal Watcher

A lightweight, real-time download scanner that quarantines new files in ~/Downloads, checks their SHA256 hash against VirusTotal, and only releases them once verified clean.

Why

Every file you download is a potential threat. Browsers don’t verify file reputation beyond basic Safe Browsing checks. VT Watcher adds a second layer — every file that lands in your Downloads folder is automatically hashed, checked against 70+ antivirus engines via VirusTotal, and held in quarantine until cleared.

How It Works

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
New file arrives in ~/Downloads
        │
        ▼
  Strip execute permission
  Move to ~/Downloads/.quarantine/
        │
        ▼
  Compute SHA256 hash
  Log hash to ~/.local/share/vt-watcher-hashes.log
        │
        ▼
  Query VirusTotal API with hash
        │
        ├── CLEAN (0 detections)
        │     → Release back to ~/Downloads
        │     → Cache hash to skip future re-scans
        │
        ├── MALICIOUS / SUSPICIOUS
        │     → Keep quarantined
        │     → Desktop notification (critical)
        │
        └── UNKNOWN (not in VT database)
              → Keep quarantined
              → Desktop notification to review

Requirements

  • OS: Linux (tested on Arch Linux)
  • Dependencies: inotify-tools, curl, jq, libnotify (notify-send)
  • VirusTotal API key: Free tier at virustotal.com (4 requests/min, 500/day)
  • GNU Stow: For dotfiles deployment

On Arch:

1
sudo pacman -S inotify-tools curl jq libnotify

Installation

1. Clone the dotfiles

The watcher lives in a stow-managed dotfiles repo with the following structure:

1
2
3
4
5
6
7
~/.dotfiles/
├── bin/.local/bin/
│   ├── vt-watcher.sh        # Main watcher daemon
│   └── vt-release.sh        # Manual quarantine release tool
├── systemd-user/.config/systemd/user/
│   └── vt-watcher.service   # Systemd user service
└── Makefile

2. Add your API key

1
2
mkdir -p ~/.config/vt-watcher
echo "YOUR_VT_API_KEY" > ~/.config/vt-watcher/api_key

3. Deploy

1
2
cd ~/.dotfiles
make security

This runs stow bin and stow systemd-user, reloads systemd, and enables the watcher service.

Or manually:

1
2
3
4
5
cd ~/.dotfiles
stow bin
stow systemd-user
systemctl --user daemon-reload
systemctl --user enable --now vt-watcher.service

Usage

Check service status

1
systemctl --user status vt-watcher.service

View logs

1
2
3
4
5
# Activity log
tail -f ~/.local/share/vt-watcher.log

# Hash log
cat ~/.local/share/vt-watcher-hashes.log

Manage quarantine

1
2
3
4
5
# List quarantined files
vt-release.sh --list

# Manually release a file after review
vt-release.sh suspicious-file.exe

Example Log Output

1
2
3
[2026-03-25 08:13:35] NEW: Processing document.pdf
[2026-03-25 08:13:35] HASH: document.pdf -> fb5a425bd3b3cd6...
[2026-03-25 08:13:51] CLEAN: document.pdf (0:0:0:63) — releasing from quarantine

The detection stats format is malicious:suspicious:harmless:undetected — representing how many of VirusTotal’s 70+ engines flagged the file.

How It Handles Edge Cases

ScenarioBehavior
Browser partial downloads (.part, .crdownload)Skipped until renamed to final filename
File still being writtenWaits for file size to stabilize before scanning
Same file re-downloadedCache hit — skips re-scan, no delay
VT rate limit exceededWaits 60 seconds and retries once
API key missing or invalidService exits with error and desktop notification
File not in VT databaseStays quarantined with notification to review manually

Limitations

  • Hash-only lookup: VT only knows files that have been previously submitted. A brand-new, targeted payload will return “unknown” — not a guarantee of safety.
  • Free API tier: 4 requests/minute, 500/day. Fine for normal download volume, but heavy downloading will hit the rate limit.
  • Not a sandbox: This checks file reputation, not runtime behavior. For behavioral analysis, pair with ClamAV or a local sandbox.
  • No browser integration: Files are caught after they land on disk. There is a brief window between write and quarantine.

Pairing with ClamAV

VT Watcher complements a local ClamAV setup. VT checks cloud reputation across 70+ engines in real time, while ClamAV performs signature-based scanning locally on a schedule. Together they provide both immediate reputation checks and periodic deep scans:

1
2
3
4
5
6
7
8
9
~/.dotfiles/
├── bin/.local/bin/
│   ├── clamav-hourly.sh      # Local AV scan
│   ├── vt-watcher.sh         # Cloud reputation check
│   └── vt-release.sh         # Quarantine management
└── systemd-user/.config/systemd/user/
    ├── clamav-hourly.service
    ├── clamav-hourly.timer
    └── vt-watcher.service

Source

vt-watcher.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#!/bin/bash
WATCH_DIR="$HOME/Downloads"
QUARANTINE_DIR="$HOME/Downloads/.quarantine"
CONFIG_DIR="$HOME/.config/vt-watcher"
API_KEY_FILE="$CONFIG_DIR/api_key"
VT_API="https://www.virustotal.com/api/v3/files"
CLEAN_CACHE="$CONFIG_DIR/clean_cache"

# Watch for new files with inotifywait
# Quarantine immediately, hash, query VT, release or hold
inotifywait -m -r \
    -e close_write -e moved_to \
    --exclude '\.quarantine' \
    --format '%w%f' \
    "$WATCH_DIR" | while read -r file; do
        # compute sha256, check cache, quarantine, query VT
        # release if clean, hold if malicious/unknown
    done

vt-release.sh

1
2
3
#!/bin/bash
# Usage: vt-release.sh <filename>    — release from quarantine
#        vt-release.sh --list        — list quarantined files
This post is licensed under CC BY 4.0 by the author.