remove infra.md.example, infra.md is the source of truth
This commit is contained in:
114
ayn-antivirus/ayn_antivirus/signatures/feeds/virusshare.py
Normal file
114
ayn-antivirus/ayn_antivirus/signatures/feeds/virusshare.py
Normal file
@@ -0,0 +1,114 @@
|
||||
"""VirusShare feed for AYN Antivirus.
|
||||
|
||||
Downloads MD5 hash lists from VirusShare.com — one of the largest
|
||||
free malware hash databases. Each list contains 65,536 MD5 hashes
|
||||
of known malware samples (.exe, .dll, .rar, .doc, .pdf, .app, etc).
|
||||
|
||||
https://virusshare.com/hashes
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import os
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
import requests
|
||||
|
||||
from ayn_antivirus.signatures.feeds.base_feed import BaseFeed
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
_BASE_URL = "https://virusshare.com/hashfiles/VirusShare_{:05d}.md5"
|
||||
_TIMEOUT = 30
|
||||
_STATE_FILE = "/var/lib/ayn-antivirus/.virusshare_last"
|
||||
|
||||
|
||||
class VirusShareFeed(BaseFeed):
|
||||
"""Fetch malware MD5 hashes from VirusShare.
|
||||
|
||||
Tracks the last downloaded list number so incremental updates
|
||||
only fetch new lists.
|
||||
"""
|
||||
|
||||
def __init__(self, **kwargs: Any) -> None:
|
||||
super().__init__(**kwargs)
|
||||
self._last_list = self._load_state()
|
||||
|
||||
def get_name(self) -> str:
|
||||
return "virusshare"
|
||||
|
||||
def fetch(self) -> List[Dict[str, Any]]:
|
||||
"""Fetch new hash lists since last update."""
|
||||
return self.fetch_new_lists(max_lists=3)
|
||||
|
||||
def fetch_new_lists(self, max_lists: int = 3) -> List[Dict[str, Any]]:
|
||||
"""Download up to max_lists new VirusShare hash files."""
|
||||
results: List[Dict[str, Any]] = []
|
||||
start = self._last_list + 1
|
||||
fetched = 0
|
||||
|
||||
for i in range(start, start + max_lists):
|
||||
self._rate_limit_wait()
|
||||
url = _BASE_URL.format(i)
|
||||
self._log("Fetching VirusShare_%05d", i)
|
||||
|
||||
try:
|
||||
resp = requests.get(url, timeout=_TIMEOUT)
|
||||
if resp.status_code == 404:
|
||||
self._log("VirusShare_%05d not found — at latest", i)
|
||||
break
|
||||
resp.raise_for_status()
|
||||
except requests.RequestException as exc:
|
||||
self._error("Failed to fetch list %d: %s", i, exc)
|
||||
break
|
||||
|
||||
hashes = [
|
||||
line.strip()
|
||||
for line in resp.text.splitlines()
|
||||
if line.strip() and not line.startswith("#") and len(line.strip()) == 32
|
||||
]
|
||||
|
||||
for h in hashes:
|
||||
results.append({
|
||||
"hash": h.lower(),
|
||||
"threat_name": "Malware.VirusShare",
|
||||
"threat_type": "MALWARE",
|
||||
"severity": "HIGH",
|
||||
"source": "virusshare",
|
||||
"details": f"md5,list={i:05d}",
|
||||
})
|
||||
|
||||
self._last_list = i
|
||||
self._save_state(i)
|
||||
fetched += 1
|
||||
self._log("VirusShare_%05d: %d hashes", i, len(hashes))
|
||||
|
||||
self._log("Fetched %d list(s), %d total hashes", fetched, len(results))
|
||||
if results:
|
||||
self._mark_updated()
|
||||
return results
|
||||
|
||||
def fetch_initial(self, start_list: int = 470, count: int = 11) -> List[Dict[str, Any]]:
|
||||
"""Bulk download for initial setup."""
|
||||
old = self._last_list
|
||||
self._last_list = start_list - 1
|
||||
results = self.fetch_new_lists(max_lists=count)
|
||||
if not results:
|
||||
self._last_list = old
|
||||
return results
|
||||
|
||||
@staticmethod
|
||||
def _load_state() -> int:
|
||||
try:
|
||||
return int(Path(_STATE_FILE).read_text().strip())
|
||||
except Exception:
|
||||
return 480 # Default: start after list 480
|
||||
|
||||
@staticmethod
|
||||
def _save_state(n: int) -> None:
|
||||
try:
|
||||
Path(_STATE_FILE).write_text(str(n))
|
||||
except Exception:
|
||||
pass
|
||||
Reference in New Issue
Block a user