ioc
Indicators of compromise (IOCs): pull IPs, domains, URLs, emails, file hashes and CVE IDs out of any text, check what something is, and defang them (1.2.3[.]4, hxxp://) so nobody clicks a bad link by accident.
import ioc
Example
import fs
import ioc
let email = fs.read("apps/data/phishing_email.txt")
let found = ioc.extract(email)
say("Domains:", found.domains)
say("IPs:", found.ips)
say("URLs:", found.urls.map(fn(u) { return ioc.defang(u) }))
say(ioc.kind("44d88612fea8a8f36de82e1278abb02f"), ioc.is_ip("10.0.0.300"))
Functions
extract(text, options?)
returns mapFind every IOC in text: {"ips", "domains", "urls", "emails", "md5", "sha1", "sha256", "cves"}, each a list without repeats, in the order they first appear. Defanged IOCs (hxxp://, [.], (.), [@]) are found too and given back refanged; pass {"refang": false} to skip that. File names like report.pdf are not counted as domains. Domains and IPs inside URLs and emails are listed as well. Email addresses keep the case of the part before @. Message-ID / References header values are not counted as emails.
rows(found)
returns listTurn the map from extract() (or the text itself) into a list of {"type", "value"} maps, one per IOC, ready for report.table().
defang(text)
returns textMake the IOCs in text safe to share: http becomes hxxp, dots in hosts become [.], colons in IPv6 addresses become [:] and @ in emails becomes [@]. Everything else in the text stays the same. refang() undoes it.
refang(text)
returns textUndo defanging: hxxp:// and hxxps:// become http(s)://, [.] (.) {.} [dot] become dots, [@] [at] become @, and [:] becomes a colon.
is_ip(text)
returns boolTrue for a valid IPv4 (each part 0-255) or IPv6 address. Defanged ones count.
is_domain(text)
returns boolTrue for a domain name with a real-looking ending, like evil.example.com. "report.pdf" is a file, so it is false.
is_hash(text)
returns boolTrue for an MD5, SHA-1, SHA-256 or SHA-512 hash (hex of the right length).
kind(text)
returns text or nilWhat an IOC is: "ip", "url", "email", "md5", "sha1", "sha256", "sha512", "cve" or "domain". nil if it's none of them.