Intel471Ex
An Elixir client library for Intel 471's Verity API providing access to cyber threat intelligence.
Table of Contents
Installation
Add intel471_ex to your list of dependencies in mix.exs:
def deps do
[
{:intel471_ex, "~> 0.2.0"}
]
end
Configuration
This library uses system environment variables for authentication. Create an application in the Verity Developer Portal to obtain a client ID and client secret:
# Required
VERITY_CLIENT_ID=your-verity-client-id
VERITY_CLIENT_SECRET=your-verity-client-secret
# Optional
VERITY_API_URL=https://api.intel471.cloud # Default API URL
Set them in your application:
System.put_env("VERITY_CLIENT_ID", "your-client-id")
System.put_env("VERITY_CLIENT_SECRET", "your-client-secret")
API Coverage
| Service | Module | Key Functions |
|---|---|---|
| Actors | Intel471Ex.Actors | stream |
| ASE | Intel471Ex.Ase | list_monitors |
| Brand Exposure | Intel471Ex.BrandExposure | Monitor CRUD, scan data, config |
| Credentials | Intel471Ex.Credentials | Credential/set/occurrence streams and lookups |
| Entities | Intel471Ex.Entities | stream |
| GIRS | Intel471Ex.Girs | tree |
| Indicators | Intel471Ex.Indicators | stream, get_by_id |
| Malware Intel | Intel471Ex.Malware | events_stream, list_malware, get_malware_family, download_file |
| Observables | Intel471Ex.Observables | stream |
| Reports | Intel471Ex.Reports | Stream/detail for FINTel, Breach Alert, Geopol, Info, Spot, Malware, Vulnerability |
| Sources | Intel471Ex.Sources | Stream/detail for data-leak-site posts, forum posts/messages, chat messages |
| TPRM | Intel471Ex.Tprm | Monitor CRUD, scan data, config |
| Watchers | Intel471Ex.Watchers | alerts_stream, update_alert_status, watcher_groups, watchers |
API Examples
Actors
# Stream actors matching a search term
{:ok, result} = Intel471Ex.Actors.stream(%{actor: "lazarus", size: 10})
IO.puts("Found #{result["count"]} actors")
# Stream with pagination
{:ok, page1} = Intel471Ex.Actors.stream(%{actor: "conti", size: 5})
cursor = page1["cursor_next"]
{:ok, page2} = Intel471Ex.Actors.stream(%{actor: "conti", size: 5, cursor: cursor})
# Filter by server type
{:ok, result} = Intel471Ex.Actors.stream(%{actor: "conti", server_type: "telegram"})
Reports
# Stream all report types
{:ok, result} = Intel471Ex.Reports.stream(%{text_filter: "ransomware", size: 10})
# FINTel reports
{:ok, result} = Intel471Ex.Reports.fintel_stream(%{text_filter: "malware"})
{:ok, report} = Intel471Ex.Reports.fintel_detail("report-id")
# Breach alerts
{:ok, result} = Intel471Ex.Reports.breach_alert_stream(%{text_filter: "Communications"})
{:ok, alert} = Intel471Ex.Reports.breach_alert_detail("alert-id")
# Geopolitical reports
{:ok, result} = Intel471Ex.Reports.geopol_stream(%{country: "US"})
{:ok, report} = Intel471Ex.Reports.geopol_detail("report-id")
# Info reports
{:ok, result} = Intel471Ex.Reports.info_stream(%{text_filter: "vulnerability"})
# Malware reports
{:ok, result} = Intel471Ex.Reports.malware_stream(%{malware_family: "lokibot"})
# Spot reports
{:ok, result} = Intel471Ex.Reports.spot_stream(%{text_filter: "phishing"})
# Vulnerability reports
{:ok, result} = Intel471Ex.Reports.vulnerability_stream(%{cve_name: "CVE-2024-1234"})
{:ok, vuln} = Intel471Ex.Reports.vulnerability_detail("vuln-id")
# Download vulnerability report as PDF
{:ok, %{body: pdf_data}} = Intel471Ex.Reports.vulnerability_download_pdf("vuln-id")
Credentials
# Stream credentials
{:ok, result} = Intel471Ex.Credentials.credential_stream(%{domain: "example.com", size: 10})
{:ok, credential} = Intel471Ex.Credentials.get_credential("credential-id")
# Credential occurrences
{:ok, result} = Intel471Ex.Credentials.credential_occurrence_stream(%{credential_id: "id"})
{:ok, occurrence} = Intel471Ex.Credentials.get_credential_occurrence("occurrence-id")
# Credential sets
{:ok, result} = Intel471Ex.Credentials.credential_set_stream(%{victim: "example.com"})
{:ok, set} = Intel471Ex.Credentials.get_credential_set("set-id")
# Accessed URLs
{:ok, result} = Intel471Ex.Credentials.credential_set_accessed_url_stream(%{credential_set_id: "id"})
Watchers
# Stream alerts
{:ok, result} = Intel471Ex.Watchers.alerts_stream(%{size: 10})
# Update alert status
{:ok, _} = Intel471Ex.Watchers.update_alert_status(12345, "read")
# List watcher groups
{:ok, result} = Intel471Ex.Watchers.watcher_groups()
# List watchers
{:ok, result} = Intel471Ex.Watchers.watchers(%{watcher_group_id: "group-id"})
Entities
# Stream entities
{:ok, result} = Intel471Ex.Entities.stream(%{entity: "intel.com", size: 10})
Indicators
# Stream indicators
{:ok, result} = Intel471Ex.Indicators.stream(%{type: "domain", size: 10})
# Get indicator by ID
{:ok, indicator} = Intel471Ex.Indicators.get_by_id("indicator-id")
Malware Intel
# Stream malware events
{:ok, result} = Intel471Ex.Malware.events_stream(%{malware_family_name: "lokibot", size: 10})
{:ok, event} = Intel471Ex.Malware.get_event("event-id")
# List malware families
{:ok, result} = Intel471Ex.Malware.list_malware(%{text_filter: "trickbot"})
# Get malware family detail
{:ok, family} = Intel471Ex.Malware.get_malware_family("family-id")
# Download malware file
{:ok, %{body: file_data}} = Intel471Ex.Malware.download_file("filename.exe")
Observables
# Stream observables
{:ok, result} = Intel471Ex.Observables.stream(%{observable: "8.8.8.8", size: 10})
GIRS
# Get the GIRS tree
{:ok, tree} = Intel471Ex.Girs.tree()
IO.puts("Found #{tree["count"]} GIR entries")
Sources
# Data leak site posts
{:ok, result} = Intel471Ex.Sources.data_leak_site_posts_stream(%{text_filter: "ransomware"})
# Forum posts
{:ok, result} = Intel471Ex.Sources.forums_posts_stream(%{author: "username"})
{:ok, post} = Intel471Ex.Sources.get_forum_post("post-id")
# Forum private messages
{:ok, result} = Intel471Ex.Sources.forums_private_messages_stream(%{author: "username"})
# Chat messages
{:ok, result} = Intel471Ex.Sources.chat_messages_stream(%{server_type: "telegram"})
# Get an image
{:ok, %{body: image_data}} = Intel471Ex.Sources.get_image("forum", "hash", "image.png")
ASE
# List ASE monitors
{:ok, monitors} = Intel471Ex.Ase.list_monitors()
Brand Exposure
# Monitor CRUD
{:ok, monitors} = Intel471Ex.BrandExposure.list_monitors()
{:ok, monitor} = Intel471Ex.BrandExposure.create_monitor(%{
name: "example.com",
targets: ["example.com"],
labels: ["brand-monitor"],
frequency: "daily",
impact: "major"
})
{:ok, monitor} = Intel471Ex.BrandExposure.get_monitor("monitor-id")
{:ok, _} = Intel471Ex.BrandExposure.delete_monitor("monitor-id")
# Configuration
{:ok, config} = Intel471Ex.BrandExposure.get_config_current("base")
{:ok, schema} = Intel471Ex.BrandExposure.get_config_schema()
TPRM
# Monitor CRUD
{:ok, monitors} = Intel471Ex.Tprm.list_monitors()
{:ok, monitor} = Intel471Ex.Tprm.create_monitor(%{
name: "vendor.com",
targets: ["vendor.com"],
collection_method: "passive"
})
# Configuration (uses Bearer token for writes)
{:ok, config} = Intel471Ex.Tprm.get_config_current("base")
{:ok, _} = Intel471Ex.Tprm.put_config_user("base", %{"key" => "value"}, "bearer-token")
File Downloads
# Download a file to a specific path
{:ok, path} = Intel471Ex.Downloader.download_file(
"https://api.intel471.cloud/integrations/malware-intel/v1/malware/files/sample/download",
"/tmp/malware_sample.zip"
)
# Auto-extract filename from URL
{:ok, path} = Intel471Ex.Downloader.download_file_auto(
"https://api.intel471.cloud/integrations/sources/v1/data-leak-sites/file-listings/123",
"downloads"
)
Error Handling
case Intel471Ex.Actors.stream(%{actor: "test"}) do
{:ok, result} ->
IO.puts("Found #{result["count"]} actors")
{:error, %{status: status, message: message}} ->
IO.puts("API error #{status}: #{message}")
{:error, exception} ->
IO.puts("Network error: #{inspect(exception)}")
end
Migration from v0.1.x (Titan API)
This version (0.2.0) is a breaking change. Key migration steps:
- Environment variables: Replace
INTEL471_USERNAME/INTEL471_API_KEYwithVERITY_CLIENT_ID/VERITY_CLIENT_SECRET - API URL: Default changed from
https://api.intel471.com/v1tohttps://api.intel471.cloud - Removed modules:
Intel471Ex.Alerts,Intel471Ex.Search,Intel471Ex.Vulnerabilities - New modules:
Entities,Indicators,Malware,Observables,Girs,Sources,Ase,BrandExposure,Tprm - API style: All endpoints now use cursor-based streaming instead of offset/count pagination