visidata: enhance IOC plugins with improved lookups and validation
Expand iplib, iptype, and ioc plugins with better caching, throttling, and lookup logic. Update validation script and showcase journal accordingly. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -21,7 +21,7 @@ from urllib.parse import urlsplit
|
||||
from visidata import vd
|
||||
from visidata.sheets import TableSheet
|
||||
|
||||
from .iplib import JSONNode, VTInfo, parse_vt_ip
|
||||
from .iplib import JSONNode, VTInfo, parse_vt_domain, parse_vt_file, parse_vt_url
|
||||
from .ioclib import MBInfo, URLParts, parse_mb_info, vt_url_id
|
||||
from .lookupcore import (
|
||||
auth_tag,
|
||||
@@ -231,7 +231,43 @@ class DomainValue:
|
||||
@property
|
||||
def vt(self) -> VTInfo:
|
||||
data = _vt_domain_raw(self._d)
|
||||
return parse_vt_ip(data) if data else VTInfo()
|
||||
return parse_vt_domain(data) if data else VTInfo(object_type="domain")
|
||||
|
||||
@property
|
||||
def resolveipv4(self):
|
||||
from .iptype import ip
|
||||
|
||||
out = []
|
||||
for v in self.dns.a:
|
||||
iv = ip(v)
|
||||
if iv is not None:
|
||||
out.append(iv)
|
||||
return tuple(out)
|
||||
|
||||
@property
|
||||
def resolveipv6(self):
|
||||
from .iptype import ip
|
||||
|
||||
out = []
|
||||
for v in self.dns.aaaa:
|
||||
iv = ip(v)
|
||||
if iv is not None:
|
||||
out.append(iv)
|
||||
return tuple(out)
|
||||
|
||||
@property
|
||||
def resolveips(self):
|
||||
return tuple(list(self.resolveipv4) + list(self.resolveipv6))
|
||||
|
||||
@property
|
||||
def resolveip(self):
|
||||
ips4 = self.resolveipv4
|
||||
if ips4:
|
||||
return ips4[0]
|
||||
ips6 = self.resolveipv6
|
||||
if ips6:
|
||||
return ips6[0]
|
||||
return None
|
||||
|
||||
|
||||
def _normalize_domain(s: str) -> str:
|
||||
@@ -308,7 +344,7 @@ class URLValue:
|
||||
@property
|
||||
def vt(self) -> VTInfo:
|
||||
data = _vt_url_raw(self._u)
|
||||
return parse_vt_ip(data) if data else VTInfo()
|
||||
return parse_vt_url(data) if data else VTInfo(object_type="url")
|
||||
|
||||
|
||||
def url_ioc(val: Any) -> Optional[URLValue]:
|
||||
@@ -381,7 +417,7 @@ class HashValue:
|
||||
@property
|
||||
def vt(self) -> VTInfo:
|
||||
data = _vt_file_raw(self._h)
|
||||
return parse_vt_ip(data) if data else VTInfo()
|
||||
return parse_vt_file(data) if data else VTInfo(object_type="file")
|
||||
|
||||
@property
|
||||
def mb(self) -> MBInfo:
|
||||
@@ -411,16 +447,16 @@ vd.addGlobals(domain=domain, url_ioc=url_ioc, hash_ioc=hash_ioc)
|
||||
|
||||
vd.addType(
|
||||
domain,
|
||||
icon="d",
|
||||
icon="🌐",
|
||||
formatter=lambda fmt, v: "" if v is None else str(v),
|
||||
name="Domain",
|
||||
)
|
||||
vd.addType(
|
||||
url_ioc, icon="u", formatter=lambda fmt, v: "" if v is None else str(v), name="URL"
|
||||
url_ioc, icon="🔗", formatter=lambda fmt, v: "" if v is None else str(v), name="URL"
|
||||
)
|
||||
vd.addType(
|
||||
hash_ioc,
|
||||
icon="#",
|
||||
icon="🔐",
|
||||
formatter=lambda fmt, v: "" if v is None else str(v),
|
||||
name="Hash",
|
||||
)
|
||||
@@ -444,3 +480,15 @@ TableSheet.addCommand(
|
||||
vd.addMenuItem("Column", "Type", "Domain", "type-domain")
|
||||
vd.addMenuItem("Column", "Type", "URL (IOC)", "type-url-ioc")
|
||||
vd.addMenuItem("Column", "Type", "Hash", "type-hash")
|
||||
|
||||
|
||||
try:
|
||||
_probe = TableSheet("_probe")
|
||||
if _probe.getCommand(";d") is None:
|
||||
TableSheet.bindkey(";d", "type-domain")
|
||||
if _probe.getCommand(";u") is None:
|
||||
TableSheet.bindkey(";u", "type-url-ioc")
|
||||
if _probe.getCommand(";h") is None:
|
||||
TableSheet.bindkey(";h", "type-hash")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
Reference in New Issue
Block a user