diff options
| author | Danilo M. <danix@danix.xyz> | 2026-09-10 16:22:10 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-09-10 16:22:10 +0200 |
| commit | eebf1f3fcc794b8a1700fe27b49c1bac202f89b8 (patch) | |
| tree | 7e0db634398e027c47a63769d1fa27633244f664 /docs/BACKLOG.md | |
| parent | f1d706040785c90f114f80e7f45c597af06a03a9 (diff) | |
| download | abusectl-eebf1f3fcc794b8a1700fe27b49c1bac202f89b8.tar.gz abusectl-eebf1f3fcc794b8a1700fe27b49c1bac202f89b8.zip | |
docs: settle the vendor and MISP destinations
The report spec names three destination kinds and the module writes only
one. Verified through the public API: generate() on a manifest with an IP
contact returns a single email row, and nothing in the tree mentions a misp
or api row. The plan the module was built from never carried that task, so
report is complete against its plan and incomplete against its spec. Found
when the submit brainstorm began, since submit is specified to fill bodies
into rows that do not exist.
The addendum settles which rows exist: configured AND holding an indicator
the destination accepts, each row carrying only the types it can act on.
The accepted types were read from each vendor's own documentation rather
than from memory, which is the provider table's lesson. That reading is
also what removed VirusTotal: its only submission endpoints are POST /urls
and file upload, so it accepts exactly what URLhaus accepts and nothing
else, and it takes no verdict with a submission. It goes to backlog item 6
with the findings, including the reason its more interesting read side
collides with the fourth property.
Also records that generate() rebuilds destinations wholesale, which is safe
only while the freeze rule holds, and names that as a constraint on the
submit spec rather than a description of this one.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0176FYdVfpzUq8S9jecqQqL6
Diffstat (limited to 'docs/BACKLOG.md')
| -rw-r--r-- | docs/BACKLOG.md | 65 |
1 files changed, 65 insertions, 0 deletions
diff --git a/docs/BACKLOG.md b/docs/BACKLOG.md index 87ec4b3..a520ae0 100644 --- a/docs/BACKLOG.md +++ b/docs/BACKLOG.md @@ -10,6 +10,7 @@ number and gains a status rather than being renumbered. | 3 | Expose kept cases so qtmaildir can tag spam | ? | open, unsized | | 4 | `Report-Type: phishing` is unverified against x-arf | XS | closed | | 5 | `report.build()` raises KeyError on an identity with no email | XS | closed | +| 6 | VirusTotal as a destination, or as enrichment | S | open, deferred | ## 1. Skip boilerplate namespace URLs @@ -183,3 +184,67 @@ remaining gap is a hand-edited config and the error message it deserves. alone, since where the check lives decides whether the user sees an exit code and a sentence or a traceback. Not a leak: the failure is loud and nothing is sent. + +## 6. VirusTotal as a destination, or as enrichment + +**Deferred**, not rejected. Recorded with the findings so it is reconsidered +from checked documentation rather than from memory. + +**Observed.** The report spec's addendum settles three destinations, MISP, +AbuseIPDB and URLhaus. VirusTotal was examined in the same pass and left out. + +**What the documentation says**, read 2026-09-10 from `docs.virustotal.com` +and the endpoint index at `virustotal.readme.io/llms.txt`: + +- The only submission endpoints are `POST /urls` and file upload. **There is + no endpoint that submits a domain or an IP address as a new indicator.** + `POST /ip/{ip}/analyse` and `POST /domains/{domain}/analyse` re-analyse data + VirusTotal already holds, which is not reporting. +- Files are submitted as FILES. This tool holds an attachment's sha256 and + never the attachment, so a hash IOC earns no VirusTotal submission either. +- The auth header is `x-apikey`. + +So its submit side accepts URLs and nothing else, which is exactly URLhaus's +accepted type. There is no case where VirusTotal earns a row and URLhaus does +not. + +**Why it was left out.** The overlap above, plus a difference in kind: +`POST /urls` means "scan this", not "this is malicious". It carries no verdict, +no category and no comment, so the argument the x-arf body makes, what happened +and what is wanted, has nowhere to land. Recording a judgement would mean a +second call to `POST /urls/{id}/votes` with different semantics. URLhaus is +purpose-built for reporting a malicious URL and takes a threat classification +and tags. + +**The interesting half is the READ side, and it collides with a property.** +`GET /domains/{d}` and `GET /ip/{ip}` answer questions review actually has: +how old is this domain, has anyone else flagged this host, is this dedicated +attacker infrastructure or a compromised legitimate one. That would change +what a user writes in a report and whether they send one. + +Two obstacles, and the second is the real one: + +- The second property says nothing is ever fetched or resolved. A VirusTotal + lookup asks a third party ABOUT an indicator rather than fetching it, so no + tracker fires and nothing confirms the address is live. That is the same + shape as an RDAP query, which the tool already makes. The property survives. +- The fourth property is the obstacle. A query discloses which host is being + investigated, and `contacts.is_queryable()` is the single admission point + that exists because that leak happened three times. **A URL lookup cannot + pass it**: VirusTotal's URL endpoint takes a URL, and the fourth property is + precisely that a query carries a bare host or IP and never a URL. Redacting + the URL first does not rescue it, since a redacted URL hashes to a different + `/urls/{id}` than the real one and the lookup returns nothing. Domain and IP + lookups are bare values and would be fine. + +There is also a preference cost: VirusTotal is Google-owned, and every lookup +tells Google which infrastructure a named consultant is investigating. RDAP +carries the same disclosure but is unavoidable if the tool wants an abuse +contact; this would be optional convenience. + +**Before building.** Use the tool on real cases first and find out whether +review actually feels under-informed. Enrichment built against a guess about +what review will want is the first rung of the ladder failing. If it is built, +it is a `contacts`-time enrichment over domains and IPs through the existing +admission point, writing what it learns into the manifest, and it needs its +own spec: it changes a module that is already swept and settled. |
