Install
openclaw skills install virustotal3Free malware, network and IP analysis! Scan files, URLs, domains, and IPs with VirusTotal. Use when the user asks to scan, check, or analyze a file, URL, dom...
openclaw skills install virustotal3Interact with the VirusTotal Public API v3 to scan files, URLs, domains, and IP addresses for malware analysis and threat intelligence.
Secrets file: Store your API key in a file (default: ~/.openclaw/secrets/virustotal.env):
VIRUSTOTAL_API_KEY=your_api_key_here
Create with chmod 600. Get your API key from https://www.virustotal.com/gui/my-apikey.
Loading: All curl examples use . (dot) source to load credentials into the shell environment:
. "${VIRUSTOTAL_SECRETS_FILE:-$HOME/.openclaw/secrets/virustotal.env}"
Override the default path with the VIRUSTOTAL_SECRETS_FILE environment variable.
NEVER display secrets in tool output. The . source command loads credentials into shell variables silently — no output is produced. This is the correct and secure approach.
Banned patterns:
cat ~/.openclaw/secrets/virustotal.envVIRUSTOTAL_API_KEY=xxx curl ...If credential loading fails: Fix the secrets file path or contents. Do NOT bypass security by hardcoding values.
Any file, URL, domain, or IP submitted to VirusTotal may be shared with VirusTotal's security partners and could become accessible to third parties beyond your control.
Before submitting:
Before sending ANY file, URL, domain, or IP to VirusTotal, you MUST:
This applies to ALL operations that send data outbound:
POST /files)POST /urls)GET /domains/{domain})GET /ip_addresses/{ip})There is NO override, NO exception, and NO "fast path". Even if the user says "just scan it" or "quick check", you must still confirm and warn about privacy implications.
Example confirmation:
"This will send [resource] to VirusTotal, which shares data with its security partners. OK to proceed?"
When a user wants to check a file:
All endpoints use this secure pattern. Replace {PATH} with the endpoint path and add any extra flags as needed:
. "${VIRUSTOTAL_SECRETS_FILE:-$HOME/.openclaw/secrets/virustotal.env}" \
&& _H=$(mktemp) && chmod 600 "$_H" \
&& printf 'x-apikey: %s\n' "$VIRUSTOTAL_API_KEY" > "$_H" \
&& curl -s "https://www.virustotal.com/api/v3{PATH}" -H@"$_H" \
&& rm -f "$_H"
For POST requests add -X POST and appropriate data flags after the URL.
For file uploads replace the curl URL with the upload URL and add -F "file=@/path/to/file".
All endpoints return JSON with this common shape:
{
"data": {
"id": "resource_identifier",
"type": "file|url|domain|ip_address|analysis",
"attributes": {
"reputation": 0,
"last_analysis_stats": {
"malicious": 0,
"suspicious": 0,
"harmless": 0,
"undetected": 0
},
"last_analysis_results": {
"EngineName": {
"category": "harmless|malicious|suspicious|undetected",
"result": "clean|Trojan.Generic|phishing|unrated",
"engine_update": "20230101",
"engine_version": "1.0.0"
}
}
}
}
}
sha256, md5, sha1, names, size, type_descriptionurl, titlewhois, creation_date, last_update_date, last_dns_records, last_https_certificate, rdapcountry, as_owner, network, tagsstatus ("completed"|"queued"|"in-progress"|"failed"), statshttps://www.virustotal.com/api/v3x-apikey header (loaded from secrets file, see Request Pattern)Endpoint: POST /files
Description: Upload and scan a file with VirusTotal. Returns an analysis ID.
⚠️ Before uploading: Always confirm with the user. File uploads share the entire file with VirusTotal and its partners. Prefer hash lookups when a SHA256/MD5/SHA1 is available.
File size limits:
/files/upload_url endpoint firstcurl example:
# Replace {PATH} in the request pattern above with: /files
curl -s -X POST "https://www.virustotal.com/api/v3/files" -H@"$_H" -F "file=@/path/to/file"
See Response Structure above. Unique fields for this endpoint: id, type.
Error handling:
Endpoint: GET /files/{id}
Description: Get analysis report for a file by its hash (MD5, SHA1, or SHA256).
curl example:
# Replace {PATH} in the request pattern above with: /files/{hash}
curl -s "https://www.virustotal.com/api/v3/files/{hash}" -H@"$_H"
See Response Structure above. Unique fields for this endpoint: sha256, md5, sha1, names, size, type_description.
Endpoint: POST /files/upload_url
Description: Get a URL for uploading files larger than 32MB (up to 650MB).
curl example:
# Replace {PATH} in the request pattern above with: /files/upload_url
curl -s "https://www.virustotal.com/api/v3/files/upload_url" -H@"$_H"
Endpoint: POST /urls
Description: Submit a URL for scanning. Returns an analysis ID.
⚠️ Privacy: URL lookups are query-only (no upload) but share the URL with VirusTotal and its partners. Always confirm with the user first.
curl example:
# Replace {PATH} in the request pattern above with: /urls
curl -s -X POST "https://www.virustotal.com/api/v3/urls" -H@"$_H" --form-urlencode "url=https://example.com"
See Response Structure above. Unique fields for this endpoint: id, type.
Endpoint: GET /urls/{id}
Description: Get analysis report for a URL. The {id} parameter should be a URL identifier (not the raw URL - see URL identifiers below).
URL identifiers: Convert URLs to identifiers using base64 encoding of - prefixed URL:
echo -n "https://example.com" | base64 # returns aHR0cHM6Ly9leGFtcGxlLmNvbQ==
curl example:
# Replace {PATH} in the request pattern above with: /urls/{url_id}
curl -s "https://www.virustotal.com/api/v3/urls/{url_id}" -H@"$_H"
See Response Structure above. Unique fields for this endpoint: url, title.
Endpoint: GET /domains/{domain}
Description: Get analysis report for a domain.
⚠️ Privacy: Domain lookups are query-only (no upload) but reveal to VirusTotal that you are investigating this domain. Always confirm with the user first.
curl example:
# Replace {PATH} in the request pattern above with: /domains/{domain}
curl -s "https://www.virustotal.com/api/v3/domains/example.com" -H@"$_H"
See Response Structure above. Unique fields for this endpoint: whois, creation_date, last_update_date, last_dns_records, last_https_certificate, rdap.
Endpoint: GET /ip_addresses/{ip}
Description: Get analysis report for an IP address.
⚠️ Privacy: IP lookups are query-only (no upload) but reveal to VirusTotal that you are investigating this IP. Always confirm with the user first.
curl example:
# Replace {PATH} in the request pattern above with: /ip_addresses/{ip}
curl -s "https://www.virustotal.com/api/v3/ip_addresses/8.8.8.8" -H@"$_H"
See Response Structure above. Unique fields for this endpoint: country, as_owner, network, tags.
Endpoint: GET /analyses/{id}
Description: Check the status of an analysis (file or URL scan).
curl example:
# Replace {PATH} in the request pattern above with: /analyses/{analysis_id}
curl -s "https://www.virustotal.com/api/v3/analyses/{analysis_id}" -H@"$_H"
See Response Structure above. Unique fields for this endpoint: status ("completed"|"queued"|"in-progress"|"failed"), stats.
| Code | Description | Action |
|---|---|---|
| 200 | Success | Process response |
| 400 | Bad Request | Check request parameters |
| 401 | Unauthorized | Verify API key |
| 403 | Forbidden | Check permissions, premium required |
| 404 | Not Found | Resource doesn't exist |
| 429 | Too Many Requests | Rate limited - wait and retry |
| 500 | Internal Server Error | Server error - try again later |
When you receive a 429 response:
{
"error": {
"code": "error_code",
"message": "Error description"
}
}
For URL reports, you need to encode the URL as follows:
# Method 1: Using base64
URL="https://example.com"
URL_ID=$(echo -n "$URL" | base64)
echo "$URL_ID" # aHR0cHM6Ly9leGFtcGxlLmNvbQ==
# Method 2: Using Python
python3 -c "import base64; print(base64.b64encode(b'https://example.com').decode())"
Last updated: 2026-05-25