rinoxRinox
EnrichmentTIP

VirusTotal to ThreatQ (ThreatQuotient) Integration

Enrich existing ThreatQ indicators with VirusTotal verdicts so analysts have multi-engine context inline with the indicator record. The integration walks a watchlist of ThreatQ indicators (or all updated-since indicators), looks each one up in VirusTotal API v3, and writes the analysis back as an attribute on the indicator.

This is an enrichment pattern, not a one-way feed: the target indicator already exists in ThreatQ. The integration's job is to add context, not to create new indicators from VT.

What you get

A real excerpt from a generated VirusTotal-to-ThreatQ (ThreatQuotient) integration. The full script ships with logging, env-var loading, error handling, FIFO-capped dedup state, and a README.

virustotal_to_threatq.py
# RINOX INTEGRATION: VirusTotal -> ThreatQ enrichment
def lookup_vt(hash_value):
    # API v3 — single call returns analysis stats AND per-engine results.
    resp = requests.get(f"https://www.virustotal.com/api/v3/files/{hash_value}",
                        headers={"x-apikey": VT_KEY}, timeout=20)
    if resp.status_code == 404:
        return None
    resp.raise_for_status()
    return resp.json()["data"]["attributes"]

def map_severity(stats):
    malicious = stats.get("malicious", 0)
    suspicious = stats.get("suspicious", 0)
    if malicious >= 5:  return "high"
    if malicious >= 1 or suspicious >= 3: return "medium"
    return "low"

Common pitfalls

The mistakes that turn this integration from "works once" into "loses data silently for three weeks."

  • 01VT API v3 returns nested `data.attributes.last_analysis_stats` and `data.attributes.last_analysis_results`. Extract from the bulk lookup payload, do not make a separate call per engine (N+1 elimination, Phase 3).
  • 02VT free tier rate-limits at 4 requests/minute, 500/day. Honour `X-RateLimit-Remaining` and back off rather than hammering the API.
  • 03ThreatQ's attribute API requires the indicator type as part of the path. Pre-resolve indicator type from the ThreatQ payload, do not re-query.
  • 04Map VT severity to ThreatQ score with an explicit table — do not silently coerce `harmless` and `undetected` into the same bucket as the type-safety appendix calls out.

Generate this for your environment.

Pre-fills the form with VirusTotal and ThreatQ (ThreatQuotient). You write a sentence about your use case, we write the rest.

Generate VirusTotalThreatQ (ThreatQuotient)

Frequently asked

  • Does this overwrite existing ThreatQ attributes?

    No — it appends. The generated script writes attributes with a `source: virustotal` tag so subsequent runs can update or replace them deterministically.

  • What if I have a VT private API key?

    Same path. Private keys give you higher rate limits and additional fields, and the generated script honours `VT_PRIVATE_KEY=true` by enabling the extra fields.

  • Can I run this on URL or domain indicators too?

    Yes — the script dispatches by indicator type. Hashes hit `/files/{id}`, URLs hit `/urls/{id}`, domains hit `/domains/{name}`.

  • How does it avoid hammering ThreatQ?

    Batched indicator pull at the top, per-indicator VT lookup with rate-aware backoff, batched ThreatQ writes at the bottom. One round-trip in, one round-trip out per indicator.